@ghostystudio/cli 0.41.1 → 0.42.1

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 +5 -1
  2. package/dist/index.js +84 -37
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -30,7 +30,7 @@ ghosty apply <file|dir> [--agent X] [--create] [--prune] [--dry-run] # make an
30
30
  ghosty agents set <id> [--name] [--engine] [--model] [--prompt | --prompt-file FILE] [--env K=V,…] [--connect ID,…] [--disconnect ID,…] [--toolsets +crm]
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
- ghosty agents teams ls|on|off <id> [workspace] [--handle x] # which Teams workspaces it answers in, and its @handle there
33
+ ghosty agents teams ls|on|off|rm <id> [workspace] [--handle x] # which Teams workspaces it answers in, and its @handle there
34
34
  ghosty agents rm <id> [--from-workspace] [--yes] # --from-workspace: turn it off in Teams first
35
35
  ghosty agents set <id> [--tool wa:tool=off,…] [--bag NAME|none] [--avatar URL|FILE]
36
36
  ghosty agents restart <id>
@@ -101,6 +101,10 @@ Every command has its own help with examples: `ghosty <command> --help` or `ghos
101
101
 
102
102
  Exit codes: `0` ok · `1` API error · `2` usage error · `3` not signed in.
103
103
 
104
+ ### Changes in 0.42.0
105
+
106
+ - `agents teams off` now **pauses** the agent in that workspace (it keeps its `@handle`; `on` resumes it). `agents teams rm <id> <workspace> [--yes]` removes it and frees the handle. `ls` shows `paused` and the workspaces where it was activated by someone else (read-only). Without `--handle`, `on` reuses the handle it already has in your other workspaces.
107
+
104
108
  ### Changes in 0.41.1
105
109
 
106
110
  - `agents teams`: the agent can be given by name (0.41.0 read `ls`/`on`/`off` as the agent).
package/dist/index.js CHANGED
@@ -7469,7 +7469,7 @@ var SERVER_MESSAGES = [
7469
7469
  ["s\xF3lo el due\xF1o del agente lo activa en sus equipos", "only the agent's owner turns it on in their workspaces"],
7470
7470
  ["s\xF3lo el due\xF1o del agente o staff lo activan en Teams", "only the agent's owner or staff turn it on in Teams"],
7471
7471
  ["falta workspace (el slug del equipo)", "missing workspace (the workspace slug)"],
7472
- ["Ese agente est\xE1 activo en {}. Ap\xE1galo ah\xED (Canales \u2192 Ghosty Teams) antes de borrarlo.", "That agent is active in {}. Turn it off there (Channels \u2192 Ghosty Teams) before deleting it."],
7472
+ ["Ese agente est\xE1 activo en {}. Qu\xEDtalo de ah\xED (Canales \u2192 Ghosty Teams) antes de borrarlo.", "That agent is active in {}. Remove it from there (Channels \u2192 Ghosty Teams) before deleting it."],
7473
7473
  ["sin cupo para otro agente ahora", "no room for another agent right now"],
7474
7474
  ["Tu plan incluye 1 agente.", "Your plan includes 1 agent."],
7475
7475
  ["Tu plan incluye {} agentes.", "Your plan includes {} agents."],
@@ -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.41.1"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
8425
+ headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.42.1"}`, "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.41.1"}`,
8467
+ "user-agent": `ghosty-cli/${"0.42.1"}`,
8468
8468
  "accept-language": lang(),
8469
8469
  ...opts.body !== void 0 ? { "content-type": "application/json" } : {},
8470
8470
  ...opts.headers
@@ -8910,22 +8910,32 @@ ${rows.length ? table(rows, ["email", "role", "accepted"]) : dim(t("Not shared w
8910
8910
  out(r, () => `${lavender("\u2713")} ${r.already ? t(`Already shared with ${r.email}`, `Ya estaba compartido con ${r.email}`) : t(`Shared with ${r.email}; they got an email`, `Compartido con ${r.email}; le lleg\xF3 un correo`)} ${dim(`(${r.role})`)}.`);
8911
8911
  }
8912
8912
  async function agentsTeams(sub, id, ws, v) {
8913
- const ex = "ghosty agents teams ls <agent-id> \xB7 on <agent-id> <workspace> [--handle x] \xB7 off <agent-id> <workspace>";
8914
- if (sub !== "ls" && sub !== "on" && sub !== "off") throw usage(t("Use ls, on or off.", "Usa ls, on u off."), ex);
8913
+ const ex = "ghosty agents teams ls <agent-id> \xB7 on <agent-id> <workspace> [--handle x] \xB7 off <agent-id> <workspace> \xB7 rm <agent-id> <workspace> [--yes]";
8914
+ if (sub !== "ls" && sub !== "on" && sub !== "off" && sub !== "rm") throw usage(t("Use ls, on, off or rm.", "Usa ls, on, off o rm."), ex);
8915
8915
  id = need(id, "agent id", ex);
8916
8916
  if (sub === "ls") {
8917
8917
  const r2 = await api(agentPath(id, "/teams"));
8918
- const rows = r2.teams.map((x) => ({
8919
- workspace: x.slug,
8920
- handle: x.active ? `@${x.handle}` : dim(`(@${x.suggested})`),
8921
- state: !x.confirmed ? t("unconfirmed", "sin confirmar") : x.active ? t("on", "prendido") : t("off", "apagado")
8922
- }));
8918
+ const label = { on: t("on", "prendido"), paused: t("paused", "en pausa"), off: t("off", "apagado") };
8919
+ const rows = r2.teams.map((x) => {
8920
+ const state = x.state ?? (x.active ? "on" : "off");
8921
+ return {
8922
+ workspace: x.slug,
8923
+ handle: state === "off" ? dim(`(@${x.suggested})`) : `@${x.handle}`,
8924
+ state: !x.confirmed ? t("unconfirmed", "sin confirmar") : label[state] + (x.editable === false ? dim(t(" \xB7 not yours to change", " \xB7 no lo administras")) : "")
8925
+ };
8926
+ });
8923
8927
  return out(r2, () => rows.length ? table(rows, ["workspace", "handle", "state"]) : dim(t("You don't administer any Teams workspace yet.", "A\xFAn no administras ning\xFAn equipo de Teams.")));
8924
8928
  }
8925
8929
  const workspace = need(ws ?? v.workspace, "workspace", ex);
8930
+ if (sub === "rm") {
8931
+ requireYesIfHeadless(v.yes, `ghosty agents teams rm ${id} ${workspace}`);
8932
+ if (!v.yes) await confirm(t(`Remove it from ${workspace}? It frees its @handle there; the agent stays.`, `\xBFQuitarlo de ${workspace}? Suelta su @handle ah\xED; el agente sigue.`), { retry: `ghosty agents teams rm ${id} ${workspace} --yes` });
8933
+ const r2 = await api(agentPath(id, `/teams?workspace=${encodeURIComponent(workspace)}&remove=1`), { method: "DELETE", body: { workspace, remove: true } });
8934
+ return out(r2, () => `${lavender("\u2713")} ${r2.wasThere ? t(`Removed from ${workspace}.`, `Quitado de ${workspace}.`) : t(`It wasn't in ${workspace}.`, `No estaba en ${workspace}.`)}`);
8935
+ }
8926
8936
  if (sub === "off") {
8927
8937
  const r2 = await api(agentPath(id, `/teams?workspace=${encodeURIComponent(workspace)}`), { method: "DELETE", body: { workspace } });
8928
- return out(r2, () => `${lavender("\u2713")} ${r2.wasActive ? t(`It no longer answers in ${workspace}.`, `Ya no contesta en ${workspace}.`) : t(`It wasn't active in ${workspace}.`, `No estaba activo en ${workspace}.`)}`);
8938
+ return out(r2, () => `${lavender("\u2713")} ${r2.wasActive ? t(`Paused in ${workspace}; it keeps its @handle.`, `En pausa en ${workspace}; conserva su @handle.`) : t(`It wasn't answering in ${workspace}.`, `No estaba contestando en ${workspace}.`)}`);
8929
8939
  }
8930
8940
  const r = await api(agentPath(id, "/teams"), { method: "POST", body: { workspace, ...v.handle ? { handle: v.handle } : {} } });
8931
8941
  out(r, () => `${lavender("\u2713")} ${r.changed ? t(`Answers in ${workspace} as @${r.handle}.`, `Contesta en ${workspace} como @${r.handle}.`) : t(`Already answering in ${workspace} as @${r.handle}.`, `Ya contestaba en ${workspace} como @${r.handle}.`)}`);
@@ -9263,26 +9273,61 @@ async function turn(agent, sid, text, first, images = []) {
9263
9273
  let answer = "";
9264
9274
  let usageData;
9265
9275
  let ours = false;
9276
+ let terminado = false;
9277
+ let stream = res;
9266
9278
  try {
9267
9279
  await sent;
9268
- for await (const ev of readSse(res.body)) {
9269
- const d = parseData(ev);
9270
- if (d.turnId) ours = d.turnId === turnId;
9271
- if (!ours) continue;
9272
- if (ev.event === "chunk" && d.text) {
9273
- answer += d.text;
9274
- if (globals.json) process.stdout.write(JSON.stringify({ event: "chunk", text: d.text }) + "\n");
9275
- else process.stdout.write(d.text);
9276
- } else if (ev.event === "tool" && d.title) {
9277
- if (globals.json) process.stdout.write(JSON.stringify({ event: "tool", ...d }) + "\n");
9278
- else if (!d.status || d.status === "pending" || d.status === "in_progress") info(dim(` \u2699 ${serverText(d.title)}`));
9279
- } else if (ev.event === "usage") {
9280
- usageData = d;
9281
- } else if (ev.event === "error") {
9282
- throw new CliError(d.message ? serverText(d.message) : t("The turn failed.", "El turno fall\xF3."), EXIT.API);
9283
- } else if (ev.event === "done") {
9284
- break;
9280
+ for (let intento = 0; ; intento++) {
9281
+ let recibido = "";
9282
+ try {
9283
+ for await (const ev of readSse(stream.body)) {
9284
+ const d = parseData(ev);
9285
+ if (intento > 0 && ev.event === "done" && d.reposo) {
9286
+ if (d.interrumpido?.turnId === turnId) throw new CliError(serverText(d.interrumpido.motivo ?? t("The turn was cut off.", "El turno se cort\xF3.")), EXIT.API);
9287
+ if (d.ultimo?.turnId === turnId && d.ultimo.text) recibido = d.ultimo.text;
9288
+ else if (!ours) {
9289
+ terminado = true;
9290
+ break;
9291
+ }
9292
+ }
9293
+ if (d.turnId) ours = d.turnId === turnId;
9294
+ if (!ours && !(intento > 0 && ev.event === "done" && d.reposo)) continue;
9295
+ if (ev.event === "chunk" && d.text) recibido += d.text;
9296
+ if (ev.event === "reemplazo" && d.text) {
9297
+ answer = recibido = d.text;
9298
+ continue;
9299
+ }
9300
+ if ((ev.event === "chunk" || ev.event === "done") && recibido.length > answer.length && recibido.startsWith(answer)) {
9301
+ const nuevo = recibido.slice(answer.length);
9302
+ answer = recibido;
9303
+ if (globals.json) process.stdout.write(JSON.stringify({ event: "chunk", text: nuevo }) + "\n");
9304
+ else process.stdout.write(nuevo);
9305
+ }
9306
+ if (ev.event === "tool" && d.title) {
9307
+ if (globals.json) process.stdout.write(JSON.stringify({ event: "tool", ...d }) + "\n");
9308
+ else if (!d.status || d.status === "pending" || d.status === "in_progress") info(dim(` \u2699 ${serverText(d.title)}`));
9309
+ } else if (ev.event === "usage") {
9310
+ usageData = d;
9311
+ } else if (ev.event === "error") {
9312
+ throw new CliError(d.message ? serverText(d.message) : t("The turn failed.", "El turno fall\xF3."), EXIT.API);
9313
+ } else if (ev.event === "done") {
9314
+ terminado = true;
9315
+ break;
9316
+ }
9317
+ }
9318
+ } catch (e) {
9319
+ if (e instanceof CliError || cancelled) throw e;
9320
+ }
9321
+ if (terminado || cancelled) break;
9322
+ if (intento >= 5) {
9323
+ throw new CliError(
9324
+ t(`Lost the connection to the turn; it may still be running. Check: ghosty conversations show ${agent} ${sid}`, `Se perdi\xF3 la conexi\xF3n con el turno; puede seguir corriendo. Revisa: ghosty conversations show ${agent} ${sid}`),
9325
+ EXIT.API
9326
+ );
9285
9327
  }
9328
+ await new Promise((r) => setTimeout(r, Math.min(5e3, 1e3 * (intento + 1))));
9329
+ if (!globals.json) info(dim(t("(reconnecting\u2026)", "(reconectando\u2026)")));
9330
+ stream = await request(convPath(agent, `/${enc3(sid)}/events`), { signal: ctrl.signal, headers: { accept: "text/event-stream" } });
9286
9331
  }
9287
9332
  } finally {
9288
9333
  process.off("SIGINT", onSigint);
@@ -9671,7 +9716,7 @@ async function call(path, v, init = {}) {
9671
9716
  "x-ghosty-ts": ts,
9672
9717
  "x-ghosty-sig": sig,
9673
9718
  "content-type": "application/json",
9674
- "user-agent": `ghosty-cli/${"0.41.1"}`
9719
+ "user-agent": `ghosty-cli/${"0.42.1"}`
9675
9720
  },
9676
9721
  body: method === "GET" ? void 0 : raw
9677
9722
  });
@@ -10980,9 +11025,10 @@ var HELPS_ES = {
10980
11025
  ["# your account: merge copies with the same content", "# tu cuenta: une las copias con el mismo contenido"],
10981
11026
  ["# staff: their agents, latest turns, plan and boxes", "# staff: sus agentes, \xFAltimos turnos, plan y cajas"],
10982
11027
  ["# what it really has: effective toolsets, connectors connected + granted", "# lo que de verdad tiene: toolsets efectivos, conectores conectados + concedidos"],
10983
- ["# Teams workspaces you administer: where it answers and with which @handle", "# los equipos de Teams que administras: d\xF3nde contesta y con qu\xE9 @handle"],
11028
+ ["# 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"],
10984
11029
  ["# turn it on there (or change its @handle)", "# lo prende ah\xED (o le cambia el @handle)"],
10985
- ["# turn it off there; the agent stays", "# lo apaga ah\xED; el agente sigue"]
11030
+ ["# pause it there; it keeps its @handle", "# lo pausa ah\xED; conserva su @handle"],
11031
+ ["# remove it from there; the agent stays", "# lo quita de ah\xED; el agente sigue"]
10986
11032
  ],
10987
11033
  flags: [
10988
11034
  ["--fields a,b", "get: regresa s\xF3lo estos campos"],
@@ -11383,9 +11429,10 @@ var HELPS = {
11383
11429
  "ghosty agents share <agent-id> <email> [--role editor|admin]",
11384
11430
  "ghosty agents unshare <agent-id> <email> [--yes]",
11385
11431
  "ghosty agents shares <agent-id>",
11386
- "ghosty agents teams ls <agent-id> # Teams workspaces you administer: where it answers and with which @handle",
11432
+ "ghosty agents teams ls <agent-id> # Teams workspaces you administer: where it answers, is paused, and with which @handle",
11387
11433
  "ghosty agents teams on <agent-id> <workspace> [--handle NAME] # turn it on there (or change its @handle)",
11388
- "ghosty agents teams off <agent-id> <workspace> # turn it off there; the agent stays",
11434
+ "ghosty agents teams off <agent-id> <workspace> # pause it there; it keeps its @handle",
11435
+ "ghosty agents teams rm <agent-id> <workspace> [--yes] # remove it from there; the agent stays",
11389
11436
  "ghosty agents restart <agent-id>",
11390
11437
  "ghosty agents box <agent-id> [--check PATH]\u2026 [--contains PATH=TEXT]\u2026 [--recycle]",
11391
11438
  "ghosty agents demo enable|disable <agent-id>",
@@ -13029,7 +13076,7 @@ ${bold("Agents")}
13029
13076
  agents docs replace <id> <doc> New version (keeps name and access) --url URL | --file PATH [--note]
13030
13077
  agents docs history|restore <id> <doc> [n] Its versions; restore n as a new version
13031
13078
  agents share|unshare <id> <email> Share it without handing it over [--role editor|admin] \xB7 agents shares <id> lists them
13032
- agents teams ls|on|off <id> [ws] Which Teams workspaces it answers in, and its @handle there [--handle x]
13079
+ agents teams ls|on|off|rm <id> [ws] Which Teams workspaces it answers in, and its @handle there [--handle x]
13033
13080
  agents rm <id> Delete an agent (owner only; asks you to type its name, or --yes) [--from-workspace: turn it off in Teams first]
13034
13081
  agents restart <id> Restart an ACP agent (needsRestart) so it loads new skills
13035
13082
  agents box <id> The machine serving it: is it on the new image? [--check PATH]\u2026 [--contains PATH=TEXT]\u2026 [--recycle [--reenv] [--force]]
@@ -13165,7 +13212,7 @@ ${bold("Agentes")}
13165
13212
  agents docs replace <id> <doc> Versi\xF3n nueva (conserva nombre y accesos) --url URL | --file RUTA [--note]
13166
13213
  agents docs history|restore <id> <doc> [n] Sus versiones; restaura la n como versi\xF3n nueva
13167
13214
  agents share|unshare <id> <correo> Comp\xE1rtelo sin cederlo [--role editor|admin] \xB7 agents shares <id> los lista
13168
- agents teams ls|on|off <id> [equipo] En qu\xE9 equipos de Teams contesta y con qu\xE9 @handle [--handle x]
13215
+ agents teams ls|on|off|rm <id> [equipo] En qu\xE9 equipos de Teams contesta y con qu\xE9 @handle [--handle x]
13169
13216
  agents rm <id> Borra un agente (s\xF3lo el due\xF1o; te pide escribir su nombre, o --yes) [--from-workspace: antes lo desactiva en Teams]
13170
13217
  agents restart <id> Reinicia un agente ACP (needsRestart) para que cargue skills nuevas
13171
13218
  agents box <id> La m\xE1quina que lo atiende: \xBFya trae la imagen nueva? [--check RUTA]\u2026 [--contains RUTA=TEXTO]\u2026 [--recycle [--reenv] [--force]]
@@ -13478,7 +13525,7 @@ async function main() {
13478
13525
  const tokens = splitTokens(pos, v);
13479
13526
  globals.token = tokens.session;
13480
13527
  globals.profile = v.profile;
13481
- if (v.version) return out({ version: "0.41.1" }, () => "0.41.1");
13528
+ if (v.version) return out({ version: "0.42.1" }, () => "0.42.1");
13482
13529
  for (const n of normalizeArgs(pos, v)) if (!globals.json) info(dim(n));
13483
13530
  const context = (await readConfig()).workspace;
13484
13531
  if (!explicitToken()?.startsWith("gat_")) {
@@ -13631,7 +13678,7 @@ async function main() {
13631
13678
  throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
13632
13679
  }
13633
13680
  }
13634
- var notice = updateNotice("0.41.1").catch(() => null);
13681
+ var notice = updateNotice("0.42.1").catch(() => null);
13635
13682
  main().then(
13636
13683
  async () => {
13637
13684
  const n = await notice;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghostystudio/cli",
3
- "version": "0.41.1",
3
+ "version": "0.42.1",
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": {