@ghostystudio/cli 0.53.0 → 0.54.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.
- package/README.md +9 -0
- package/dist/index.js +101 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,6 +42,7 @@ ghosty agents embed enable|disable <id> [--domains a.com,*.b.com] # web widget
|
|
|
42
42
|
ghosty agents embed <id> [--per-visitor N] [--per-ip N] [--welcome TEXT] [--color #hex] [--rotate] # state + the <script> to paste
|
|
43
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
44
|
ghosty agents embed <id> [--style '{json}'] [--theme-source teams|site --site-url URL] [--style-file theme.json] # its theme · `theme` exports it
|
|
45
|
+
ghosty agents embed <id> [--copy '{"waking":"One moment, please","thinking":["Reviewing your question…"]}'|none] [--copy-file texts.json] # what it says while it works
|
|
45
46
|
ghosty agents embed <id> --out src/components/GhostyChat.tsx [--force] # your own React chat component (no deps), token + theme baked in · the output also prints the React snippet (`react` in --json)
|
|
46
47
|
ghosty agents doctor <id> [--verbose] # exits 1 when it finds a problem
|
|
47
48
|
ghosty agents docs ls|grant|revoke <id> [doc] [--write] [--no-deliver]
|
|
@@ -108,6 +109,14 @@ Every command has its own help with examples: `ghosty <command> --help` or `ghos
|
|
|
108
109
|
|
|
109
110
|
Exit codes: `0` ok · `1` API error · `2` usage error · `3` not signed in.
|
|
110
111
|
|
|
112
|
+
### Changes in 0.54.0
|
|
113
|
+
|
|
114
|
+
- `agents embed <id> --copy '{json}'|none` and `--copy-file texts.json`: the phrases the web widget shows while the agent works (`waking`, `connecting`, `thinking` — 1–8 that rotate —, `stillWorking`, `cantStart`, `interrupted`, `retryLabel`, plain text ≤ 80 chars) and whether it shows the duration and the Copy button (`showDuration`, `showCopyButton`). Patch per key; `null` = Ghosty's phrase. `agents embed <id> theme` exports `copy` too, and `--out` bakes it into the component.
|
|
115
|
+
|
|
116
|
+
### Changes in 0.53.0
|
|
117
|
+
|
|
118
|
+
- `agents embed` prints the React snippet (`react` in `--json`) and `--out <file.jsx|.tsx> [--force]` writes your own dependency-free chat component with your token and theme.
|
|
119
|
+
|
|
111
120
|
### Changes in 0.52.0
|
|
112
121
|
|
|
113
122
|
- `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.
|
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.
|
|
8471
|
+
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.54.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.
|
|
8513
|
+
"user-agent": `ghosty-cli/${"0.54.0"}`,
|
|
8514
8514
|
"accept-language": lang(),
|
|
8515
8515
|
...opts.body !== void 0 ? { "content-type": "application/json" } : {},
|
|
8516
8516
|
...opts.headers
|
|
@@ -8765,6 +8765,24 @@ function lineDiff(before, after, context = 2) {
|
|
|
8765
8765
|
}
|
|
8766
8766
|
|
|
8767
8767
|
// src/embed-component.ts
|
|
8768
|
+
var COPY_DEFAULTS = {
|
|
8769
|
+
waking: "Me estoy poniendo los lentes \u{1F453}",
|
|
8770
|
+
connecting: "Estoy abriendo mi libreta",
|
|
8771
|
+
thinking: [
|
|
8772
|
+
"Estoy pensando",
|
|
8773
|
+
"Estoy leyendo tu mensaje",
|
|
8774
|
+
"Estoy revisando lo que me mandaste",
|
|
8775
|
+
"Estoy ordenando las ideas",
|
|
8776
|
+
"Estoy armando tu respuesta",
|
|
8777
|
+
"Estoy afinando los detalles"
|
|
8778
|
+
],
|
|
8779
|
+
stillWorking: "Sigo trabajando",
|
|
8780
|
+
cantStart: "No pude arrancar.",
|
|
8781
|
+
interrupted: "Me qued\xE9 a medias",
|
|
8782
|
+
retryLabel: "Reintentar",
|
|
8783
|
+
showDuration: true,
|
|
8784
|
+
showCopyButton: true
|
|
8785
|
+
};
|
|
8768
8786
|
var DEFAULT_BASE = "https://www.ghosty.studio";
|
|
8769
8787
|
function baseFromSnippet(snippet) {
|
|
8770
8788
|
const m = snippet?.match(/src="(https?:\/\/[^"]+?)\/widget\.js"/);
|
|
@@ -8786,6 +8804,7 @@ function reactComponentSource(th, ts) {
|
|
|
8786
8804
|
const st = th.style ?? {};
|
|
8787
8805
|
const radius = typeof st.radius === "number" ? st.radius : 16;
|
|
8788
8806
|
const font = typeof st.fontFamily === "string" ? st.fontFamily : null;
|
|
8807
|
+
const cp = { ...COPY_DEFAULTS, ...th.copy ?? {} };
|
|
8789
8808
|
return `// Chat de Ghosty Studio \u2014 generado por \`ghosty agents embed --out\`. Este archivo es TUYO:
|
|
8790
8809
|
// c\xE1mbialo como quieras; no depende de ning\xFAn paquete, s\xF3lo de React.
|
|
8791
8810
|
// Habla con tu agente por el contrato del widget web (/embed/w/<token>/v/<visitante>/\u2026).
|
|
@@ -8805,6 +8824,16 @@ const WELCOME = ${s(th.welcome)}; // el primer mensaje que ve el visitante
|
|
|
8805
8824
|
const PLACEHOLDER = ${s(th.placeholder ?? `Escr\xEDbele a ${th.title}`)};
|
|
8806
8825
|
const STARTERS${T(": string[]")} = ${s(th.starters)}; // sugerencias (se ocultan al primer mensaje)
|
|
8807
8826
|
|
|
8827
|
+
// Lo que dice mientras trabaja (ficha \u2192 Canales \u2192 Widget web \u2192 Textos, o \`--copy\`).
|
|
8828
|
+
const COPY = {
|
|
8829
|
+
connecting: ${s(cp.connecting)}, // mientras se conecta
|
|
8830
|
+
thinking: ${s(cp.thinking)}, // rotan cada 4 s mientras piensa
|
|
8831
|
+
stillWorking: ${s(cp.stillWorking)}, // pasados 90 s
|
|
8832
|
+
interrupted: ${s(cp.interrupted)}, // si la respuesta se cort\xF3
|
|
8833
|
+
showDuration: ${cp.showDuration === true}, // el \xAB\xB7 12 s\xBB de la espera
|
|
8834
|
+
showCopyButton: ${cp.showCopyButton === true}, // \xABCopiar\xBB bajo cada respuesta
|
|
8835
|
+
};
|
|
8836
|
+
|
|
8808
8837
|
// \u2500\u2500 Tema (tu tema actual del widget) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
8809
8838
|
const THEME = {
|
|
8810
8839
|
font: ${s(font ? `"${font}", ui-sans-serif, system-ui, sans-serif` : "ui-sans-serif, system-ui, sans-serif")},
|
|
@@ -8890,7 +8919,7 @@ export function useGhostyChat() {
|
|
|
8890
8919
|
// \`reposo\`: llega al conectar (no hab\xEDa turno). S\xF3lo cuenta si esper\xE1bamos respuesta.
|
|
8891
8920
|
if (d.reposo && !waiting.current) return;
|
|
8892
8921
|
if (d.reposo && d.ultimo?.text) put(d.ultimo.text, true);
|
|
8893
|
-
if (d.interrumpido) setError(
|
|
8922
|
+
if (d.interrumpido) setError(COPY.interrupted);
|
|
8894
8923
|
waiting.current = false;
|
|
8895
8924
|
settle();
|
|
8896
8925
|
setStatus("idle");
|
|
@@ -8929,9 +8958,23 @@ export function useGhostyChat() {
|
|
|
8929
8958
|
return { messages, send, stop, status, error };
|
|
8930
8959
|
}
|
|
8931
8960
|
|
|
8961
|
+
// La frase de espera: rota mientras piensa y, pasado minuto y medio, dice que sigue.
|
|
8962
|
+
function useWaitLabel(active${T(": boolean")}) {
|
|
8963
|
+
const [secs, setSecs] = useState(0);
|
|
8964
|
+
useEffect(() => {
|
|
8965
|
+
if (!active) return;
|
|
8966
|
+
setSecs(0);
|
|
8967
|
+
const t = setInterval(() => setSecs((x) => x + 1), 1000);
|
|
8968
|
+
return () => clearInterval(t);
|
|
8969
|
+
}, [active]);
|
|
8970
|
+
const phrase = secs >= 90 ? COPY.stillWorking : COPY.thinking[Math.floor(secs / 4) % COPY.thinking.length];
|
|
8971
|
+
return COPY.showDuration ? \`\${phrase} \xB7 \${secs} s\` : phrase;
|
|
8972
|
+
}
|
|
8973
|
+
|
|
8932
8974
|
// \u2500\u2500 La UI: c\xE1mbiala a tu gusto \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
8933
8975
|
export default function GhostyChat() {
|
|
8934
8976
|
const { messages, send, stop, status, error } = useGhostyChat();
|
|
8977
|
+
const waitLabel = useWaitLabel(status === "thinking");
|
|
8935
8978
|
const [text, setText] = useState("");
|
|
8936
8979
|
const end = useRef${T("<HTMLDivElement>")}(null);
|
|
8937
8980
|
const busy = status === "thinking" || status === "streaming";
|
|
@@ -8975,9 +9018,17 @@ export default function GhostyChat() {
|
|
|
8975
9018
|
<div style={{ flex: 1, overflowY: "auto", padding: 16, display: "flex", flexDirection: "column", gap: 8, fontSize: 14, lineHeight: 1.45 }}>
|
|
8976
9019
|
<div style={bubble("assistant")}>{WELCOME}</div>
|
|
8977
9020
|
{messages.map((m) => (
|
|
8978
|
-
<div key={m.id} style={
|
|
9021
|
+
<div key={m.id} style={{ display: "flex", flexDirection: "column", alignItems: m.role === "user" ? "flex-end" : "flex-start" }}>
|
|
9022
|
+
<div style={bubble(m.role)}>{m.text}</div>
|
|
9023
|
+
{COPY.showCopyButton && m.role === "assistant" && !m.pending && m.text && (
|
|
9024
|
+
<button type="button" onClick={() => void navigator.clipboard?.writeText(m.text)} style={{ border: 0, background: "transparent", color: "#6b7280", fontSize: 12, cursor: "pointer", padding: "2px 0" }}>
|
|
9025
|
+
Copiar
|
|
9026
|
+
</button>
|
|
9027
|
+
)}
|
|
9028
|
+
</div>
|
|
8979
9029
|
))}
|
|
8980
|
-
{status === "
|
|
9030
|
+
{status === "connecting" && <div style={{ ...bubble("assistant"), opacity: 0.6 }}>{COPY.connecting}</div>}
|
|
9031
|
+
{status === "thinking" && <div style={{ ...bubble("assistant"), opacity: 0.6 }}>{waitLabel}</div>}
|
|
8981
9032
|
{!messages.length && STARTERS.length > 0 && (
|
|
8982
9033
|
<div style={{ display: "flex", flexWrap: "wrap", gap: 6 }}>
|
|
8983
9034
|
{STARTERS.map((s) => (
|
|
@@ -9590,6 +9641,10 @@ async function agentsDemo(id, v) {
|
|
|
9590
9641
|
].join("\n")
|
|
9591
9642
|
);
|
|
9592
9643
|
}
|
|
9644
|
+
function copySummary(c) {
|
|
9645
|
+
if (!c) return "";
|
|
9646
|
+
return Object.entries(c).filter(([k, x]) => JSON.stringify(x) !== JSON.stringify(COPY_DEFAULTS[k])).map(([k, x]) => `${k}=${Array.isArray(x) ? x.join(" | ") : x}`).join(" \xB7 ");
|
|
9647
|
+
}
|
|
9593
9648
|
function styleSummary(st) {
|
|
9594
9649
|
if (!st) return "";
|
|
9595
9650
|
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(" ");
|
|
@@ -9598,7 +9653,7 @@ async function agentsEmbed(id, v, action) {
|
|
|
9598
9653
|
id = need(id, "agent id", "ghosty agents embed enable <agent-id> --domains tusitio.com");
|
|
9599
9654
|
if (action === "theme") {
|
|
9600
9655
|
const e2 = await api(agentPath(id, "/embed"));
|
|
9601
|
-
const theme = { themeSource: e2.themeSource ?? "custom", themeFrom: e2.themeFrom ?? null, siteUrl: e2.siteUrl ?? null, color: e2.color, theme: e2.theme ?? "light", style: e2.style ?? {} };
|
|
9656
|
+
const theme = { themeSource: e2.themeSource ?? "custom", themeFrom: e2.themeFrom ?? null, siteUrl: e2.siteUrl ?? null, color: e2.color, theme: e2.theme ?? "light", style: e2.style ?? {}, copy: e2.copy ?? {} };
|
|
9602
9657
|
return out(theme, () => JSON.stringify(theme, null, 2));
|
|
9603
9658
|
}
|
|
9604
9659
|
if (action) throw usage(t(`Unknown: agents embed <id> ${action}`, `No existe: agents embed <id> ${action}`), "ghosty agents embed <id> theme");
|
|
@@ -9649,6 +9704,26 @@ async function agentsEmbed(id, v, action) {
|
|
|
9649
9704
|
body.style = isTheme ? j.style : j;
|
|
9650
9705
|
if (isTheme && typeof j.color === "string" && body.color === void 0) body.color = j.color;
|
|
9651
9706
|
if (isTheme && typeof j.theme === "string" && body.theme === void 0) body.theme = j.theme;
|
|
9707
|
+
if (isTheme && j.copy && typeof j.copy === "object" && body.copy === void 0) body.copy = j.copy;
|
|
9708
|
+
}
|
|
9709
|
+
if (v.copy !== void 0) {
|
|
9710
|
+
if (v.copy.toLowerCase() === "none") body.copy = null;
|
|
9711
|
+
else {
|
|
9712
|
+
try {
|
|
9713
|
+
body.copy = JSON.parse(v.copy);
|
|
9714
|
+
} catch {
|
|
9715
|
+
throw usage(t(`--copy takes JSON: '{"waking":"One moment, please","thinking":["Reviewing your question\u2026"]}'`, `--copy va en JSON: '{"waking":"Un momento, por favor","thinking":["Revisando su consulta\u2026"]}'`));
|
|
9716
|
+
}
|
|
9717
|
+
}
|
|
9718
|
+
}
|
|
9719
|
+
if (v["copy-file"]) {
|
|
9720
|
+
let j;
|
|
9721
|
+
try {
|
|
9722
|
+
j = JSON.parse(await readFile3(v["copy-file"], "utf8"));
|
|
9723
|
+
} catch (err) {
|
|
9724
|
+
throw usage(t(`Could not read ${v["copy-file"]} as JSON: ${err.message}`, `No pude leer ${v["copy-file"]} como JSON: ${err.message}`));
|
|
9725
|
+
}
|
|
9726
|
+
body.copy = j && typeof j === "object" && "copy" in j && !("waking" in j) ? j.copy : j;
|
|
9652
9727
|
}
|
|
9653
9728
|
if (v["theme-source"] !== void 0) body.themeSource = v["theme-source"];
|
|
9654
9729
|
if (v["site-url"] !== void 0) {
|
|
@@ -9682,6 +9757,7 @@ async function agentsEmbed(id, v, action) {
|
|
|
9682
9757
|
e.starters?.length ? `${t("suggestions", "sugerencias")}: ${e.starters.join(" | ")}` : dim(t("suggestions: (none)", "sugerencias: (ninguna)")),
|
|
9683
9758
|
`${t("theme", "tema")}: ${e.themeSource ?? "custom"}${e.themeFrom ? dim(` \xB7 ${e.themeFrom}`) : ""}`,
|
|
9684
9759
|
...styleSummary(e.style) ? [dim(`${t("style", "estilo")}: ${styleSummary(e.style)}`)] : [],
|
|
9760
|
+
...copySummary(e.copy) ? [dim(`${t("texts", "textos")}: ${copySummary(e.copy)}`)] : [],
|
|
9685
9761
|
...e.snippet ? ["", t("Paste before </body> on your site:", "Pega antes de </body> en tu sitio:"), e.snippet, ""] : [],
|
|
9686
9762
|
...react ? [t("React:", "React:"), dim(` ${react.install}`), ...react.code.split("\n").map((l) => ` ${l}`), ""] : [],
|
|
9687
9763
|
...wrote ? [`${lavender("\u2713")} ${t(`Component written to ${wrote} (yours to edit: token and theme are at the top)`, `Componente escrito en ${wrote} (es tuyo: el token y el tema van arriba)`)}`, ""] : [],
|
|
@@ -9708,7 +9784,8 @@ async function writeEmbedComponent(e, baseUrl2, file, force) {
|
|
|
9708
9784
|
color: e.color,
|
|
9709
9785
|
textColor: e.textColor ?? "#ffffff",
|
|
9710
9786
|
starters: e.starters ?? [],
|
|
9711
|
-
style: e.style ?? {}
|
|
9787
|
+
style: e.style ?? {},
|
|
9788
|
+
copy: { ...COPY_DEFAULTS, ...e.copy ?? {} }
|
|
9712
9789
|
},
|
|
9713
9790
|
ext === "tsx"
|
|
9714
9791
|
);
|
|
@@ -10327,7 +10404,7 @@ async function call(path, v, init = {}) {
|
|
|
10327
10404
|
"x-ghosty-ts": ts,
|
|
10328
10405
|
"x-ghosty-sig": sig,
|
|
10329
10406
|
"content-type": "application/json",
|
|
10330
|
-
"user-agent": `ghosty-cli/${"0.
|
|
10407
|
+
"user-agent": `ghosty-cli/${"0.54.0"}`
|
|
10331
10408
|
},
|
|
10332
10409
|
body: method === "GET" ? void 0 : raw
|
|
10333
10410
|
});
|
|
@@ -11936,6 +12013,8 @@ var HELPS_ES = {
|
|
|
11936
12013
|
["# its theme", "# su tema"],
|
|
11937
12014
|
["# 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)"],
|
|
11938
12015
|
["--style-file theme.json", "--style-file tema.json"],
|
|
12016
|
+
["# what it says while it works", "# lo que dice mientras trabaja"],
|
|
12017
|
+
["--copy-file texts.json", "--copy-file textos.json"],
|
|
11939
12018
|
["# 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)"],
|
|
11940
12019
|
["# change one piece of the prompt, on the server's current one", "# cambia un pedazo del prompt, sobre el actual del servidor"],
|
|
11941
12020
|
["# your account: merge copies with the same content", "# tu cuenta: une las copias con el mismo contenido"],
|
|
@@ -12005,6 +12084,8 @@ var HELPS_ES = {
|
|
|
12005
12084
|
["--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"],
|
|
12006
12085
|
["--site-url URL", "embed: el sitio del que se sacan los colores (implica --theme-source site)"],
|
|
12007
12086
|
["--style-file FILE", "embed: aplica un tema exportado con `agents embed <id> theme` (o s\xF3lo el objeto style)"],
|
|
12087
|
+
["--copy '{json}'|none", "embed: las frases mientras el agente trabaja, texto plano \u2264 80 car.: waking, connecting, thinking (lista de 1 a 8 que rotan), stillWorking, cantStart, interrupted, retryLabel, showDuration, showCopyButton (true|false). PATCH por llave (null = la frase de Ghosty); none las regresa todas"],
|
|
12088
|
+
["--copy-file FILE", "embed: aplica el objeto copy desde un archivo (o el `copy` de un tema exportado)"],
|
|
12008
12089
|
["--out FILE.tsx|.jsx [--force]", "embed: escribe en tu proyecto un componente de React COMPLETO y editable (sin depender de ning\xFAn paquete) con tu token y tu tema horneados arriba; no pisa un archivo que ya existe sin --force"],
|
|
12009
12090
|
["--check PATH", "box: este archivo debe existir en la m\xE1quina (repetible), p. ej. un archivo que trae la imagen nueva"],
|
|
12010
12091
|
["--contains PATH=TEXT", "box: ese archivo debe contener TEXT (un cambio que no agrega archivos)"],
|
|
@@ -12469,6 +12550,7 @@ var HELPS = {
|
|
|
12469
12550
|
"ghosty agents embed <agent-id> [--domains \u2026] [--per-visitor N] [--per-ip N] [--welcome TEXT] [--color #hex] [--rotate] # state + the <script> to paste",
|
|
12470
12551
|
'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',
|
|
12471
12552
|
"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",
|
|
12553
|
+
"ghosty agents embed <agent-id> [--copy '{json}'|none] [--copy-file texts.json] # what it says while it works",
|
|
12472
12554
|
"ghosty agents embed <agent-id> theme # export the theme as JSON (edit it, apply it back with --style-file)",
|
|
12473
12555
|
"ghosty agents embed <agent-id> --out src/components/GhostyChat.tsx [--force] # your own React component, token and theme baked in",
|
|
12474
12556
|
"ghosty agents rm <agent-id> [--from-workspace] [--yes]"
|
|
@@ -12530,6 +12612,8 @@ var HELPS = {
|
|
|
12530
12612
|
["--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"],
|
|
12531
12613
|
["--site-url URL", "embed: the site to take colors from (implies --theme-source site)"],
|
|
12532
12614
|
["--style-file FILE", "embed: apply a theme exported with `agents embed <id> theme` (or a plain style object)"],
|
|
12615
|
+
["--copy '{json}'|none", "embed: the phrases while the agent works, plain text \u2264 80 chars: waking, connecting, thinking (list of 1\u20138 that rotate), stillWorking, cantStart, interrupted, retryLabel, showDuration, showCopyButton (true|false). Patch per key (null = Ghosty's phrase); none resets all"],
|
|
12616
|
+
["--copy-file FILE", "embed: apply the copy object from a file (or the `copy` of an exported theme)"],
|
|
12533
12617
|
["--out FILE.tsx|.jsx [--force]", "embed: write a COMPLETE, editable React component into your project (no package needed) with your token and theme baked in at the top; won't overwrite an existing file without --force"],
|
|
12534
12618
|
["--check PATH", "box: this file must exist in the machine (repeatable), e.g. a file the new image brings"],
|
|
12535
12619
|
["--contains PATH=TEXT", "box: that file must contain TEXT (a change that adds no files)"],
|
|
@@ -12570,6 +12654,7 @@ var HELPS = {
|
|
|
12570
12654
|
'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?"',
|
|
12571
12655
|
"ghosty agents embed <id> --theme-source site --site-url https://palmeralegal.mx",
|
|
12572
12656
|
"ghosty agents embed <id> theme > tema.json \xB7 ghosty agents embed <id> --style-file tema.json",
|
|
12657
|
+
`ghosty agents embed <id> --copy '{"waking":"Un momento, por favor","thinking":["Revisando su consulta\u2026","Consultando el expediente\u2026"],"showDuration":false}'`,
|
|
12573
12658
|
"ghosty agents box <id> --contains /opt/claude-worker/skills/pdf-doc/render-doc.mjs=pdf-templates",
|
|
12574
12659
|
"ghosty agents set <id> --tool wa:actualizar_identidad=off,teams:actualizar_identidad=on",
|
|
12575
12660
|
'ghosty agents set minighosty --bag "MiniGhosty" --avatar https://www.ghosty.studio/avatars/minighosty.png',
|
|
@@ -14400,7 +14485,8 @@ ${bold("Agents")}
|
|
|
14400
14485
|
look: [--title T] [--subtitle T] [--avatar URL|none] [--icon chat|avatar|ghost] [--placeholder T] [--position right|left] [--offset X[,Y]]
|
|
14401
14486
|
[--theme light|dark|auto] [--bubble-text T] [--auto-open SECONDS|off] [--starters "a|b|c"] [--font NAME] [--radius 0-24] [--style '{json}'|none]
|
|
14402
14487
|
theme: [--theme-source custom|teams|site] [--workspace SLUG] [--site-url URL] [--style-file theme.json]
|
|
14403
|
-
|
|
14488
|
+
texts while it works: [--copy '{"waking":"One moment, please","thinking":["Reviewing your question\u2026"]}'|none] [--copy-file copy.json]
|
|
14489
|
+
agents embed <id> theme Export the widget theme + texts as JSON (edit it and apply it back with --style-file)
|
|
14404
14490
|
agents embed <id> --out F.tsx Write your own React chat component (token + theme baked in; --force to overwrite) \xB7 the output also prints the React snippet
|
|
14405
14491
|
agents embed <id> Its state and the snippet to paste (claude engine only: it isolates each visitor)
|
|
14406
14492
|
[--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]
|
|
@@ -14563,7 +14649,8 @@ ${bold("Agentes")}
|
|
|
14563
14649
|
look: [--title T] [--subtitle T] [--avatar URL|none] [--icon chat|avatar|ghost] [--placeholder T] [--position right|left] [--offset X[,Y]]
|
|
14564
14650
|
[--theme light|dark|auto] [--bubble-text T] [--auto-open SEGUNDOS|off] [--starters "a|b|c"] [--font NOMBRE] [--radius 0-24] [--style '{json}'|none]
|
|
14565
14651
|
tema: [--theme-source custom|teams|site] [--workspace SLUG] [--site-url URL] [--style-file tema.json]
|
|
14566
|
-
|
|
14652
|
+
textos mientras trabaja: [--copy '{"waking":"Un momento, por favor","thinking":["Revisando su consulta\u2026"]}'|none] [--copy-file textos.json]
|
|
14653
|
+
agents embed <id> theme Exporta el tema y los textos del widget en JSON (ed\xEDtalo y vuelve a aplicarlo con --style-file)
|
|
14567
14654
|
agents embed <id> --out F.tsx Escribe tu propio componente de chat en React (token y tema horneados; --force para pisar) \xB7 la salida tambi\xE9n trae el snippet de React
|
|
14568
14655
|
agents embed <id> Su estado y el snippet para pegar (Claude o MiniGhosty: a\xEDslan a cada visitante)
|
|
14569
14656
|
[--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]
|
|
@@ -14761,6 +14848,8 @@ async function main() {
|
|
|
14761
14848
|
font: { type: "string" },
|
|
14762
14849
|
radius: { type: "string" },
|
|
14763
14850
|
"style-file": { type: "string" },
|
|
14851
|
+
copy: { type: "string" },
|
|
14852
|
+
"copy-file": { type: "string" },
|
|
14764
14853
|
"theme-source": { type: "string" },
|
|
14765
14854
|
"site-url": { type: "string" },
|
|
14766
14855
|
sponsor: { type: "string" },
|
|
@@ -14915,7 +15004,7 @@ async function main() {
|
|
|
14915
15004
|
const tokens = splitTokens(pos, v);
|
|
14916
15005
|
globals.token = tokens.session;
|
|
14917
15006
|
globals.profile = v.profile;
|
|
14918
|
-
if (v.version) return out({ version: "0.
|
|
15007
|
+
if (v.version) return out({ version: "0.54.0" }, () => "0.54.0");
|
|
14919
15008
|
for (const n of normalizeArgs(pos, v)) if (!globals.json) info(dim(n));
|
|
14920
15009
|
const context = (await readConfig()).workspace;
|
|
14921
15010
|
if (!explicitToken()?.startsWith("gat_")) {
|
|
@@ -15081,7 +15170,7 @@ async function main() {
|
|
|
15081
15170
|
throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
|
|
15082
15171
|
}
|
|
15083
15172
|
}
|
|
15084
|
-
var notice = updateNotice("0.
|
|
15173
|
+
var notice = updateNotice("0.54.0").catch(() => null);
|
|
15085
15174
|
main().then(
|
|
15086
15175
|
async () => {
|
|
15087
15176
|
const n = await notice;
|
package/package.json
CHANGED