@ghostystudio/cli 0.59.0 → 0.62.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 +13 -0
- package/dist/index.js +132 -7
- 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,18 @@ 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.62.0
|
|
114
|
+
|
|
115
|
+
- `support link <id> <owner/repo#12 | URL>` (staff): ties a case to the GitHub issue or PR that fixes it; when it closes, every tied customer gets "fixed" by email.
|
|
116
|
+
|
|
117
|
+
### Changes in 0.61.0
|
|
118
|
+
|
|
119
|
+
- `support open --title T --body TEXT`: ask Ghosty for help; we answer by email and replying to that email adds to the case. `support ls | show <id> | reply <id>` follow your cases. Staff: `support ls|show|reply --all`, `support resolve|reopen <id>`.
|
|
120
|
+
|
|
121
|
+
### Changes in 0.60.0
|
|
122
|
+
|
|
123
|
+
- `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.
|
|
124
|
+
|
|
112
125
|
### Changes in 0.59.0
|
|
113
126
|
|
|
114
127
|
- `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`.
|
package/dist/index.js
CHANGED
|
@@ -8478,7 +8478,7 @@ async function postLong(path, body, timeoutMs = 9e5) {
|
|
|
8478
8478
|
const req = httpsRequest(`${base}${path}`, {
|
|
8479
8479
|
method: "POST",
|
|
8480
8480
|
timeout: timeoutMs,
|
|
8481
|
-
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.
|
|
8481
|
+
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.62.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
|
|
8482
8482
|
}, (res) => {
|
|
8483
8483
|
let buf = "";
|
|
8484
8484
|
res.on("data", (c) => buf += c);
|
|
@@ -8520,7 +8520,7 @@ async function request(path, opts = {}, retried = false) {
|
|
|
8520
8520
|
method: opts.method ?? (opts.body !== void 0 || opts.raw !== void 0 ? "POST" : "GET"),
|
|
8521
8521
|
headers: {
|
|
8522
8522
|
authorization: `Bearer ${token}`,
|
|
8523
|
-
"user-agent": `ghosty-cli/${"0.
|
|
8523
|
+
"user-agent": `ghosty-cli/${"0.62.0"}`,
|
|
8524
8524
|
"accept-language": lang(),
|
|
8525
8525
|
...opts.body !== void 0 ? { "content-type": "application/json" } : {},
|
|
8526
8526
|
...opts.headers
|
|
@@ -9080,6 +9080,11 @@ var need = (v, what, example) => {
|
|
|
9080
9080
|
return v;
|
|
9081
9081
|
};
|
|
9082
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
|
+
}
|
|
9083
9088
|
if (f.sponsor || f.owner) {
|
|
9084
9089
|
const q = new URLSearchParams({ ...f.sponsor ? { sponsor: f.sponsor } : {}, ...f.owner ? { owner: f.owner } : {}, ...f.engine ? { engine: f.engine } : {}, ...workspace && !f.all ? { workspace } : {} });
|
|
9085
9090
|
const s2 = await api(`/api/v2/agents?${q}`);
|
|
@@ -9354,6 +9359,15 @@ async function agentsRm(id, v = {}) {
|
|
|
9354
9359
|
const r = await api(agentPath(id, from ? "?fromWorkspace=1" : ""), { method: "DELETE" });
|
|
9355
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(", ")}.`)) : ""}`);
|
|
9356
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
|
+
}
|
|
9357
9371
|
async function setAvatar(id, src) {
|
|
9358
9372
|
if (/^https:\/\//.test(src)) return api(agentPath(id, "/avatar"), { method: "PUT", body: { url: src } });
|
|
9359
9373
|
const bytes = await readFile3(src).catch(() => {
|
|
@@ -10418,7 +10432,7 @@ async function call(path, v, init = {}) {
|
|
|
10418
10432
|
"x-ghosty-ts": ts,
|
|
10419
10433
|
"x-ghosty-sig": sig,
|
|
10420
10434
|
"content-type": "application/json",
|
|
10421
|
-
"user-agent": `ghosty-cli/${"0.
|
|
10435
|
+
"user-agent": `ghosty-cli/${"0.62.0"}`
|
|
10422
10436
|
},
|
|
10423
10437
|
body: method === "GET" ? void 0 : raw
|
|
10424
10438
|
});
|
|
@@ -10919,6 +10933,16 @@ ${table(rows, ["table", "rows", "customer writes", "customer sees", "columns"])}
|
|
|
10919
10933
|
${dim(`${t(`${rows.length} row(s)`, `${rows.length} fila(s)`)} \xB7 ${Math.round(r.ms)} ms`)}`;
|
|
10920
10934
|
});
|
|
10921
10935
|
}
|
|
10936
|
+
case "origin": {
|
|
10937
|
+
db = need2(db, t("database (id or name)", "la base (id o nombre)"), "ghosty dbs origin <agent-id> <db> [--table T] [--since 2026-10-10] [--limit N]");
|
|
10938
|
+
const q = new URLSearchParams();
|
|
10939
|
+
if (v.table) q.set("table", v.table);
|
|
10940
|
+
if (v.since) q.set("since", v.since);
|
|
10941
|
+
if (v.limit) q.set("limit", v.limit);
|
|
10942
|
+
const r = await api(agentPath2(id, `/dbs/${enc6(db)}/origin${q.size ? `?${q}` : ""}`));
|
|
10943
|
+
const rows = r.writes.map((w) => ({ when: w.created_at, agent: w.agent_id.slice(0, 10), channel: w.canal ?? "", what: `${w.op} ${w.table_name}${w.row_id != null ? ` #${w.row_id}` : ""}`, customer: w.phone10 ?? "", why: w.reason ?? "" }));
|
|
10944
|
+
return out(r, () => rows.length ? table(rows, ["when", "agent", "channel", "what", "customer", "why"]) : dim(t("No agent writes recorded yet in this database.", "Todav\xEDa no hay escrituras de agentes registradas en esta base.")));
|
|
10945
|
+
}
|
|
10922
10946
|
case "grant": {
|
|
10923
10947
|
db = need2(db, t("database (id or name)", "la base (id o nombre)"), "ghosty dbs grant <agent-id> <db> [--external-write t1,t2] [--external-rows t:col+col]");
|
|
10924
10948
|
let rowScope;
|
|
@@ -12090,6 +12114,8 @@ var HELPS_ES = {
|
|
|
12090
12114
|
["# 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"],
|
|
12091
12115
|
["# turn it on there (or change its @handle)", "# lo prende ah\xED (o le cambia el @handle)"],
|
|
12092
12116
|
["# pause it there; it keeps its @handle", "# lo pausa ah\xED; conserva su @handle"],
|
|
12117
|
+
["# 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"],
|
|
12118
|
+
["# archived agents and when they get deleted", "# los archivados y cu\xE1ndo se borran"],
|
|
12093
12119
|
["# remove it from there; the agent stays", "# lo quita de ah\xED; el agente sigue"],
|
|
12094
12120
|
["# web widget: chat bubble on your site, only on those domains", "# widget web: burbuja de chat en tu sitio, s\xF3lo en esos dominios"],
|
|
12095
12121
|
["# state + the <script> to paste", "# su estado + el <script> para pegar"]
|
|
@@ -12214,6 +12240,7 @@ var HELPS_ES = {
|
|
|
12214
12240
|
["--rehost-images t.col,\u2026", "import: copia las ligas de imagen de esas columnas al almacenamiento p\xFAblico de Ghosty (ligas permanentes en img.ghosty.studio)"],
|
|
12215
12241
|
["--table T --column C", "rehost: la columna de ligas de imagen (p. ej. catalogo imagen_url) que se pasa a ligas permanentes en img.ghosty.studio; idempotente, una liga que falla se queda como estaba"],
|
|
12216
12242
|
["--key-column K --dir DIR", "photos put: cada archivo de DIR se llama como la clave de su fila (SKU-123.jpg \u2192 la fila donde K = SKU-123); va al almacenamiento permanente y su liga a --column (default imagen_url)"],
|
|
12243
|
+
["--since DATE --limit N", "origin: escrituras de agentes desde esa fecha (p. ej. 2026-10-10), hasta N (default 50)"],
|
|
12217
12244
|
["-n, --dry-run", "import: imprime el plan (+ crear, ~ reemplazar N \u2192 M filas) y las filas que s\xF3lo existen en gs, no cambia nada \xB7 photos put: lista las claves sin fila y los archivos que no son imagen \xB7 rename: lista los prompts, env y skills que la nombran \xB7 rm: tablas, filas y agentes que la perder\xEDan"],
|
|
12218
12245
|
["--yes", "import, rm: se salta la confirmaci\xF3n (obligatorio sin terminal)"]
|
|
12219
12246
|
],
|
|
@@ -12500,6 +12527,16 @@ var HELPS_ES = {
|
|
|
12500
12527
|
summary: "Staff de Ghosty: qu\xE9 avisos (push) se le mandaron a alguien (fin de turno, permiso, escalar, documento\u2026), a qu\xE9 dispositivo y qu\xE9 contest\xF3 Apple/Google. Se registran desde el 1-oct y se guardan 30 d\xEDas.",
|
|
12501
12528
|
notes: ["thread ense\xF1a la clase de la conversaci\xF3n: un \xABchannel\xBB ah\xED es que le lleg\xF3 el aviso de un cliente.", "Tambi\xE9n salen los push de las novedades (kind novedad)."]
|
|
12502
12529
|
},
|
|
12530
|
+
support: {
|
|
12531
|
+
summary: "Pide ayuda a Ghosty. Soporte es por correo: cada caso tiene su propia direcci\xF3n de respuesta, as\xED que contestar el correo (o escribir aqu\xED) cae en el mismo caso. Tambi\xE9n te lo abre tu agente: \xABrep\xF3rtalo a soporte\xBB.",
|
|
12532
|
+
notes: [
|
|
12533
|
+
"El caso lleva tu cuenta, tus agentes y sus errores recientes: no hace falta repetirlos.",
|
|
12534
|
+
"Contestar un caso resuelto lo reabre.",
|
|
12535
|
+
"El staff contesta desde su correo: /resuelto en la primera l\xEDnea cierra el caso.",
|
|
12536
|
+
"link liga un caso al issue o PR de GitHub que lo arregla; varios casos al mismo funcionan como tracker. Al cerrarse el issue como completado (o mergearse el PR) a cada cliente le llega \xABya qued\xF3\xBB y su caso se resuelve.",
|
|
12537
|
+
"Solo: al staff le llega un recordatorio tras 4 h sin respuesta; un caso esperando al cliente se cierra solo a los 7 d\xEDas (si contesta, se reabre)."
|
|
12538
|
+
]
|
|
12539
|
+
},
|
|
12503
12540
|
novedades: {
|
|
12504
12541
|
summary: "Staff de Ghosty: las novedades de la app Android. Cada una con su ilustraci\xF3n; mismos frenos que el admin: pru\xE9bala en tu tel\xE9fono primero, push de 9 a 21 h CDMX, 72 h entre env\xEDos a todos.",
|
|
12505
12542
|
notes: ["ls dice, de una ya enviada, cu\xE1ntos entreg\xF3 y rechaz\xF3 FCM (del push log, desde el 1-oct).", "--image se publica en img.ghosty.studio (WebP 1200 px). --to vac\xEDo = todos. send sin --at la manda ya (pregunta); --at '' quita la programaci\xF3n.", "--min-version N (versionCode de Android): si la app del tester es m\xE1s vieja, el bot\xF3n dice \xABActualizar\xBB y lanza la actualizaci\xF3n de Play desde la app; '' la quita."]
|
|
@@ -12639,6 +12676,8 @@ var HELPS = {
|
|
|
12639
12676
|
"ghosty agents embed <agent-id> [--copy '{json}'|none] [--copy-file texts.json] # what it says while it works",
|
|
12640
12677
|
"ghosty agents embed <agent-id> theme # export the theme as JSON (edit it, apply it back with --style-file)",
|
|
12641
12678
|
"ghosty agents embed <agent-id> --out src/components/GhostyChat.tsx [--force] # your own React component, token and theme baked in",
|
|
12679
|
+
"ghosty agents archive|restore <agent-id> # archive: stops answering everywhere, hidden, deleted after 7 days unless restored",
|
|
12680
|
+
"ghosty agents ls --archived # archived agents and when they get deleted",
|
|
12642
12681
|
"ghosty agents rm <agent-id> [--from-workspace] [--yes]"
|
|
12643
12682
|
],
|
|
12644
12683
|
flags: [
|
|
@@ -12788,6 +12827,7 @@ var HELPS = {
|
|
|
12788
12827
|
"ghosty dbs ls <agent-id> [--orphans]",
|
|
12789
12828
|
"ghosty dbs tables <agent-id> <db>",
|
|
12790
12829
|
'ghosty dbs query <agent-id> <db> "SQL" [--arg V]\u2026 [--write]',
|
|
12830
|
+
"ghosty dbs origin <agent-id> <db> [--table T] [--since 2026-10-10] [--limit N]",
|
|
12791
12831
|
"ghosty dbs export <agent-id> <db> [--table T] [--out FILE]",
|
|
12792
12832
|
"ghosty dbs import <agent-id> <db> --from easybits:<db-id> [--tables a,b] [--append] [--rehost-images t.col,\u2026] [--dry-run] [--yes]",
|
|
12793
12833
|
"ghosty dbs rehost <agent-id> <db> --table T --column C [--dry-run]",
|
|
@@ -12811,6 +12851,7 @@ var HELPS = {
|
|
|
12811
12851
|
["--rehost-images t.col,\u2026", "import: copy the image links in those columns to Ghosty's public storage (permanent links at img.ghosty.studio)"],
|
|
12812
12852
|
["--table T --column C", "rehost: the column of image links (e.g. catalogo imagen_url) to move to permanent links at img.ghosty.studio; idempotent, a link that fails stays as it was"],
|
|
12813
12853
|
["--key-column K --dir DIR", "photos put: each file in DIR is named after its row's key (SKU-123.jpg \u2192 the row where K = SKU-123); it goes to permanent storage and its link to --column (default imagen_url)"],
|
|
12854
|
+
["--since DATE --limit N", "origin: agent writes since that date (e.g. 2026-10-10), up to N (default 50)"],
|
|
12814
12855
|
["-n, --dry-run", "import: print the plan (+ create, ~ replace N \u2192 M rows) and the rows that exist only in gs, change nothing \xB7 photos put: list keys with no row and files that aren't images \xB7 rename: list the prompts, env and skills that name it \xB7 rm: tables, rows and agents that would lose it"],
|
|
12815
12856
|
["--yes", "import, rm: skip the confirmation (required when not interactive)"]
|
|
12816
12857
|
],
|
|
@@ -13242,6 +13283,22 @@ HELPS.push = {
|
|
|
13242
13283
|
notes: ["thread shows the conversation's kind: a \xABchannel\xBB there means a customer's push reached them.", "Announcement pushes appear too (kind novedad)."],
|
|
13243
13284
|
learn: `${REF}#soporte`
|
|
13244
13285
|
};
|
|
13286
|
+
HELPS.support = {
|
|
13287
|
+
summary: "Ask Ghosty for help. Support is by email: each case has its own reply address, so answering the email (or writing here) lands in the same case. Your agent can open one too: \xABreport it to support\xBB.",
|
|
13288
|
+
usage: [
|
|
13289
|
+
"ghosty support open --title T --body TEXT",
|
|
13290
|
+
"ghosty support ls",
|
|
13291
|
+
"ghosty support show <id>",
|
|
13292
|
+
"ghosty support reply <id> --body TEXT",
|
|
13293
|
+
"ghosty support ls|show --all [--status open|waiting_customer|resolved] (staff)",
|
|
13294
|
+
"ghosty support reply <id> --all --body TEXT [--resolve] (staff: emails the customer)",
|
|
13295
|
+
"ghosty support resolve|reopen <id> (staff)",
|
|
13296
|
+
"ghosty support link <id> <owner/repo#12 | issue/PR URL> (staff)"
|
|
13297
|
+
],
|
|
13298
|
+
examples: ['ghosty support open --title "My agent stopped answering on WhatsApp" --body "Since yesterday 6 pm\u2026"', "ghosty support ls --all", 'ghosty support reply <id> --all --body "Fixed, try again" --resolve'],
|
|
13299
|
+
notes: ["The case carries your account, agents and recent errors, so you don't have to repeat them.", "Replying to a resolved case reopens it.", "Staff answer from their own inbox too: /resuelto on the first line closes the case.", "link ties a case to the GitHub issue or PR that fixes it; several cases on the same one act as a tracker. When the issue closes as completed (or the PR merges) each customer gets \xABya qued\xF3\xBB and the case resolves.", "Automatic: staff get a reminder after 4 h without an answer; a case waiting on the customer closes itself after 7 days (replying reopens it)."],
|
|
13300
|
+
learn: `${REF}#soporte`
|
|
13301
|
+
};
|
|
13245
13302
|
HELPS.novedades = {
|
|
13246
13303
|
summary: "Ghosty staff: the Android app's announcements (what's new). Each one needs its own illustration; same brakes as the admin: test on your phone first, push 9\u201321 h CDMX, 72 h between pushes to everyone.",
|
|
13247
13304
|
usage: [
|
|
@@ -14643,6 +14700,59 @@ async function userDoctor(email, agent, v) {
|
|
|
14643
14700
|
return lines.join("\n");
|
|
14644
14701
|
});
|
|
14645
14702
|
}
|
|
14703
|
+
var STATUS = (s2) => s2 === "open" ? t("open", "abierto") : s2 === "waiting_customer" ? t("waiting on customer", "esperando al cliente") : s2 === "resolved" ? t("resolved", "resuelto") : s2;
|
|
14704
|
+
async function supportCmd(sub, id, v) {
|
|
14705
|
+
if (!sub || sub === "ls" || sub === "list") {
|
|
14706
|
+
const q = new URLSearchParams({ ...v.status ? { status: v.status } : {}, ...v.limit ? { limit: v.limit } : {} });
|
|
14707
|
+
const r = v.all ? await api(`/api/v2/support/cases?${q}`) : await api("/api/v2/me/support");
|
|
14708
|
+
const rows = r.cases.map((c) => ({ id: c.id, code: c.code, status: STATUS(c.status), title: c.title.slice(0, 50), ...v.all ? { email: c.email } : {}, last: cdmx(c.lastMessageAt) }));
|
|
14709
|
+
return out(r, () => rows.length ? table(rows, v.all ? ["id", "code", "status", "title", "email", "last"] : ["id", "code", "status", "title", "last"]) : dim(t("No cases.", "Sin casos.")));
|
|
14710
|
+
}
|
|
14711
|
+
if (sub === "open") {
|
|
14712
|
+
if (!v.body) throw usage(t("Missing --body.", "Falta --body."), 'ghosty support open --title "\u2026" --body "\u2026"');
|
|
14713
|
+
const r = await api("/api/v2/me/support", { method: "POST", body: { title: v.title ?? "", body: v.body } });
|
|
14714
|
+
return out(r, () => `${lavender("\u2713")} ${t("Case", "Caso")} ${bold(r.case.code)} ${t("opened. We'll answer by email at", "abierto. Te contestamos por correo a")} ${r.case.email}.
|
|
14715
|
+
${dim(r.case.id)}`);
|
|
14716
|
+
}
|
|
14717
|
+
if (!id) throw usage(t("Missing the case id.", "Falta el id del caso."), "ghosty support ls");
|
|
14718
|
+
if (sub === "show") {
|
|
14719
|
+
const r = v.all ? await api(`/api/v2/support/cases/${enc13(id)}`) : await api(`/api/v2/me/support/${enc13(id)}`);
|
|
14720
|
+
const c = r.case;
|
|
14721
|
+
return out(r, () => {
|
|
14722
|
+
const who2 = (a) => a === "staff" ? "Ghosty" : a === "agent" ? t("agent", "agente") : a === "customer" ? t("customer", "cliente") : a;
|
|
14723
|
+
const ctx = c.context;
|
|
14724
|
+
const lines = [
|
|
14725
|
+
`${bold(c.code)} ${c.title} ${dim(`\xB7 ${STATUS(c.status)} \xB7 ${c.email} \xB7 ${c.channel}`)}`,
|
|
14726
|
+
...ctx?.plan ? [dim(` plan ${ctx.plan}`)] : [],
|
|
14727
|
+
...(ctx?.agents ?? []).map((a) => dim(` ${a.name ?? a.id} ${a.engine}/${a.model ?? ""}`) + (a.failedTurns ? red(` \xB7 ${a.failedTurns} ${t("failed turns", "turnos con error")}`) : "")),
|
|
14728
|
+
...ctx?.thread ? [dim(` ghosty conversations show ${ctx.thread.agentId} ${ctx.thread.sessionId} --why`)] : [],
|
|
14729
|
+
"",
|
|
14730
|
+
...c.thread.map((m) => `${dim(cdmx(m.createdAt))} ${bold(who2(m.author))}${m.authorEmail ? dim(` ${m.authorEmail}`) : ""} ${dim(`(${m.via})`)}
|
|
14731
|
+
${m.body}${m.attachments.length ? dim(`
|
|
14732
|
+
\u{1F4CE} ${m.attachments.map((a) => a.name).join(", ")}`) : ""}
|
|
14733
|
+
`)
|
|
14734
|
+
];
|
|
14735
|
+
return lines.join("\n");
|
|
14736
|
+
});
|
|
14737
|
+
}
|
|
14738
|
+
if (sub === "reply") {
|
|
14739
|
+
if (!v.body && !v.resolve) throw usage(t("Missing --body.", "Falta --body."), 'ghosty support reply <id> --body "\u2026" [--resolve]');
|
|
14740
|
+
const r = v.all || v.resolve ? await api(`/api/v2/support/cases/${enc13(id)}`, { method: "POST", body: { intent: "reply", body: v.body ?? "", resolve: Boolean(v.resolve) } }) : await api(`/api/v2/me/support/${enc13(id)}`, { method: "POST", body: { body: v.body } });
|
|
14741
|
+
return out(r, () => `${lavender("\u2713")} ${r.case.code} \xB7 ${STATUS(r.case.status)}`);
|
|
14742
|
+
}
|
|
14743
|
+
if (sub === "link") {
|
|
14744
|
+
const ref = v.ref;
|
|
14745
|
+
if (!ref) throw usage(t("Missing the issue or PR.", "Falta el issue o PR."), "ghosty support link <id> owner/repo#12 (or the issue/PR URL)");
|
|
14746
|
+
const r = await api(`/api/v2/support/cases/${enc13(id)}`, { method: "POST", body: { intent: "link", ref } });
|
|
14747
|
+
return out(r, () => `${lavender("\u2713")} ${r.case.code} \u2192 ${bold(r.ref)} ${dim(`(${r.github.title})`)}${r.alsoWaiting ? `
|
|
14748
|
+
${dim(t(`${r.alsoWaiting} more cases wait for the same fix: all get "fixed" when it closes.`, `Otros ${r.alsoWaiting} casos esperan el mismo arreglo: a todos les llega \xABya qued\xF3\xBB al cerrarse.`))}` : ""}`);
|
|
14749
|
+
}
|
|
14750
|
+
if (sub === "resolve" || sub === "reopen") {
|
|
14751
|
+
const r = await api(`/api/v2/support/cases/${enc13(id)}`, { method: "POST", body: { intent: sub } });
|
|
14752
|
+
return out(r, () => `${lavender("\u2713")} ${r.case.code} \xB7 ${STATUS(r.case.status)}`);
|
|
14753
|
+
}
|
|
14754
|
+
throw usage(t(`Unknown support subcommand: ${sub}.`, `Subcomando de support desconocido: ${sub}.`), "ghosty support ls | open | show | reply | resolve | reopen | link");
|
|
14755
|
+
}
|
|
14646
14756
|
|
|
14647
14757
|
// src/index.ts
|
|
14648
14758
|
var HELP_EN = `${bold("Usage:")} ghosty <command> [options]
|
|
@@ -14752,6 +14862,9 @@ ${bold("Support")} ${dim("(Ghosty staff)")}
|
|
|
14752
14862
|
conversations show <id> <conv> --why A conversation: who sees it in their list / by link, and why
|
|
14753
14863
|
turns stats Real failure rate by engine and error [--since 14d] [--all]
|
|
14754
14864
|
push log --user EMAIL Pushes sent to someone and what Apple/Google answered [--since 2d]
|
|
14865
|
+
support ls|show|reply --all The support inbox; reply goes to the customer by email [--status open|waiting_customer|resolved] [--resolve]
|
|
14866
|
+
support resolve|reopen <id> Close a case (emails the customer) or reopen it
|
|
14867
|
+
support link <id> <issue|PR> Tie it to the GitHub fix: when it closes, every tied case gets "fixed" by email
|
|
14755
14868
|
novedades ls|create|set|test|send|rm Android app announcements [--title] [--body] [--image FILE] [--link] [--to] [--min-version N] [--publish] [--at]
|
|
14756
14869
|
|
|
14757
14870
|
${bold("Sites")} ${dim("(sites your agents publish at <slug>.ghosty.page)")}
|
|
@@ -14798,6 +14911,8 @@ ${bold("Your account")}
|
|
|
14798
14911
|
me memories ls|add|edit|rm What your agents remember about you (personal chats only) add "text" \xB7 edit <id> "text" \xB7 rm <id>
|
|
14799
14912
|
credentials ls|set|rm [provider] Provider keys in your vault (hidden prompt, stdin or --from-env VAR; never as an argument)
|
|
14800
14913
|
connectors ls|connect|disconnect [id] Your connectors (Shopify, Stripe, monday\u2026): connect opens the browser or asks for the key
|
|
14914
|
+
support open --title T --body TEXT Ask Ghosty for help; we answer by email (or ask your agent: "report it to support")
|
|
14915
|
+
support ls | show <id> | reply <id> --body TEXT Your support cases (replying to the email works too)
|
|
14801
14916
|
docs [topic] Open the docs
|
|
14802
14917
|
|
|
14803
14918
|
${bold("Global options")}
|
|
@@ -14920,6 +15035,9 @@ ${bold("Soporte")} ${dim("(staff de Ghosty)")}
|
|
|
14920
15035
|
conversations show <id> <conv> --why Una conversaci\xF3n: qui\xE9n la ve en su lista / por liga, y por qu\xE9
|
|
14921
15036
|
turns stats Tasa real de fallos por motor y por error [--since 14d] [--all]
|
|
14922
15037
|
push log --user CORREO Avisos que se le mandaron a alguien y qu\xE9 contest\xF3 Apple/Google [--since 2d]
|
|
15038
|
+
support ls|show|reply --all La bandeja de soporte; la respuesta le llega al cliente por correo [--status open|waiting_customer|resolved] [--resolve]
|
|
15039
|
+
support resolve|reopen <id> Cerrar un caso (le avisa al cliente) o reabrirlo
|
|
15040
|
+
support link <id> <issue|PR> Ligarlo al arreglo en GitHub: al cerrarse, a todos los casos ligados les llega \xABya qued\xF3\xBB
|
|
14923
15041
|
novedades ls|create|set|test|send|rm Novedades de la app Android [--title] [--body] [--image ARCHIVO] [--link] [--to] [--min-version N] [--publish] [--at]
|
|
14924
15042
|
|
|
14925
15043
|
${bold("Sitios")} ${dim("(los sitios que publican tus agentes en <slug>.ghosty.page)")}
|
|
@@ -14966,6 +15084,8 @@ ${bold("Tu cuenta")}
|
|
|
14966
15084
|
me memories ls|add|edit|rm Lo que tus agentes recuerdan de ti (s\xF3lo chats personales) add "texto" \xB7 edit <id> "texto" \xB7 rm <id>
|
|
14967
15085
|
credentials ls|set|rm [proveedor] Llaves de proveedor en tu b\xF3veda (prompt oculto, stdin o --from-env VAR; nunca como argumento)
|
|
14968
15086
|
connectors ls|connect|disconnect [id] Tus conectores (Shopify, Stripe, monday\u2026): connect abre el navegador o pide la llave
|
|
15087
|
+
support open --title T --body TEXTO Pide ayuda a Ghosty; te contestamos por correo (o dile a tu agente \xABrep\xF3rtalo a soporte\xBB)
|
|
15088
|
+
support ls | show <id> | reply <id> --body TEXTO Tus casos de soporte (contestar el correo tambi\xE9n sirve)
|
|
14969
15089
|
docs [tema] Abre la documentaci\xF3n
|
|
14970
15090
|
|
|
14971
15091
|
${bold("Opciones globales")}
|
|
@@ -14981,7 +15101,7 @@ ${bold("Opciones globales")}
|
|
|
14981
15101
|
${bold("C\xF3digos de salida")} 0 bien \xB7 1 error de la API \xB7 2 uso incorrecto \xB7 3 sin sesi\xF3n
|
|
14982
15102
|
${dim("Nota: `ghosty` puede chocar con otro binario en tu PATH (p. ej. la TUI de Ghosty). Rev\xEDsalo con `which -a ghosty`; corre `npx @ghostystudio/cli` para ir a la segura.")}
|
|
14983
15103
|
${dim("Documentaci\xF3n: https://www.ghosty.studio/docs/cli")}`;
|
|
14984
|
-
var COMMANDS = ["login", "logout", "whoami", "usage", "use", "apply", "plan", "engines", "bags", "spaces", "workspaces", "staff", "audit", "factory", "demos", "sites", "push", "novedades", "completion", "cards", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "partner", "me", "channels", "board", "boards", "credentials", "connectors", "browser", "docs", "help"];
|
|
15104
|
+
var COMMANDS = ["login", "logout", "whoami", "usage", "use", "apply", "plan", "engines", "bags", "spaces", "workspaces", "staff", "audit", "factory", "demos", "sites", "push", "novedades", "completion", "cards", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "partner", "me", "channels", "board", "boards", "credentials", "connectors", "browser", "support", "docs", "help"];
|
|
14985
15105
|
var AGENTS_SUBS = ["ls", "create", "get", "set", "export", "doctor", "docs", "share", "unshare", "shares", "teams", "rm", "restart", "demo", "embed", "box"];
|
|
14986
15106
|
function unknown(what, input, known, helpCmd) {
|
|
14987
15107
|
const s2 = suggest(input, known);
|
|
@@ -15134,6 +15254,7 @@ async function main() {
|
|
|
15134
15254
|
"columns-from": { type: "string" },
|
|
15135
15255
|
to: { type: "string" },
|
|
15136
15256
|
all: { type: "boolean" },
|
|
15257
|
+
resolve: { type: "boolean" },
|
|
15137
15258
|
clear: { type: "boolean" },
|
|
15138
15259
|
expires: { type: "string" },
|
|
15139
15260
|
lang: { type: "string" },
|
|
@@ -15200,6 +15321,7 @@ async function main() {
|
|
|
15200
15321
|
avatar: { type: "string" },
|
|
15201
15322
|
tool: { type: "string" },
|
|
15202
15323
|
"from-workspace": { type: "boolean" },
|
|
15324
|
+
archived: { type: "boolean" },
|
|
15203
15325
|
"quota-hits": { type: "boolean" },
|
|
15204
15326
|
by: { type: "string" },
|
|
15205
15327
|
models: { type: "boolean" },
|
|
@@ -15249,7 +15371,7 @@ async function main() {
|
|
|
15249
15371
|
const tokens = splitTokens(pos, v);
|
|
15250
15372
|
globals.token = tokens.session;
|
|
15251
15373
|
globals.profile = v.profile;
|
|
15252
|
-
if (v.version) return out({ version: "0.
|
|
15374
|
+
if (v.version) return out({ version: "0.62.0" }, () => "0.62.0");
|
|
15253
15375
|
for (const n of normalizeArgs(pos, v)) if (!globals.json) info(dim(n));
|
|
15254
15376
|
const context = (await readConfig()).workspace;
|
|
15255
15377
|
if (!explicitToken()?.startsWith("gat_")) {
|
|
@@ -15298,7 +15420,7 @@ async function main() {
|
|
|
15298
15420
|
case "agents":
|
|
15299
15421
|
if ((sub === "ls" || sub === "list" || !sub) && v.user && v.app !== void 0) return appAgentsOf(v.user, v.app);
|
|
15300
15422
|
if ((sub === "ls" || sub === "list" || !sub) && v.handle) return agentsByHandle(v.handle, v.workspace ?? context);
|
|
15301
|
-
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 });
|
|
15423
|
+
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 });
|
|
15302
15424
|
if (sub === "create" || sub === "new") {
|
|
15303
15425
|
if (v.workspace === "none") delete v.workspace;
|
|
15304
15426
|
else if (!v.workspace && context) {
|
|
@@ -15312,6 +15434,7 @@ async function main() {
|
|
|
15312
15434
|
if (sub === "teams") return agentsTeams(a1, a2, pos[4], v);
|
|
15313
15435
|
if (sub === "set") return agentsSet(a1, v);
|
|
15314
15436
|
if (sub === "rm" || sub === "delete") return agentsRm(a1, v);
|
|
15437
|
+
if (sub === "archive" || sub === "restore") return agentsArchive(a1, sub === "archive");
|
|
15315
15438
|
if (sub === "doctor" && v.user) return userDoctor(v.user, a1, v);
|
|
15316
15439
|
if (sub === "doctor") return agentsDoctor(a1, v);
|
|
15317
15440
|
if (sub === "docs" || sub === "documents") return agentDocsCmd(a1, a2, pos.slice(4).filter((x) => x !== void 0), v);
|
|
@@ -15335,6 +15458,8 @@ async function main() {
|
|
|
15335
15458
|
if (v["quota-hits"]) return workspaceUsage(v.workspace ?? context, v);
|
|
15336
15459
|
if (sub === "stats") return turnStats(v);
|
|
15337
15460
|
return turnsCmd(sub, a1, a2, v);
|
|
15461
|
+
case "support":
|
|
15462
|
+
return supportCmd(sub, a1, { ...v, ref: a2 });
|
|
15338
15463
|
case "push":
|
|
15339
15464
|
if (sub !== "log") throw usage(t("Unknown push subcommand.", "Subcomando de push desconocido."), "ghosty push log --user <email> [--since 2d]");
|
|
15340
15465
|
return pushLog(v.user, v);
|
|
@@ -15415,7 +15540,7 @@ async function main() {
|
|
|
15415
15540
|
throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
|
|
15416
15541
|
}
|
|
15417
15542
|
}
|
|
15418
|
-
var notice = updateNotice("0.
|
|
15543
|
+
var notice = updateNotice("0.62.0").catch(() => null);
|
|
15419
15544
|
main().then(
|
|
15420
15545
|
async () => {
|
|
15421
15546
|
const n = await notice;
|
package/package.json
CHANGED