@ghostystudio/cli 0.100.0 → 0.103.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.
Files changed (3) hide show
  1. package/README.md +10 -3
  2. package/dist/index.js +85 -26
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -64,7 +64,7 @@ ghosty files ls|get|put|rm <id> [path] [--file LOCAL] [--out LOCAL]
64
64
  ghosty skills ls|get|add|rm <id> [slug] [--file SKILL.md | --dir FOLDER] [--out DIR] # get without --out only lists
65
65
  ghosty skills enable|disable <id> <slug> # turn one of its skills (or one of Ghosty's) on or off
66
66
  ghosty skills templates <id> # pdf-doc templates: its own and Ghosty's, and which folder wins
67
- ghosty dbs ls|tables|query|export|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]
67
+ ghosty dbs ls|create|tables|query|export|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]
68
68
  ghosty turns ls <id> [--errors | --status running|done|stopped|error] [--since 1h] [--limit N]
69
69
  ghosty turns ls --workspace SLUG --quota-hits [--days 14] # messages that got no turn because the balance ran out
70
70
  ghosty turns show <id> [turn-id|last] # what came in, what it answered, where the time went [--full]
@@ -79,7 +79,7 @@ ghosty channels messenger on|off <id> <page> [--take] # this agent answers the
79
79
  ghosty channels mteams connect <id> --app-id <guid> --tenant <guid> # Microsoft Teams bot (secret in GHOSTY_MTEAMS_SECRET); ls shows endpoint and last problem
80
80
  ghosty channels wa import <id> --from-file F | --from-dir DIR --own-number yes|no [--dry-run] # personal WhatsApp session, no QR
81
81
  ghosty channels wa pair|status|pause <id> [--phone N]
82
- ghosty channels wa groups ls|enable|disable|set|create <id> [group] [--role equipo|cliente] [--wake mention|all] [--dry-run] # personal WhatsApp groups
82
+ ghosty channels wa groups ls|enable|disable|set|create <id> [group] [--role equipo|cliente] [--wake mention|name|all] [--dry-run] # personal WhatsApp groups
83
83
  ghosty boards ls|create|assign|move-cards|access|rm <workspace|agent> … [--columns-from main] [--from A --to B --integration X --dry-run] [--add|--remove EMAIL --until 14d]
84
84
  ghosty spaces members ls|add|rm <workspace> [email] [--role member|admin] [--until 2026-10-28|14d]
85
85
  ghosty access why <email> <agent-id> | --workspace W --board B
@@ -157,6 +157,12 @@ Every command has its own help with examples: `ghosty <command> --help` or `ghos
157
157
 
158
158
  Exit codes: `0` ok · `1` API error · `2` usage error · `3` not signed in.
159
159
 
160
+ ### Changes in 0.103.0
161
+
162
+ - `agents set --quote catalogo_base=DB:TABLE`: the house `cotizar` tool reads its catalog from a table of a granted database, so the owner edits prices on the web, by CSV or by chatting with the agent (`catalogo_base=` goes back to the `--catalog` list, which stays as the fallback).
163
+ - New `dbs create <id> <name> [--description] [--read] [--no-grant]` and `dbs import <id> <db> --from csv:FILE --table T [--append] [--dry-run]`.
164
+ - `agents set --quote plantilla=@quote.html,logo=…,comercial=…,numeros=es`: your own PDF template for quotes.
165
+
160
166
  ### Changes in 0.100.0
161
167
 
162
168
  - `app ls | suggest <build> | min <build> --yes [--platform ios|android|notch]` (staff): the apps' version gates without opening /admin/settings. `suggest` raises the dismissible "update" notice (do it once the build is live in the store); `min` stops older builds and asks for confirmation; `min` can never be above `suggest`. Only that number changes; the emergency flags stay in the panel. Logged as `platform.app-config`.
@@ -297,6 +303,7 @@ Exit codes: `0` ok · `1` API error · `2` usage error · `3` not signed in.
297
303
 
298
304
  ### Changes in 0.57.1 (0.57.0 + the 0.56.1 factory fix)
299
305
 
306
+ - `sites rm <slug> [--yes]`: delete one of your sites (stops being served, files removed, slot freed if pinned; can't be undone).
300
307
  - `sites pin <slug>` / `sites unpin <slug>` / `sites pins [--cancel|--resume]`: «Pinned sites» — $49 USD/yr or $5 USD/mo ($990 / $99 MXN), includes up to 3 sites per pack; a pinned site never pauses and has no banner. `--interval year|month`, `--currency usd|mxn`, `--add-pack`.
301
308
  - `sites ls` shows each site's state (pinned, paused, pauses on…).
302
309
 
@@ -407,7 +414,7 @@ Exit codes: `0` ok · `1` API error · `2` usage error · `3` not signed in.
407
414
  - `agents ls --user --app` also lists their phones with the app build. `novedades ls` shows how many FCM delivered and rejected; announcement pushes show up in `push log`.
408
415
  - `novedades create|set --min-version N`: if the app is older, its button reads «Actualizar». `turns stats` breaks down by channel (Teams and A2A included).
409
416
  - `schedule add --conversation wa:<jid>` answers in that WhatsApp group/chat. `channels wa pair` warns that in Mexico `52…` and `521…` are different accounts.
410
- - `channels wa groups set <agent> <group> --wake mention|all`: in `mention` (default when you turn a group on) the agent only answers when @-mentioned, named or replied to; the rest reaches it as context. `ls` shows the column.
417
+ - `channels wa groups set <agent> <group> --wake mention|name|all`: in `mention` (default when you turn a group on) the agent only answers when @-mentioned, named or replied to; the rest reaches it as context. `ls` shows the column. `--alias "tilin"` adds other names that wake it in that group (comma-separated, `""` clears).
411
418
  - `channels wa groups enable|disable` now work (they were in the help but got rejected).
412
419
 
413
420
  ### Changes in 0.31.0
package/dist/index.js CHANGED
@@ -1190,7 +1190,7 @@ async function postLong(path2, body, timeoutMs = 9e5) {
1190
1190
  const req = httpsRequest(`${base}${path2}`, {
1191
1191
  method: "POST",
1192
1192
  timeout: timeoutMs,
1193
- headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.100.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
1193
+ headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.103.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
1194
1194
  }, (res) => {
1195
1195
  let buf = "";
1196
1196
  res.on("data", (c) => buf += c);
@@ -1232,7 +1232,7 @@ async function request(path2, opts = {}, retried = false) {
1232
1232
  method: opts.method ?? (opts.body !== void 0 || opts.raw !== void 0 ? "POST" : "GET"),
1233
1233
  headers: {
1234
1234
  authorization: `Bearer ${token}`,
1235
- "user-agent": `ghosty-cli/${"0.100.0"}`,
1235
+ "user-agent": `ghosty-cli/${"0.103.0"}`,
1236
1236
  "accept-language": lang(),
1237
1237
  ...opts.body !== void 0 ? { "content-type": "application/json" } : {},
1238
1238
  ...opts.headers
@@ -9920,11 +9920,11 @@ ${dim(t(`${r3.beforeChars} \u2192 ${r3.chars} chars. Dry run: nothing changed.`,
9920
9920
  const emailAny = parseSwitch("--email-any", v["email-any"]);
9921
9921
  if (emailAny !== void 0) body.email = { anyFromClients: emailAny };
9922
9922
  if (v.quote) {
9923
- const alias = { iva: "ivaPct", vigencia: "vigenciaDias", prefix: "folioPrefijo", prefijo: "folioPrefijo", currency: "moneda", company: "empresa", phone: "telefono", email: "correo", address: "direccion", notes: "notas", "iva-incluido": "ivaIncluido", "solo-factura": "ivaSoloConFactura" };
9923
+ const alias = { "catalogo-base": "catalogoBase", catalogo_base: "catalogoBase", "catalog-db": "catalogoBase", catalog_db: "catalogoBase", iva: "ivaPct", vigencia: "vigenciaDias", prefix: "folioPrefijo", prefijo: "folioPrefijo", currency: "moneda", company: "empresa", phone: "telefono", email: "correo", address: "direccion", notes: "notas", "iva-incluido": "ivaIncluido", "solo-factura": "ivaSoloConFactura", template: "plantilla", numbers: "numeros", seller: "comercial" };
9924
9924
  const cot = {};
9925
9925
  let cats = null;
9926
- for (const part of v.quote.split(/,(?=\s*[a-zA-Z-]+(?:\.[^=,]+)?=)/)) {
9927
- const m = /^\s*([a-zA-Z-]+)(?:\.([^=]+))?=(.*)$/.exec(part);
9926
+ for (const part of v.quote.split(/,(?=\s*[a-zA-Z_-]+(?:\.[^=,]+)?=)/)) {
9927
+ const m = /^\s*([a-zA-Z_-]+)(?:\.([^=]+))?=(.*)$/.exec(part);
9928
9928
  if (!m) throw usage(t(`Bad --quote: ${part}.`, `--quote inv\xE1lido: ${part}.`), "--quote iva=0,prefix=SFI,vigencia=15");
9929
9929
  const [, rawKey, category, rawVal] = m;
9930
9930
  const val = rawVal.trim().replace(/^"(.*)"$/, "$1");
@@ -9935,6 +9935,10 @@ ${dim(t(`${r3.beforeChars} \u2192 ${r3.chars} chars. Dry run: nothing changed.`,
9935
9935
  continue;
9936
9936
  }
9937
9937
  const key = alias[rawKey] ?? rawKey;
9938
+ if (key === "plantilla" && val.startsWith("@")) {
9939
+ cot[key] = await readFile3(resolve(val.slice(1)), "utf8");
9940
+ continue;
9941
+ }
9938
9942
  cot[key] = key === "ivaPct" || key === "vigenciaDias" ? Number(val) : key === "ivaIncluido" || key === "ivaSoloConFactura" ? /^(true|on|yes|si|sí|1)$/i.test(val) : val;
9939
9943
  }
9940
9944
  if (cats) cot.ivaPorCategoria = cats;
@@ -11293,7 +11297,7 @@ async function call(path2, v, init = {}) {
11293
11297
  "x-ghosty-ts": ts,
11294
11298
  "x-ghosty-sig": sig,
11295
11299
  "content-type": "application/json",
11296
- "user-agent": `ghosty-cli/${"0.100.0"}`
11300
+ "user-agent": `ghosty-cli/${"0.103.0"}`
11297
11301
  },
11298
11302
  body: method === "GET" ? void 0 : raw
11299
11303
  });
@@ -11909,8 +11913,29 @@ ${dim(t("Is the source the right database? Leave those tables out with --tables.
11909
11913
  function planText(plan) {
11910
11914
  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");
11911
11915
  }
11916
+ async function dbsImportCsv(id, db, file, v) {
11917
+ const ex = "ghosty dbs import <agent-id> <db> --from csv:products.csv --table products [--append] [--dry-run]";
11918
+ if (!v.table) throw usage(t("Missing --table.", "Falta --table."), `${t("Example", "Ejemplo")}: ${ex}`);
11919
+ const csvText = await readFile6(file, "utf8");
11920
+ const body = { from: "csv", table: v.table, csv: csvText, mode: v.append ? "append" : "replace" };
11921
+ const path2 = agentPath2(id, `/dbs/${enc9(db)}/import`);
11922
+ const dry = await api(path2, { body: { ...body, dryRun: true } });
11923
+ const cols = dim(` ${t("columns", "columnas")}: ${dry.columns.join(", ")}`);
11924
+ if (v["dry-run"]) return out(dry, () => `${dry.db} \u2190 ${basename3(file)}
11925
+ ${planText(dry.plan)}
11926
+ ${cols}
11927
+ ${dim(t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada."))}`);
11928
+ if (dry.plan.some((p) => p.action === "replace" && p.targetRows > 0)) await confirm(`${planText(dry.plan)}${shrinkWarnings(dry.plan)}
11929
+ ${t(`Replace the rows of ${v.table} in ${dry.db}?`, `\xBFReemplazar las filas de ${v.table} en ${dry.db}?`)}`, { yes: v.yes, retry: ex });
11930
+ const r = await api(path2, { body });
11931
+ return out(r, () => `${planText(r.plan)}
11932
+ ${cols}
11933
+ ${lavender("\u2713")} ${t(`${r.imported} row(s) imported into ${r.db}.${v.table}${r.created ? " (new table)" : ""}.`, `${r.imported} fila(s) importadas en ${r.db}.${v.table}${r.created ? " (tabla nueva)" : ""}.`)}`);
11934
+ }
11912
11935
  async function dbsImport(id, db, v) {
11913
- const ex = "ghosty dbs import <agent-id> <db> --from easybits:<db-id> [--tables a,b] [--dry-run]";
11936
+ const ex = "ghosty dbs import <agent-id> <db> --from easybits:<db-id> [--tables a,b] [--dry-run] | --from csv:file.csv --table T";
11937
+ const csvFile = /^csv:(.+)$/.exec(v.from ?? "");
11938
+ if (csvFile) return dbsImportCsv(id, db, csvFile[1], v);
11914
11939
  const m = /^easybits:(.+)$/.exec(v.from ?? "");
11915
11940
  if (!m) throw usage(t("Missing or bad --from.", "Falta --from o no es v\xE1lido."), `${t("Example", "Ejemplo")}: ${ex}`);
11916
11941
  const easybitsKey = process.env.EASYBITS_API_KEY;
@@ -12029,6 +12054,13 @@ async function dbsCmd(sub, id, db, rest, v) {
12029
12054
  ${dim(`${t("Delete one", "Borrar una")}: ghosty dbs rm ${id} <db> --dry-run`)}` : dim(t("No orphan databases: every one is granted to some agent.", "Sin bases hu\xE9rfanas: todas est\xE1n concedidas a alg\xFAn agente.")));
12030
12055
  return out(dbs, () => rows.length ? table(rows, ["name", "access", "agents", "writes from customers", "own rows", "id"]) : dim(t("This account has no databases yet: create one at /app/dbs.", "Esta cuenta todav\xEDa no tiene bases: crea una en /app/dbs.")));
12031
12056
  }
12057
+ case "create":
12058
+ case "new": {
12059
+ const name = need2(db, t("database name", "el nombre de la base"), "ghosty dbs create <agent-id> <name> [--description TEXT] [--read]");
12060
+ const r = await api(agentPath2(id, "/dbs"), { body: { name, ...v.description ? { description: v.description } : {}, grant: v["no-grant"] ? false : v.read ? "read" : "write" } });
12061
+ return out(r, () => `${lavender("\u2713")} ${t(`Database ${bold(r.name)} created${r.granted ? " and granted to the agent" : ""}.`, `Base ${bold(r.name)} creada${r.granted ? " y concedida al agente" : ""}.`)}
12062
+ ${dim(`${t("Fill it", "Ll\xE9nala")}: ghosty dbs import ${id} ${r.name} --from csv:file.csv --table T`)}`);
12063
+ }
12032
12064
  case "tables": {
12033
12065
  db = need2(db, t("database (id or name)", "la base (id o nombre)"), "ghosty dbs tables <agent-id> <db>");
12034
12066
  const r = await api(agentPath2(id, `/dbs/${enc9(db)}`));
@@ -12800,15 +12832,15 @@ async function groupsCmd(args, v) {
12800
12832
  const sub = rawSub === "enable" || rawSub === "disable" ? "set" : rawSub;
12801
12833
  if (rawSub === "enable") v = { ...v, on: true };
12802
12834
  if (rawSub === "disable") v = { ...v, off: true };
12803
- const ex = 'ghosty channels wa groups ls <agent-id> | set <agent-id> <group> [--role equipo|cliente] [--wake mention|all] [--on|--off] [--name "New name"] | create <agent-id> "Name" | inject <agent-id> <group> --text "\u2026" [--sender NAME]';
12835
+ const ex = 'ghosty channels wa groups ls <agent-id> | set <agent-id> <group> [--role equipo|cliente] [--wake mention|name|all] [--alias "tilin,ghos"] [--on|--off] [--name "New name"] | create <agent-id> "Name" | inject <agent-id> <group> --text "\u2026" [--sender NAME]';
12804
12836
  if (!id) throw usage(t("Missing agent id.", "Falta el id del agente."), `${t("Example", "Ejemplo")}: ${ex}`);
12805
12837
  const path2 = `/api/v2/agents/${enc11(id)}/wa/groups`;
12806
12838
  if (v.role !== void 0 && v.role !== "equipo" && v.role !== "cliente") throw usage(t("--role takes equipo or cliente.", "--role acepta equipo o cliente."));
12807
- if (v.wake !== void 0 && v.wake !== "mention" && v.wake !== "all") throw usage(t("--wake takes mention or all.", "--wake acepta mention o all."));
12839
+ if (v.wake !== void 0 && v.wake !== "mention" && v.wake !== "name" && v.wake !== "all") throw usage(t("--wake takes mention, name or all.", "--wake acepta mention, name o all."));
12808
12840
  if (!sub || sub === "ls" || sub === "list") {
12809
12841
  const r2 = await api(`${path2}${v.cached ? "?live=0" : ""}`);
12810
- const rows = r2.groups.map((g2) => ({ name: g2.name, on: g2.enabled ? lavender("on") : dim("off"), role: g2.role, wake: g2.wake ?? "all", jid: g2.jid }));
12811
- return out(r2, () => rows.length ? table(rows, ["name", "on", "role", "wake", "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"')));
12842
+ const rows = r2.groups.map((g2) => ({ name: g2.name, on: g2.enabled ? lavender("on") : dim("off"), role: g2.role, wake: g2.wake ?? "all", alias: (g2.alias ?? []).join(", "), jid: g2.jid }));
12843
+ return out(r2, () => rows.length ? table(rows, ["name", "on", "role", "wake", "alias", "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"')));
12812
12844
  }
12813
12845
  if (sub === "create") {
12814
12846
  const name2 = rest.join(" ").trim();
@@ -12835,16 +12867,18 @@ async function groupsCmd(args, v) {
12835
12867
  const role = v.role ?? g.role;
12836
12868
  const name = v.name?.trim() || void 0;
12837
12869
  const wake = v.wake;
12838
- const changes = [...wake && wake !== g.wake ? [`wake ${g.wake ?? "all"}\u2192${wake}`] : [], ...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`] : []];
12870
+ const alias = v.alias === void 0 ? void 0 : v.alias.split(",").map((x) => x.trim()).filter(Boolean);
12871
+ const aliasChanged = alias !== void 0 && alias.join(",") !== (g.alias ?? []).join(",");
12872
+ const changes = [...aliasChanged ? [`alias [${(g.alias ?? []).join(", ")}]\u2192[${alias.join(", ")}]`] : [], ...wake && wake !== g.wake ? [`wake ${g.wake ?? "all"}\u2192${wake}`] : [], ...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`] : []];
12839
12873
  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}.`)));
12840
12874
  const plan = `~ ${g.name}: ${changes.join(", ")}`;
12841
12875
  if (v["dry-run"]) return out({ dryRun: true, plan: [plan] }, () => `${plan}
12842
12876
  ${dim(t("Dry run: nothing changed.", "Simulacro: no se cambi\xF3 nada."))}`);
12843
12877
  if (enabled && !g.enabled) await confirm(`${bold(plan)}
12844
12878
  ${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` });
12845
- const r = await api(path2, { method: "POST", body: { jid: g.jid, enabled, role, ...wake ? { wake } : {}, ...name ? { name } : {} } });
12879
+ const r = await api(path2, { method: "POST", body: { jid: g.jid, enabled, role, ...wake ? { wake } : {}, ...alias ? { alias } : {}, ...name ? { name } : {} } });
12846
12880
  const now = r.groups.find((x) => x.jid === g.jid);
12847
- return out({ group: now ?? null, plan: [plan] }, () => `${lavender("\u2713")} ${now?.name ?? g.name}: ${now?.enabled ? "on" : "off"} ${t("as", "como")} ${now?.role ?? role} \xB7 wake ${now?.wake ?? wake ?? "all"}`);
12881
+ return out({ group: now ?? null, plan: [plan] }, () => `${lavender("\u2713")} ${now?.name ?? g.name}: ${now?.enabled ? "on" : "off"} ${t("as", "como")} ${now?.role ?? role} \xB7 wake ${now?.wake ?? wake ?? "all"}${now?.alias?.length ? ` \xB7 alias ${now.alias.join(", ")}` : ""}`);
12848
12882
  }
12849
12883
  var EB_EXPORT = (agentId) => `${(process.env.EASYBITS_URL ?? "https://www.easybits.cloud").replace(/\/+$/, "")}/api/v2/fleet-agents/${enc11(agentId)}/baileys/export`;
12850
12884
  async function easybitsSession(agentId, keys) {
@@ -13514,7 +13548,7 @@ var HELPS_ES = {
13514
13548
  ["--channel-roles k=role,\u2026", "set: qui\xE9n habla en teams y en programado (turnos programados): equipo (escribe en bases, tools admin) o cliente. chat siempre es el equipo; WhatsApp Business, Messenger, demo y widget son clientes; el WhatsApp personal va por grupo (channels wa groups set --role)"],
13515
13549
  ["--extensions name=on|off,\u2026", "set: prende o apaga extensiones del config.yaml de su caja (developer, memory\u2026; Ghosty \xB7 Lite y Goose); se funden con lo guardado. L\xEDstalas: get (configuraci\xF3n completa)"],
13516
13550
  ["--email-any on|off", "set: on = enviar_correo puede escribir a CUALQUIER direcci\xF3n desde canales de cliente; off (default) = s\xF3lo a quien escribe. S\xF3lo el due\xF1o"],
13517
- ["--quote k=v,\u2026", "set: config de la tool de casa `cotizar` \u2014 iva (0-100), iva-incluido=true (los precios del cat\xE1logo ya traen IVA), iva.CATEGORIA=16 (IVA de esa categor\xEDa del cat\xE1logo; iva.CATEGORIA= la quita), solo-factura=true (IVA s\xF3lo si el cliente pide factura), vigencia (d\xEDas), prefix (folio), moneda, empresa, rfc, telefono, correo, direccion, notas, color. Vac\xEDo lo borra (rfc=). El contador de folio no se toca"],
13551
+ ["--quote k=v,\u2026", "set: config de la tool de casa `cotizar` \u2014 iva (0-100), iva-incluido=true (los precios del cat\xE1logo ya traen IVA), iva.CATEGORIA=16 (IVA de esa categor\xEDa del cat\xE1logo; iva.CATEGORIA= la quita), solo-factura=true (IVA s\xF3lo si el cliente pide factura), vigencia (d\xEDas), prefix (folio), moneda, empresa, rfc, telefono, correo, direccion, notas, color. Vac\xEDo lo borra (rfc=). El contador de folio no se toca \xB7 plantilla=@cotizacion.html (tu propia plantilla del PDF: {{folio}}, {{cliente}}, {{#items}}\u2026{{/items}}), logo=https://\u2026, comercial=NOMBRE, numeros=es (1.234,56) \xB7 catalogo_base=BASE:TABLA (el cat\xE1logo sale de esa tabla de una base concedida, editable en la web o platicando con el agente; catalogo_base= regresa a la lista de la ficha)"],
13518
13552
  ["--catalog FILE", "set: el cat\xE1logo de la tool de casa `cotizar` desde un CSV (sku,nombre,precio[,unidad,categoria,descripcion]) o una lista JSON; reemplaza al anterior. Su `categoria` decide las reglas de IVA"],
13519
13553
  ["--verbose", "doctor: lista tambi\xE9n las revisiones que pasaron"],
13520
13554
  ["--user EMAIL", "doctor (staff de Ghosty): los agentes de esa persona \u2014 doctor, \xFAltimos turnos (estado, segundos, error y \xABfetched\xBB: si su app pidi\xF3 el hilo despu\xE9s de que termin\xF3 el turno), plan/bolsa y cajas (marca duplicadas); queda en bit\xE1cora"],
@@ -13838,7 +13872,8 @@ var HELPS_ES = {
13838
13872
  ["-n, --dry-run", "imprime el plan (+ empieza a contestar, - deja de contestar) y no cambia nada"],
13839
13873
  ["--yes", "enable / groups enable: se salta la confirmaci\xF3n (obligatorio sin terminal)"],
13840
13874
  ["--role R", "groups: equipo (el equipo: escribe en bases de datos, tools de admin) o cliente (como cualquier cliente)"],
13841
- ["--wake mention|all", "groups set/enable: contesta todo, o s\xF3lo cuando lo mencionan o le responden"],
13875
+ ["--wake mention|name|all", "groups set/enable: contesta todo (all), s\xF3lo si lo mencionan con @, lo nombran o le responden (mention), o s\xF3lo si escriben su nombre o un alias (name: sin @ ni respuestas)"],
13876
+ ['--alias "a,b"', 'groups set: otros nombres que lo despiertan en ese grupo adem\xE1s del suyo (el apodo con que le dicen ah\xED); "" los quita'],
13842
13877
  ["--text T / --sender N", "groups inject (staff): un mensaje que se perdi\xF3 (grupo apagado, deploy) entra como si N lo hubiera escrito ah\xED, y el agente contesta en el grupo"],
13843
13878
  ["--cached", "groups ls: no le pide a WhatsApp la lista en vivo (m\xE1s r\xE1pido; puede faltar alg\xFAn grupo nuevo)"],
13844
13879
  ["--ads-only on|off", "link: s\xF3lo las conversaciones que vienen de un anuncio abren tarjeta (tambi\xE9n solo, para cambiarlo en un n\xFAmero ya ligado)"],
@@ -14045,6 +14080,7 @@ var HELPS_ES = {
14045
14080
  ["--interval I", "pin: year (default, 2 meses gratis) o month \u2014 el periodo del cobro si hay que pagar"],
14046
14081
  ["--currency C", "pin: usd (default) o mxn \u2014 la moneda del cobro si hay que pagar"],
14047
14082
  ["--add-pack", "pin: ya usas tus lugares \u2014 suma 3 m\xE1s (mismo periodo y moneda de tu suscripci\xF3n) y fija \xE9ste"],
14083
+ ["--yes", "rm: sin confirmaci\xF3n (obligatorio sin terminal)"],
14048
14084
  ["--cancel / --resume", "pins: cancela al final del periodo (los sitios vuelven a Gratis con aviso), o lo deshace"],
14049
14085
  ["--days N", "search: la ventana de Search Console en d\xEDas (default 28; Google reporta con 2\u20133 d\xEDas de retraso)"],
14050
14086
  ["--since S / --kind K / --req-id ID", "errors: ventana (30m, 6h, 7d; default 24h), un tipo de evento, o todo lo de una petici\xF3n (su x-request-id)"],
@@ -14271,7 +14307,7 @@ var HELPS = {
14271
14307
  ["--channel-roles k=role,\u2026", "set: who talks on teams and programado (scheduled turns): equipo (team: writes to databases, admin tools) or cliente (customer). chat is always the team; WhatsApp Business, Messenger, demo and widget are customers; personal WhatsApp goes per group (channels wa groups set --role)"],
14272
14308
  ["--extensions name=on|off,\u2026", "set: turn its box's config.yaml extensions on or off (developer, memory\u2026; Ghosty \xB7 Lite and Goose); merged with what's saved. List them: get (full config)"],
14273
14309
  ["--email-any on|off", "set: on = enviar_correo may write to ANY address from customer channels; off (default) = only to the person writing. Owner only"],
14274
- ["--quote k=v,\u2026", "set: config of the house `cotizar` tool \u2014 iva (0-100), iva-incluido=true (catalog prices already include VAT), iva.CATEGORY=16 (VAT for that catalog category; iva.CATEGORY= removes it), solo-factura=true (VAT only when the customer asks for an invoice), vigencia (days), prefix (folio), moneda, empresa, rfc, telefono, correo, direccion, notas, color. Empty value clears it (rfc=). The folio counter is never touched"],
14310
+ ["--quote k=v,\u2026", "set: config of the house `cotizar` tool \u2014 iva (0-100), iva-incluido=true (catalog prices already include VAT), iva.CATEGORY=16 (VAT for that catalog category; iva.CATEGORY= removes it), solo-factura=true (VAT only when the customer asks for an invoice), vigencia (days), prefix (folio), moneda, empresa, rfc, telefono, correo, direccion, notas, color. Empty value clears it (rfc=). The folio counter is never touched \xB7 plantilla=@quote.html (your own PDF template: {{folio}}, {{cliente}}, {{#items}}\u2026{{/items}}), logo=https://\u2026, comercial=NAME, numeros=es (1.234,56) \xB7 catalogo_base=DB:TABLE (the catalog comes from that table of a granted database, editable on the web or by chatting with the agent; catalogo_base= goes back to the agent's own list)"],
14275
14311
  ["--catalog FILE", "set: the house `cotizar` tool's catalog from a CSV (sku,nombre,precio[,unidad,categoria,descripcion]) or JSON list; replaces the previous one. Its `categoria` drives the VAT rules"],
14276
14312
  ["--verbose", "doctor: also list the checks that passed"],
14277
14313
  ["--user EMAIL", "doctor (Ghosty staff): that person's agents \u2014 doctor, latest turns (state, seconds, error, and \xABfetched\xBB: whether their app asked for the thread after the turn ended), plan/bag and boxes (duplicates flagged); logged"],
@@ -14403,6 +14439,7 @@ var HELPS = {
14403
14439
  summary: "Databases an agent can use, and what it may write from customer channels.",
14404
14440
  usage: [
14405
14441
  "ghosty dbs ls <agent-id> [--orphans]",
14442
+ "ghosty dbs create <agent-id> <name> [--description TEXT] [--read] [--no-grant]",
14406
14443
  "ghosty dbs tables <agent-id> <db>",
14407
14444
  'ghosty dbs query <agent-id> <db> "SQL" [--arg V]\u2026 [--write]',
14408
14445
  "ghosty dbs origin <agent-id> <db> [--table T] [--since 2026-10-10] [--limit N]",
@@ -14410,6 +14447,7 @@ var HELPS = {
14410
14447
  "ghosty dbs restore <agent-id> <db> <checkpoint-id> [--yes]",
14411
14448
  "ghosty dbs export <agent-id> <db> [--table T] [--out FILE]",
14412
14449
  "ghosty dbs import <agent-id> <db> --from easybits:<db-id> [--tables a,b] [--append] [--rehost-images t.col,\u2026] [--dry-run] [--yes]",
14450
+ "ghosty dbs import <agent-id> <db> --from csv:FILE --table T [--append] [--dry-run] [--yes]",
14413
14451
  "ghosty dbs rehost <agent-id> <db> --table T --column C [--dry-run]",
14414
14452
  "ghosty dbs photos put <agent-id> <db> --table T --key-column K --dir DIR [--column C] [--dry-run]",
14415
14453
  "ghosty dbs grant <agent-id> <db> [--external-write t1,t2] [--external-rows t:col+col,\u2026]",
@@ -14761,7 +14799,8 @@ HELPS.channels = {
14761
14799
  ["-n, --dry-run", "print the plan (+ starts answering, - stops) and change nothing"],
14762
14800
  ["--yes", "enable / groups enable: skip the confirmation (required without a terminal)"],
14763
14801
  ["--role R", "groups: equipo (the team: writes to databases, admin tools) or cliente (like any customer)"],
14764
- ["--wake mention|all", "groups set/enable: answer every message, or only when it's mentioned or replied to"],
14802
+ ["--wake mention|name|all", "groups set/enable: answer every message (all), only when it's @-mentioned, named or replied to (mention), or only when its name or an alias is written (name: no @, no replies)"],
14803
+ ['--alias "a,b"', 'groups set: other names that wake it in that group besides its own (the nickname people use there); "" clears them'],
14765
14804
  ["--text T / --sender N", "groups inject (staff): a message that got lost (group off, deploy) enters as if N wrote it there, and the agent answers in the group"],
14766
14805
  ["--cached", "groups ls: don't ask WhatsApp for the live list (faster; may miss new groups)"],
14767
14806
  ["--ads-only on|off", "link: only conversations that come from an ad open a card (also alone, to change it on a linked number)"],
@@ -15083,6 +15122,7 @@ HELPS.sites = {
15083
15122
  "ghosty sites agent <slug> [create] [--agent id] [--model m] [--prompt p]",
15084
15123
  "ghosty sites pin <slug|id> [--interval year|month] [--currency usd|mxn] [--add-pack]",
15085
15124
  "ghosty sites unpin <slug|id>",
15125
+ "ghosty sites rm <slug|id> [--yes]",
15086
15126
  "ghosty sites pins [--cancel | --resume]",
15087
15127
  "ghosty sites search <slug|id> [--days 28]",
15088
15128
  "ghosty sites publish <file.html|folder> [--title T] [--slug S] [--site <slug|id>]",
@@ -15093,6 +15133,7 @@ HELPS.sites = {
15093
15133
  ["--interval I", "pin: year (default, 2 months free) or month \u2014 the billing period if you need to pay"],
15094
15134
  ["--currency C", "pin: usd (default) or mxn \u2014 the currency charged if you need to pay"],
15095
15135
  ["--add-pack", "pin: you already use your slots \u2014 add 3 more (same period and currency as your subscription) and pin this one"],
15136
+ ["--yes", "rm: skip the confirmation (required without a terminal)"],
15096
15137
  ["--cancel / --resume", "pins: cancel at period end (sites go back to Free with notice), or undo it"],
15097
15138
  ["--days N", "search: Search Console window in days (default 28; Google reports 2\u20133 days late)"],
15098
15139
  ["--since S / --kind K / --req-id ID", "errors: window (30m, 6h, 7d; default 24h), one event kind, or everything from one request (its x-request-id)"],
@@ -15101,7 +15142,7 @@ HELPS.sites = {
15101
15142
  ["--slug S", "publish: the slug for a NEW site (a-z, 0-9 and hyphens, 3\u201360, not reserved)"],
15102
15143
  ["--site X", "publish: update that house site (slug, id or URL) instead of creating one"]
15103
15144
  ],
15104
- examples: ["ghosty sites show agenda-perdix", "ghosty sites env set agenda-perdix STRIPE_SECRET_KEY=sk_live_\u2026", "ghosty sites cron pause agenda-perdix cm123", "ghosty sites agent agenda-perdix create", "ghosty sites agent agenda-perdix --model claude-sonnet-5-5", "ghosty sites pin tacos-don-pepe", "ghosty sites pin tacos-don-pepe --interval month --currency mxn", "ghosty sites unpin landing-vieja", "ghosty sites pins --cancel", "ghosty sites publish ./landing/ --site gelatina-de-sandia"],
15145
+ examples: ["ghosty sites show agenda-perdix", "ghosty sites env set agenda-perdix STRIPE_SECRET_KEY=sk_live_\u2026", "ghosty sites cron pause agenda-perdix cm123", "ghosty sites agent agenda-perdix create", "ghosty sites agent agenda-perdix --model claude-sonnet-5-5", "ghosty sites pin tacos-don-pepe", "ghosty sites pin tacos-don-pepe --interval month --currency mxn", "ghosty sites unpin landing-vieja", "ghosty sites rm landing-de-prueba --yes", "ghosty sites pins --cancel", "ghosty sites publish ./landing/ --site gelatina-de-sandia"],
15105
15146
  notes: [
15106
15147
  "pin: with a free slot it pins right away; without a subscription it prints a card-payment link (the site is pinned when you pay); with every slot used it answers needs_pack (repeat with --add-pack).",
15107
15148
  "unpin, cancelling, or a failed payment never pause a site at once: it goes back to the Free rule (30 days since its last change, at least 8 from today, notice 7 days before).",
@@ -15769,10 +15810,10 @@ async function demosCmd(sub, a1, rest, v) {
15769
15810
 
15770
15811
  // src/commands/sites.ts
15771
15812
  init_api();
15772
- init_i18n();
15773
- init_util();
15774
15813
  import { readdir as readdir5, readFile as readFile13, stat as stat3 } from "node:fs/promises";
15775
15814
  import { basename as basename4, join as join11, relative as relative2, sep } from "node:path";
15815
+ init_i18n();
15816
+ init_util();
15776
15817
  var TEXT_EXT = /* @__PURE__ */ new Set(["html", "htm", "css", "js", "mjs", "json", "txt", "xml", "svg", "webmanifest", "md"]);
15777
15818
  var SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", ".DS_Store"]);
15778
15819
  async function collectSiteFiles(target) {
@@ -15812,7 +15853,7 @@ var packPrice = (c, i, lang2) => `$${(PACK[i]?.[c] ?? 0).toLocaleString("en-US")
15812
15853
  var kb = (n) => n ? `${Math.max(1, Math.round(n / 1024))} KB` : "";
15813
15854
  var day = (iso) => iso ? iso.slice(0, 10) : "";
15814
15855
  async function sitesCmd(sub, a1, a2, v, a3, rest = []) {
15815
- const ex = "ghosty sites ls [--house] \xB7 show <slug|id> \xB7 env|cron|files \u2026 \xB7 errors <slug> [--since 24h] [--kind K] [--req-id ID] \xB7 agent <slug> [--agent id] [--model m] \xB7 pin|unpin <slug|id> \xB7 pins [--cancel|--resume] \xB7 search <slug|id> [--days 28] \xB7 domain [ls|add|verify|rm] <slug> [domain] \xB7 publish <file.html|folder> [--title T] [--slug S] [--site <slug|id>] \xB7 slug <slug|id> <new-slug>";
15856
+ const ex = "ghosty sites ls [--house] \xB7 show <slug|id> \xB7 env|cron|files \u2026 \xB7 errors <slug> [--since 24h] [--kind K] [--req-id ID] \xB7 agent <slug> [--agent id] [--model m] \xB7 pin|unpin|rm <slug|id> \xB7 pins [--cancel|--resume] \xB7 search <slug|id> [--days 28] \xB7 domain [ls|add|verify|rm] <slug> [domain] \xB7 publish <file.html|folder> [--title T] [--slug S] [--site <slug|id>] \xB7 slug <slug|id> <new-slug>";
15816
15857
  switch (sub ?? "ls") {
15817
15858
  case "ls":
15818
15859
  case "list": {
@@ -15841,6 +15882,15 @@ ${dim(t("or release one: ghosty sites unpin <slug>", "o suelta uno: ghosty sites
15841
15882
  return out(r, () => `${lavender("\u2713")} ${t("Released", "Suelto")} ${bold(r.url)}
15842
15883
  ${dim(r.expiresAt ? t(`Free rule: pauses ${day(r.expiresAt)} unless it changes (notice 7 days before).`, `Regla Gratis: se pausa el ${day(r.expiresAt)} si no cambia (aviso 7 d\xEDas antes).`) : t("Your plan keeps it published.", "Tu plan lo mantiene publicado."))}`);
15843
15884
  }
15885
+ case "rm":
15886
+ case "delete": {
15887
+ if (!a1) throw usage(t("Missing the site (slug, id or URL).", "Falta el sitio (slug, id o URL)."), "ghosty sites rm <slug|id> [--yes]");
15888
+ const retry = `ghosty sites rm ${a1}`;
15889
+ requireYesIfHeadless(v.yes, retry);
15890
+ await confirm(t(`Delete ${a1}? It stops being served and its files go away. This can't be undone.`, `\xBFBorrar ${a1}? Deja de servirse y sus archivos se van. No se puede deshacer.`), { yes: v.yes, retry });
15891
+ const r = await api(`/api/v2/sites/${encodeURIComponent(a1)}`, { method: "DELETE" });
15892
+ return out(r, () => `${lavender("\u2713")} ${t("Deleted", "Borrado")} ${bold(a1)}`);
15893
+ }
15844
15894
  case "pins": {
15845
15895
  if (v.cancel || v.resume) {
15846
15896
  const r2 = await api("/api/v2/sites/pins", { method: "POST", body: { cancel: Boolean(v.cancel) } });
@@ -17579,6 +17629,8 @@ ${bold("Configure an agent")}
17579
17629
  skills ls|get|add|rm <id> [slug] Skills (add from catalog, --file SKILL.md or --dir FOLDER; get lists, --out DIR saves; rm keeps a copy)
17580
17630
  skills templates <id> pdf-doc templates it sees: its own and Ghosty's, and which folder wins
17581
17631
  dbs ls|tables|query|export|import|rehost|grant|revoke|rename|rm <id> [db] Databases it uses [--write] [--external-write t1,t2] [--external-rows t:col] [--from easybits:ID --dry-run] [--table T --column C]
17632
+ dbs create <id> <name> New database in the owner's account, granted to this agent [--description TEXT] [--read] [--no-grant]
17633
+ dbs import <id> <db> --from csv:FILE --table T A CSV (with header) into a table; creates it if missing [--append] [--dry-run]
17582
17634
  dbs export <id> <db> Download a database: whole as .sqlite, or one table as CSV [--table T] [--out FILE]
17583
17635
  dbs photos put <id> <db> --table T --key-column K --dir DIR Loose photos to a catalog (SKU-123.jpg \u2192 its row), permanent links [--column C] [--dry-run]
17584
17636
  mcp get|set <id> MCP servers [--file servers.json] (http headers accept \${turn.token})
@@ -17597,7 +17649,7 @@ ${bold("Configure an agent")}
17597
17649
  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]
17598
17650
  channels wa pair <id> --phone N Pair a personal WhatsApp by code (no QR)
17599
17651
  channels wa status|pause <id> Personal WhatsApp: how it is / pause it (keeps the session)
17600
- channels wa groups ls|enable|disable|set|create <id> [group] Personal WhatsApp groups it answers, and as whom [--role equipo|cliente] [--wake mention|all] [--name "New name"] [--dry-run]
17652
+ channels wa groups ls|enable|disable|set|create <id> [group] Personal WhatsApp groups it answers, and as whom [--role equipo|cliente] [--wake mention|name|all] [--alias "tilin"] [--name "New name"] [--dry-run]
17601
17653
  boards ls|create|assign|move-cards|access|followups|rm A workspace's boards: create (--columns-from), where an agent sends cards, move cards, who sees each one, quote follow-ups
17602
17654
  cards ls <id> Recent cards on its sales board [--q TEXT] [--paused] [--limit 20]
17603
17655
  cards show <id> <card> A card's thread: who wrote, text, attachments and whether gs kept a copy (folio, phone or id)
@@ -17620,6 +17672,7 @@ ${bold("Sites")} ${dim("(sites your agents publish at <slug>.ghosty.page)")}
17620
17672
  sites ls Sites your agents published (live and paused) [--house: Ghosty's own demos and landings (staff)]
17621
17673
  sites pin <slug|id> Pin it: never pauses, no banner ($49 USD/yr or $5/mo, includes up to 3 sites) [--interval year|month] [--currency usd|mxn] [--add-pack: 3 more]
17622
17674
  sites unpin <slug|id> Release its slot (back to the Free rule: 30 days + notice)
17675
+ sites rm <slug|id> Delete one of your sites (stops being served; can't be undone) [--yes]
17623
17676
  sites search <slug|id> How Google finds it: clicks, impressions, searches, indexed or not [--days 28]
17624
17677
  sites domain add|verify|rm <slug> <dom> Custom domain for a site or app (DNS record \u2192 verify \u2192 HTTPS) [ls <slug>: list]
17625
17678
  sites show <slug|id> Everything about a site or app: status, domains, variable names, data, files, tasks
@@ -17810,6 +17863,8 @@ ${bold("Configurar un agente")}
17810
17863
  skills ls|get|add|rm <id> [slug] Skills (add del cat\xE1logo, --file SKILL.md o --dir CARPETA; get lista, --out DIR guarda; rm deja copia)
17811
17864
  skills templates <id> Plantillas de pdf-doc que ve: las suyas y las de Ghosty, y qu\xE9 carpeta gana
17812
17865
  dbs ls|tables|query|export|import|rehost|grant|revoke|rename|rm <id> [db] Bases de datos que usa [--write] [--external-write t1,t2] [--external-rows t:col] [--from easybits:ID --dry-run] [--table T --column C]
17866
+ dbs create <id> <nombre> Base nueva en la cuenta del due\xF1o, concedida a este agente [--description TEXTO] [--read] [--no-grant]
17867
+ dbs import <id> <db> --from csv:ARCHIVO --table T Un CSV (con encabezado) a una tabla; la crea si no existe [--append] [--dry-run]
17813
17868
  dbs export <id> <db> Descarga una base: entera como .sqlite, o una tabla en CSV [--table T] [--out ARCHIVO]
17814
17869
  dbs photos put <id> <db> --table T --key-column K --dir DIR Fotos sueltas a un cat\xE1logo (SKU-123.jpg \u2192 su fila), ligas permanentes [--column C] [--dry-run]
17815
17870
  mcp get|set <id> Servidores MCP [--file servers.json] (headers http admiten \${turn.token})
@@ -17828,7 +17883,7 @@ ${bold("Configurar un agente")}
17828
17883
  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]
17829
17884
  channels wa pair <id> --phone N Vincula un WhatsApp personal con c\xF3digo (sin QR)
17830
17885
  channels wa status|pause <id> WhatsApp personal: c\xF3mo est\xE1 / pausarlo (conserva la sesi\xF3n)
17831
- channels wa groups ls|enable|disable|set|create <id> [grupo] Grupos de WhatsApp personal que contesta, y como qui\xE9n [--role equipo|cliente] [--wake mention|all] [--name "Nombre nuevo"] [--dry-run]
17886
+ channels wa groups ls|enable|disable|set|create <id> [grupo] Grupos de WhatsApp personal que contesta, y como qui\xE9n [--role equipo|cliente] [--wake mention|name|all] [--alias "tilin"] [--name "Nombre nuevo"] [--dry-run]
17832
17887
  boards ls|create|assign|move-cards|access|followups|rm Los tableros de un espacio: crear (--columns-from), a d\xF3nde manda tarjetas un agente, mover tarjetas, qui\xE9n ve cada uno, seguimiento de cotizaciones
17833
17888
  cards ls <id> Tarjetas recientes de su tablero de ventas [--q TEXTO] [--paused] [--limit 20]
17834
17889
  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)
@@ -17851,6 +17906,7 @@ ${bold("Sitios")} ${dim("(los sitios que publican tus agentes en <slug>.ghosty.
17851
17906
  sites ls Sitios que publicaron tus agentes (vivos y en pausa) [--house: demos y landings de Ghosty (staff)]
17852
17907
  sites pin <slug|id> F\xEDjalo: no se pausa y va sin franja ($49 USD al a\xF1o o $5 al mes, incluye hasta 3 sitios) [--interval year|month] [--currency usd|mxn] [--add-pack: 3 m\xE1s]
17853
17908
  sites unpin <slug|id> Suelta su lugar (vuelve a la regla Gratis: 30 d\xEDas + aviso)
17909
+ sites rm <slug|id> Borra uno de tus sitios (deja de servirse; no se deshace) [--yes]
17854
17910
  sites pins Tu suscripci\xF3n de Sitios fijos y sus lugares [--cancel | --resume]
17855
17911
  sites domain add|verify|rm <slug> <dom> Dominio propio para un sitio o app (registro DNS \u2192 verificar \u2192 HTTPS) [ls <slug>: lista]
17856
17912
  sites show <slug|id> Todo de un sitio o app: estado, dominios, nombres de variables, datos, archivos, tareas
@@ -18061,6 +18117,8 @@ async function main() {
18061
18117
  dir: { type: "string" },
18062
18118
  arg: { type: "string", multiple: true },
18063
18119
  write: { type: "boolean" },
18120
+ read: { type: "boolean" },
18121
+ "no-grant": { type: "boolean" },
18064
18122
  "external-write": { type: "string" },
18065
18123
  "external-rows": { type: "string" },
18066
18124
  "no-deliver": { type: "boolean" },
@@ -18166,6 +18224,7 @@ async function main() {
18166
18224
  handle: { type: "string" },
18167
18225
  factory: { type: "string" },
18168
18226
  wake: { type: "string" },
18227
+ alias: { type: "string" },
18169
18228
  text: { type: "string" },
18170
18229
  sender: { type: "string" },
18171
18230
  "from-file": { type: "string" },
@@ -18266,7 +18325,7 @@ async function main() {
18266
18325
  const tokens = splitTokens(pos, v);
18267
18326
  globals.token = tokens.session;
18268
18327
  globals.profile = v.profile;
18269
- if (v.version) return out({ version: "0.100.0" }, () => "0.100.0");
18328
+ if (v.version) return out({ version: "0.103.0" }, () => "0.103.0");
18270
18329
  for (const n of normalizeArgs(pos, v)) if (!globals.json) info(dim(n));
18271
18330
  const context = (await readConfig()).workspace;
18272
18331
  if (!explicitToken()?.startsWith("gat_")) {
@@ -18476,7 +18535,7 @@ async function main() {
18476
18535
  throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
18477
18536
  }
18478
18537
  }
18479
- var notice = updateNotice("0.100.0").catch(() => null);
18538
+ var notice = updateNotice("0.103.0").catch(() => null);
18480
18539
  main().then(
18481
18540
  async () => {
18482
18541
  const n = process.stderr.isTTY ? await notice : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghostystudio/cli",
3
- "version": "0.100.0",
3
+ "version": "0.103.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": {