@ghostystudio/cli 0.45.0 → 0.47.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 +9 -0
  2. package/dist/index.js +143 -20
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -103,6 +103,15 @@ Every command has its own help with examples: `ghosty <command> --help` or `ghos
103
103
 
104
104
  Exit codes: `0` ok · `1` API error · `2` usage error · `3` not signed in.
105
105
 
106
+ ### Changes in 0.47.0
107
+
108
+ - `agents set --quote` takes VAT rules for the house `cotizar` tool: `iva-incluido=true` (prices already include VAT), `iva.CATEGORY=16` (VAT for that category), `solo-factura=true` (VAT only when the customer asks for an invoice).
109
+ - New `agents set --catalog products.csv|json`: the house `cotizar` tool's catalog (sku, nombre, precio, unidad, categoria). The category comes from here, not from the model.
110
+
111
+ ### Changes in 0.46.0
112
+
113
+ - New `staff ls|assign|unassign <staff-email> <client-email>` (Ghosty staff): a staff member assigned to a client account sees and runs its agents (with or without a workspace), documents, databases and boards as themselves — no keys, billing or transfers, every write logged. The client sees «Your Ghosty account manager».
114
+
106
115
  ### Changes in 0.45.0
107
116
 
108
117
  - New `turns grep <id> "text" [--since 7d] [--role user|agent]`: finds what the customer or the agent said, with its turn, to open with `turns show`.
package/dist/index.js CHANGED
@@ -3122,9 +3122,9 @@ var require_timestamp = __commonJS({
3122
3122
  const match = str.match(timestamp.test);
3123
3123
  if (!match)
3124
3124
  throw new Error("!!timestamp expects a date, starting with yyyy-mm-dd");
3125
- const [, year, month, day2, hour, minute, second] = match.map(Number);
3125
+ const [, year, month, day3, hour, minute, second] = match.map(Number);
3126
3126
  const millisec = match[7] ? Number((match[7] + "00").substr(1, 3)) : 0;
3127
- let date = Date.UTC(year, month - 1, day2, hour || 0, minute || 0, second || 0, millisec);
3127
+ let date = Date.UTC(year, month - 1, day3, hour || 0, minute || 0, second || 0, millisec);
3128
3128
  const tz = match[8];
3129
3129
  if (tz && tz !== "Z") {
3130
3130
  let d = parseSexagesimal(tz, false);
@@ -8422,7 +8422,7 @@ async function postLong(path, body, timeoutMs = 9e5) {
8422
8422
  const req = httpsRequest(`${base}${path}`, {
8423
8423
  method: "POST",
8424
8424
  timeout: timeoutMs,
8425
- headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.45.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
8425
+ headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.47.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
8426
8426
  }, (res) => {
8427
8427
  let buf = "";
8428
8428
  res.on("data", (c) => buf += c);
@@ -8464,7 +8464,7 @@ async function request(path, opts = {}, retried = false) {
8464
8464
  method: opts.method ?? (opts.body !== void 0 || opts.raw !== void 0 ? "POST" : "GET"),
8465
8465
  headers: {
8466
8466
  authorization: `Bearer ${token}`,
8467
- "user-agent": `ghosty-cli/${"0.45.0"}`,
8467
+ "user-agent": `ghosty-cli/${"0.47.0"}`,
8468
8468
  "accept-language": lang(),
8469
8469
  ...opts.body !== void 0 ? { "content-type": "application/json" } : {},
8470
8470
  ...opts.headers
@@ -8764,6 +8764,36 @@ async function agentsGet(id, fields, promptOut) {
8764
8764
  const q = fields ? `?fields=${enc2(fields)}` : "?full=1";
8765
8765
  out(await api(agentPath(id, q)));
8766
8766
  }
8767
+ function parseCsv(text) {
8768
+ const rows = [];
8769
+ let row = [], cell = "", q = false;
8770
+ for (let i = 0; i < text.length; i++) {
8771
+ const c = text[i];
8772
+ if (q) {
8773
+ if (c === '"' && text[i + 1] === '"') {
8774
+ cell += '"';
8775
+ i++;
8776
+ } else if (c === '"') q = false;
8777
+ else cell += c;
8778
+ } else if (c === '"') q = true;
8779
+ else if (c === ",") {
8780
+ row.push(cell);
8781
+ cell = "";
8782
+ } else if (c === "\n" || c === "\r") {
8783
+ if (c === "\r" && text[i + 1] === "\n") i++;
8784
+ row.push(cell);
8785
+ cell = "";
8786
+ if (row.some((x) => x.trim())) rows.push(row);
8787
+ row = [];
8788
+ } else cell += c;
8789
+ }
8790
+ row.push(cell);
8791
+ if (row.some((x) => x.trim())) rows.push(row);
8792
+ const [head, ...body] = rows;
8793
+ if (!head) return [];
8794
+ const keys = head.map((h) => h.trim().toLowerCase());
8795
+ return body.map((r) => Object.fromEntries(keys.map((k, i) => [k, k === "precio" ? Number((r[i] ?? "").replace(/[$,\s]/g, "")) : (r[i] ?? "").trim()])));
8796
+ }
8767
8797
  async function agentsSet(id, v) {
8768
8798
  id = need(id, "agent id", "ghosty agents set <agent-id> --prompt-file PROMPT.md");
8769
8799
  const done = [];
@@ -8864,19 +8894,32 @@ ${dim(t("Dry run: nothing changed.", "Simulacro: no se cambi\xF3 nada."))}`);
8864
8894
  body.autoStage = v["auto-stage"] === "on";
8865
8895
  }
8866
8896
  if (v.quote) {
8867
- const alias = { iva: "ivaPct", vigencia: "vigenciaDias", prefix: "folioPrefijo", prefijo: "folioPrefijo", currency: "moneda", company: "empresa", phone: "telefono", email: "correo", address: "direccion", notes: "notas" };
8897
+ 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" };
8868
8898
  const cot = {};
8869
- for (const part of v.quote.split(/,(?=\s*[a-zA-Z]+=)/)) {
8870
- const m = /^\s*([a-zA-Z]+)=(.*)$/.exec(part);
8899
+ let cats = null;
8900
+ for (const part of v.quote.split(/,(?=\s*[a-zA-Z-]+(?:\.[^=,]+)?=)/)) {
8901
+ const m = /^\s*([a-zA-Z-]+)(?:\.([^=]+))?=(.*)$/.exec(part);
8871
8902
  if (!m) throw usage(t(`Bad --quote: ${part}.`, `--quote inv\xE1lido: ${part}.`), "--quote iva=0,prefix=SFI,vigencia=15");
8872
- const key = alias[m[1]] ?? m[1];
8873
- const val = m[2].trim().replace(/^"(.*)"$/, "$1");
8874
- cot[key] = key === "ivaPct" || key === "vigenciaDias" ? Number(val) : val;
8903
+ const [, rawKey, category, rawVal] = m;
8904
+ const val = rawVal.trim().replace(/^"(.*)"$/, "$1");
8905
+ if (rawKey.toLowerCase() === "iva" && category) {
8906
+ if (!cats) cats = { ...(await api(agentPath(id, "?fields=cotizacion"))).cotizacion?.ivaPorCategoria ?? {} };
8907
+ if (val === "") delete cats[category];
8908
+ else cats[category] = Number(val);
8909
+ continue;
8910
+ }
8911
+ const key = alias[rawKey] ?? rawKey;
8912
+ cot[key] = key === "ivaPct" || key === "vigenciaDias" ? Number(val) : key === "ivaIncluido" || key === "ivaSoloConFactura" ? /^(true|on|yes|si|sí|1)$/i.test(val) : val;
8875
8913
  }
8914
+ if (cats) cot.ivaPorCategoria = cats;
8876
8915
  body.cotizacion = cot;
8877
8916
  }
8917
+ if (v.catalog) {
8918
+ const raw = await readFile3(resolve(v.catalog), "utf8");
8919
+ body.catalogo = v.catalog.toLowerCase().endsWith(".json") ? JSON.parse(raw) : parseCsv(raw);
8920
+ }
8878
8921
  if (!Object.keys(body).length && done.length) return out({ id, updated: done }, () => `${lavender("\u2713")} ${t("Updated", "Actualizado")}: ${done.join(" \xB7 ")}`);
8879
- 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, --quote, --bag " + t("or", "o") + " --avatar.");
8922
+ 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, --quote, --catalog, --bag " + t("or", "o") + " --avatar.");
8880
8923
  const r = await api(agentPath(id), { method: "PATCH", body });
8881
8924
  out(r, () => `${lavender("\u2713")} ${t("Updated", "Actualizado")}: ${[...Object.keys(body), ...done].join(", ")}.`);
8882
8925
  }
@@ -9738,7 +9781,7 @@ async function call(path, v, init = {}) {
9738
9781
  "x-ghosty-ts": ts,
9739
9782
  "x-ghosty-sig": sig,
9740
9783
  "content-type": "application/json",
9741
- "user-agent": `ghosty-cli/${"0.45.0"}`
9784
+ "user-agent": `ghosty-cli/${"0.47.0"}`
9742
9785
  },
9743
9786
  body: method === "GET" ? void 0 : raw
9744
9787
  });
@@ -11225,7 +11268,8 @@ var HELPS_ES = {
11225
11268
  ["--voice E:ID", "set: voz de las notas de voz: elevenlabs:<voiceId> o kokoro:em_santa|em_alex|ef_dora"],
11226
11269
  ["--voice-replies M", "set: auto = contesta con nota de voz cuando el cliente manda o pide audio; off = siempre texto"],
11227
11270
  ["--auto-stage on|off", "set: mueve solo las tarjetas del tablero tras cada turno del cliente (una llamada extra al modelo por turno)"],
11228
- ["--quote k=v,\u2026", "set: config de la tool de casa `cotizar` \u2014 iva (0-100), 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"],
11271
+ ["--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"],
11272
+ ["--catalog ARCHIVO", "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"],
11229
11273
  ["--verbose", "doctor: lista tambi\xE9n las revisiones que pasaron"],
11230
11274
  ["--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"],
11231
11275
  ["--turns N", "doctor --user: cu\xE1ntos turnos recientes por agente (default 8, m\xE1x. 50)"],
@@ -11598,6 +11642,18 @@ var HELPS_ES = {
11598
11642
  ["--yes", "s\xE1ltate la confirmaci\xF3n (obligatorio sin terminal)"]
11599
11643
  ],
11600
11644
  notes: ["create sigue las reglas de la web: queda a tu nombre, nace como prueba de 30 d\xEDas, tu correo debe estar confirmado y tu plan topa cu\xE1ntos tienes. Sigue: ghosty partner enable <slug>.", "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.", "transfer se lleva los agentes con el espacio y recicla sus cajas (el due\xF1o va horneado en la caja); sin --copy-keys la simulaci\xF3n dice qu\xE9 llaves le faltan al nuevo \u2014 esos agentes dejar\xEDan de contestar."]
11645
+ },
11646
+ staff: {
11647
+ summary: "Staff de Ghosty asignado a CUENTAS cliente (admin delegado que act\xFAa como s\xED mismo): ve y opera con su propia sesi\xF3n los agentes (con y sin espacio), documentos, bases y tableros de esa cuenta. Sin llaves, cobro, traspasos ni borrados. S\xF3lo el staff de Ghosty asigna; el cliente s\xF3lo ve \xABTu ejecutivo de Ghosty\xBB. Cada cambio y cada escritura quedan en la bit\xE1cora.",
11648
+ flags: [
11649
+ ["--all", "ls: tambi\xE9n las asignaciones revocadas"],
11650
+ ["--reason TEXT", "assign: por qu\xE9 (va a la bit\xE1cora)"],
11651
+ ["--yes", "unassign: s\xE1ltate la confirmaci\xF3n (obligatorio sin terminal)"]
11652
+ ],
11653
+ notes: [
11654
+ "En el panel web el staff elige \xABAtendiendo: <cliente>\xBB (Documentos y bases, Agentes); es una cookie que se revalida en cada request, nunca otra sesi\xF3n.",
11655
+ "Revocar corta en el siguiente request. Asignar dos veces no hace nada (una sola activa por par)."
11656
+ ]
11601
11657
  }
11602
11658
  };
11603
11659
  var HELP_ALIASES = { creds: "credentials", conectores: "connectors", board: "cards", conversations: "chat", workspaces: "spaces" };
@@ -11652,7 +11708,8 @@ var HELPS = {
11652
11708
  ["--voice E:ID", "set: voice for voice notes: elevenlabs:<voiceId> or kokoro:em_santa|em_alex|ef_dora"],
11653
11709
  ["--voice-replies M", "set: auto = answer with a voice note when the customer sends or asks for audio; off = always text"],
11654
11710
  ["--auto-stage on|off", "set: move board cards by itself after each customer turn (one extra model call per turn)"],
11655
- ["--quote k=v,\u2026", "set: config of the house `cotizar` tool \u2014 iva (0-100), vigencia (days), prefix (folio), moneda, empresa, rfc, telefono, correo, direccion, notas, color. Empty value clears it (rfc=). The folio counter is never touched"],
11711
+ ["--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"],
11712
+ ["--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"],
11656
11713
  ["--verbose", "doctor: also list the checks that passed"],
11657
11714
  ["--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"],
11658
11715
  ["--turns N", "doctor --user: how many recent turns per agent (default 8, max 50)"],
@@ -12229,6 +12286,25 @@ HELPS.sites = {
12229
12286
  notes: ["A folder needs index.html at its root; hidden files and node_modules are skipped. Limits: 1 MB per file, 8 MB and 40 files per site.", "Without --site, publish always creates a new site (house sites are never matched by title).", "Slug errors: slug_invalid, slug_taken, and slug_unsupported while EasyBits does not accept a slug yet (exit 1, nothing changes)."],
12230
12287
  learn: `${REF}#sitios`
12231
12288
  };
12289
+ HELPS.staff = {
12290
+ summary: "Ghosty staff assigned to client ACCOUNTS (delegated admin acting as themselves): the staff member sees and runs that account's agents (with and without a workspace), documents, databases and boards with their own session. No keys, billing, transfers or deletes. Only Ghosty staff assigns; the customer just sees \xABYour Ghosty account executive\xBB. Every change and every write is logged.",
12291
+ usage: [
12292
+ "ghosty staff ls [staff-email] [client-email] [--all]",
12293
+ "ghosty staff assign <staff-email> <client-email> [--reason TEXT]",
12294
+ "ghosty staff unassign <staff-email> <client-email> [--yes]"
12295
+ ],
12296
+ flags: [
12297
+ ["--all", "ls: also revoked assignments"],
12298
+ ["--reason TEXT", "assign: why (goes to the audit log)"],
12299
+ ["--yes", "unassign: skip the confirmation (required without a terminal)"]
12300
+ ],
12301
+ examples: ['ghosty staff assign brenda@siiqtec.com siiqtec@gmail.com --reason "migraci\xF3n SIIQTEC"', "ghosty staff ls brenda@siiqtec.com", "ghosty staff unassign brenda@siiqtec.com siiqtec@gmail.com --yes"],
12302
+ notes: [
12303
+ "In the web panel the staff member picks \xABAtendiendo: <client>\xBB (Documents and databases, Agents); it is a cookie revalidated on every request, never another session.",
12304
+ "Revoking cuts on the next request. Assigning twice is a no-op (one active assignment per pair)."
12305
+ ],
12306
+ learn: `${REF}#staff-asignado`
12307
+ };
12232
12308
  function helpText(cmd) {
12233
12309
  const base = HELPS[cmd];
12234
12310
  if (!base) return void 0;
@@ -12843,7 +12919,7 @@ ${dim(t(`${total2} message(s) unanswered for lack of balance in ${r.days} day(s)
12843
12919
  d.noQuota += x.noQuota;
12844
12920
  days.set(x.day, d);
12845
12921
  }
12846
- rows = [...days].map(([day2, x]) => ({ day: day2, ...fmt2(x) }));
12922
+ rows = [...days].map(([day3, x]) => ({ day: day3, ...fmt2(x) }));
12847
12923
  } else rows = r.agents.map((x) => ({ agent: x.agent, ...fmt2(x) }));
12848
12924
  const cols = [...by.has("day") ? ["day"] : [], ...by.has("agent") ? ["agent"] : [], "turns", "failed", "tokens", "no balance"];
12849
12925
  const total = r.agents.reduce((n, a) => n + a.billable, 0);
@@ -13044,6 +13120,43 @@ ${dim(t("Links with the old slug may stop working.", "Las ligas con el slug viej
13044
13120
  }
13045
13121
  }
13046
13122
 
13123
+ // src/commands/staff.ts
13124
+ var day2 = (iso) => iso ? iso.slice(0, 10) : "";
13125
+ async function staffCmd(sub, staff, client, v) {
13126
+ if (!sub || sub === "ls" || sub === "list") {
13127
+ const q = new URLSearchParams();
13128
+ if (staff) q.set("staff", staff);
13129
+ if (client) q.set("client", client);
13130
+ if (v.all) q.set("all", "1");
13131
+ const r = await api(`/api/v2/staff/assignments${q.toString() ? `?${q}` : ""}`);
13132
+ const rows = r.assignments.map((a) => ({
13133
+ staff: a.staff.email,
13134
+ client: a.client.email,
13135
+ since: day2(a.createdAt),
13136
+ by: a.grantedBy,
13137
+ reason: a.reason ?? "",
13138
+ ...v.all ? { revoked: day2(a.revokedAt) } : {}
13139
+ }));
13140
+ return out(r, () => rows.length ? table(rows, ["staff", "client", "since", "by", "reason", ...v.all ? ["revoked"] : []]) : dim(t("No assignments.", "Sin asignaciones.")));
13141
+ }
13142
+ if (sub === "assign" || sub === "unassign") {
13143
+ if (!staff || !client) throw usage(t("Missing emails.", "Faltan correos."), `ghosty staff ${sub} <staff-email> <client-email>${sub === "assign" ? " [--reason TEXT]" : ""}`);
13144
+ if (sub === "assign") {
13145
+ const r2 = await api("/api/v2/staff/assignments", { method: "POST", body: { staff, client, ...v.reason ? { reason: v.reason } : {} } });
13146
+ return out(
13147
+ r2,
13148
+ () => r2.already ? dim(t(`${r2.staff} already serves ${r2.client}.`, `${r2.staff} ya atend\xEDa a ${r2.client}.`)) : `${lavender("\u2713")} ${t(`${r2.staff} now serves ${r2.client}: agents, documents, databases and boards, with no keys, billing or transfers (logged).`, `${r2.staff} ahora atiende a ${r2.client}: agentes, documentos, bases y tableros, sin llaves, cobro ni traspasos (queda en bit\xE1cora).`)}`
13149
+ );
13150
+ }
13151
+ const retry = `ghosty staff unassign ${staff} ${client} --yes`;
13152
+ requireYesIfHeadless(v.yes, retry);
13153
+ if (!v.yes) await confirm(t(`Stop ${staff} serving ${client}? It cuts on their next click.`, `\xBFQue ${staff} deje de atender a ${client}? Se corta en su siguiente clic.`), { retry });
13154
+ const r = await api("/api/v2/staff/assignments", { method: "DELETE", body: { staff, client } });
13155
+ return out(r, () => r.revoked ? `${lavender("\u2713")} ${t(`${r.staff} no longer serves ${r.client}.`, `${r.staff} ya no atiende a ${r.client}.`)}` : dim(t("There was no active assignment.", "No hab\xEDa asignaci\xF3n activa.")));
13156
+ }
13157
+ throw usage(t(`Unknown staff subcommand: ${sub}.`, `Subcomando de staff desconocido: ${sub}.`), "ghosty staff ls|assign|unassign <staff-email> [client-email]");
13158
+ }
13159
+
13047
13160
  // src/commands/support.ts
13048
13161
  import { readFile as readFile14 } from "node:fs/promises";
13049
13162
  var enc12 = encodeURIComponent;
@@ -13286,6 +13399,8 @@ ${bold("Workspaces")} ${dim("(changing them: Ghosty staff)")}
13286
13399
  spaces rename <old> <new> Change its slug only [--dry-run]
13287
13400
  spaces transfer <workspace> --to EMAIL New owner; agents go with it and their boxes are recycled [--keep-as admin|member|none] [--copy-keys] [--dry-run]
13288
13401
  spaces log <workspace> Its log: transfers, staff in/out, renewals (owner or staff) [--since 30d] [--action workspace]
13402
+ staff ls [staff-email] [client-email] Ghosty staff assigned to client accounts (staff) [--all: also revoked]
13403
+ staff assign|unassign <staff> <client> Staff sees and runs that account's agents, documents, databases and boards as themselves (no keys, billing or transfers; logged) [--reason]
13289
13404
 
13290
13405
  ${bold("Agents")}
13291
13406
  agents ls List your agents [--engine E] [--ids: only ids, for xargs] \xB7 --user EMAIL --app all|android|ios|desktop: as their app sees it (staff)
@@ -13300,7 +13415,8 @@ ${bold("Agents")}
13300
13415
  [--connect google-drive,\u2026] [--disconnect \u2026] give/remove access to your connectors
13301
13416
  [--channels wa=on,teams=off] turn channels on/off (wa = personal WhatsApp)
13302
13417
  [--voice elevenlabs:ID | kokoro:em_santa] [--voice-replies auto|off] [--auto-stage on|off]
13303
- [--quote iva=0,prefix=SFI,vigencia=15] house cotizar tool config (issuer, VAT, folio)
13418
+ [--quote iva=16,iva-incluido=true,iva.GRANEL=16,solo-factura=true,prefix=SFI] house cotizar tool config (issuer, VAT rules, folio)
13419
+ [--catalog productos.csv|json] its catalog: sku,nombre,precio[,unidad,categoria]
13304
13420
  [--toolsets crm,sales | +crm,-sales] gs tool groups it gets
13305
13421
  [--prompt-base AGENT|none --prompt-overlay FILE [--dry-run]] prompt = another agent's + edits, kept in sync
13306
13422
  [--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
@@ -13429,6 +13545,8 @@ ${bold("Espacios")} ${dim("(cambiarlos: staff de Ghosty)")}
13429
13545
  spaces rename <viejo> <nuevo> Cambia s\xF3lo su slug [--dry-run]
13430
13546
  spaces transfer <espacio> --to CORREO Due\xF1o nuevo; los agentes se van con \xE9l y se reciclan sus cajas [--keep-as admin|member|none] [--copy-keys] [--dry-run]
13431
13547
  spaces log <espacio> Su bit\xE1cora: traspasos, staff que entra o sale, renovaciones (due\xF1o o staff) [--since 30d] [--action workspace]
13548
+ staff ls [correo-staff] [correo-cliente] Staff de Ghosty asignado a cuentas cliente (staff) [--all: tambi\xE9n revocadas]
13549
+ staff assign|unassign <staff> <cliente> El staff ve y opera agentes, documentos, bases y tableros de esa cuenta como s\xED mismo (sin llaves, cobro ni traspasos; queda en bit\xE1cora) [--reason]
13432
13550
 
13433
13551
  ${bold("Agentes")}
13434
13552
  agents ls Lista tus agentes [--engine E] [--ids: s\xF3lo ids, para xargs] \xB7 --user CORREO --app all|android|ios|desktop: como lo ve su app (staff)
@@ -13443,7 +13561,8 @@ ${bold("Agentes")}
13443
13561
  [--connect google-drive,\u2026] [--disconnect \u2026] dale o qu\xEDtale acceso a tus conectores
13444
13562
  [--channels wa=on,teams=off] prende/apaga canales (wa = WhatsApp personal)
13445
13563
  [--voice elevenlabs:ID | kokoro:em_santa] [--voice-replies auto|off] [--auto-stage on|off]
13446
- [--quote iva=0,prefix=SFI,vigencia=15] config de la tool de casa cotizar (emisor, IVA, folio)
13564
+ [--quote iva=16,iva-incluido=true,iva.GRANEL=16,solo-factura=true,prefix=SFI] config de la tool de casa cotizar (emisor, reglas de IVA, folio)
13565
+ [--catalog productos.csv|json] su cat\xE1logo: sku,nombre,precio[,unidad,categoria]
13447
13566
  [--toolsets crm,sales | +crm,-sales] grupos de tools de gs que recibe
13448
13567
  [--prompt-base AGENTE|none --prompt-overlay ARCHIVO [--dry-run]] prompt = el de otro agente + ediciones, sincronizado
13449
13568
  [--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
@@ -13548,7 +13667,7 @@ ${bold("Opciones globales")}
13548
13667
  ${bold("C\xF3digos de salida")} 0 bien \xB7 1 error de la API \xB7 2 uso incorrecto \xB7 3 sin sesi\xF3n
13549
13668
  ${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.")}
13550
13669
  ${dim("Documentaci\xF3n: https://www.ghosty.studio/docs/cli")}`;
13551
- var COMMANDS = ["login", "logout", "whoami", "usage", "use", "apply", "plan", "engines", "bags", "spaces", "workspaces", "demos", "sites", "push", "novedades", "completion", "cards", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "partner", "me", "channels", "board", "boards", "credentials", "connectors", "browser", "docs", "help"];
13670
+ var COMMANDS = ["login", "logout", "whoami", "usage", "use", "apply", "plan", "engines", "bags", "spaces", "workspaces", "staff", "demos", "sites", "push", "novedades", "completion", "cards", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "partner", "me", "channels", "board", "boards", "credentials", "connectors", "browser", "docs", "help"];
13552
13671
  var AGENTS_SUBS = ["ls", "create", "get", "set", "export", "doctor", "docs", "share", "unshare", "shares", "teams", "rm", "restart", "demo", "box"];
13553
13672
  function unknown(what, input, known, helpCmd) {
13554
13673
  const s = suggest(input, known);
@@ -13690,6 +13809,7 @@ async function main() {
13690
13809
  days: { type: "string" },
13691
13810
  voice: { type: "string" },
13692
13811
  quote: { type: "string" },
13812
+ catalog: { type: "string" },
13693
13813
  "voice-replies": { type: "string" },
13694
13814
  "auto-stage": { type: "string" },
13695
13815
  "from-env": { type: "string" },
@@ -13724,6 +13844,7 @@ async function main() {
13724
13844
  recurring: { type: "string" },
13725
13845
  "hard-cap": { type: "string" },
13726
13846
  note: { type: "string" },
13847
+ reason: { type: "string" },
13727
13848
  bag: { type: "string" },
13728
13849
  avatar: { type: "string" },
13729
13850
  tool: { type: "string" },
@@ -13777,7 +13898,7 @@ async function main() {
13777
13898
  const tokens = splitTokens(pos, v);
13778
13899
  globals.token = tokens.session;
13779
13900
  globals.profile = v.profile;
13780
- if (v.version) return out({ version: "0.45.0" }, () => "0.45.0");
13901
+ if (v.version) return out({ version: "0.47.0" }, () => "0.47.0");
13781
13902
  for (const n of normalizeArgs(pos, v)) if (!globals.json) info(dim(n));
13782
13903
  const context = (await readConfig()).workspace;
13783
13904
  if (!explicitToken()?.startsWith("gat_")) {
@@ -13918,6 +14039,8 @@ async function main() {
13918
14039
  case "space":
13919
14040
  case "workspaces":
13920
14041
  return spacesCmd(sub, a1, a2, v, context);
14042
+ case "staff":
14043
+ return staffCmd(sub, a1, a2, v);
13921
14044
  case "plan":
13922
14045
  return planCmd(sub, a1, v, context);
13923
14046
  case "me":
@@ -13935,7 +14058,7 @@ async function main() {
13935
14058
  throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
13936
14059
  }
13937
14060
  }
13938
- var notice = updateNotice("0.45.0").catch(() => null);
14061
+ var notice = updateNotice("0.47.0").catch(() => null);
13939
14062
  main().then(
13940
14063
  async () => {
13941
14064
  const n = await notice;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghostystudio/cli",
3
- "version": "0.45.0",
3
+ "version": "0.47.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": {