@ghostystudio/cli 0.51.0 → 0.52.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 +8 -0
  2. package/dist/index.js +141 -8
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -40,6 +40,8 @@ ghosty agents demo <id> [--slug NAME] [--expires "YYYY-MM-DDTHH:MM"|no] [--turns
40
40
  [--welcome TEXT] [--starter TEXT] [--chips "a|b|c"] [--sponsor on|off] [--rotate] # public /p/ link, no account needed
41
41
  ghosty agents embed enable|disable <id> [--domains a.com,*.b.com] # web widget: chat bubble on your site, only on those domains
42
42
  ghosty agents embed <id> [--per-visitor N] [--per-ip N] [--welcome TEXT] [--color #hex] [--rotate] # state + the <script> to paste
43
+ ghosty agents embed <id> [--title T] [--avatar URL] [--icon chat|avatar|ghost] [--position left] [--theme auto] [--bubble-text T] [--auto-open S] [--starters "a|b"] # its look
44
+ ghosty agents embed <id> [--style '{json}'] [--theme-source teams|site --site-url URL] [--style-file theme.json] # its theme · `theme` exports it
43
45
  ghosty agents doctor <id> [--verbose] # exits 1 when it finds a problem
44
46
  ghosty agents docs ls|grant|revoke <id> [doc] [--write] [--no-deliver]
45
47
  ghosty agents docs add <id> --url URL | --file PATH [--description TEXT] [--grant read|write|no]
@@ -105,6 +107,12 @@ Every command has its own help with examples: `ghosty <command> --help` or `ghos
105
107
 
106
108
  Exit codes: `0` ok · `1` API error · `2` usage error · `3` not signed in.
107
109
 
110
+ ### Changes in 0.52.0
111
+
112
+ - `agents embed <id>` customizes the web widget: `--title`, `--subtitle`, `--avatar URL|none`, `--icon chat|avatar|ghost`, `--placeholder`, `--position right|left`, `--offset X[,Y]`, `--theme light|dark|auto`, `--bubble-text`, `--auto-open S|off`, `--starters "a|b"` (up to 4 of yours; Ghosty's never show). The pasted `<script>` doesn't change.
113
+ - Fine style without free CSS: `--style '{json}'|none` (fontFamily from a Google Fonts allow-list, radius 0–24, header/bubble/input/send/launcher colors, titleUppercase, statusDot, sendArrow), shortcuts `--font` and `--radius`.
114
+ - Theme source: `--theme-source teams` (your Ghosty Teams workspace brand, `--workspace SLUG`) or `--theme-source site --site-url URL` (your site's CSS colors), imported as editable values. `agents embed <id> theme` exports it as JSON; `--style-file theme.json` applies it back.
115
+
108
116
  ### Changes in 0.51.0
109
117
 
110
118
  - `agents embed enable|disable <id> [--domains a.com,*.b.com] [--per-visitor N] [--per-ip N] [--welcome T] [--color #hex] [--rotate]`: the **web widget**, a chat bubble with the agent on your own site (one `<script>`), loading only on the domains you list; each visitor gets an isolated conversation and talks as a customer. Claude engine only. No flags = state + the snippet.
package/dist/index.js CHANGED
@@ -8468,7 +8468,7 @@ async function postLong(path, body, timeoutMs = 9e5) {
8468
8468
  const req = httpsRequest(`${base}${path}`, {
8469
8469
  method: "POST",
8470
8470
  timeout: timeoutMs,
8471
- headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.51.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
8471
+ headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.52.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
8472
8472
  }, (res) => {
8473
8473
  let buf = "";
8474
8474
  res.on("data", (c) => buf += c);
@@ -8510,7 +8510,7 @@ async function request(path, opts = {}, retried = false) {
8510
8510
  method: opts.method ?? (opts.body !== void 0 || opts.raw !== void 0 ? "POST" : "GET"),
8511
8511
  headers: {
8512
8512
  authorization: `Bearer ${token}`,
8513
- "user-agent": `ghosty-cli/${"0.51.0"}`,
8513
+ "user-agent": `ghosty-cli/${"0.52.0"}`,
8514
8514
  "accept-language": lang(),
8515
8515
  ...opts.body !== void 0 ? { "content-type": "application/json" } : {},
8516
8516
  ...opts.headers
@@ -9344,8 +9344,18 @@ async function agentsDemo(id, v) {
9344
9344
  ].join("\n")
9345
9345
  );
9346
9346
  }
9347
- async function agentsEmbed(id, v) {
9347
+ function styleSummary(st) {
9348
+ if (!st) return "";
9349
+ return Object.entries(st).filter(([k, x]) => x !== null && !(k === "headerAvatar" && x === true) && !(k === "titleUppercase" && x === false) && !(k === "sendArrow" && x === "up")).map(([k, x]) => `${k}=${x}`).join(" ");
9350
+ }
9351
+ async function agentsEmbed(id, v, action) {
9348
9352
  id = need(id, "agent id", "ghosty agents embed enable <agent-id> --domains tusitio.com");
9353
+ if (action === "theme") {
9354
+ const e2 = await api(agentPath(id, "/embed"));
9355
+ const theme = { themeSource: e2.themeSource ?? "custom", themeFrom: e2.themeFrom ?? null, siteUrl: e2.siteUrl ?? null, color: e2.color, theme: e2.theme ?? "light", style: e2.style ?? {} };
9356
+ return out(theme, () => JSON.stringify(theme, null, 2));
9357
+ }
9358
+ if (action) throw usage(t(`Unknown: agents embed <id> ${action}`, `No existe: agents embed <id> ${action}`), "ghosty agents embed <id> theme");
9349
9359
  if (v.on && v.off) throw usage(t("Use enable or disable, not both.", "Usa enable o disable, no los dos."));
9350
9360
  const body = {};
9351
9361
  if (v.on) body.enabled = true;
@@ -9355,6 +9365,57 @@ async function agentsEmbed(id, v) {
9355
9365
  if (v["per-ip"]) body.perIpDay = Number(v["per-ip"]);
9356
9366
  if (v.welcome !== void 0) body.welcome = v.welcome || null;
9357
9367
  if (v.color !== void 0) body.color = v.color;
9368
+ const clear = (x) => x === "" || x.toLowerCase() === "none" ? null : x;
9369
+ if (v.avatar !== void 0) body.avatarUrl = clear(v.avatar);
9370
+ if (v.title !== void 0) body.title = clear(v.title);
9371
+ if (v.subtitle !== void 0) body.subtitle = clear(v.subtitle);
9372
+ if (v.placeholder !== void 0) body.placeholder = clear(v.placeholder);
9373
+ if (v["bubble-text"] !== void 0) body.bubbleText = clear(v["bubble-text"]);
9374
+ if (v.icon !== void 0) body.icon = v.icon;
9375
+ if (v.position !== void 0) body.position = v.position;
9376
+ if (v.theme !== void 0) body.theme = v.theme;
9377
+ if (v.offset !== void 0) {
9378
+ const [x, y] = v.offset.split(/[,x ]+/).map((n) => Number(n));
9379
+ if (!Number.isFinite(x)) throw usage(t("--offset takes px: 20 or 20,40 (side,bottom)", "--offset va en px: 20 o 20,40 (lado,abajo)"));
9380
+ body.offsetX = x;
9381
+ body.offsetY = Number.isFinite(y) ? y : x;
9382
+ }
9383
+ if (v["auto-open"] !== void 0) body.autoOpen = /^(off|no|0|)$/i.test(v["auto-open"]) ? 0 : Number(v["auto-open"]);
9384
+ if (v.starters !== void 0) body.starters = v.starters.split("|").map((x) => x.trim()).filter(Boolean);
9385
+ if (v.style !== void 0) {
9386
+ if (v.style.toLowerCase() === "none") body.style = null;
9387
+ else {
9388
+ try {
9389
+ body.style = JSON.parse(v.style);
9390
+ } catch {
9391
+ throw usage(t(`--style takes JSON: '{"headerBg":"#2d4a2e","radius":0}'`, `--style va en JSON: '{"headerBg":"#2d4a2e","radius":0}'`));
9392
+ }
9393
+ }
9394
+ }
9395
+ if (v["style-file"]) {
9396
+ let j;
9397
+ try {
9398
+ j = JSON.parse(await readFile3(v["style-file"], "utf8"));
9399
+ } catch (err) {
9400
+ throw usage(t(`Could not read ${v["style-file"]} as JSON: ${err.message}`, `No pude leer ${v["style-file"]} como JSON: ${err.message}`));
9401
+ }
9402
+ const isTheme = j && typeof j === "object" && "style" in j;
9403
+ body.style = isTheme ? j.style : j;
9404
+ if (isTheme && typeof j.color === "string" && body.color === void 0) body.color = j.color;
9405
+ if (isTheme && typeof j.theme === "string" && body.theme === void 0) body.theme = j.theme;
9406
+ }
9407
+ if (v["theme-source"] !== void 0) body.themeSource = v["theme-source"];
9408
+ if (v["site-url"] !== void 0) {
9409
+ body.siteUrl = v["site-url"];
9410
+ if (body.themeSource === void 0) body.themeSource = "site";
9411
+ }
9412
+ if (body.themeSource === "teams" && v.workspace) body.teamsWorkspace = v.workspace;
9413
+ if (v.font !== void 0 || v.radius !== void 0) {
9414
+ const st = body.style && typeof body.style === "object" ? body.style : {};
9415
+ if (v.font !== void 0) st.fontFamily = clear(v.font);
9416
+ if (v.radius !== void 0) st.radius = v.radius === "" || v.radius === "none" ? null : Number(v.radius);
9417
+ body.style = st;
9418
+ }
9358
9419
  if (v.rotate) body.rotate = true;
9359
9420
  const e = Object.keys(body).length ? await api(agentPath(id, "/embed"), { method: "PUT", body }) : await api(agentPath(id, "/embed"));
9360
9421
  out(
@@ -9364,7 +9425,14 @@ async function agentsEmbed(id, v) {
9364
9425
  e.domains.length ? `${t("domains", "dominios")}: ${e.domains.join(", ")}` : red(t("domains: (none \u2014 it loads nowhere; add --domains yoursite.com)", "dominios: (ninguno \u2014 no carga en ning\xFAn lado; agrega --domains tusitio.com)")),
9365
9426
  ...e.engineOk ? [] : [red(t("engine: only claude isolates each visitor; switch with `ghosty agents set <id> --engine claude`", "motor: s\xF3lo claude a\xEDsla a cada visitante; c\xE1mbialo con `ghosty agents set <id> --engine claude`"))],
9366
9427
  dim(t(`cap ${e.perVisitorDay} messages per visitor per day \xB7 ${e.perIpDay} per IP per day \xB7 color ${e.color}`, `tope ${e.perVisitorDay} mensajes por visitante al d\xEDa \xB7 ${e.perIpDay} por IP al d\xEDa \xB7 color ${e.color}`)),
9367
- e.welcome ? `${t("welcome", "bienvenida")}: ${e.welcome}` : dim(t("welcome: (default greeting)", "bienvenida: (saludo por defecto)")),
9428
+ `${t("first message", "primer mensaje")}: ${e.welcome ?? dim(e.welcomeShown ?? t("(default greeting)", "(saludo por defecto)"))}`,
9429
+ `${t("header", "encabezado")}: ${e.title ?? e.agentName ?? ""}${e.subtitle ? ` \xB7 ${e.subtitle}` : ""} ${dim(`${t("avatar", "avatar")} ${e.avatarUrl ?? t("ghost", "fantasma")} \xB7 ${t("icon", "\xEDcono")} ${e.icon ?? "chat"}`)}`,
9430
+ dim(
9431
+ `${t("position", "lado")} ${e.position ?? "right"} (${e.offsetX ?? 20},${e.offsetY ?? 20}px) \xB7 ${t("theme", "tema")} ${e.theme ?? "light"} \xB7 ${t("text on color", "texto sobre el color")} ${e.textColor ?? ""} \xB7 ${t("auto-open", "abrir solo")} ${e.autoOpen ? `${e.autoOpen}s` : "off"}${e.bubbleText ? ` \xB7 ${t("label", "etiqueta")} \xAB${e.bubbleText}\xBB` : ""}${e.placeholder ? ` \xB7 placeholder \xAB${e.placeholder}\xBB` : ""}`
9432
+ ),
9433
+ e.starters?.length ? `${t("suggestions", "sugerencias")}: ${e.starters.join(" | ")}` : dim(t("suggestions: (none)", "sugerencias: (ninguna)")),
9434
+ `${t("theme", "tema")}: ${e.themeSource ?? "custom"}${e.themeFrom ? dim(` \xB7 ${e.themeFrom}`) : ""}`,
9435
+ ...styleSummary(e.style) ? [dim(`${t("style", "estilo")}: ${styleSummary(e.style)}`)] : [],
9368
9436
  ...e.snippet ? ["", t("Paste before </body> on your site:", "Pega antes de </body> en tu sitio:"), e.snippet, ""] : [],
9369
9437
  t(`visitors ${e.stats.visitantes} \xB7 messages ${e.stats.mensajes}`, `visitantes ${e.stats.visitantes} \xB7 mensajes ${e.stats.mensajes}`),
9370
9438
  ...e.lastError ? [red(`${t("last failure", "\xFAltima falla")} ${e.lastError.at}: ${serverText(e.lastError.message ?? e.lastError.error ?? "")}`)] : []
@@ -9982,7 +10050,7 @@ async function call(path, v, init = {}) {
9982
10050
  "x-ghosty-ts": ts,
9983
10051
  "x-ghosty-sig": sig,
9984
10052
  "content-type": "application/json",
9985
- "user-agent": `ghosty-cli/${"0.51.0"}`
10053
+ "user-agent": `ghosty-cli/${"0.52.0"}`
9986
10054
  },
9987
10055
  body: method === "GET" ? void 0 : raw
9988
10056
  });
@@ -11587,6 +11655,10 @@ var HELPS_ES = {
11587
11655
  summary: "Crea, revisa, cambia, diagnostica y borra agentes.",
11588
11656
  replace: [
11589
11657
  ["# the agent as a file; see ghosty apply --help", "# el agente como archivo; ver ghosty apply --help"],
11658
+ ["# its look", "# su apariencia"],
11659
+ ["# its theme", "# su tema"],
11660
+ ["# export the theme as JSON (edit it, apply it back with --style-file)", "# exporta el tema en JSON (ed\xEDtalo y vuelve a aplicarlo con --style-file)"],
11661
+ ["--style-file theme.json", "--style-file tema.json"],
11590
11662
  ["# who is @build in Teams (that workspace, or all of yours)", "# qui\xE9n es @build en Teams (en ese espacio, o en todos los tuyos)"],
11591
11663
  ["# change one piece of the prompt, on the server's current one", "# cambia un pedazo del prompt, sobre el actual del servidor"],
11592
11664
  ["# your account: merge copies with the same content", "# tu cuenta: une las copias con el mismo contenido"],
@@ -11640,6 +11712,22 @@ var HELPS_ES = {
11640
11712
  ['--chips "a|b|c"', "demo: sugerencias, separadas por |"],
11641
11713
  ["--sponsor on|off", "demo: las copias que la gente se queda corren con TU llave del motor"],
11642
11714
  ["--rotate", "demo: c\xF3digo nuevo; la liga vieja deja de servir"],
11715
+ ["--domains a.com,*.b.com", "embed: d\xF3nde puede cargarse el widget (vac\xEDo = en ning\xFAn lado)"],
11716
+ ["--welcome TEXT", "embed: el primer mensaje que ve el visitante (vac\xEDo = \xAB\xA1Hola! \xBFEn qu\xE9 te ayudo?\xBB)"],
11717
+ ["--title T / --subtitle T", "embed: nombre en el encabezado del widget (default: el del agente) y un rengl\xF3n chico debajo (\xABResponde en minutos\xBB)"],
11718
+ ["--avatar URL|none", "embed: tu imagen (https JPEG/PNG/WebP, se rehospeda en img.ghosty.studio); none = el fantasma del agente"],
11719
+ ["--icon chat|avatar|ghost", "embed: qu\xE9 lleva el bot\xF3n flotante"],
11720
+ ["--placeholder T", "embed: el texto gris de la caja para escribir"],
11721
+ ["--position right|left / --offset X[,Y]", "embed: esquina de la p\xE1gina y separaci\xF3n en px del lado y de abajo (0-200, default 20)"],
11722
+ ["--theme light|dark|auto", "embed: tema del panel del chat (auto = el del visitante)"],
11723
+ ["--bubble-text T", "embed: etiqueta junto al bot\xF3n (\xAB\xBFDudas?\xBB)"],
11724
+ ["--auto-open S|off", "embed: se abre solo a los S segundos (1-120), una vez por visita, nunca en el tel\xE9fono"],
11725
+ ['--starters "a|b|c"', 'embed: hasta 4 sugerencias TUYAS ("" las quita; nunca salen las de Ghosty)'],
11726
+ ["--style '{json}'|none", "embed: estilo fino, s\xF3lo llaves conocidas: fontFamily, radius, headerBg, headerText, titleUppercase, statusDot, headerAvatar, panelBg, userBubbleBg/Text, agentBubbleBg/Text, inputBg, sendBg, sendIcon, sendArrow (up|right), launcherBg, launcherIcon. PATCH por llave (null resetea una); none resetea todo"],
11727
+ ["--font NOMBRE / --radius 0-24", "embed: atajos de style.fontFamily (lista de Google Fonts: Barlow, Inter, Poppins\u2026) y style.radius"],
11728
+ ["--theme-source custom|teams|site", "embed: importa el tema de la marca de tu espacio de Ghosty Teams (--workspace SLUG para elegir) o de tu sitio (--site-url); queda editable"],
11729
+ ["--site-url URL", "embed: el sitio del que se sacan los colores (implica --theme-source site)"],
11730
+ ["--style-file ARCHIVO", "embed: aplica un tema exportado con `agents embed <id> theme` (o s\xF3lo el objeto style)"],
11643
11731
  ["--check PATH", "box: este archivo debe existir en la m\xE1quina (repetible), p. ej. un archivo que trae la imagen nueva"],
11644
11732
  ["--contains PATH=TEXT", "box: ese archivo debe contener TEXT (un cambio que no agrega archivos)"],
11645
11733
  ["--recycle", "box: reemplaza sus m\xE1quinas (nunca a media respuesta); pool: la nueva nace en el siguiente turno; ACP (goose, lite): su caja se recrea ya con la imagen actual"],
@@ -12101,6 +12189,9 @@ var HELPS = {
12101
12189
  'ghosty agents demo <agent-id> [--slug NAME] [--expires "YYYY-MM-DDTHH:MM"|no] [--turns N] [--per-ip N] [--welcome TEXT] [--starter TEXT] [--chips "a|b|c"] [--sponsor on|off] [--rotate]',
12102
12190
  "ghosty agents embed enable|disable <agent-id> [--domains a.com,*.b.com] # web widget: chat bubble on your site, only on those domains",
12103
12191
  "ghosty agents embed <agent-id> [--domains \u2026] [--per-visitor N] [--per-ip N] [--welcome TEXT] [--color #hex] [--rotate] # state + the <script> to paste",
12192
+ 'ghosty agents embed <agent-id> [--title T] [--subtitle T] [--avatar URL] [--icon chat|avatar|ghost] [--placeholder T] [--position right|left] [--offset X,Y] [--theme light|dark|auto] [--bubble-text T] [--auto-open S] [--starters "a|b"] # its look',
12193
+ "ghosty agents embed <agent-id> [--theme-source custom|teams|site] [--site-url URL] [--style '{json}'] [--style-file theme.json] [--font NAME] [--radius 0-24] # its theme",
12194
+ "ghosty agents embed <agent-id> theme # export the theme as JSON (edit it, apply it back with --style-file)",
12104
12195
  "ghosty agents rm <agent-id> [--from-workspace] [--yes]"
12105
12196
  ],
12106
12197
  flags: [
@@ -12144,6 +12235,22 @@ var HELPS = {
12144
12235
  ['--chips "a|b|c"', "demo: suggestions, separated by |"],
12145
12236
  ["--sponsor on|off", "demo: copies people keep run on YOUR engine key"],
12146
12237
  ["--rotate", "demo: new code; the old link stops working"],
12238
+ ["--domains a.com,*.b.com", "embed: where the widget may load (empty = nowhere)"],
12239
+ ["--welcome TEXT", "embed: the first message the visitor sees (empty = \xAB\xA1Hola! \xBFEn qu\xE9 te ayudo?\xBB)"],
12240
+ ["--title T / --subtitle T", `embed: name in the widget header (default: the agent's name) and a small line under it ("Replies in minutes")`],
12241
+ ["--avatar URL|none", "embed: your own image (https JPEG/PNG/WebP, re-hosted on img.ghosty.studio); none = the agent's ghost"],
12242
+ ["--icon chat|avatar|ghost", "embed: what the floating button shows"],
12243
+ ["--placeholder T", "embed: grey text in the message box"],
12244
+ ["--position right|left / --offset X[,Y]", "embed: corner of the page and distance in px from the side and the bottom (0-200, default 20)"],
12245
+ ["--theme light|dark|auto", "embed: the chat panel's theme (auto = the visitor's)"],
12246
+ ["--bubble-text T", 'embed: label next to the button ("Questions?")'],
12247
+ ["--auto-open S|off", "embed: open by itself after S seconds (1-120), once per visit, never on phones"],
12248
+ ['--starters "a|b|c"', `embed: up to 4 of YOUR suggestions ("" removes them; Ghosty's are never shown)`],
12249
+ ["--style '{json}'|none", "embed: fine style, only known keys: fontFamily, radius, headerBg, headerText, titleUppercase, statusDot, headerAvatar, panelBg, userBubbleBg/Text, agentBubbleBg/Text, inputBg, sendBg, sendIcon, sendArrow (up|right), launcherBg, launcherIcon. Patch per key (null resets one); none resets all"],
12250
+ ["--font NAME / --radius 0-24", "embed: shortcuts for style.fontFamily (Google Fonts allow-list: Barlow, Inter, Poppins\u2026) and style.radius"],
12251
+ ["--theme-source custom|teams|site", "embed: import the theme from the brand of your Ghosty Teams workspace (--workspace SLUG to pick one) or from your site (--site-url); it stays editable"],
12252
+ ["--site-url URL", "embed: the site to take colors from (implies --theme-source site)"],
12253
+ ["--style-file FILE", "embed: apply a theme exported with `agents embed <id> theme` (or a plain style object)"],
12147
12254
  ["--check PATH", "box: this file must exist in the machine (repeatable), e.g. a file the new image brings"],
12148
12255
  ["--contains PATH=TEXT", "box: that file must contain TEXT (a change that adds no files)"],
12149
12256
  ["--recycle", "box: replace its machines (not mid-turn); pool: the new one starts on the next turn; ACP (goose, lite): its box is recreated now with the current image"],
@@ -12180,6 +12287,9 @@ var HELPS = {
12180
12287
  "ghosty agents demo enable <id>",
12181
12288
  'ghosty agents demo <id> --slug guardias --expires "2026-10-15T20:00" --starter "Ens\xE9\xF1ame c\xF3mo empezar"',
12182
12289
  'ghosty agents embed enable <id> --domains "mitienda.com,*.mitienda.mx" --welcome "\xA1Hola! \xBFEn qu\xE9 te ayudo?"',
12290
+ 'ghosty agents embed <id> --title "Palmera Legal" --subtitle "Responde en minutos" --position left --bubble-text "\xBFDudas?" --starters "\xBFCu\xE1nto cuesta una consulta?|\xBFD\xF3nde est\xE1n?"',
12291
+ "ghosty agents embed <id> --theme-source site --site-url https://palmeralegal.mx",
12292
+ "ghosty agents embed <id> theme > tema.json \xB7 ghosty agents embed <id> --style-file tema.json",
12183
12293
  "ghosty agents box <id> --contains /opt/claude-worker/skills/pdf-doc/render-doc.mjs=pdf-templates",
12184
12294
  "ghosty agents set <id> --tool wa:actualizar_identidad=off,teams:actualizar_identidad=on",
12185
12295
  'ghosty agents set minighosty --bag "MiniGhosty" --avatar https://www.ghosty.studio/avatars/minighosty.png',
@@ -14007,6 +14117,10 @@ ${bold("Agents")}
14007
14117
  agents demo enable|disable <id> Turn its public demo link on or off
14008
14118
  agents demo <id> Public demo link /p/<slug>: anyone tries it without an account, "Keep it" clones it to theirs
14009
14119
  agents embed enable|disable <id> Web widget: a chat bubble on YOUR site (one <script>), only on the domains you list [--domains a.com,*.b.com] [--per-visitor N] [--per-ip N] [--welcome T] [--color #hex] [--rotate]
14120
+ look: [--title T] [--subtitle T] [--avatar URL|none] [--icon chat|avatar|ghost] [--placeholder T] [--position right|left] [--offset X[,Y]]
14121
+ [--theme light|dark|auto] [--bubble-text T] [--auto-open SECONDS|off] [--starters "a|b|c"] [--font NAME] [--radius 0-24] [--style '{json}'|none]
14122
+ theme: [--theme-source custom|teams|site] [--workspace SLUG] [--site-url URL] [--style-file theme.json]
14123
+ agents embed <id> theme Export the widget theme as JSON (edit it and apply it back with --style-file)
14010
14124
  agents embed <id> Its state and the snippet to paste (claude engine only: it isolates each visitor)
14011
14125
  [--slug NAME] [--expires "YYYY-MM-DDTHH:MM"|no] [--turns N] [--per-ip N] [--welcome TEXT] [--chips "a|b|c"] [--starter TEXT] [--sponsor on|off] [--rotate]
14012
14126
 
@@ -14165,6 +14279,10 @@ ${bold("Agentes")}
14165
14279
  agents demo enable|disable <id> Prende o apaga su liga p\xFAblica de demo
14166
14280
  agents demo <id> Liga p\xFAblica de demo /p/<slug>: cualquiera lo prueba sin cuenta y \xABQued\xE1rmelo\xBB lo clona a la suya
14167
14281
  agents embed enable|disable <id> Widget web: burbuja de chat en TU sitio (un <script>), s\xF3lo en los dominios que pongas [--domains a.com,*.b.com] [--per-visitor N] [--per-ip N] [--welcome T] [--color #hex] [--rotate]
14282
+ look: [--title T] [--subtitle T] [--avatar URL|none] [--icon chat|avatar|ghost] [--placeholder T] [--position right|left] [--offset X[,Y]]
14283
+ [--theme light|dark|auto] [--bubble-text T] [--auto-open SEGUNDOS|off] [--starters "a|b|c"] [--font NOMBRE] [--radius 0-24] [--style '{json}'|none]
14284
+ tema: [--theme-source custom|teams|site] [--workspace SLUG] [--site-url URL] [--style-file tema.json]
14285
+ agents embed <id> theme Exporta el tema del widget en JSON (ed\xEDtalo y vuelve a aplicarlo con --style-file)
14168
14286
  agents embed <id> Su estado y el snippet para pegar (s\xF3lo motor claude: a\xEDsla a cada visitante)
14169
14287
  [--slug NOMBRE] [--expires "AAAA-MM-DDTHH:MM"|no] [--turns N] [--per-ip N] [--welcome TEXTO] [--chips "a|b|c"] [--starter TEXTO] [--sponsor on|off] [--rotate]
14170
14288
 
@@ -14348,6 +14466,21 @@ async function main() {
14348
14466
  chips: { type: "string" },
14349
14467
  starter: { type: "string" },
14350
14468
  rotate: { type: "boolean" },
14469
+ subtitle: { type: "string" },
14470
+ placeholder: { type: "string" },
14471
+ position: { type: "string" },
14472
+ offset: { type: "string" },
14473
+ theme: { type: "string" },
14474
+ icon: { type: "string" },
14475
+ "bubble-text": { type: "string" },
14476
+ "auto-open": { type: "string" },
14477
+ starters: { type: "string" },
14478
+ style: { type: "string" },
14479
+ font: { type: "string" },
14480
+ radius: { type: "string" },
14481
+ "style-file": { type: "string" },
14482
+ "theme-source": { type: "string" },
14483
+ "site-url": { type: "string" },
14351
14484
  sponsor: { type: "string" },
14352
14485
  yes: { type: "boolean", short: "y" },
14353
14486
  dir: { type: "string" },
@@ -14500,7 +14633,7 @@ async function main() {
14500
14633
  const tokens = splitTokens(pos, v);
14501
14634
  globals.token = tokens.session;
14502
14635
  globals.profile = v.profile;
14503
- if (v.version) return out({ version: "0.51.0" }, () => "0.51.0");
14636
+ if (v.version) return out({ version: "0.52.0" }, () => "0.52.0");
14504
14637
  for (const n of normalizeArgs(pos, v)) if (!globals.json) info(dim(n));
14505
14638
  const context = (await readConfig()).workspace;
14506
14639
  if (!explicitToken()?.startsWith("gat_")) {
@@ -14568,7 +14701,7 @@ async function main() {
14568
14701
  if (sub === "docs" || sub === "documents") return agentDocsCmd(a1, a2, pos.slice(4).filter((x) => x !== void 0), v);
14569
14702
  if (sub === "restart") return agentsRestart(a1);
14570
14703
  if (sub === "demo") return agentsDemo(a1, v);
14571
- if (sub === "embed" || sub === "widget") return agentsEmbed(a1, v);
14704
+ if (sub === "embed" || sub === "widget") return agentsEmbed(a1, v, a2);
14572
14705
  if (sub === "box") return agentsBox(a1, v);
14573
14706
  if (sub === "export") return agentsExport(a1, v);
14574
14707
  throw unknown(`agents ${sub}`, sub, AGENTS_SUBS, "ghosty agents --help");
@@ -14666,7 +14799,7 @@ async function main() {
14666
14799
  throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
14667
14800
  }
14668
14801
  }
14669
- var notice = updateNotice("0.51.0").catch(() => null);
14802
+ var notice = updateNotice("0.52.0").catch(() => null);
14670
14803
  main().then(
14671
14804
  async () => {
14672
14805
  const n = await notice;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghostystudio/cli",
3
- "version": "0.51.0",
3
+ "version": "0.52.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": {