@ghostystudio/cli 0.28.0 → 0.30.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 +23 -2
- package/dist/index.js +447 -68
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ ghosty engines ls [--models] # valid --engine / --model
|
|
|
20
20
|
ghosty bags ls|create|set|rm <workspace> ["bag"] [--tokens 20M] [--recurring on|off] [--hard-cap on|off] [--note] # token bags (changing: staff)
|
|
21
21
|
ghosty spaces renew <workspace> --until YYYY-MM-DD [--note] [--dry-run] # staff
|
|
22
22
|
ghosty spaces rename <old> <new> [--dry-run] # staff, slug only
|
|
23
|
-
ghosty agents ls
|
|
23
|
+
ghosty agents ls [--engine E] [--ids] [--sponsor EMAIL | --owner EMAIL] # --sponsor/--owner: any account (Ghosty staff)
|
|
24
24
|
ghosty agents create --name NAME [--engine E] [--model M] [--prompt TEXT | --prompt-file FILE] [--env K=V,…] [--workspace SLUG] [--channels teams=off]
|
|
25
25
|
ghosty agents get <id> [--fields name,model,prompt] [--prompt-out FILE]
|
|
26
26
|
ghosty agents export <id> [--out DIR] # the agent as a YAML file (+ its skills with --out)
|
|
@@ -72,7 +72,13 @@ ghosty schedule ls <id>
|
|
|
72
72
|
ghosty schedule rm <id> <schedule-id>
|
|
73
73
|
ghosty schedule add|ls|rm <id> … --teams <workspace>#<room>[/<thread-id>] # in a Teams room/thread: it answers there as if mentioned
|
|
74
74
|
|
|
75
|
-
ghosty me files ls|upload|rm
|
|
75
|
+
ghosty me files ls|get|upload|rm [--q TEXT] [--kind image|video|audio|document|other] [--cursor C]
|
|
76
|
+
ghosty conversations archive|rm <id> --kind test|channel [--before 7d] [--dry-run]
|
|
77
|
+
ghosty agents docs dedupe [--dry-run] # your account: merge documents with the same content
|
|
78
|
+
# Ghosty staff
|
|
79
|
+
ghosty conversations ls --user EMAIL [--since 30d] · conversations show <id> <conv> --why
|
|
80
|
+
ghosty turns stats [--since 14d] [--all] · push log --user EMAIL [--since 2d]
|
|
81
|
+
ghosty novedades ls|create|set|test|send|rm [--title] [--body] [--image FILE] [--link] [--to] [--publish] [--at]
|
|
76
82
|
ghosty credentials ls|set|rm [provider] [--from-env VAR] # never as an argument
|
|
77
83
|
ghosty docs
|
|
78
84
|
|
|
@@ -89,6 +95,21 @@ Every command has its own help with examples: `ghosty <command> --help` or `ghos
|
|
|
89
95
|
|
|
90
96
|
Exit codes: `0` ok · `1` API error · `2` usage error · `3` not signed in.
|
|
91
97
|
|
|
98
|
+
### Changes in 0.30.0
|
|
99
|
+
|
|
100
|
+
- Support (Ghosty staff): `conversations ls --user EMAIL`, `conversations show <agent> <conv> --why` (who sees it and why), `turns stats` (real failure rate), `push log --user EMAIL`, `novedades ls|create|set|test|send|rm` (Android app announcements, `--image FILE`).
|
|
101
|
+
- `conversations archive|rm <agent> --kind test|channel [--before 7d]`: clean an agent's history (rm: tests only). Deleting an agent now also deletes its chat copy.
|
|
102
|
+
- `agents docs dedupe`: merge your documents with the same content (keeps every agent's access); `dedupe <keep> <drop>` for two by hand.
|
|
103
|
+
- `me files ls --q TEXT [--cursor]`, `me files get <id> [--out]`, `chat --file <id>` (attach from your files).
|
|
104
|
+
- `channels wa groups set <agent> <group> --name "…"`.
|
|
105
|
+
|
|
106
|
+
### Changes in 0.29.0
|
|
107
|
+
|
|
108
|
+
- `agents ls --engine E --ids` (ids only, for `xargs`); staff: `agents ls --sponsor EMAIL | --owner EMAIL` lists any account's agents, and `agents get|set|box` work on them by id (logged).
|
|
109
|
+
- `try --media FILE` and `chat --media FILE` attach files as from the app (no `--waba` needed).
|
|
110
|
+
- `turns cancel <agent> <conversation>`: stops the turn AND that conversation's background jobs (video, subagents, pending wake-ups).
|
|
111
|
+
- `agents box --recycle` on Ghosty · Lite / Goose recreates its box with the current image (`--reenv`, `--force`); `agents set --prompt-mode append|replace`.
|
|
112
|
+
|
|
92
113
|
### Changes in 0.28.0
|
|
93
114
|
|
|
94
115
|
- `ghosty demos try <slug> "…"`: try a public demo as a visitor from the terminal (keeps the thread; `--new`; `--recycle-between --agent X` checks memory across boxes). `ghosty demos history <agent>` (Ghosty staff): visitors' turns, messages, files and clones, `--timeline`.
|
package/dist/index.js
CHANGED
|
@@ -8016,6 +8016,34 @@ var SERVER_MESSAGES = [
|
|
|
8016
8016
|
["no hay un turno pendiente con ese id (\xBFya corri\xF3?)", "no pending turn with that id (did it already run?)"],
|
|
8017
8017
|
["los chats de los visitantes de un demo: s\xF3lo el staff de Ghosty", "a demo's visitor chats: Ghosty staff only"],
|
|
8018
8018
|
["since inv\xE1lido: \xAB{}\xBB (7d, 12h o una fecha ISO)", "invalid since: \xAB{}\xBB (7d, 12h or an ISO date)"],
|
|
8019
|
+
["buscar agentes de otras cuentas: s\xF3lo el staff de Ghosty", "searching other accounts' agents: Ghosty staff only"],
|
|
8020
|
+
["pon al menos un filtro: sponsor, owner, workspace o engine", "add at least one filter: sponsor, owner, workspace or engine"],
|
|
8021
|
+
["no hay una cuenta con ese correo", "there's no account with that email"],
|
|
8022
|
+
["files: [{ name, mimeType, data (base64) }], hasta 8 de 10 MB", "files: [{ name, mimeType, data (base64) }], up to 8 of 10 MB"],
|
|
8023
|
+
["leer los hilos de otra persona: s\xF3lo el staff de Ghosty", "reading another person's conversations: Ghosty staff only"],
|
|
8024
|
+
["leer un hilo ajeno: s\xF3lo el staff de Ghosty", "reading someone else's conversation: Ghosty staff only"],
|
|
8025
|
+
["la salud de los turnos: s\xF3lo el staff de Ghosty", "turn health: Ghosty staff only"],
|
|
8026
|
+
["la bit\xE1cora de push: s\xF3lo el staff de Ghosty", "the push log: Ghosty staff only"],
|
|
8027
|
+
["las novedades de la app: s\xF3lo el staff de Ghosty", "app announcements: Ghosty staff only"],
|
|
8028
|
+
["falta user (correo)", "missing user (email)"],
|
|
8029
|
+
["no hay una cuenta con el correo {}", "there's no account with the email {}"],
|
|
8030
|
+
["limpiar el historial: due\xF1o, admin del agente o staff", "cleaning the history: the agent's owner, admin or staff"],
|
|
8031
|
+
["los hilos de cliente (channel) se archivan, no se borran: su memoria es la que los sigue atendiendo", "customer threads (channel) are archived, not deleted: their memory is what keeps serving them"],
|
|
8032
|
+
["kind: test | channel", "kind: test | channel"],
|
|
8033
|
+
["action: archive | rm", "action: archive | rm"],
|
|
8034
|
+
["before: 7d, 12h o una fecha ISO", "before: 7d, 12h or an ISO date"],
|
|
8035
|
+
["keep y drop van juntos", "keep and drop go together"],
|
|
8036
|
+
["los dos documentos tienen que ser tuyos y existir", "both documents must exist and be yours"],
|
|
8037
|
+
["es el mismo documento", "it's the same document"],
|
|
8038
|
+
["no se pudo borrar el duplicado", "couldn't delete the duplicate"],
|
|
8039
|
+
["t\xEDtulo y cuerpo son obligatorios", "title and body are required"],
|
|
8040
|
+
["Esa imagen ya la usa \xAB{}\xBB. Cada novedad lleva la suya.", "That image is already used by \xAB{}\xBB. Each announcement needs its own."],
|
|
8041
|
+
["Elige una hora futura.", "Pick a future time."],
|
|
8042
|
+
["S\xF3lo de 9 a 21 h (CDMX).", "Only from 9 to 21 h (CDMX)."],
|
|
8043
|
+
["Publ\xEDcala primero.", "Publish it first."],
|
|
8044
|
+
["Esta novedad ya se mand\xF3.", "This announcement was already sent."],
|
|
8045
|
+
["no hay novedad {}", "no announcement {}"],
|
|
8046
|
+
["lista, b\xFAsqueda y push: s\xF3lo kind=personal de quien lo abri\xF3; por liga: el suyo, y el due\xF1o/admin lee los de canal y prueba", "list, search and push: only kind=personal of whoever opened it; by link: their own, and the owner/admin reads channel and test threads"],
|
|
8019
8047
|
// — Infra interna (casi nunca llega al CLI, pero por si acaso)
|
|
8020
8048
|
["SESSION_SECRET ausente en producci\xF3n \u2014 las cookies quedar\xEDan firmadas con un valor p\xFAblico. Inyectarlo con ./scripts/set_gs_secret.sh SESSION_SECRET (openssl rand -hex 32).", "SESSION_SECRET missing in production \u2014 cookies would be signed with a public value. Inject it with ./scripts/set_gs_secret.sh SESSION_SECRET (openssl rand -hex 32)."],
|
|
8021
8049
|
["sqld: fetch sin namespace", "sqld: fetch without namespace"],
|
|
@@ -8318,7 +8346,7 @@ async function postLong(path, body, timeoutMs = 9e5) {
|
|
|
8318
8346
|
const req = httpsRequest(`${base}${path}`, {
|
|
8319
8347
|
method: "POST",
|
|
8320
8348
|
timeout: timeoutMs,
|
|
8321
|
-
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.
|
|
8349
|
+
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.30.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
|
|
8322
8350
|
}, (res) => {
|
|
8323
8351
|
let buf = "";
|
|
8324
8352
|
res.on("data", (c) => buf += c);
|
|
@@ -8360,7 +8388,7 @@ async function request(path, opts = {}, retried = false) {
|
|
|
8360
8388
|
method: opts.method ?? (opts.body !== void 0 || opts.raw !== void 0 ? "POST" : "GET"),
|
|
8361
8389
|
headers: {
|
|
8362
8390
|
authorization: `Bearer ${token}`,
|
|
8363
|
-
"user-agent": `ghosty-cli/${"0.
|
|
8391
|
+
"user-agent": `ghosty-cli/${"0.30.0"}`,
|
|
8364
8392
|
"accept-language": lang(),
|
|
8365
8393
|
...opts.body !== void 0 ? { "content-type": "application/json" } : {},
|
|
8366
8394
|
...opts.headers
|
|
@@ -8438,6 +8466,16 @@ import { basename, join as join2 } from "node:path";
|
|
|
8438
8466
|
var enc = encodeURIComponent;
|
|
8439
8467
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
8440
8468
|
var MIME = { jpg: "image/jpeg", jpeg: "image/jpeg", png: "image/png", webp: "image/webp", gif: "image/gif", pdf: "application/pdf", ogg: "audio/ogg", opus: "audio/ogg", mp3: "audio/mpeg", m4a: "audio/mp4", wav: "audio/wav", mp4: "video/mp4" };
|
|
8469
|
+
async function localFiles(paths) {
|
|
8470
|
+
const { readFile: readFile14 } = await import("node:fs/promises");
|
|
8471
|
+
const { basename: basename4 } = await import("node:path");
|
|
8472
|
+
return Promise.all(
|
|
8473
|
+
paths.map(async (p) => {
|
|
8474
|
+
const ext = p.toLowerCase().split(".").pop() ?? "";
|
|
8475
|
+
return { name: basename4(p), mimeType: MIME[ext] ?? "application/octet-stream", data: (await readFile14(p)).toString("base64") };
|
|
8476
|
+
})
|
|
8477
|
+
);
|
|
8478
|
+
}
|
|
8441
8479
|
async function tryPost(id, body) {
|
|
8442
8480
|
for (let attempt = 0; ; attempt++) {
|
|
8443
8481
|
const r = await postLong(`/api/v2/agents/${enc(id)}/try`, body);
|
|
@@ -8563,9 +8601,16 @@ var need = (v, what, example) => {
|
|
|
8563
8601
|
if (!v) throw usage(t(`Missing ${what}.`, `Falta ${WHAT_ES[what] ?? what}.`), `${t("Example", "Ejemplo")}: ${example}`);
|
|
8564
8602
|
return v;
|
|
8565
8603
|
};
|
|
8566
|
-
async function agentsLs(workspace) {
|
|
8604
|
+
async function agentsLs(workspace, f = {}) {
|
|
8605
|
+
if (f.sponsor || f.owner) {
|
|
8606
|
+
const q = new URLSearchParams({ ...f.sponsor ? { sponsor: f.sponsor } : {}, ...f.owner ? { owner: f.owner } : {}, ...f.engine ? { engine: f.engine } : {}, ...workspace && !f.all ? { workspace } : {} });
|
|
8607
|
+
const s = await api(`/api/v2/agents?${q}`);
|
|
8608
|
+
if (f.ids) return void process.stdout.write(s.agents.map((a) => a.id).join("\n") + (s.agents.length ? "\n" : ""));
|
|
8609
|
+
return out(s.agents, () => table(s.agents.map((a) => ({ id: a.id, name: a.name, engine: a.engine, model: a.model, owner: a.owner, sponsored: a.sponsored ? t("yes", "s\xED") : "" })), ["id", "name", "engine", "model", "owner", "sponsored"]));
|
|
8610
|
+
}
|
|
8567
8611
|
const r = await api("/api/v2/me/agents?client=cli");
|
|
8568
|
-
const list = workspace ? r.agentes.filter((a) => a.espacio?.nombre === workspace) : r.agentes;
|
|
8612
|
+
const list = (workspace ? r.agentes.filter((a) => a.espacio?.nombre === workspace) : r.agentes).filter((a) => !f.engine || (a.motor ?? a.origen) === f.engine);
|
|
8613
|
+
if (f.ids) return void process.stdout.write(list.map((a) => a.id).join("\n") + (list.length ? "\n" : ""));
|
|
8569
8614
|
const spaceOf = (a) => a.espacio?.tipo === "workspace" ? a.espacio.nombre : a.espacio?.tipo ?? "";
|
|
8570
8615
|
const rows = list.map((a) => ({
|
|
8571
8616
|
id: a.id,
|
|
@@ -8664,12 +8709,24 @@ ${dim(t("Dry run: nothing changed.", "Simulacro: no se cambi\xF3 nada."))}`);
|
|
|
8664
8709
|
}
|
|
8665
8710
|
return out(r2, () => r2.unlinked ? `${lavender("\u2713")} ${t("Unlinked: the prompt stays as it is and is edited by hand again.", "Desenlazado: el prompt se queda como est\xE1 y se vuelve a editar a mano.")}` : `${lavender("\u2713")} Prompt = ${v["prompt-base"]} + ${v["prompt-overlay"]} ${t(`(${r2.chars} chars). It regenerates when the base changes; ghosty agents doctor ${id} tells if the overlay stops applying.`, `(${r2.chars} caracteres). Se regenera cuando cambia la base; ghosty agents doctor ${id} avisa si la capa deja de aplicar.`)}`);
|
|
8666
8711
|
}
|
|
8712
|
+
if (v.engine && v["dry-run"]) {
|
|
8713
|
+
const p = await api(agentPath(id), { method: "PATCH", body: { engine: v.engine, ...v.model ? { model: v.model } : {}, dryRun: true } });
|
|
8714
|
+
return out(p, () => [
|
|
8715
|
+
`${p.from.engine}/${p.from.model ?? "?"} \u2192 ${p.to ? `${p.to.engine}/${p.to.model ?? "?"}` : v.engine}`,
|
|
8716
|
+
...(p.steps ?? []).map((x) => ` \xB7 ${serverText(x)}`),
|
|
8717
|
+
p.ok ? dim(t("Dry run: nothing changed. Run it again without --dry-run to apply.", "Simulacro: no se cambi\xF3 nada. Repite sin --dry-run para aplicarlo.")) : `\u2717 ${serverText(p.error ?? "")}`
|
|
8718
|
+
].join("\n"));
|
|
8719
|
+
}
|
|
8667
8720
|
const body = {};
|
|
8668
8721
|
if (v.name) body.name = v.name;
|
|
8669
8722
|
if (v.engine) body.engine = v.engine;
|
|
8670
8723
|
if (v.model) body.model = v.model;
|
|
8671
8724
|
if (v.prompt) body.prompt = v.prompt;
|
|
8672
8725
|
if (v["prompt-file"]) body.prompt = await readFile3(v["prompt-file"], "utf8");
|
|
8726
|
+
if (v["prompt-mode"]) {
|
|
8727
|
+
if (v["prompt-mode"] !== "append" && v["prompt-mode"] !== "replace") throw usage(t("--prompt-mode takes append or replace.", "--prompt-mode acepta append o replace."));
|
|
8728
|
+
body.promptMode = v["prompt-mode"];
|
|
8729
|
+
}
|
|
8673
8730
|
const channels = parseChannels(v.channels);
|
|
8674
8731
|
if (channels) body.channels = channels;
|
|
8675
8732
|
if (v.toolsets) {
|
|
@@ -8781,11 +8838,12 @@ async function tryTurn(id, text, v) {
|
|
|
8781
8838
|
id = need(id, "agent id", 'ghosty try <agent-id> "hello"');
|
|
8782
8839
|
if (v.replay) return tryReplay(id, v);
|
|
8783
8840
|
if (v.whatsapp && !v.waba) info(dim(t("Note: --whatsapp is now --waba (the real WhatsApp path).", "Nota: --whatsapp ahora es --waba (el camino real de WhatsApp).")));
|
|
8784
|
-
if (v.waba || v.whatsapp || v.
|
|
8785
|
-
|
|
8841
|
+
if (v.waba || v.whatsapp || v.history) return tryWaba(id, text, v);
|
|
8842
|
+
const files = v.media?.length ? await localFiles(v.media) : [];
|
|
8843
|
+
if (!text && !v.reset && !files.length) throw usage(t("Missing message.", "Falta el mensaje."), t("Example", "Ejemplo") + ': ghosty try <agent-id> "hello"');
|
|
8786
8844
|
if (process.stderr.isTTY && text && !globals.json) info(dim(t("Thinking\u2026 (a test turn can take up to 3 minutes)", "Pensando\u2026 (un turno de prueba puede tardar hasta 3 minutos)")));
|
|
8787
8845
|
const r = await api(agentPath(id, "/try"), {
|
|
8788
|
-
body: { text, session: v.session, reset: v.reset }
|
|
8846
|
+
body: { text, session: v.session, reset: v.reset, ...files.length ? { files } : {} }
|
|
8789
8847
|
});
|
|
8790
8848
|
out(r, () => (r.text ?? "") + (r.error ? `
|
|
8791
8849
|
${dim(`(${t("warning", "aviso")}: ${serverText(r.error)})`)}` : ""));
|
|
@@ -9005,7 +9063,7 @@ async function agentsDemo(id, v) {
|
|
|
9005
9063
|
async function agentsBox(id, v) {
|
|
9006
9064
|
id = need(id, "agent id", "ghosty agents box <agent-id> [--check PATH] [--contains PATH=TEXT] [--recycle]");
|
|
9007
9065
|
if (v.recycle) {
|
|
9008
|
-
const r2 = await api(agentPath(id, "/box"), { method: "POST", body: { recycle: true } });
|
|
9066
|
+
const r2 = await api(agentPath(id, "/box"), { method: "POST", body: { recycle: true, ...v.force ? { force: true } : {}, ...v.reenv ? { reenv: true } : {} } });
|
|
9009
9067
|
out(r2, () => `${lavender("\u2713")} ${t("Recycled.", "Reciclada.")} ${dim(serverText(r2?.note ?? ""))}
|
|
9010
9068
|
${dim(`${t("Verify after one turn", "Verifica despu\xE9s de un turno")}: ghosty agents box ${id} --check <path>`)}`);
|
|
9011
9069
|
return;
|
|
@@ -9033,7 +9091,7 @@ function capacityLine(c) {
|
|
|
9033
9091
|
// src/commands/chat.ts
|
|
9034
9092
|
import { randomUUID } from "node:crypto";
|
|
9035
9093
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
9036
|
-
import { readFile as readFile4 } from "node:fs/promises";
|
|
9094
|
+
import { readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
|
|
9037
9095
|
import { basename as basename2 } from "node:path";
|
|
9038
9096
|
|
|
9039
9097
|
// src/sse.ts
|
|
@@ -9088,7 +9146,7 @@ async function conversationsShow(agent, sid, v) {
|
|
|
9088
9146
|
return [...head, ...lines, ...tailNote].join("\n\n") || dim(t("(empty)", "(vac\xEDa)"));
|
|
9089
9147
|
});
|
|
9090
9148
|
}
|
|
9091
|
-
async function turn(agent, sid, text, first) {
|
|
9149
|
+
async function turn(agent, sid, text, first, images = []) {
|
|
9092
9150
|
const turnId = randomUUID();
|
|
9093
9151
|
const ctrl = new AbortController();
|
|
9094
9152
|
const res = await request(convPath(agent, `/${enc3(sid)}/events`), { signal: ctrl.signal, headers: { accept: "text/event-stream" } });
|
|
@@ -9101,7 +9159,7 @@ async function turn(agent, sid, text, first) {
|
|
|
9101
9159
|
});
|
|
9102
9160
|
};
|
|
9103
9161
|
process.on("SIGINT", onSigint);
|
|
9104
|
-
const sent = api(convPath(agent, `/${enc3(sid)}/messages`), { body: { content: text, turnId, primerTurno: first } });
|
|
9162
|
+
const sent = api(convPath(agent, `/${enc3(sid)}/messages`), { body: { content: text, turnId, primerTurno: first, ...images.length ? { images } : {} } });
|
|
9105
9163
|
let answer = "";
|
|
9106
9164
|
let usageData;
|
|
9107
9165
|
let ours = false;
|
|
@@ -9143,8 +9201,12 @@ async function chat(agent, message, v) {
|
|
|
9143
9201
|
first = true;
|
|
9144
9202
|
if (!globals.json) info(dim(t(`conversation ${sid} (continue it with --conversation ${sid})`, `conversaci\xF3n ${sid} (s\xEDguela con --conversation ${sid})`)));
|
|
9145
9203
|
}
|
|
9146
|
-
|
|
9147
|
-
await
|
|
9204
|
+
const images = [
|
|
9205
|
+
...v.media?.length ? await localFiles(v.media) : [],
|
|
9206
|
+
...(v.file ?? "").split(",").map((x) => x.trim()).filter(Boolean).map((fileId) => ({ fileId }))
|
|
9207
|
+
];
|
|
9208
|
+
if (message || images.length) {
|
|
9209
|
+
await turn(agent, sid, message ?? "", first, images);
|
|
9148
9210
|
return;
|
|
9149
9211
|
}
|
|
9150
9212
|
if (!process.stdin.isTTY) {
|
|
@@ -9176,9 +9238,20 @@ async function readAll() {
|
|
|
9176
9238
|
return s;
|
|
9177
9239
|
}
|
|
9178
9240
|
async function meFiles(sub, arg, v) {
|
|
9179
|
-
const ex = "ghosty me files ls [--kind image|video|audio|document|other] | upload <file> | rm <file-id>";
|
|
9241
|
+
const ex = "ghosty me files ls [--q TEXT] [--kind image|video|audio|document|other] [--cursor C] | get <file-id> [--out FILE] | upload <file> | rm <file-id>";
|
|
9180
9242
|
switch (sub) {
|
|
9181
9243
|
case "ls": {
|
|
9244
|
+
if (v.q !== void 0 || v.cursor) {
|
|
9245
|
+
const q = new URLSearchParams({ q: v.q ?? "", ...v.kind ? { kind: v.kind } : {}, ...v.cursor ? { cursor: v.cursor } : {}, ...v.limit ? { limit: v.limit } : {} });
|
|
9246
|
+
const r2 = await api(`/api/v2/me/files?${q}`);
|
|
9247
|
+
out(r2, () => {
|
|
9248
|
+
const rows = r2.files.map((f) => ({ id: f.id, name: f.name, kind: f.kind, size: f.size, created: f.createdAt.slice(0, 10) }));
|
|
9249
|
+
return `${table(rows, ["id", "name", "kind", "size", "created"])}${r2.nextCursor ? `
|
|
9250
|
+
${dim(t(`More: ghosty me files ls --q "${v.q ?? ""}" --cursor ${r2.nextCursor}`, `M\xE1s: ghosty me files ls --q "${v.q ?? ""}" --cursor ${r2.nextCursor}`))}` : ""}
|
|
9251
|
+
${dim(t('Attach one in a chat: ghosty chat <agent> "\u2026" --file <id>', 'Adj\xFAntalo en un chat: ghosty chat <agente> "\u2026" --file <id>'))}`;
|
|
9252
|
+
});
|
|
9253
|
+
return;
|
|
9254
|
+
}
|
|
9182
9255
|
const r = await api(
|
|
9183
9256
|
`/api/v2/me/files${v.kind ? `?kind=${enc3(v.kind)}` : ""}`
|
|
9184
9257
|
);
|
|
@@ -9189,6 +9262,18 @@ ${dim(t(`${(r.storage.usedBytes / 1e9).toFixed(2)} GB of ${r.storage.limitGb} GB
|
|
|
9189
9262
|
});
|
|
9190
9263
|
return;
|
|
9191
9264
|
}
|
|
9265
|
+
case "get": {
|
|
9266
|
+
if (!arg) throw usage(t("Missing file id.", "Falta el id del archivo."), ex);
|
|
9267
|
+
const r = await api(`/api/v2/me/files/${enc3(arg)}`);
|
|
9268
|
+
if (v.out) {
|
|
9269
|
+
const res = await fetch(r.url);
|
|
9270
|
+
if (!res.ok) throw new CliError(`${res.status} ${t("downloading the file", "al bajar el archivo")}`, EXIT.API);
|
|
9271
|
+
const bytes = Buffer.from(await res.arrayBuffer());
|
|
9272
|
+
await writeFile4(v.out, bytes);
|
|
9273
|
+
return out({ id: arg, saved: v.out, bytes: bytes.length }, () => `${lavender("\u2713")} ${v.out} (${bytes.length} bytes)`);
|
|
9274
|
+
}
|
|
9275
|
+
return out(r, () => r.url);
|
|
9276
|
+
}
|
|
9192
9277
|
case "upload": {
|
|
9193
9278
|
if (!arg) throw usage(t("Missing file.", "Falta el archivo."), ex);
|
|
9194
9279
|
const bytes = await readFile4(arg);
|
|
@@ -9343,7 +9428,7 @@ ${dim(`${t("conversation", "conversaci\xF3n")} ${p.sessionId}`)}`
|
|
|
9343
9428
|
|
|
9344
9429
|
// src/commands/partner.ts
|
|
9345
9430
|
import { createHmac } from "node:crypto";
|
|
9346
|
-
import { readFile as readFile5, writeFile as
|
|
9431
|
+
import { readFile as readFile5, writeFile as writeFile5 } from "node:fs/promises";
|
|
9347
9432
|
var partnerHelp = () => t(
|
|
9348
9433
|
`${bold("Usage:")} ghosty partner <command> [options]
|
|
9349
9434
|
|
|
@@ -9430,7 +9515,7 @@ async function call(path, v, init = {}) {
|
|
|
9430
9515
|
"x-ghosty-ts": ts,
|
|
9431
9516
|
"x-ghosty-sig": sig,
|
|
9432
9517
|
"content-type": "application/json",
|
|
9433
|
-
"user-agent": `ghosty-cli/${"0.
|
|
9518
|
+
"user-agent": `ghosty-cli/${"0.30.0"}`
|
|
9434
9519
|
},
|
|
9435
9520
|
body: method === "GET" ? void 0 : raw
|
|
9436
9521
|
});
|
|
@@ -9454,7 +9539,7 @@ GHOSTY_PARTNER_KEY_SECRET=${r.secret}
|
|
|
9454
9539
|
`;
|
|
9455
9540
|
async function showSecret(r, v) {
|
|
9456
9541
|
if (v["env-file"]) {
|
|
9457
|
-
await
|
|
9542
|
+
await writeFile5(v["env-file"], envLines(r), { mode: 384 });
|
|
9458
9543
|
return out({ keyId: r.keyId, envFile: v["env-file"] }, () => `${lavender("\u2713")} ${r.keyId} ${t(`\u2192 ${v["env-file"]} (mode 600; the secret was not printed)`, `\u2192 ${v["env-file"]} (modo 600; el secreto no se imprimi\xF3)`)}`);
|
|
9459
9544
|
}
|
|
9460
9545
|
if (v.format === "env") return void process.stdout.write(envLines(r));
|
|
@@ -9857,8 +9942,29 @@ ${dim(`${t(`${rows.length} row(s)`, `${rows.length} fila(s)`)} \xB7 ${Math.round
|
|
|
9857
9942
|
throw usage(t(`Unknown dbs subcommand: ${sub}.`, `Subcomando de dbs desconocido: ${sub}.`), t("Try", "Prueba") + ": ghosty dbs ls | tables | query | import | rehost | grant | revoke | rename | rm (ghosty dbs --help)");
|
|
9858
9943
|
}
|
|
9859
9944
|
}
|
|
9945
|
+
async function docsDedupe(v) {
|
|
9946
|
+
const path = "/api/v2/me/documents/dedupe";
|
|
9947
|
+
const pair = v.keep || v.drop ? { keep: v.keep, drop: v.drop } : {};
|
|
9948
|
+
const plan = await api(path, { method: "POST", body: { ...pair, dryRun: true } });
|
|
9949
|
+
const name = (d) => typeof d === "string" ? d : `\xAB${d.name}\xBB ${dim(`${d.id.slice(0, 10)} \xB7 ${d.agents} ${t("agent(s)", "agente(s)")}`)}`;
|
|
9950
|
+
const text = [
|
|
9951
|
+
...plan.merges.map((m) => `${t("keep", "queda")} ${name(m.keep)}
|
|
9952
|
+
${m.drop.map((d) => ` - ${name(d)}`).join("\n")}`),
|
|
9953
|
+
...plan.sameName?.length ? ["", dim(t("Same name, different content (not merged; if they're the same: ghosty agents docs dedupe <keep-id> <drop-id>):", "Mismo nombre, otro contenido (no se unen; si s\xED son el mismo: ghosty agents docs dedupe <id-que-queda> <id-que-se-va>):")), ...plan.sameName.map((g) => ` ${g.map(name).join(" \xB7 ")}`)] : []
|
|
9954
|
+
].join("\n");
|
|
9955
|
+
if (!plan.merges.length) return out(plan, () => text.trim() ? text : dim(t("No duplicate documents.", "Sin documentos duplicados.")));
|
|
9956
|
+
if (v["dry-run"]) return out(plan, () => `${text}
|
|
9957
|
+
${dim(t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada."))}`);
|
|
9958
|
+
const retry = `ghosty agents docs dedupe${v.keep ? ` ${v.keep} ${v.drop ?? ""}` : ""}`;
|
|
9959
|
+
requireYesIfHeadless(v.yes, retry);
|
|
9960
|
+
await confirm(`${text}
|
|
9961
|
+
${t("Merge? The extra copies are deleted and their agents keep access through the one that stays.", "\xBFUnir? Las copias sobrantes se borran y sus agentes siguen leyendo por la que queda.")}`, { yes: v.yes, retry });
|
|
9962
|
+
const r = await api(path, { method: "POST", body: pair });
|
|
9963
|
+
out(r, () => `${lavender("\u2713")} ${t(`${r.merged} duplicate(s) merged \xB7 ${r.grantsMoved} permission(s) moved.`, `${r.merged} duplicado(s) unido(s) \xB7 ${r.grantsMoved} permiso(s) movido(s).`)}`);
|
|
9964
|
+
}
|
|
9860
9965
|
async function agentDocsCmd(sub, id, docs, v) {
|
|
9861
9966
|
let doc = docs[0];
|
|
9967
|
+
if (sub === "dedupe") return docsDedupe(v);
|
|
9862
9968
|
id = need2(id, t("agent id", "el id del agente"), "ghosty agents docs ls <agent-id>");
|
|
9863
9969
|
switch (sub ?? "ls") {
|
|
9864
9970
|
case "ls":
|
|
@@ -9963,9 +10069,22 @@ async function turnsSkipped(id, v) {
|
|
|
9963
10069
|
out(r, () => rows.length ? `${table(rows, ["at", "reason", "phone", "message"])}
|
|
9964
10070
|
${dim(tally)}` : dim(t(`Every customer message${v.since ? ` since ${v.since}` : ""} opened a turn (or none arrived: check ghosty channels whatsapp ls ${id}, "formmy forwards").`, `Todo mensaje de cliente${v.since ? ` desde ${v.since}` : ""} abri\xF3 turno (o no lleg\xF3 ninguno: revisa ghosty channels whatsapp ls ${id}, \xABformmy forwards\xBB).`)));
|
|
9965
10071
|
}
|
|
10072
|
+
async function turnsCancel(id, sid) {
|
|
10073
|
+
const ex = "ghosty turns cancel <agent-id> <conversation-id>";
|
|
10074
|
+
id = need2(id, t("agent id", "el id del agente"), ex);
|
|
10075
|
+
sid = need2(sid, t("conversation id", "el id de la conversaci\xF3n"), ex);
|
|
10076
|
+
const r = await api(`/api/v2/me/agents/${enc6(id)}/conversations/${enc6(sid)}/cancel`, { method: "POST", body: {} });
|
|
10077
|
+
const b = r.background ?? { videos: 0, subagents: 0, wakes: 0 };
|
|
10078
|
+
const bg = b.videos + b.subagents + b.wakes;
|
|
10079
|
+
out(
|
|
10080
|
+
r,
|
|
10081
|
+
() => !r.detenido && !bg ? dim(t("Nothing running in that conversation.", "No hab\xEDa nada corriendo en esa conversaci\xF3n.")) : `${lavender("\u2713")} ${[r.detenido ? t("turn stopped", "turno detenido") : "", b.videos ? t(`${b.videos} video job(s)`, `${b.videos} encargo(s) de video`) : "", b.subagents ? t(`${b.subagents} subagent(s)`, `${b.subagents} subagente(s)`) : "", b.wakes ? t(`${b.wakes} pending wake-up(s)`, `${b.wakes} despertador(es) pendiente(s)`) : ""].filter(Boolean).join(" \xB7 ")}${bg ? dim(t(" \u2014 work already running in its box finishes on its own, but nothing is delivered and the agent isn't woken.", " \u2014 lo que ya corre en su caja termina solo, pero no se entrega ni despierta al agente.")) : ""}`
|
|
10082
|
+
);
|
|
10083
|
+
}
|
|
9966
10084
|
async function turnsCmd(sub, id, turnId, v) {
|
|
9967
10085
|
if (sub === "show") return turnShow(id, turnId, !!v.full);
|
|
9968
|
-
if (sub
|
|
10086
|
+
if (sub === "cancel" || sub === "stop") return turnsCancel(id, turnId);
|
|
10087
|
+
if (sub && sub !== "ls" && sub !== "list") throw usage(t(`Unknown turns subcommand: ${sub}.`, `Subcomando de turns desconocido: ${sub}.`), t("Try", "Prueba") + ": ghosty turns ls <agent-id> --errors --since 1h \xB7 ghosty turns show <agent-id> [turn-id|last] \xB7 ghosty turns cancel <agent-id> <conversation>");
|
|
9969
10088
|
id = need2(id, t("agent id", "el id del agente"), "ghosty turns ls <agent-id> --errors --since 1h");
|
|
9970
10089
|
if (v.skipped) return turnsSkipped(id, v);
|
|
9971
10090
|
const q = new URLSearchParams();
|
|
@@ -10242,7 +10361,7 @@ function findGroup(groups, ref) {
|
|
|
10242
10361
|
}
|
|
10243
10362
|
async function groupsCmd(args, v) {
|
|
10244
10363
|
const [sub, id, ...rest] = args;
|
|
10245
|
-
const ex = 'ghosty channels wa groups ls <agent-id> | set <agent-id> <group> [--role equipo|cliente] [--on|--off] | create <agent-id> "Name"';
|
|
10364
|
+
const ex = 'ghosty channels wa groups ls <agent-id> | set <agent-id> <group> [--role equipo|cliente] [--on|--off] [--name "New name"] | create <agent-id> "Name"';
|
|
10246
10365
|
if (!id) throw usage(t("Missing agent id.", "Falta el id del agente."), `${t("Example", "Ejemplo")}: ${ex}`);
|
|
10247
10366
|
const path = `/api/v2/agents/${enc7(id)}/wa/groups`;
|
|
10248
10367
|
if (v.role !== void 0 && v.role !== "equipo" && v.role !== "cliente") throw usage(t("--role takes equipo or cliente.", "--role acepta equipo o cliente."));
|
|
@@ -10252,10 +10371,10 @@ async function groupsCmd(args, v) {
|
|
|
10252
10371
|
return out(r2, () => rows.length ? table(rows, ["name", "on", "role", "jid"]) : dim(t('No groups. Pair its personal WhatsApp in the web, or: ghosty channels wa groups create <agent-id> "Name"', 'Sin grupos. Parea su WhatsApp personal en la web, o: ghosty channels wa groups create <agent-id> "Nombre"')));
|
|
10253
10372
|
}
|
|
10254
10373
|
if (sub === "create") {
|
|
10255
|
-
const
|
|
10256
|
-
if (!
|
|
10257
|
-
const r2 = await api(path, { method: "POST", body: { create:
|
|
10258
|
-
return out(r2, () => `${lavender("\u2713")} ${t("Created", "Creado")} ${bold(
|
|
10374
|
+
const name2 = rest.join(" ").trim();
|
|
10375
|
+
if (!name2) throw usage(t("Missing group name.", "Falta el nombre del grupo."), t("Example", "Ejemplo") + `: ghosty channels wa groups create ${id} "Equipo ventas" --role equipo`);
|
|
10376
|
+
const r2 = await api(path, { method: "POST", body: { create: name2, role: v.role ?? "equipo" } });
|
|
10377
|
+
return out(r2, () => `${lavender("\u2713")} ${t("Created", "Creado")} ${bold(name2)} (${v.role ?? "equipo"}, on)
|
|
10259
10378
|
${t("invite", "invitaci\xF3n")}: ${r2.inviteUrl}`);
|
|
10260
10379
|
}
|
|
10261
10380
|
if (sub !== "set") throw usage(t(`Unknown groups subcommand: ${sub}.`, `Subcomando de groups desconocido: ${sub}.`), ex);
|
|
@@ -10266,16 +10385,17 @@ async function groupsCmd(args, v) {
|
|
|
10266
10385
|
const g = findGroup(cur.groups, ref);
|
|
10267
10386
|
const enabled = v.on ? true : v.off ? false : g.enabled;
|
|
10268
10387
|
const role = v.role ?? g.role;
|
|
10269
|
-
const
|
|
10388
|
+
const name = v.name?.trim() || void 0;
|
|
10389
|
+
const changes = [...enabled !== g.enabled ? [`${g.enabled ? "on" : "off"}\u2192${enabled ? "on" : "off"}`] : [], ...role !== g.role ? [`${g.role}\u2192${role}`] : [], ...name && name !== g.name ? [`\xAB${g.name}\xBB\u2192\xAB${name}\xBB`] : []];
|
|
10270
10390
|
if (!changes.length) return out({ changed: 0, group: g }, () => dim(t(`Nothing to do: ${g.name} is already ${g.enabled ? "on" : "off"} as ${g.role}.`, `Nada que hacer: ${g.name} ya est\xE1 ${g.enabled ? "encendido" : "apagado"} como ${g.role}.`)));
|
|
10271
10391
|
const plan = `~ ${g.name}: ${changes.join(", ")}`;
|
|
10272
10392
|
if (v["dry-run"]) return out({ dryRun: true, plan: [plan] }, () => `${plan}
|
|
10273
10393
|
${dim(t("Dry run: nothing changed.", "Simulacro: no se cambi\xF3 nada."))}`);
|
|
10274
10394
|
if (enabled && !g.enabled) await confirm(`${bold(plan)}
|
|
10275
10395
|
${t(`The agent will start answering in this group as ${role}.`, `El agente empezar\xE1 a contestar en este grupo como ${role}.`)}`, { yes: v.yes, retry: `ghosty channels wa groups set ${id} "${ref}"${v.role ? ` --role ${v.role}` : ""} --on` });
|
|
10276
|
-
const r = await api(path, { method: "POST", body: { jid: g.jid, enabled, role } });
|
|
10396
|
+
const r = await api(path, { method: "POST", body: { jid: g.jid, enabled, role, ...name ? { name } : {} } });
|
|
10277
10397
|
const now = r.groups.find((x) => x.jid === g.jid);
|
|
10278
|
-
return out({ group: now ?? null, plan: [plan] }, () => `${lavender("\u2713")} ${g.name}: ${now?.enabled ? "on" : "off"} ${t("as", "como")} ${now?.role ?? role}`);
|
|
10398
|
+
return out({ group: now ?? null, plan: [plan] }, () => `${lavender("\u2713")} ${now?.name ?? g.name}: ${now?.enabled ? "on" : "off"} ${t("as", "como")} ${now?.role ?? role}`);
|
|
10279
10399
|
}
|
|
10280
10400
|
var EB_EXPORT = (agentId) => `${(process.env.EASYBITS_URL ?? "https://www.easybits.cloud").replace(/\/+$/, "")}/api/v2/fleet-agents/${enc7(agentId)}/baileys/export`;
|
|
10281
10401
|
async function easybitsSession(agentId, keys) {
|
|
@@ -10585,6 +10705,7 @@ var HELPS_ES = {
|
|
|
10585
10705
|
summary: "Crea, revisa, cambia, diagnostica y borra agentes.",
|
|
10586
10706
|
replace: [
|
|
10587
10707
|
["# the agent as a file; see ghosty apply --help", "# el agente como archivo; ver ghosty apply --help"],
|
|
10708
|
+
["# your account: merge copies with the same content", "# tu cuenta: une las copias con el mismo contenido"],
|
|
10588
10709
|
["# what it really has: effective toolsets, connectors connected + granted", "# lo que de verdad tiene: toolsets efectivos, conectores conectados + concedidos"]
|
|
10589
10710
|
],
|
|
10590
10711
|
flags: [
|
|
@@ -10596,6 +10717,7 @@ var HELPS_ES = {
|
|
|
10596
10717
|
["--no-notify", "create --like: lo comparte con las mismas personas SIN mandarles correo"],
|
|
10597
10718
|
["--workspace SLUG", "create: nace en ese espacio (del que eres due\xF1o o admin), a nombre de su due\xF1o y activo en su Teams"],
|
|
10598
10719
|
["--channels k=on|off,\u2026", "create/set: prende/apaga canales: teams, web, whatsapp, messenger, wa (WhatsApp personal)"],
|
|
10720
|
+
["--engine E --dry-run", "set: qu\xE9 hace cambiar de motor (sesiones que se olvidan, caja que se levanta y cu\xE1l se destruye, si est\xE1 trabajando) sin cambiarlo; cada hilo conserva su conversaci\xF3n"],
|
|
10599
10721
|
["--toolsets LIST", "set: grupos de tools de gs que recibe (crm, sales\u2026): una lista reemplaza; +x / -x agrega o quita"],
|
|
10600
10722
|
["--prompt-base ID", "set: el prompt de este agente = el prompt de ese agente + --prompt-overlay; se regenera cuando cambia la base. none = desliga (conserva el prompt de hoy)"],
|
|
10601
10723
|
["--prompt-overlay FILE", 'set: JSON {"edits":[{"what","find","replace","regex"?,"all"?}]}, aplicados en orden; cada uno debe encontrar su lugar o no cambia nada'],
|
|
@@ -10621,12 +10743,16 @@ var HELPS_ES = {
|
|
|
10621
10743
|
["--rotate", "demo: c\xF3digo nuevo; la liga vieja deja de servir"],
|
|
10622
10744
|
["--check PATH", "box: este archivo debe existir en la m\xE1quina (repetible), p. ej. un archivo que trae la imagen nueva"],
|
|
10623
10745
|
["--contains PATH=TEXT", "box: ese archivo debe contener TEXT (un cambio que no agrega archivos)"],
|
|
10624
|
-
["--recycle", "box: reemplaza sus m\xE1quinas (
|
|
10746
|
+
["--recycle", "box: reemplaza sus m\xE1quinas (nunca a media respuesta); pool: la nueva nace en el siguiente turno; ACP (goose, lite): su caja se recrea ya con la imagen actual"],
|
|
10747
|
+
["--reenv / --force", "box --recycle en ACP: --reenv s\xF3lo re-hornea el env en la misma caja; --force la recrea aunque est\xE9 ocupada (due\xF1o o staff)"],
|
|
10625
10748
|
["--unset-env K1,K2", "set: borra esas variables del env (--env K= tambi\xE9n borra K)"],
|
|
10626
10749
|
["--tool [ch:]tool=on|off", "set: prende/apaga una tool en un canal (wa:actualizar_identidad=off) o en todos (crm_buscar=off); separadas por comas"],
|
|
10627
10750
|
["--bag NAME|none", "set: de qu\xE9 bolsa de tokens de su espacio gasta; none = el saldo del espacio (s\xF3lo staff)"],
|
|
10628
10751
|
["--avatar URL|FILE", "set: su cara en Teams: una liga https o un JPEG/PNG/WebP local (se guarda en img.ghosty.studio)"],
|
|
10629
|
-
["--from-workspace", "rm: lo desactiva en cada Teams donde est\xE9 y luego lo borra (due\xF1o o staff)"]
|
|
10752
|
+
["--from-workspace", "rm: lo desactiva en cada Teams donde est\xE9 y luego lo borra (due\xF1o o staff)"],
|
|
10753
|
+
["--prompt-mode M", "set: append (default: su prompt + la persona de Ghosty) o replace (s\xF3lo su prompt)"],
|
|
10754
|
+
["--sponsor/--owner EMAIL", "ls: agentes de CUALQUIER cuenta que paga / de quien es ese correo (staff de Ghosty); luego se operan por id"],
|
|
10755
|
+
["--ids", "ls: s\xF3lo los ids, uno por rengl\xF3n (para xargs)"]
|
|
10630
10756
|
],
|
|
10631
10757
|
notes: [
|
|
10632
10758
|
"doctor sale con 1 cuando encuentra un problema, as\xED puede frenar un script o un CI.",
|
|
@@ -10680,7 +10806,7 @@ var HELPS_ES = {
|
|
|
10680
10806
|
},
|
|
10681
10807
|
turns: {
|
|
10682
10808
|
summary: "Los turnos recientes de un agente: qu\xE9 contest\xF3, qu\xE9 fall\xF3 y por qu\xE9, y en qu\xE9 se fue el tiempo de un turno.",
|
|
10683
|
-
replace: [["# last turn: each tool, its seconds, output tokens", "# \xFAltimo turno: cada tool, sus segundos, tokens de salida"]],
|
|
10809
|
+
replace: [["# last turn: each tool, its seconds, output tokens", "# \xFAltimo turno: cada tool, sus segundos, tokens de salida"], ["# stop the turn AND its background jobs", "# detiene el turno Y sus encargos en fondo"], ["# staff: real failure rate by engine and error", "# staff: tasa real de fallos por motor y por error"]],
|
|
10684
10810
|
flags: [
|
|
10685
10811
|
["--errors", "s\xF3lo turnos fallidos (igual que --status error)"],
|
|
10686
10812
|
["--skipped", "ls: mensajes de clientes de WhatsApp Business que NO abrieron turno, y por qu\xE9 (operador activo, s\xF3lo registra, pausa, reacci\xF3n, canal apagado, sin saldo)"],
|
|
@@ -10689,6 +10815,7 @@ var HELPS_ES = {
|
|
|
10689
10815
|
["--limit N", "m\xE1ximo de filas (default 20, m\xE1x. 500)"],
|
|
10690
10816
|
["--full", "show: el mensaje que entr\xF3 y la respuesta completos (default: primeros 600 caracteres)"],
|
|
10691
10817
|
["--quota-hits", "ls --workspace: mensajes (WhatsApp Business y Teams) que no abrieron turno porque se acab\xF3 el saldo"],
|
|
10818
|
+
["--all", "stats (staff): cuenta tambi\xE9n staff, canal prueba y agentes borrados"],
|
|
10692
10819
|
["--session S", "ls: s\xF3lo los turnos de esa conversaci\xF3n. Los de Claude dicen \xABresumed\xBB: \u2713 = retom\xF3 la sesi\xF3n del SDK, \u26A0 desde cero (orphan|stale|replaced) = perdi\xF3 la memoria"]
|
|
10693
10820
|
],
|
|
10694
10821
|
notes: ["--skipped guarda 30 d\xEDas y s\xF3lo ve lo que Formmy reenvi\xF3: si Formmy no reenv\xEDa (ghosty channels whatsapp ls, \xABformmy forwards\xBB), a gs nunca le llega el mensaje.", "show imprime lo que entr\xF3 y lo que sali\xF3 en cualquier motor o canal (grupos de WhatsApp tambi\xE9n); la l\xEDnea de tiempo lee el transcript del worker, as\xED que s\xF3lo motores del pool (no ACP/goose)."]
|
|
@@ -10701,7 +10828,7 @@ var HELPS_ES = {
|
|
|
10701
10828
|
flags: [
|
|
10702
10829
|
["--waba", "el camino real de WhatsApp Business: contexto heredado, regla de precios, tablero, voz. Muestra lo que MANDAR\xCDA"],
|
|
10703
10830
|
["--session PHONE", "con --waba: un tel\xE9fono de cliente inventado (10-15 d\xEDgitos); cada tel\xE9fono es su propia conversaci\xF3n"],
|
|
10704
|
-
["--media FILE", "
|
|
10831
|
+
["--media FILE", "adjunta esta foto/PDF/audio. Solo: como desde la app (se repite, hasta 8). Con --waba: la manda el cliente (una por turno)"],
|
|
10705
10832
|
["--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"],
|
|
10706
10833
|
["--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)"],
|
|
10707
10834
|
["--history FILE", "con --waba: lo que se dijo antes, como [{role: user|agent, text, at?}]; cuenta en el primer turno (--reset)"],
|
|
@@ -10720,7 +10847,15 @@ var HELPS_ES = {
|
|
|
10720
10847
|
},
|
|
10721
10848
|
chat: {
|
|
10722
10849
|
summary: "Conversa en streaming; sin mensaje es interactivo.",
|
|
10723
|
-
replace: [['["message"]', '["mensaje"]']]
|
|
10850
|
+
replace: [['["message"]', '["mensaje"]'], ["# staff: who sees it and why", "# staff: qui\xE9n lo ve y por qu\xE9"]],
|
|
10851
|
+
flags: [
|
|
10852
|
+
["--media FILE", "chat: adjunta un archivo local (se repite)"],
|
|
10853
|
+
["--file ID", "chat: adjunta de tus archivos (ghosty me files ls --q \u2026), separados por comas"],
|
|
10854
|
+
["--kind K", "archive|rm: test (pruebas) o channel (clientes: s\xF3lo archivar, su memoria los sigue atendiendo)"],
|
|
10855
|
+
["--before T", "archive|rm: s\xF3lo conversaciones sin mensajes desde T (7d, 12h o ISO)"],
|
|
10856
|
+
["--user EMAIL", "ls/show (staff de Ghosty): las conversaciones de esa persona en cualquier agente; queda en bit\xE1cora"],
|
|
10857
|
+
["--why", "show (staff de Ghosty): clase, de qui\xE9n es, qu\xE9 turno la fij\xF3, y si el due\xF1o / su usuario la ven en su lista o por liga"]
|
|
10858
|
+
]
|
|
10724
10859
|
},
|
|
10725
10860
|
schedule: {
|
|
10726
10861
|
summary: "Turnos que corren despu\xE9s, con el reloj propio del agente.",
|
|
@@ -10745,6 +10880,7 @@ var HELPS_ES = {
|
|
|
10745
10880
|
["--yes", "enable / groups enable: se salta la confirmaci\xF3n (obligatorio sin terminal)"],
|
|
10746
10881
|
["--role R", "groups: equipo (el equipo: escribe en bases de datos, tools de admin) o cliente (como cualquier cliente)"],
|
|
10747
10882
|
["--cached", "groups ls: no le pide a WhatsApp la lista en vivo (m\xE1s r\xE1pido; puede faltar alg\xFAn grupo nuevo)"],
|
|
10883
|
+
["--name N", "groups set: el nombre con que lo ve gs (p. ej. un grupo guardado con su jid de nombre); el asunto en WhatsApp no cambia"],
|
|
10748
10884
|
["--integration ID", "link/unlink/history: el id de la Integration del n\xFAmero de WhatsApp Business en Formmy"],
|
|
10749
10885
|
["--email E", "integrations (staff): lista los n\xFAmeros de otra cuenta de Formmy"],
|
|
10750
10886
|
["--days N", "history: conversaciones activas en los \xFAltimos N d\xEDas (1-30, default 7)"],
|
|
@@ -10892,6 +11028,14 @@ var HELPS_ES = {
|
|
|
10892
11028
|
],
|
|
10893
11029
|
notes: ["try cuenta contra los topes del demo (mensajes por visitante, por IP al d\xEDa) y sale en sus n\xFAmeros y en el CRM.", "history es s\xF3lo para staff de Ghosty: los chats de los visitantes no se le ense\xF1an al due\xF1o."]
|
|
10894
11030
|
},
|
|
11031
|
+
push: {
|
|
11032
|
+
summary: "Staff de Ghosty: qu\xE9 avisos (push) se le mandaron a alguien (fin de turno, permiso, escalar, documento\u2026), a qu\xE9 dispositivo y qu\xE9 contest\xF3 Apple/Google. Se registran desde el 1-oct y se guardan 30 d\xEDas.",
|
|
11033
|
+
notes: ["thread ense\xF1a la clase de la conversaci\xF3n: un \xABchannel\xBB ah\xED es que le lleg\xF3 el aviso de un cliente."]
|
|
11034
|
+
},
|
|
11035
|
+
novedades: {
|
|
11036
|
+
summary: "Staff de Ghosty: las novedades de la app Android. Cada una con su ilustraci\xF3n; mismos frenos que el admin: pru\xE9bala en tu tel\xE9fono primero, push de 9 a 21 h CDMX, 72 h entre env\xEDos a todos.",
|
|
11037
|
+
notes: ["--image se publica en img.ghosty.studio (WebP 1200 px). --to vac\xEDo = todos. send sin --at la manda ya (pregunta); --at '' quita la programaci\xF3n."]
|
|
11038
|
+
},
|
|
10895
11039
|
spaces: {
|
|
10896
11040
|
summary: "Operaciones del staff de Ghosty sobre un espacio: mover \xABpagado hasta\xBB y renombrar su slug. Cada una queda en la bit\xE1cora del espacio.",
|
|
10897
11041
|
flags: [
|
|
@@ -10912,15 +11056,17 @@ var HELPS = {
|
|
|
10912
11056
|
agents: {
|
|
10913
11057
|
summary: "Create, inspect, change, check and delete agents.",
|
|
10914
11058
|
usage: [
|
|
10915
|
-
"ghosty agents ls",
|
|
11059
|
+
"ghosty agents ls [--engine E] [--ids] [--sponsor EMAIL | --owner EMAIL]",
|
|
10916
11060
|
"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]]",
|
|
10917
11061
|
"ghosty agents get <agent-id> [--fields name,model,prompt] [--prompt-out FILE]",
|
|
10918
11062
|
"ghosty agents export <agent-id> [--out DIR] # the agent as a file; see ghosty apply --help",
|
|
10919
11063
|
"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] [--tool [channel:]tool=on|off,\u2026] [--bag NAME|none] [--avatar URL|FILE]",
|
|
11064
|
+
"ghosty agents set <agent-id> --engine E [--model M] --dry-run # what changing the engine does (sessions, machines, memory), without changing it",
|
|
10920
11065
|
"ghosty agents set <agent-id> --prompt-base <base-agent-id> --prompt-overlay overlay.json [--dry-run [--out FILE]] | --prompt-base none",
|
|
10921
11066
|
"ghosty agents doctor <agent-id> [--verbose]",
|
|
10922
11067
|
"ghosty agents docs ls|grant|revoke <agent-id> [doc\u2026] [--write] [--no-deliver] [--like AGENT]",
|
|
10923
11068
|
"ghosty agents docs add <agent-id> --url URL | --file PATH [--name N] [--description TEXT] [--grant read|write|no] [--no-deliver]",
|
|
11069
|
+
"ghosty agents docs dedupe [<keep-id> <drop-id>] [--dry-run] # your account: merge copies with the same content",
|
|
10924
11070
|
"ghosty agents docs replace <agent-id> <doc> --url URL | --file PATH [--note TEXT] # new version: keeps name, access and the agents that use it",
|
|
10925
11071
|
"ghosty agents docs history|restore <agent-id> <doc> [n]",
|
|
10926
11072
|
"ghosty agents share <agent-id> <email> [--role editor|admin]",
|
|
@@ -10941,6 +11087,7 @@ var HELPS = {
|
|
|
10941
11087
|
["--no-notify", "create --like: share it with the same people WITHOUT emailing them"],
|
|
10942
11088
|
["--workspace SLUG", "create: born in that workspace (you own or administer it), owned by its owner and active in its Teams"],
|
|
10943
11089
|
["--channels k=on|off,\u2026", "create/set: turn channels on/off: teams, web, whatsapp, messenger, wa (personal WhatsApp)"],
|
|
11090
|
+
["--engine E --dry-run", "set: what changing the engine does (sessions forgotten, machine started and which one is destroyed, whether it is busy) without changing it; each conversation keeps its history"],
|
|
10944
11091
|
["--toolsets LIST", "set: gs tool groups it gets (crm, sales\u2026): a list replaces; +x / -x add or remove"],
|
|
10945
11092
|
["--prompt-base ID", "set: this agent's prompt = that agent's prompt + --prompt-overlay; regenerates when the base changes. none = unlink (keeps today's prompt)"],
|
|
10946
11093
|
["--prompt-overlay FILE", 'set: JSON {"edits":[{"what","find","replace","regex"?,"all"?}]}, applied in order; each must find its place or nothing changes'],
|
|
@@ -10966,12 +11113,16 @@ var HELPS = {
|
|
|
10966
11113
|
["--rotate", "demo: new code; the old link stops working"],
|
|
10967
11114
|
["--check PATH", "box: this file must exist in the machine (repeatable), e.g. a file the new image brings"],
|
|
10968
11115
|
["--contains PATH=TEXT", "box: that file must contain TEXT (a change that adds no files)"],
|
|
10969
|
-
["--recycle", "box: replace its machines (not mid-turn); the new one starts on the next turn
|
|
11116
|
+
["--recycle", "box: replace its machines (not mid-turn); pool: the new one starts on the next turn; ACP (goose, lite): its box is recreated now with the current image"],
|
|
11117
|
+
["--reenv / --force", "box --recycle on ACP: --reenv only re-bakes the env in the same box; --force recreates it even if busy (owner or staff)"],
|
|
10970
11118
|
["--unset-env K1,K2", "set: delete these env variables (--env K= also deletes K)"],
|
|
10971
11119
|
["--tool [ch:]tool=on|off", "set: one tool on/off in one channel (wa:actualizar_identidad=off) or everywhere (crm_buscar=off); comma-separated"],
|
|
10972
11120
|
["--bag NAME|none", "set: the token bag of its workspace it spends from; none = the workspace balance (staff only)"],
|
|
10973
11121
|
["--avatar URL|FILE", "set: its face in Teams: an https link or a local JPEG/PNG/WebP (stored at img.ghosty.studio)"],
|
|
10974
|
-
["--from-workspace", "rm: turn it off in every Teams where it's active, then delete it (owner or staff)"]
|
|
11122
|
+
["--from-workspace", "rm: turn it off in every Teams where it's active, then delete it (owner or staff)"],
|
|
11123
|
+
["--prompt-mode M", "set: append (default: its prompt + Ghosty's persona) or replace (its prompt only)"],
|
|
11124
|
+
["--sponsor/--owner EMAIL", "ls: agents of ANY account paid by / owned by that email (Ghosty staff); then operate them by id"],
|
|
11125
|
+
["--ids", "ls: only the ids, one per line (for xargs)"]
|
|
10975
11126
|
],
|
|
10976
11127
|
examples: [
|
|
10977
11128
|
"ghosty agents doctor cmu7pgbyr0001gee3ruk172mm",
|
|
@@ -10995,7 +11146,8 @@ var HELPS = {
|
|
|
10995
11146
|
"ghosty agents box <id> --contains /opt/claude-worker/skills/pdf-doc/render-doc.mjs=pdf-templates",
|
|
10996
11147
|
"ghosty agents set <id> --tool wa:actualizar_identidad=off,teams:actualizar_identidad=on",
|
|
10997
11148
|
'ghosty agents set minighosty --bag "MiniGhosty" --avatar https://www.ghosty.studio/avatars/minighosty.png',
|
|
10998
|
-
"ghosty agents rm fable --from-workspace"
|
|
11149
|
+
"ghosty agents rm fable --from-workspace",
|
|
11150
|
+
"ghosty agents ls --sponsor testers@ghosty.studio --engine ghosty-lite --ids | xargs -I{} ghosty agents set {} --engine claude --dry-run"
|
|
10999
11151
|
],
|
|
11000
11152
|
notes: ["doctor exits 1 when it finds a problem, so it can gate a script or CI.", "A derived prompt can't be edited directly (409): change the base or the overlay, or unlink it with --prompt-base none.", "demo with no flags only prints its status: link, expiry, caps and numbers."],
|
|
11001
11153
|
learn: `${REF}#agentes`
|
|
@@ -11081,7 +11233,7 @@ var HELPS = {
|
|
|
11081
11233
|
},
|
|
11082
11234
|
turns: {
|
|
11083
11235
|
summary: "An agent's recent turns: what answered, what failed and why, and where one turn's time went.",
|
|
11084
|
-
usage: ["ghosty turns ls <agent-id> [--errors | --status STATE | --skipped] [--since 1h] [--limit N]", "ghosty turns ls --workspace <slug> --quota-hits [--days 14]", "ghosty turns show <agent-id> [turn-id | last] [--full]"],
|
|
11236
|
+
usage: ["ghosty turns ls <agent-id> [--errors | --status STATE | --skipped] [--since 1h] [--limit N]", "ghosty turns ls --workspace <slug> --quota-hits [--days 14]", "ghosty turns show <agent-id> [turn-id | last] [--full]", "ghosty turns cancel <agent-id> <conversation-id> # stop the turn AND its background jobs", "ghosty turns stats [--since 14d] [--all] # staff: real failure rate by engine and error"],
|
|
11085
11237
|
flags: [
|
|
11086
11238
|
["--errors", "only failed turns (same as --status error)"],
|
|
11087
11239
|
["--skipped", "ls: WhatsApp Business customer messages that did NOT open a turn, and why (operator active, records only, paused, reaction, channel off, no balance)"],
|
|
@@ -11090,6 +11242,7 @@ var HELPS = {
|
|
|
11090
11242
|
["--limit N", "max rows (default 20, max 500)"],
|
|
11091
11243
|
["--full", "show: the whole message that came in and the whole answer (default: first 600 chars)"],
|
|
11092
11244
|
["--quota-hits", "ls --workspace: messages (WhatsApp Business and Teams) that got no turn because the balance ran out"],
|
|
11245
|
+
["--all", "stats (staff): also count staff users, the test channel and deleted agents"],
|
|
11093
11246
|
["--session S", "ls: only this conversation's turns. Claude turns show \xABresumed\xBB: \u2713 = it kept the SDK session, \u26A0 from scratch (orphan|stale|replaced) = it lost its memory"]
|
|
11094
11247
|
],
|
|
11095
11248
|
examples: ["ghosty turns ls <id> --errors --since 1h", "ghosty turns ls --workspace descti --quota-hits --days 30", "ghosty turns ls <id> --skipped --since 1d", "ghosty turns ls <id> --json --limit 100", "ghosty turns show <id> # last turn: each tool, its seconds, output tokens"],
|
|
@@ -11118,7 +11271,7 @@ var HELPS = {
|
|
|
11118
11271
|
flags: [
|
|
11119
11272
|
["--waba", "the real WhatsApp Business path: inherited context, price rule, board, voice. Shows what it WOULD send"],
|
|
11120
11273
|
["--session PHONE", "with --waba: a made-up customer phone (10-15 digits); each phone is its own conversation"],
|
|
11121
|
-
["--media FILE", "
|
|
11274
|
+
["--media FILE", "attach this photo/PDF/audio. Alone: as from the app (repeatable, up to 8). With --waba: the customer sends it (one per turn)"],
|
|
11122
11275
|
["--as-formmy", "with --media: it arrives like a real Formmy forward (proxy with credential, copy to gs, transcription) and says if the copy worked"],
|
|
11123
11276
|
["--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)"],
|
|
11124
11277
|
["--history FILE", "with --waba: what was said before, as [{role: user|agent, text, at?}]; counts on the first turn (--reset)"],
|
|
@@ -11144,8 +11297,23 @@ var HELPS = {
|
|
|
11144
11297
|
},
|
|
11145
11298
|
chat: {
|
|
11146
11299
|
summary: "Stream a conversation; with no message it is interactive.",
|
|
11147
|
-
usage: [
|
|
11148
|
-
|
|
11300
|
+
usage: [
|
|
11301
|
+
'ghosty chat <agent-id> ["message"] [--conversation ID] [--media FILE]\u2026 [--file ID[,ID\u2026]]',
|
|
11302
|
+
"ghosty conversations ls <agent-id>",
|
|
11303
|
+
"ghosty conversations show <agent-id> <conversation-id> [--limit 20]",
|
|
11304
|
+
"ghosty conversations archive|rm <agent-id> --kind test|channel [--before 7d] [--dry-run] [--yes]",
|
|
11305
|
+
"ghosty conversations ls --user <email> [--since 30d] [--surface android] # staff",
|
|
11306
|
+
"ghosty conversations show <agent-id> <conversation-id> --why # staff: who sees it and why"
|
|
11307
|
+
],
|
|
11308
|
+
flags: [
|
|
11309
|
+
["--media FILE", "chat: attach a local file (repeatable)"],
|
|
11310
|
+
["--file ID", "chat: attach from your files (ghosty me files ls --q \u2026), comma-separated"],
|
|
11311
|
+
["--kind K", "archive|rm: test (test chats) or channel (customers: archive only, their memory keeps serving them)"],
|
|
11312
|
+
["--before T", "archive|rm: only conversations with no message since T (7d, 12h or ISO)"],
|
|
11313
|
+
["--user EMAIL", "ls/show (Ghosty staff): that person's conversations in any agent; logged"],
|
|
11314
|
+
["--why", "show (Ghosty staff): kind, whose it is, which turn set it, and whether the owner / its user see it in their list or by link"]
|
|
11315
|
+
],
|
|
11316
|
+
examples: ['ghosty chat <id> "hola"', "ghosty chat <id>", 'ghosty chat <id> "p\xE1salo a PDF" --media foto.jpg', "ghosty conversations rm <id> --kind test --before 7d --dry-run", "ghosty conversations archive <id> --kind channel --before 30d", "ghosty conversations ls --user david@empresa.com --since 7d"],
|
|
11149
11317
|
learn: `${REF}#conversaciones`
|
|
11150
11318
|
},
|
|
11151
11319
|
schedule: {
|
|
@@ -11157,8 +11325,8 @@ var HELPS = {
|
|
|
11157
11325
|
},
|
|
11158
11326
|
me: {
|
|
11159
11327
|
summary: "Your account: files and usage.",
|
|
11160
|
-
usage: ["ghosty me files ls
|
|
11161
|
-
examples: ["ghosty me files ls --kind document"],
|
|
11328
|
+
usage: ["ghosty me files ls [--q TEXT] [--kind image|video|audio|document|other] [--cursor C]", "ghosty me files get <file-id> [--out FILE]", "ghosty me files upload <file> | rm <file-id>", "ghosty usage"],
|
|
11329
|
+
examples: ["ghosty me files ls --kind document", "ghosty me files ls --q contrato", "ghosty me files get <id> --out contrato.pdf", 'ghosty chat <agent> "res\xFAmelo" --file <id>'],
|
|
11162
11330
|
learn: `${REF}#tu-cuenta`
|
|
11163
11331
|
}
|
|
11164
11332
|
};
|
|
@@ -11193,7 +11361,7 @@ HELPS.channels = {
|
|
|
11193
11361
|
"ghosty channels wa status|pause <agent-id>",
|
|
11194
11362
|
"ghosty channels wa groups ls <agent-id> [--cached]",
|
|
11195
11363
|
"ghosty channels wa groups enable|disable <agent-id> <group> [--role equipo|cliente] [--dry-run] [--yes]",
|
|
11196
|
-
|
|
11364
|
+
'ghosty channels wa groups set <agent-id> <group> [--role equipo|cliente] [--name "New name"] [--dry-run]',
|
|
11197
11365
|
'ghosty channels wa groups create <agent-id> "Name" [--role equipo|cliente]'
|
|
11198
11366
|
],
|
|
11199
11367
|
flags: [
|
|
@@ -11201,6 +11369,7 @@ HELPS.channels = {
|
|
|
11201
11369
|
["--yes", "enable / groups enable: skip the confirmation (required without a terminal)"],
|
|
11202
11370
|
["--role R", "groups: equipo (the team: writes to databases, admin tools) or cliente (like any customer)"],
|
|
11203
11371
|
["--cached", "groups ls: don't ask WhatsApp for the live list (faster; may miss new groups)"],
|
|
11372
|
+
["--name N", "groups set: the name gs shows (e.g. a group saved with its jid as name); WhatsApp's subject doesn't change"],
|
|
11204
11373
|
["--integration ID", "link/unlink/history: the WhatsApp Business number's Integration id in Formmy"],
|
|
11205
11374
|
["--email E", "integrations (staff): list the numbers of another Formmy account"],
|
|
11206
11375
|
["--days N", "history: conversations active in the last N days (1-30, default 7)"],
|
|
@@ -11400,6 +11569,27 @@ HELPS.demos = {
|
|
|
11400
11569
|
notes: ["try counts against the demo's caps (messages per visitor, per IP per day) and shows up in its numbers and CRM.", "history is Ghosty staff only: visitors' chats are not shown to the owner."],
|
|
11401
11570
|
learn: `${REF}#demo-publico-agents-demo`
|
|
11402
11571
|
};
|
|
11572
|
+
HELPS.push = {
|
|
11573
|
+
summary: "Ghosty staff: which pushes were sent to someone (turn end, permission, escalate, document\u2026), to which device and what Apple/Google answered. Logged since 2026-10-01, kept 30 days.",
|
|
11574
|
+
usage: ["ghosty push log --user <email> [--since 2d] [--limit 100]"],
|
|
11575
|
+
examples: ["ghosty push log --user brenda@siiqtec.com --since 1d"],
|
|
11576
|
+
notes: ["thread shows the conversation's kind: a \xABchannel\xBB there means a customer's push reached them."],
|
|
11577
|
+
learn: `${REF}#soporte`
|
|
11578
|
+
};
|
|
11579
|
+
HELPS.novedades = {
|
|
11580
|
+
summary: "Ghosty staff: the Android app's announcements (what's new). Each one needs its own illustration; same brakes as the admin: test on your phone first, push 9\u201321 h CDMX, 72 h between pushes to everyone.",
|
|
11581
|
+
usage: [
|
|
11582
|
+
"ghosty novedades ls",
|
|
11583
|
+
'ghosty novedades create --title T --body B --image hero.png [--link ruta] [--button "Abrir"] [--to a@x.com,b@y.com] [--publish]',
|
|
11584
|
+
"ghosty novedades set <id> [--title] [--body] [--image FILE] [--link] [--button] [--to] [--publish|--draft]",
|
|
11585
|
+
"ghosty novedades test <id>",
|
|
11586
|
+
'ghosty novedades send <id> [--at "2026-10-02T12:00"]',
|
|
11587
|
+
"ghosty novedades rm <id>"
|
|
11588
|
+
],
|
|
11589
|
+
examples: ['ghosty novedades create --title "PDF y audio" --body "Ahora\u2026" --image hero.png --link chats --publish', "ghosty novedades test <id>", 'ghosty novedades send <id> --at "2026-10-02T12:00"'],
|
|
11590
|
+
notes: ["--image is published to img.ghosty.studio (WebP 1200 px). --to empty = everyone. send without --at pushes now (asks); --at '' removes the schedule."],
|
|
11591
|
+
learn: `${REF}#soporte`
|
|
11592
|
+
};
|
|
11403
11593
|
HELPS.spaces = {
|
|
11404
11594
|
summary: "Ghosty staff operations on a workspace: move \xABpaid until\xBB and rename its slug. Each one is logged in the workspace's audit.",
|
|
11405
11595
|
usage: ["ghosty spaces renew <workspace> --until YYYY-MM-DD [--note TEXT] [--dry-run] [--yes]", "ghosty spaces rename <old> <new> [--dry-run] [--yes]"],
|
|
@@ -11436,7 +11626,7 @@ function helpText(cmd) {
|
|
|
11436
11626
|
}
|
|
11437
11627
|
|
|
11438
11628
|
// src/update.ts
|
|
11439
|
-
import { mkdir as mkdir4, readFile as readFile9, writeFile as
|
|
11629
|
+
import { mkdir as mkdir4, readFile as readFile9, writeFile as writeFile6 } from "node:fs/promises";
|
|
11440
11630
|
import { homedir as homedir3 } from "node:os";
|
|
11441
11631
|
import { dirname as dirname3, join as join6 } from "node:path";
|
|
11442
11632
|
var FILE = join6(process.env.XDG_CONFIG_HOME || join6(homedir3(), ".config"), "ghosty", "update.json");
|
|
@@ -11461,7 +11651,7 @@ async function latestVersion() {
|
|
|
11461
11651
|
const latest = (await r.json()).version;
|
|
11462
11652
|
if (latest) cache2 = { latest, checkedAt: Date.now() };
|
|
11463
11653
|
await mkdir4(dirname3(FILE), { recursive: true });
|
|
11464
|
-
await
|
|
11654
|
+
await writeFile6(FILE, JSON.stringify(cache2));
|
|
11465
11655
|
} catch {
|
|
11466
11656
|
}
|
|
11467
11657
|
}
|
|
@@ -11515,6 +11705,7 @@ function agentArgIndex(pos) {
|
|
|
11515
11705
|
case "db":
|
|
11516
11706
|
return sub === "photos" ? 3 : 2;
|
|
11517
11707
|
case "turns":
|
|
11708
|
+
return pos[1] === "stats" ? null : 2;
|
|
11518
11709
|
case "board":
|
|
11519
11710
|
case "conversations":
|
|
11520
11711
|
case "schedule":
|
|
@@ -11535,7 +11726,7 @@ function agentArgIndex(pos) {
|
|
|
11535
11726
|
}
|
|
11536
11727
|
|
|
11537
11728
|
// src/completion.ts
|
|
11538
|
-
import { mkdir as mkdir5, readFile as readFile10, writeFile as
|
|
11729
|
+
import { mkdir as mkdir5, readFile as readFile10, writeFile as writeFile7 } from "node:fs/promises";
|
|
11539
11730
|
import { homedir as homedir4 } from "node:os";
|
|
11540
11731
|
import { join as join7 } from "node:path";
|
|
11541
11732
|
var GLOBAL_FLAGS = ["--json", "--token", "--profile", "--yes", "--help", "--version", "--lang", "--dry-run"];
|
|
@@ -11587,7 +11778,7 @@ async function names() {
|
|
|
11587
11778
|
const workspaces = [...new Set(list.map((a) => a.espacio?.tipo === "workspace" ? a.espacio.nombre : void 0).filter((x) => !!x))];
|
|
11588
11779
|
await mkdir5(join7(cacheFile(), ".."), { recursive: true }).catch(() => {
|
|
11589
11780
|
});
|
|
11590
|
-
await
|
|
11781
|
+
await writeFile7(cacheFile(), JSON.stringify({ at: Date.now(), agents, workspaces })).catch(() => {
|
|
11591
11782
|
});
|
|
11592
11783
|
return { agents, workspaces };
|
|
11593
11784
|
}
|
|
@@ -11665,7 +11856,7 @@ function normalizeArgs(pos, v) {
|
|
|
11665
11856
|
|
|
11666
11857
|
// src/commands/spec.ts
|
|
11667
11858
|
var import_yaml = __toESM(require_dist(), 1);
|
|
11668
|
-
import { mkdir as mkdir6, readdir as readdir4, readFile as readFile11, stat, writeFile as
|
|
11859
|
+
import { mkdir as mkdir6, readdir as readdir4, readFile as readFile11, stat, writeFile as writeFile8 } from "node:fs/promises";
|
|
11669
11860
|
import { join as join8, resolve as resolve2 } from "node:path";
|
|
11670
11861
|
var enc9 = encodeURIComponent;
|
|
11671
11862
|
async function agentsExport(id, v) {
|
|
@@ -11677,7 +11868,7 @@ async function agentsExport(id, v) {
|
|
|
11677
11868
|
}
|
|
11678
11869
|
const dir = resolve2(v.out);
|
|
11679
11870
|
await mkdir6(dir, { recursive: true });
|
|
11680
|
-
await
|
|
11871
|
+
await writeFile8(join8(dir, "agent.yaml"), yaml);
|
|
11681
11872
|
const own = (0, import_yaml.parse)(yaml)["x-ghosty"]?.skills?.own ?? [];
|
|
11682
11873
|
for (const slug of own) {
|
|
11683
11874
|
const sk = await api(`/api/v2/agents/${enc9(id)}/skills/${enc9(slug)}`);
|
|
@@ -11927,9 +12118,9 @@ ${t("Rename?", "\xBFRenombrar?")}`, { yes: v.yes, expect: plan.from, retry: `gho
|
|
|
11927
12118
|
async function workspaceUsage(slug, v) {
|
|
11928
12119
|
slug = needSpace(slug, "ghosty usage --workspace <slug> --by agent,day --days 14");
|
|
11929
12120
|
const r = await api(wsPath(slug, `/usage?days=${enc10(v.days ?? "14")}`));
|
|
11930
|
-
const
|
|
12121
|
+
const cdmx2 = (iso) => new Date(Date.parse(iso) - 6 * 36e5).toISOString().slice(5, 16).replace("T", " ");
|
|
11931
12122
|
if (v["quota-hits"]) {
|
|
11932
|
-
const rows2 = r.quotaHits.map((q) => ({ at:
|
|
12123
|
+
const rows2 = r.quotaHits.map((q) => ({ at: cdmx2(q.at), agent: q.agent, channel: q.channel, from: q.phone ?? q.conversationId.slice(0, 24), message: (q.preview ?? "").slice(0, 60) }));
|
|
11933
12124
|
const total2 = r.agents.reduce((n, a) => n + a.noQuota, 0);
|
|
11934
12125
|
return out(r.quotaHits, () => rows2.length ? `${table(rows2, ["at", "agent", "channel", "from", "message"])}
|
|
11935
12126
|
${dim(t(`${total2} message(s) unanswered for lack of balance in ${r.days} day(s) (CDMX time)`, `${total2} mensaje(s) sin atender por saldo en ${r.days} d\xEDa(s) (hora CDMX)`))}` : dim(t(`No message ran out of balance in ${r.days} day(s).`, `Ning\xFAn mensaje se qued\xF3 sin saldo en ${r.days} d\xEDa(s).`)));
|
|
@@ -11960,7 +12151,7 @@ ${dim(t(`${fmtTokens(total)} billable tokens \xB7 failed = error or stopped \xB7
|
|
|
11960
12151
|
|
|
11961
12152
|
// src/commands/demos.ts
|
|
11962
12153
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
11963
|
-
import { mkdir as mkdir7, readFile as readFile12, writeFile as
|
|
12154
|
+
import { mkdir as mkdir7, readFile as readFile12, writeFile as writeFile9 } from "node:fs/promises";
|
|
11964
12155
|
import { homedir as homedir5 } from "node:os";
|
|
11965
12156
|
import { dirname as dirname4, join as join9 } from "node:path";
|
|
11966
12157
|
var enc11 = encodeURIComponent;
|
|
@@ -12003,7 +12194,7 @@ async function visitorCookie(base, slug, fresh) {
|
|
|
12003
12194
|
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);
|
|
12004
12195
|
jar[slug] = cookie;
|
|
12005
12196
|
await mkdir7(dirname4(jarPath()), { recursive: true });
|
|
12006
|
-
await
|
|
12197
|
+
await writeFile9(jarPath(), JSON.stringify(jar, null, 2), { mode: 384 });
|
|
12007
12198
|
return cookie;
|
|
12008
12199
|
}
|
|
12009
12200
|
async function visitorTurn(base, slug, cookie, text) {
|
|
@@ -12074,6 +12265,146 @@ async function demosCmd(sub, a1, rest, v) {
|
|
|
12074
12265
|
throw usage(t(`Unknown demos subcommand: ${sub ?? "(none)"}.`, `Subcomando de demos desconocido: ${sub ?? "(ninguno)"}.`), 'ghosty demos history <agent> \xB7 ghosty demos try <slug> "message"');
|
|
12075
12266
|
}
|
|
12076
12267
|
|
|
12268
|
+
// src/commands/support.ts
|
|
12269
|
+
import { readFile as readFile13 } from "node:fs/promises";
|
|
12270
|
+
var enc12 = encodeURIComponent;
|
|
12271
|
+
var cdmx = (iso) => new Date(Date.parse(iso) - 6 * 36e5).toISOString().slice(5, 16).replace("T", " ");
|
|
12272
|
+
async function supportThreads(email, v) {
|
|
12273
|
+
const q = new URLSearchParams({ user: email, since: v.since ?? "30d", ...v.surface ? { surface: v.surface } : {} });
|
|
12274
|
+
const r = await api(`/api/v2/support/threads?${q}`);
|
|
12275
|
+
const rows = r.threads.map((x) => ({
|
|
12276
|
+
last: cdmx(x.lastTurn),
|
|
12277
|
+
agent: x.deletedAgent ? dim(t("(deleted)", "(borrado)")) : x.agent ?? x.agentId.slice(0, 8),
|
|
12278
|
+
conversation: x.sessionId,
|
|
12279
|
+
title: (x.title ?? "").slice(0, 40),
|
|
12280
|
+
kind: x.kind ?? "?",
|
|
12281
|
+
listed: x.listed ? t("yes", "s\xED") : "",
|
|
12282
|
+
turns: x.failed ? `${x.turns} (${red(String(x.failed))})` : x.turns,
|
|
12283
|
+
from: x.surfaces.join(",")
|
|
12284
|
+
}));
|
|
12285
|
+
out(r, () => `${bold(r.user)}
|
|
12286
|
+
${table(rows, ["last", "agent", "conversation", "title", "kind", "listed", "turns", "from"])}
|
|
12287
|
+
${dim(t("Read one: ghosty conversations show <agent> <conversation> --why \xB7 times in CDMX \xB7 listed = in their app list", "Leer uno: ghosty conversations show <agente> <conversaci\xF3n> --why \xB7 horas CDMX \xB7 listed = sale en su lista de la app"))}`);
|
|
12288
|
+
}
|
|
12289
|
+
async function supportThread(agent, sid, v) {
|
|
12290
|
+
const r = await api(`/api/v2/support/threads/${enc12(agent)}/${enc12(sid)}?tail=${enc12(v.limit ?? "40")}`);
|
|
12291
|
+
out(r, () => {
|
|
12292
|
+
const w = r.why;
|
|
12293
|
+
const who2 = (x, label) => x ? ` ${label} ${x.email}: ${x.listed ? lavender(t("in their list/search/push", "en su lista/b\xFAsqueda/push")) : dim(t("not listed", "no sale en su lista"))} \xB7 ${t("by link", "por liga")}: ${x.open || t("no", "no")}` : "";
|
|
12294
|
+
const why = [
|
|
12295
|
+
`${bold(r.agent.name ?? r.agent.id)}${r.agent.deleted ? red(t(" (deleted)", " (borrado)")) : ""} \xB7 ${r.title ?? ""}`,
|
|
12296
|
+
` kind ${bold(w.kind ?? "?")} \xB7 ${t("thread of", "hilo de")} ${w.user ?? "\u2014"}${w.archived ? dim(` \xB7 ${t("archived", "archivado")} ${cdmx(w.archived)}`) : ""}`,
|
|
12297
|
+
w.lastTurn ? dim(` ${t("last turn", "\xFAltimo turno")}: canal ${w.lastTurn.canal ?? "?"} \xB7 origen ${w.lastTurn.origen ?? "?"} \xB7 ${w.lastTurn.surface ?? "?"} \xB7 ${w.lastTurn.userId ?? "?"}`) : "",
|
|
12298
|
+
who2(w.owner, t("agent owner", "due\xF1o del agente")),
|
|
12299
|
+
who2(w.threadUser, t("thread user", "usuario del hilo")),
|
|
12300
|
+
dim(` ${serverText(w.rule)}`)
|
|
12301
|
+
].filter(Boolean);
|
|
12302
|
+
if (v.why) return why.join("\n");
|
|
12303
|
+
const msgs = r.messages.map((m) => `${dim(cdmx(m.at))} ${bold(m.role.padEnd(9))} ${m.text.replace(/\s+/g, " ").slice(0, 400)}`);
|
|
12304
|
+
return [...why, "", ...msgs].join("\n");
|
|
12305
|
+
});
|
|
12306
|
+
}
|
|
12307
|
+
async function cleanupConversations(action, agent, v) {
|
|
12308
|
+
const ex = `ghosty conversations ${action} <agent> --kind ${action === "rm" ? "test" : "test|channel"} [--before 7d] [--dry-run]`;
|
|
12309
|
+
if (!agent) throw usage(t("Missing agent.", "Falta el agente."), ex);
|
|
12310
|
+
if (v.kind !== "test" && v.kind !== "channel") throw usage(t("--kind takes test or channel.", "--kind acepta test o channel."), ex);
|
|
12311
|
+
const path = `/api/v2/agents/${enc12(agent)}/conversations/cleanup`;
|
|
12312
|
+
const body = { kind: v.kind, action, ...v.before ? { before: v.before } : {} };
|
|
12313
|
+
const plan = await api(path, { method: "POST", body: { ...body, dryRun: true } });
|
|
12314
|
+
const verb = action === "rm" ? t("delete (rows + agent memory)", "borrar (filas + memoria del agente)") : t("archive (hidden; memory stays)", "archivar (se ocultan; la memoria se queda)");
|
|
12315
|
+
const line2 = t(`${plan.threads} ${v.kind} conversation(s), ${plan.messages} message(s) to ${verb}`, `${plan.threads} conversaci\xF3n(es) ${v.kind}, ${plan.messages} mensaje(s) para ${verb}`);
|
|
12316
|
+
if (v["dry-run"] || !plan.threads) return out(plan, () => `${line2}${plan.sample.length ? `
|
|
12317
|
+
${dim(plan.sample.join("\n"))}` : ""}
|
|
12318
|
+
${dim(t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada."))}`);
|
|
12319
|
+
requireYesIfHeadless(v.yes, `ghosty conversations ${action} ${agent} --kind ${v.kind}${v.before ? ` --before ${v.before}` : ""}`);
|
|
12320
|
+
await confirm(`${line2}
|
|
12321
|
+
${t("Go ahead?", "\xBFSigo?")}`, { yes: v.yes, retry: `ghosty conversations ${action} ${agent} --kind ${v.kind}` });
|
|
12322
|
+
const r = await api(path, { method: "POST", body });
|
|
12323
|
+
out(r, () => `${lavender("\u2713")} ${t(`${r.threads} conversation(s) ${action === "rm" ? "deleted" : "archived"}.`, `${r.threads} conversaci\xF3n(es) ${action === "rm" ? "borradas" : "archivadas"}.`)}`);
|
|
12324
|
+
}
|
|
12325
|
+
async function turnStats(v) {
|
|
12326
|
+
const r = await api(`/api/v2/support/turn-stats?since=${enc12(v.since ?? "14d")}${v.all ? "&all=1" : ""}`);
|
|
12327
|
+
out(
|
|
12328
|
+
r,
|
|
12329
|
+
() => [
|
|
12330
|
+
`${bold(`${r.failRate}%`)} ${t("of turns failed", "de los turnos fallaron")} ${dim(`(${r.failed}/${r.total}, ${v.since ?? "14d"}${r.filtered ? t(" \xB7 without staff, test channel or deleted agents (--all to include)", " \xB7 sin staff, canal prueba ni agentes borrados (--all los incluye)") : ""})`)}`,
|
|
12331
|
+
dim(Object.entries(r.byState).map(([k, n]) => `${k} ${n}`).join(" \xB7 ")),
|
|
12332
|
+
"",
|
|
12333
|
+
table(r.byEngine.map((e) => ({ engine: e.engine, turns: e.turns, failed: e.failed, rate: `${e.failRate}%` })), ["engine", "turns", "failed", "rate"]),
|
|
12334
|
+
...r.topErrors.length ? ["", table(r.topErrors.map((e) => ({ n: e.n, error: e.error })), ["n", "error"])] : []
|
|
12335
|
+
].join("\n")
|
|
12336
|
+
);
|
|
12337
|
+
}
|
|
12338
|
+
async function pushLog(email, v) {
|
|
12339
|
+
if (!email) throw usage(t("Missing --user.", "Falta --user."), "ghosty push log --user ana@empresa.com [--since 2d]");
|
|
12340
|
+
const r = await api(`/api/v2/support/push-log?user=${enc12(email)}&since=${enc12(v.since ?? "2d")}${v.limit ? `&limit=${enc12(v.limit)}` : ""}`);
|
|
12341
|
+
const rows = r.pushes.map((p) => ({ at: cdmx(p.at), kind: p.kind, to: p.platform, status: p.status === "sent" ? p.status : red(p.status), thread: p.threadKind ? `${p.threadId?.slice(0, 18)} (${p.threadKind})` : (p.threadId ?? "").slice(0, 24), title: (p.title ?? "").slice(0, 40), detail: p.detail ?? "" }));
|
|
12342
|
+
out(r, () => rows.length ? `${bold(r.user)}
|
|
12343
|
+
${table(rows, ["at", "kind", "to", "status", "thread", "title", "detail"])}` : dim(t(`No pushes for ${r.user} in that range (logged since 2026-10-01).`, `Sin avisos para ${r.user} en ese rango (se registran desde el 1-oct).`)));
|
|
12344
|
+
}
|
|
12345
|
+
async function novedadesCmd(sub, id, v) {
|
|
12346
|
+
const base = "/api/v2/support/announcements";
|
|
12347
|
+
const ex = 'ghosty novedades ls \xB7 create --title T --body B --image hero.png [--link ruta] [--button "Abrir"] [--to a@x.com,b@y.com] [--publish] \xB7 set <id> \u2026 \xB7 test <id> \xB7 send <id> [--at "2026-10-02T12:00"] \xB7 rm <id>';
|
|
12348
|
+
const fields = async () => ({
|
|
12349
|
+
...v.title !== void 0 ? { title: v.title } : {},
|
|
12350
|
+
...v.body !== void 0 ? { body: v.body } : {},
|
|
12351
|
+
...v.image ? { image: (await readFile13(v.image)).toString("base64") } : {},
|
|
12352
|
+
...v.link !== void 0 ? { enlace: v.link || null } : {},
|
|
12353
|
+
...v.button !== void 0 ? { boton: v.button || null } : {},
|
|
12354
|
+
...v.to !== void 0 ? { destinatarios: v.to || null } : {},
|
|
12355
|
+
...v.publish ? { published: true } : v.draft ? { published: false } : {}
|
|
12356
|
+
});
|
|
12357
|
+
const line2 = (a) => `${bold(a.title)} ${dim(a.id)}
|
|
12358
|
+
${a.published ? lavender(t("published", "publicada")) : dim(t("draft", "borrador"))} \xB7 ${a.pushedAt ? t(`pushed ${cdmx(a.pushedAt)} (${a.pushCount})`, `enviada ${cdmx(a.pushedAt)} (${a.pushCount})`) : a.schedule ? t(`push scheduled ${cdmx(a.schedule)} CDMX`, `push programado ${cdmx(a.schedule)} CDMX`) : t("not pushed", "sin enviar")} \xB7 ${a.destinatarios ? t(`to ${a.destinatarios.split(",").length}`, `para ${a.destinatarios.split(",").length}`) : t("everyone", "todos")}`;
|
|
12359
|
+
switch (sub ?? "ls") {
|
|
12360
|
+
case "ls":
|
|
12361
|
+
case "list": {
|
|
12362
|
+
const r = await api(base);
|
|
12363
|
+
return out(r, () => r.announcements.map(line2).join("\n") || dim(t("No announcements.", "Sin novedades.")));
|
|
12364
|
+
}
|
|
12365
|
+
case "create":
|
|
12366
|
+
case "new": {
|
|
12367
|
+
if (!v.title || !v.body) throw usage(t("Missing --title and --body.", "Faltan --title y --body."), ex);
|
|
12368
|
+
const r = await api(base, { method: "POST", body: await fields() });
|
|
12369
|
+
return out(r, () => `${lavender("\u2713")} ${line2(r)}
|
|
12370
|
+
${dim(t(`Try it on your phone: ghosty novedades test ${r.id}`, `Pru\xE9bala en tu tel\xE9fono: ghosty novedades test ${r.id}`))}`);
|
|
12371
|
+
}
|
|
12372
|
+
case "set": {
|
|
12373
|
+
if (!id) throw usage(t("Missing id.", "Falta el id."), ex);
|
|
12374
|
+
const b = await fields();
|
|
12375
|
+
if (!Object.keys(b).length) throw usage(t("Nothing to change.", "Nada que cambiar."), ex);
|
|
12376
|
+
const r = await api(`${base}/${enc12(id)}`, { method: "PATCH", body: b });
|
|
12377
|
+
return out(r, () => `${lavender("\u2713")} ${line2(r)}`);
|
|
12378
|
+
}
|
|
12379
|
+
case "test": {
|
|
12380
|
+
if (!id) throw usage(t("Missing id.", "Falta el id."), ex);
|
|
12381
|
+
const r = await api(`${base}/${enc12(id)}`, { method: "POST", body: { push: "test" } });
|
|
12382
|
+
return out(r, () => `${lavender("\u2713")} ${t(`Sent to your phone (${r.enviados} device(s)).`, `Enviada a tu tel\xE9fono (${r.enviados} dispositivo(s)).`)}`);
|
|
12383
|
+
}
|
|
12384
|
+
case "send":
|
|
12385
|
+
case "schedule": {
|
|
12386
|
+
if (!id) throw usage(t("Missing id.", "Falta el id."), ex);
|
|
12387
|
+
if (v.at !== void 0) {
|
|
12388
|
+
const r2 = await api(`${base}/${enc12(id)}`, { method: "PATCH", body: { schedule: v.at || null } });
|
|
12389
|
+
return out(r2, () => `${lavender("\u2713")} ${line2(r2)}`);
|
|
12390
|
+
}
|
|
12391
|
+
requireYesIfHeadless(v.yes, `ghosty novedades send ${id}`);
|
|
12392
|
+
await confirm(t("Push this announcement to every recipient now?", "\xBFMandar ya el push de esta novedad a todos sus destinatarios?"), { yes: v.yes, retry: `ghosty novedades send ${id}` });
|
|
12393
|
+
const r = await api(`${base}/${enc12(id)}`, { method: "POST", body: { push: "all" } });
|
|
12394
|
+
return out(r, () => `${lavender("\u2713")} ${t(`Pushed to ${r.enviados} device(s).`, `Enviada a ${r.enviados} dispositivo(s).`)}`);
|
|
12395
|
+
}
|
|
12396
|
+
case "rm":
|
|
12397
|
+
case "delete": {
|
|
12398
|
+
if (!id) throw usage(t("Missing id.", "Falta el id."), ex);
|
|
12399
|
+
requireYesIfHeadless(v.yes, `ghosty novedades rm ${id}`);
|
|
12400
|
+
await confirm(t(`Delete announcement ${id}?`, `\xBFBorrar la novedad ${id}?`), { yes: v.yes, retry: `ghosty novedades rm ${id}` });
|
|
12401
|
+
return out(await api(`${base}/${enc12(id)}`, { method: "DELETE" }), () => `${lavender("\u2713")} ${t("Deleted.", "Borrada.")}`);
|
|
12402
|
+
}
|
|
12403
|
+
default:
|
|
12404
|
+
throw usage(t(`Unknown novedades subcommand: ${sub}.`, `Subcomando de novedades desconocido: ${sub}.`), ex);
|
|
12405
|
+
}
|
|
12406
|
+
}
|
|
12407
|
+
|
|
12077
12408
|
// src/index.ts
|
|
12078
12409
|
var HELP_EN = `${bold("Usage:")} ghosty <command> [options]
|
|
12079
12410
|
|
|
@@ -12095,7 +12426,8 @@ ${bold("Workspaces")} ${dim("(changing them: Ghosty staff)")}
|
|
|
12095
12426
|
spaces rename <old> <new> Change its slug only [--dry-run]
|
|
12096
12427
|
|
|
12097
12428
|
${bold("Agents")}
|
|
12098
|
-
agents ls List your agents
|
|
12429
|
+
agents ls List your agents [--engine E] [--ids: only ids, for xargs]
|
|
12430
|
+
[--sponsor EMAIL | --owner EMAIL] any account's agents (Ghosty staff)
|
|
12099
12431
|
agents create Create an agent --name [--engine] [--model] [--prompt | --prompt-file FILE] [--env K=V,\u2026]
|
|
12100
12432
|
[--workspace SLUG] [--channels teams=off,\u2026] born in a workspace, active in its Teams
|
|
12101
12433
|
[--like AGENT [--copy-dbs] [--board same|main|new|NAME] [--no-notify] [--dry-run]] with another agent's setup (not its prompt, env or channels)
|
|
@@ -12109,16 +12441,18 @@ ${bold("Agents")}
|
|
|
12109
12441
|
[--toolsets crm,sales | +crm,-sales] gs tool groups it gets
|
|
12110
12442
|
[--prompt-base AGENT|none --prompt-overlay FILE [--dry-run]] prompt = another agent's + edits, kept in sync
|
|
12111
12443
|
[--tool wa:tool=off,\u2026] [--bag NAME|none] [--avatar URL|FILE] a tool per channel \xB7 its token bag (staff) \xB7 its face in Teams
|
|
12444
|
+
[--prompt-mode append|replace] replace = its prompt WITHOUT Ghosty's house persona
|
|
12112
12445
|
agents doctor <id> Check the config: overlapping skills, tools the prompt names, broken grants, failing turns
|
|
12113
12446
|
agents docs ls|grant|revoke <id> [doc] Documents it reads and may send [--write] [--no-deliver]
|
|
12114
12447
|
agents docs grant <id> A B\u2026 Grant several documents at once [--like AGENT] copies another agent's
|
|
12115
12448
|
agents docs add <id> Upload a document and grant it --url URL | --file PATH [--description] [--grant read|write|no]
|
|
12449
|
+
agents docs dedupe Your account: merge documents with the same content (keeps every agent's access) [--dry-run] \xB7 dedupe <keep> <drop>
|
|
12116
12450
|
agents docs replace <id> <doc> New version (keeps name and access) --url URL | --file PATH [--note]
|
|
12117
12451
|
agents docs history|restore <id> <doc> [n] Its versions; restore n as a new version
|
|
12118
12452
|
agents share|unshare <id> <email> Share it without handing it over [--role editor|admin] \xB7 agents shares <id> lists them
|
|
12119
12453
|
agents rm <id> Delete an agent (owner only; asks you to type its name, or --yes) [--from-workspace: turn it off in Teams first]
|
|
12120
12454
|
agents restart <id> Restart an ACP agent (needsRestart) so it loads new skills
|
|
12121
|
-
agents box <id> The machine serving it: is it on the new image? [--check PATH]\u2026 [--contains PATH=TEXT]\u2026 [--recycle]
|
|
12455
|
+
agents box <id> The machine serving it: is it on the new image? [--check PATH]\u2026 [--contains PATH=TEXT]\u2026 [--recycle [--reenv] [--force]]
|
|
12122
12456
|
agents demo enable|disable <id> Turn its public demo link on or off
|
|
12123
12457
|
agents demo <id> Public demo link /p/<slug>: anyone tries it without an account, "Keep it" clones it to theirs
|
|
12124
12458
|
[--slug NAME] [--expires "YYYY-MM-DDTHH:MM"|no] [--turns N] [--per-ip N] [--welcome TEXT] [--chips "a|b|c"] [--starter TEXT] [--sponsor on|off] [--rotate]
|
|
@@ -12139,26 +12473,35 @@ ${bold("Configure an agent")}
|
|
|
12139
12473
|
channels wa import <id> --from easybits:ID | --from-file F | --from-dir DIR --own-number yes|no Move a personal WhatsApp session here, no QR [--dry-run] [--replace] [--no-connect]
|
|
12140
12474
|
channels wa pair <id> --phone N Pair a personal WhatsApp by code (no QR)
|
|
12141
12475
|
channels wa status|pause <id> Personal WhatsApp: how it is / pause it (keeps the session)
|
|
12142
|
-
channels wa groups ls|enable|disable|set|create <id> [group] Personal WhatsApp groups it answers, and as whom [--role equipo|cliente] [--dry-run]
|
|
12476
|
+
channels wa groups ls|enable|disable|set|create <id> [group] Personal WhatsApp groups it answers, and as whom [--role equipo|cliente] [--name "New name"] [--dry-run]
|
|
12143
12477
|
boards ls|create|assign|move-cards|rm A workspace's boards: create (--columns-from), where an agent sends cards, move cards
|
|
12144
12478
|
cards ls <id> Recent cards on its sales board [--q TEXT] [--paused] [--limit 20]
|
|
12145
12479
|
cards show <id> <card> A card's thread: who wrote, text, attachments and whether gs kept a copy (folio, phone or id)
|
|
12146
12480
|
cards archive <id> Archive cards in bulk (reversible) --integration X | --column X | --before 30d [--dry-run]
|
|
12147
12481
|
|
|
12482
|
+
${bold("Support")} ${dim("(Ghosty staff)")}
|
|
12483
|
+
conversations ls --user EMAIL Someone's conversations in any agent [--since 30d] [--surface android]
|
|
12484
|
+
conversations show <id> <conv> --why A conversation: who sees it in their list / by link, and why
|
|
12485
|
+
turns stats Real failure rate by engine and error [--since 14d] [--all]
|
|
12486
|
+
push log --user EMAIL Pushes sent to someone and what Apple/Google answered [--since 2d]
|
|
12487
|
+
novedades ls|create|set|test|send|rm Android app announcements [--title] [--body] [--image FILE] [--link] [--to] [--publish] [--at]
|
|
12488
|
+
|
|
12148
12489
|
${bold("Demos")}
|
|
12149
12490
|
demos try <slug> "msg" ["msg2"\u2026] Try a public demo as a visitor (same thread across runs) [--new] [--recycle-between --agent X]
|
|
12150
12491
|
demos history <id> What visitors did: turns, messages, files (Ghosty staff) [--since 2d] [--session web:\u2026] [--timeline] [--full]
|
|
12151
12492
|
|
|
12152
12493
|
${bold("Talk to an agent")}
|
|
12153
|
-
try <id> "message" One test turn, prints the full answer [--session] [--reset]
|
|
12494
|
+
try <id> "message" One test turn, prints the full answer [--session] [--reset] [--media FILE: attach, as from the app]
|
|
12154
12495
|
--waba --session PHONE \u2026through the real WhatsApp path, nothing sent; lists what it would send (tools too) [--media FILE [--as-formmy]] [--history FILE]
|
|
12155
12496
|
--replay FILE --out DIR Replay real conversations; report written locally [--max 15] [--concurrency 8] [--resume]
|
|
12156
|
-
chat <id> ["message"] Stream a conversation (no message = interactive) [--conversation ID]
|
|
12497
|
+
chat <id> ["message"] Stream a conversation (no message = interactive) [--conversation ID] [--media FILE] [--file ID: from your files]
|
|
12157
12498
|
conversations ls <id> List conversations
|
|
12158
12499
|
conversations show <id> <conversation> What was said [--limit 20] (0 = all)
|
|
12500
|
+
conversations archive|rm <id> --kind test|channel Clean its history (rm: tests only) [--before 7d] [--dry-run]
|
|
12159
12501
|
turns ls <id> Recent turns and why they failed [--errors] [--skipped] [--since 1h] [--limit N]
|
|
12160
12502
|
turns ls --workspace SLUG --quota-hits Messages that got no turn because the balance ran out [--days 14]
|
|
12161
12503
|
turns show <id> [turn] What came in, what it answered, and where the time went (default: last) [--full]
|
|
12504
|
+
turns cancel <id> <conv> Stop: the running turn AND that conversation's background jobs (video, subagents, pending wake-ups)
|
|
12162
12505
|
|
|
12163
12506
|
${bold("Schedule turns")} ${dim("(each run lands in a conversation and notifies your phone; an answer of exactly OK stays silent)")}
|
|
12164
12507
|
schedule ls <id> Pending turns and recent runs
|
|
@@ -12170,7 +12513,7 @@ ${bold("Partners")} ${dim("(give an agent to each of YOUR customers \u2014 see:
|
|
|
12170
12513
|
partner ls | enable | credentials | agent | tenants | audit | try
|
|
12171
12514
|
|
|
12172
12515
|
${bold("Your account")}
|
|
12173
|
-
me files ls|upload|rm
|
|
12516
|
+
me files ls|get|upload|rm Files in your account [--q TEXT] [--kind image|video|audio|document|other] [--cursor C] \xB7 get <id> [--out FILE]
|
|
12174
12517
|
credentials ls|set|rm [provider] Provider keys in your vault (hidden prompt, stdin or --from-env VAR; never as an argument)
|
|
12175
12518
|
docs [topic] Open the docs
|
|
12176
12519
|
|
|
@@ -12207,7 +12550,8 @@ ${bold("Espacios")} ${dim("(cambiarlos: staff de Ghosty)")}
|
|
|
12207
12550
|
spaces rename <viejo> <nuevo> Cambia s\xF3lo su slug [--dry-run]
|
|
12208
12551
|
|
|
12209
12552
|
${bold("Agentes")}
|
|
12210
|
-
agents ls Lista tus agentes
|
|
12553
|
+
agents ls Lista tus agentes [--engine E] [--ids: s\xF3lo ids, para xargs]
|
|
12554
|
+
[--sponsor CORREO | --owner CORREO] agentes de cualquier cuenta (staff de Ghosty)
|
|
12211
12555
|
agents create Crea un agente --name [--engine] [--model] [--prompt | --prompt-file ARCHIVO] [--env K=V,\u2026]
|
|
12212
12556
|
[--workspace SLUG] [--channels teams=off,\u2026] nace en un espacio, activo en su Teams
|
|
12213
12557
|
[--like AGENTE [--copy-dbs] [--board same|main|new|NOMBRE] [--no-notify] [--dry-run]] con la configuraci\xF3n de otro agente (no su prompt, env ni canales)
|
|
@@ -12221,16 +12565,18 @@ ${bold("Agentes")}
|
|
|
12221
12565
|
[--toolsets crm,sales | +crm,-sales] grupos de tools de gs que recibe
|
|
12222
12566
|
[--prompt-base AGENTE|none --prompt-overlay ARCHIVO [--dry-run]] prompt = el de otro agente + ediciones, sincronizado
|
|
12223
12567
|
[--tool wa:tool=off,\u2026] [--bag NOMBRE|none] [--avatar URL|ARCHIVO] una tool por canal \xB7 su bolsa de tokens (staff) \xB7 su cara en Teams
|
|
12568
|
+
[--prompt-mode append|replace] replace = su prompt SIN la persona de casa de Ghosty
|
|
12224
12569
|
agents doctor <id> Revisa la configuraci\xF3n: skills encimadas, tools que nombra el prompt, permisos rotos, turnos que fallan
|
|
12225
12570
|
agents docs ls|grant|revoke <id> [doc] Documentos que lee y puede enviar [--write] [--no-deliver]
|
|
12226
12571
|
agents docs grant <id> A B\u2026 Da varios documentos a la vez [--like AGENTE] copia los de otro agente
|
|
12227
12572
|
agents docs add <id> Sube un documento y d\xE1selo --url URL | --file RUTA [--description] [--grant read|write|no]
|
|
12573
|
+
agents docs dedupe Tu cuenta: une los documentos con el mismo contenido (conserva el acceso de cada agente) [--dry-run] \xB7 dedupe <queda> <se-va>
|
|
12228
12574
|
agents docs replace <id> <doc> Versi\xF3n nueva (conserva nombre y accesos) --url URL | --file RUTA [--note]
|
|
12229
12575
|
agents docs history|restore <id> <doc> [n] Sus versiones; restaura la n como versi\xF3n nueva
|
|
12230
12576
|
agents share|unshare <id> <correo> Comp\xE1rtelo sin cederlo [--role editor|admin] \xB7 agents shares <id> los lista
|
|
12231
12577
|
agents rm <id> Borra un agente (s\xF3lo el due\xF1o; te pide escribir su nombre, o --yes) [--from-workspace: antes lo desactiva en Teams]
|
|
12232
12578
|
agents restart <id> Reinicia un agente ACP (needsRestart) para que cargue skills nuevas
|
|
12233
|
-
agents box <id> La m\xE1quina que lo atiende: \xBFya trae la imagen nueva? [--check RUTA]\u2026 [--contains RUTA=TEXTO]\u2026 [--recycle]
|
|
12579
|
+
agents box <id> La m\xE1quina que lo atiende: \xBFya trae la imagen nueva? [--check RUTA]\u2026 [--contains RUTA=TEXTO]\u2026 [--recycle [--reenv] [--force]]
|
|
12234
12580
|
agents demo enable|disable <id> Prende o apaga su liga p\xFAblica de demo
|
|
12235
12581
|
agents demo <id> Liga p\xFAblica de demo /p/<slug>: cualquiera lo prueba sin cuenta y \xABQued\xE1rmelo\xBB lo clona a la suya
|
|
12236
12582
|
[--slug NOMBRE] [--expires "AAAA-MM-DDTHH:MM"|no] [--turns N] [--per-ip N] [--welcome TEXTO] [--chips "a|b|c"] [--starter TEXTO] [--sponsor on|off] [--rotate]
|
|
@@ -12251,26 +12597,35 @@ ${bold("Configurar un agente")}
|
|
|
12251
12597
|
channels wa import <id> --from easybits:ID | --from-file F | --from-dir DIR --own-number yes|no Mueve aqu\xED una sesi\xF3n de WhatsApp personal, sin QR [--dry-run] [--replace] [--no-connect]
|
|
12252
12598
|
channels wa pair <id> --phone N Vincula un WhatsApp personal con c\xF3digo (sin QR)
|
|
12253
12599
|
channels wa status|pause <id> WhatsApp personal: c\xF3mo est\xE1 / pausarlo (conserva la sesi\xF3n)
|
|
12254
|
-
channels wa groups ls|enable|disable|set|create <id> [grupo] Grupos de WhatsApp personal que contesta, y como qui\xE9n [--role equipo|cliente] [--dry-run]
|
|
12600
|
+
channels wa groups ls|enable|disable|set|create <id> [grupo] Grupos de WhatsApp personal que contesta, y como qui\xE9n [--role equipo|cliente] [--name "Nombre nuevo"] [--dry-run]
|
|
12255
12601
|
boards ls|create|assign|move-cards|rm Los tableros de un espacio: crear (--columns-from), a d\xF3nde manda tarjetas un agente, mover tarjetas
|
|
12256
12602
|
cards ls <id> Tarjetas recientes de su tablero de ventas [--q TEXTO] [--paused] [--limit 20]
|
|
12257
12603
|
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)
|
|
12258
12604
|
cards archive <id> Archiva tarjetas en bloque (reversible) --integration X | --column X | --before 30d [--dry-run]
|
|
12259
12605
|
|
|
12606
|
+
${bold("Soporte")} ${dim("(staff de Ghosty)")}
|
|
12607
|
+
conversations ls --user CORREO Las conversaciones de alguien en cualquier agente [--since 30d] [--surface android]
|
|
12608
|
+
conversations show <id> <conv> --why Una conversaci\xF3n: qui\xE9n la ve en su lista / por liga, y por qu\xE9
|
|
12609
|
+
turns stats Tasa real de fallos por motor y por error [--since 14d] [--all]
|
|
12610
|
+
push log --user CORREO Avisos que se le mandaron a alguien y qu\xE9 contest\xF3 Apple/Google [--since 2d]
|
|
12611
|
+
novedades ls|create|set|test|send|rm Novedades de la app Android [--title] [--body] [--image ARCHIVO] [--link] [--to] [--publish] [--at]
|
|
12612
|
+
|
|
12260
12613
|
${bold("Demos")}
|
|
12261
12614
|
demos try <slug> "msg" ["msg2"\u2026] Prueba un demo p\xFAblico como visitante (mismo hilo entre corridas) [--new] [--recycle-between --agent X]
|
|
12262
12615
|
demos history <id> Qu\xE9 hicieron los visitantes: turnos, mensajes, archivos (staff de Ghosty) [--since 2d] [--session web:\u2026] [--timeline] [--full]
|
|
12263
12616
|
|
|
12264
12617
|
${bold("Hablar con un agente")}
|
|
12265
|
-
try <id> "mensaje" Un turno de prueba; imprime la respuesta completa [--session] [--reset]
|
|
12618
|
+
try <id> "mensaje" Un turno de prueba; imprime la respuesta completa [--session] [--reset] [--media ARCHIVO: adjunto, como desde la app]
|
|
12266
12619
|
--waba --session TEL \u2026por el camino real de WhatsApp, sin enviar nada; lista lo que mandar\xEDa (tools tambi\xE9n) [--media ARCHIVO [--as-formmy]] [--history ARCHIVO]
|
|
12267
12620
|
--replay ARCHIVO --out DIR Repite conversaciones reales; el reporte se escribe local [--max 15] [--concurrency 8] [--resume]
|
|
12268
|
-
chat <id> ["mensaje"] Conversaci\xF3n en streaming (sin mensaje = interactivo) [--conversation ID]
|
|
12621
|
+
chat <id> ["mensaje"] Conversaci\xF3n en streaming (sin mensaje = interactivo) [--conversation ID] [--media ARCHIVO] [--file ID: de tus archivos]
|
|
12269
12622
|
conversations ls <id> Lista conversaciones
|
|
12270
12623
|
conversations show <id> <conversaci\xF3n> Qu\xE9 se dijo [--limit 20] (0 = todo)
|
|
12624
|
+
conversations archive|rm <id> --kind test|channel Limpia su historial (rm: s\xF3lo pruebas) [--before 7d] [--dry-run]
|
|
12271
12625
|
turns ls <id> Turnos recientes y por qu\xE9 fallaron [--errors] [--skipped] [--since 1h] [--limit N]
|
|
12272
12626
|
turns ls --workspace SLUG --quota-hits Mensajes que no abrieron turno porque se acab\xF3 el saldo [--days 14]
|
|
12273
12627
|
turns show <id> [turno] Qu\xE9 lleg\xF3, qu\xE9 contest\xF3 y en qu\xE9 se fue el tiempo (default: el \xFAltimo) [--full]
|
|
12628
|
+
turns cancel <id> <conv> Detener: el turno en curso Y los encargos en fondo de esa conversaci\xF3n (video, subagentes, despertadores)
|
|
12274
12629
|
|
|
12275
12630
|
${bold("Turnos programados")} ${dim("(cada corrida cae en una conversaci\xF3n y avisa a tu tel\xE9fono; una respuesta de exactamente OK no avisa)")}
|
|
12276
12631
|
schedule ls <id> Turnos pendientes y corridas recientes
|
|
@@ -12282,7 +12637,7 @@ ${bold("Partners")} ${dim("(dale un agente a cada uno de TUS clientes \u2014 ve
|
|
|
12282
12637
|
partner ls | enable | credentials | agent | tenants | audit | try
|
|
12283
12638
|
|
|
12284
12639
|
${bold("Tu cuenta")}
|
|
12285
|
-
me files ls|upload|rm
|
|
12640
|
+
me files ls|get|upload|rm Archivos de tu cuenta [--q TEXTO] [--kind image|video|audio|document|other] [--cursor C] \xB7 get <id> [--out ARCHIVO]
|
|
12286
12641
|
credentials ls|set|rm [proveedor] Llaves de proveedor en tu b\xF3veda (prompt oculto, stdin o --from-env VAR; nunca como argumento)
|
|
12287
12642
|
docs [tema] Abre la documentaci\xF3n
|
|
12288
12643
|
|
|
@@ -12299,7 +12654,7 @@ ${bold("Opciones globales")}
|
|
|
12299
12654
|
${bold("C\xF3digos de salida")} 0 bien \xB7 1 error de la API \xB7 2 uso incorrecto \xB7 3 sin sesi\xF3n
|
|
12300
12655
|
${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.")}
|
|
12301
12656
|
${dim("Documentaci\xF3n: https://www.ghosty.studio/docs/cli")}`;
|
|
12302
|
-
var COMMANDS = ["login", "logout", "whoami", "usage", "use", "apply", "plan", "engines", "bags", "spaces", "demos", "completion", "cards", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "partner", "me", "channels", "board", "boards", "credentials", "docs", "help"];
|
|
12657
|
+
var COMMANDS = ["login", "logout", "whoami", "usage", "use", "apply", "plan", "engines", "bags", "spaces", "demos", "push", "novedades", "completion", "cards", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "partner", "me", "channels", "board", "boards", "credentials", "docs", "help"];
|
|
12303
12658
|
var AGENTS_SUBS = ["ls", "create", "get", "set", "export", "doctor", "docs", "share", "unshare", "shares", "rm", "restart", "demo", "box"];
|
|
12304
12659
|
function unknown(what, input, known, helpCmd) {
|
|
12305
12660
|
const s = suggest(input, known);
|
|
@@ -12471,6 +12826,20 @@ async function main() {
|
|
|
12471
12826
|
by: { type: "string" },
|
|
12472
12827
|
models: { type: "boolean" },
|
|
12473
12828
|
teams: { type: "string" },
|
|
12829
|
+
owner: { type: "string" },
|
|
12830
|
+
cursor: { type: "string" },
|
|
12831
|
+
user: { type: "string" },
|
|
12832
|
+
why: { type: "boolean" },
|
|
12833
|
+
surface: { type: "string" },
|
|
12834
|
+
image: { type: "string" },
|
|
12835
|
+
link: { type: "string" },
|
|
12836
|
+
button: { type: "string" },
|
|
12837
|
+
publish: { type: "boolean" },
|
|
12838
|
+
draft: { type: "boolean" },
|
|
12839
|
+
ids: { type: "boolean" },
|
|
12840
|
+
force: { type: "boolean" },
|
|
12841
|
+
reenv: { type: "boolean" },
|
|
12842
|
+
"prompt-mode": { type: "string" },
|
|
12474
12843
|
format: { type: "string" },
|
|
12475
12844
|
timeline: { type: "boolean" },
|
|
12476
12845
|
new: { type: "boolean" },
|
|
@@ -12483,7 +12852,7 @@ async function main() {
|
|
|
12483
12852
|
const tokens = splitTokens(pos, v);
|
|
12484
12853
|
globals.token = tokens.session;
|
|
12485
12854
|
globals.profile = v.profile;
|
|
12486
|
-
if (v.version) return out({ version: "0.
|
|
12855
|
+
if (v.version) return out({ version: "0.30.0" }, () => "0.30.0");
|
|
12487
12856
|
for (const n of normalizeArgs(pos, v)) if (!globals.json) info(dim(n));
|
|
12488
12857
|
const context = (await readConfig()).workspace;
|
|
12489
12858
|
if (!explicitToken()?.startsWith("gat_")) {
|
|
@@ -12529,7 +12898,7 @@ async function main() {
|
|
|
12529
12898
|
case "apply":
|
|
12530
12899
|
return applyCmd(sub, v);
|
|
12531
12900
|
case "agents":
|
|
12532
|
-
if (sub === "ls" || sub === "list" || !sub) return agentsLs(v.all ? void 0 : context);
|
|
12901
|
+
if (sub === "ls" || sub === "list" || !sub) return agentsLs(v.all ? void 0 : v.workspace ?? context, { sponsor: v.sponsor, owner: v.owner, engine: v.engine, ids: v.ids, all: v.all });
|
|
12533
12902
|
if (sub === "create" || sub === "new") {
|
|
12534
12903
|
if (v.workspace === "none") delete v.workspace;
|
|
12535
12904
|
else if (!v.workspace && context) {
|
|
@@ -12561,7 +12930,14 @@ async function main() {
|
|
|
12561
12930
|
return dbsCmd(sub, a1, a2, pos.slice(4).filter((x) => x !== void 0), v);
|
|
12562
12931
|
case "turns":
|
|
12563
12932
|
if (v["quota-hits"]) return workspaceUsage(v.workspace ?? context, v);
|
|
12933
|
+
if (sub === "stats") return turnStats(v);
|
|
12564
12934
|
return turnsCmd(sub, a1, a2, v);
|
|
12935
|
+
case "push":
|
|
12936
|
+
if (sub !== "log") throw usage(t("Unknown push subcommand.", "Subcomando de push desconocido."), "ghosty push log --user <email> [--since 2d]");
|
|
12937
|
+
return pushLog(v.user, v);
|
|
12938
|
+
case "novedades":
|
|
12939
|
+
case "announcements":
|
|
12940
|
+
return novedadesCmd(sub, a1, v);
|
|
12565
12941
|
case "board":
|
|
12566
12942
|
return boardCmd(sub, a1, v, a2);
|
|
12567
12943
|
case "boards":
|
|
@@ -12577,8 +12953,11 @@ async function main() {
|
|
|
12577
12953
|
case "chat":
|
|
12578
12954
|
return chat(sub, a1, v);
|
|
12579
12955
|
case "conversations":
|
|
12956
|
+
if ((sub === "ls" || sub === "list") && v.user) return supportThreads(v.user, v);
|
|
12580
12957
|
if (sub === "ls" || sub === "list") return conversationsLs(a1);
|
|
12958
|
+
if (sub === "show" && (v.why || v.user)) return supportThread(a1, a2, v);
|
|
12581
12959
|
if (sub === "show") return conversationsShow(a1, a2, v);
|
|
12960
|
+
if (sub === "archive" || sub === "rm") return cleanupConversations(sub, a1, v);
|
|
12582
12961
|
throw usage(t(`Unknown conversations subcommand: ${sub ?? "(none)"}.`, `Subcomando de conversations desconocido: ${sub ?? "(ninguno)"}.`), t("Try: ghosty conversations ls <agent-id> \xB7 ghosty conversations show <agent-id> <conversation-id>", "Prueba: ghosty conversations ls <agent-id> \xB7 ghosty conversations show <agent-id> <conversation-id>"));
|
|
12583
12962
|
case "schedule":
|
|
12584
12963
|
return scheduleCmd(sub, a1, a2, v);
|
|
@@ -12615,7 +12994,7 @@ async function main() {
|
|
|
12615
12994
|
throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
|
|
12616
12995
|
}
|
|
12617
12996
|
}
|
|
12618
|
-
var notice = updateNotice("0.
|
|
12997
|
+
var notice = updateNotice("0.30.0").catch(() => null);
|
|
12619
12998
|
main().then(
|
|
12620
12999
|
async () => {
|
|
12621
13000
|
const n = await notice;
|
package/package.json
CHANGED