@ghostystudio/cli 0.58.1 → 0.60.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 -0
  2. package/dist/index.js +64 -16
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -31,6 +31,7 @@ ghosty agents set <id> [--name] [--engine] [--model] [--prompt | --prompt-file F
31
31
  ghosty agents set <id> --prompt-base <base-id> --prompt-overlay overlay.json [--dry-run] # prompt = another agent's + edits, kept in sync
32
32
  ghosty agents share|unshare <id> <email> [--role editor|admin] # share without handing it over · agents shares <id> lists them
33
33
  ghosty agents teams ls|on|off|rm <id> [workspace] [--handle x] # which Teams workspaces it answers in, and its @handle there
34
+ ghosty agents archive|restore <id> # archive: stops answering, deleted after 7 days unless restored
34
35
  ghosty agents rm <id> [--from-workspace] [--yes] # --from-workspace: turn it off in Teams first
35
36
  ghosty agents set <id> [--tool wa:tool=off,…] [--bag NAME|none] [--avatar URL|FILE]
36
37
  ghosty agents restart <id>
@@ -109,6 +110,15 @@ Every command has its own help with examples: `ghosty <command> --help` or `ghos
109
110
 
110
111
  Exit codes: `0` ok · `1` API error · `2` usage error · `3` not signed in.
111
112
 
113
+ ### Changes in 0.60.0
114
+
115
+ - `agents archive <id>` / `agents restore <id>`: archive an agent (it stops answering on every channel and leaves the lists; deleted automatically after 7 days) or bring it back as it was. `agents ls --archived` lists archived agents and when they get deleted.
116
+
117
+ ### Changes in 0.59.0
118
+
119
+ - `spaces archive <workspace>` / `spaces restore <workspace>`: archive a workspace (same as Settings → Archive: it leaves `/app` and `spaces ls`; agents and data stay) or bring it back. Owner or staff; dry run first, archiving asks you to type the slug; logged as `workspace.archive` / `workspace.restore`.
120
+ - `spaces ls` no longer lists archived workspaces you are a member of (same as `/app`).
121
+
112
122
  ### Changes in 0.58.1
113
123
 
114
124
  - `agents docs add|replace --file` sends the file type from its extension. Before, the server stored every CLI upload as `application/octet-stream`, and a PDF reached WhatsApp customers as `.bin`.
package/dist/index.js CHANGED
@@ -8169,7 +8169,17 @@ var SERVER_MESSAGES = [
8169
8169
  ["s\xF3lo el due\xF1o del agente maneja su widget web", "only the agent's owner manages its web widget"],
8170
8170
  ["dominio no v\xE1lido: {} (usa ejemplo.com o *.ejemplo.com, sin https:// ni rutas)", "invalid domain: {} (use example.com or *.example.com, no https:// or paths)"],
8171
8171
  ["el widget web s\xF3lo corre con Claude o MiniGhosty (ghosty-lite): son los que a\xEDslan a cada visitante", "the web widget only runs on Claude or MiniGhosty (ghosty-lite): they isolate each visitor"],
8172
- ["pon al menos un dominio donde va a vivir el widget (--domains ejemplo.com)", "add at least one domain where the widget will live (--domains example.com)"]
8172
+ ["pon al menos un dominio donde va a vivir el widget (--domains ejemplo.com)", "add at least one domain where the widget will live (--domains example.com)"],
8173
+ // — Espacios (`workspaces.server.ts`: crear y archivar)
8174
+ ["Ahora mismo no tenemos sitio para espacios nuevos. Te apuntamos en la lista y te avisamos en cuanto se libere \u2014 normalmente es cuesti\xF3n de horas.", "We have no room for new workspaces right now. You're on the list and we'll let you know as soon as there is \u2014 usually within hours."],
8175
+ ["Para archivar escribe el nombre exacto: {}", "To archive, type the exact name: {}"],
8176
+ ['correo inv\xE1lido: "{}"', 'invalid email: "{}"'],
8177
+ ['el workspace "{}" ya existe', 'workspace "{}" already exists'],
8178
+ ["slug inv\xE1lido (2-40, min\xFAsculas/n\xFAmeros/guiones, no reservado)", "invalid slug (2-40, lowercase/numbers/hyphens, not reserved)"],
8179
+ ["{} rebot\xF3 antes y dejamos de escribirle. Hay que revisar esa direcci\xF3n.", "{} bounced before and we stopped writing to it. Check that address."],
8180
+ ["{} tiene que confirmar su correo antes de crear un espacio, y no pudimos mandarte la liga ahora mismo. P\xEDdela otra vez desde /verify-email.", "{} must confirm their email before creating a workspace, and we couldn't send the link right now. Ask for it again from /verify-email."],
8181
+ ["{} tiene que confirmar su correo antes de crear un espacio. Acabamos de mandarte la liga; \xE1brela y vuelve a intentarlo.", "{} must confirm their email before creating a workspace. We just sent you the link; open it and try again."],
8182
+ ["{} ya tiene {} workspace(s) y su plan ({}) permite {}.", "{} already has {} workspace(s) and their plan ({}) allows {}."]
8173
8183
  ];
8174
8184
 
8175
8185
  // src/i18n.ts
@@ -8468,7 +8478,7 @@ async function postLong(path, body, timeoutMs = 9e5) {
8468
8478
  const req = httpsRequest(`${base}${path}`, {
8469
8479
  method: "POST",
8470
8480
  timeout: timeoutMs,
8471
- headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.58.1"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
8481
+ headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.60.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
8472
8482
  }, (res) => {
8473
8483
  let buf = "";
8474
8484
  res.on("data", (c) => buf += c);
@@ -8510,7 +8520,7 @@ async function request(path, opts = {}, retried = false) {
8510
8520
  method: opts.method ?? (opts.body !== void 0 || opts.raw !== void 0 ? "POST" : "GET"),
8511
8521
  headers: {
8512
8522
  authorization: `Bearer ${token}`,
8513
- "user-agent": `ghosty-cli/${"0.58.1"}`,
8523
+ "user-agent": `ghosty-cli/${"0.60.0"}`,
8514
8524
  "accept-language": lang(),
8515
8525
  ...opts.body !== void 0 ? { "content-type": "application/json" } : {},
8516
8526
  ...opts.headers
@@ -9070,6 +9080,11 @@ var need = (v, what, example) => {
9070
9080
  return v;
9071
9081
  };
9072
9082
  async function agentsLs(workspace, f = {}) {
9083
+ if (f.archived) {
9084
+ const r2 = await api("/api/v2/me/agents/archived");
9085
+ if (f.ids) return void process.stdout.write(r2.agents.map((a) => a.id).join("\n") + (r2.agents.length ? "\n" : ""));
9086
+ return out(r2.agents, () => table(r2.agents.map((a) => ({ id: a.id, name: a.name, engine: a.engine, archivedAt: a.archivedAt, purgeAt: a.purgeAt })), ["id", "name", "engine", "archivedAt", "purgeAt"]));
9087
+ }
9073
9088
  if (f.sponsor || f.owner) {
9074
9089
  const q = new URLSearchParams({ ...f.sponsor ? { sponsor: f.sponsor } : {}, ...f.owner ? { owner: f.owner } : {}, ...f.engine ? { engine: f.engine } : {}, ...workspace && !f.all ? { workspace } : {} });
9075
9090
  const s2 = await api(`/api/v2/agents?${q}`);
@@ -9344,6 +9359,15 @@ async function agentsRm(id, v = {}) {
9344
9359
  const r = await api(agentPath(id, from ? "?fromWorkspace=1" : ""), { method: "DELETE" });
9345
9360
  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(", ")}.`)) : ""}`);
9346
9361
  }
9362
+ async function agentsArchive(id, archive) {
9363
+ id = need(id, "agent id", `ghosty agents ${archive ? "archive" : "restore"} <agent-id>`);
9364
+ const r = await api(agentPath(id, "/archive"), { method: "POST", body: { archive } });
9365
+ const day3 = r.purgeAt ? r.purgeAt.slice(0, 10) : "";
9366
+ out(
9367
+ r,
9368
+ () => !r.changed ? dim(archive ? t(`Already archived (deleted on ${day3}).`, `Ya estaba archivado (se borra el ${day3}).`) : t("It wasn't archived.", "No estaba archivado.")) : archive ? `${lavender("\u2713")} ${t(`Archived ${id}. It no longer answers anywhere; deleted on ${day3} unless you restore it (ghosty agents restore ${id}).`, `Archivado ${id}. Ya no contesta en ning\xFAn canal; se borra el ${day3} si no lo restauras (ghosty agents restore ${id}).`)}` : `${lavender("\u2713")} ${t(`Restored ${id}.`, `Restaurado ${id}.`)}${r.teams.length || r.whatsapp ? dim(t(` Back on: ${[...r.teams.length ? [`Teams (${r.teams.length})`] : [], ...r.whatsapp ? ["WhatsApp"] : []].join(", ")}.`, ` De vuelta: ${[...r.teams.length ? [`Teams (${r.teams.length})`] : [], ...r.whatsapp ? ["WhatsApp"] : []].join(", ")}.`)) : ""}`
9369
+ );
9370
+ }
9347
9371
  async function setAvatar(id, src) {
9348
9372
  if (/^https:\/\//.test(src)) return api(agentPath(id, "/avatar"), { method: "PUT", body: { url: src } });
9349
9373
  const bytes = await readFile3(src).catch(() => {
@@ -10408,7 +10432,7 @@ async function call(path, v, init = {}) {
10408
10432
  "x-ghosty-ts": ts,
10409
10433
  "x-ghosty-sig": sig,
10410
10434
  "content-type": "application/json",
10411
- "user-agent": `ghosty-cli/${"0.58.1"}`
10435
+ "user-agent": `ghosty-cli/${"0.60.0"}`
10412
10436
  },
10413
10437
  body: method === "GET" ? void 0 : raw
10414
10438
  });
@@ -12080,6 +12104,8 @@ var HELPS_ES = {
12080
12104
  ["# Teams workspaces you administer: where it answers, is paused, and with which @handle", "# los equipos de Teams que administras: d\xF3nde contesta, d\xF3nde est\xE1 en pausa y con qu\xE9 @handle"],
12081
12105
  ["# turn it on there (or change its @handle)", "# lo prende ah\xED (o le cambia el @handle)"],
12082
12106
  ["# pause it there; it keeps its @handle", "# lo pausa ah\xED; conserva su @handle"],
12107
+ ["# archive: stops answering everywhere, hidden, deleted after 7 days unless restored", "# archivar: deja de contestar en todos lados, se oculta y se borra a los 7 d\xEDas si no lo restauras"],
12108
+ ["# archived agents and when they get deleted", "# los archivados y cu\xE1ndo se borran"],
12083
12109
  ["# remove it from there; the agent stays", "# lo quita de ah\xED; el agente sigue"],
12084
12110
  ["# web widget: chat bubble on your site, only on those domains", "# widget web: burbuja de chat en tu sitio, s\xF3lo en esos dominios"],
12085
12111
  ["# state + the <script> to paste", "# su estado + el <script> para pegar"]
@@ -12514,7 +12540,7 @@ var HELPS_ES = {
12514
12540
  ]
12515
12541
  },
12516
12542
  spaces: {
12517
- summary: "Espacios: crea uno a tu nombre (prueba), lista los tuyos (o, staff, los de cualquiera) y operaciones del staff de Ghosty \u2014 entrar/salir del espacio de un cliente, cambiar su combo, mover \xABpagado hasta\xBB, renombrar su slug, cambiar de due\xF1o. Cada cambio queda en la bit\xE1cora del espacio.",
12543
+ summary: "Espacios: crea uno a tu nombre (prueba), lista los tuyos (o, staff, los de cualquiera) y operaciones del staff de Ghosty \u2014 entrar/salir del espacio de un cliente, cambiar su combo, mover \xABpagado hasta\xBB, renombrar su slug, cambiar de due\xF1o \u2014 y, due\xF1o o staff, archivarlo o restaurarlo. Cada cambio queda en la bit\xE1cora del espacio.",
12518
12544
  flags: [
12519
12545
  ["--user EMAIL", "ls (staff): los espacios de esa persona"],
12520
12546
  ["--combo C", "set (staff): teams | sales | both \u2014 sin Ventas sus tableros existen pero nadie los ve; create: distinto de teams, s\xF3lo staff"],
@@ -12529,7 +12555,7 @@ var HELPS_ES = {
12529
12555
  ["-n, --dry-run", "ense\xF1a qu\xE9 cambia, no cambia nada"],
12530
12556
  ["--yes", "s\xE1ltate la confirmaci\xF3n (obligatorio sin terminal)"]
12531
12557
  ],
12532
- 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."]
12558
+ 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.", "archive hace lo mismo que Ajustes \u2192 Archivar: el espacio sale de /app y de spaces ls, sus agentes y datos se quedan; pide escribir el slug. Deshacer: ghosty spaces restore <slug>. Queda en bit\xE1cora como workspace.archive / workspace.restore.", "transfer se lleva los agentes con el espacio: las cajas ACP se mudan al due\xF1o nuevo con su /data, las del pool se reciclan (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."]
12533
12559
  },
12534
12560
  staff: {
12535
12561
  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.",
@@ -12629,6 +12655,8 @@ var HELPS = {
12629
12655
  "ghosty agents embed <agent-id> [--copy '{json}'|none] [--copy-file texts.json] # what it says while it works",
12630
12656
  "ghosty agents embed <agent-id> theme # export the theme as JSON (edit it, apply it back with --style-file)",
12631
12657
  "ghosty agents embed <agent-id> --out src/components/GhostyChat.tsx [--force] # your own React component, token and theme baked in",
12658
+ "ghosty agents archive|restore <agent-id> # archive: stops answering everywhere, hidden, deleted after 7 days unless restored",
12659
+ "ghosty agents ls --archived # archived agents and when they get deleted",
12632
12660
  "ghosty agents rm <agent-id> [--from-workspace] [--yes]"
12633
12661
  ],
12634
12662
  flags: [
@@ -13247,8 +13275,8 @@ HELPS.novedades = {
13247
13275
  learn: `${REF}#soporte`
13248
13276
  };
13249
13277
  HELPS.spaces = {
13250
- summary: "Workspaces: create one of your own (trial), list yours (or, staff, anyone's), and Ghosty staff operations \u2014 join/leave a client's workspace, change its combo, move \xABpaid until\xBB, rename its slug, change its owner. Each change is logged in the workspace's audit.",
13251
- usage: ["ghosty spaces ls [--user EMAIL]", "ghosty spaces create <slug> [--combo teams|sales|both] [--dry-run]", "ghosty spaces join|leave <workspace>", "ghosty spaces set <workspace> --combo teams|sales|both | --model-cap 0.25|1|4|none | --house-claude on|off [--dry-run]", "ghosty spaces renew <workspace> --until YYYY-MM-DD [--note TEXT] [--dry-run] [--yes]", "ghosty spaces rename <old> <new> [--dry-run] [--yes]", "ghosty spaces transfer <workspace> --to EMAIL [--keep-as admin|member|none] [--copy-keys] [--dry-run] [--yes]", "ghosty spaces log <workspace> [--since 30d] [--action workspace] [--limit N]"],
13278
+ summary: "Workspaces: create one of your own (trial), list yours (or, staff, anyone's), and Ghosty staff operations \u2014 join/leave a client's workspace, change its combo, move \xABpaid until\xBB, rename its slug, change its owner \u2014 and, owner or staff, archive or restore it. Each change is logged in the workspace's audit.",
13279
+ usage: ["ghosty spaces ls [--user EMAIL]", "ghosty spaces create <slug> [--combo teams|sales|both] [--dry-run]", "ghosty spaces join|leave <workspace>", "ghosty spaces set <workspace> --combo teams|sales|both | --model-cap 0.25|1|4|none | --house-claude on|off [--dry-run]", "ghosty spaces renew <workspace> --until YYYY-MM-DD [--note TEXT] [--dry-run] [--yes]", "ghosty spaces rename <old> <new> [--dry-run] [--yes]", "ghosty spaces archive|restore <workspace> [--dry-run] [--yes]", "ghosty spaces transfer <workspace> --to EMAIL [--keep-as admin|member|none] [--copy-keys] [--dry-run] [--yes]", "ghosty spaces log <workspace> [--since 30d] [--action workspace] [--limit N]"],
13252
13280
  flags: [
13253
13281
  ["--user EMAIL", "ls (staff): that person's workspaces"],
13254
13282
  ["--combo C", "set (staff): teams | sales | both \u2014 without Sales its boards exist but nobody sees them; create: other than teams, staff only"],
@@ -13263,8 +13291,8 @@ HELPS.spaces = {
13263
13291
  ["-n, --dry-run", "show what changes, change nothing"],
13264
13292
  ["--yes", "skip the confirmation (required without a terminal)"]
13265
13293
  ],
13266
- examples: ["ghosty spaces ls", "ghosty spaces create mailmask --dry-run", "ghosty spaces ls --user brenda@siiqtec.com", "ghosty spaces join denik", "ghosty spaces set denik --combo sales --dry-run", "ghosty spaces set mailmask --model-cap 4 --house-claude on", 'ghosty spaces renew descti --until 2026-11-01 --note "pag\xF3 octubre"', "ghosty spaces rename totequim siiqtec --dry-run", "ghosty spaces transfer siiqtec --to siiqtec@gmail.com --keep-as admin --copy-keys --dry-run", "ghosty spaces log siiqtec --since 30d"],
13267
- notes: ["create follows the same rules as the web: you own it, it starts as a 30-day trial, your email must be confirmed, and your plan caps how many you own. Next: ghosty partner enable <slug>.", "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.", "transfer moves the agents with the workspace and recycles their boxes (the owner is baked into the box); without --copy-keys the dry run lists which keys the new owner lacks \u2014 those agents would stop answering."],
13294
+ examples: ["ghosty spaces ls", "ghosty spaces create mailmask --dry-run", "ghosty spaces ls --user brenda@siiqtec.com", "ghosty spaces join denik", "ghosty spaces set denik --combo sales --dry-run", "ghosty spaces set mailmask --model-cap 4 --house-claude on", 'ghosty spaces renew descti --until 2026-11-01 --note "pag\xF3 octubre"', "ghosty spaces rename totequim siiqtec --dry-run", "ghosty spaces archive box-xfer-test --dry-run", "ghosty spaces transfer siiqtec --to siiqtec@gmail.com --keep-as admin --copy-keys --dry-run", "ghosty spaces log siiqtec --since 30d"],
13295
+ notes: ["create follows the same rules as the web: you own it, it starts as a 30-day trial, your email must be confirmed, and your plan caps how many you own. Next: ghosty partner enable <slug>.", "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.", "archive does what Settings \u2192 Archive does: the workspace leaves /app and spaces ls, its agents and data stay; it asks you to type the slug. Undo: ghosty spaces restore <slug>. Logged as workspace.archive / workspace.restore.", "transfer moves the agents with the workspace: ACP boxes move to the new owner with their /data, pool boxes are recycled (the owner is baked into the box); without --copy-keys the dry run lists which keys the new owner lacks \u2014 those agents would stop answering."],
13268
13296
  learn: `${REF}#espacios`
13269
13297
  };
13270
13298
  HELPS.workspaces = HELPS.spaces;
@@ -13844,7 +13872,7 @@ ${bagsText(r)}`);
13844
13872
  }
13845
13873
  }
13846
13874
  async function spacesCmd(sub, slug, arg, v, context) {
13847
- const ex = "ghosty spaces ls [--user EMAIL] \xB7 create <slug> [--combo teams|sales|both] [--dry-run] \xB7 join|leave <workspace> \xB7 set <workspace> --combo teams|sales|both | --model-cap 0.25|1|4|none | --house-claude on|off \xB7 renew <workspace> --until 2026-11-01 [--note TEXT] \xB7 rename <old> <new> [--dry-run] \xB7 transfer <workspace> --to EMAIL [--keep-as admin|member|none] [--copy-keys] [--dry-run] \xB7 log <workspace> [--since 30d] [--action workspace]";
13875
+ const ex = "ghosty spaces ls [--user EMAIL] \xB7 create <slug> [--combo teams|sales|both] [--dry-run] \xB7 join|leave <workspace> \xB7 set <workspace> --combo teams|sales|both | --model-cap 0.25|1|4|none | --house-claude on|off \xB7 renew <workspace> --until 2026-11-01 [--note TEXT] \xB7 rename <old> <new> [--dry-run] \xB7 archive|restore <workspace> [--dry-run] \xB7 transfer <workspace> --to EMAIL [--keep-as admin|member|none] [--copy-keys] [--dry-run] \xB7 log <workspace> [--since 30d] [--action workspace]";
13848
13876
  if (!sub || sub === "ls" || sub === "list") {
13849
13877
  const r = await api(`/api/v2/workspaces${v.user ? `?user=${enc10(v.user)}` : ""}`);
13850
13878
  const rows = r.workspaces.map((w) => ({ workspace: w.slug, role: w.role ?? "", plan: w.plan, combo: w.combo, owner: w.owner ?? "", paid: w.paidUntil ?? "" }));
@@ -13927,6 +13955,22 @@ ${t("Rename?", "\xBFRenombrar?")}`, { yes: v.yes, expect: plan.from, retry: `gho
13927
13955
  const r = await api(path, { method: "POST", body: { to: arg } });
13928
13956
  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}`)}`);
13929
13957
  }
13958
+ if (sub === "archive" || sub === "restore") {
13959
+ slug = needSpace(slug, `ghosty spaces ${sub} <workspace> --dry-run`);
13960
+ const archive = sub === "archive";
13961
+ const path = wsPath(slug, "/archive");
13962
+ const plan = await api(path, { method: "POST", body: { archive, dryRun: true } });
13963
+ const line2 = `${plan.slug}: ${plan.from} \u2192 ${bold(plan.to)} ${dim(t(`(${plan.agents} agent(s); they and their data stay as they are)`, `(${plan.agents} agente(s); ellos y sus datos se quedan igual)`))}`;
13964
+ if (!plan.changed) return out(plan, () => dim(t(`Nothing to do: ${plan.slug} is already ${plan.from}.`, `Nada que hacer: ${plan.slug} ya est\xE1 ${plan.from}.`)));
13965
+ if (v["dry-run"]) return out(plan, () => `${line2}
13966
+ ${dim(t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada."))}`);
13967
+ const retry = `ghosty spaces ${sub} ${slug}`;
13968
+ requireYesIfHeadless(v.yes, retry);
13969
+ await confirm(`${line2}
13970
+ ${archive ? t("Archive it?", "\xBFArchivarlo?") : t("Restore it?", "\xBFRestaurarlo?")}`, { yes: v.yes, retry, ...archive ? { expect: plan.slug } : {} });
13971
+ const r = await api(path, { method: "POST", body: { archive } });
13972
+ return out(r, () => archive ? `${lavender("\u2713")} ${t(`Archived: ${r.slug}. Undo: ghosty spaces restore ${r.slug}`, `Archivado: ${r.slug}. Deshacer: ghosty spaces restore ${r.slug}`)}` : `${lavender("\u2713")} ${t(`Restored: ${r.slug}.`, `Restaurado: ${r.slug}.`)}`);
13973
+ }
13930
13974
  if (sub === "transfer") {
13931
13975
  slug = needSpace(slug ?? context, "ghosty spaces transfer <workspace> --to EMAIL --dry-run");
13932
13976
  const toEmail = v.to ?? arg;
@@ -14641,7 +14685,8 @@ ${bold("Workspaces")} ${dim("(changing them: Ghosty staff)")}
14641
14685
  spaces set <workspace> --combo sales teams | sales | both (without Sales its boards are invisible) [--model-cap 0.25|1|4|none: most expensive model] [--house-claude on|off]
14642
14686
  spaces renew <workspace> --until DATE Move \xABpaid until\xBB (logged) [--note] [--dry-run]
14643
14687
  spaces rename <old> <new> Change its slug only [--dry-run]
14644
- 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]
14688
+ spaces archive|restore <workspace> Archive it (out of /app and spaces ls; agents and data stay) or bring it back (owner or staff) [--dry-run]
14689
+ spaces transfer <workspace> --to EMAIL New owner; agents go with it (ACP boxes move with their /data, pool boxes are recycled) [--keep-as admin|member|none] [--copy-keys] [--dry-run]
14645
14690
  spaces log <workspace> Its log: transfers, staff in/out, renewals (owner or staff) [--since 30d] [--action workspace]
14646
14691
  staff ls [staff-email] [client-email] Ghosty staff assigned to client accounts (staff) [--all: also revoked]
14647
14692
  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]
@@ -14808,7 +14853,8 @@ ${bold("Espacios")} ${dim("(cambiarlos: staff de Ghosty)")}
14808
14853
  spaces set <espacio> --combo sales teams | sales | both (sin Ventas sus tableros no se ven) [--model-cap 0.25|1|4|none: el modelo m\xE1s caro] [--house-claude on|off]
14809
14854
  spaces renew <espacio> --until FECHA Mueve \xABpagado hasta\xBB (queda en bit\xE1cora) [--note] [--dry-run]
14810
14855
  spaces rename <viejo> <nuevo> Cambia s\xF3lo su slug [--dry-run]
14811
- 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]
14856
+ spaces archive|restore <espacio> Arch\xEDvalo (sale de /app y de spaces ls; agentes y datos se quedan) o tr\xE1elo de vuelta (due\xF1o o staff) [--dry-run]
14857
+ spaces transfer <espacio> --to CORREO Due\xF1o nuevo; los agentes se van con \xE9l (las cajas ACP se mudan con su /data, las del pool se reciclan) [--keep-as admin|member|none] [--copy-keys] [--dry-run]
14812
14858
  spaces log <espacio> Su bit\xE1cora: traspasos, staff que entra o sale, renovaciones (due\xF1o o staff) [--since 30d] [--action workspace]
14813
14859
  staff ls [correo-staff] [correo-cliente] Staff de Ghosty asignado a cuentas cliente (staff) [--all: tambi\xE9n revocadas]
14814
14860
  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]
@@ -15172,6 +15218,7 @@ async function main() {
15172
15218
  avatar: { type: "string" },
15173
15219
  tool: { type: "string" },
15174
15220
  "from-workspace": { type: "boolean" },
15221
+ archived: { type: "boolean" },
15175
15222
  "quota-hits": { type: "boolean" },
15176
15223
  by: { type: "string" },
15177
15224
  models: { type: "boolean" },
@@ -15221,7 +15268,7 @@ async function main() {
15221
15268
  const tokens = splitTokens(pos, v);
15222
15269
  globals.token = tokens.session;
15223
15270
  globals.profile = v.profile;
15224
- if (v.version) return out({ version: "0.58.1" }, () => "0.58.1");
15271
+ if (v.version) return out({ version: "0.60.0" }, () => "0.60.0");
15225
15272
  for (const n of normalizeArgs(pos, v)) if (!globals.json) info(dim(n));
15226
15273
  const context = (await readConfig()).workspace;
15227
15274
  if (!explicitToken()?.startsWith("gat_")) {
@@ -15270,7 +15317,7 @@ async function main() {
15270
15317
  case "agents":
15271
15318
  if ((sub === "ls" || sub === "list" || !sub) && v.user && v.app !== void 0) return appAgentsOf(v.user, v.app);
15272
15319
  if ((sub === "ls" || sub === "list" || !sub) && v.handle) return agentsByHandle(v.handle, v.workspace ?? context);
15273
- if (sub === "ls" || sub === "list" || !sub) return agentsLs(v.all ? void 0 : v.workspace ?? context, { sponsor: v.sponsor, owner: v.owner, engine: v.engine, ids: v.ids, all: v.all });
15320
+ if (sub === "ls" || sub === "list" || !sub) return agentsLs(v.all ? void 0 : v.workspace ?? context, { sponsor: v.sponsor, owner: v.owner, engine: v.engine, ids: v.ids, all: v.all, archived: v.archived });
15274
15321
  if (sub === "create" || sub === "new") {
15275
15322
  if (v.workspace === "none") delete v.workspace;
15276
15323
  else if (!v.workspace && context) {
@@ -15284,6 +15331,7 @@ async function main() {
15284
15331
  if (sub === "teams") return agentsTeams(a1, a2, pos[4], v);
15285
15332
  if (sub === "set") return agentsSet(a1, v);
15286
15333
  if (sub === "rm" || sub === "delete") return agentsRm(a1, v);
15334
+ if (sub === "archive" || sub === "restore") return agentsArchive(a1, sub === "archive");
15287
15335
  if (sub === "doctor" && v.user) return userDoctor(v.user, a1, v);
15288
15336
  if (sub === "doctor") return agentsDoctor(a1, v);
15289
15337
  if (sub === "docs" || sub === "documents") return agentDocsCmd(a1, a2, pos.slice(4).filter((x) => x !== void 0), v);
@@ -15387,7 +15435,7 @@ async function main() {
15387
15435
  throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
15388
15436
  }
15389
15437
  }
15390
- var notice = updateNotice("0.58.1").catch(() => null);
15438
+ var notice = updateNotice("0.60.0").catch(() => null);
15391
15439
  main().then(
15392
15440
  async () => {
15393
15441
  const n = await notice;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghostystudio/cli",
3
- "version": "0.58.1",
3
+ "version": "0.60.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": {