@ghostystudio/cli 0.48.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 +7 -2
- package/dist/index.js +170 -51
- 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,11 @@ 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
|
+
|
|
106
111
|
### Changes in 0.48.0
|
|
107
112
|
|
|
108
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.
|
package/dist/index.js
CHANGED
|
@@ -8127,6 +8127,11 @@ var SERVER_MESSAGES = [
|
|
|
8127
8127
|
["no pude conectar con el servicio de sitios ({})", "couldn't reach the sites service ({})"],
|
|
8128
8128
|
["no se pudo crear el sitio; int\xE9ntalo en un momento", "couldn't create the site; try again in a moment"],
|
|
8129
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 {}"],
|
|
8130
8135
|
// — Infra interna (casi nunca llega al CLI, pero por si acaso)
|
|
8131
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)."],
|
|
8132
8137
|
["sqld: fetch sin namespace", "sqld: fetch without namespace"],
|
|
@@ -8429,7 +8434,7 @@ async function postLong(path, body, timeoutMs = 9e5) {
|
|
|
8429
8434
|
const req = httpsRequest(`${base}${path}`, {
|
|
8430
8435
|
method: "POST",
|
|
8431
8436
|
timeout: timeoutMs,
|
|
8432
|
-
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) }
|
|
8433
8438
|
}, (res) => {
|
|
8434
8439
|
let buf = "";
|
|
8435
8440
|
res.on("data", (c) => buf += c);
|
|
@@ -8471,7 +8476,7 @@ async function request(path, opts = {}, retried = false) {
|
|
|
8471
8476
|
method: opts.method ?? (opts.body !== void 0 || opts.raw !== void 0 ? "POST" : "GET"),
|
|
8472
8477
|
headers: {
|
|
8473
8478
|
authorization: `Bearer ${token}`,
|
|
8474
|
-
"user-agent": `ghosty-cli/${"0.
|
|
8479
|
+
"user-agent": `ghosty-cli/${"0.49.0"}`,
|
|
8475
8480
|
"accept-language": lang(),
|
|
8476
8481
|
...opts.body !== void 0 ? { "content-type": "application/json" } : {},
|
|
8477
8482
|
...opts.headers
|
|
@@ -9271,8 +9276,8 @@ function capacityLine(c) {
|
|
|
9271
9276
|
// src/commands/chat.ts
|
|
9272
9277
|
import { randomUUID } from "node:crypto";
|
|
9273
9278
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
9274
|
-
import { readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
|
|
9275
|
-
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";
|
|
9276
9281
|
|
|
9277
9282
|
// src/sse.ts
|
|
9278
9283
|
async function* readSse(body) {
|
|
@@ -9482,9 +9487,65 @@ async function meMemories(sub, arg, text) {
|
|
|
9482
9487
|
throw usage(t(`Unknown memories subcommand: ${sub}.`, `Subcomando de memories desconocido: ${sub}.`), ex);
|
|
9483
9488
|
}
|
|
9484
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
|
+
}
|
|
9485
9543
|
async function meFiles(sub, arg, v) {
|
|
9486
|
-
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>";
|
|
9487
9545
|
switch (sub) {
|
|
9546
|
+
case "export":
|
|
9547
|
+
case "download":
|
|
9548
|
+
return meFilesExport(v);
|
|
9488
9549
|
case "ls": {
|
|
9489
9550
|
if (v.q !== void 0 || v.cursor) {
|
|
9490
9551
|
const q = new URLSearchParams({ q: v.q ?? "", ...v.kind ? { kind: v.kind } : {}, ...v.cursor ? { cursor: v.cursor } : {}, ...v.limit ? { limit: v.limit } : {} });
|
|
@@ -9795,7 +9856,7 @@ async function call(path, v, init = {}) {
|
|
|
9795
9856
|
"x-ghosty-ts": ts,
|
|
9796
9857
|
"x-ghosty-sig": sig,
|
|
9797
9858
|
"content-type": "application/json",
|
|
9798
|
-
"user-agent": `ghosty-cli/${"0.
|
|
9859
|
+
"user-agent": `ghosty-cli/${"0.49.0"}`
|
|
9799
9860
|
},
|
|
9800
9861
|
body: method === "GET" ? void 0 : raw
|
|
9801
9862
|
});
|
|
@@ -10050,8 +10111,8 @@ async function partnerCmd(sub, a1, a2, v, a3) {
|
|
|
10050
10111
|
}
|
|
10051
10112
|
|
|
10052
10113
|
// src/commands/data.ts
|
|
10053
|
-
import { readdir as readdir2, readFile as readFile6 } from "node:fs/promises";
|
|
10054
|
-
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";
|
|
10055
10116
|
|
|
10056
10117
|
// src/rows.ts
|
|
10057
10118
|
var IDENT = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
@@ -10208,6 +10269,26 @@ ${t(`Delete ${dry.name} for good? This cannot be undone.`, `\xBFBorrar ${dry.nam
|
|
|
10208
10269
|
const r = await api(path, { body: {} });
|
|
10209
10270
|
return out(r, () => `${lavender("\u2713")} ${t(`${r.name} deleted.`, `${r.name} borrada.`)}`);
|
|
10210
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
|
+
}
|
|
10211
10292
|
async function dbsCmd(sub, id, db, rest, v) {
|
|
10212
10293
|
const ex = "ghosty dbs ls <agent-id>";
|
|
10213
10294
|
id = need2(id, t("agent id", "el id del agente"), ex);
|
|
@@ -10282,6 +10363,11 @@ ${dim(`${t(`${rows.length} row(s)`, `${rows.length} fila(s)`)} \xB7 ${Math.round
|
|
|
10282
10363
|
db = need2(db, t("database (id or name)", "la base (id o nombre)"), ex2);
|
|
10283
10364
|
return dbsRename(id, db, need2(rest[0], t("new name", "el nombre nuevo"), ex2), !!v["dry-run"]);
|
|
10284
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
|
+
}
|
|
10285
10371
|
case "rm":
|
|
10286
10372
|
case "delete": {
|
|
10287
10373
|
db = need2(db, t("database (id or name)", "la base (id o nombre)"), "ghosty dbs rm <agent-id> <db> [--dry-run] [--yes]");
|
|
@@ -10557,7 +10643,7 @@ async function dbsPhotosCmd(verb, id, db, v) {
|
|
|
10557
10643
|
const results = [];
|
|
10558
10644
|
for (let i = 0; i < files.length; i += 8) {
|
|
10559
10645
|
const batch = files.slice(i, i + 8);
|
|
10560
|
-
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") })));
|
|
10561
10647
|
const r = await api(path, { body: { table: tableName, keyColumn, column, items, ...v["dry-run"] ? { dryRun: true } : {} } });
|
|
10562
10648
|
results.push(...r.items);
|
|
10563
10649
|
if (!globals.json && files.length > 8) info(dim(` ${Math.min(i + 8, files.length)}/${files.length}`));
|
|
@@ -10789,7 +10875,7 @@ import { readFile as readFile8 } from "node:fs/promises";
|
|
|
10789
10875
|
|
|
10790
10876
|
// src/wa-auth.ts
|
|
10791
10877
|
import { readdir as readdir3, readFile as readFile7 } from "node:fs/promises";
|
|
10792
|
-
import { join as
|
|
10878
|
+
import { join as join6 } from "node:path";
|
|
10793
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"];
|
|
10794
10880
|
function keyOfFileName(name) {
|
|
10795
10881
|
for (const type of TYPES) {
|
|
@@ -10803,7 +10889,7 @@ function keyOfFileName(name) {
|
|
|
10803
10889
|
async function readMultiFileAuth(dir) {
|
|
10804
10890
|
const files = (await readdir3(dir)).filter((f) => f.endsWith(".json"));
|
|
10805
10891
|
if (!files.includes("creds.json")) throw new Error(`${dir} has no creds.json: not a Baileys multi-file auth folder`);
|
|
10806
|
-
const authCreds = JSON.parse(await readFile7(
|
|
10892
|
+
const authCreds = JSON.parse(await readFile7(join6(dir, "creds.json"), "utf8"));
|
|
10807
10893
|
const authKeys = {};
|
|
10808
10894
|
const counts = {};
|
|
10809
10895
|
const skipped = [];
|
|
@@ -10814,7 +10900,7 @@ async function readMultiFileAuth(dir) {
|
|
|
10814
10900
|
skipped.push(f);
|
|
10815
10901
|
continue;
|
|
10816
10902
|
}
|
|
10817
|
-
(authKeys[k.type] ??= {})[k.id] = JSON.parse(await readFile7(
|
|
10903
|
+
(authKeys[k.type] ??= {})[k.id] = JSON.parse(await readFile7(join6(dir, f), "utf8"));
|
|
10818
10904
|
counts[k.type] = (counts[k.type] ?? 0) + 1;
|
|
10819
10905
|
}
|
|
10820
10906
|
return { authCreds, authKeys, counts, skipped };
|
|
@@ -11346,13 +11432,18 @@ var HELPS_ES = {
|
|
|
11346
11432
|
},
|
|
11347
11433
|
dbs: {
|
|
11348
11434
|
summary: "Bases de datos que un agente puede usar, y qu\xE9 puede escribir desde canales de clientes.",
|
|
11349
|
-
replace: [
|
|
11435
|
+
replace: [
|
|
11436
|
+
["# reads $EASYBITS_API_KEY", "# lee $EASYBITS_API_KEY"],
|
|
11437
|
+
["# totequim-productos.csv (Excel)", "# totequim-productos.csv (Excel)"]
|
|
11438
|
+
],
|
|
11350
11439
|
flags: [
|
|
11351
11440
|
["--orphans", "ls: s\xF3lo las bases que ning\xFAn agente tiene concedidas (candidatas a rm)"],
|
|
11352
11441
|
["--arg V", "query: valor para un marcador ? (repetible)"],
|
|
11353
11442
|
["--write", "query: permite INSERT/UPDATE/DELETE/DDL (default: s\xF3lo lectura)"],
|
|
11354
11443
|
["--external-write t1,t2", 'grant: tablas que puede escribir desde WhatsApp/Messenger ("" = ninguna)'],
|
|
11355
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)"],
|
|
11356
11447
|
["--from easybits:ID", "import: base de origen en EasyBits (llave de $EASYBITS_API_KEY)"],
|
|
11357
11448
|
["--tables a,b", "import: s\xF3lo estas tablas (default: todas)"],
|
|
11358
11449
|
["--append", "import: agrega filas en vez de reemplazarlas (reemplazar es el default idempotente)"],
|
|
@@ -11364,6 +11455,7 @@ var HELPS_ES = {
|
|
|
11364
11455
|
],
|
|
11365
11456
|
notes: [
|
|
11366
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.",
|
|
11367
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.",
|
|
11368
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.",
|
|
11369
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)."
|
|
@@ -11435,7 +11527,16 @@ var HELPS_ES = {
|
|
|
11435
11527
|
},
|
|
11436
11528
|
me: {
|
|
11437
11529
|
summary: "Tu cuenta: archivos, memorias y uso.",
|
|
11438
|
-
|
|
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
|
+
]
|
|
11439
11540
|
},
|
|
11440
11541
|
credentials: {
|
|
11441
11542
|
summary: "Llaves de proveedores en la b\xF3veda de tu cuenta (cifradas; s\xF3lo regresan los \xFAltimos 4 caracteres).",
|
|
@@ -11828,6 +11929,7 @@ var HELPS = {
|
|
|
11828
11929
|
"ghosty dbs ls <agent-id> [--orphans]",
|
|
11829
11930
|
"ghosty dbs tables <agent-id> <db>",
|
|
11830
11931
|
'ghosty dbs query <agent-id> <db> "SQL" [--arg V]\u2026 [--write]',
|
|
11932
|
+
"ghosty dbs export <agent-id> <db> [--table T] [--out FILE]",
|
|
11831
11933
|
"ghosty dbs import <agent-id> <db> --from easybits:<db-id> [--tables a,b] [--append] [--rehost-images t.col,\u2026] [--dry-run] [--yes]",
|
|
11832
11934
|
"ghosty dbs rehost <agent-id> <db> --table T --column C [--dry-run]",
|
|
11833
11935
|
"ghosty dbs photos put <agent-id> <db> --table T --key-column K --dir DIR [--column C] [--dry-run]",
|
|
@@ -11842,6 +11944,8 @@ var HELPS = {
|
|
|
11842
11944
|
["--write", "query: allow INSERT/UPDATE/DELETE/DDL (default: read only)"],
|
|
11843
11945
|
["--external-write t1,t2", 'grant: tables it may write from WhatsApp/Messenger ("" = none)'],
|
|
11844
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)"],
|
|
11845
11949
|
["--from easybits:ID", "import: source database in EasyBits (key from $EASYBITS_API_KEY)"],
|
|
11846
11950
|
["--tables a,b", "import: only these tables (default: all)"],
|
|
11847
11951
|
["--append", "import: add rows instead of replacing them (replace is the idempotent default)"],
|
|
@@ -11855,6 +11959,8 @@ var HELPS = {
|
|
|
11855
11959
|
"ghosty dbs ls <id>",
|
|
11856
11960
|
"ghosty dbs tables <id> totequim",
|
|
11857
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)",
|
|
11858
11964
|
"ghosty dbs grant <id> totequim --external-write cotizaciones,folio",
|
|
11859
11965
|
"ghosty dbs grant <id> totequim --external-rows clientes:telefono+tel_secundario,cotizaciones:cliente_tel",
|
|
11860
11966
|
"ghosty dbs import <id> totequim --from easybits:6a10c84c\u2026 --dry-run # reads $EASYBITS_API_KEY",
|
|
@@ -11864,6 +11970,7 @@ var HELPS = {
|
|
|
11864
11970
|
],
|
|
11865
11971
|
notes: [
|
|
11866
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.",
|
|
11867
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.",
|
|
11868
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.",
|
|
11869
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."
|
|
@@ -11974,9 +12081,17 @@ var HELPS = {
|
|
|
11974
12081
|
},
|
|
11975
12082
|
me: {
|
|
11976
12083
|
summary: "Your account: files, memories and usage.",
|
|
11977
|
-
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"],
|
|
11978
|
-
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"'],
|
|
11979
|
-
|
|
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
|
+
],
|
|
11980
12095
|
learn: `${REF}#tu-cuenta`
|
|
11981
12096
|
}
|
|
11982
12097
|
};
|
|
@@ -12346,10 +12461,10 @@ function helpText(cmd) {
|
|
|
12346
12461
|
}
|
|
12347
12462
|
|
|
12348
12463
|
// src/update.ts
|
|
12349
|
-
import { mkdir as
|
|
12464
|
+
import { mkdir as mkdir5, readFile as readFile9, writeFile as writeFile7 } from "node:fs/promises";
|
|
12350
12465
|
import { homedir as homedir3 } from "node:os";
|
|
12351
|
-
import { dirname as dirname3, join as
|
|
12352
|
-
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");
|
|
12353
12468
|
var TTL = 60 * 60 * 1e3;
|
|
12354
12469
|
var UPGRADE = "npm i -g @ghostystudio/cli@latest";
|
|
12355
12470
|
function newer(a, b) {
|
|
@@ -12371,8 +12486,8 @@ async function latestVersion(current2) {
|
|
|
12371
12486
|
const r = await fetch("https://registry.npmjs.org/@ghostystudio/cli/latest", { signal: AbortSignal.timeout(1500) });
|
|
12372
12487
|
const latest = (await r.json()).version;
|
|
12373
12488
|
if (latest) cache2 = { latest, checkedAt: Date.now() };
|
|
12374
|
-
await
|
|
12375
|
-
await
|
|
12489
|
+
await mkdir5(dirname3(FILE), { recursive: true });
|
|
12490
|
+
await writeFile7(FILE, JSON.stringify(cache2));
|
|
12376
12491
|
} catch {
|
|
12377
12492
|
}
|
|
12378
12493
|
}
|
|
@@ -12447,9 +12562,9 @@ function agentArgIndex(pos) {
|
|
|
12447
12562
|
}
|
|
12448
12563
|
|
|
12449
12564
|
// src/completion.ts
|
|
12450
|
-
import { mkdir as
|
|
12565
|
+
import { mkdir as mkdir6, readFile as readFile10, writeFile as writeFile8 } from "node:fs/promises";
|
|
12451
12566
|
import { homedir as homedir4 } from "node:os";
|
|
12452
|
-
import { join as
|
|
12567
|
+
import { join as join8 } from "node:path";
|
|
12453
12568
|
var GLOBAL_FLAGS = ["--json", "--token", "--profile", "--yes", "--help", "--version", "--lang", "--dry-run"];
|
|
12454
12569
|
var AGENT_FLAGS = /* @__PURE__ */ new Set(["--like", "--agent", "--prompt-base"]);
|
|
12455
12570
|
var WORKSPACE_FLAGS = /* @__PURE__ */ new Set(["--workspace"]);
|
|
@@ -12487,7 +12602,7 @@ function completionFor(words, current2, commands) {
|
|
|
12487
12602
|
if (agentArgIndex([...pos, current2]) === pos.length) return { kind: "agents" };
|
|
12488
12603
|
return { kind: "words", words: [] };
|
|
12489
12604
|
}
|
|
12490
|
-
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`);
|
|
12491
12606
|
async function names() {
|
|
12492
12607
|
try {
|
|
12493
12608
|
const c = JSON.parse(await readFile10(cacheFile(), "utf8"));
|
|
@@ -12497,9 +12612,9 @@ async function names() {
|
|
|
12497
12612
|
const list = await listAgents();
|
|
12498
12613
|
const agents = [...new Set(list.map((a) => a.nombre))];
|
|
12499
12614
|
const workspaces = [...new Set(list.map((a) => a.espacio?.tipo === "workspace" ? a.espacio.nombre : void 0).filter((x) => !!x))];
|
|
12500
|
-
await
|
|
12615
|
+
await mkdir6(join8(cacheFile(), ".."), { recursive: true }).catch(() => {
|
|
12501
12616
|
});
|
|
12502
|
-
await
|
|
12617
|
+
await writeFile8(cacheFile(), JSON.stringify({ at: Date.now(), agents, workspaces })).catch(() => {
|
|
12503
12618
|
});
|
|
12504
12619
|
return { agents, workspaces };
|
|
12505
12620
|
}
|
|
@@ -12577,8 +12692,8 @@ function normalizeArgs(pos, v) {
|
|
|
12577
12692
|
|
|
12578
12693
|
// src/commands/spec.ts
|
|
12579
12694
|
var import_yaml = __toESM(require_dist(), 1);
|
|
12580
|
-
import { mkdir as
|
|
12581
|
-
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";
|
|
12582
12697
|
var enc9 = encodeURIComponent;
|
|
12583
12698
|
async function agentsExport(id, v) {
|
|
12584
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");
|
|
@@ -12588,12 +12703,12 @@ async function agentsExport(id, v) {
|
|
|
12588
12703
|
return void process.stdout.write(yaml);
|
|
12589
12704
|
}
|
|
12590
12705
|
const dir = resolve2(v.out);
|
|
12591
|
-
await
|
|
12592
|
-
await
|
|
12706
|
+
await mkdir7(dir, { recursive: true });
|
|
12707
|
+
await writeFile9(join9(dir, "agent.yaml"), yaml);
|
|
12593
12708
|
const own = (0, import_yaml.parse)(yaml)["x-ghosty"]?.skills?.own ?? [];
|
|
12594
12709
|
for (const slug of own) {
|
|
12595
12710
|
const sk = await api(`/api/v2/agents/${enc9(id)}/skills/${enc9(slug)}`);
|
|
12596
|
-
await writeSkill(
|
|
12711
|
+
await writeSkill(join9(dir, "skills", slug), sk);
|
|
12597
12712
|
}
|
|
12598
12713
|
out({ saved: dir, skills: own }, () => `${lavender("\u2713")} ${dir}/agent.yaml${own.length ? ` + skills/ (${own.join(", ")})` : ""}
|
|
12599
12714
|
${dim(t(`Edit it, then: ghosty apply ${v.out} --dry-run`, `Ed\xEDtalo y luego: ghosty apply ${v.out} --dry-run`))}`);
|
|
@@ -12614,12 +12729,12 @@ function fillSecrets(file) {
|
|
|
12614
12729
|
return missing;
|
|
12615
12730
|
}
|
|
12616
12731
|
async function readSkills(dir) {
|
|
12617
|
-
const root =
|
|
12732
|
+
const root = join9(dir, "skills");
|
|
12618
12733
|
const out2 = {};
|
|
12619
12734
|
const slugs = await readdir4(root, { withFileTypes: true }).catch(() => []);
|
|
12620
12735
|
for (const e of slugs) {
|
|
12621
12736
|
if (!e.isDirectory() || e.name.startsWith(".")) continue;
|
|
12622
|
-
const { markdown, assets } = await readSkillDir(
|
|
12737
|
+
const { markdown, assets } = await readSkillDir(join9(root, e.name));
|
|
12623
12738
|
out2[e.name] = [{ path: "SKILL.md", contentBase64: Buffer.from(markdown).toString("base64") }, ...assets.map((a) => ({ path: a.name, contentBase64: a.contentBase64 }))];
|
|
12624
12739
|
}
|
|
12625
12740
|
return out2;
|
|
@@ -12627,8 +12742,8 @@ async function readSkills(dir) {
|
|
|
12627
12742
|
var line = (c) => c.op === "!" ? dim(`! ${c.what}`) : `${c.op === "+" ? lavender("+") : c.op === "-" ? red("-") : "~"} ${c.what}`;
|
|
12628
12743
|
async function applyCmd(target, v) {
|
|
12629
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)"));
|
|
12630
|
-
const isDir = (await
|
|
12631
|
-
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");
|
|
12632
12747
|
const file = (0, import_yaml.parse)(text);
|
|
12633
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.`));
|
|
12634
12749
|
const missing = fillSecrets(file);
|
|
@@ -12948,9 +13063,9 @@ ${dim(t(`${fmtTokens(total)} billable tokens \xB7 failed = error or stopped \xB7
|
|
|
12948
13063
|
|
|
12949
13064
|
// src/commands/demos.ts
|
|
12950
13065
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
12951
|
-
import { mkdir as
|
|
13066
|
+
import { mkdir as mkdir8, readFile as readFile12, writeFile as writeFile10 } from "node:fs/promises";
|
|
12952
13067
|
import { homedir as homedir5 } from "node:os";
|
|
12953
|
-
import { dirname as dirname4, join as
|
|
13068
|
+
import { dirname as dirname4, join as join10 } from "node:path";
|
|
12954
13069
|
var enc11 = encodeURIComponent;
|
|
12955
13070
|
var hora = (iso) => new Date(iso).toLocaleString("es-MX", { timeZone: "America/Mexico_City", hour12: false, dateStyle: "short", timeStyle: "short" });
|
|
12956
13071
|
async function history(agent, v) {
|
|
@@ -12970,7 +13085,7 @@ async function history(agent, v) {
|
|
|
12970
13085
|
return lines.join("\n");
|
|
12971
13086
|
});
|
|
12972
13087
|
}
|
|
12973
|
-
var jarPath = () =>
|
|
13088
|
+
var jarPath = () => join10(process.env.XDG_CONFIG_HOME || join10(homedir5(), ".config"), "ghosty", "demo-cookies.json");
|
|
12974
13089
|
async function readJar() {
|
|
12975
13090
|
try {
|
|
12976
13091
|
return JSON.parse(await readFile12(jarPath(), "utf8"));
|
|
@@ -12990,8 +13105,8 @@ async function visitorCookie(base, slug, fresh) {
|
|
|
12990
13105
|
const cookie = anonCookieOf(res.headers.get("set-cookie"));
|
|
12991
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);
|
|
12992
13107
|
jar[slug] = cookie;
|
|
12993
|
-
await
|
|
12994
|
-
await
|
|
13108
|
+
await mkdir8(dirname4(jarPath()), { recursive: true });
|
|
13109
|
+
await writeFile10(jarPath(), JSON.stringify(jar, null, 2), { mode: 384 });
|
|
12995
13110
|
return cookie;
|
|
12996
13111
|
}
|
|
12997
13112
|
async function visitorTurn(base, slug, cookie, text) {
|
|
@@ -13063,12 +13178,12 @@ async function demosCmd(sub, a1, rest, v) {
|
|
|
13063
13178
|
}
|
|
13064
13179
|
|
|
13065
13180
|
// src/commands/sites.ts
|
|
13066
|
-
import { readdir as readdir5, readFile as readFile13, stat as
|
|
13067
|
-
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";
|
|
13068
13183
|
var TEXT_EXT = /* @__PURE__ */ new Set(["html", "htm", "css", "js", "mjs", "json", "txt", "xml", "svg", "webmanifest", "md"]);
|
|
13069
13184
|
var SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", ".DS_Store"]);
|
|
13070
13185
|
async function collectSiteFiles(target) {
|
|
13071
|
-
const st = await
|
|
13186
|
+
const st = await stat3(target).catch(() => null);
|
|
13072
13187
|
if (!st) throw usage(t(`Not found: ${target}`, `No existe: ${target}`));
|
|
13073
13188
|
if (st.isFile()) {
|
|
13074
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)."));
|
|
@@ -13079,7 +13194,7 @@ async function collectSiteFiles(target) {
|
|
|
13079
13194
|
const walk = async (dir) => {
|
|
13080
13195
|
for (const e of await readdir5(dir, { withFileTypes: true })) {
|
|
13081
13196
|
if (e.name.startsWith(".") || SKIP.has(e.name)) continue;
|
|
13082
|
-
const full =
|
|
13197
|
+
const full = join11(dir, e.name);
|
|
13083
13198
|
if (e.isDirectory()) await walk(full);
|
|
13084
13199
|
else if (e.isFile()) {
|
|
13085
13200
|
const path = relative2(target, full).split(sep).join("/");
|
|
@@ -13461,7 +13576,8 @@ ${bold("Configure an agent")}
|
|
|
13461
13576
|
skills house <id> Ghosty's skills in bulk [--only a,b | --enable a,b | --disable a,b] [--dry-run]
|
|
13462
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)
|
|
13463
13578
|
skills templates <id> pdf-doc templates it sees: its own and Ghosty's, and which folder wins
|
|
13464
|
-
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]
|
|
13465
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]
|
|
13466
13582
|
mcp get|set <id> MCP servers [--file servers.json] (http headers accept \${turn.token})
|
|
13467
13583
|
channels whatsapp ls|enable|disable <id> [number] Which WhatsApp Business numbers it answers [--dry-run]
|
|
@@ -13520,7 +13636,8 @@ ${bold("Partners")} ${dim("(give an agent to each of YOUR customers \u2014 see:
|
|
|
13520
13636
|
partner ls | enable | credentials | agent | tenants | audit | try
|
|
13521
13637
|
|
|
13522
13638
|
${bold("Your account")}
|
|
13523
|
-
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]
|
|
13524
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>
|
|
13525
13642
|
credentials ls|set|rm [provider] Provider keys in your vault (hidden prompt, stdin or --from-env VAR; never as an argument)
|
|
13526
13643
|
connectors ls|connect|disconnect [id] Your connectors (Shopify, Stripe, monday\u2026): connect opens the browser or asks for the key
|
|
@@ -13607,7 +13724,8 @@ ${bold("Configurar un agente")}
|
|
|
13607
13724
|
skills house <id> Las skills de Ghosty en bloque [--only a,b | --enable a,b | --disable a,b] [--dry-run]
|
|
13608
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)
|
|
13609
13726
|
skills templates <id> Plantillas de pdf-doc que ve: las suyas y las de Ghosty, y qu\xE9 carpeta gana
|
|
13610
|
-
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]
|
|
13611
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]
|
|
13612
13730
|
mcp get|set <id> Servidores MCP [--file servers.json] (headers http admiten \${turn.token})
|
|
13613
13731
|
channels whatsapp ls|enable|disable <id> [n\xFAmero] Qu\xE9 n\xFAmeros de WhatsApp Business contesta [--dry-run]
|
|
@@ -13666,7 +13784,8 @@ ${bold("Partners")} ${dim("(dale un agente a cada uno de TUS clientes \u2014 ve
|
|
|
13666
13784
|
partner ls | enable | credentials | agent | tenants | audit | try
|
|
13667
13785
|
|
|
13668
13786
|
${bold("Tu cuenta")}
|
|
13669
|
-
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]
|
|
13670
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>
|
|
13671
13790
|
credentials ls|set|rm [proveedor] Llaves de proveedor en tu b\xF3veda (prompt oculto, stdin o --from-env VAR; nunca como argumento)
|
|
13672
13791
|
connectors ls|connect|disconnect [id] Tus conectores (Shopify, Stripe, monday\u2026): connect abre el navegador o pide la llave
|
|
@@ -13917,7 +14036,7 @@ async function main() {
|
|
|
13917
14036
|
const tokens = splitTokens(pos, v);
|
|
13918
14037
|
globals.token = tokens.session;
|
|
13919
14038
|
globals.profile = v.profile;
|
|
13920
|
-
if (v.version) return out({ version: "0.
|
|
14039
|
+
if (v.version) return out({ version: "0.49.0" }, () => "0.49.0");
|
|
13921
14040
|
for (const n of normalizeArgs(pos, v)) if (!globals.json) info(dim(n));
|
|
13922
14041
|
const context = (await readConfig()).workspace;
|
|
13923
14042
|
if (!explicitToken()?.startsWith("gat_")) {
|
|
@@ -14077,7 +14196,7 @@ async function main() {
|
|
|
14077
14196
|
throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
|
|
14078
14197
|
}
|
|
14079
14198
|
}
|
|
14080
|
-
var notice = updateNotice("0.
|
|
14199
|
+
var notice = updateNotice("0.49.0").catch(() => null);
|
|
14081
14200
|
main().then(
|
|
14082
14201
|
async () => {
|
|
14083
14202
|
const n = await notice;
|
package/package.json
CHANGED