@ghostystudio/cli 0.25.0 → 0.27.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 +22 -1
- package/dist/index.js +509 -38
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -15,6 +15,11 @@ npm i -g @ghostystudio/cli && ghosty login
|
|
|
15
15
|
```bash
|
|
16
16
|
ghosty whoami
|
|
17
17
|
ghosty usage # plan and usage this week / month (also: ghosty me usage)
|
|
18
|
+
ghosty usage --workspace SLUG [--by agent,day] [--days 14] # a workspace's turns and tokens per agent/day (owner or staff)
|
|
19
|
+
ghosty engines ls [--models] # valid --engine / --model values and what each model costs
|
|
20
|
+
ghosty bags ls|create|set|rm <workspace> ["bag"] [--tokens 20M] [--recurring on|off] [--hard-cap on|off] [--note] # token bags (changing: staff)
|
|
21
|
+
ghosty spaces renew <workspace> --until YYYY-MM-DD [--note] [--dry-run] # staff
|
|
22
|
+
ghosty spaces rename <old> <new> [--dry-run] # staff, slug only
|
|
18
23
|
ghosty agents ls
|
|
19
24
|
ghosty agents create --name NAME [--engine E] [--model M] [--prompt TEXT | --prompt-file FILE] [--env K=V,…] [--workspace SLUG] [--channels teams=off]
|
|
20
25
|
ghosty agents get <id> [--fields name,model,prompt] [--prompt-out FILE]
|
|
@@ -23,7 +28,8 @@ ghosty apply <file|dir> [--agent X] [--create] [--prune] [--dry-run] # make an
|
|
|
23
28
|
ghosty agents set <id> [--name] [--engine] [--model] [--prompt | --prompt-file FILE] [--env K=V,…] [--connect ID,…] [--disconnect ID,…] [--toolsets +crm]
|
|
24
29
|
ghosty agents set <id> --prompt-base <base-id> --prompt-overlay overlay.json [--dry-run] # prompt = another agent's + edits, kept in sync
|
|
25
30
|
ghosty agents share|unshare <id> <email> [--role editor|admin] # share without handing it over · agents shares <id> lists them
|
|
26
|
-
ghosty agents rm <id> [--yes]
|
|
31
|
+
ghosty agents rm <id> [--from-workspace] [--yes] # --from-workspace: turn it off in Teams first
|
|
32
|
+
ghosty agents set <id> [--tool wa:tool=off,…] [--bag NAME|none] [--avatar URL|FILE]
|
|
27
33
|
ghosty agents restart <id>
|
|
28
34
|
ghosty agents box <id> [--check PATH]… [--contains PATH=TEXT]… [--recycle] # is its machine on the new image?
|
|
29
35
|
ghosty agents demo enable|disable <id>
|
|
@@ -40,6 +46,7 @@ ghosty skills enable|disable <id> <slug> # turn one of its skills (or one of Gho
|
|
|
40
46
|
ghosty skills templates <id> # pdf-doc templates: its own and Ghosty's, and which folder wins
|
|
41
47
|
ghosty dbs ls|tables|query|import|grant|revoke <id> [db] [--write] [--arg V]… [--external-write t1,t2] [--external-rows t:col+col] [--from easybits:ID --tables a,b --append --dry-run]
|
|
42
48
|
ghosty turns ls <id> [--errors | --status running|done|stopped|error] [--since 1h] [--limit N]
|
|
49
|
+
ghosty turns ls --workspace SLUG --quota-hits [--days 14] # messages that got no turn because the balance ran out
|
|
43
50
|
ghosty turns show <id> [turn-id|last] # what came in, what it answered, where the time went [--full]
|
|
44
51
|
ghosty channels whatsapp ls|enable|disable <id> [number] [--dry-run]
|
|
45
52
|
ghosty channels whatsapp link|unlink <id> --integration ID --answer on|off [--forward] [--dry-run] # bring a WABA number from Formmy
|
|
@@ -63,6 +70,7 @@ ghosty conversations show <id> <conversation-id> [--limit 20] # what was said
|
|
|
63
70
|
ghosty schedule add <id> "what to do" --in 30m | --at ISO [--every 12h --until ISO] [--conversation ID] [--title T]
|
|
64
71
|
ghosty schedule ls <id>
|
|
65
72
|
ghosty schedule rm <id> <schedule-id>
|
|
73
|
+
ghosty schedule add|ls|rm <id> … --teams <workspace>#<room>[/<thread-id>] # in a Teams room/thread: it answers there as if mentioned
|
|
66
74
|
|
|
67
75
|
ghosty me files ls|upload|rm [--kind image|video|audio|document|other]
|
|
68
76
|
ghosty credentials ls|set|rm [provider] [--from-env VAR] # never as an argument
|
|
@@ -79,6 +87,19 @@ Every command has its own help with examples: `ghosty <command> --help` or `ghos
|
|
|
79
87
|
|
|
80
88
|
Exit codes: `0` ok · `1` API error · `2` usage error · `3` not signed in.
|
|
81
89
|
|
|
90
|
+
### Changes in 0.27.0
|
|
91
|
+
|
|
92
|
+
- `schedule add <id> "…" --teams <workspace>#<room>[/<thread-id>] --at ISO | --in 30m`: at that time the agent answers in that Teams room or thread as if mentioned (with the room's memory). `schedule ls|rm <id> --teams <workspace>`.
|
|
93
|
+
|
|
94
|
+
### Changes in 0.26.0
|
|
95
|
+
|
|
96
|
+
- `ghosty engines ls [--models]`: engines and models valid for `--engine`/`--model`; an unknown model is rejected with the valid list (also without `--engine`).
|
|
97
|
+
- `ghosty bags ls|create|set|rm <workspace>`: token bags, monthly or one-time (`--recurring off`); `agents set --bag NAME|none` hangs an agent on one. Changing them is Ghosty staff only.
|
|
98
|
+
- `ghosty spaces renew <workspace> --until DATE` and `spaces rename <old> <new>` (staff, logged in the workspace's audit).
|
|
99
|
+
- `ghosty usage --workspace SLUG --by agent,day`: turns, failed, billable tokens and «no balance» per agent and day; `turns ls --workspace SLUG --quota-hits` lists those messages (WhatsApp Business and Teams).
|
|
100
|
+
- `agents set --tool [channel:]tool=on|off`, `--avatar URL|FILE` (its face in Teams); `agents rm --from-workspace` turns it off in Teams before deleting it.
|
|
101
|
+
- `dbs import` asks you to type the database name when a replace leaves a table empty or loses more than half its rows.
|
|
102
|
+
|
|
82
103
|
### Changes in 0.9.1
|
|
83
104
|
|
|
84
105
|
- `ghosty agents --help` describes every `agents demo` flag; `ghosty usage --help` has its own page.
|
package/dist/index.js
CHANGED
|
@@ -7476,6 +7476,9 @@ var SERVER_MESSAGES = [
|
|
|
7476
7476
|
["falta email", "missing email"],
|
|
7477
7477
|
["no est\xE1 compartido con {}", "it isn't shared with {}"],
|
|
7478
7478
|
// — Motor, modelo y máquina del agente (updateFleetAgentConfig, createFleetAgentRecord)
|
|
7479
|
+
["motor desconocido: {}; v\xE1lidos: {} (ghosty engines ls)", "unknown engine: {}; valid: {} (ghosty engines ls)"],
|
|
7480
|
+
["el modelo {} no es del motor {}; v\xE1lidos: {} (ghosty engines ls)", "model {} doesn't belong to engine {}; valid: {} (ghosty engines ls)"],
|
|
7481
|
+
["modelo {} inv\xE1lido para {}; v\xE1lidos: {} (ghosty engines ls)", "invalid model {} for {}; valid: {} (ghosty engines ls)"],
|
|
7479
7482
|
["motor desconocido: {}", "unknown engine: {}"],
|
|
7480
7483
|
["el modelo {} no es del motor {}", "model {} doesn't belong to engine {}"],
|
|
7481
7484
|
["modelo {} inv\xE1lido para {}", "invalid model {} for {}"],
|
|
@@ -7967,6 +7970,50 @@ var SERVER_MESSAGES = [
|
|
|
7967
7970
|
["no tienes tablero de ventas asignado: p\xEDdele al due\xF1o que te asigne uno (ghosty.studio \u2192 Ventas)", "you have no sales board assigned: ask the owner to assign you one (ghosty.studio \u2192 Sales)"],
|
|
7968
7971
|
["no tienes tablero de ventas asignado", "you have no sales board assigned"],
|
|
7969
7972
|
["tu identidad se cambia desde el equipo, no en una conversaci\xF3n con clientes", "your identity is changed from the team, not in a conversation with customers"],
|
|
7973
|
+
// — Espacios, bolsas, avatar y bajas (CLI 0.26)
|
|
7974
|
+
["las bolsas son topes pactados: s\xF3lo el staff de Ghosty", "bags are agreed caps: Ghosty staff only"],
|
|
7975
|
+
["renovar un espacio: s\xF3lo el staff de Ghosty", "renewing a workspace: Ghosty staff only"],
|
|
7976
|
+
["renombrar un espacio: s\xF3lo el staff de Ghosty", "renaming a workspace: Ghosty staff only"],
|
|
7977
|
+
["s\xF3lo el due\xF1o de \xAB{}\xBB o el staff de Ghosty", "only the owner of \xAB{}\xBB or Ghosty staff"],
|
|
7978
|
+
["la bolsa de un agente s\xF3lo la cambia el staff de Ghosty", "only Ghosty staff changes an agent's bag"],
|
|
7979
|
+
["este agente no tiene espacio: no puede tener bolsa", "this agent has no workspace: it can't have a bag"],
|
|
7980
|
+
["no hay bolsa \xAB{}\xBB en su espacio; hay: {}", "no bag \xAB{}\xBB in its workspace; there are: {}"],
|
|
7981
|
+
["no hay bolsa \xAB{}\xBB en su espacio (no tiene bolsas: ghosty bags create)", "no bag \xAB{}\xBB in its workspace (it has none: ghosty bags create)"],
|
|
7982
|
+
["no hay bolsa \xAB{}\xBB en {}", "no bag \xAB{}\xBB in {}"],
|
|
7983
|
+
["ya hay una bolsa \xAB{}\xBB en {}", "there's already a bag \xAB{}\xBB in {}"],
|
|
7984
|
+
["falta name: el cliente lo ve en Ajustes \u2192 Uso", "missing name: the customer sees it in Settings \u2192 Usage"],
|
|
7985
|
+
["falta tokens", "missing tokens"],
|
|
7986
|
+
["tokens: un n\xFAmero \u2265 0 (facturables)", "tokens: a number \u2265 0 (billable)"],
|
|
7987
|
+
["{} agente{} usa{} \xAB{}\xBB: mu\xE9velos a otra bolsa primero", "{} agent{} use{} \xAB{}\xBB: move them to another bag first"],
|
|
7988
|
+
["esa bolsa no es de este espacio", "that bag isn't from this workspace"],
|
|
7989
|
+
["esa bolsa no es del espacio de este agente", "that bag isn't from this agent's workspace"],
|
|
7990
|
+
["la bolsa necesita un nombre: el cliente lo ve en Ajustes \u2192 Uso", "the bag needs a name: the customer sees it in Settings \u2192 Usage"],
|
|
7991
|
+
["fecha inv\xE1lida: \xAB{}\xBB (AAAA-MM-DD)", "invalid date: \xAB{}\xBB (YYYY-MM-DD)"],
|
|
7992
|
+
["{} paga con Stripe: \xABpagado hasta\xBB lo mueve la factura pagada, no se toca a mano", "{} pays with Stripe: the paid invoice moves \xABpaid until\xBB, it isn't changed by hand"],
|
|
7993
|
+
["slug inv\xE1lido: \xAB{}\xBB (2 a 40 letras min\xFAsculas, n\xFAmeros o guiones; no reservado)", "invalid slug: \xAB{}\xBB (2 to 40 lowercase letters, digits or dashes; not reserved)"],
|
|
7994
|
+
["el slug {} ya es de otro espacio", "slug {} already belongs to another workspace"],
|
|
7995
|
+
["el slug {} lo tiene un tablero de prueba (vence {})", "slug {} is taken by a trial board (expires {})"],
|
|
7996
|
+
["quedan con el nombre viejo: https://{}.sales\u2026 (404) y https://{}.teams\u2026 (vivo hasta reiniciar Teams)", "these keep the old name: https://{}.sales\u2026 (404) and https://{}.teams\u2026 (alive until Teams restarts)"],
|
|
7997
|
+
["tiene cajas de CI: los runners usan la etiqueta ws-{}; hay que re-registrarlos y cambiar runs-on", "it has CI boxes: the runners use the label ws-{}; re-register them and change runs-on"],
|
|
7998
|
+
["\xA1cambiaron campos que no deb\xEDan! {}", "fields that shouldn't change did! {}"],
|
|
7999
|
+
["el token del agente no cambia su avatar: usa tu sesi\xF3n", "the agent's token doesn't change its avatar: use your session"],
|
|
8000
|
+
["manda la imagen (image/png|jpeg|webp) o {url: https://\u2026}", "send the image (image/png|jpeg|webp) or {url: https://\u2026}"],
|
|
8001
|
+
["no est\xE1 activado en ning\xFAn Teams todav\xEDa: se le pone al activarlo", "not active in any Teams yet: it gets it when activated"],
|
|
8002
|
+
["s\xF3lo el due\xF1o (o staff) puede borrar el agente", "only the owner (or staff) can delete the agent"],
|
|
8003
|
+
["ghosty agents rm <agente> --from-workspace lo quita de Teams y lo borra", "ghosty agents rm <agent> --from-workspace takes it out of Teams and deletes it"],
|
|
8004
|
+
["Se acab\xF3 el saldo de este agente ({}). Era una carga \xFAnica y no se recarga. Los dem\xE1s agentes del espacio siguen con el suyo.", "This agent ran out of balance ({}). It was a one-time load and doesn't refill. The other agents in the workspace keep theirs."],
|
|
8005
|
+
["el token del agente no programa turnos en Teams: usa tu sesi\xF3n", "the agent's token doesn't schedule turns in Teams: use your session"],
|
|
8006
|
+
["falta workspace (el espacio de Teams)", "missing workspace (the Teams workspace)"],
|
|
8007
|
+
["no eres miembro de \xAB{}\xBB", "you're not a member of \xAB{}\xBB"],
|
|
8008
|
+
["el agente no est\xE1 activo en el Teams de {}", "the agent isn't active in {}'s Teams"],
|
|
8009
|
+
["faltan channel, text o dueAt (ISO)", "missing channel, text or dueAt (ISO)"],
|
|
8010
|
+
["no pude hablar con Teams de {}: {}", "couldn't reach {}'s Teams: {}"],
|
|
8011
|
+
["@{} no est\xE1 activo en este espacio", "@{} isn't active in this workspace"],
|
|
8012
|
+
["no hay un room \xAB{}\xBB en este espacio", "there's no room \xAB{}\xBB in this workspace"],
|
|
8013
|
+
["el mensaje {} no es de #{}", "message {} isn't from #{}"],
|
|
8014
|
+
["el mensaje {} es una respuesta; usa el del inicio del hilo ({})", "message {} is a reply; use the one that starts the thread ({})"],
|
|
8015
|
+
["dueAt tiene que ser una fecha futura (epoch en segundos)", "dueAt must be a future date (epoch seconds)"],
|
|
8016
|
+
["no hay un turno pendiente con ese id (\xBFya corri\xF3?)", "no pending turn with that id (did it already run?)"],
|
|
7970
8017
|
// — Infra interna (casi nunca llega al CLI, pero por si acaso)
|
|
7971
8018
|
["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)."],
|
|
7972
8019
|
["sqld: fetch sin namespace", "sqld: fetch without namespace"],
|
|
@@ -8268,7 +8315,7 @@ async function postLong(path, body, timeoutMs = 9e5) {
|
|
|
8268
8315
|
const req = httpsRequest(`${base}${path}`, {
|
|
8269
8316
|
method: "POST",
|
|
8270
8317
|
timeout: timeoutMs,
|
|
8271
|
-
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.
|
|
8318
|
+
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.27.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
|
|
8272
8319
|
}, (res) => {
|
|
8273
8320
|
let buf = "";
|
|
8274
8321
|
res.on("data", (c) => buf += c);
|
|
@@ -8310,7 +8357,7 @@ async function request(path, opts = {}, retried = false) {
|
|
|
8310
8357
|
method: opts.method ?? (opts.body !== void 0 || opts.raw !== void 0 ? "POST" : "GET"),
|
|
8311
8358
|
headers: {
|
|
8312
8359
|
authorization: `Bearer ${token}`,
|
|
8313
|
-
"user-agent": `ghosty-cli/${"0.
|
|
8360
|
+
"user-agent": `ghosty-cli/${"0.27.0"}`,
|
|
8314
8361
|
"accept-language": lang(),
|
|
8315
8362
|
...opts.body !== void 0 ? { "content-type": "application/json" } : {},
|
|
8316
8363
|
...opts.headers
|
|
@@ -8591,6 +8638,15 @@ async function agentsGet(id, fields, promptOut) {
|
|
|
8591
8638
|
}
|
|
8592
8639
|
async function agentsSet(id, v) {
|
|
8593
8640
|
id = need(id, "agent id", "ghosty agents set <agent-id> --prompt-file PROMPT.md");
|
|
8641
|
+
const done = [];
|
|
8642
|
+
if (v.bag) {
|
|
8643
|
+
const r2 = await api(agentPath(id), { method: "PATCH", body: { bag: v.bag === "none" ? null : v.bag } });
|
|
8644
|
+
done.push(r2.bag ? t(`bag \xAB${r2.bag}\xBB`, `bolsa \xAB${r2.bag}\xBB`) : t("workspace balance (no bag)", "saldo del espacio (sin bolsa)"));
|
|
8645
|
+
}
|
|
8646
|
+
if (v.avatar) {
|
|
8647
|
+
const r2 = await setAvatar(id, v.avatar);
|
|
8648
|
+
done.push(`avatar ${r2.avatar}${r2.workspaces.length ? dim(t(` (Teams: ${r2.workspaces.join(", ")})`, ` (Teams: ${r2.workspaces.join(", ")})`)) : r2.note ? dim(` (${serverText(r2.note)})`) : ""}`);
|
|
8649
|
+
}
|
|
8594
8650
|
if (v["prompt-base"]) {
|
|
8595
8651
|
const unlink = v["prompt-base"] === "none";
|
|
8596
8652
|
if (!unlink && !v["prompt-overlay"]) throw usage(t("Missing --prompt-overlay FILE.", "Falta --prompt-overlay FILE."), t("Its format", "Su formato") + ': {"edits":[{"what":"name","find":"Tania","replace":"Pia","all":true}]} (regex: true ' + t("for patterns", "para patrones") + ")");
|
|
@@ -8628,6 +8684,22 @@ ${dim(t("Dry run: nothing changed.", "Simulacro: no se cambi\xF3 nada."))}`);
|
|
|
8628
8684
|
next.delete("core");
|
|
8629
8685
|
body.tools = { toolsets: [...next] };
|
|
8630
8686
|
}
|
|
8687
|
+
if (v.tool) {
|
|
8688
|
+
const cur = await api(agentPath(id, "?fields=tools"));
|
|
8689
|
+
const canales = cur.tools?.canales ?? {};
|
|
8690
|
+
const known = new Set(Object.values(canales).flatMap((m) => Object.keys(m)));
|
|
8691
|
+
const tools = { gs: {}, canales: {} };
|
|
8692
|
+
for (const item of v.tool.split(",").map((x) => x.trim()).filter(Boolean)) {
|
|
8693
|
+
const m = /^(?:([a-z]+):)?([a-z0-9_-]+)=(on|off)$/i.exec(item);
|
|
8694
|
+
if (!m) throw usage(t(`Bad --tool entry: ${item}.`, `Entrada de --tool inv\xE1lida: ${item}.`), t("Use [channel:]tool=on|off, e.g. wa:actualizar_identidad=off", "Usa [canal:]tool=on|off, p. ej. wa:actualizar_identidad=off"));
|
|
8695
|
+
const [, ch, tool, onOff] = m;
|
|
8696
|
+
if (!known.has(tool)) throw usage(t(`Unknown tool: ${tool}.`, `Tool desconocida: ${tool}.`), t(`List them: ghosty agents get ${id} --fields tools`, `L\xEDstalas: ghosty agents get ${id} --fields tools`));
|
|
8697
|
+
if (ch && !(ch in canales)) throw usage(t(`Unknown channel: ${ch}.`, `Canal desconocido: ${ch}.`), `${t("Channels", "Canales")}: ${Object.keys(canales).join(", ")}`);
|
|
8698
|
+
if (ch) (tools.canales[ch] ??= {})[tool] = onOff === "on";
|
|
8699
|
+
else tools.gs[tool] = onOff === "on";
|
|
8700
|
+
}
|
|
8701
|
+
body.tools = { ...body.tools ?? {}, ...tools };
|
|
8702
|
+
}
|
|
8631
8703
|
const env = parseEnv(v.env);
|
|
8632
8704
|
const unset = (v["unset-env"] ?? "").split(",").map((k) => k.trim()).filter(Boolean);
|
|
8633
8705
|
if (env || unset.length) body.env = { ...env ?? {}, ...Object.fromEntries(unset.map((k) => [k, ""])) };
|
|
@@ -8651,18 +8723,32 @@ ${dim(t("Dry run: nothing changed.", "Simulacro: no se cambi\xF3 nada."))}`);
|
|
|
8651
8723
|
if (v["auto-stage"] !== "on" && v["auto-stage"] !== "off") throw usage(t("--auto-stage takes on or off.", "--auto-stage acepta on u off."));
|
|
8652
8724
|
body.autoStage = v["auto-stage"] === "on";
|
|
8653
8725
|
}
|
|
8654
|
-
if (!Object.keys(body).length)
|
|
8726
|
+
if (!Object.keys(body).length && done.length) return out({ id, updated: done }, () => `${lavender("\u2713")} ${t("Updated", "Actualizado")}: ${done.join(" \xB7 ")}`);
|
|
8727
|
+
if (!Object.keys(body).length) throw usage(t("Nothing to change.", "Nada que cambiar."), t("Use", "Usa") + " --name, --engine, --model, --prompt, --prompt-file, --env, --unset-env, --channels, --toolsets, --tool, --connect, --disconnect, --voice, --voice-replies, --auto-stage, --bag " + t("or", "o") + " --avatar.");
|
|
8655
8728
|
const r = await api(agentPath(id), { method: "PATCH", body });
|
|
8656
|
-
out(r, () => `${lavender("\u2713")} ${t("Updated", "Actualizado")}: ${Object.keys(body).join(", ")}.`);
|
|
8729
|
+
out(r, () => `${lavender("\u2713")} ${t("Updated", "Actualizado")}: ${[...Object.keys(body), ...done].join(", ")}.`);
|
|
8657
8730
|
}
|
|
8658
8731
|
async function agentsRm(id, v = {}) {
|
|
8659
|
-
id = need(id, "agent id", "ghosty agents rm <agent-id>");
|
|
8660
|
-
|
|
8732
|
+
id = need(id, "agent id", "ghosty agents rm <agent-id> [--from-workspace]");
|
|
8733
|
+
const from = v["from-workspace"];
|
|
8734
|
+
const retry = `ghosty agents rm ${id}${from ? " --from-workspace" : ""}`;
|
|
8735
|
+
requireYesIfHeadless(v.yes, retry);
|
|
8661
8736
|
if (!v.yes) {
|
|
8662
8737
|
const a = await api(agentPath(id, "?fields=name"));
|
|
8663
|
-
|
|
8738
|
+
const teams = from ? t(" It's turned off in every Teams where it's active first.", " Antes se desactiva en cada Teams donde est\xE9 activo.") : "";
|
|
8739
|
+
await confirm(t(`Delete agent ${a.name ?? id}? Its machine, memory and files go too. This can't be undone.${teams}`, `\xBFBorrar el agente ${a.name ?? id}? Se van tambi\xE9n su m\xE1quina, memoria y archivos. No se puede deshacer.${teams}`), { expect: a.name || id, retry });
|
|
8664
8740
|
}
|
|
8665
|
-
|
|
8741
|
+
const r = await api(agentPath(id, from ? "?fromWorkspace=1" : ""), { method: "DELETE" });
|
|
8742
|
+
out(r, () => `${lavender("\u2713")} ${t("Deleted", "Borrado")} ${id}.${r?.unbound?.length ? dim(t(` Turned off in Teams: ${r.unbound.join(", ")}.`, ` Desactivado en Teams: ${r.unbound.join(", ")}.`)) : ""}`);
|
|
8743
|
+
}
|
|
8744
|
+
async function setAvatar(id, src) {
|
|
8745
|
+
if (/^https:\/\//.test(src)) return api(agentPath(id, "/avatar"), { method: "PUT", body: { url: src } });
|
|
8746
|
+
const bytes = await readFile3(src).catch(() => {
|
|
8747
|
+
throw usage(t(`Can't read ${src}.`, `No puedo leer ${src}.`), t("--avatar takes an https:// link or a local JPEG/PNG/WebP file.", "--avatar acepta una liga https:// o un archivo JPEG/PNG/WebP local."));
|
|
8748
|
+
});
|
|
8749
|
+
const ext = src.toLowerCase().split(".").pop();
|
|
8750
|
+
const type = ext === "png" ? "image/png" : ext === "webp" ? "image/webp" : "image/jpeg";
|
|
8751
|
+
return api(agentPath(id, "/avatar"), { method: "PUT", raw: new Uint8Array(bytes), headers: { "content-type": type } });
|
|
8666
8752
|
}
|
|
8667
8753
|
async function agentsShare(sub, id, email, v) {
|
|
8668
8754
|
id = need(id, "agent id", "ghosty agents share <agent-id> <email> [--role admin|editor]");
|
|
@@ -9153,7 +9239,40 @@ var short = (s, n = 60) => s.length > n ? s.slice(0, n - 1) + "\u2026" : s;
|
|
|
9153
9239
|
async function agentSchedule(agent) {
|
|
9154
9240
|
return api(`/api/v2/me/agents/${enc4(agent)}/schedule`);
|
|
9155
9241
|
}
|
|
9242
|
+
function parseTeamsTarget(raw) {
|
|
9243
|
+
const m = /^([a-z0-9-]+)(?:#([^/\s]+)(?:\/(\d+))?)?$/i.exec(raw.trim());
|
|
9244
|
+
if (!m) throw usage(t(`Bad --teams: ${raw}.`, `--teams inv\xE1lido: ${raw}.`), t("Use workspace#room or workspace#room/<thread message id>, e.g. descti#general/4812", "Usa espacio#room o espacio#room/<id del mensaje del hilo>, p. ej. descti#general/4812"));
|
|
9245
|
+
return { workspace: m[1], ...m[2] ? { channel: m[2] } : {}, ...m[3] ? { parentId: Number(m[3]) } : {} };
|
|
9246
|
+
}
|
|
9247
|
+
async function teamsSchedule(sub, agent, arg, v) {
|
|
9248
|
+
const target = parseTeamsTarget(v.teams);
|
|
9249
|
+
const path = `/api/v2/agents/${enc4(agent)}/teams-schedule`;
|
|
9250
|
+
const when = (epoch) => new Date(epoch * 1e3).toLocaleString("es-MX", { timeZone: "America/Mexico_City", dateStyle: "medium", timeStyle: "short" });
|
|
9251
|
+
if (sub === "ls" || sub === "list") {
|
|
9252
|
+
const r2 = await api(`${path}?workspace=${enc4(target.workspace)}`);
|
|
9253
|
+
const rows = r2.scheduled.map((x) => ({ id: x.id, due: when(x.dueAt), where: `#${x.channel}${x.parentId ? `/${x.parentId}` : ""}`, by: x.by, text: short(x.text) }));
|
|
9254
|
+
return out(r2, () => rows.length ? table(rows, ["id", "due", "where", "by", "text"]) : dim(t("No scheduled turns in Teams.", "No hay turnos programados en Teams.")));
|
|
9255
|
+
}
|
|
9256
|
+
if (sub === "rm" || sub === "cancel") {
|
|
9257
|
+
if (!arg) throw usage(t("Missing schedule id.", "Falta el id del turno."), `ghosty schedule rm <agent-id> <id> --teams ${target.workspace}`);
|
|
9258
|
+
const r2 = await api(path, { method: "DELETE", body: { workspace: target.workspace, id: arg } });
|
|
9259
|
+
return out(r2, () => `${lavender("\u2713")} ${t("Canceled.", "Cancelado.")}`);
|
|
9260
|
+
}
|
|
9261
|
+
if (sub !== "add") throw usage(t(`Unknown schedule subcommand: ${sub ?? "(none)"}.`, `Subcomando de schedule desconocido: ${sub ?? "(ninguno)"}.`), "ghosty schedule add|ls|rm <agent-id> \u2026 --teams workspace#room[/thread]");
|
|
9262
|
+
if (!arg) throw usage(t("Missing what to do.", "Falta qu\xE9 hacer."), `ghosty schedule add <agent-id> "\u2026" --teams ${v.teams} --at 2026-10-01T09:00`);
|
|
9263
|
+
if (!target.channel) throw usage(t("Missing the room.", "Falta el room."), t("Use --teams workspace#room or workspace#room/<thread id>", "Usa --teams espacio#room o espacio#room/<id del hilo>"));
|
|
9264
|
+
if (!v.at && !v.in) throw usage(t("Say when.", "Di cu\xE1ndo."), t("Use --in 30m or --at 2026-10-01T09:00-06:00", "Usa --in 30m o --at 2026-10-01T09:00-06:00"));
|
|
9265
|
+
if (v.at && v.in) throw usage(t("Use --at or --in, not both.", "Usa --at o --in, no ambos."));
|
|
9266
|
+
if (v.every) throw usage(t("--every is not supported with --teams yet.", "--every todav\xEDa no va con --teams."));
|
|
9267
|
+
const dueAt = v.in ? new Date(Date.now() + minutesOf(v.in, "--in") * 6e4).toISOString() : isoOf(v.at, "--at");
|
|
9268
|
+
const r = await api(path, { method: "POST", body: { workspace: target.workspace, channel: target.channel, ...target.parentId ? { parentId: target.parentId } : {}, text: arg, dueAt } });
|
|
9269
|
+
out(r, () => `${lavender("\u2713")} ${t(`@${r.handle} will answer in #${r.channel}${r.parentId ? ` (thread ${r.parentId})` : ""} on ${when(r.dueAt)}.`, `@${r.handle} contestar\xE1 en #${r.channel}${r.parentId ? ` (hilo ${r.parentId})` : ""} el ${when(r.dueAt)}.`)} ${dim(`id ${r.id} \xB7 ${t("cancel", "cancelar")}: ghosty schedule rm ${agent} ${r.id} --teams ${target.workspace}`)}`);
|
|
9270
|
+
}
|
|
9156
9271
|
async function scheduleCmd(sub, agent, arg, v) {
|
|
9272
|
+
if (v.teams) {
|
|
9273
|
+
if (!agent) throw usage(t("Missing agent id.", "Falta el id del agente."), 'ghosty schedule add <agent-id> "\u2026" --teams workspace#room/thread --at ISO');
|
|
9274
|
+
return teamsSchedule(sub, agent, arg, v);
|
|
9275
|
+
}
|
|
9157
9276
|
if (sub === "ls" || sub === "list") {
|
|
9158
9277
|
if (!agent) throw usage(t("Missing agent id.", "Falta el id del agente."), t("Example", "Ejemplo") + ": ghosty schedule ls <agent-id>");
|
|
9159
9278
|
const r = await agentSchedule(agent);
|
|
@@ -9305,7 +9424,7 @@ async function call(path, v, init = {}) {
|
|
|
9305
9424
|
"x-ghosty-ts": ts,
|
|
9306
9425
|
"x-ghosty-sig": sig,
|
|
9307
9426
|
"content-type": "application/json",
|
|
9308
|
-
"user-agent": `ghosty-cli/${"0.
|
|
9427
|
+
"user-agent": `ghosty-cli/${"0.27.0"}`
|
|
9309
9428
|
},
|
|
9310
9429
|
body: method === "GET" ? void 0 : raw
|
|
9311
9430
|
});
|
|
@@ -9526,6 +9645,19 @@ function gsOnlyWarnings(gs) {
|
|
|
9526
9645
|
${lines.join("\n")}
|
|
9527
9646
|
${dim(t("Other agents may be writing there already: leave those tables out (--tables) or use --append.", "Puede que otros agentes ya escriban ah\xED: deja fuera esas tablas (--tables) o usa --append."))}`;
|
|
9528
9647
|
}
|
|
9648
|
+
function shrinking(plan) {
|
|
9649
|
+
return plan.filter((p) => p.action === "replace" && p.targetRows > 0 && (p.resultRows === 0 || p.resultRows < p.targetRows / 2));
|
|
9650
|
+
}
|
|
9651
|
+
function shrinkWarnings(plan) {
|
|
9652
|
+
const s = shrinking(plan);
|
|
9653
|
+
if (!s.length) return "";
|
|
9654
|
+
const lines = s.map(
|
|
9655
|
+
(p) => red(p.resultRows === 0 ? t(` ! ${p.table}: would be left EMPTY (${p.targetRows} \u2192 0)`, ` ! ${p.table}: quedar\xEDa VAC\xCDA (${p.targetRows} \u2192 0)`) : t(` ! ${p.table}: loses ${p.targetRows - p.resultRows} of ${p.targetRows} rows`, ` ! ${p.table}: pierde ${p.targetRows - p.resultRows} de ${p.targetRows} filas`))
|
|
9656
|
+
);
|
|
9657
|
+
return `
|
|
9658
|
+
${lines.join("\n")}
|
|
9659
|
+
${dim(t("Is the source the right database? Leave those tables out with --tables.", "\xBFLa base de origen es la correcta? D\xE9jalas fuera con --tables."))}`;
|
|
9660
|
+
}
|
|
9529
9661
|
function planText(plan) {
|
|
9530
9662
|
return plan.map((p) => p.action === "create" ? t(` + ${p.table} create, ${p.sourceRows} row(s)`, ` + ${p.table} crear, ${p.sourceRows} fila(s)`) : t(` ~ ${p.table} ${p.action} ${p.targetRows} \u2192 ${p.resultRows} row(s)`, ` ~ ${p.table} ${p.action === "replace" ? "reemplazar" : "agregar"} ${p.targetRows} \u2192 ${p.resultRows} fila(s)`)).join("\n");
|
|
9531
9663
|
}
|
|
@@ -9538,12 +9670,12 @@ async function dbsImport(id, db, v) {
|
|
|
9538
9670
|
const body = { from: "easybits", sourceDb: m[1], easybitsKey, mode: v.append ? "append" : "replace", ...v.tables ? { tables: csv(v.tables) } : {}, ...v["rehost-images"] ? { rehost: csv(v["rehost-images"]) } : {} };
|
|
9539
9671
|
const path = agentPath2(id, `/dbs/${enc6(db)}/import`);
|
|
9540
9672
|
const dry = await api(path, { body: { ...body, dryRun: true } });
|
|
9541
|
-
if (v["dry-run"]) return out(dry, () => `${dry.db} \u2190 easybits:${m[1]}
|
|
9542
|
-
${planText(dry.plan)}${importWarnings(dry.warnings)}${gsOnlyWarnings(dry.gsOnly)}
|
|
9673
|
+
if (v["dry-run"]) return out({ ...dry, shrinking: shrinking(dry.plan).map((p) => p.table) }, () => `${dry.db} \u2190 easybits:${m[1]}
|
|
9674
|
+
${planText(dry.plan)}${shrinkWarnings(dry.plan)}${importWarnings(dry.warnings)}${gsOnlyWarnings(dry.gsOnly)}
|
|
9543
9675
|
${dim(t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada."))}`);
|
|
9544
9676
|
const destructive = dry.plan.some((p) => p.action === "replace" && p.targetRows > 0);
|
|
9545
|
-
if (destructive) await confirm(`${planText(dry.plan)}${importWarnings(dry.warnings)}${gsOnlyWarnings(dry.gsOnly)}
|
|
9546
|
-
${t(`Replace the rows above in ${dry.db}?`, `\xBFReemplazar las filas de arriba en ${dry.db}?`)}`, { yes: v.yes, retry: `ghosty dbs import ${id} ${db} --from ${v.from}${v.tables ? ` --tables ${v.tables}` : ""}` });
|
|
9677
|
+
if (destructive) await confirm(`${planText(dry.plan)}${shrinkWarnings(dry.plan)}${importWarnings(dry.warnings)}${gsOnlyWarnings(dry.gsOnly)}
|
|
9678
|
+
${t(`Replace the rows above in ${dry.db}?`, `\xBFReemplazar las filas de arriba en ${dry.db}?`)}`, { yes: v.yes, ...shrinking(dry.plan).length ? { expect: dry.db } : {}, retry: `ghosty dbs import ${id} ${db} --from ${v.from}${v.tables ? ` --tables ${v.tables}` : ""}` });
|
|
9547
9679
|
const r = await api(path, { body });
|
|
9548
9680
|
const img = r.rehosted ? `
|
|
9549
9681
|
${t(`${r.rehosted.copied} image link(s) now permanent`, `${r.rehosted.copied} liga(s) de imagen ya permanentes`)}${r.rehosted.failed.length ? `, ${red(String(r.rehosted.failed.length))} ${t("kept as they were", "se quedaron como estaban")}:
|
|
@@ -9751,8 +9883,37 @@ async function agentDocsCmd(sub, id, docs, v) {
|
|
|
9751
9883
|
const r = await api(agentPath2(id, `/documents/${enc6(doc)}`), { method: "DELETE" });
|
|
9752
9884
|
return out(r, () => `${lavender("\u2713")} ${r.name} ${t("revoked.", "revocado.")} ${dim(`${t("Undo", "Deshacer")}: ghosty agents docs grant ${id} "${r.name}"`)}`);
|
|
9753
9885
|
}
|
|
9886
|
+
case "replace": {
|
|
9887
|
+
const ex = 'ghosty agents docs replace <agent-id> "Cat\xE1logo.pdf" --file nuevo.pdf (o --url URL)';
|
|
9888
|
+
doc = need2(doc, t("document (id or exact name)", "el documento (id o nombre exacto)"), ex);
|
|
9889
|
+
if (!v.url === !v.file) throw usage(t("Use --url URL or --file PATH (one of them).", "Usa --url URL o --file RUTA (uno de los dos)."), t("Example", "Ejemplo") + ": " + ex);
|
|
9890
|
+
const body = {
|
|
9891
|
+
...v.url ? { url: v.url } : { contentBase64: (await readFile6(v.file)).toString("base64") },
|
|
9892
|
+
...v.note ? { note: v.note } : {}
|
|
9893
|
+
};
|
|
9894
|
+
const r = await api(agentPath2(id, `/documents/${enc6(doc)}/versions`), { method: "POST", body });
|
|
9895
|
+
return out(r, () => `${lavender("\u2713")} ${r.name}: ${t("version", "versi\xF3n")} ${r.version?.n ?? "?"} ${dim(`(${r.size} bytes)`)}. ${dim(`${t("Undo", "Deshacer")}: ghosty agents docs restore ${id} "${r.name}" ${(r.version?.n ?? 2) - 1}`)}`);
|
|
9896
|
+
}
|
|
9897
|
+
case "history":
|
|
9898
|
+
case "versions": {
|
|
9899
|
+
doc = need2(doc, t("document (id or exact name)", "el documento (id o nombre exacto)"), 'ghosty agents docs history <agent-id> "Cat\xE1logo.pdf"');
|
|
9900
|
+
const r = await api(agentPath2(id, `/documents/${enc6(doc)}/versions`));
|
|
9901
|
+
const rows = r.versions.map((x) => ({ v: `${x.n}${x.current ? "*" : ""}`, when: x.createdAt.slice(0, 16).replace("T", " "), by: x.agent ?? x.via ?? x.source, size: String(x.size), note: x.note ?? "" }));
|
|
9902
|
+
const used = r.usedBy.map((u) => `${u.agent} (${u.mode === "write" ? t("updates it", "lo actualiza") + (u.writableFrom.length ? `: ${u.writableFrom.join(", ")}` : "") : t("reads it", "lo lee")})`).join(" \xB7 ");
|
|
9903
|
+
return out(r, () => `${r.document.name}
|
|
9904
|
+
${table(rows, ["v", "when", "by", "size", "note"])}${used ? `
|
|
9905
|
+
${dim(t("Used by", "Lo usan"))}: ${used}` : ""}`);
|
|
9906
|
+
}
|
|
9907
|
+
case "restore": {
|
|
9908
|
+
const ex = 'ghosty agents docs restore <agent-id> "Cat\xE1logo.pdf" <n>';
|
|
9909
|
+
doc = need2(doc, t("document (id or exact name)", "el documento (id o nombre exacto)"), ex);
|
|
9910
|
+
const n = Number(need2(docs[1], t("version number", "el n\xFAmero de versi\xF3n"), ex));
|
|
9911
|
+
if (!Number.isInteger(n) || n < 1) throw usage(t("The version is a whole number (see docs history).", "La versi\xF3n es un n\xFAmero entero (mira docs history)."), ex);
|
|
9912
|
+
const r = await api(agentPath2(id, `/documents/${enc6(doc)}/versions/${n}/restore`), { method: "POST", body: v.note ? { note: v.note } : {} });
|
|
9913
|
+
return out(r, () => `${lavender("\u2713")} ${r.name}: ${t(`restored from v${n} as version`, `restaurado de la v${n} como versi\xF3n`)} ${r.version?.n ?? "?"}.`);
|
|
9914
|
+
}
|
|
9754
9915
|
default:
|
|
9755
|
-
throw usage(t(`Unknown agents docs subcommand: ${sub}.`, `Subcomando de agents docs desconocido: ${sub}.`), t("Try", "Prueba") + ": ghosty agents docs ls | add | grant | revoke");
|
|
9916
|
+
throw usage(t(`Unknown agents docs subcommand: ${sub}.`, `Subcomando de agents docs desconocido: ${sub}.`), t("Try", "Prueba") + ": ghosty agents docs ls | add | grant | replace | history | restore | revoke");
|
|
9756
9917
|
}
|
|
9757
9918
|
}
|
|
9758
9919
|
function skipReason(r) {
|
|
@@ -10417,6 +10578,7 @@ var HELPS_ES = {
|
|
|
10417
10578
|
["--no-deliver", "docs grant/add: puede leerlo pero nunca mand\xE1rselo a quien escribe"],
|
|
10418
10579
|
["--url URL | --file PATH", "docs add: de d\xF3nde sale el documento (URL p\xFAblica, hasta 25 MB, o un archivo local)"],
|
|
10419
10580
|
["--grant read|write|no", "docs add: acceso de este agente (default read; no = s\xF3lo subirlo a la cuenta)"],
|
|
10581
|
+
["--note TEXT", "docs replace/restore: por qu\xE9 cambi\xF3 (sale en docs history)"],
|
|
10420
10582
|
["--role R", "share: editor (lo configura y lo usa) o admin (adem\xE1s lo comparte); repetirlo sobre un correo ya compartido cambia el rol"],
|
|
10421
10583
|
["--yes", "rm: se salta la confirmaci\xF3n (obligatorio sin terminal)"],
|
|
10422
10584
|
["--slug NAME", "demo: nombre de la liga; se le agrega un c\xF3digo de 6 caracteres (/p/NAME-CODE)"],
|
|
@@ -10431,7 +10593,11 @@ var HELPS_ES = {
|
|
|
10431
10593
|
["--check PATH", "box: este archivo debe existir en la m\xE1quina (repetible), p. ej. un archivo que trae la imagen nueva"],
|
|
10432
10594
|
["--contains PATH=TEXT", "box: ese archivo debe contener TEXT (un cambio que no agrega archivos)"],
|
|
10433
10595
|
["--recycle", "box: reemplaza sus m\xE1quinas (no a media respuesta); la nueva arranca en el siguiente turno. ACP: usa restart"],
|
|
10434
|
-
["--unset-env K1,K2", "set: borra esas variables del env (--env K= tambi\xE9n borra K)"]
|
|
10596
|
+
["--unset-env K1,K2", "set: borra esas variables del env (--env K= tambi\xE9n borra K)"],
|
|
10597
|
+
["--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"],
|
|
10598
|
+
["--bag NAME|none", "set: de qu\xE9 bolsa de tokens de su espacio gasta; none = el saldo del espacio (s\xF3lo staff)"],
|
|
10599
|
+
["--avatar URL|FILE", "set: su cara en Teams: una liga https o un JPEG/PNG/WebP local (se guarda en img.ghosty.studio)"],
|
|
10600
|
+
["--from-workspace", "rm: lo desactiva en cada Teams donde est\xE9 y luego lo borra (due\xF1o o staff)"]
|
|
10435
10601
|
],
|
|
10436
10602
|
notes: [
|
|
10437
10603
|
"doctor sale con 1 cuando encuentra un problema, as\xED puede frenar un script o un CI.",
|
|
@@ -10492,7 +10658,8 @@ var HELPS_ES = {
|
|
|
10492
10658
|
["--status S", "running | done | stopped | error"],
|
|
10493
10659
|
["--since T", "30m, 1h, 2d o una fecha ISO"],
|
|
10494
10660
|
["--limit N", "m\xE1ximo de filas (default 20, m\xE1x. 500)"],
|
|
10495
|
-
["--full", "show: el mensaje que entr\xF3 y la respuesta completos (default: primeros 600 caracteres)"]
|
|
10661
|
+
["--full", "show: el mensaje que entr\xF3 y la respuesta completos (default: primeros 600 caracteres)"],
|
|
10662
|
+
["--quota-hits", "ls --workspace: mensajes (WhatsApp Business y Teams) que no abrieron turno porque se acab\xF3 el saldo"]
|
|
10496
10663
|
],
|
|
10497
10664
|
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)."]
|
|
10498
10665
|
},
|
|
@@ -10527,7 +10694,8 @@ var HELPS_ES = {
|
|
|
10527
10694
|
},
|
|
10528
10695
|
schedule: {
|
|
10529
10696
|
summary: "Turnos que corren despu\xE9s, con el reloj propio del agente.",
|
|
10530
|
-
replace: [['"what to do"', '"qu\xE9 hacer"']]
|
|
10697
|
+
replace: [['"what to do"', '"qu\xE9 hacer"'], ["# answers in that thread as if mentioned", "# contesta en ese hilo como si lo mencionaran"]],
|
|
10698
|
+
notes: ["--teams: el agente tiene que estar activo en el Teams de ese espacio y t\xFA ser miembro. El id del hilo es el del mensaje con que empieza."]
|
|
10531
10699
|
},
|
|
10532
10700
|
me: { summary: "Tu cuenta: archivos y uso." },
|
|
10533
10701
|
credentials: {
|
|
@@ -10656,8 +10824,40 @@ var HELPS_ES = {
|
|
|
10656
10824
|
]
|
|
10657
10825
|
},
|
|
10658
10826
|
usage: {
|
|
10659
|
-
summary: "Tu plan y cu\xE1nto llevas usado esta semana y este mes. Con --
|
|
10660
|
-
flags: [
|
|
10827
|
+
summary: "Tu plan y cu\xE1nto llevas usado esta semana y este mes. Con --workspace: turnos y tokens facturables de un espacio por agente y/o por d\xEDa. Con --boxes: sus cajas contra el tope.",
|
|
10828
|
+
flags: [
|
|
10829
|
+
["--boxes", "las cajas del espacio contra su tope (spares, dormidas) y, por agente: conversaciones por caja, cajas, despiertas, spares, turnos corriendo ahora"],
|
|
10830
|
+
["--workspace SLUG", "qu\xE9 espacio (due\xF1o o staff); con --boxes el default es el de ghosty use"],
|
|
10831
|
+
["--by agent|day|agent,day", "--workspace: filas por agente (default), por d\xEDa (CDMX) o las dos"],
|
|
10832
|
+
["--days N", "--workspace: cu\xE1ntos d\xEDas hacia atr\xE1s (default 14, m\xE1x. 92)"]
|
|
10833
|
+
],
|
|
10834
|
+
notes: ["failed = turnos que terminaron en error o se detuvieron. \xABno balance\xBB = mensajes que no abrieron turno porque se acab\xF3 la bolsa (ghosty turns ls --workspace X --quota-hits los lista)."]
|
|
10835
|
+
},
|
|
10836
|
+
engines: {
|
|
10837
|
+
summary: "Motores y modelos que puede usar un agente: los valores v\xE1lidos de --engine y --model, el default y cu\xE1nto cuesta cada modelo contra el saldo.",
|
|
10838
|
+
flags: [["--models", "cada modelo de cada motor con su nombre, multiplicador de costo y si necesita tu propia llave"]]
|
|
10839
|
+
},
|
|
10840
|
+
bags: {
|
|
10841
|
+
summary: "Las bolsas de tokens de un espacio: un tope con nombre del que gastan uno o varios agentes. Mensual (se recarga cada periodo) o de carga \xFAnica (cuenta desde que se cre\xF3 y no se recarga). Leer: due\xF1o o staff; cambiar: staff de Ghosty.",
|
|
10842
|
+
flags: [
|
|
10843
|
+
["--tokens N", "tokens facturables: 20M, 500k o un n\xFAmero"],
|
|
10844
|
+
["--recurring on|off", "on = se recarga cada periodo (default); off = carga \xFAnica, cuenta desde hoy"],
|
|
10845
|
+
["--hard-cap on|off", "on = al 100% los turnos se frenan con \xABse acab\xF3 el saldo\xBB (default); off = s\xF3lo cuenta"],
|
|
10846
|
+
["--note TEXT", "qu\xE9 se pact\xF3 y qui\xE9n lo autoriz\xF3"],
|
|
10847
|
+
["--name NEW", "set: renombra la bolsa (el cliente la ve en Ajustes \u2192 Uso)"],
|
|
10848
|
+
["--yes", "rm: s\xE1ltate la confirmaci\xF3n (obligatorio sin terminal)"]
|
|
10849
|
+
],
|
|
10850
|
+
notes: ["Un agente sin bolsa gasta del saldo del espacio. rm s\xF3lo borra una bolsa que ning\xFAn agente usa: mu\xE9velos primero (agents set --bag)."]
|
|
10851
|
+
},
|
|
10852
|
+
spaces: {
|
|
10853
|
+
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.",
|
|
10854
|
+
flags: [
|
|
10855
|
+
["--until DATE", "renew: el nuevo \xABpagado hasta\xBB (AAAA-MM-DD)"],
|
|
10856
|
+
["--note TEXT", "renew: por qu\xE9 (va a la bit\xE1cora)"],
|
|
10857
|
+
["-n, --dry-run", "ense\xF1a qu\xE9 cambia, no cambia nada"],
|
|
10858
|
+
["--yes", "s\xE1ltate la confirmaci\xF3n (obligatorio sin terminal)"]
|
|
10859
|
+
],
|
|
10860
|
+
notes: ["Con suscripci\xF3n de Stripe renew se niega: la fecha la mueve la factura pagada.", "rename cambia s\xF3lo el slug (id y namespace se quedan, as\xED que tablero, Stripe y WhatsApp siguen funcionando); las ligas viejas <slug>.sales dejan de servir."]
|
|
10661
10861
|
}
|
|
10662
10862
|
};
|
|
10663
10863
|
var HELP_ALIASES = { creds: "credentials", board: "cards", conversations: "chat" };
|
|
@@ -10673,11 +10873,13 @@ var HELPS = {
|
|
|
10673
10873
|
"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]]",
|
|
10674
10874
|
"ghosty agents get <agent-id> [--fields name,model,prompt] [--prompt-out FILE]",
|
|
10675
10875
|
"ghosty agents export <agent-id> [--out DIR] # the agent as a file; see ghosty apply --help",
|
|
10676
|
-
"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]",
|
|
10876
|
+
"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]",
|
|
10677
10877
|
"ghosty agents set <agent-id> --prompt-base <base-agent-id> --prompt-overlay overlay.json [--dry-run [--out FILE]] | --prompt-base none",
|
|
10678
10878
|
"ghosty agents doctor <agent-id> [--verbose]",
|
|
10679
10879
|
"ghosty agents docs ls|grant|revoke <agent-id> [doc\u2026] [--write] [--no-deliver] [--like AGENT]",
|
|
10680
10880
|
"ghosty agents docs add <agent-id> --url URL | --file PATH [--name N] [--description TEXT] [--grant read|write|no] [--no-deliver]",
|
|
10881
|
+
"ghosty agents docs replace <agent-id> <doc> --url URL | --file PATH [--note TEXT] # new version: keeps name, access and the agents that use it",
|
|
10882
|
+
"ghosty agents docs history|restore <agent-id> <doc> [n]",
|
|
10681
10883
|
"ghosty agents share <agent-id> <email> [--role editor|admin]",
|
|
10682
10884
|
"ghosty agents unshare <agent-id> <email> [--yes]",
|
|
10683
10885
|
"ghosty agents shares <agent-id>",
|
|
@@ -10685,7 +10887,7 @@ var HELPS = {
|
|
|
10685
10887
|
"ghosty agents box <agent-id> [--check PATH]\u2026 [--contains PATH=TEXT]\u2026 [--recycle]",
|
|
10686
10888
|
"ghosty agents demo enable|disable <agent-id>",
|
|
10687
10889
|
'ghosty agents demo <agent-id> [--slug NAME] [--expires "YYYY-MM-DDTHH:MM"|no] [--turns N] [--per-ip N] [--welcome TEXT] [--starter TEXT] [--chips "a|b|c"] [--sponsor on|off] [--rotate]',
|
|
10688
|
-
"ghosty agents rm <agent-id> [--yes]"
|
|
10890
|
+
"ghosty agents rm <agent-id> [--from-workspace] [--yes]"
|
|
10689
10891
|
],
|
|
10690
10892
|
flags: [
|
|
10691
10893
|
["--fields a,b", "get: return only these fields"],
|
|
@@ -10707,6 +10909,7 @@ var HELPS = {
|
|
|
10707
10909
|
["--no-deliver", "docs grant/add: it can read it but never send it to whoever writes"],
|
|
10708
10910
|
["--url URL | --file PATH", "docs add: where the document comes from (public URL, up to 25 MB, or a local file)"],
|
|
10709
10911
|
["--grant read|write|no", "docs add: access for this agent (default read; no = only upload to the account)"],
|
|
10912
|
+
["--note TEXT", "docs replace/restore: why it changed (shown in docs history)"],
|
|
10710
10913
|
["--role R", "share: editor (configures and uses it) or admin (also shares it); again on a shared email changes the role"],
|
|
10711
10914
|
["--yes", "rm: skip the confirmation (required without a terminal)"],
|
|
10712
10915
|
["--slug NAME", "demo: link name; a 6-char code is appended (/p/NAME-CODE)"],
|
|
@@ -10721,7 +10924,11 @@ var HELPS = {
|
|
|
10721
10924
|
["--check PATH", "box: this file must exist in the machine (repeatable), e.g. a file the new image brings"],
|
|
10722
10925
|
["--contains PATH=TEXT", "box: that file must contain TEXT (a change that adds no files)"],
|
|
10723
10926
|
["--recycle", "box: replace its machines (not mid-turn); the new one starts on the next turn. ACP: use restart"],
|
|
10724
|
-
["--unset-env K1,K2", "set: delete these env variables (--env K= also deletes K)"]
|
|
10927
|
+
["--unset-env K1,K2", "set: delete these env variables (--env K= also deletes K)"],
|
|
10928
|
+
["--tool [ch:]tool=on|off", "set: one tool on/off in one channel (wa:actualizar_identidad=off) or everywhere (crm_buscar=off); comma-separated"],
|
|
10929
|
+
["--bag NAME|none", "set: the token bag of its workspace it spends from; none = the workspace balance (staff only)"],
|
|
10930
|
+
["--avatar URL|FILE", "set: its face in Teams: an https link or a local JPEG/PNG/WebP (stored at img.ghosty.studio)"],
|
|
10931
|
+
["--from-workspace", "rm: turn it off in every Teams where it's active, then delete it (owner or staff)"]
|
|
10725
10932
|
],
|
|
10726
10933
|
examples: [
|
|
10727
10934
|
"ghosty agents doctor cmu7pgbyr0001gee3ruk172mm",
|
|
@@ -10730,6 +10937,8 @@ var HELPS = {
|
|
|
10730
10937
|
'ghosty agents docs grant <id> "Cat\xE1logo 2026.pdf"',
|
|
10731
10938
|
"ghosty agents docs grant <id> precios.xlsx --write --no-deliver",
|
|
10732
10939
|
'ghosty agents docs add <id> --url https://example.com/lista.pdf --description "Precios 2026"',
|
|
10940
|
+
'ghosty agents docs replace <id> "Cat\xE1logo.pdf" --file catalogo-oct.pdf --note "precios de octubre"',
|
|
10941
|
+
'ghosty agents docs history <id> "Cat\xE1logo.pdf" \xB7 ghosty agents docs restore <id> "Cat\xE1logo.pdf" 3',
|
|
10733
10942
|
"ghosty agents create --name Pia --engine claude --workspace ventas --channels teams=off",
|
|
10734
10943
|
"ghosty agents set <id> --toolsets +crm",
|
|
10735
10944
|
"ghosty agents share <id> ana@empresa.com --role admin",
|
|
@@ -10740,7 +10949,10 @@ var HELPS = {
|
|
|
10740
10949
|
"ghosty agents set <pia-id> --prompt-base <tania-id> --prompt-overlay capa.json --dry-run --out /tmp/pia.md",
|
|
10741
10950
|
"ghosty agents demo enable <id>",
|
|
10742
10951
|
'ghosty agents demo <id> --slug guardias --expires "2026-10-15T20:00" --starter "Ens\xE9\xF1ame c\xF3mo empezar"',
|
|
10743
|
-
"ghosty agents box <id> --contains /opt/claude-worker/skills/pdf-doc/render-doc.mjs=pdf-templates"
|
|
10952
|
+
"ghosty agents box <id> --contains /opt/claude-worker/skills/pdf-doc/render-doc.mjs=pdf-templates",
|
|
10953
|
+
"ghosty agents set <id> --tool wa:actualizar_identidad=off,teams:actualizar_identidad=on",
|
|
10954
|
+
'ghosty agents set minighosty --bag "MiniGhosty" --avatar https://www.ghosty.studio/avatars/minighosty.png',
|
|
10955
|
+
"ghosty agents rm fable --from-workspace"
|
|
10744
10956
|
],
|
|
10745
10957
|
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."],
|
|
10746
10958
|
learn: `${REF}#agentes`
|
|
@@ -10826,16 +11038,17 @@ var HELPS = {
|
|
|
10826
11038
|
},
|
|
10827
11039
|
turns: {
|
|
10828
11040
|
summary: "An agent's recent turns: what answered, what failed and why, and where one turn's time went.",
|
|
10829
|
-
usage: ["ghosty turns ls <agent-id> [--errors | --status STATE | --skipped] [--since 1h] [--limit N]", "ghosty turns show <agent-id> [turn-id | last] [--full]"],
|
|
11041
|
+
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]"],
|
|
10830
11042
|
flags: [
|
|
10831
11043
|
["--errors", "only failed turns (same as --status error)"],
|
|
10832
11044
|
["--skipped", "ls: WhatsApp Business customer messages that did NOT open a turn, and why (operator active, records only, paused, reaction, channel off, no balance)"],
|
|
10833
11045
|
["--status S", "running | done | stopped | error"],
|
|
10834
11046
|
["--since T", "30m, 1h, 2d or an ISO date"],
|
|
10835
11047
|
["--limit N", "max rows (default 20, max 500)"],
|
|
10836
|
-
["--full", "show: the whole message that came in and the whole answer (default: first 600 chars)"]
|
|
11048
|
+
["--full", "show: the whole message that came in and the whole answer (default: first 600 chars)"],
|
|
11049
|
+
["--quota-hits", "ls --workspace: messages (WhatsApp Business and Teams) that got no turn because the balance ran out"]
|
|
10837
11050
|
],
|
|
10838
|
-
examples: ["ghosty turns ls <id> --errors --since 1h", "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"],
|
|
11051
|
+
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"],
|
|
10839
11052
|
notes: ["--skipped keeps 30 days and only sees what Formmy forwarded: if Formmy doesn't forward (ghosty channels whatsapp ls, \xABformmy forwards\xBB), gs never gets the message.", "show prints what came in and what went out on any engine or channel (WhatsApp groups too); the timeline reads the worker's transcript, so pool engines only (not ACP/goose)."],
|
|
10840
11053
|
learn: `${REF}#turnos`
|
|
10841
11054
|
},
|
|
@@ -10893,8 +11106,9 @@ var HELPS = {
|
|
|
10893
11106
|
},
|
|
10894
11107
|
schedule: {
|
|
10895
11108
|
summary: "Turns that run later, on the agent's own clock.",
|
|
10896
|
-
usage: ['ghosty schedule add <agent-id> "what to do" --in 30m | --at ISO [--every 12h --until ISO] [--conversation ID] [--title T]', "ghosty schedule ls <agent-id>", "ghosty schedule rm <agent-id> <schedule-id>"],
|
|
10897
|
-
examples: ['ghosty schedule add <id> "revisa pedidos pendientes" --every 12h --in 1h'],
|
|
11109
|
+
usage: ['ghosty schedule add <agent-id> "what to do" --in 30m | --at ISO [--every 12h --until ISO] [--conversation ID] [--title T]', "ghosty schedule ls <agent-id>", "ghosty schedule rm <agent-id> <schedule-id>", 'ghosty schedule add <agent-id> "what to do" --teams <workspace>#<room>[/<thread-id>] --in 30m | --at ISO', "ghosty schedule ls|rm <agent-id> [id] --teams <workspace>"],
|
|
11110
|
+
examples: ['ghosty schedule add <id> "revisa pedidos pendientes" --every 12h --in 1h', 'ghosty schedule add minighosty "Pres\xE9ntate al equipo" --teams descti#general/4812 --at 2026-10-01T09:00 # answers in that thread as if mentioned'],
|
|
11111
|
+
notes: ["--teams: the agent must be active in that workspace's Teams and you must be a member. The thread id is the id of the message that starts it."],
|
|
10898
11112
|
learn: `${REF}#programar-turnos`
|
|
10899
11113
|
},
|
|
10900
11114
|
me: {
|
|
@@ -11084,12 +11298,58 @@ HELPS.board = HELPS.cards;
|
|
|
11084
11298
|
HELPS.conversations = HELPS.chat;
|
|
11085
11299
|
HELPS.usage = {
|
|
11086
11300
|
...HELPS.me,
|
|
11087
|
-
summary: "Your plan and how much you've used this week and month. With --
|
|
11088
|
-
usage: ["ghosty usage", "ghosty me usage", "ghosty usage --boxes [--workspace SLUG]"],
|
|
11089
|
-
flags: [
|
|
11090
|
-
|
|
11301
|
+
summary: "Your plan and how much you've used this week and month. With --workspace: a workspace's turns and billable tokens per agent and/or day. With --boxes: its boxes against its limit.",
|
|
11302
|
+
usage: ["ghosty usage", "ghosty me usage", "ghosty usage --workspace SLUG [--by agent|day|agent,day] [--days 14]", "ghosty usage --boxes [--workspace SLUG]"],
|
|
11303
|
+
flags: [
|
|
11304
|
+
["--boxes", "the workspace's boxes against its limit (spares, asleep) and, per agent: conversations per box, boxes, awake, spares, turns running now"],
|
|
11305
|
+
["--workspace SLUG", "which workspace (owner or staff); with --boxes the default is the one from ghosty use"],
|
|
11306
|
+
["--by agent|day|agent,day", "--workspace: rows per agent (default), per day (CDMX) or both"],
|
|
11307
|
+
["--days N", "--workspace: how far back (default 14, max 92)"]
|
|
11308
|
+
],
|
|
11309
|
+
examples: ["ghosty usage", "ghosty usage --json", "ghosty usage --workspace descti --by agent,day --days 14", "ghosty usage --boxes --workspace siiqtec"],
|
|
11310
|
+
notes: ["failed = turns that ended in error or were stopped. \xABno balance\xBB = messages that got no turn because the bag ran out (ghosty turns ls --workspace X --quota-hits lists them)."],
|
|
11091
11311
|
learn: `${REF}#uso`
|
|
11092
11312
|
};
|
|
11313
|
+
HELPS.engines = {
|
|
11314
|
+
summary: "Engines and models an agent can use: the valid values of --engine and --model, the default, and what each model costs against the balance.",
|
|
11315
|
+
usage: ["ghosty engines ls [--models]"],
|
|
11316
|
+
flags: [["--models", "every model of each engine with its label, cost multiplier and whether it needs your own key"]],
|
|
11317
|
+
examples: ["ghosty engines ls", "ghosty engines ls --models", "ghosty engines ls --json"],
|
|
11318
|
+
learn: `${REF}#agentes`
|
|
11319
|
+
};
|
|
11320
|
+
HELPS.bags = {
|
|
11321
|
+
summary: "A workspace's token bags: a named top that one or more agents spend from. Monthly (refills every period) or one-time (counts from when it was created and never refills). Reading: owner or staff; changing: Ghosty staff.",
|
|
11322
|
+
usage: [
|
|
11323
|
+
"ghosty bags ls <workspace>",
|
|
11324
|
+
'ghosty bags create <workspace> "<bag>" --tokens 20M [--recurring on|off] [--hard-cap on|off] [--note TEXT]',
|
|
11325
|
+
'ghosty bags set <workspace> "<bag>" [--tokens 30M] [--recurring on|off] [--hard-cap on|off] [--note TEXT] [--name NEW]',
|
|
11326
|
+
'ghosty bags rm <workspace> "<bag>" [--yes]'
|
|
11327
|
+
],
|
|
11328
|
+
flags: [
|
|
11329
|
+
["--tokens N", "billable tokens: 20M, 500k or a plain number"],
|
|
11330
|
+
["--recurring on|off", "on = refills every period (default); off = one-time, counts from today"],
|
|
11331
|
+
["--hard-cap on|off", "on = at 100% turns stop with \xABout of balance\xBB (default); off = only counts"],
|
|
11332
|
+
["--note TEXT", "what was agreed and who approved it"],
|
|
11333
|
+
["--name NEW", "set: rename the bag (customers see it in Settings \u2192 Usage)"],
|
|
11334
|
+
["--yes", "rm: skip the confirmation (required without a terminal)"]
|
|
11335
|
+
],
|
|
11336
|
+
examples: ["ghosty bags ls descti", 'ghosty bags create descti "MiniGhosty" --tokens 20M --note "pactado 30-sep"', 'ghosty bags set descti "Astra" --recurring off', 'ghosty agents set minighosty --bag "MiniGhosty"'],
|
|
11337
|
+
notes: ["An agent with no bag spends from the workspace balance. rm only deletes a bag no agent uses: move them first (agents set --bag)."],
|
|
11338
|
+
learn: `${REF}#espacios`
|
|
11339
|
+
};
|
|
11340
|
+
HELPS.spaces = {
|
|
11341
|
+
summary: "Ghosty staff operations on a workspace: move \xABpaid until\xBB and rename its slug. Each one is logged in the workspace's audit.",
|
|
11342
|
+
usage: ["ghosty spaces renew <workspace> --until YYYY-MM-DD [--note TEXT] [--dry-run] [--yes]", "ghosty spaces rename <old> <new> [--dry-run] [--yes]"],
|
|
11343
|
+
flags: [
|
|
11344
|
+
["--until DATE", "renew: the new \xABpaid until\xBB (YYYY-MM-DD)"],
|
|
11345
|
+
["--note TEXT", "renew: why (goes into the audit)"],
|
|
11346
|
+
["-n, --dry-run", "show what changes, change nothing"],
|
|
11347
|
+
["--yes", "skip the confirmation (required without a terminal)"]
|
|
11348
|
+
],
|
|
11349
|
+
examples: ['ghosty spaces renew descti --until 2026-11-01 --note "pag\xF3 octubre"', "ghosty spaces rename totequim siiqtec --dry-run"],
|
|
11350
|
+
notes: ["With a Stripe subscription renew refuses: the paid invoice moves the date.", "rename changes only the slug (id and namespace stay, so board, Stripe and WhatsApp keep working); old <slug>.sales links stop working."],
|
|
11351
|
+
learn: `${REF}#espacios`
|
|
11352
|
+
};
|
|
11093
11353
|
function helpText(cmd) {
|
|
11094
11354
|
const base = HELPS[cmd];
|
|
11095
11355
|
if (!base) return void 0;
|
|
@@ -11467,6 +11727,171 @@ ${table(rows, ["agent", "engine", "per box", "boxes", "on", "spares", "running n
|
|
|
11467
11727
|
${dim(t(`${r.totals.activeTurns} turn(s) running`, `${r.totals.activeTurns} turno(s) corriendo`))}`);
|
|
11468
11728
|
}
|
|
11469
11729
|
|
|
11730
|
+
// src/commands/spaces.ts
|
|
11731
|
+
var enc10 = encodeURIComponent;
|
|
11732
|
+
var wsPath = (slug, rest = "") => `/api/v2/workspaces/${enc10(slug)}${rest}`;
|
|
11733
|
+
var needSpace = (slug, example) => {
|
|
11734
|
+
if (!slug) throw usage(t("Which workspace?", "\xBFQu\xE9 espacio?"), `${t("Example", "Ejemplo")}: ${example} ${t("(or ghosty use <workspace>)", "(o ghosty use <espacio>)")}`);
|
|
11735
|
+
return slug;
|
|
11736
|
+
};
|
|
11737
|
+
function parseTokens(raw) {
|
|
11738
|
+
const m = /^\s*(\d+(?:\.\d+)?)\s*([kKmM]?)\s*$/.exec(raw);
|
|
11739
|
+
if (!m) throw usage(t(`Bad token amount: ${raw}.`, `Cantidad de tokens inv\xE1lida: ${raw}.`), t("Use 20M, 500k or a plain number.", "Usa 20M, 500k o un n\xFAmero."));
|
|
11740
|
+
const n = Number(m[1]) * (m[2].toLowerCase() === "m" ? 1e6 : m[2].toLowerCase() === "k" ? 1e3 : 1);
|
|
11741
|
+
return Math.round(n);
|
|
11742
|
+
}
|
|
11743
|
+
function fmtTokens(n) {
|
|
11744
|
+
if (n >= 1e6) return `${+(n / 1e6).toFixed(n % 1e6 === 0 ? 0 : 1)}M`;
|
|
11745
|
+
if (n >= 1e3) return `${Math.round(n / 1e3)}k`;
|
|
11746
|
+
return String(n);
|
|
11747
|
+
}
|
|
11748
|
+
function parseSwitch(flag, raw) {
|
|
11749
|
+
if (raw === void 0) return void 0;
|
|
11750
|
+
if (/^(on|true|yes|s[ií])$/i.test(raw)) return true;
|
|
11751
|
+
if (/^(off|false|no)$/i.test(raw)) return false;
|
|
11752
|
+
throw usage(t(`${flag} takes on or off.`, `${flag} acepta on u off.`));
|
|
11753
|
+
}
|
|
11754
|
+
async function enginesCmd(sub, v) {
|
|
11755
|
+
if (sub && sub !== "ls" && sub !== "list") throw usage(t(`Unknown engines subcommand: ${sub}.`, `Subcomando de engines desconocido: ${sub}.`), "ghosty engines ls [--models]");
|
|
11756
|
+
const r = await api("/api/v2/engines");
|
|
11757
|
+
out(r, () => {
|
|
11758
|
+
if (!v.models) {
|
|
11759
|
+
const rows = r.engines.map((e) => ({ engine: e.id, provider: e.provider, default: e.defaultModel ?? "", models: e.models.map((m) => m.id).join(", ") }));
|
|
11760
|
+
return `${table(rows, ["engine", "provider", "default"])}
|
|
11761
|
+
${dim(t("Models per engine: ghosty engines ls --models", "Modelos de cada motor: ghosty engines ls --models"))}`;
|
|
11762
|
+
}
|
|
11763
|
+
return r.engines.map((e) => {
|
|
11764
|
+
const rows = e.models.map((m) => ({ model: m.id + (m.id === e.defaultModel ? " *" : ""), label: m.label, cost: `${m.multiplier}\xD7`, note: m.ownKeyOnly ? t("your own key only", "s\xF3lo con llave propia") : "" }));
|
|
11765
|
+
return `${bold(e.id)} ${dim(`${e.provider} \xB7 ${e.protocol}${e.vision ? t(" \xB7 sees images", " \xB7 ve im\xE1genes") : ""}`)}
|
|
11766
|
+
${table(rows, ["model", "label", "cost", "note"])}`;
|
|
11767
|
+
}).join("\n\n") + `
|
|
11768
|
+
${dim(t("* default \xB7 cost = balance multiplier per turn", "* por default \xB7 costo = multiplicador de saldo por turno"))}`;
|
|
11769
|
+
});
|
|
11770
|
+
}
|
|
11771
|
+
function bagsText(r) {
|
|
11772
|
+
const rows = r.bags.map((b) => ({
|
|
11773
|
+
bag: b.name,
|
|
11774
|
+
tokens: fmtTokens(b.tokens),
|
|
11775
|
+
used: `${fmtTokens(b.used)} (${Math.round(b.ratio * 100)}%)`,
|
|
11776
|
+
kind: b.recurring ? t("monthly", "mensual") : t(`one-time since ${b.since?.slice(0, 10)}`, `carga \xFAnica desde ${b.since?.slice(0, 10)}`),
|
|
11777
|
+
cap: b.hardCap ? t("cuts", "corta") : t("warns", "avisa"),
|
|
11778
|
+
agents: b.agents.map((a) => a.name ?? a.id).join(", ") || "\u2014"
|
|
11779
|
+
}));
|
|
11780
|
+
const free = r.unbagged.length ? `
|
|
11781
|
+
${dim(t(`Workspace balance (no bag): ${r.unbagged.map((a) => a.name ?? a.id).join(", ")}`, `Saldo del espacio (sin bolsa): ${r.unbagged.map((a) => a.name ?? a.id).join(", ")}`))}` : "";
|
|
11782
|
+
return `${bold(r.workspace)}
|
|
11783
|
+
${r.bags.length ? table(rows, ["bag", "tokens", "used", "kind", "cap", "agents"]) : dim(t("No bags: every agent uses the workspace balance.", "Sin bolsas: todos los agentes usan el saldo del espacio."))}${free}`;
|
|
11784
|
+
}
|
|
11785
|
+
async function bagsCmd(sub, slug, name, v, context) {
|
|
11786
|
+
const ex = 'ghosty bags ls <workspace> \xB7 create <workspace> "<bag>" --tokens 20M [--recurring off] \xB7 set <workspace> "<bag>" --tokens 30M \xB7 rm <workspace> "<bag>"';
|
|
11787
|
+
if (sub && !["ls", "list", "create", "new", "set", "rm", "delete"].includes(sub)) {
|
|
11788
|
+
name = slug;
|
|
11789
|
+
slug = sub;
|
|
11790
|
+
sub = "ls";
|
|
11791
|
+
}
|
|
11792
|
+
slug = needSpace(slug ?? context, ex);
|
|
11793
|
+
const recurring = parseSwitch("--recurring", v.recurring);
|
|
11794
|
+
const hardCap = parseSwitch("--hard-cap", v["hard-cap"]);
|
|
11795
|
+
switch (sub ?? "ls") {
|
|
11796
|
+
case "ls":
|
|
11797
|
+
case "list": {
|
|
11798
|
+
const r = await api(wsPath(slug, "/bags"));
|
|
11799
|
+
return out(r, () => bagsText(r));
|
|
11800
|
+
}
|
|
11801
|
+
case "create":
|
|
11802
|
+
case "new": {
|
|
11803
|
+
if (!name) throw usage(t("Missing the bag's name.", "Falta el nombre de la bolsa."), ex);
|
|
11804
|
+
if (!v.tokens) throw usage(t("Missing --tokens.", "Falta --tokens."), `ghosty bags create ${slug} "${name}" --tokens 20M`);
|
|
11805
|
+
const r = await api(wsPath(slug, "/bags"), { method: "POST", body: { name, tokens: parseTokens(v.tokens), ...recurring !== void 0 ? { recurring } : {}, ...hardCap !== void 0 ? { hardCap } : {}, ...v.note ? { note: v.note } : {} } });
|
|
11806
|
+
return out(r, () => `${lavender("\u2713")} ${t(`Bag \xAB${name}\xBB created.`, `Bolsa \xAB${name}\xBB creada.`)} ${dim(t(`Give it agents: ghosty agents set <agent> --bag "${name}"`, `Cu\xE9lgale agentes: ghosty agents set <agente> --bag "${name}"`))}
|
|
11807
|
+
${bagsText(r)}`);
|
|
11808
|
+
}
|
|
11809
|
+
case "set": {
|
|
11810
|
+
if (!name) throw usage(t("Missing the bag's name.", "Falta el nombre de la bolsa."), ex);
|
|
11811
|
+
const body = { ...v.tokens ? { tokens: parseTokens(v.tokens) } : {}, ...recurring !== void 0 ? { recurring } : {}, ...hardCap !== void 0 ? { hardCap } : {}, ...v.note !== void 0 ? { note: v.note } : {}, ...v.name ? { name: v.name } : {} };
|
|
11812
|
+
if (!Object.keys(body).length) throw usage(t("Nothing to change.", "Nada que cambiar."), "--tokens 30M | --recurring on|off | --hard-cap on|off | --note TEXT | --name NEW");
|
|
11813
|
+
const r = await api(wsPath(slug, `/bags/${enc10(name)}`), { method: "PATCH", body });
|
|
11814
|
+
return out(r, () => `${lavender("\u2713")} ${t(`Bag \xAB${r.updated}\xBB updated.`, `Bolsa \xAB${r.updated}\xBB actualizada.`)}
|
|
11815
|
+
${bagsText(r)}`);
|
|
11816
|
+
}
|
|
11817
|
+
case "rm":
|
|
11818
|
+
case "delete": {
|
|
11819
|
+
if (!name) throw usage(t("Missing the bag's name.", "Falta el nombre de la bolsa."), ex);
|
|
11820
|
+
requireYesIfHeadless(v.yes, `ghosty bags rm ${slug} "${name}"`);
|
|
11821
|
+
await confirm(t(`Delete bag \xAB${name}\xBB from ${slug}? (only if no agent uses it)`, `\xBFBorrar la bolsa \xAB${name}\xBB de ${slug}? (s\xF3lo si ning\xFAn agente la usa)`), { yes: v.yes, retry: `ghosty bags rm ${slug} "${name}"` });
|
|
11822
|
+
const r = await api(wsPath(slug, `/bags/${enc10(name)}`), { method: "DELETE" });
|
|
11823
|
+
return out(r, () => `${lavender("\u2713")} ${t(`Bag \xAB${r.deleted}\xBB deleted.`, `Bolsa \xAB${r.deleted}\xBB borrada.`)}
|
|
11824
|
+
${bagsText(r)}`);
|
|
11825
|
+
}
|
|
11826
|
+
}
|
|
11827
|
+
}
|
|
11828
|
+
async function spacesCmd(sub, slug, arg, v, context) {
|
|
11829
|
+
const ex = "ghosty spaces renew <workspace> --until 2026-11-01 [--note TEXT] \xB7 rename <old> <new> [--dry-run]";
|
|
11830
|
+
if (sub === "renew") {
|
|
11831
|
+
slug = needSpace(slug ?? context, ex);
|
|
11832
|
+
if (!v.until) throw usage(t("Missing --until YYYY-MM-DD.", "Falta --until AAAA-MM-DD."), `ghosty spaces renew ${slug} --until 2026-11-01`);
|
|
11833
|
+
const path = wsPath(slug, "/renew");
|
|
11834
|
+
const plan = await api(path, { method: "POST", body: { until: v.until, dryRun: true } });
|
|
11835
|
+
const line2 = t(`${plan.workspace} (${plan.plan}): paid until ${plan.from ?? "\u2014"} \u2192 ${plan.to}`, `${plan.workspace} (${plan.plan}): pagado hasta ${plan.from ?? "\u2014"} \u2192 ${plan.to}`);
|
|
11836
|
+
if (v["dry-run"]) return out(plan, () => `${line2}
|
|
11837
|
+
${dim(t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada."))}`);
|
|
11838
|
+
requireYesIfHeadless(v.yes, `ghosty spaces renew ${slug} --until ${v.until}`);
|
|
11839
|
+
await confirm(`${line2}
|
|
11840
|
+
${t("Renew?", "\xBFRenovar?")}`, { yes: v.yes, retry: `ghosty spaces renew ${slug} --until ${v.until}` });
|
|
11841
|
+
const r = await api(path, { method: "POST", body: { until: v.until, ...v.note ? { note: v.note } : {} } });
|
|
11842
|
+
return out(r, () => `${lavender("\u2713")} ${line2} ${dim(t("(logged in the workspace's audit)", "(queda en la bit\xE1cora del espacio)"))}`);
|
|
11843
|
+
}
|
|
11844
|
+
if (sub === "rename") {
|
|
11845
|
+
slug = needSpace(slug, ex);
|
|
11846
|
+
if (!arg) throw usage(t("Missing the new name.", "Falta el nombre nuevo."), `ghosty spaces rename ${slug} <new> --dry-run`);
|
|
11847
|
+
const path = wsPath(slug, "/rename");
|
|
11848
|
+
const plan = await api(path, { method: "POST", body: { to: arg, dryRun: true } });
|
|
11849
|
+
const text = `${plan.from} \u2192 ${bold(plan.to)}
|
|
11850
|
+
${plan.warnings.map((w) => dim(` \xB7 ${serverText(w)}`)).join("\n")}`;
|
|
11851
|
+
if (v["dry-run"]) return out(plan, () => `${text}
|
|
11852
|
+
${dim(t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada."))}`);
|
|
11853
|
+
requireYesIfHeadless(v.yes, `ghosty spaces rename ${slug} ${arg}`);
|
|
11854
|
+
await confirm(`${text}
|
|
11855
|
+
${t("Rename?", "\xBFRenombrar?")}`, { yes: v.yes, expect: plan.from, retry: `ghosty spaces rename ${slug} ${arg}` });
|
|
11856
|
+
const r = await api(path, { method: "POST", body: { to: arg } });
|
|
11857
|
+
return out(r, () => `${lavender("\u2713")} ${t(`Renamed: ${r.from} \u2192 ${r.to}. Undo: ghosty spaces rename ${r.to} ${r.from}`, `Renombrado: ${r.from} \u2192 ${r.to}. Deshacer: ghosty spaces rename ${r.to} ${r.from}`)}`);
|
|
11858
|
+
}
|
|
11859
|
+
throw usage(t(`Unknown spaces subcommand: ${sub ?? "(none)"}.`, `Subcomando de spaces desconocido: ${sub ?? "(ninguno)"}.`), ex);
|
|
11860
|
+
}
|
|
11861
|
+
async function workspaceUsage(slug, v) {
|
|
11862
|
+
slug = needSpace(slug, "ghosty usage --workspace <slug> --by agent,day --days 14");
|
|
11863
|
+
const r = await api(wsPath(slug, `/usage?days=${enc10(v.days ?? "14")}`));
|
|
11864
|
+
const cdmx = (iso) => new Date(Date.parse(iso) - 6 * 36e5).toISOString().slice(5, 16).replace("T", " ");
|
|
11865
|
+
if (v["quota-hits"]) {
|
|
11866
|
+
const rows2 = r.quotaHits.map((q) => ({ at: cdmx(q.at), agent: q.agent, channel: q.channel, from: q.phone ?? q.conversationId.slice(0, 24), message: (q.preview ?? "").slice(0, 60) }));
|
|
11867
|
+
const total2 = r.agents.reduce((n, a) => n + a.noQuota, 0);
|
|
11868
|
+
return out(r.quotaHits, () => rows2.length ? `${table(rows2, ["at", "agent", "channel", "from", "message"])}
|
|
11869
|
+
${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).`)));
|
|
11870
|
+
}
|
|
11871
|
+
const by = new Set((v.by ?? "agent").split(",").map((x) => x.trim()));
|
|
11872
|
+
if ([...by].some((b) => b !== "agent" && b !== "day")) throw usage(t("--by takes agent, day or agent,day.", "--by acepta agent, day o agent,day."));
|
|
11873
|
+
let rows;
|
|
11874
|
+
const fmt2 = (x) => ({ turns: x.turns, failed: x.failed ? red(String(x.failed)) : 0, tokens: fmtTokens(x.billable), "no balance": x.noQuota || "" });
|
|
11875
|
+
if (by.has("day") && by.has("agent")) rows = r.rows.map((x) => ({ day: x.day, agent: x.agent, ...fmt2(x) }));
|
|
11876
|
+
else if (by.has("day")) {
|
|
11877
|
+
const days = /* @__PURE__ */ new Map();
|
|
11878
|
+
for (const x of r.rows) {
|
|
11879
|
+
const d = days.get(x.day) ?? { agentId: "", agent: "", turns: 0, failed: 0, billable: 0, outputTokens: 0, cacheRead: 0, noQuota: 0 };
|
|
11880
|
+
d.turns += x.turns;
|
|
11881
|
+
d.failed += x.failed;
|
|
11882
|
+
d.billable += x.billable;
|
|
11883
|
+
d.noQuota += x.noQuota;
|
|
11884
|
+
days.set(x.day, d);
|
|
11885
|
+
}
|
|
11886
|
+
rows = [...days].map(([day, x]) => ({ day, ...fmt2(x) }));
|
|
11887
|
+
} else rows = r.agents.map((x) => ({ agent: x.agent, ...fmt2(x) }));
|
|
11888
|
+
const cols = [...by.has("day") ? ["day"] : [], ...by.has("agent") ? ["agent"] : [], "turns", "failed", "tokens", "no balance"];
|
|
11889
|
+
const total = r.agents.reduce((n, a) => n + a.billable, 0);
|
|
11890
|
+
out(r, () => `${bold(r.workspace)} ${dim(t(`last ${r.days} day(s), CDMX days`, `\xFAltimos ${r.days} d\xEDa(s), d\xEDas en hora CDMX`))}
|
|
11891
|
+
${table(rows, cols)}
|
|
11892
|
+
${dim(t(`${fmtTokens(total)} billable tokens \xB7 failed = error or stopped \xB7 no balance: ghosty turns ls --workspace ${r.workspace} --quota-hits`, `${fmtTokens(total)} tokens facturables \xB7 failed = error o detenido \xB7 sin saldo: ghosty turns ls --workspace ${r.workspace} --quota-hits`))}`);
|
|
11893
|
+
}
|
|
11894
|
+
|
|
11470
11895
|
// src/index.ts
|
|
11471
11896
|
var HELP_EN = `${bold("Usage:")} ghosty <command> [options]
|
|
11472
11897
|
|
|
@@ -11478,7 +11903,14 @@ ${bold("Session")}
|
|
|
11478
11903
|
use [workspace] Default workspace for boards, agents create and agents ls [--clear]
|
|
11479
11904
|
completion zsh|bash|fish Tab completion for commands, flags, agents and workspaces
|
|
11480
11905
|
usage Your plan and how much you've used this week and month [--boxes: boxes vs the workspace limit]
|
|
11906
|
+
usage --workspace SLUG A workspace's turns and tokens per agent/day [--by agent,day] [--days 14]
|
|
11481
11907
|
plan apply [workspace] Apply a scheduled plan now, showing what changes [--dry-run]
|
|
11908
|
+
engines ls Engines and models valid for --engine/--model [--models]
|
|
11909
|
+
|
|
11910
|
+
${bold("Workspaces")} ${dim("(changing them: Ghosty staff)")}
|
|
11911
|
+
bags ls|create|set|rm <workspace> ["bag"] Token bags its agents spend from [--tokens 20M] [--recurring on|off] [--hard-cap on|off] [--note]
|
|
11912
|
+
spaces renew <workspace> --until DATE Move \xABpaid until\xBB (logged) [--note] [--dry-run]
|
|
11913
|
+
spaces rename <old> <new> Change its slug only [--dry-run]
|
|
11482
11914
|
|
|
11483
11915
|
${bold("Agents")}
|
|
11484
11916
|
agents ls List your agents
|
|
@@ -11494,12 +11926,15 @@ ${bold("Agents")}
|
|
|
11494
11926
|
[--voice elevenlabs:ID | kokoro:em_santa] [--voice-replies auto|off] [--auto-stage on|off]
|
|
11495
11927
|
[--toolsets crm,sales | +crm,-sales] gs tool groups it gets
|
|
11496
11928
|
[--prompt-base AGENT|none --prompt-overlay FILE [--dry-run]] prompt = another agent's + edits, kept in sync
|
|
11929
|
+
[--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
|
|
11497
11930
|
agents doctor <id> Check the config: overlapping skills, tools the prompt names, broken grants, failing turns
|
|
11498
11931
|
agents docs ls|grant|revoke <id> [doc] Documents it reads and may send [--write] [--no-deliver]
|
|
11499
11932
|
agents docs grant <id> A B\u2026 Grant several documents at once [--like AGENT] copies another agent's
|
|
11500
11933
|
agents docs add <id> Upload a document and grant it --url URL | --file PATH [--description] [--grant read|write|no]
|
|
11934
|
+
agents docs replace <id> <doc> New version (keeps name and access) --url URL | --file PATH [--note]
|
|
11935
|
+
agents docs history|restore <id> <doc> [n] Its versions; restore n as a new version
|
|
11501
11936
|
agents share|unshare <id> <email> Share it without handing it over [--role editor|admin] \xB7 agents shares <id> lists them
|
|
11502
|
-
agents rm <id> Delete an agent (owner only; asks you to type its name, or --yes)
|
|
11937
|
+
agents rm <id> Delete an agent (owner only; asks you to type its name, or --yes) [--from-workspace: turn it off in Teams first]
|
|
11503
11938
|
agents restart <id> Restart an ACP agent (needsRestart) so it loads new skills
|
|
11504
11939
|
agents box <id> The machine serving it: is it on the new image? [--check PATH]\u2026 [--contains PATH=TEXT]\u2026 [--recycle]
|
|
11505
11940
|
agents demo enable|disable <id> Turn its public demo link on or off
|
|
@@ -11536,12 +11971,14 @@ ${bold("Talk to an agent")}
|
|
|
11536
11971
|
conversations ls <id> List conversations
|
|
11537
11972
|
conversations show <id> <conversation> What was said [--limit 20] (0 = all)
|
|
11538
11973
|
turns ls <id> Recent turns and why they failed [--errors] [--skipped] [--since 1h] [--limit N]
|
|
11974
|
+
turns ls --workspace SLUG --quota-hits Messages that got no turn because the balance ran out [--days 14]
|
|
11539
11975
|
turns show <id> [turn] What came in, what it answered, and where the time went (default: last) [--full]
|
|
11540
11976
|
|
|
11541
11977
|
${bold("Schedule turns")} ${dim("(each run lands in a conversation and notifies your phone; an answer of exactly OK stays silent)")}
|
|
11542
11978
|
schedule ls <id> Pending turns and recent runs
|
|
11543
11979
|
schedule add <id> "what to do" Schedule a turn --in 30m | --at ISO [--every 12h --until ISO] [--conversation ID] [--title T]
|
|
11544
11980
|
schedule rm <id> <schedule-id> Cancel a pending turn
|
|
11981
|
+
schedule add|ls|rm <id> \u2026 --teams WS#room[/thread] The same in a Teams room or thread: it answers there as if mentioned
|
|
11545
11982
|
|
|
11546
11983
|
${bold("Partners")} ${dim("(give an agent to each of YOUR customers \u2014 see: ghosty partner --help)")}
|
|
11547
11984
|
partner ls | enable | credentials | agent | tenants | audit | try
|
|
@@ -11574,7 +12011,14 @@ ${bold("Sesi\xF3n")}
|
|
|
11574
12011
|
use [espacio] Espacio por default para boards, agents create y agents ls [--clear]
|
|
11575
12012
|
completion zsh|bash|fish Autocompletado con TAB: comandos, opciones, agentes y espacios
|
|
11576
12013
|
usage Tu plan y cu\xE1nto llevas usado esta semana y este mes [--boxes: cajas contra el tope del espacio]
|
|
12014
|
+
usage --workspace SLUG Turnos y tokens de un espacio por agente/d\xEDa [--by agent,day] [--days 14]
|
|
11577
12015
|
plan apply [espacio] Aplica ya un plan programado, ense\xF1ando qu\xE9 cambia [--dry-run]
|
|
12016
|
+
engines ls Motores y modelos v\xE1lidos para --engine/--model [--models]
|
|
12017
|
+
|
|
12018
|
+
${bold("Espacios")} ${dim("(cambiarlos: staff de Ghosty)")}
|
|
12019
|
+
bags ls|create|set|rm <espacio> ["bolsa"] Bolsas de tokens de las que gastan sus agentes [--tokens 20M] [--recurring on|off] [--hard-cap on|off] [--note]
|
|
12020
|
+
spaces renew <espacio> --until FECHA Mueve \xABpagado hasta\xBB (queda en bit\xE1cora) [--note] [--dry-run]
|
|
12021
|
+
spaces rename <viejo> <nuevo> Cambia s\xF3lo su slug [--dry-run]
|
|
11578
12022
|
|
|
11579
12023
|
${bold("Agentes")}
|
|
11580
12024
|
agents ls Lista tus agentes
|
|
@@ -11590,12 +12034,15 @@ ${bold("Agentes")}
|
|
|
11590
12034
|
[--voice elevenlabs:ID | kokoro:em_santa] [--voice-replies auto|off] [--auto-stage on|off]
|
|
11591
12035
|
[--toolsets crm,sales | +crm,-sales] grupos de tools de gs que recibe
|
|
11592
12036
|
[--prompt-base AGENTE|none --prompt-overlay ARCHIVO [--dry-run]] prompt = el de otro agente + ediciones, sincronizado
|
|
12037
|
+
[--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
|
|
11593
12038
|
agents doctor <id> Revisa la configuraci\xF3n: skills encimadas, tools que nombra el prompt, permisos rotos, turnos que fallan
|
|
11594
12039
|
agents docs ls|grant|revoke <id> [doc] Documentos que lee y puede enviar [--write] [--no-deliver]
|
|
11595
12040
|
agents docs grant <id> A B\u2026 Da varios documentos a la vez [--like AGENTE] copia los de otro agente
|
|
11596
12041
|
agents docs add <id> Sube un documento y d\xE1selo --url URL | --file RUTA [--description] [--grant read|write|no]
|
|
12042
|
+
agents docs replace <id> <doc> Versi\xF3n nueva (conserva nombre y accesos) --url URL | --file RUTA [--note]
|
|
12043
|
+
agents docs history|restore <id> <doc> [n] Sus versiones; restaura la n como versi\xF3n nueva
|
|
11597
12044
|
agents share|unshare <id> <correo> Comp\xE1rtelo sin cederlo [--role editor|admin] \xB7 agents shares <id> los lista
|
|
11598
|
-
agents rm <id> Borra un agente (s\xF3lo el due\xF1o; te pide escribir su nombre, o --yes)
|
|
12045
|
+
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]
|
|
11599
12046
|
agents restart <id> Reinicia un agente ACP (needsRestart) para que cargue skills nuevas
|
|
11600
12047
|
agents box <id> La m\xE1quina que lo atiende: \xBFya trae la imagen nueva? [--check RUTA]\u2026 [--contains RUTA=TEXTO]\u2026 [--recycle]
|
|
11601
12048
|
agents demo enable|disable <id> Prende o apaga su liga p\xFAblica de demo
|
|
@@ -11632,12 +12079,14 @@ ${bold("Hablar con un agente")}
|
|
|
11632
12079
|
conversations ls <id> Lista conversaciones
|
|
11633
12080
|
conversations show <id> <conversaci\xF3n> Qu\xE9 se dijo [--limit 20] (0 = todo)
|
|
11634
12081
|
turns ls <id> Turnos recientes y por qu\xE9 fallaron [--errors] [--skipped] [--since 1h] [--limit N]
|
|
12082
|
+
turns ls --workspace SLUG --quota-hits Mensajes que no abrieron turno porque se acab\xF3 el saldo [--days 14]
|
|
11635
12083
|
turns show <id> [turno] Qu\xE9 lleg\xF3, qu\xE9 contest\xF3 y en qu\xE9 se fue el tiempo (default: el \xFAltimo) [--full]
|
|
11636
12084
|
|
|
11637
12085
|
${bold("Turnos programados")} ${dim("(cada corrida cae en una conversaci\xF3n y avisa a tu tel\xE9fono; una respuesta de exactamente OK no avisa)")}
|
|
11638
12086
|
schedule ls <id> Turnos pendientes y corridas recientes
|
|
11639
12087
|
schedule add <id> "qu\xE9 hacer" Programa un turno --in 30m | --at ISO [--every 12h --until ISO] [--conversation ID] [--title T]
|
|
11640
12088
|
schedule rm <id> <schedule-id> Cancela un turno pendiente
|
|
12089
|
+
schedule add|ls|rm <id> \u2026 --teams ESP#room[/hilo] Lo mismo en un room o hilo de Teams: contesta ah\xED como si lo mencionaran
|
|
11641
12090
|
|
|
11642
12091
|
${bold("Partners")} ${dim("(dale un agente a cada uno de TUS clientes \u2014 ver: ghosty partner --help)")}
|
|
11643
12092
|
partner ls | enable | credentials | agent | tenants | audit | try
|
|
@@ -11660,7 +12109,7 @@ ${bold("Opciones globales")}
|
|
|
11660
12109
|
${bold("C\xF3digos de salida")} 0 bien \xB7 1 error de la API \xB7 2 uso incorrecto \xB7 3 sin sesi\xF3n
|
|
11661
12110
|
${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.")}
|
|
11662
12111
|
${dim("Documentaci\xF3n: https://www.ghosty.studio/docs/cli")}`;
|
|
11663
|
-
var COMMANDS = ["login", "logout", "whoami", "usage", "use", "apply", "plan", "completion", "cards", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "partner", "me", "channels", "board", "boards", "credentials", "docs", "help"];
|
|
12112
|
+
var COMMANDS = ["login", "logout", "whoami", "usage", "use", "apply", "plan", "engines", "bags", "spaces", "completion", "cards", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "partner", "me", "channels", "board", "boards", "credentials", "docs", "help"];
|
|
11664
12113
|
var AGENTS_SUBS = ["ls", "create", "get", "set", "export", "doctor", "docs", "share", "unshare", "shares", "rm", "restart", "demo", "box"];
|
|
11665
12114
|
function unknown(what, input, known, helpCmd) {
|
|
11666
12115
|
const s = suggest(input, known);
|
|
@@ -11819,7 +12268,19 @@ async function main() {
|
|
|
11819
12268
|
"no-connect": { type: "boolean" },
|
|
11820
12269
|
cached: { type: "boolean" },
|
|
11821
12270
|
check: { type: "string", multiple: true },
|
|
11822
|
-
contains: { type: "string", multiple: true }
|
|
12271
|
+
contains: { type: "string", multiple: true },
|
|
12272
|
+
tokens: { type: "string" },
|
|
12273
|
+
recurring: { type: "string" },
|
|
12274
|
+
"hard-cap": { type: "string" },
|
|
12275
|
+
note: { type: "string" },
|
|
12276
|
+
bag: { type: "string" },
|
|
12277
|
+
avatar: { type: "string" },
|
|
12278
|
+
tool: { type: "string" },
|
|
12279
|
+
"from-workspace": { type: "boolean" },
|
|
12280
|
+
"quota-hits": { type: "boolean" },
|
|
12281
|
+
by: { type: "string" },
|
|
12282
|
+
models: { type: "boolean" },
|
|
12283
|
+
teams: { type: "string" }
|
|
11823
12284
|
}
|
|
11824
12285
|
});
|
|
11825
12286
|
globals.json = Boolean(v.json);
|
|
@@ -11827,7 +12288,7 @@ async function main() {
|
|
|
11827
12288
|
const tokens = splitTokens(pos, v);
|
|
11828
12289
|
globals.token = tokens.session;
|
|
11829
12290
|
globals.profile = v.profile;
|
|
11830
|
-
if (v.version) return out({ version: "0.
|
|
12291
|
+
if (v.version) return out({ version: "0.27.0" }, () => "0.27.0");
|
|
11831
12292
|
for (const n of normalizeArgs(pos, v)) if (!globals.json) info(dim(n));
|
|
11832
12293
|
const context = (await readConfig()).workspace;
|
|
11833
12294
|
if (!explicitToken()?.startsWith("gat_")) {
|
|
@@ -11904,6 +12365,7 @@ async function main() {
|
|
|
11904
12365
|
if (sub === "photos") return dbsPhotosCmd(a1, a2, pos[4], v);
|
|
11905
12366
|
return dbsCmd(sub, a1, a2, pos.slice(4).filter((x) => x !== void 0), v);
|
|
11906
12367
|
case "turns":
|
|
12368
|
+
if (v["quota-hits"]) return workspaceUsage(v.workspace ?? context, v);
|
|
11907
12369
|
return turnsCmd(sub, a1, a2, v);
|
|
11908
12370
|
case "board":
|
|
11909
12371
|
return boardCmd(sub, a1, v, a2);
|
|
@@ -11929,7 +12391,16 @@ async function main() {
|
|
|
11929
12391
|
return partnerCmd(sub, a1, a2, { ...v, token: tokens.turn });
|
|
11930
12392
|
case "usage":
|
|
11931
12393
|
if (v.boxes) return boxesUsage(v.workspace ?? context);
|
|
12394
|
+
if (v.workspace || v.by || v.days) return workspaceUsage(v.workspace ?? context, v);
|
|
11932
12395
|
return meUsage();
|
|
12396
|
+
case "engines":
|
|
12397
|
+
return enginesCmd(sub, v);
|
|
12398
|
+
case "bags":
|
|
12399
|
+
case "bag":
|
|
12400
|
+
return bagsCmd(sub, a1, a2, v, context);
|
|
12401
|
+
case "spaces":
|
|
12402
|
+
case "space":
|
|
12403
|
+
return spacesCmd(sub, a1, a2, v, context);
|
|
11933
12404
|
case "plan":
|
|
11934
12405
|
return planCmd(sub, a1, v, context);
|
|
11935
12406
|
case "me":
|
|
@@ -11946,7 +12417,7 @@ async function main() {
|
|
|
11946
12417
|
throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
|
|
11947
12418
|
}
|
|
11948
12419
|
}
|
|
11949
|
-
var notice = updateNotice("0.
|
|
12420
|
+
var notice = updateNotice("0.27.0").catch(() => null);
|
|
11950
12421
|
main().then(
|
|
11951
12422
|
async () => {
|
|
11952
12423
|
const n = await notice;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ghostystudio/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"description": "Official Ghosty Studio CLI — configure, test and chat with your agents from the terminal. Built for humans and coding agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"scripts": {
|
|
17
17
|
"build": "esbuild src/index.ts --bundle --platform=node --format=esm --target=node22 --outfile=dist/index.js --banner:js='#!/usr/bin/env node\nimport { createRequire as __cr } from \"node:module\"; const require = __cr(import.meta.url);' --define:__VERSION__=\\\"$npm_package_version\\\"",
|
|
18
18
|
"prepack": "npm run build",
|
|
19
|
-
"test": "for t in flags prompt api rows wa-auth resolve aliases i18n help completion photos channels-retry; do esbuild src/$t.test.ts --bundle --platform=node --format=esm --outfile=/tmp/ghosty-cli-$t.test.mjs --log-level=error || exit 1; done && node --test /tmp/ghosty-cli-*.test.mjs"
|
|
19
|
+
"test": "for t in flags prompt api rows wa-auth resolve aliases i18n help completion photos channels-retry spaces; do esbuild src/$t.test.ts --bundle --platform=node --format=esm --outfile=/tmp/ghosty-cli-$t.test.mjs --log-level=error || exit 1; done && node --test /tmp/ghosty-cli-*.test.mjs"
|
|
20
20
|
},
|
|
21
21
|
"keywords": [
|
|
22
22
|
"ghosty",
|