@ghostystudio/cli 0.47.0 → 0.49.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 +11 -2
- package/dist/index.js +194 -56
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ ghosty files ls|get|put|rm <id> [path] [--file LOCAL] [--out LOCAL]
|
|
|
47
47
|
ghosty skills ls|get|add|rm <id> [slug] [--file SKILL.md | --dir FOLDER] [--out DIR] # get without --out only lists
|
|
48
48
|
ghosty skills enable|disable <id> <slug> # turn one of its skills (or one of Ghosty's) on or off
|
|
49
49
|
ghosty skills templates <id> # pdf-doc templates: its own and Ghosty's, and which folder wins
|
|
50
|
-
ghosty dbs ls|tables|query|import|grant|revoke <id> [db] [--write] [--arg V]… [--external-write t1,t2] [--external-rows t:col+col] [--from easybits:ID --tables a,b --append --dry-run]
|
|
50
|
+
ghosty dbs ls|tables|query|export|import|grant|revoke <id> [db] [--write] [--arg V]… [--external-write t1,t2] [--external-rows t:col+col] [--from easybits:ID --tables a,b --append --dry-run]
|
|
51
51
|
ghosty turns ls <id> [--errors | --status running|done|stopped|error] [--since 1h] [--limit N]
|
|
52
52
|
ghosty turns ls --workspace SLUG --quota-hits [--days 14] # messages that got no turn because the balance ran out
|
|
53
53
|
ghosty turns show <id> [turn-id|last] # what came in, what it answered, where the time went [--full]
|
|
@@ -76,7 +76,7 @@ ghosty schedule ls <id>
|
|
|
76
76
|
ghosty schedule rm <id> <schedule-id>
|
|
77
77
|
ghosty schedule add|ls|rm <id> … --teams <workspace>#<room>[/<thread-id>] # in a Teams room/thread: it answers there as if mentioned
|
|
78
78
|
|
|
79
|
-
ghosty me files ls|get|upload|rm
|
|
79
|
+
ghosty me files ls|get|export|upload|rm [--q TEXT] [--kind image|video|audio|document|other] [--cursor C]
|
|
80
80
|
ghosty me memories ls|add|edit|rm
|
|
81
81
|
ghosty conversations archive|rm <id> --kind test|channel [--before 7d] [--dry-run]
|
|
82
82
|
ghosty agents docs dedupe [--dry-run] # your account: merge documents with the same content
|
|
@@ -103,6 +103,15 @@ Every command has its own help with examples: `ghosty <command> --help` or `ghos
|
|
|
103
103
|
|
|
104
104
|
Exit codes: `0` ok · `1` API error · `2` usage error · `3` not signed in.
|
|
105
105
|
|
|
106
|
+
### Changes in 0.49.0
|
|
107
|
+
|
|
108
|
+
- `dbs export <id> <db> [--table T] [--out FILE]`: download a database — the whole thing as `.sqlite` (sqlite3, DB Browser, DBeaver) or one table as CSV (Excel). Same buttons on each database's page in /app/datos.
|
|
109
|
+
- `me files export [--out DIR] [--kind K] [--dry-run]`: download every file in your account into `documents/`, `conversations/` and `library/`; rerunning only brings what's new.
|
|
110
|
+
|
|
111
|
+
### Changes in 0.48.0
|
|
112
|
+
|
|
113
|
+
- `agents teams on <id> <workspace> --factory plan,build,check`: make an agent those Software Factory roles in that workspace (same as «Team» in /factory; each role keeps its name and avatar). `agents teams ls` shows the roles it holds.
|
|
114
|
+
|
|
106
115
|
### Changes in 0.47.0
|
|
107
116
|
|
|
108
117
|
- `agents set --quote` takes VAT rules for the house `cotizar` tool: `iva-incluido=true` (prices already include VAT), `iva.CATEGORY=16` (VAT for that category), `solo-factura=true` (VAT only when the customer asks for an invoice).
|
package/dist/index.js
CHANGED
|
@@ -7462,6 +7462,13 @@ var SERVER_MESSAGES = [
|
|
|
7462
7462
|
["@{} ya est\xE1 ocupado por otro agente en este espacio: dale un handle propio", "@{} is already taken by another agent in this workspace: give it its own handle"],
|
|
7463
7463
|
// — Activar en equipos de Teams (`agents teams`)
|
|
7464
7464
|
["@{} ya est\xE1 ocupado en {}", "@{} is already taken in {}"],
|
|
7465
|
+
["la f\xE1brica no est\xE1 instalada en {}: inst\xE1lala desde /factory en Teams", "the Software Factory isn't installed in {}: install it from /factory in Teams"],
|
|
7466
|
+
["la f\xE1brica no tiene el rol {}: usa {}", "the Software Factory has no role {}: use {}"],
|
|
7467
|
+
["faltan los roles: {}", "missing the roles: {}"],
|
|
7468
|
+
["@eval no existe en {}: act\xEDvalo desde /factory en Teams", "@eval doesn't exist in {}: turn it on from /factory in Teams"],
|
|
7469
|
+
["@{} no existe en {}: reinstala la f\xE1brica desde /factory en Teams", "@{} doesn't exist in {}: reinstall the Software Factory from /factory in Teams"],
|
|
7470
|
+
["@{} en {} es de otro agente, no de la f\xE1brica", "@{} in {} belongs to another agent, not to the Software Factory"],
|
|
7471
|
+
["factory: lista de roles (plan, build, check, eval)", "factory: a list of roles (plan, build, check, eval)"],
|
|
7465
7472
|
["@{} est\xE1 reservado", "@{} is reserved"],
|
|
7466
7473
|
["los @handle {} a {}5 ya est\xE1n ocupados en {}", "handles @{} to @{}5 are already taken in {}"],
|
|
7467
7474
|
["no administras el equipo {}", "you don't administer the workspace {}"],
|
|
@@ -8120,6 +8127,11 @@ var SERVER_MESSAGES = [
|
|
|
8120
8127
|
["no pude conectar con el servicio de sitios ({})", "couldn't reach the sites service ({})"],
|
|
8121
8128
|
["no se pudo crear el sitio; int\xE9ntalo en un momento", "couldn't create the site; try again in a moment"],
|
|
8122
8129
|
["no se pudo subir {}: {}", "couldn't upload {}: {}"],
|
|
8130
|
+
// — Exportar bases (dbs export)
|
|
8131
|
+
["el CSV es de una tabla: falta table", "a CSV is one table: table is missing"],
|
|
8132
|
+
["el token del agente no descarga bases: usa tu sesi\xF3n (ghosty login)", "the agent's token doesn't download databases: use your session (ghosty login)"],
|
|
8133
|
+
["format es sqlite o csv", "format is sqlite or csv"],
|
|
8134
|
+
["no existe la tabla {} en {}", "there's no table {} in {}"],
|
|
8123
8135
|
// — Infra interna (casi nunca llega al CLI, pero por si acaso)
|
|
8124
8136
|
["SESSION_SECRET ausente en producci\xF3n \u2014 las cookies quedar\xEDan firmadas con un valor p\xFAblico. Inyectarlo con ./scripts/set_gs_secret.sh SESSION_SECRET (openssl rand -hex 32).", "SESSION_SECRET missing in production \u2014 cookies would be signed with a public value. Inject it with ./scripts/set_gs_secret.sh SESSION_SECRET (openssl rand -hex 32)."],
|
|
8125
8137
|
["sqld: fetch sin namespace", "sqld: fetch without namespace"],
|
|
@@ -8422,7 +8434,7 @@ async function postLong(path, body, timeoutMs = 9e5) {
|
|
|
8422
8434
|
const req = httpsRequest(`${base}${path}`, {
|
|
8423
8435
|
method: "POST",
|
|
8424
8436
|
timeout: timeoutMs,
|
|
8425
|
-
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.
|
|
8437
|
+
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.49.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
|
|
8426
8438
|
}, (res) => {
|
|
8427
8439
|
let buf = "";
|
|
8428
8440
|
res.on("data", (c) => buf += c);
|
|
@@ -8464,7 +8476,7 @@ async function request(path, opts = {}, retried = false) {
|
|
|
8464
8476
|
method: opts.method ?? (opts.body !== void 0 || opts.raw !== void 0 ? "POST" : "GET"),
|
|
8465
8477
|
headers: {
|
|
8466
8478
|
authorization: `Bearer ${token}`,
|
|
8467
|
-
"user-agent": `ghosty-cli/${"0.
|
|
8479
|
+
"user-agent": `ghosty-cli/${"0.49.0"}`,
|
|
8468
8480
|
"accept-language": lang(),
|
|
8469
8481
|
...opts.body !== void 0 ? { "content-type": "application/json" } : {},
|
|
8470
8482
|
...opts.headers
|
|
@@ -8965,7 +8977,7 @@ ${rows.length ? table(rows, ["email", "role", "accepted"]) : dim(t("Not shared w
|
|
|
8965
8977
|
out(r, () => `${lavender("\u2713")} ${r.already ? t(`Already shared with ${r.email}`, `Ya estaba compartido con ${r.email}`) : t(`Shared with ${r.email}; they got an email`, `Compartido con ${r.email}; le lleg\xF3 un correo`)} ${dim(`(${r.role})`)}.`);
|
|
8966
8978
|
}
|
|
8967
8979
|
async function agentsTeams(sub, id, ws, v) {
|
|
8968
|
-
const ex = "ghosty agents teams ls <agent-id> \xB7 on <agent-id> <workspace> [--handle x] \xB7 off <agent-id> <workspace> \xB7 rm <agent-id> <workspace> [--yes]";
|
|
8980
|
+
const ex = "ghosty agents teams ls <agent-id> \xB7 on <agent-id> <workspace> [--handle x | --factory plan,build,check] \xB7 off <agent-id> <workspace> \xB7 rm <agent-id> <workspace> [--yes]";
|
|
8969
8981
|
if (sub !== "ls" && sub !== "on" && sub !== "off" && sub !== "rm") throw usage(t("Use ls, on, off or rm.", "Usa ls, on, off o rm."), ex);
|
|
8970
8982
|
id = need(id, "agent id", ex);
|
|
8971
8983
|
if (sub === "ls") {
|
|
@@ -8975,7 +8987,7 @@ async function agentsTeams(sub, id, ws, v) {
|
|
|
8975
8987
|
const state = x.state ?? (x.active ? "on" : "off");
|
|
8976
8988
|
return {
|
|
8977
8989
|
workspace: x.slug,
|
|
8978
|
-
handle: state === "off" ? dim(`(@${x.suggested})`) : `@${x.handle}
|
|
8990
|
+
handle: state === "off" ? dim(`(@${x.suggested})`) : `@${x.handle}` + (x.roles?.filter((h) => h !== x.handle).length ? dim(` + ${x.roles.filter((h) => h !== x.handle).map((h) => `@${h}`).join(" ")}`) : ""),
|
|
8979
8991
|
state: !x.confirmed ? t("unconfirmed", "sin confirmar") : label[state] + (x.editable === false ? dim(t(" \xB7 not yours to change", " \xB7 no lo administras")) : "")
|
|
8980
8992
|
};
|
|
8981
8993
|
});
|
|
@@ -8992,6 +9004,13 @@ async function agentsTeams(sub, id, ws, v) {
|
|
|
8992
9004
|
const r2 = await api(agentPath(id, `/teams?workspace=${encodeURIComponent(workspace)}`), { method: "DELETE", body: { workspace } });
|
|
8993
9005
|
return out(r2, () => `${lavender("\u2713")} ${r2.wasActive ? t(`Paused in ${workspace}; it keeps its @handle.`, `En pausa en ${workspace}; conserva su @handle.`) : t(`It wasn't answering in ${workspace}.`, `No estaba contestando en ${workspace}.`)}`);
|
|
8994
9006
|
}
|
|
9007
|
+
if (v.factory !== void 0) {
|
|
9008
|
+
if (v.handle) throw usage(t("Use --handle or --factory, not both.", "Usa --handle o --factory, no los dos."), ex);
|
|
9009
|
+
const roles = v.factory.split(",").map((x) => x.trim().replace(/^@/, "")).filter(Boolean);
|
|
9010
|
+
if (!roles.length) throw usage(t("--factory needs roles: plan,build,check (or eval).", "--factory necesita roles: plan,build,check (o eval)."), ex);
|
|
9011
|
+
const r2 = await api(agentPath(id, "/teams"), { method: "POST", body: { workspace, factory: roles } });
|
|
9012
|
+
return out(r2, () => `${lavender("\u2713")} ${t(`In ${workspace} it now is`, `En ${workspace} ahora es`)} ${r2.roles.map((x) => `@${x.handle}`).join(", ")}${dim(t(` (before: ${r2.roles.map((x) => x.previous ?? "\u2014").join(", ")})`, ` (antes: ${r2.roles.map((x) => x.previous ?? "\u2014").join(", ")})`))}. ${t("The next turn of each role runs on it.", "El siguiente turno de cada rol ya corre en \xE9l.")}`);
|
|
9013
|
+
}
|
|
8995
9014
|
const r = await api(agentPath(id, "/teams"), { method: "POST", body: { workspace, ...v.handle ? { handle: v.handle } : {} } });
|
|
8996
9015
|
out(r, () => `${lavender("\u2713")} ${r.changed ? t(`Answers in ${workspace} as @${r.handle}.`, `Contesta en ${workspace} como @${r.handle}.`) : t(`Already answering in ${workspace} as @${r.handle}.`, `Ya contestaba en ${workspace} como @${r.handle}.`)}`);
|
|
8997
9016
|
}
|
|
@@ -9257,8 +9276,8 @@ function capacityLine(c) {
|
|
|
9257
9276
|
// src/commands/chat.ts
|
|
9258
9277
|
import { randomUUID } from "node:crypto";
|
|
9259
9278
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
9260
|
-
import { readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
|
|
9261
|
-
import { basename as basename2 } from "node:path";
|
|
9279
|
+
import { mkdir as mkdir4, readFile as readFile4, stat, writeFile as writeFile4 } from "node:fs/promises";
|
|
9280
|
+
import { basename as basename2, extname, join as join4 } from "node:path";
|
|
9262
9281
|
|
|
9263
9282
|
// src/sse.ts
|
|
9264
9283
|
async function* readSse(body) {
|
|
@@ -9468,9 +9487,65 @@ async function meMemories(sub, arg, text) {
|
|
|
9468
9487
|
throw usage(t(`Unknown memories subcommand: ${sub}.`, `Subcomando de memories desconocido: ${sub}.`), ex);
|
|
9469
9488
|
}
|
|
9470
9489
|
}
|
|
9490
|
+
var safeName = (s) => s.replace(/[\\/:*?"<>|\u0000-\u001f]+/g, "_").trim() || "archivo";
|
|
9491
|
+
async function meFilesExport(v) {
|
|
9492
|
+
const dir = v.out ?? "ghosty-archivos";
|
|
9493
|
+
const all = [];
|
|
9494
|
+
let cursor = null;
|
|
9495
|
+
do {
|
|
9496
|
+
const q = new URLSearchParams({ all: "1", ...cursor ? { cursor } : {} });
|
|
9497
|
+
const r = await api(`/api/v2/me/files?${q}`);
|
|
9498
|
+
all.push(...r.files);
|
|
9499
|
+
cursor = r.nextCursor;
|
|
9500
|
+
} while (cursor);
|
|
9501
|
+
const files = v.kind ? all.filter((f) => f.kind === v.kind) : all;
|
|
9502
|
+
const folder = { documents: t("documents", "documentos"), conversations: t("conversations", "conversaciones"), library: t("library", "biblioteca") };
|
|
9503
|
+
const total = files.reduce((n, f) => n + f.size, 0);
|
|
9504
|
+
if (v["dry-run"]) {
|
|
9505
|
+
const byGroup = ["documents", "conversations", "library"].map((g) => files.filter((f) => f.group === g)).filter((fs) => fs.length).map((fs) => ({ folder: folder[fs[0].group], files: fs.length, MB: (fs.reduce((n, f) => n + f.size, 0) / 1e6).toFixed(1) }));
|
|
9506
|
+
return out({ dir, files: files.length, bytes: total, groups: byGroup }, () => `${table(byGroup, ["folder", "files", "MB"])}
|
|
9507
|
+
${dim(t(`${files.length} file(s), ${(total / 1e6).toFixed(1)} MB \u2192 ${dir}/ (without --dry-run it downloads them)`, `${files.length} archivo(s), ${(total / 1e6).toFixed(1)} MB \u2192 ${dir}/ (sin --dry-run los baja)`))}`);
|
|
9508
|
+
}
|
|
9509
|
+
const used = /* @__PURE__ */ new Set();
|
|
9510
|
+
let saved = 0, skipped = 0;
|
|
9511
|
+
const failed = [];
|
|
9512
|
+
for (const [i, f] of files.entries()) {
|
|
9513
|
+
const sub = join4(dir, folder[f.group]);
|
|
9514
|
+
let name = safeName(f.name);
|
|
9515
|
+
if (used.has(join4(sub, name).toLowerCase())) name = `${basename2(name, extname(name))}-${f.id.slice(-6)}${extname(name)}`;
|
|
9516
|
+
used.add(join4(sub, name).toLowerCase());
|
|
9517
|
+
const path = join4(sub, name);
|
|
9518
|
+
const prev = await stat(path).catch(() => null);
|
|
9519
|
+
if (prev && prev.size === f.size) {
|
|
9520
|
+
skipped++;
|
|
9521
|
+
continue;
|
|
9522
|
+
}
|
|
9523
|
+
if (!globals.json) process.stderr.write(dim(`\r${i + 1}/${files.length} ${name.slice(0, 60)}`.padEnd(80)));
|
|
9524
|
+
try {
|
|
9525
|
+
const { url } = await api(`/api/v2/me/files/${enc3(f.id)}`);
|
|
9526
|
+
const res = await fetch(url);
|
|
9527
|
+
if (!res.ok) throw new Error(`${res.status}`);
|
|
9528
|
+
await mkdir4(sub, { recursive: true });
|
|
9529
|
+
await writeFile4(path, Buffer.from(await res.arrayBuffer()));
|
|
9530
|
+
saved++;
|
|
9531
|
+
} catch (e) {
|
|
9532
|
+
failed.push({ id: f.id, name: f.name, error: e.message });
|
|
9533
|
+
}
|
|
9534
|
+
}
|
|
9535
|
+
if (!globals.json) process.stderr.write("\r".padEnd(81) + "\r");
|
|
9536
|
+
return out(
|
|
9537
|
+
{ dir, saved, skipped, failed },
|
|
9538
|
+
() => `${lavender("\u2713")} ${t(`${saved} downloaded, ${skipped} already there \u2192 ${dir}/`, `${saved} bajado(s), ${skipped} ya estaban \u2192 ${dir}/`)}` + (failed.length ? `
|
|
9539
|
+
${red(t(`${failed.length} failed:`, `${failed.length} fallaron:`))}
|
|
9540
|
+
${failed.map((x) => ` ${x.name} ${dim(`${x.id} \xB7 ${x.error}`)}`).join("\n")}` : "")
|
|
9541
|
+
);
|
|
9542
|
+
}
|
|
9471
9543
|
async function meFiles(sub, arg, v) {
|
|
9472
|
-
const ex = "ghosty me files ls [--q TEXT] [--kind image|video|audio|document|other] [--cursor C] | get <file-id> [--out FILE] | upload <file> | rm <file-id>";
|
|
9544
|
+
const ex = "ghosty me files ls [--q TEXT] [--kind image|video|audio|document|other] [--cursor C] | get <file-id> [--out FILE] | export [--out DIR] | upload <file> | rm <file-id>";
|
|
9473
9545
|
switch (sub) {
|
|
9546
|
+
case "export":
|
|
9547
|
+
case "download":
|
|
9548
|
+
return meFilesExport(v);
|
|
9474
9549
|
case "ls": {
|
|
9475
9550
|
if (v.q !== void 0 || v.cursor) {
|
|
9476
9551
|
const q = new URLSearchParams({ q: v.q ?? "", ...v.kind ? { kind: v.kind } : {}, ...v.cursor ? { cursor: v.cursor } : {}, ...v.limit ? { limit: v.limit } : {} });
|
|
@@ -9781,7 +9856,7 @@ async function call(path, v, init = {}) {
|
|
|
9781
9856
|
"x-ghosty-ts": ts,
|
|
9782
9857
|
"x-ghosty-sig": sig,
|
|
9783
9858
|
"content-type": "application/json",
|
|
9784
|
-
"user-agent": `ghosty-cli/${"0.
|
|
9859
|
+
"user-agent": `ghosty-cli/${"0.49.0"}`
|
|
9785
9860
|
},
|
|
9786
9861
|
body: method === "GET" ? void 0 : raw
|
|
9787
9862
|
});
|
|
@@ -10036,8 +10111,8 @@ async function partnerCmd(sub, a1, a2, v, a3) {
|
|
|
10036
10111
|
}
|
|
10037
10112
|
|
|
10038
10113
|
// src/commands/data.ts
|
|
10039
|
-
import { readdir as readdir2, readFile as readFile6 } from "node:fs/promises";
|
|
10040
|
-
import { basename as basename3, join as
|
|
10114
|
+
import { readdir as readdir2, readFile as readFile6, writeFile as writeFile6 } from "node:fs/promises";
|
|
10115
|
+
import { basename as basename3, join as join5 } from "node:path";
|
|
10041
10116
|
|
|
10042
10117
|
// src/rows.ts
|
|
10043
10118
|
var IDENT = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
@@ -10194,6 +10269,26 @@ ${t(`Delete ${dry.name} for good? This cannot be undone.`, `\xBFBorrar ${dry.nam
|
|
|
10194
10269
|
const r = await api(path, { body: {} });
|
|
10195
10270
|
return out(r, () => `${lavender("\u2713")} ${t(`${r.name} deleted.`, `${r.name} borrada.`)}`);
|
|
10196
10271
|
}
|
|
10272
|
+
async function dbsExport(id, db, v) {
|
|
10273
|
+
const format = v.format ?? (v.table ? "csv" : "sqlite");
|
|
10274
|
+
if (format !== "sqlite" && format !== "csv") throw usage(t("--format takes sqlite or csv.", "--format acepta sqlite o csv."), `ghosty dbs export ${id} ${db} --format csv --table <table>`);
|
|
10275
|
+
if (format === "csv" && !v.table) throw usage(t("A CSV is one table: add --table.", "Un CSV es de una tabla: agrega --table."), `ghosty dbs export ${id} ${db} --table <table> (ghosty dbs tables ${id} ${db})`);
|
|
10276
|
+
const q = new URLSearchParams({ format, ...v.table ? { table: v.table } : {} });
|
|
10277
|
+
const res = await request(agentPath2(id, `/dbs/${enc6(db)}/export?${q}`));
|
|
10278
|
+
const suggested = /filename="([^"]+)"/.exec(res.headers.get("content-disposition") ?? "")?.[1] ?? (format === "csv" ? `${db}-${v.table}.csv` : `${db}.sqlite`);
|
|
10279
|
+
const file = v.out ?? suggested;
|
|
10280
|
+
const bytes = Buffer.from(await res.arrayBuffer());
|
|
10281
|
+
await writeFile6(file, bytes);
|
|
10282
|
+
const countsHeader = res.headers.get("x-gs-row-counts");
|
|
10283
|
+
const counts = countsHeader ? JSON.parse(Buffer.from(countsHeader, "base64").toString("utf8")) : {};
|
|
10284
|
+
const rows = Object.entries(counts).map(([tb, n]) => ({ table: tb, rows: n }));
|
|
10285
|
+
return out(
|
|
10286
|
+
{ saved: file, bytes: bytes.length, format, ...v.table ? { table: v.table } : { tables: counts } },
|
|
10287
|
+
() => `${lavender("\u2713")} ${file} ${dim(`(${bytes.length} bytes)`)}` + (rows.length ? `
|
|
10288
|
+
${table(rows, ["table", "rows"])}
|
|
10289
|
+
${dim(t("Open it with sqlite3, DB Browser for SQLite or DBeaver.", "\xC1brela con sqlite3, DB Browser for SQLite o DBeaver."))}` : "")
|
|
10290
|
+
);
|
|
10291
|
+
}
|
|
10197
10292
|
async function dbsCmd(sub, id, db, rest, v) {
|
|
10198
10293
|
const ex = "ghosty dbs ls <agent-id>";
|
|
10199
10294
|
id = need2(id, t("agent id", "el id del agente"), ex);
|
|
@@ -10268,6 +10363,11 @@ ${dim(`${t(`${rows.length} row(s)`, `${rows.length} fila(s)`)} \xB7 ${Math.round
|
|
|
10268
10363
|
db = need2(db, t("database (id or name)", "la base (id o nombre)"), ex2);
|
|
10269
10364
|
return dbsRename(id, db, need2(rest[0], t("new name", "el nombre nuevo"), ex2), !!v["dry-run"]);
|
|
10270
10365
|
}
|
|
10366
|
+
case "export":
|
|
10367
|
+
case "download": {
|
|
10368
|
+
db = need2(db, t("database (id or name)", "la base (id o nombre)"), "ghosty dbs export <agent-id> <db> [--table T] [--out FILE]");
|
|
10369
|
+
return dbsExport(id, db, v);
|
|
10370
|
+
}
|
|
10271
10371
|
case "rm":
|
|
10272
10372
|
case "delete": {
|
|
10273
10373
|
db = need2(db, t("database (id or name)", "la base (id o nombre)"), "ghosty dbs rm <agent-id> <db> [--dry-run] [--yes]");
|
|
@@ -10543,7 +10643,7 @@ async function dbsPhotosCmd(verb, id, db, v) {
|
|
|
10543
10643
|
const results = [];
|
|
10544
10644
|
for (let i = 0; i < files.length; i += 8) {
|
|
10545
10645
|
const batch = files.slice(i, i + 8);
|
|
10546
|
-
const items = await Promise.all(batch.map(async (f) => ({ key: f.key, filename: f.file, contentBase64: (await readFile6(
|
|
10646
|
+
const items = await Promise.all(batch.map(async (f) => ({ key: f.key, filename: f.file, contentBase64: (await readFile6(join5(dir, f.file))).toString("base64") })));
|
|
10547
10647
|
const r = await api(path, { body: { table: tableName, keyColumn, column, items, ...v["dry-run"] ? { dryRun: true } : {} } });
|
|
10548
10648
|
results.push(...r.items);
|
|
10549
10649
|
if (!globals.json && files.length > 8) info(dim(` ${Math.min(i + 8, files.length)}/${files.length}`));
|
|
@@ -10775,7 +10875,7 @@ import { readFile as readFile8 } from "node:fs/promises";
|
|
|
10775
10875
|
|
|
10776
10876
|
// src/wa-auth.ts
|
|
10777
10877
|
import { readdir as readdir3, readFile as readFile7 } from "node:fs/promises";
|
|
10778
|
-
import { join as
|
|
10878
|
+
import { join as join6 } from "node:path";
|
|
10779
10879
|
var TYPES = ["app-state-sync-version", "app-state-sync-key", "sender-key-memory", "sender-key", "lid-mapping", "device-list", "identity-key", "pre-key", "session", "tctoken"];
|
|
10780
10880
|
function keyOfFileName(name) {
|
|
10781
10881
|
for (const type of TYPES) {
|
|
@@ -10789,7 +10889,7 @@ function keyOfFileName(name) {
|
|
|
10789
10889
|
async function readMultiFileAuth(dir) {
|
|
10790
10890
|
const files = (await readdir3(dir)).filter((f) => f.endsWith(".json"));
|
|
10791
10891
|
if (!files.includes("creds.json")) throw new Error(`${dir} has no creds.json: not a Baileys multi-file auth folder`);
|
|
10792
|
-
const authCreds = JSON.parse(await readFile7(
|
|
10892
|
+
const authCreds = JSON.parse(await readFile7(join6(dir, "creds.json"), "utf8"));
|
|
10793
10893
|
const authKeys = {};
|
|
10794
10894
|
const counts = {};
|
|
10795
10895
|
const skipped = [];
|
|
@@ -10800,7 +10900,7 @@ async function readMultiFileAuth(dir) {
|
|
|
10800
10900
|
skipped.push(f);
|
|
10801
10901
|
continue;
|
|
10802
10902
|
}
|
|
10803
|
-
(authKeys[k.type] ??= {})[k.id] = JSON.parse(await readFile7(
|
|
10903
|
+
(authKeys[k.type] ??= {})[k.id] = JSON.parse(await readFile7(join6(dir, f), "utf8"));
|
|
10804
10904
|
counts[k.type] = (counts[k.type] ?? 0) + 1;
|
|
10805
10905
|
}
|
|
10806
10906
|
return { authCreds, authKeys, counts, skipped };
|
|
@@ -11269,7 +11369,7 @@ var HELPS_ES = {
|
|
|
11269
11369
|
["--voice-replies M", "set: auto = contesta con nota de voz cuando el cliente manda o pide audio; off = siempre texto"],
|
|
11270
11370
|
["--auto-stage on|off", "set: mueve solo las tarjetas del tablero tras cada turno del cliente (una llamada extra al modelo por turno)"],
|
|
11271
11371
|
["--quote k=v,\u2026", "set: config de la tool de casa `cotizar` \u2014 iva (0-100), iva-incluido=true (los precios del cat\xE1logo ya traen IVA), iva.CATEGORIA=16 (IVA de esa categor\xEDa del cat\xE1logo; iva.CATEGORIA= la quita), solo-factura=true (IVA s\xF3lo si el cliente pide factura), vigencia (d\xEDas), prefix (folio), moneda, empresa, rfc, telefono, correo, direccion, notas, color. Vac\xEDo lo borra (rfc=). El contador de folio no se toca"],
|
|
11272
|
-
["--catalog
|
|
11372
|
+
["--catalog FILE", "set: el cat\xE1logo de la tool de casa `cotizar` desde un CSV (sku,nombre,precio[,unidad,categoria,descripcion]) o una lista JSON; reemplaza al anterior. Su `categoria` decide las reglas de IVA"],
|
|
11273
11373
|
["--verbose", "doctor: lista tambi\xE9n las revisiones que pasaron"],
|
|
11274
11374
|
["--user EMAIL", "doctor (staff de Ghosty): los agentes de esa persona \u2014 doctor, \xFAltimos turnos (estado, segundos, error y \xABfetched\xBB: si su app pidi\xF3 el hilo despu\xE9s de que termin\xF3 el turno), plan/bolsa y cajas (marca duplicadas); queda en bit\xE1cora"],
|
|
11275
11375
|
["--turns N", "doctor --user: cu\xE1ntos turnos recientes por agente (default 8, m\xE1x. 50)"],
|
|
@@ -11280,6 +11380,7 @@ var HELPS_ES = {
|
|
|
11280
11380
|
["--note TEXT", "docs replace/restore: por qu\xE9 cambi\xF3 (sale en docs history)"],
|
|
11281
11381
|
["--role R", "share: editor (lo configura y lo usa) o admin (adem\xE1s lo comparte); repetirlo sobre un correo ya compartido cambia el rol"],
|
|
11282
11382
|
["--handle NAME", "teams on: su @handle en ese equipo (default: sacado de su nombre, con sufijo si est\xE1 ocupado; @ghosty est\xE1 reservado). Sobre un equipo donde ya est\xE1, le cambia el handle"],
|
|
11383
|
+
["--factory ROLES", "teams on: le pasa esos roles de la Software Factory del equipo (plan, build, check, eval; separados por comas) \u2014 lo mismo que \xABEquipo\xBB en /factory. Cada rol conserva su nombre y avatar; su siguiente turno ya corre en \xE9l. La f\xE1brica tiene que estar instalada ah\xED"],
|
|
11283
11384
|
["--yes", "rm: se salta la confirmaci\xF3n (obligatorio sin terminal)"],
|
|
11284
11385
|
["--slug NAME", "demo: nombre de la liga; se le agrega un c\xF3digo de 6 caracteres (/p/NAME-CODE)"],
|
|
11285
11386
|
["--expires DATE|no", 'demo: caducidad fija en la hora de tu m\xE1quina ("2026-10-15T20:00"); no = nunca'],
|
|
@@ -11331,13 +11432,18 @@ var HELPS_ES = {
|
|
|
11331
11432
|
},
|
|
11332
11433
|
dbs: {
|
|
11333
11434
|
summary: "Bases de datos que un agente puede usar, y qu\xE9 puede escribir desde canales de clientes.",
|
|
11334
|
-
replace: [
|
|
11435
|
+
replace: [
|
|
11436
|
+
["# reads $EASYBITS_API_KEY", "# lee $EASYBITS_API_KEY"],
|
|
11437
|
+
["# totequim-productos.csv (Excel)", "# totequim-productos.csv (Excel)"]
|
|
11438
|
+
],
|
|
11335
11439
|
flags: [
|
|
11336
11440
|
["--orphans", "ls: s\xF3lo las bases que ning\xFAn agente tiene concedidas (candidatas a rm)"],
|
|
11337
11441
|
["--arg V", "query: valor para un marcador ? (repetible)"],
|
|
11338
11442
|
["--write", "query: permite INSERT/UPDATE/DELETE/DDL (default: s\xF3lo lectura)"],
|
|
11339
11443
|
["--external-write t1,t2", 'grant: tablas que puede escribir desde WhatsApp/Messenger ("" = ninguna)'],
|
|
11340
11444
|
["--external-rows t:col+col", 'grant: desde WhatsApp/Messenger cada cliente ve s\xF3lo SUS filas de t, por columna(s) de tel\xE9fono ("" = sin filtro)'],
|
|
11445
|
+
["--table T", "export: baja s\xF3lo esa tabla, en CSV (sin ella: la base entera como .sqlite)"],
|
|
11446
|
+
["--out FILE", "export: d\xF3nde guardarla (default: el nombre de la base, p. ej. totequim.sqlite o totequim-productos.csv)"],
|
|
11341
11447
|
["--from easybits:ID", "import: base de origen en EasyBits (llave de $EASYBITS_API_KEY)"],
|
|
11342
11448
|
["--tables a,b", "import: s\xF3lo estas tablas (default: todas)"],
|
|
11343
11449
|
["--append", "import: agrega filas en vez de reemplazarlas (reemplazar es el default idempotente)"],
|
|
@@ -11349,6 +11455,7 @@ var HELPS_ES = {
|
|
|
11349
11455
|
],
|
|
11350
11456
|
notes: [
|
|
11351
11457
|
"<db> es el id de la base o su nombre. Desde canales del due\xF1o el agente lee y escribe todo lo concedido.",
|
|
11458
|
+
"export usa tu sesi\xF3n (no el token gat_ del agente) y deja registro en la bit\xE1cora; las mismas descargas est\xE1n en la p\xE1gina de la base, en /app/datos?tab=dbs.",
|
|
11352
11459
|
"tables dice, por columna de fotos/ligas, cu\xE1ntas son permanentes (img.ghosty.studio), de fuera o vac\xEDas; import avisa antes de reemplazar ligas permanentes por ligas de fuera.",
|
|
11353
11460
|
"import con replace avisa de las filas escritas en gs que EasyBits no tiene (una base compartida con agentes que ya corren en gs) y pregunta antes de borrarlas.",
|
|
11354
11461
|
"rename tambi\xE9n reescribe cada prompt, valor de env y SKILL.md que nombra la base; un prompt derivado sigue a su base. rm borra la base misma (revoke s\xF3lo le quita el acceso a este agente); las dos son s\xF3lo del due\xF1o (o del staff de Ghosty)."
|
|
@@ -11420,7 +11527,16 @@ var HELPS_ES = {
|
|
|
11420
11527
|
},
|
|
11421
11528
|
me: {
|
|
11422
11529
|
summary: "Tu cuenta: archivos, memorias y uso.",
|
|
11423
|
-
|
|
11530
|
+
replace: [["# documents/, conversations/, library/", "# documentos/, conversaciones/, biblioteca/"]],
|
|
11531
|
+
flags: [
|
|
11532
|
+
["--out DIR", "export: carpeta destino (default ghosty-archivos); get: archivo donde guardarlo"],
|
|
11533
|
+
["--kind K", "ls, export: s\xF3lo image | video | audio | document | other"],
|
|
11534
|
+
["-n, --dry-run", "export: cu\xE1ntos archivos y MB por carpeta, sin bajar nada"]
|
|
11535
|
+
],
|
|
11536
|
+
notes: [
|
|
11537
|
+
"Las memorias viajan a tus agentes s\xF3lo en tus chats personales (app, /c, notch), nunca a WhatsApp, Messenger ni demos.",
|
|
11538
|
+
"export baja TODO lo de tu cuenta: documentos, archivos de tus chats y de los canales de tus agentes (lo que mandaron los clientes). Lo ya bajado con el mismo tama\xF1o se salta, as\xED que repetirlo s\xF3lo trae lo nuevo."
|
|
11539
|
+
]
|
|
11424
11540
|
},
|
|
11425
11541
|
credentials: {
|
|
11426
11542
|
summary: "Llaves de proveedores en la b\xF3veda de tu cuenta (cifradas; s\xF3lo regresan los \xFAltimos 4 caracteres).",
|
|
@@ -11684,6 +11800,7 @@ var HELPS = {
|
|
|
11684
11800
|
"ghosty agents shares <agent-id>",
|
|
11685
11801
|
"ghosty agents teams ls <agent-id> # Teams workspaces you administer: where it answers, is paused, and with which @handle",
|
|
11686
11802
|
"ghosty agents teams on <agent-id> <workspace> [--handle NAME] # turn it on there (or change its @handle)",
|
|
11803
|
+
"ghosty agents teams on <agent-id> <workspace> --factory plan,build,check # make it the workspace's Software Factory roles",
|
|
11687
11804
|
"ghosty agents teams off <agent-id> <workspace> # pause it there; it keeps its @handle",
|
|
11688
11805
|
"ghosty agents teams rm <agent-id> <workspace> [--yes] # remove it from there; the agent stays",
|
|
11689
11806
|
"ghosty agents restart <agent-id>",
|
|
@@ -11720,6 +11837,7 @@ var HELPS = {
|
|
|
11720
11837
|
["--note TEXT", "docs replace/restore: why it changed (shown in docs history)"],
|
|
11721
11838
|
["--role R", "share: editor (configures and uses it) or admin (also shares it); again on a shared email changes the role"],
|
|
11722
11839
|
["--handle NAME", "teams on: its @handle in that workspace (default: from its name, with a suffix if taken; @ghosty is reserved). On an active workspace it changes the handle"],
|
|
11840
|
+
["--factory ROLES", "teams on: give it those Software Factory roles in that workspace (plan, build, check, eval; comma-separated) \u2014 the same as \xABTeam\xBB in /factory. Keeps each role's name and avatar; the next turn of each role runs on it. The factory must be installed there"],
|
|
11723
11841
|
["--yes", "rm: skip the confirmation (required without a terminal)"],
|
|
11724
11842
|
["--slug NAME", "demo: link name; a 6-char code is appended (/p/NAME-CODE)"],
|
|
11725
11843
|
["--expires DATE|no", `demo: fixed expiry in your machine's time ("2026-10-15T20:00"); no = never`],
|
|
@@ -11757,6 +11875,7 @@ var HELPS = {
|
|
|
11757
11875
|
"ghosty agents set <id> --toolsets +crm",
|
|
11758
11876
|
"ghosty agents share <id> ana@empresa.com --role admin",
|
|
11759
11877
|
"ghosty agents teams on <id> abogados --handle licenciado",
|
|
11878
|
+
"ghosty agents teams on <id> mercadito-verde --factory plan,build,check",
|
|
11760
11879
|
"ghosty agents get <id> --prompt-out PROMPT.md",
|
|
11761
11880
|
"ghosty agents get <id> --fields toolsets,connectors # what it really has: effective toolsets, connectors connected + granted",
|
|
11762
11881
|
"ghosty agents create --name tania-mkt --like <pia-id> --board new --no-notify --dry-run",
|
|
@@ -11810,6 +11929,7 @@ var HELPS = {
|
|
|
11810
11929
|
"ghosty dbs ls <agent-id> [--orphans]",
|
|
11811
11930
|
"ghosty dbs tables <agent-id> <db>",
|
|
11812
11931
|
'ghosty dbs query <agent-id> <db> "SQL" [--arg V]\u2026 [--write]',
|
|
11932
|
+
"ghosty dbs export <agent-id> <db> [--table T] [--out FILE]",
|
|
11813
11933
|
"ghosty dbs import <agent-id> <db> --from easybits:<db-id> [--tables a,b] [--append] [--rehost-images t.col,\u2026] [--dry-run] [--yes]",
|
|
11814
11934
|
"ghosty dbs rehost <agent-id> <db> --table T --column C [--dry-run]",
|
|
11815
11935
|
"ghosty dbs photos put <agent-id> <db> --table T --key-column K --dir DIR [--column C] [--dry-run]",
|
|
@@ -11824,6 +11944,8 @@ var HELPS = {
|
|
|
11824
11944
|
["--write", "query: allow INSERT/UPDATE/DELETE/DDL (default: read only)"],
|
|
11825
11945
|
["--external-write t1,t2", 'grant: tables it may write from WhatsApp/Messenger ("" = none)'],
|
|
11826
11946
|
["--external-rows t:col+col", 'grant: from WhatsApp/Messenger each customer sees only THEIR rows of t, matched by phone column(s) ("" = no filter)'],
|
|
11947
|
+
["--table T", "export: download only that table, as CSV (without it: the whole database as .sqlite)"],
|
|
11948
|
+
["--out FILE", "export: where to save it (default: the database name, e.g. totequim.sqlite or totequim-productos.csv)"],
|
|
11827
11949
|
["--from easybits:ID", "import: source database in EasyBits (key from $EASYBITS_API_KEY)"],
|
|
11828
11950
|
["--tables a,b", "import: only these tables (default: all)"],
|
|
11829
11951
|
["--append", "import: add rows instead of replacing them (replace is the idempotent default)"],
|
|
@@ -11837,6 +11959,8 @@ var HELPS = {
|
|
|
11837
11959
|
"ghosty dbs ls <id>",
|
|
11838
11960
|
"ghosty dbs tables <id> totequim",
|
|
11839
11961
|
'ghosty dbs query <id> totequim "SELECT count(*) AS n FROM productos"',
|
|
11962
|
+
"ghosty dbs export <id> totequim # totequim.sqlite (sqlite3, DB Browser, DBeaver)",
|
|
11963
|
+
"ghosty dbs export <id> totequim --table productos # totequim-productos.csv (Excel)",
|
|
11840
11964
|
"ghosty dbs grant <id> totequim --external-write cotizaciones,folio",
|
|
11841
11965
|
"ghosty dbs grant <id> totequim --external-rows clientes:telefono+tel_secundario,cotizaciones:cliente_tel",
|
|
11842
11966
|
"ghosty dbs import <id> totequim --from easybits:6a10c84c\u2026 --dry-run # reads $EASYBITS_API_KEY",
|
|
@@ -11846,6 +11970,7 @@ var HELPS = {
|
|
|
11846
11970
|
],
|
|
11847
11971
|
notes: [
|
|
11848
11972
|
"<db> is the database id or its name. From owner channels the agent reads and writes everything granted.",
|
|
11973
|
+
"export uses your session (not the agent's gat_ token) and leaves an audit entry; the same downloads are on the database page at /app/datos?tab=dbs.",
|
|
11849
11974
|
"tables shows, per photo/link column, how many links are permanent (img.ghosty.studio), external or empty; import warns before replacing permanent links with external ones.",
|
|
11850
11975
|
"import with replace warns about rows written in gs that EasyBits doesn't have (a database shared with agents already running in gs) and asks before deleting them.",
|
|
11851
11976
|
"rename also rewrites every prompt, env value and SKILL.md that names the database; a derived prompt follows its base. rm deletes the database itself (revoke only removes this agent's access); only its owner (or Ghosty staff) can do either."
|
|
@@ -11956,9 +12081,17 @@ var HELPS = {
|
|
|
11956
12081
|
},
|
|
11957
12082
|
me: {
|
|
11958
12083
|
summary: "Your account: files, memories and usage.",
|
|
11959
|
-
usage: ["ghosty me files ls [--q TEXT] [--kind image|video|audio|document|other] [--cursor C]", "ghosty me files get <file-id> [--out FILE]", "ghosty me files upload <file> | rm <file-id>", 'ghosty me memories ls | add "text" | edit <id> "text" | rm <id>', "ghosty usage"],
|
|
11960
|
-
examples: ["ghosty me files ls --kind document", "ghosty me files ls --q contrato", "ghosty me files get <id> --out contrato.pdf", 'ghosty chat <agent> "res\xFAmelo" --file <id>', 'ghosty me memories add "I prefer short answers"'],
|
|
11961
|
-
|
|
12084
|
+
usage: ["ghosty me files ls [--q TEXT] [--kind image|video|audio|document|other] [--cursor C]", "ghosty me files get <file-id> [--out FILE]", "ghosty me files export [--out DIR] [--kind K] [--dry-run]", "ghosty me files upload <file> | rm <file-id>", 'ghosty me memories ls | add "text" | edit <id> "text" | rm <id>', "ghosty usage"],
|
|
12085
|
+
examples: ["ghosty me files ls --kind document", "ghosty me files ls --q contrato", "ghosty me files get <id> --out contrato.pdf", "ghosty me files export --dry-run", "ghosty me files export --out respaldo/ # documents/, conversations/, library/", 'ghosty chat <agent> "res\xFAmelo" --file <id>', 'ghosty me memories add "I prefer short answers"'],
|
|
12086
|
+
flags: [
|
|
12087
|
+
["--out DIR", "export: destination folder (default ghosty-archivos); get: file to save"],
|
|
12088
|
+
["--kind K", "ls, export: only image | video | audio | document | other"],
|
|
12089
|
+
["-n, --dry-run", "export: how many files and MB per folder, without downloading"]
|
|
12090
|
+
],
|
|
12091
|
+
notes: [
|
|
12092
|
+
"Memories travel to your agents only in your personal chats (app, /c, notch), never to WhatsApp, Messenger or demo threads.",
|
|
12093
|
+
"export brings EVERYTHING in your account: documents, files from your chats and from your agents' channels (what customers sent). A file already downloaded with the same size is skipped, so rerunning it only brings what's new."
|
|
12094
|
+
],
|
|
11962
12095
|
learn: `${REF}#tu-cuenta`
|
|
11963
12096
|
}
|
|
11964
12097
|
};
|
|
@@ -12328,10 +12461,10 @@ function helpText(cmd) {
|
|
|
12328
12461
|
}
|
|
12329
12462
|
|
|
12330
12463
|
// src/update.ts
|
|
12331
|
-
import { mkdir as
|
|
12464
|
+
import { mkdir as mkdir5, readFile as readFile9, writeFile as writeFile7 } from "node:fs/promises";
|
|
12332
12465
|
import { homedir as homedir3 } from "node:os";
|
|
12333
|
-
import { dirname as dirname3, join as
|
|
12334
|
-
var FILE =
|
|
12466
|
+
import { dirname as dirname3, join as join7 } from "node:path";
|
|
12467
|
+
var FILE = join7(process.env.XDG_CONFIG_HOME || join7(homedir3(), ".config"), "ghosty", "update.json");
|
|
12335
12468
|
var TTL = 60 * 60 * 1e3;
|
|
12336
12469
|
var UPGRADE = "npm i -g @ghostystudio/cli@latest";
|
|
12337
12470
|
function newer(a, b) {
|
|
@@ -12353,8 +12486,8 @@ async function latestVersion(current2) {
|
|
|
12353
12486
|
const r = await fetch("https://registry.npmjs.org/@ghostystudio/cli/latest", { signal: AbortSignal.timeout(1500) });
|
|
12354
12487
|
const latest = (await r.json()).version;
|
|
12355
12488
|
if (latest) cache2 = { latest, checkedAt: Date.now() };
|
|
12356
|
-
await
|
|
12357
|
-
await
|
|
12489
|
+
await mkdir5(dirname3(FILE), { recursive: true });
|
|
12490
|
+
await writeFile7(FILE, JSON.stringify(cache2));
|
|
12358
12491
|
} catch {
|
|
12359
12492
|
}
|
|
12360
12493
|
}
|
|
@@ -12429,9 +12562,9 @@ function agentArgIndex(pos) {
|
|
|
12429
12562
|
}
|
|
12430
12563
|
|
|
12431
12564
|
// src/completion.ts
|
|
12432
|
-
import { mkdir as
|
|
12565
|
+
import { mkdir as mkdir6, readFile as readFile10, writeFile as writeFile8 } from "node:fs/promises";
|
|
12433
12566
|
import { homedir as homedir4 } from "node:os";
|
|
12434
|
-
import { join as
|
|
12567
|
+
import { join as join8 } from "node:path";
|
|
12435
12568
|
var GLOBAL_FLAGS = ["--json", "--token", "--profile", "--yes", "--help", "--version", "--lang", "--dry-run"];
|
|
12436
12569
|
var AGENT_FLAGS = /* @__PURE__ */ new Set(["--like", "--agent", "--prompt-base"]);
|
|
12437
12570
|
var WORKSPACE_FLAGS = /* @__PURE__ */ new Set(["--workspace"]);
|
|
@@ -12469,7 +12602,7 @@ function completionFor(words, current2, commands) {
|
|
|
12469
12602
|
if (agentArgIndex([...pos, current2]) === pos.length) return { kind: "agents" };
|
|
12470
12603
|
return { kind: "words", words: [] };
|
|
12471
12604
|
}
|
|
12472
|
-
var cacheFile = () =>
|
|
12605
|
+
var cacheFile = () => join8(process.env.XDG_CACHE_HOME || join8(homedir4(), ".cache"), "ghosty", `complete${process.env.GHOSTY_PROFILE ? `.${process.env.GHOSTY_PROFILE}` : ""}.json`);
|
|
12473
12606
|
async function names() {
|
|
12474
12607
|
try {
|
|
12475
12608
|
const c = JSON.parse(await readFile10(cacheFile(), "utf8"));
|
|
@@ -12479,9 +12612,9 @@ async function names() {
|
|
|
12479
12612
|
const list = await listAgents();
|
|
12480
12613
|
const agents = [...new Set(list.map((a) => a.nombre))];
|
|
12481
12614
|
const workspaces = [...new Set(list.map((a) => a.espacio?.tipo === "workspace" ? a.espacio.nombre : void 0).filter((x) => !!x))];
|
|
12482
|
-
await
|
|
12615
|
+
await mkdir6(join8(cacheFile(), ".."), { recursive: true }).catch(() => {
|
|
12483
12616
|
});
|
|
12484
|
-
await
|
|
12617
|
+
await writeFile8(cacheFile(), JSON.stringify({ at: Date.now(), agents, workspaces })).catch(() => {
|
|
12485
12618
|
});
|
|
12486
12619
|
return { agents, workspaces };
|
|
12487
12620
|
}
|
|
@@ -12559,8 +12692,8 @@ function normalizeArgs(pos, v) {
|
|
|
12559
12692
|
|
|
12560
12693
|
// src/commands/spec.ts
|
|
12561
12694
|
var import_yaml = __toESM(require_dist(), 1);
|
|
12562
|
-
import { mkdir as
|
|
12563
|
-
import { join as
|
|
12695
|
+
import { mkdir as mkdir7, readdir as readdir4, readFile as readFile11, stat as stat2, writeFile as writeFile9 } from "node:fs/promises";
|
|
12696
|
+
import { join as join9, resolve as resolve2 } from "node:path";
|
|
12564
12697
|
var enc9 = encodeURIComponent;
|
|
12565
12698
|
async function agentsExport(id, v) {
|
|
12566
12699
|
if (!id) throw usage(t("Missing agent.", "Falta el agente."), t("Example", "Ejemplo") + ": ghosty agents export pia-0 > pia.yaml \xB7 ghosty agents export pia-0 --out ./pia");
|
|
@@ -12570,12 +12703,12 @@ async function agentsExport(id, v) {
|
|
|
12570
12703
|
return void process.stdout.write(yaml);
|
|
12571
12704
|
}
|
|
12572
12705
|
const dir = resolve2(v.out);
|
|
12573
|
-
await
|
|
12574
|
-
await
|
|
12706
|
+
await mkdir7(dir, { recursive: true });
|
|
12707
|
+
await writeFile9(join9(dir, "agent.yaml"), yaml);
|
|
12575
12708
|
const own = (0, import_yaml.parse)(yaml)["x-ghosty"]?.skills?.own ?? [];
|
|
12576
12709
|
for (const slug of own) {
|
|
12577
12710
|
const sk = await api(`/api/v2/agents/${enc9(id)}/skills/${enc9(slug)}`);
|
|
12578
|
-
await writeSkill(
|
|
12711
|
+
await writeSkill(join9(dir, "skills", slug), sk);
|
|
12579
12712
|
}
|
|
12580
12713
|
out({ saved: dir, skills: own }, () => `${lavender("\u2713")} ${dir}/agent.yaml${own.length ? ` + skills/ (${own.join(", ")})` : ""}
|
|
12581
12714
|
${dim(t(`Edit it, then: ghosty apply ${v.out} --dry-run`, `Ed\xEDtalo y luego: ghosty apply ${v.out} --dry-run`))}`);
|
|
@@ -12596,12 +12729,12 @@ function fillSecrets(file) {
|
|
|
12596
12729
|
return missing;
|
|
12597
12730
|
}
|
|
12598
12731
|
async function readSkills(dir) {
|
|
12599
|
-
const root =
|
|
12732
|
+
const root = join9(dir, "skills");
|
|
12600
12733
|
const out2 = {};
|
|
12601
12734
|
const slugs = await readdir4(root, { withFileTypes: true }).catch(() => []);
|
|
12602
12735
|
for (const e of slugs) {
|
|
12603
12736
|
if (!e.isDirectory() || e.name.startsWith(".")) continue;
|
|
12604
|
-
const { markdown, assets } = await readSkillDir(
|
|
12737
|
+
const { markdown, assets } = await readSkillDir(join9(root, e.name));
|
|
12605
12738
|
out2[e.name] = [{ path: "SKILL.md", contentBase64: Buffer.from(markdown).toString("base64") }, ...assets.map((a) => ({ path: a.name, contentBase64: a.contentBase64 }))];
|
|
12606
12739
|
}
|
|
12607
12740
|
return out2;
|
|
@@ -12609,8 +12742,8 @@ async function readSkills(dir) {
|
|
|
12609
12742
|
var line = (c) => c.op === "!" ? dim(`! ${c.what}`) : `${c.op === "+" ? lavender("+") : c.op === "-" ? red("-") : "~"} ${c.what}`;
|
|
12610
12743
|
async function applyCmd(target, v) {
|
|
12611
12744
|
if (!target) throw usage(t("Missing file or directory.", "Falta el archivo o el directorio."), t("Example: ghosty apply pia.yaml --dry-run \xB7 ghosty apply ./pia (from agents export --out)", "Ejemplo: ghosty apply pia.yaml --dry-run \xB7 ghosty apply ./pia (de agents export --out)"));
|
|
12612
|
-
const isDir = (await
|
|
12613
|
-
const text = await readFile11(isDir ?
|
|
12745
|
+
const isDir = (await stat2(target).catch(() => null))?.isDirectory() ?? false;
|
|
12746
|
+
const text = await readFile11(isDir ? join9(target, "agent.yaml") : target, "utf8");
|
|
12614
12747
|
const file = (0, import_yaml.parse)(text);
|
|
12615
12748
|
if (!file || typeof file !== "object") throw usage(t(`${target} is not a YAML/JSON agent file.`, `${target} no es un archivo de agente YAML/JSON.`));
|
|
12616
12749
|
const missing = fillSecrets(file);
|
|
@@ -12930,9 +13063,9 @@ ${dim(t(`${fmtTokens(total)} billable tokens \xB7 failed = error or stopped \xB7
|
|
|
12930
13063
|
|
|
12931
13064
|
// src/commands/demos.ts
|
|
12932
13065
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
12933
|
-
import { mkdir as
|
|
13066
|
+
import { mkdir as mkdir8, readFile as readFile12, writeFile as writeFile10 } from "node:fs/promises";
|
|
12934
13067
|
import { homedir as homedir5 } from "node:os";
|
|
12935
|
-
import { dirname as dirname4, join as
|
|
13068
|
+
import { dirname as dirname4, join as join10 } from "node:path";
|
|
12936
13069
|
var enc11 = encodeURIComponent;
|
|
12937
13070
|
var hora = (iso) => new Date(iso).toLocaleString("es-MX", { timeZone: "America/Mexico_City", hour12: false, dateStyle: "short", timeStyle: "short" });
|
|
12938
13071
|
async function history(agent, v) {
|
|
@@ -12952,7 +13085,7 @@ async function history(agent, v) {
|
|
|
12952
13085
|
return lines.join("\n");
|
|
12953
13086
|
});
|
|
12954
13087
|
}
|
|
12955
|
-
var jarPath = () =>
|
|
13088
|
+
var jarPath = () => join10(process.env.XDG_CONFIG_HOME || join10(homedir5(), ".config"), "ghosty", "demo-cookies.json");
|
|
12956
13089
|
async function readJar() {
|
|
12957
13090
|
try {
|
|
12958
13091
|
return JSON.parse(await readFile12(jarPath(), "utf8"));
|
|
@@ -12972,8 +13105,8 @@ async function visitorCookie(base, slug, fresh) {
|
|
|
12972
13105
|
const cookie = anonCookieOf(res.headers.get("set-cookie"));
|
|
12973
13106
|
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);
|
|
12974
13107
|
jar[slug] = cookie;
|
|
12975
|
-
await
|
|
12976
|
-
await
|
|
13108
|
+
await mkdir8(dirname4(jarPath()), { recursive: true });
|
|
13109
|
+
await writeFile10(jarPath(), JSON.stringify(jar, null, 2), { mode: 384 });
|
|
12977
13110
|
return cookie;
|
|
12978
13111
|
}
|
|
12979
13112
|
async function visitorTurn(base, slug, cookie, text) {
|
|
@@ -13045,12 +13178,12 @@ async function demosCmd(sub, a1, rest, v) {
|
|
|
13045
13178
|
}
|
|
13046
13179
|
|
|
13047
13180
|
// src/commands/sites.ts
|
|
13048
|
-
import { readdir as readdir5, readFile as readFile13, stat as
|
|
13049
|
-
import { basename as basename4, join as
|
|
13181
|
+
import { readdir as readdir5, readFile as readFile13, stat as stat3 } from "node:fs/promises";
|
|
13182
|
+
import { basename as basename4, join as join11, relative as relative2, sep } from "node:path";
|
|
13050
13183
|
var TEXT_EXT = /* @__PURE__ */ new Set(["html", "htm", "css", "js", "mjs", "json", "txt", "xml", "svg", "webmanifest", "md"]);
|
|
13051
13184
|
var SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", ".DS_Store"]);
|
|
13052
13185
|
async function collectSiteFiles(target) {
|
|
13053
|
-
const st = await
|
|
13186
|
+
const st = await stat3(target).catch(() => null);
|
|
13054
13187
|
if (!st) throw usage(t(`Not found: ${target}`, `No existe: ${target}`));
|
|
13055
13188
|
if (st.isFile()) {
|
|
13056
13189
|
if (!/\.html?$/i.test(target)) throw usage(t("A single file must be .html (or pass a folder).", "Un archivo suelto debe ser .html (o pasa una carpeta)."));
|
|
@@ -13061,7 +13194,7 @@ async function collectSiteFiles(target) {
|
|
|
13061
13194
|
const walk = async (dir) => {
|
|
13062
13195
|
for (const e of await readdir5(dir, { withFileTypes: true })) {
|
|
13063
13196
|
if (e.name.startsWith(".") || SKIP.has(e.name)) continue;
|
|
13064
|
-
const full =
|
|
13197
|
+
const full = join11(dir, e.name);
|
|
13065
13198
|
if (e.isDirectory()) await walk(full);
|
|
13066
13199
|
else if (e.isFile()) {
|
|
13067
13200
|
const path = relative2(target, full).split(sep).join("/");
|
|
@@ -13429,7 +13562,7 @@ ${bold("Agents")}
|
|
|
13429
13562
|
agents docs replace <id> <doc> New version (keeps name and access) --url URL | --file PATH [--note]
|
|
13430
13563
|
agents docs history|restore <id> <doc> [n] Its versions; restore n as a new version
|
|
13431
13564
|
agents share|unshare <id> <email> Share it without handing it over [--role editor|admin] \xB7 agents shares <id> lists them
|
|
13432
|
-
agents teams ls|on|off|rm <id> [ws] Which Teams workspaces it answers in, and its @handle there [--handle x]
|
|
13565
|
+
agents teams ls|on|off|rm <id> [ws] Which Teams workspaces it answers in, and its @handle there [--handle x] [--factory plan,build,check: its Software Factory roles]
|
|
13433
13566
|
agents rm <id> Delete an agent (owner only; asks you to type its name, or --yes) [--from-workspace: turn it off in Teams first]
|
|
13434
13567
|
agents restart <id> Restart an ACP agent (needsRestart) so it loads new skills
|
|
13435
13568
|
agents box <id> The machine serving it: is it on the new image? [--check PATH]\u2026 [--contains PATH=TEXT]\u2026 [--recycle [--reenv] [--force]]
|
|
@@ -13443,7 +13576,8 @@ ${bold("Configure an agent")}
|
|
|
13443
13576
|
skills house <id> Ghosty's skills in bulk [--only a,b | --enable a,b | --disable a,b] [--dry-run]
|
|
13444
13577
|
skills ls|get|add|rm <id> [slug] Skills (add from catalog, --file SKILL.md or --dir FOLDER; get lists, --out DIR saves; rm keeps a copy)
|
|
13445
13578
|
skills templates <id> pdf-doc templates it sees: its own and Ghosty's, and which folder wins
|
|
13446
|
-
dbs ls|tables|query|import|rehost|grant|revoke|rename|rm <id> [db] Databases it uses [--write] [--external-write t1,t2] [--external-rows t:col] [--from easybits:ID --dry-run] [--table T --column C]
|
|
13579
|
+
dbs ls|tables|query|export|import|rehost|grant|revoke|rename|rm <id> [db] Databases it uses [--write] [--external-write t1,t2] [--external-rows t:col] [--from easybits:ID --dry-run] [--table T --column C]
|
|
13580
|
+
dbs export <id> <db> Download a database: whole as .sqlite, or one table as CSV [--table T] [--out FILE]
|
|
13447
13581
|
dbs photos put <id> <db> --table T --key-column K --dir DIR Loose photos to a catalog (SKU-123.jpg \u2192 its row), permanent links [--column C] [--dry-run]
|
|
13448
13582
|
mcp get|set <id> MCP servers [--file servers.json] (http headers accept \${turn.token})
|
|
13449
13583
|
channels whatsapp ls|enable|disable <id> [number] Which WhatsApp Business numbers it answers [--dry-run]
|
|
@@ -13502,7 +13636,8 @@ ${bold("Partners")} ${dim("(give an agent to each of YOUR customers \u2014 see:
|
|
|
13502
13636
|
partner ls | enable | credentials | agent | tenants | audit | try
|
|
13503
13637
|
|
|
13504
13638
|
${bold("Your account")}
|
|
13505
|
-
me files ls|get|upload|rm Files in your account
|
|
13639
|
+
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]
|
|
13640
|
+
me files export Download ALL your files to a folder (documents, conversations, library); rerun = only new [--out DIR] [--kind K] [--dry-run]
|
|
13506
13641
|
me memories ls|add|edit|rm What your agents remember about you (personal chats only) add "text" \xB7 edit <id> "text" \xB7 rm <id>
|
|
13507
13642
|
credentials ls|set|rm [provider] Provider keys in your vault (hidden prompt, stdin or --from-env VAR; never as an argument)
|
|
13508
13643
|
connectors ls|connect|disconnect [id] Your connectors (Shopify, Stripe, monday\u2026): connect opens the browser or asks for the key
|
|
@@ -13575,7 +13710,7 @@ ${bold("Agentes")}
|
|
|
13575
13710
|
agents docs replace <id> <doc> Versi\xF3n nueva (conserva nombre y accesos) --url URL | --file RUTA [--note]
|
|
13576
13711
|
agents docs history|restore <id> <doc> [n] Sus versiones; restaura la n como versi\xF3n nueva
|
|
13577
13712
|
agents share|unshare <id> <correo> Comp\xE1rtelo sin cederlo [--role editor|admin] \xB7 agents shares <id> los lista
|
|
13578
|
-
agents teams ls|on|off|rm <id> [equipo] En qu\xE9 equipos de Teams contesta y con qu\xE9 @handle [--handle x]
|
|
13713
|
+
agents teams ls|on|off|rm <id> [equipo] En qu\xE9 equipos de Teams contesta y con qu\xE9 @handle [--handle x] [--factory plan,build,check: sus roles de la Software Factory]
|
|
13579
13714
|
agents rm <id> Borra un agente (s\xF3lo el due\xF1o; te pide escribir su nombre, o --yes) [--from-workspace: antes lo desactiva en Teams]
|
|
13580
13715
|
agents restart <id> Reinicia un agente ACP (needsRestart) para que cargue skills nuevas
|
|
13581
13716
|
agents box <id> La m\xE1quina que lo atiende: \xBFya trae la imagen nueva? [--check RUTA]\u2026 [--contains RUTA=TEXTO]\u2026 [--recycle [--reenv] [--force]]
|
|
@@ -13589,7 +13724,8 @@ ${bold("Configurar un agente")}
|
|
|
13589
13724
|
skills house <id> Las skills de Ghosty en bloque [--only a,b | --enable a,b | --disable a,b] [--dry-run]
|
|
13590
13725
|
skills ls|get|add|rm <id> [slug] Skills (add del cat\xE1logo, --file SKILL.md o --dir CARPETA; get lista, --out DIR guarda; rm deja copia)
|
|
13591
13726
|
skills templates <id> Plantillas de pdf-doc que ve: las suyas y las de Ghosty, y qu\xE9 carpeta gana
|
|
13592
|
-
dbs ls|tables|query|import|rehost|grant|revoke|rename|rm <id> [db] Bases de datos que usa [--write] [--external-write t1,t2] [--external-rows t:col] [--from easybits:ID --dry-run] [--table T --column C]
|
|
13727
|
+
dbs ls|tables|query|export|import|rehost|grant|revoke|rename|rm <id> [db] Bases de datos que usa [--write] [--external-write t1,t2] [--external-rows t:col] [--from easybits:ID --dry-run] [--table T --column C]
|
|
13728
|
+
dbs export <id> <db> Descarga una base: entera como .sqlite, o una tabla en CSV [--table T] [--out ARCHIVO]
|
|
13593
13729
|
dbs photos put <id> <db> --table T --key-column K --dir DIR Fotos sueltas a un cat\xE1logo (SKU-123.jpg \u2192 su fila), ligas permanentes [--column C] [--dry-run]
|
|
13594
13730
|
mcp get|set <id> Servidores MCP [--file servers.json] (headers http admiten \${turn.token})
|
|
13595
13731
|
channels whatsapp ls|enable|disable <id> [n\xFAmero] Qu\xE9 n\xFAmeros de WhatsApp Business contesta [--dry-run]
|
|
@@ -13648,7 +13784,8 @@ ${bold("Partners")} ${dim("(dale un agente a cada uno de TUS clientes \u2014 ve
|
|
|
13648
13784
|
partner ls | enable | credentials | agent | tenants | audit | try
|
|
13649
13785
|
|
|
13650
13786
|
${bold("Tu cuenta")}
|
|
13651
|
-
me files ls|get|upload|rm Archivos de tu cuenta
|
|
13787
|
+
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]
|
|
13788
|
+
me files export Baja TODOS tus archivos a una carpeta (documentos, conversaciones, biblioteca); repetirlo = s\xF3lo lo nuevo [--out CARPETA] [--kind K] [--dry-run]
|
|
13652
13789
|
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>
|
|
13653
13790
|
credentials ls|set|rm [proveedor] Llaves de proveedor en tu b\xF3veda (prompt oculto, stdin o --from-env VAR; nunca como argumento)
|
|
13654
13791
|
connectors ls|connect|disconnect [id] Tus conectores (Shopify, Stripe, monday\u2026): connect abre el navegador o pide la llave
|
|
@@ -13825,6 +13962,7 @@ async function main() {
|
|
|
13825
13962
|
recycle: { type: "boolean" },
|
|
13826
13963
|
role: { type: "string" },
|
|
13827
13964
|
handle: { type: "string" },
|
|
13965
|
+
factory: { type: "string" },
|
|
13828
13966
|
wake: { type: "string" },
|
|
13829
13967
|
text: { type: "string" },
|
|
13830
13968
|
sender: { type: "string" },
|
|
@@ -13898,7 +14036,7 @@ async function main() {
|
|
|
13898
14036
|
const tokens = splitTokens(pos, v);
|
|
13899
14037
|
globals.token = tokens.session;
|
|
13900
14038
|
globals.profile = v.profile;
|
|
13901
|
-
if (v.version) return out({ version: "0.
|
|
14039
|
+
if (v.version) return out({ version: "0.49.0" }, () => "0.49.0");
|
|
13902
14040
|
for (const n of normalizeArgs(pos, v)) if (!globals.json) info(dim(n));
|
|
13903
14041
|
const context = (await readConfig()).workspace;
|
|
13904
14042
|
if (!explicitToken()?.startsWith("gat_")) {
|
|
@@ -14058,7 +14196,7 @@ async function main() {
|
|
|
14058
14196
|
throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
|
|
14059
14197
|
}
|
|
14060
14198
|
}
|
|
14061
|
-
var notice = updateNotice("0.
|
|
14199
|
+
var notice = updateNotice("0.49.0").catch(() => null);
|
|
14062
14200
|
main().then(
|
|
14063
14201
|
async () => {
|
|
14064
14202
|
const n = await notice;
|
package/package.json
CHANGED