@ghostystudio/cli 0.77.0 → 0.78.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/dist/index.js +152 -32
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1175,7 +1175,7 @@ async function postLong(path, body, timeoutMs = 9e5) {
|
|
|
1175
1175
|
const req = httpsRequest(`${base}${path}`, {
|
|
1176
1176
|
method: "POST",
|
|
1177
1177
|
timeout: timeoutMs,
|
|
1178
|
-
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.
|
|
1178
|
+
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.78.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
|
|
1179
1179
|
}, (res) => {
|
|
1180
1180
|
let buf = "";
|
|
1181
1181
|
res.on("data", (c) => buf += c);
|
|
@@ -1217,7 +1217,7 @@ async function request(path, opts = {}, retried = false) {
|
|
|
1217
1217
|
method: opts.method ?? (opts.body !== void 0 || opts.raw !== void 0 ? "POST" : "GET"),
|
|
1218
1218
|
headers: {
|
|
1219
1219
|
authorization: `Bearer ${token}`,
|
|
1220
|
-
"user-agent": `ghosty-cli/${"0.
|
|
1220
|
+
"user-agent": `ghosty-cli/${"0.78.0"}`,
|
|
1221
1221
|
"accept-language": lang(),
|
|
1222
1222
|
...opts.body !== void 0 ? { "content-type": "application/json" } : {},
|
|
1223
1223
|
...opts.headers
|
|
@@ -1264,7 +1264,7 @@ var mteams_exports = {};
|
|
|
1264
1264
|
__export(mteams_exports, {
|
|
1265
1265
|
mteamsCmd: () => mteamsCmd
|
|
1266
1266
|
});
|
|
1267
|
-
import { writeFile as
|
|
1267
|
+
import { writeFile as writeFile8 } from "node:fs/promises";
|
|
1268
1268
|
function list(bots) {
|
|
1269
1269
|
return bots.length ? table(
|
|
1270
1270
|
bots.map((b) => ({
|
|
@@ -1306,7 +1306,7 @@ ${dim(`${t("Next", "Sigue")}: ghosty channels mteams zip ${id} ${b?.id ?? "<bot-
|
|
|
1306
1306
|
const res = await request(`${path}/${enc9(botId)}/zip`);
|
|
1307
1307
|
const bytes = Buffer.from(await res.arrayBuffer());
|
|
1308
1308
|
const file = v.out ?? "ghosty-teams.zip";
|
|
1309
|
-
await
|
|
1309
|
+
await writeFile8(file, bytes);
|
|
1310
1310
|
return out({ saved: file, bytes: bytes.length }, () => `${lavender("\u2713")} ${t(`Saved ${file}: upload it in Teams admin \u2192 Manage apps.`, `Se guard\xF3 ${file}: s\xFAbelo en Teams admin \u2192 Administrar apps.`)}`);
|
|
1311
1311
|
}
|
|
1312
1312
|
case "on":
|
|
@@ -11205,7 +11205,7 @@ async function call(path, v, init = {}) {
|
|
|
11205
11205
|
"x-ghosty-ts": ts,
|
|
11206
11206
|
"x-ghosty-sig": sig,
|
|
11207
11207
|
"content-type": "application/json",
|
|
11208
|
-
"user-agent": `ghosty-cli/${"0.
|
|
11208
|
+
"user-agent": `ghosty-cli/${"0.78.0"}`
|
|
11209
11209
|
},
|
|
11210
11210
|
body: method === "GET" ? void 0 : raw
|
|
11211
11211
|
});
|
|
@@ -11482,9 +11482,41 @@ async function partnerCmd(sub, a1, a2, v, a3) {
|
|
|
11482
11482
|
}
|
|
11483
11483
|
}
|
|
11484
11484
|
|
|
11485
|
+
// src/commands/image.ts
|
|
11486
|
+
import { writeFile as writeFile6 } from "node:fs/promises";
|
|
11487
|
+
init_util();
|
|
11488
|
+
init_i18n();
|
|
11489
|
+
async function imageCmd(sub, prompt, v) {
|
|
11490
|
+
if (sub !== "generate" && sub !== "gen") {
|
|
11491
|
+
throw usage(t(`Unknown image subcommand: ${sub ?? "(none)"}.`, `Subcomando de image desconocido: ${sub ?? "(ninguno)"}.`), t("Try", "Prueba") + ': ghosty image generate "a cat astronaut" [--ref URL] [--aspect 16:9]');
|
|
11492
|
+
}
|
|
11493
|
+
if (!prompt?.trim()) throw usage(t("Missing the prompt.", "Falta el prompt."), t("Example", "Ejemplo") + ': ghosty image generate "my mascot in a Halloween scene" --ref https://example.com/mascot.png --aspect 16:9');
|
|
11494
|
+
const body = {
|
|
11495
|
+
prompt,
|
|
11496
|
+
...v.ref?.length ? { referenceUrls: v.ref } : {},
|
|
11497
|
+
...v.aspect ? { aspectRatio: v.aspect } : {},
|
|
11498
|
+
...v.tenant ? { tenant: { externalId: v.tenant } } : {},
|
|
11499
|
+
...v.agent ? { agent: v.agent } : {}
|
|
11500
|
+
};
|
|
11501
|
+
const r = await json("/api/v2/partner/images", v, { method: "POST", body });
|
|
11502
|
+
if (v.out) {
|
|
11503
|
+
const res = await fetch(r.url);
|
|
11504
|
+
if (!res.ok) throw new Error(t(`Could not download the image (${res.status}).`, `No se pudo bajar la imagen (${res.status}).`));
|
|
11505
|
+
await writeFile6(v.out, Buffer.from(await res.arrayBuffer()));
|
|
11506
|
+
}
|
|
11507
|
+
return out(
|
|
11508
|
+
r,
|
|
11509
|
+
() => [
|
|
11510
|
+
r.url,
|
|
11511
|
+
dim(`${r.mode === "edit" ? t("edited from references", "editada desde referencias") : t("generated", "generada")}${r.width && r.height ? ` \xB7 ${r.width}\xD7${r.height}` : ""} \xB7 ${t("charged", "cobrado")} USD ${r.priceUsd.toFixed(2)}`),
|
|
11512
|
+
...v.out ? [`${lavender("\u2713")} ${t(`saved to ${v.out}`, `guardada en ${v.out}`)}`] : []
|
|
11513
|
+
].join("\n")
|
|
11514
|
+
);
|
|
11515
|
+
}
|
|
11516
|
+
|
|
11485
11517
|
// src/commands/data.ts
|
|
11486
11518
|
init_api();
|
|
11487
|
-
import { readdir as readdir2, readFile as readFile6, writeFile as
|
|
11519
|
+
import { readdir as readdir2, readFile as readFile6, writeFile as writeFile7 } from "node:fs/promises";
|
|
11488
11520
|
import { basename as basename3, join as join5 } from "node:path";
|
|
11489
11521
|
|
|
11490
11522
|
// src/rows.ts
|
|
@@ -11653,7 +11685,7 @@ async function dbsExport(id, db, v) {
|
|
|
11653
11685
|
const suggested = /filename="([^"]+)"/.exec(res.headers.get("content-disposition") ?? "")?.[1] ?? (format === "csv" ? `${db}-${v.table}.csv` : `${db}.sqlite`);
|
|
11654
11686
|
const file = v.out ?? suggested;
|
|
11655
11687
|
const bytes = Buffer.from(await res.arrayBuffer());
|
|
11656
|
-
await
|
|
11688
|
+
await writeFile7(file, bytes);
|
|
11657
11689
|
const countsHeader = res.headers.get("x-gs-row-counts");
|
|
11658
11690
|
const counts = countsHeader ? JSON.parse(Buffer.from(countsHeader, "base64").toString("utf8")) : {};
|
|
11659
11691
|
const rows = Object.entries(counts).map(([tb, n]) => ({ table: tb, rows: n }));
|
|
@@ -11719,6 +11751,19 @@ ${dim(`${t(`${rows.length} row(s)`, `${rows.length} fila(s)`)} \xB7 ${Math.round
|
|
|
11719
11751
|
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 ?? "" }));
|
|
11720
11752
|
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.")));
|
|
11721
11753
|
}
|
|
11754
|
+
case "checkpoints": {
|
|
11755
|
+
db = need2(db, t("database (id or name)", "la base (id o nombre)"), "ghosty dbs checkpoints <agent-id> <db>");
|
|
11756
|
+
const r = await api(agentPath2(id, `/dbs/${enc8(db)}/checkpoints`));
|
|
11757
|
+
const rows = r.checkpoints.map((c) => ({ id: c.id, when: c.at, rows: Object.values(c.counts).reduce((a, b) => a + b, 0), why: c.reason }));
|
|
11758
|
+
return out(r, () => rows.length ? table(rows, ["id", "when", "rows", "why"]) : dim(t("No checkpoints yet: one is taken before the first write of each agent turn.", "Todav\xEDa no hay checkpoints: se toma uno antes de la primera escritura de cada turno de un agente.")));
|
|
11759
|
+
}
|
|
11760
|
+
case "restore": {
|
|
11761
|
+
db = need2(db, t("database (id or name)", "la base (id o nombre)"), "ghosty dbs restore <agent-id> <db> <checkpoint-id> [--yes]");
|
|
11762
|
+
const ckpt = need2(rest[0], t("checkpoint id (from dbs checkpoints)", "el id del checkpoint (de dbs checkpoints)"), `ghosty dbs restore ${id} ${db} <checkpoint-id>`);
|
|
11763
|
+
await confirm(t(`Return every table of ${db} to checkpoint ${ckpt}? A new checkpoint is taken first, so you can undo it.`, `\xBFRegresar todas las tablas de ${db} al checkpoint ${ckpt}? Antes se toma otro, as\xED que se puede deshacer.`), { yes: v.yes, retry: `ghosty dbs restore ${id} ${db} ${ckpt}` });
|
|
11764
|
+
const r = await api(agentPath2(id, `/dbs/${enc8(db)}/checkpoints`), { method: "POST", body: { checkpointId: ckpt } });
|
|
11765
|
+
return out(r, () => `${lavender("\u2713")} ${t(`${r.db} returned to ${r.restoredTo.at}.`, `${r.db} regresada a ${r.restoredTo.at}.`)} ${dim(r.undo ? t(`Undo: ghosty dbs restore ${id} ${db} ${r.undo.id}`, `Para deshacer: ghosty dbs restore ${id} ${db} ${r.undo.id}`) : r.warning ?? "")}`);
|
|
11766
|
+
}
|
|
11722
11767
|
case "grant": {
|
|
11723
11768
|
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]");
|
|
11724
11769
|
let rowScope;
|
|
@@ -11881,6 +11926,7 @@ function skipReason(r) {
|
|
|
11881
11926
|
reaction: ["only a reaction", "s\xF3lo una reacci\xF3n"],
|
|
11882
11927
|
channel_off: ["WhatsApp channel off for this agent", "canal de WhatsApp apagado en este agente"],
|
|
11883
11928
|
no_quota: ["out of balance", "sin saldo"],
|
|
11929
|
+
own_number: ["sent by another of our agent numbers (bot loop guard)", "lo mand\xF3 otro n\xFAmero nuestro con agente (guarda de bucle)"],
|
|
11884
11930
|
partner_token: ["partner did not issue a turn token", "el partner no dio token de turno"],
|
|
11885
11931
|
partner_busy: ["partner at its concurrent-turn limit", "el partner lleg\xF3 a su tope de turnos a la vez"]
|
|
11886
11932
|
};
|
|
@@ -13238,6 +13284,24 @@ var HELPS_ES = {
|
|
|
13238
13284
|
summary: "Ghosty para Chrome: cerrar la sesi\xF3n del navegador.",
|
|
13239
13285
|
notes: ["Revoca todos los tokens de Ghosty para Chrome que tengan tus agentes de terminal (Claude Code, CLI, @ghostystudio/browser-mcp) y desconecta la extensi\xF3n. Para volver, copia un comando nuevo del panel de la extensi\xF3n."]
|
|
13240
13286
|
},
|
|
13287
|
+
image: {
|
|
13288
|
+
summary: "Una imagen con IA (Gemini \xABNano Banana Pro\xBB) para los negocios de tu espacio de partner, cobrada del saldo del espacio.",
|
|
13289
|
+
replace: [
|
|
13290
|
+
["our mascot dressed as a pumpkin in a Halloween scene, flat illustration, no text", "nuestra mascota disfrazada de calabaza en una escena de Halloween, ilustraci\xF3n plana, sin texto"],
|
|
13291
|
+
["a cozy spa with candles, warm light", "un spa acogedor con velas, luz c\xE1lida"]
|
|
13292
|
+
],
|
|
13293
|
+
flags: [
|
|
13294
|
+
["--ref URL", "una imagen de referencia (https; JPEG, PNG, WebP, GIF o SVG); se repite, hasta 4. La imagen CONSERVA su personaje u objeto (mascota, logo, producto)"],
|
|
13295
|
+
["--aspect RATIO", "1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9 o 21:9"],
|
|
13296
|
+
["--tenant ID", "el negocio (externalId) para el que es: va al desglose de consumo y a su tope diario opcional"],
|
|
13297
|
+
["--agent SLUG", "a qu\xE9 agente del partner se carga en el desglose (default: el de siempre)"],
|
|
13298
|
+
["--out FILE", "adem\xE1s baja el PNG aqu\xED"]
|
|
13299
|
+
],
|
|
13300
|
+
notes: [
|
|
13301
|
+
`Cada imagen se cobra aparte: USD 0.25 del saldo del espacio de partner (las fallidas no se cobran). Sin saldo: 402. gs no pone topes propios; un negocio puede tener el suyo: tenants set <id> --config '{"imageDailyCap":20}'.`,
|
|
13302
|
+
"Auth: tu login (due\xF1a del espacio; --workspace si tienes varios) o la credencial del servidor del partner en el env (GHOSTY_PARTNER_KEY + GHOSTY_PARTNER_KEY_SECRET), que entonces exige --tenant."
|
|
13303
|
+
]
|
|
13304
|
+
},
|
|
13241
13305
|
me: {
|
|
13242
13306
|
summary: "Tu cuenta: archivos, memorias, documentos editables y uso.",
|
|
13243
13307
|
replace: [
|
|
@@ -13494,6 +13558,7 @@ var HELPS_ES = {
|
|
|
13494
13558
|
["--currency C", "pin: usd (default) o mxn \u2014 la moneda del cobro si hay que pagar"],
|
|
13495
13559
|
["--add-pack", "pin: ya usas tus lugares \u2014 suma 3 m\xE1s (mismo periodo y moneda de tu suscripci\xF3n) y fija \xE9ste"],
|
|
13496
13560
|
["--cancel / --resume", "pins: cancela al final del periodo (los sitios vuelven a Gratis con aviso), o lo deshace"],
|
|
13561
|
+
["--days N", "search: la ventana de Search Console en d\xEDas (default 28; Google reporta con 2\u20133 d\xEDas de retraso)"],
|
|
13497
13562
|
["--title T", "publish: el nombre del sitio (default: el <title> del HTML o el nombre de la carpeta)"],
|
|
13498
13563
|
["--slug S", "publish: el slug de un sitio NUEVO (a-z, 0-9 y guiones, 3\u201360, no reservado)"],
|
|
13499
13564
|
["--site X", "publish: actualiza ese sitio de la casa (slug, id o URL) en vez de crear uno"]
|
|
@@ -13589,7 +13654,7 @@ var HELPS_ES = {
|
|
|
13589
13654
|
["--note T", "changes: qu\xE9 cambiar del plan (o por qu\xE9 replanear tras un escalado). approve tras un escalado: qu\xE9 debe arreglar @build"],
|
|
13590
13655
|
["--check-scope T", "approve/changes: lo \xFAnico que @check revisa en la siguiente vuelta (lo propone runs advise); lo nuevo va como nota, no como hallazgo"],
|
|
13591
13656
|
["--skip-preview", "merge: mergea sin la preview que ped\xEDa el plan (queda en bit\xE1cora)"],
|
|
13592
|
-
["--gen
|
|
13657
|
+
["--gen KEY,KEY", "env push: genera ah\xED mismo llaves al azar (32 bytes hex) con esos nombres; nunca se imprimen"],
|
|
13593
13658
|
["--replace", "env push: reemplaza el .env entero de la preview (por defecto agrega y deja las dem\xE1s)"],
|
|
13594
13659
|
["--create", "rooms connect: crea el room si no existe"],
|
|
13595
13660
|
["--private", "rooms connect --create: el room nuevo es privado (s\xF3lo t\xFA, y a quien agregues)"],
|
|
@@ -13836,6 +13901,8 @@ var HELPS = {
|
|
|
13836
13901
|
"ghosty dbs tables <agent-id> <db>",
|
|
13837
13902
|
'ghosty dbs query <agent-id> <db> "SQL" [--arg V]\u2026 [--write]',
|
|
13838
13903
|
"ghosty dbs origin <agent-id> <db> [--table T] [--since 2026-10-10] [--limit N]",
|
|
13904
|
+
"ghosty dbs checkpoints <agent-id> <db>",
|
|
13905
|
+
"ghosty dbs restore <agent-id> <db> <checkpoint-id> [--yes]",
|
|
13839
13906
|
"ghosty dbs export <agent-id> <db> [--table T] [--out FILE]",
|
|
13840
13907
|
"ghosty dbs import <agent-id> <db> --from easybits:<db-id> [--tables a,b] [--append] [--rehost-images t.col,\u2026] [--dry-run] [--yes]",
|
|
13841
13908
|
"ghosty dbs rehost <agent-id> <db> --table T --column C [--dry-run]",
|
|
@@ -14020,6 +14087,26 @@ var HELPS = {
|
|
|
14020
14087
|
notes: ["Revokes every Ghosty for Chrome token your terminal agents hold (Claude Code, CLI, @ghostystudio/browser-mcp) and disconnects the extension. Copy a new command from the extension panel to reconnect."],
|
|
14021
14088
|
learn: `${REF}#navegador`
|
|
14022
14089
|
},
|
|
14090
|
+
image: {
|
|
14091
|
+
summary: "An AI image (Gemini \xABNano Banana Pro\xBB) for your partner businesses, charged to the space's balance.",
|
|
14092
|
+
usage: ['ghosty image generate "prompt" [--ref URL]... [--aspect 16:9] [--tenant ID] [--agent SLUG] [--out FILE]'],
|
|
14093
|
+
flags: [
|
|
14094
|
+
["--ref URL", "a reference image (https; JPEG, PNG, WebP, GIF or SVG); repeat it, up to 4. The image KEEPS its character or object (mascot, logo, product)"],
|
|
14095
|
+
["--aspect RATIO", "1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9 or 21:9"],
|
|
14096
|
+
["--tenant ID", "the business (externalId) it is for: it goes to the usage breakdown and to its optional daily cap"],
|
|
14097
|
+
["--agent SLUG", "which partner agent it is charged to in the breakdown (default: the main one)"],
|
|
14098
|
+
["--out FILE", "also download the PNG here"]
|
|
14099
|
+
],
|
|
14100
|
+
examples: [
|
|
14101
|
+
'ghosty image generate "our mascot dressed as a pumpkin in a Halloween scene, flat illustration, no text" --ref https://example.com/mascot.svg --aspect 16:9 --tenant org_123',
|
|
14102
|
+
'ghosty image generate "a cozy spa with candles, warm light" --aspect 1:1 --out spa.png'
|
|
14103
|
+
],
|
|
14104
|
+
notes: [
|
|
14105
|
+
`Each image is charged apart: USD 0.25 from the partner space's balance (failed ones are not charged). Without balance: 402. gs sets no cap of its own; a business can have one: tenants set <id> --config '{"imageDailyCap":20}'.`,
|
|
14106
|
+
"Auth: your login (space owner; --workspace if you have several) or the partner server credential in the env (GHOSTY_PARTNER_KEY + GHOSTY_PARTNER_KEY_SECRET), which then requires --tenant."
|
|
14107
|
+
],
|
|
14108
|
+
learn: `${DOCS}/partners#imagenes-con-ia`
|
|
14109
|
+
},
|
|
14023
14110
|
me: {
|
|
14024
14111
|
summary: "Your account: files, memories, editable docs and usage.",
|
|
14025
14112
|
usage: [
|
|
@@ -14425,6 +14512,7 @@ HELPS.sites = {
|
|
|
14425
14512
|
"ghosty sites pin <slug|id> [--interval year|month] [--currency usd|mxn] [--add-pack]",
|
|
14426
14513
|
"ghosty sites unpin <slug|id>",
|
|
14427
14514
|
"ghosty sites pins [--cancel | --resume]",
|
|
14515
|
+
"ghosty sites search <slug|id> [--days 28]",
|
|
14428
14516
|
"ghosty sites publish <file.html|folder> [--title T] [--slug S] [--site <slug|id>]",
|
|
14429
14517
|
"ghosty sites slug <slug|id> <new-slug>"
|
|
14430
14518
|
],
|
|
@@ -14434,6 +14522,7 @@ HELPS.sites = {
|
|
|
14434
14522
|
["--currency C", "pin: usd (default) or mxn \u2014 the currency charged if you need to pay"],
|
|
14435
14523
|
["--add-pack", "pin: you already use your slots \u2014 add 3 more (same period and currency as your subscription) and pin this one"],
|
|
14436
14524
|
["--cancel / --resume", "pins: cancel at period end (sites go back to Free with notice), or undo it"],
|
|
14525
|
+
["--days N", "search: Search Console window in days (default 28; Google reports 2\u20133 days late)"],
|
|
14437
14526
|
["--title T", "publish: the site's name (default: the HTML <title> or the folder name)"],
|
|
14438
14527
|
["--slug S", "publish: the slug for a NEW site (a-z, 0-9 and hyphens, 3\u201360, not reserved)"],
|
|
14439
14528
|
["--site X", "publish: update that house site (slug, id or URL) instead of creating one"]
|
|
@@ -14585,7 +14674,7 @@ function helpText(cmd) {
|
|
|
14585
14674
|
|
|
14586
14675
|
// src/update.ts
|
|
14587
14676
|
init_i18n();
|
|
14588
|
-
import { mkdir as mkdir5, readFile as readFile9, writeFile as
|
|
14677
|
+
import { mkdir as mkdir5, readFile as readFile9, writeFile as writeFile9 } from "node:fs/promises";
|
|
14589
14678
|
import { homedir as homedir3 } from "node:os";
|
|
14590
14679
|
import { dirname as dirname3, join as join7 } from "node:path";
|
|
14591
14680
|
var FILE = join7(process.env.XDG_CONFIG_HOME || join7(homedir3(), ".config"), "ghosty", "update.json");
|
|
@@ -14611,7 +14700,7 @@ async function latestVersion(current2) {
|
|
|
14611
14700
|
const latest = (await r.json()).version;
|
|
14612
14701
|
if (latest) cache2 = { latest, checkedAt: Date.now() };
|
|
14613
14702
|
await mkdir5(dirname3(FILE), { recursive: true });
|
|
14614
|
-
await
|
|
14703
|
+
await writeFile9(FILE, JSON.stringify(cache2));
|
|
14615
14704
|
} catch {
|
|
14616
14705
|
}
|
|
14617
14706
|
}
|
|
@@ -14691,7 +14780,7 @@ function agentArgIndex(pos) {
|
|
|
14691
14780
|
}
|
|
14692
14781
|
|
|
14693
14782
|
// src/completion.ts
|
|
14694
|
-
import { mkdir as mkdir6, readFile as readFile10, writeFile as
|
|
14783
|
+
import { mkdir as mkdir6, readFile as readFile10, writeFile as writeFile10 } from "node:fs/promises";
|
|
14695
14784
|
import { homedir as homedir4 } from "node:os";
|
|
14696
14785
|
import { join as join8 } from "node:path";
|
|
14697
14786
|
var GLOBAL_FLAGS = ["--json", "--token", "--profile", "--yes", "--help", "--version", "--lang", "--dry-run"];
|
|
@@ -14743,7 +14832,7 @@ async function names() {
|
|
|
14743
14832
|
const workspaces = [...new Set(list2.map((a) => a.espacio?.tipo === "workspace" ? a.espacio.nombre : void 0).filter((x) => !!x))];
|
|
14744
14833
|
await mkdir6(join8(cacheFile(), ".."), { recursive: true }).catch(() => {
|
|
14745
14834
|
});
|
|
14746
|
-
await
|
|
14835
|
+
await writeFile10(cacheFile(), JSON.stringify({ at: Date.now(), agents, workspaces })).catch(() => {
|
|
14747
14836
|
});
|
|
14748
14837
|
return { agents, workspaces };
|
|
14749
14838
|
}
|
|
@@ -14830,7 +14919,7 @@ init_i18n();
|
|
|
14830
14919
|
// src/commands/spec.ts
|
|
14831
14920
|
var import_yaml = __toESM(require_dist(), 1);
|
|
14832
14921
|
init_api();
|
|
14833
|
-
import { mkdir as mkdir7, readdir as readdir4, readFile as readFile11, stat as stat2, writeFile as
|
|
14922
|
+
import { mkdir as mkdir7, readdir as readdir4, readFile as readFile11, stat as stat2, writeFile as writeFile11 } from "node:fs/promises";
|
|
14834
14923
|
import { join as join9, resolve as resolve2 } from "node:path";
|
|
14835
14924
|
init_util();
|
|
14836
14925
|
init_i18n();
|
|
@@ -14844,7 +14933,7 @@ async function agentsExport(id, v) {
|
|
|
14844
14933
|
}
|
|
14845
14934
|
const dir = resolve2(v.out);
|
|
14846
14935
|
await mkdir7(dir, { recursive: true });
|
|
14847
|
-
await
|
|
14936
|
+
await writeFile11(join9(dir, "agent.yaml"), yaml);
|
|
14848
14937
|
const own = (0, import_yaml.parse)(yaml)["x-ghosty"]?.skills?.own ?? [];
|
|
14849
14938
|
for (const slug of own) {
|
|
14850
14939
|
const sk = await api(`/api/v2/agents/${enc12(id)}/skills/${enc12(slug)}`);
|
|
@@ -14967,7 +15056,7 @@ ${dim(t(`${r.totals.activeTurns} turn(s) running`, `${r.totals.activeTurns} turn
|
|
|
14967
15056
|
init_api();
|
|
14968
15057
|
init_auth();
|
|
14969
15058
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
14970
|
-
import { mkdir as mkdir8, readFile as readFile12, writeFile as
|
|
15059
|
+
import { mkdir as mkdir8, readFile as readFile12, writeFile as writeFile12 } from "node:fs/promises";
|
|
14971
15060
|
import { homedir as homedir5 } from "node:os";
|
|
14972
15061
|
import { dirname as dirname4, join as join10 } from "node:path";
|
|
14973
15062
|
init_i18n();
|
|
@@ -15012,7 +15101,7 @@ async function visitorCookie(base, slug, fresh) {
|
|
|
15012
15101
|
if (!cookie) throw new CliError(t("The demo didn't hand out a visitor cookie.", "El demo no entreg\xF3 cookie de visitante."), EXIT.API);
|
|
15013
15102
|
jar[slug] = cookie;
|
|
15014
15103
|
await mkdir8(dirname4(jarPath()), { recursive: true });
|
|
15015
|
-
await
|
|
15104
|
+
await writeFile12(jarPath(), JSON.stringify(jar, null, 2), { mode: 384 });
|
|
15016
15105
|
return cookie;
|
|
15017
15106
|
}
|
|
15018
15107
|
async function visitorTurn(base, slug, cookie, text) {
|
|
@@ -15128,7 +15217,7 @@ var packPrice = (c, i, lang2) => `$${(PACK[i]?.[c] ?? 0).toLocaleString("en-US")
|
|
|
15128
15217
|
var kb = (n) => n ? `${Math.max(1, Math.round(n / 1024))} KB` : "";
|
|
15129
15218
|
var day = (iso) => iso ? iso.slice(0, 10) : "";
|
|
15130
15219
|
async function sitesCmd(sub, a1, a2, v) {
|
|
15131
|
-
const ex = "ghosty sites ls [--house] \xB7 pin|unpin <slug|id> \xB7 pins [--cancel|--resume] \xB7 publish <file.html|folder> [--title T] [--slug S] [--site <slug|id>] \xB7 slug <slug|id> <new-slug>";
|
|
15220
|
+
const ex = "ghosty sites ls [--house] \xB7 pin|unpin <slug|id> \xB7 pins [--cancel|--resume] \xB7 search <slug|id> [--days 28] \xB7 publish <file.html|folder> [--title T] [--slug S] [--site <slug|id>] \xB7 slug <slug|id> <new-slug>";
|
|
15132
15221
|
switch (sub ?? "ls") {
|
|
15133
15222
|
case "ls":
|
|
15134
15223
|
case "list": {
|
|
@@ -15169,6 +15258,22 @@ ${dim(r.expiresAt ? t(`Free rule: pauses ${day(r.expiresAt)} unless it changes (
|
|
|
15169
15258
|
${table(r.pinned.map((p) => ({ slug: p.slug, title: (p.title ?? "").slice(0, 40), url: p.url, since: day(p.pinnedAt) })), ["slug", "title", "url", "since"])}` : t("No pinned sites. Pin one ($49 USD/yr or $5 USD/mo, includes up to 3 sites): ghosty sites pin <slug>", "Sin sitios fijos. Fija uno ($49 USD al a\xF1o o $5 USD al mes, incluye hasta 3 sitios): ghosty sites pin <slug>")
|
|
15170
15259
|
);
|
|
15171
15260
|
}
|
|
15261
|
+
case "search": {
|
|
15262
|
+
if (!a1) throw usage(t("Missing the site (slug, id or URL).", "Falta el sitio (slug, id o URL)."), "ghosty sites search <slug|id> [--days 28]");
|
|
15263
|
+
const r = await api(`/api/v2/sites/${encodeURIComponent(a1)}/search${v.days ? `?days=${encodeURIComponent(v.days)}` : ""}`);
|
|
15264
|
+
const p = r.portada;
|
|
15265
|
+
const index = p.error ? dim(p.error) : p.indexada ? `${lavender("\u2713")} ${t("indexed", "indexada")} ${dim(day(p.ultimoRastreo ?? null))}` : `${t("not indexed", "sin indexar")} ${dim(p.estado ?? "")}`;
|
|
15266
|
+
const tot = r.total;
|
|
15267
|
+
return out(
|
|
15268
|
+
r,
|
|
15269
|
+
() => [
|
|
15270
|
+
`${bold(r.sitio)} ${dim(`${r.desde} \u2192 ${r.hasta}`)}`,
|
|
15271
|
+
`${t("Home", "Portada")}: ${index}`,
|
|
15272
|
+
`${t("clicks", "clics")} ${tot.clics} \xB7 ${t("impressions", "impresiones")} ${tot.impresiones} \xB7 CTR ${tot.ctr} \xB7 ${t("position", "posici\xF3n")} ${tot.posicion ?? "\u2014"}`,
|
|
15273
|
+
r.busquedas.length ? table(r.busquedas.map((q) => ({ query: q.busqueda, clicks: q.clics, impressions: q.impresiones, position: q.posicion })), ["query", "clicks", "impressions", "position"]) : dim(t("No searches yet (Google reports 2\u20133 days late).", "A\xFAn sin b\xFAsquedas (Google reporta con 2\u20133 d\xEDas de atraso)."))
|
|
15274
|
+
].join("\n")
|
|
15275
|
+
);
|
|
15276
|
+
}
|
|
15172
15277
|
case "publish":
|
|
15173
15278
|
case "deploy": {
|
|
15174
15279
|
if (!a1) throw usage(t("Missing the file or folder.", "Falta el archivo o la carpeta."), ex);
|
|
@@ -15198,7 +15303,7 @@ ${dim(t("Links with the old slug may stop working.", "Las ligas con el slug viej
|
|
|
15198
15303
|
|
|
15199
15304
|
// src/commands/docs.ts
|
|
15200
15305
|
init_api();
|
|
15201
|
-
import { writeFile as
|
|
15306
|
+
import { writeFile as writeFile13 } from "node:fs/promises";
|
|
15202
15307
|
|
|
15203
15308
|
// ../docs/src/core/doc-blocks.ts
|
|
15204
15309
|
var DOC_ENVELOPE_VERSION = 1;
|
|
@@ -15361,7 +15466,7 @@ ${t("Ids: ghosty me docs ls", "Ids: ghosty me docs ls")}`);
|
|
|
15361
15466
|
}
|
|
15362
15467
|
const named = /filename\*=UTF-8''([^;]+)/i.exec(res.headers.get("content-disposition") ?? "")?.[1];
|
|
15363
15468
|
const file = v.out ?? (named ? decodeURIComponent(named) : fileName(id, format));
|
|
15364
|
-
await
|
|
15469
|
+
await writeFile13(file, bytes);
|
|
15365
15470
|
return out({ id, format, saved: file, bytes: bytes.length }, () => `${lavender("\u2713")} ${file} (${bytes.length} bytes)`);
|
|
15366
15471
|
}
|
|
15367
15472
|
case "check": {
|
|
@@ -15387,7 +15492,7 @@ ${t("Ids: ghosty me docs ls", "Ids: ghosty me docs ls")}`);
|
|
|
15387
15492
|
if (res.status === 204) return out({ id, block, segment, segments, saved: null }, () => dim(t("That block has no text to read.", "Ese bloque no tiene texto que leer.")));
|
|
15388
15493
|
const bytes = Buffer.from(await res.arrayBuffer());
|
|
15389
15494
|
const file = v.out ?? `${id}-${block}${segment !== null ? `-${segment}` : ""}.${audioExt(res.headers.get("content-type"))}`;
|
|
15390
|
-
await
|
|
15495
|
+
await writeFile13(file, bytes);
|
|
15391
15496
|
return out({ id, block, segment, segments, saved: file, bytes: bytes.length }, () => `${lavender("\u2713")} ${file} (${bytes.length} bytes) ${dim(t(`\xB7 the block has ${segments} sentence(s): --segment 0\u2026${Math.max(segments - 1, 0)}`, `\xB7 el bloque tiene ${segments} frase(s): --segment 0\u2026${Math.max(segments - 1, 0)}`))}`);
|
|
15392
15497
|
}
|
|
15393
15498
|
case "restore": {
|
|
@@ -15901,20 +16006,25 @@ ${dim(`${t("Detail", "Detalle")}: ghosty factory runs show <run> --workspace ${r
|
|
|
15901
16006
|
const { advice: a, workspace } = await api(`${path}/advise`, { body: {} });
|
|
15902
16007
|
const note = a.buildNote.map((p, i) => `${i + 1}. ${p.point}`).join(" ");
|
|
15903
16008
|
const sh = (x) => `'${x.replace(/'/g, `'\\''`)}'`;
|
|
15904
|
-
const apply = a.decision === "fix" ? `ghosty factory runs approve ${id} --note ${sh(note)}${a.checkScope ? ` --check-scope ${sh(a.checkScope)}` : ""} --workspace ${workspace}` : `ghosty factory runs changes ${id} --note ${sh(note)} --workspace ${workspace}
|
|
16009
|
+
const apply = a.decision === "fix" ? `ghosty factory runs approve ${id} --note ${sh(note)}${a.checkScope ? ` --check-scope ${sh(a.checkScope)}` : ""} --workspace ${workspace}` : a.decision === "replan" ? `ghosty factory runs changes ${id} --note ${sh(note)} --workspace ${workspace}` : a.decision === "recheck" ? `ghosty factory runs recheck ${id} --workspace ${workspace}` : null;
|
|
16010
|
+
const label = {
|
|
16011
|
+
fix: t("Let @build fix it", "Que @build lo arregle"),
|
|
16012
|
+
replan: t("Redo the plan", "Rehacer el plan"),
|
|
16013
|
+
close: t("Close the PR", "Cerrar el PR"),
|
|
16014
|
+
close_ignore: t("Close the PR and ignore this version", "Cerrar el PR e ignorar esta versi\xF3n"),
|
|
16015
|
+
recheck: t("Let @check review again", "Que @check revise otra vez"),
|
|
16016
|
+
wait: t("Wait: it's a platform fault", "Esperar: es una falla de la plataforma")
|
|
16017
|
+
}[a.decision] ?? a.decision;
|
|
15905
16018
|
return out({ workspace, advice: a, apply }, () => {
|
|
15906
16019
|
const conf = { high: t("high", "alta"), medium: t("medium", "media"), low: t("low", "baja") }[a.confidence] ?? a.confidence;
|
|
15907
16020
|
return [
|
|
15908
|
-
`${bold(`#${id}`)} ${bold(
|
|
16021
|
+
`${bold(`#${id}`)} ${bold(label)} ${dim(`\xB7 ${t("confidence", "confianza")} ${conf} \xB7 ${a.model}`)}`,
|
|
15909
16022
|
a.why,
|
|
15910
|
-
"",
|
|
15911
|
-
|
|
15912
|
-
...a.buildNote.map((p, i) => ` ${i + 1}. ${p.point}
|
|
15913
|
-
${dim(p.cites)}`),
|
|
16023
|
+
...a.buildNote.length ? ["", bold(a.decision === "fix" ? t("Note for @build", "Nota para @build") : t("What to change in the plan", "Qu\xE9 cambiar en el plan")), ...a.buildNote.map((p, i) => ` ${i + 1}. ${p.point}
|
|
16024
|
+
${dim(p.cites)}`)] : [],
|
|
15914
16025
|
...a.checkScope && a.decision === "fix" ? ["", bold(t("@check reviews only", "@check revisa s\xF3lo")), ` ${a.checkScope}`] : [],
|
|
15915
16026
|
"",
|
|
15916
|
-
dim(t("Nothing was applied. To apply it (edit the note first if you want):", "No se aplic\xF3 nada. Para aplicarla (edita la nota antes si quieres):")),
|
|
15917
|
-
` ${apply}`
|
|
16027
|
+
...apply ? [dim(t("Nothing was applied. To apply it (edit the note first if you want):", "No se aplic\xF3 nada. Para aplicarla (edita la nota antes si quieres):")), ` ${apply}`] : [dim(t("Nothing was applied. Decide it on the run's card or in the app.", "No se aplic\xF3 nada. Dec\xEDdelo en la tarjeta del pedido o en la app."))]
|
|
15918
16028
|
].join("\n");
|
|
15919
16029
|
});
|
|
15920
16030
|
}
|
|
@@ -16058,6 +16168,9 @@ async function adsCmd(sub, action, v) {
|
|
|
16058
16168
|
lavender(t("Meta ad accounts", "Cuentas de Meta")),
|
|
16059
16169
|
r.accounts.length ? table(r.accounts.map((a) => ({ workspace: a.workspace, account: a.account ?? "", active: `${a.active}/${a.campaigns}`, status: a.accountStatus ?? "", token: day3(a.tokenExpiresAt), checked: day3(a.checkedAt), alerted: day3(a.alertedAt), error: a.error ?? "" })), ["workspace", "account", "active", "status", "token", "checked", "alerted", "error"]) : dim(t("(none connected)", "(ninguna conectada)")),
|
|
16060
16170
|
"",
|
|
16171
|
+
lavender(t("Google Ads accounts", "Cuentas de Google Ads")),
|
|
16172
|
+
r.google?.length ? table(r.google.map((g) => ({ workspace: g.workspace, account: g.account, link: g.link, active: `${g.active}/${g.campaigns}`, status: g.accountStatus ?? "", checked: day3(g.checkedAt), alerted: day3(g.alertedAt), error: g.error ?? "" })), ["workspace", "account", "link", "active", "status", "checked", "alerted", "error"]) : dim(t("(none linked)", "(ninguna vinculada)")),
|
|
16173
|
+
"",
|
|
16061
16174
|
lavender(t("Ads-only WhatsApp numbers", "N\xFAmeros de WhatsApp \xABs\xF3lo anuncios\xBB")),
|
|
16062
16175
|
r.numbers.length ? table(r.numbers.map((n) => ({ number: n.number, "last ad lead": day3(n.lastAdLeadAt), alerted: day3(n.alertedAt) })), ["number", "last ad lead", "alerted"]) : dim(t("(none)", "(ninguno)"))
|
|
16063
16176
|
].join("\n")
|
|
@@ -16484,6 +16597,7 @@ ${bold("Sites")} ${dim("(sites your agents publish at <slug>.ghosty.page)")}
|
|
|
16484
16597
|
sites ls Sites your agents published (live and paused) [--house: Ghosty's own demos and landings (staff)]
|
|
16485
16598
|
sites pin <slug|id> Pin it: never pauses, no banner ($49 USD/yr or $5/mo, includes up to 3 sites) [--interval year|month] [--currency usd|mxn] [--add-pack: 3 more]
|
|
16486
16599
|
sites unpin <slug|id> Release its slot (back to the Free rule: 30 days + notice)
|
|
16600
|
+
sites search <slug|id> How Google finds it: clicks, impressions, searches, indexed or not [--days 28]
|
|
16487
16601
|
sites pins Your pinned-sites subscription and slots [--cancel | --resume]
|
|
16488
16602
|
sites publish <file.html|folder> Publish a house site (staff): no owner, no expiry, no banner [--title] [--slug S] [--site <slug|id>: update that one]
|
|
16489
16603
|
sites slug <slug|id> <new-slug> Change a site's slug (staff)
|
|
@@ -16526,6 +16640,7 @@ ${bold("Browser")} ${dim("(Ghosty for Chrome)")}
|
|
|
16526
16640
|
|
|
16527
16641
|
${bold("Partners")} ${dim("(give an agent to each of YOUR customers \u2014 see: ghosty partner --help)")}
|
|
16528
16642
|
partner ls | enable | credentials | agent | tenants | audit | try
|
|
16643
|
+
image generate "prompt" An AI image for your businesses, charged to the space's balance [--ref URL (repeat, keeps the character)] [--aspect 16:9] [--tenant ID] [--out FILE]
|
|
16529
16644
|
|
|
16530
16645
|
${bold("Your account")}
|
|
16531
16646
|
me files ls|get|export|upload|rm Files in your account [--q TEXT] [--kind image|video|audio|document|other] [--cursor C] \xB7 get <id> [--out FILE]
|
|
@@ -16727,6 +16842,7 @@ ${bold("Navegador")} ${dim("(Ghosty para Chrome)")}
|
|
|
16727
16842
|
|
|
16728
16843
|
${bold("Partners")} ${dim("(dale un agente a cada uno de TUS clientes \u2014 ver: ghosty partner --help)")}
|
|
16729
16844
|
partner ls | enable | credentials | agent | tenants | audit | try
|
|
16845
|
+
image generate "prompt" Una imagen con IA para tus negocios, cobrada del saldo del espacio [--ref URL (se repite, conserva al personaje)] [--aspect 16:9] [--tenant ID] [--out ARCHIVO]
|
|
16730
16846
|
|
|
16731
16847
|
${bold("Tu cuenta")}
|
|
16732
16848
|
me files ls|get|export|upload|rm Archivos de tu cuenta [--q TEXTO] [--kind image|video|audio|document|other] [--cursor C] \xB7 get <id> [--out ARCHIVO]
|
|
@@ -16752,7 +16868,7 @@ ${bold("Opciones globales")}
|
|
|
16752
16868
|
${bold("C\xF3digos de salida")} 0 bien \xB7 1 error de la API \xB7 2 uso incorrecto \xB7 3 sin sesi\xF3n
|
|
16753
16869
|
${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.")}
|
|
16754
16870
|
${dim("Documentaci\xF3n: https://www.ghosty.studio/docs/cli")}`;
|
|
16755
|
-
var COMMANDS = ["login", "logout", "whoami", "usage", "use", "apply", "plan", "engines", "bags", "spaces", "workspaces", "staff", "settings", "ads", "audit", "factory", "demos", "sites", "push", "novedades", "completion", "cards", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "teams", "partner", "me", "channels", "board", "boards", "access", "credentials", "connectors", "browser", "support", "docs", "help"];
|
|
16871
|
+
var COMMANDS = ["login", "logout", "whoami", "usage", "use", "apply", "plan", "engines", "bags", "spaces", "workspaces", "staff", "settings", "ads", "audit", "factory", "demos", "sites", "push", "novedades", "completion", "cards", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "teams", "partner", "image", "me", "channels", "board", "boards", "access", "credentials", "connectors", "browser", "support", "docs", "help"];
|
|
16756
16872
|
var AGENTS_SUBS = ["ls", "create", "get", "show", "set", "export", "doctor", "docs", "share", "unshare", "shares", "teams", "rm", "restart", "demo", "embed", "box", "token", "memory", "transfer"];
|
|
16757
16873
|
function unknown(what, input, known, helpCmd) {
|
|
16758
16874
|
const s2 = suggest(input, known);
|
|
@@ -17021,6 +17137,8 @@ async function main() {
|
|
|
17021
17137
|
draft: { type: "boolean" },
|
|
17022
17138
|
"ads-only": { type: "string" },
|
|
17023
17139
|
"partner-tenant": { type: "string" },
|
|
17140
|
+
ref: { type: "string", multiple: true },
|
|
17141
|
+
aspect: { type: "string" },
|
|
17024
17142
|
tenant: { type: "string" },
|
|
17025
17143
|
"app-id": { type: "string" },
|
|
17026
17144
|
secret: { type: "string" },
|
|
@@ -17055,14 +17173,14 @@ async function main() {
|
|
|
17055
17173
|
const tokens = splitTokens(pos, v);
|
|
17056
17174
|
globals.token = tokens.session;
|
|
17057
17175
|
globals.profile = v.profile;
|
|
17058
|
-
if (v.version) return out({ version: "0.
|
|
17176
|
+
if (v.version) return out({ version: "0.78.0" }, () => "0.78.0");
|
|
17059
17177
|
for (const n of normalizeArgs(pos, v)) if (!globals.json) info(dim(n));
|
|
17060
17178
|
const context = (await readConfig()).workspace;
|
|
17061
17179
|
if (!explicitToken()?.startsWith("gat_")) {
|
|
17062
17180
|
const at = agentArgIndex(pos);
|
|
17063
17181
|
if (at !== null && pos[at]) pos[at] = await resolveAgent(pos[at], context);
|
|
17064
17182
|
for (const f of ["like", "prompt-base", "agent"]) {
|
|
17065
|
-
if (f === "agent" && pos[0] === "partner") continue;
|
|
17183
|
+
if (f === "agent" && (pos[0] === "partner" || pos[0] === "image")) continue;
|
|
17066
17184
|
const val = v[f];
|
|
17067
17185
|
if (typeof val === "string" && val !== "none") v[f] = await resolveAgent(val, context);
|
|
17068
17186
|
}
|
|
@@ -17196,6 +17314,8 @@ async function main() {
|
|
|
17196
17314
|
return browserCmd(sub, v);
|
|
17197
17315
|
case "partner":
|
|
17198
17316
|
return partnerCmd(sub, a1, a2, { ...v, token: tokens.turn }, pos[4]);
|
|
17317
|
+
case "image":
|
|
17318
|
+
return imageCmd(sub, a1, v);
|
|
17199
17319
|
case "usage":
|
|
17200
17320
|
if (v.images) return imageUsage(v);
|
|
17201
17321
|
if (v.boxes && v.by === "template") return fleetBoxes();
|
|
@@ -17250,7 +17370,7 @@ async function main() {
|
|
|
17250
17370
|
throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
|
|
17251
17371
|
}
|
|
17252
17372
|
}
|
|
17253
|
-
var notice = updateNotice("0.
|
|
17373
|
+
var notice = updateNotice("0.78.0").catch(() => null);
|
|
17254
17374
|
main().then(
|
|
17255
17375
|
async () => {
|
|
17256
17376
|
const n = await notice;
|
package/package.json
CHANGED