@easybits.cloud/cli 0.12.0 → 0.13.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 +10 -1
- package/dist/index.js +293 -79
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -68,7 +68,8 @@ easybits domains rm sb_abc123 shop.example.com --yes
|
|
|
68
68
|
# Databases
|
|
69
69
|
easybits db ls
|
|
70
70
|
easybits db create leads
|
|
71
|
-
easybits db tables leads # tables, row counts, columns
|
|
71
|
+
easybits db tables leads # tables, row counts, columns, permanent vs external links
|
|
72
|
+
easybits db photos put catalog --table products --key-column sku --dir photos/ # SKU-123.jpg → row sku=SKU-123
|
|
72
73
|
easybits db query leads "SELECT * FROM leads" --json
|
|
73
74
|
easybits db rm leads --yes
|
|
74
75
|
|
|
@@ -163,6 +164,14 @@ Host *.ghosty
|
|
|
163
164
|
|
|
164
165
|
Full reference: https://www.easybits.cloud/docs#cli · https://www.easybits.cloud/en/docs/cli.md
|
|
165
166
|
|
|
167
|
+
## Changes in 0.13.0
|
|
168
|
+
|
|
169
|
+
- Catalog photos: `db photos put <db> --table T --key-column sku --dir photos/ [--column C]
|
|
170
|
+
[--replace] [--dry-run]` uploads each photo as a public file with a permanent link and writes it
|
|
171
|
+
in the matching row (real type by bytes, JPEG/PNG/WebP, 10 MB; one upload per distinct photo).
|
|
172
|
+
- `db tables` shows, per text column with links, how many are permanent, expiring (signed),
|
|
173
|
+
external or empty (`--json`: `links`).
|
|
174
|
+
|
|
166
175
|
## Changes in 0.12.0
|
|
167
176
|
|
|
168
177
|
- `easybits completion zsh|bash|fish`: TAB completes commands, subcommands, flags and the names of
|
package/dist/index.js
CHANGED
|
@@ -252,7 +252,12 @@ var HEADERS_ES = {
|
|
|
252
252
|
TABLE: "TABLA",
|
|
253
253
|
TYPE: "TIPO",
|
|
254
254
|
"URL / COMMAND": "URL / COMANDO",
|
|
255
|
-
VERSION: "VERSI\xD3N"
|
|
255
|
+
VERSION: "VERSI\xD3N",
|
|
256
|
+
COLUMN: "COLUMNA",
|
|
257
|
+
PERMANENT: "PERMANENTES",
|
|
258
|
+
EXPIRING: "CADUCAN",
|
|
259
|
+
EXTERNAL: "DE FUERA",
|
|
260
|
+
EMPTY: "VAC\xCDAS"
|
|
256
261
|
};
|
|
257
262
|
function table(rows, cols, empty = t("Nothing here yet.", "Todav\xEDa no hay nada.")) {
|
|
258
263
|
if (lang() === "es") cols = cols.map(([k, l]) => [k, HEADERS_ES[l] ?? l]);
|
|
@@ -1570,6 +1575,101 @@ var domains = {
|
|
|
1570
1575
|
}
|
|
1571
1576
|
};
|
|
1572
1577
|
|
|
1578
|
+
// src/commands/db.ts
|
|
1579
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7, statSync as statSync3 } from "fs";
|
|
1580
|
+
|
|
1581
|
+
// src/photos.ts
|
|
1582
|
+
import { createHash as createHash2 } from "crypto";
|
|
1583
|
+
import { readFileSync as readFileSync6, readdirSync, statSync as statSync2 } from "fs";
|
|
1584
|
+
import { extname as extname2, join as join3 } from "path";
|
|
1585
|
+
var PHOTO_MAX_BYTES = 10 * 1024 * 1024;
|
|
1586
|
+
var WHATSAPP_MAX_BYTES = 5 * 1024 * 1024;
|
|
1587
|
+
function imageTypeOf(b) {
|
|
1588
|
+
if (b.length >= 3 && b[0] === 255 && b[1] === 216 && b[2] === 255) return { mime: "image/jpeg", ext: "jpg" };
|
|
1589
|
+
if (b.length >= 8 && b[0] === 137 && b[1] === 80 && b[2] === 78 && b[3] === 71 && b[4] === 13 && b[5] === 10 && b[6] === 26 && b[7] === 10) {
|
|
1590
|
+
return { mime: "image/png", ext: "png" };
|
|
1591
|
+
}
|
|
1592
|
+
if (b.length >= 12 && b[0] === 82 && b[1] === 73 && b[2] === 70 && b[3] === 70 && b[8] === 87 && b[9] === 69 && b[10] === 66 && b[11] === 80) {
|
|
1593
|
+
return { mime: "image/webp", ext: "webp" };
|
|
1594
|
+
}
|
|
1595
|
+
return null;
|
|
1596
|
+
}
|
|
1597
|
+
function photoFiles(dir) {
|
|
1598
|
+
const photos = [];
|
|
1599
|
+
const rejected = [];
|
|
1600
|
+
for (const name of readdirSync(dir).sort()) {
|
|
1601
|
+
if (name.startsWith(".")) continue;
|
|
1602
|
+
const path = join3(dir, name);
|
|
1603
|
+
const st = statSync2(path);
|
|
1604
|
+
if (!st.isFile()) continue;
|
|
1605
|
+
if (st.size === 0) {
|
|
1606
|
+
rejected.push({ file: name, reason: "empty" });
|
|
1607
|
+
continue;
|
|
1608
|
+
}
|
|
1609
|
+
if (st.size > PHOTO_MAX_BYTES) {
|
|
1610
|
+
rejected.push({ file: name, reason: "too_large" });
|
|
1611
|
+
continue;
|
|
1612
|
+
}
|
|
1613
|
+
const bytes = readFileSync6(path);
|
|
1614
|
+
const type = imageTypeOf(bytes);
|
|
1615
|
+
if (!type) {
|
|
1616
|
+
rejected.push({ file: name, reason: "not_an_image" });
|
|
1617
|
+
continue;
|
|
1618
|
+
}
|
|
1619
|
+
const key = name.slice(0, name.length - extname2(name).length);
|
|
1620
|
+
photos.push({ key, file: name, path, ...type, bytes: st.size, sha: createHash2("sha256").update(bytes).digest("hex") });
|
|
1621
|
+
}
|
|
1622
|
+
return { photos, rejected };
|
|
1623
|
+
}
|
|
1624
|
+
function linkKind(v) {
|
|
1625
|
+
if (v == null || String(v).trim() === "") return "empty";
|
|
1626
|
+
const s = String(v).trim();
|
|
1627
|
+
let u;
|
|
1628
|
+
try {
|
|
1629
|
+
u = new URL(s);
|
|
1630
|
+
} catch {
|
|
1631
|
+
return "other";
|
|
1632
|
+
}
|
|
1633
|
+
if (u.protocol !== "https:" && u.protocol !== "http:") return "other";
|
|
1634
|
+
if (/X-Amz-Signature=|[?&]Signature=|[?&]sig=/i.test(u.search)) return "expiring";
|
|
1635
|
+
if (/^easybits-public\./.test(u.hostname) || u.hostname === "easybits.cloud" || u.hostname.endsWith(".easybits.cloud")) return "permanent";
|
|
1636
|
+
return "external";
|
|
1637
|
+
}
|
|
1638
|
+
var PHOTO_COLUMNS = ["image_url", "photo_url", "imagen_url", "foto_url", "image", "photo", "imagen", "foto", "img"];
|
|
1639
|
+
function defaultPhotoColumn(columns) {
|
|
1640
|
+
const lower = new Map(columns.map((c) => [c.toLowerCase(), c]));
|
|
1641
|
+
for (const c of PHOTO_COLUMNS) if (lower.has(c)) return lower.get(c);
|
|
1642
|
+
return void 0;
|
|
1643
|
+
}
|
|
1644
|
+
function planPhotos(photos, current2, replace) {
|
|
1645
|
+
return photos.map((p) => {
|
|
1646
|
+
if (!current2.has(p.key)) return { key: p.key, file: p.file, action: "no_row" };
|
|
1647
|
+
const cur = current2.get(p.key);
|
|
1648
|
+
const keep = !replace && linkKind(cur) === "permanent";
|
|
1649
|
+
return { key: p.key, file: p.file, action: keep ? "keep" : "update", ...cur ? { current: String(cur) } : {} };
|
|
1650
|
+
});
|
|
1651
|
+
}
|
|
1652
|
+
var ident = (n) => `"${n.replace(/"/g, '""')}"`;
|
|
1653
|
+
function linkStatsSql(table2, columns) {
|
|
1654
|
+
const parts = columns.flatMap((c) => {
|
|
1655
|
+
const v = `trim(CAST(${ident(c)} AS TEXT))`;
|
|
1656
|
+
const url = `(${v} LIKE 'http://%' OR ${v} LIKE 'https://%')`;
|
|
1657
|
+
const exp = `(${v} LIKE '%X-Amz-Signature=%' OR ${v} LIKE '%?Signature=%' OR ${v} LIKE '%&Signature=%')`;
|
|
1658
|
+
const perm = `(${v} LIKE 'https://easybits-public.%' OR ${v} LIKE 'http%://easybits.cloud/%' OR ${v} LIKE 'http%.easybits.cloud/%')`;
|
|
1659
|
+
return [
|
|
1660
|
+
`SUM(CASE WHEN ${url} AND NOT ${exp} AND ${perm} THEN 1 ELSE 0 END)`,
|
|
1661
|
+
`SUM(CASE WHEN ${url} AND ${exp} THEN 1 ELSE 0 END)`,
|
|
1662
|
+
`SUM(CASE WHEN ${url} AND NOT ${exp} AND NOT ${perm} THEN 1 ELSE 0 END)`,
|
|
1663
|
+
`SUM(CASE WHEN ${ident(c)} IS NULL OR ${v} = '' THEN 1 ELSE 0 END)`
|
|
1664
|
+
];
|
|
1665
|
+
});
|
|
1666
|
+
return `SELECT ${parts.join(", ")} FROM ${ident(table2)}`;
|
|
1667
|
+
}
|
|
1668
|
+
function parseLinkStats(columns, row) {
|
|
1669
|
+
const n = (i) => Number(row[i] ?? 0) || 0;
|
|
1670
|
+
return columns.map((column, i) => ({ column, permanent: n(i * 4), expiring: n(i * 4 + 1), external: n(i * 4 + 2), empty: n(i * 4 + 3) })).filter((s) => s.permanent + s.expiring + s.external > 0);
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1573
1673
|
// src/refs.ts
|
|
1574
1674
|
var ID_FORMAT = {
|
|
1575
1675
|
agent: /^[0-9a-f]{24}$/i,
|
|
@@ -1609,7 +1709,8 @@ var REF_ARGS = {
|
|
|
1609
1709
|
"domains rm": { index: 0, kind: "sandbox" },
|
|
1610
1710
|
"db rm": { index: 0, kind: "db" },
|
|
1611
1711
|
"db tables": { index: 0, kind: "db" },
|
|
1612
|
-
"db query": { index: 0, kind: "db" }
|
|
1712
|
+
"db query": { index: 0, kind: "db" },
|
|
1713
|
+
"db photos": { index: 1, kind: "db" }
|
|
1613
1714
|
};
|
|
1614
1715
|
function matchRef(kind, ref, items) {
|
|
1615
1716
|
if (looksLikeId(kind, ref)) return { ok: true, id: ref };
|
|
@@ -1731,7 +1832,7 @@ var db = {
|
|
|
1731
1832
|
},
|
|
1732
1833
|
tables: {
|
|
1733
1834
|
aliases: ["inspect", "schema"],
|
|
1734
|
-
summary: "List tables with row counts and columns",
|
|
1835
|
+
summary: "List tables with row counts and columns, and how many links are permanent",
|
|
1735
1836
|
usage: "easybits db tables <db-id|name>",
|
|
1736
1837
|
examples: ["easybits db tables leads", "easybits db tables leads --json"],
|
|
1737
1838
|
// Patrón de `turso db inspect` / `.tables` + `.schema` de sqlite3, en una sola vista.
|
|
@@ -1741,28 +1842,133 @@ var db = {
|
|
|
1741
1842
|
const hit = await findDb(ctx, ref);
|
|
1742
1843
|
const q = (sql, args) => eb.queryDatabase(hit.id, sql, args);
|
|
1743
1844
|
const names = (await q("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '_litestream_%' ORDER BY name")).rows.map((r) => String(r[0]));
|
|
1744
|
-
const
|
|
1845
|
+
const ident2 = (n) => `"${n.replace(/"/g, '""')}"`;
|
|
1745
1846
|
const tables = [];
|
|
1746
1847
|
for (const name of names) {
|
|
1747
|
-
const info2 = await q(`PRAGMA table_info(${
|
|
1848
|
+
const info2 = await q(`PRAGMA table_info(${ident2(name)})`);
|
|
1748
1849
|
const col = (k) => info2.cols.indexOf(k);
|
|
1749
1850
|
const columns = info2.rows.map((r) => ({ name: String(r[col("name")]), type: String(r[col("type")] ?? ""), pk: Number(r[col("pk")]) > 0 }));
|
|
1750
|
-
const count = await q(`SELECT count(*) FROM ${
|
|
1751
|
-
|
|
1851
|
+
const count = await q(`SELECT count(*) FROM ${ident2(name)}`);
|
|
1852
|
+
const textCols = columns.filter((c) => !c.type || /CHAR|TEXT|CLOB/i.test(c.type)).map((c) => c.name);
|
|
1853
|
+
const links = textCols.length ? parseLinkStats(textCols, (await q(linkStatsSql(name, textCols))).rows[0] ?? []) : [];
|
|
1854
|
+
tables.push({ name, rows: Number(count.rows[0]?.[0] ?? 0), columns, links });
|
|
1752
1855
|
}
|
|
1753
|
-
emit(
|
|
1754
|
-
|
|
1755
|
-
tables,
|
|
1756
|
-
() => table(
|
|
1856
|
+
emit(ctx, tables, () => {
|
|
1857
|
+
table(
|
|
1757
1858
|
tables.map((t2) => ({
|
|
1758
1859
|
name: t2.name,
|
|
1759
1860
|
rows: t2.rows,
|
|
1760
1861
|
columns: t2.columns.map((c) => `${c.name}${c.type ? ` ${c.type}` : ""}${c.pk ? " PK" : ""}`).join(", ")
|
|
1761
1862
|
})),
|
|
1762
1863
|
[["name", "TABLE"], ["rows", "ROWS"], ["columns", "COLUMNS"]],
|
|
1763
|
-
`No tables in ${hit.name}. Create one: easybits db query ${hit.name} 'CREATE TABLE \u2026'`
|
|
1764
|
-
)
|
|
1765
|
-
|
|
1864
|
+
t(`No tables in ${hit.name}. Create one: easybits db query ${hit.name} 'CREATE TABLE \u2026'`, `No hay tablas en ${hit.name}. Crea una: easybits db query ${hit.name} 'CREATE TABLE \u2026'`)
|
|
1865
|
+
);
|
|
1866
|
+
const withLinks = tables.flatMap((tb) => tb.links.map((l) => ({ ...l, table: tb.name })));
|
|
1867
|
+
if (withLinks.length) {
|
|
1868
|
+
console.log("\n" + t("Links (permanent = EasyBits public storage; expiring = signed URL):", "Ligas (permanente = almacenamiento p\xFAblico de EasyBits; caduca = URL firmada):"));
|
|
1869
|
+
table(
|
|
1870
|
+
withLinks.map((l) => ({ where: `${l.table}.${l.column}`, permanent: l.permanent, expiring: l.expiring, external: l.external, empty: l.empty })),
|
|
1871
|
+
[["where", "COLUMN"], ["permanent", "PERMANENT"], ["expiring", "EXPIRING"], ["external", "EXTERNAL"], ["empty", "EMPTY"]]
|
|
1872
|
+
);
|
|
1873
|
+
if (withLinks.some((l) => l.external + l.expiring > 0)) {
|
|
1874
|
+
console.log(t("Make them permanent: easybits db photos put <db> --table T --key-column sku --dir photos/", "Hazlas permanentes: easybits db photos put <db> --table T --key-column sku --dir fotos/"));
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
});
|
|
1878
|
+
}
|
|
1879
|
+
},
|
|
1880
|
+
photos: {
|
|
1881
|
+
summary: "Put catalog photos in a table: SKU-123.jpg \u2192 the row whose key is SKU-123, as a permanent public link",
|
|
1882
|
+
usage: "easybits db photos put <db> --table <table> --key-column <col> --dir <folder> [--column <col>] [--replace] [--dry-run]",
|
|
1883
|
+
options: {
|
|
1884
|
+
table: { type: "string", value: "table", description: "Table to update" },
|
|
1885
|
+
"key-column": { type: "string", value: "col", description: "Column that matches the file name (e.g. sku)" },
|
|
1886
|
+
dir: { type: "string", value: "folder", description: "Folder with the photos (JPEG, PNG or WebP, max 10 MB each)" },
|
|
1887
|
+
column: { type: "string", value: "col", description: "Column for the link (default: image_url, photo_url, imagen\u2026)" },
|
|
1888
|
+
replace: { type: "boolean", description: "Also replace rows that already have a permanent link" },
|
|
1889
|
+
"dry-run": { type: "boolean", description: "Show the plan; upload nothing" }
|
|
1890
|
+
},
|
|
1891
|
+
examples: [
|
|
1892
|
+
"easybits db photos put catalogo --table productos --key-column sku --dir fotos/ --dry-run",
|
|
1893
|
+
"easybits db photos put catalogo --table productos --key-column sku --dir fotos/ --column foto",
|
|
1894
|
+
"easybits db tables catalogo # how many links are permanent, external or empty"
|
|
1895
|
+
],
|
|
1896
|
+
async run(ctx) {
|
|
1897
|
+
const action = need(ctx, 0, "put", this.usage);
|
|
1898
|
+
if (action !== "put") throw usageError(t(`Unknown photos action "${action}".`, `Acci\xF3n de photos desconocida "${action}".`), this.usage);
|
|
1899
|
+
const ref = need(ctx, 1, "db", this.usage);
|
|
1900
|
+
const tableName = str(ctx, "table");
|
|
1901
|
+
const keyCol = str(ctx, "key-column");
|
|
1902
|
+
const dir = str(ctx, "dir");
|
|
1903
|
+
if (!tableName || !keyCol || !dir) throw usageError(t("Missing --table, --key-column or --dir.", "Faltan --table, --key-column o --dir."), this.usage);
|
|
1904
|
+
if (!existsSync6(dir) || !statSync3(dir).isDirectory()) throw usageError(t(`Not a folder: ${dir}`, `No es una carpeta: ${dir}`), this.usage);
|
|
1905
|
+
const { photos, rejected } = photoFiles(dir);
|
|
1906
|
+
const eb = await getClient(ctx);
|
|
1907
|
+
const hit = await findDb(ctx, ref);
|
|
1908
|
+
const q = (sql, args) => eb.queryDatabase(hit.id, sql, args);
|
|
1909
|
+
const ident2 = (n) => `"${n.replace(/"/g, '""')}"`;
|
|
1910
|
+
const info2 = await q(`PRAGMA table_info(${ident2(tableName)})`);
|
|
1911
|
+
const cols = info2.rows.map((r) => String(r[info2.cols.indexOf("name")]));
|
|
1912
|
+
if (!cols.length) throw new CliError(t(`No table "${tableName}" in ${hit.name}.`, `No hay tabla "${tableName}" en ${hit.name}.`), 1, t(`See: easybits db tables ${hit.name}`, `Mira: easybits db tables ${hit.name}`), "not_found", 404);
|
|
1913
|
+
if (!cols.includes(keyCol)) throw usageError(t(`No column "${keyCol}" in ${tableName} (${cols.join(", ")}).`, `No hay columna "${keyCol}" en ${tableName} (${cols.join(", ")}).`), this.usage);
|
|
1914
|
+
const column = str(ctx, "column") ?? defaultPhotoColumn(cols);
|
|
1915
|
+
if (!column) throw usageError(t(`${tableName} has no photo column: pass --column (${cols.join(", ")}).`, `${tableName} no tiene columna de foto: pasa --column (${cols.join(", ")}).`), this.usage);
|
|
1916
|
+
if (!cols.includes(column)) throw usageError(t(`No column "${column}" in ${tableName} (${cols.join(", ")}).`, `No hay columna "${column}" en ${tableName} (${cols.join(", ")}).`), this.usage);
|
|
1917
|
+
const current2 = /* @__PURE__ */ new Map();
|
|
1918
|
+
for (let i = 0; i < photos.length; i += 100) {
|
|
1919
|
+
const keys = photos.slice(i, i + 100).map((p) => p.key);
|
|
1920
|
+
const r = await q(`SELECT ${ident2(keyCol)}, ${ident2(column)} FROM ${ident2(tableName)} WHERE ${ident2(keyCol)} IN (${keys.map(() => "?").join(",")})`, keys);
|
|
1921
|
+
for (const row of r.rows) current2.set(String(row[0]), row[1]);
|
|
1922
|
+
}
|
|
1923
|
+
const plan = planPhotos(photos, current2, bool(ctx, "replace"));
|
|
1924
|
+
const big = photos.filter((p) => p.bytes > WHATSAPP_MAX_BYTES).map((p) => p.file);
|
|
1925
|
+
const summary = { db: hit.name, table: tableName, keyColumn: keyCol, column, plan, rejected, overWhatsappLimit: big };
|
|
1926
|
+
const count = (a) => plan.filter((p) => p.action === a).length;
|
|
1927
|
+
const why = (r) => r === "not_an_image" ? t("not a JPEG/PNG/WebP", "no es JPEG/PNG/WebP") : r === "too_large" ? t("over 10 MB", "m\xE1s de 10 MB") : t("empty file", "archivo vac\xEDo");
|
|
1928
|
+
const printPlan2 = () => {
|
|
1929
|
+
for (const p of plan) {
|
|
1930
|
+
const what = p.action === "update" ? t("update", "actualizar") : p.action === "keep" ? t("keep (already permanent; --replace to redo)", "se queda (ya es permanente; --replace para rehacerla)") : t("no row with that key", "no hay fila con esa llave");
|
|
1931
|
+
console.log(`${p.action === "update" ? "~" : "!"} ${p.file} \u2192 ${keyCol}=${p.key}: ${what}`);
|
|
1932
|
+
}
|
|
1933
|
+
for (const r of rejected) console.log(`! ${r.file}: ${why(r.reason)}`);
|
|
1934
|
+
if (big.length) console.log(t(`Over 5 MB (WhatsApp won't send them by link): ${big.join(", ")}`, `M\xE1s de 5 MB (WhatsApp no las manda por liga): ${big.join(", ")}`));
|
|
1935
|
+
};
|
|
1936
|
+
if (bool(ctx, "dry-run") || !count("update")) {
|
|
1937
|
+
emit(ctx, { ...summary, dryRun: bool(ctx, "dry-run") }, () => {
|
|
1938
|
+
printPlan2();
|
|
1939
|
+
console.log(count("update") ? t("(dry run: nothing uploaded)", "(simulaci\xF3n: no se subi\xF3 nada)") : t("Nothing to update.", "Nada que actualizar."));
|
|
1940
|
+
});
|
|
1941
|
+
return;
|
|
1942
|
+
}
|
|
1943
|
+
const urls = /* @__PURE__ */ new Map();
|
|
1944
|
+
const results = [];
|
|
1945
|
+
for (const item of plan.filter((p) => p.action === "update")) {
|
|
1946
|
+
const photo = photos.find((p) => p.key === item.key);
|
|
1947
|
+
try {
|
|
1948
|
+
let url = urls.get(photo.sha);
|
|
1949
|
+
if (!url) {
|
|
1950
|
+
const up = await eb.uploadFile({ fileName: `${photo.sha.slice(0, 32)}.${photo.ext}`, contentType: photo.mime, size: photo.bytes, access: "public" });
|
|
1951
|
+
const res = await fetch(up.putUrl, { method: "PUT", body: readFileSync7(photo.path), headers: { "Content-Type": photo.mime } });
|
|
1952
|
+
if (!res.ok) throw new Error(t(`storage answered ${res.status}`, `el almacenamiento contest\xF3 ${res.status}`));
|
|
1953
|
+
url = up.file.url;
|
|
1954
|
+
if (!url) throw new Error(t("the API returned no public link", "la API no devolvi\xF3 liga p\xFAblica"));
|
|
1955
|
+
urls.set(photo.sha, url);
|
|
1956
|
+
}
|
|
1957
|
+
await q(`UPDATE ${ident2(tableName)} SET ${ident2(column)} = ? WHERE ${ident2(keyCol)} = ?`, [url, item.key]);
|
|
1958
|
+
results.push({ key: item.key, file: item.file, url });
|
|
1959
|
+
if (!ctx.json) console.log(`~ ${item.file} \u2192 ${keyCol}=${item.key}: ${url}`);
|
|
1960
|
+
} catch (e) {
|
|
1961
|
+
results.push({ key: item.key, file: item.file, error: e.message });
|
|
1962
|
+
if (!ctx.json) console.error(`! ${item.file}: ${e.message}`);
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
const failed = results.filter((r) => r.error).length;
|
|
1966
|
+
emit(ctx, { ...summary, results }, () => {
|
|
1967
|
+
for (const p of plan.filter((x) => x.action !== "update")) console.log(`! ${p.file} \u2192 ${keyCol}=${p.key}: ${p.action === "keep" ? t("kept", "se qued\xF3") : t("no row with that key", "no hay fila con esa llave")}`);
|
|
1968
|
+
for (const r of rejected) console.log(`! ${r.file}: ${why(r.reason)}`);
|
|
1969
|
+
console.log(t(`Updated ${results.length - failed} row(s)${failed ? `, ${failed} failed` : ""}.`, `${results.length - failed} fila(s) actualizadas${failed ? `, ${failed} fallaron` : ""}.`));
|
|
1970
|
+
});
|
|
1971
|
+
if (failed) process.exitCode = 1;
|
|
1766
1972
|
}
|
|
1767
1973
|
},
|
|
1768
1974
|
query: {
|
|
@@ -1804,7 +2010,7 @@ var db = {
|
|
|
1804
2010
|
};
|
|
1805
2011
|
|
|
1806
2012
|
// src/commands/agents.ts
|
|
1807
|
-
import { existsSync as
|
|
2013
|
+
import { existsSync as existsSync10, readFileSync as readFileSync11, statSync as statSync6 } from "fs";
|
|
1808
2014
|
|
|
1809
2015
|
// src/api.ts
|
|
1810
2016
|
import { EasybitsError as EasybitsError2, resolveBaseUrl as resolveBaseUrl4 } from "@easybits.cloud/sdk";
|
|
@@ -1834,8 +2040,8 @@ async function api(ctx, method, path, body, opts = {}) {
|
|
|
1834
2040
|
var encodePath = (p) => p.replace(/^\/+/, "").split("/").map(encodeURIComponent).join("/");
|
|
1835
2041
|
|
|
1836
2042
|
// src/commands/agents-config.ts
|
|
1837
|
-
import { existsSync as
|
|
1838
|
-
import { basename as basename2, dirname, join as
|
|
2043
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync8, readdirSync as readdirSync2, statSync as statSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
2044
|
+
import { basename as basename2, dirname, join as join4, relative, resolve } from "path";
|
|
1839
2045
|
var WORK_DIR = "/data/work";
|
|
1840
2046
|
var MACHINE_TEMPLATES = /* @__PURE__ */ new Set(["ghosty-lite", "goose"]);
|
|
1841
2047
|
var SKILLS_DIR = "/data/agent/skills";
|
|
@@ -1914,8 +2120,8 @@ function readLocalText(path, usage2) {
|
|
|
1914
2120
|
if (process.stdin.isTTY) throw usageError(t("Expected the content on stdin.", "Se esperaba el contenido por stdin."), usage2);
|
|
1915
2121
|
return readStdin().then((b) => b.toString("utf8"));
|
|
1916
2122
|
}
|
|
1917
|
-
if (!
|
|
1918
|
-
return
|
|
2123
|
+
if (!existsSync7(path)) throw usageError(t(`File not found: ${path}`, `No existe el archivo: ${path}`), usage2);
|
|
2124
|
+
return readFileSync8(path, "utf8");
|
|
1919
2125
|
}
|
|
1920
2126
|
function parseMcpFile(text, usage2) {
|
|
1921
2127
|
let j;
|
|
@@ -2130,8 +2336,8 @@ var files2 = {
|
|
|
2130
2336
|
if (!rest.length) throw usageError(t("Give one or more local files.", "Pasa uno o m\xE1s archivos locales."), FILES_USAGE2);
|
|
2131
2337
|
const to = (str(ctx, "to") ?? "").replace(/^\/+|\/+$/g, "");
|
|
2132
2338
|
const plan = rest.map((local) => {
|
|
2133
|
-
if (!
|
|
2134
|
-
return { local, path: to ? `${to}/${basename2(local)}` : basename2(local), bytes:
|
|
2339
|
+
if (!existsSync7(local) || !statSync4(local).isFile()) throw usageError(t(`File not found: ${local}`, `No existe el archivo: ${local}`), FILES_USAGE2);
|
|
2340
|
+
return { local, path: to ? `${to}/${basename2(local)}` : basename2(local), bytes: statSync4(local).size };
|
|
2135
2341
|
});
|
|
2136
2342
|
if (bool(ctx, "dry-run")) {
|
|
2137
2343
|
emit(ctx, { agentId: id, dryRun: true, upload: plan }, () => {
|
|
@@ -2142,7 +2348,7 @@ var files2 = {
|
|
|
2142
2348
|
}
|
|
2143
2349
|
const done = [];
|
|
2144
2350
|
for (const p of plan) {
|
|
2145
|
-
const r = await api(ctx, "PUT", `/agents/${id}/files/${encodePath(p.path)}`,
|
|
2351
|
+
const r = await api(ctx, "PUT", `/agents/${id}/files/${encodePath(p.path)}`, readFileSync8(p.local), { raw: true });
|
|
2146
2352
|
done.push({ local: p.local, ...r });
|
|
2147
2353
|
if (!ctx.json) console.log(`${t("Uploaded", "Subido")} ${p.local} \u2192 ${WORK_DIR}/${p.path} (${fmtBytes(p.bytes)})`);
|
|
2148
2354
|
}
|
|
@@ -2168,11 +2374,11 @@ var SKILLS_USAGE = "easybits agents skills <ls|get|add|rm> <agent> [slug...] [--
|
|
|
2168
2374
|
function readSkillDir(dir) {
|
|
2169
2375
|
const assets = [];
|
|
2170
2376
|
const visit = (d) => {
|
|
2171
|
-
for (const e of
|
|
2377
|
+
for (const e of readdirSync2(d, { withFileTypes: true })) {
|
|
2172
2378
|
if (e.name.startsWith(".") || e.name === "node_modules") continue;
|
|
2173
|
-
const p =
|
|
2379
|
+
const p = join4(d, e.name);
|
|
2174
2380
|
if (e.isDirectory()) visit(p);
|
|
2175
|
-
else if (e.isFile() && relative(dir, p) !== "SKILL.md") assets.push({ name: relative(dir, p).split("\\").join("/"), contentBase64:
|
|
2381
|
+
else if (e.isFile() && relative(dir, p) !== "SKILL.md") assets.push({ name: relative(dir, p).split("\\").join("/"), contentBase64: readFileSync8(p).toString("base64") });
|
|
2176
2382
|
}
|
|
2177
2383
|
};
|
|
2178
2384
|
visit(dir);
|
|
@@ -2225,19 +2431,19 @@ var skills = {
|
|
|
2225
2431
|
}
|
|
2226
2432
|
const s = await readSkill(box, entry);
|
|
2227
2433
|
mkdirSync3(out, { recursive: true });
|
|
2228
|
-
writeFileSync4(
|
|
2434
|
+
writeFileSync4(join4(out, "SKILL.md"), s.markdown);
|
|
2229
2435
|
for (const a of s.assets) {
|
|
2230
|
-
mkdirSync3(dirname(
|
|
2231
|
-
writeFileSync4(
|
|
2436
|
+
mkdirSync3(dirname(join4(out, a.name)), { recursive: true });
|
|
2437
|
+
writeFileSync4(join4(out, a.name), Buffer.from(a.contentBase64, "base64"));
|
|
2232
2438
|
}
|
|
2233
2439
|
emit(ctx, { slug, out: resolve(out), files: entry.files }, () => console.log(t(`Saved ${entry.files.length} file(s) to ${resolve(out)}`, `Guard\xE9 ${entry.files.length} archivo(s) en ${resolve(out)}`)));
|
|
2234
2440
|
} else if (action === "add" || action === "put") {
|
|
2235
2441
|
const file = str(ctx, "file");
|
|
2236
2442
|
const dir = str(ctx, "dir");
|
|
2237
2443
|
if (!!file === !!dir) throw usageError(t("Pass --file SKILL.md or --dir <folder>.", "Pasa --file SKILL.md o --dir <carpeta>."), SKILLS_USAGE);
|
|
2238
|
-
const mdPath = file ??
|
|
2239
|
-
if (!
|
|
2240
|
-
const markdown =
|
|
2444
|
+
const mdPath = file ?? join4(dir, "SKILL.md");
|
|
2445
|
+
if (!existsSync7(mdPath)) throw usageError(t(`File not found: ${mdPath}`, `No existe el archivo: ${mdPath}`), SKILLS_USAGE);
|
|
2446
|
+
const markdown = readFileSync8(mdPath, "utf8");
|
|
2241
2447
|
const fm = skillFrontmatter(markdown);
|
|
2242
2448
|
if (fm.error) throw usageError(`${mdPath}: ${fm.error}`, SKILLS_USAGE);
|
|
2243
2449
|
const slug = str(ctx, "slug") ?? (fm.name && SLUG_RE.test(fm.name) ? fm.name : basename2(resolve(dir ?? dirname(mdPath))).toLowerCase());
|
|
@@ -2497,10 +2703,10 @@ var exportAgent = {
|
|
|
2497
2703
|
}
|
|
2498
2704
|
};
|
|
2499
2705
|
function readExport(path, usage2) {
|
|
2500
|
-
if (!
|
|
2706
|
+
if (!existsSync7(path)) throw usageError(t(`File not found: ${path}`, `No existe el archivo: ${path}`), usage2);
|
|
2501
2707
|
let x;
|
|
2502
2708
|
try {
|
|
2503
|
-
x = JSON.parse(
|
|
2709
|
+
x = JSON.parse(readFileSync8(path, "utf8"));
|
|
2504
2710
|
} catch (e) {
|
|
2505
2711
|
throw usageError(t(`${path} is not JSON: ${e.message}`, `${path} no es JSON: ${e.message}`), usage2);
|
|
2506
2712
|
}
|
|
@@ -2545,37 +2751,37 @@ async function applyExport(ctx, id, x, opts) {
|
|
|
2545
2751
|
}
|
|
2546
2752
|
|
|
2547
2753
|
// src/commands/spec.ts
|
|
2548
|
-
import { existsSync as
|
|
2754
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync10, statSync as statSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
2549
2755
|
import { homedir as homedir3 } from "os";
|
|
2550
|
-
import { dirname as dirname2, join as
|
|
2756
|
+
import { dirname as dirname2, join as join6, resolve as resolve2 } from "path";
|
|
2551
2757
|
|
|
2552
2758
|
// src/specfile.ts
|
|
2553
|
-
import { existsSync as
|
|
2554
|
-
import { join as
|
|
2759
|
+
import { existsSync as existsSync8, readFileSync as readFileSync9, readdirSync as readdirSync3 } from "fs";
|
|
2760
|
+
import { join as join5, relative as relative2 } from "path";
|
|
2555
2761
|
function walkLocal(root) {
|
|
2556
2762
|
const out = [];
|
|
2557
2763
|
const visit = (d) => {
|
|
2558
|
-
for (const e of
|
|
2764
|
+
for (const e of readdirSync3(d, { withFileTypes: true })) {
|
|
2559
2765
|
if (e.name.startsWith(".") || e.name === "node_modules") continue;
|
|
2560
|
-
const p =
|
|
2766
|
+
const p = join5(d, e.name);
|
|
2561
2767
|
if (e.isDirectory()) visit(p);
|
|
2562
2768
|
else if (e.isFile()) out.push(relative2(root, p).split("\\").join("/"));
|
|
2563
2769
|
}
|
|
2564
2770
|
};
|
|
2565
|
-
if (
|
|
2771
|
+
if (existsSync8(root)) visit(root);
|
|
2566
2772
|
return out;
|
|
2567
2773
|
}
|
|
2568
2774
|
function readUploads(dir) {
|
|
2569
2775
|
const skills2 = {};
|
|
2570
|
-
const skillsRoot =
|
|
2571
|
-
if (
|
|
2572
|
-
for (const e of
|
|
2776
|
+
const skillsRoot = join5(dir, "skills");
|
|
2777
|
+
if (existsSync8(skillsRoot)) {
|
|
2778
|
+
for (const e of readdirSync3(skillsRoot, { withFileTypes: true })) {
|
|
2573
2779
|
if (!e.isDirectory() || e.name.startsWith(".")) continue;
|
|
2574
|
-
skills2[e.name] = walkLocal(
|
|
2780
|
+
skills2[e.name] = walkLocal(join5(skillsRoot, e.name)).map((path) => ({ path, contentBase64: readFileSync9(join5(skillsRoot, e.name, path)).toString("base64") }));
|
|
2575
2781
|
}
|
|
2576
2782
|
}
|
|
2577
|
-
const filesRoot =
|
|
2578
|
-
const files3 = Object.fromEntries(walkLocal(filesRoot).map((p) => [p,
|
|
2783
|
+
const filesRoot = join5(dir, "files");
|
|
2784
|
+
const files3 = Object.fromEntries(walkLocal(filesRoot).map((p) => [p, readFileSync9(join5(filesRoot, p)).toString("base64")]));
|
|
2579
2785
|
return { ...Object.keys(skills2).length ? { skills: skills2 } : {}, ...Object.keys(files3).length ? { files: files3 } : {} };
|
|
2580
2786
|
}
|
|
2581
2787
|
function secretsFor(text, env = process.env) {
|
|
@@ -2628,17 +2834,17 @@ Ed\xEDtalo y luego: easybits apply ${out} --dry-run`)));
|
|
|
2628
2834
|
const yaml = await getYaml();
|
|
2629
2835
|
const dir = resolve2(out);
|
|
2630
2836
|
mkdirSync4(dir, { recursive: true });
|
|
2631
|
-
writeFileSync5(
|
|
2837
|
+
writeFileSync5(join6(dir, "agent.yaml"), yaml);
|
|
2632
2838
|
const x = await buildExport(ctx, id, { secrets: false, fileContents: true });
|
|
2633
2839
|
const put = (path, b64) => {
|
|
2634
2840
|
mkdirSync4(dirname2(path), { recursive: true });
|
|
2635
2841
|
writeFileSync5(path, Buffer.from(b64, "base64"));
|
|
2636
2842
|
};
|
|
2637
2843
|
for (const s of x.skills ?? []) {
|
|
2638
|
-
writeFileSync5((mkdirSync4(
|
|
2639
|
-
for (const a of s.assets) put(
|
|
2844
|
+
writeFileSync5((mkdirSync4(join6(dir, "skills", s.slug), { recursive: true }), join6(dir, "skills", s.slug, "SKILL.md")), s.markdown);
|
|
2845
|
+
for (const a of s.assets) put(join6(dir, "skills", s.slug, a.name), a.contentBase64);
|
|
2640
2846
|
}
|
|
2641
|
-
for (const f of x.files ?? []) if (f.contentBase64 !== void 0) put(
|
|
2847
|
+
for (const f of x.files ?? []) if (f.contentBase64 !== void 0) put(join6(dir, "files", f.path), f.contentBase64);
|
|
2642
2848
|
const summary = { saved: dir, skills: (x.skills ?? []).map((s) => s.slug), files: (x.files ?? []).map((f) => f.path) };
|
|
2643
2849
|
emit(
|
|
2644
2850
|
ctx,
|
|
@@ -2651,20 +2857,20 @@ ${t("Edit it, then", "Ed\xEDtalo y luego")}: easybits apply ${out} --dry-run`
|
|
|
2651
2857
|
}
|
|
2652
2858
|
};
|
|
2653
2859
|
function readTarget(target, usage2) {
|
|
2654
|
-
if (!
|
|
2655
|
-
if (
|
|
2656
|
-
const name = SPEC_NAMES.find((n) =>
|
|
2860
|
+
if (!existsSync9(target)) throw usageError(t(`Not found: ${target}`, `No existe: ${target}`), usage2);
|
|
2861
|
+
if (statSync5(target).isDirectory()) {
|
|
2862
|
+
const name = SPEC_NAMES.find((n) => existsSync9(join6(target, n)));
|
|
2657
2863
|
if (!name) throw usageError(t(`${target} has no agent.yaml.`, `${target} no tiene agent.yaml.`), usage2);
|
|
2658
|
-
return { text:
|
|
2864
|
+
return { text: readFileSync10(join6(target, name), "utf8"), dir: target };
|
|
2659
2865
|
}
|
|
2660
|
-
return { text:
|
|
2866
|
+
return { text: readFileSync10(target, "utf8") };
|
|
2661
2867
|
}
|
|
2662
2868
|
var line = (c) => `${c.op} ${c.what}`;
|
|
2663
2869
|
var printPlan = (plan) => console.log(plan.map(line).join("\n"));
|
|
2664
2870
|
function saveBackup(id, yaml) {
|
|
2665
|
-
const dir =
|
|
2871
|
+
const dir = join6(homedir3(), ".easybits", "backups");
|
|
2666
2872
|
mkdirSync4(dir, { recursive: true });
|
|
2667
|
-
const path =
|
|
2873
|
+
const path = join6(dir, `${id}-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.yaml`);
|
|
2668
2874
|
writeFileSync5(path, yaml);
|
|
2669
2875
|
return path;
|
|
2670
2876
|
}
|
|
@@ -2778,7 +2984,7 @@ var apply = {
|
|
|
2778
2984
|
// src/commands/agents.ts
|
|
2779
2985
|
function isLegacyExport(path) {
|
|
2780
2986
|
try {
|
|
2781
|
-
return
|
|
2987
|
+
return existsSync10(path) && !statSync6(path).isDirectory() && /"kind"\s*:\s*"easybits\.agent"/.test(readFileSync11(path, "utf8"));
|
|
2782
2988
|
} catch {
|
|
2783
2989
|
return false;
|
|
2784
2990
|
}
|
|
@@ -2845,9 +3051,9 @@ var agents = {
|
|
|
2845
3051
|
if (promptInline != null && promptFile) throw usageError(t("Use --prompt or --prompt-file, not both.", "Usa --prompt o --prompt-file, no los dos."), this.usage);
|
|
2846
3052
|
const mode = str(ctx, "prompt-mode");
|
|
2847
3053
|
if (mode && mode !== "append" && mode !== "replace") throw usageError(t("--prompt-mode must be append or replace.", "--prompt-mode debe ser append o replace."), this.usage);
|
|
2848
|
-
if (promptFile && !
|
|
3054
|
+
if (promptFile && !existsSync10(promptFile)) throw usageError(t(`File not found: ${promptFile}`, `No existe el archivo: ${promptFile}`), this.usage);
|
|
2849
3055
|
const mcpFile = str(ctx, "mcp-file");
|
|
2850
|
-
if (mcpFile && !
|
|
3056
|
+
if (mcpFile && !existsSync10(mcpFile)) throw usageError(t(`File not found: ${mcpFile}`, `No existe el archivo: ${mcpFile}`), this.usage);
|
|
2851
3057
|
if (bool(ctx, "copy-files") && !like && !from) throw usageError(t("--copy-files needs --like or --from.", "--copy-files necesita --like o --from."), this.usage);
|
|
2852
3058
|
if (from && !isLegacyExport(from)) {
|
|
2853
3059
|
await applyTarget(ctx, from, { create: true, name: str(ctx, "name"), dryRun: bool(ctx, "dry-run"), usage: this.usage });
|
|
@@ -2857,7 +3063,7 @@ var agents = {
|
|
|
2857
3063
|
const template = str(ctx, "template") ?? source?.template;
|
|
2858
3064
|
if (!template) throw usageError(t("Missing --template (or --like / --from).", "Falta --template (o --like / --from)."), this.usage);
|
|
2859
3065
|
const env = await envFrom(ctx, this.usage);
|
|
2860
|
-
const prompt = promptInline ?? (promptFile ?
|
|
3066
|
+
const prompt = promptInline ?? (promptFile ? readFileSync11(promptFile, "utf8") : source?.systemPrompt);
|
|
2861
3067
|
const promptMode = mode ?? source?.systemPromptMode;
|
|
2862
3068
|
const multiline = !!prompt && /[\r\n]/.test(prompt.trimEnd());
|
|
2863
3069
|
const promptAfter = multiline ? prompt : void 0;
|
|
@@ -2866,7 +3072,7 @@ var agents = {
|
|
|
2866
3072
|
}
|
|
2867
3073
|
if (prompt && !multiline) env.SYSTEM_PROMPT = prompt.trimEnd();
|
|
2868
3074
|
if (promptMode && (prompt || mode) && !multiline) env.SYSTEM_PROMPT_MODE = promptMode;
|
|
2869
|
-
const mcpServers = mcpFile ? parseMcpFile(
|
|
3075
|
+
const mcpServers = mcpFile ? parseMcpFile(readFileSync11(mcpFile, "utf8"), this.usage) : source?.mcpServers;
|
|
2870
3076
|
if (mcpServers && hasMasked(mcpServers)) {
|
|
2871
3077
|
throw usageError(t(`${from ?? "the source"} has masked MCP values (\u2026***).`, `${from ?? "la fuente"} trae valores del MCP tapados (\u2026***).`), t("Re-export with --show-secrets, or pass --mcp-file", "Vuelve a exportar con --show-secrets, o pasa --mcp-file"));
|
|
2872
3078
|
}
|
|
@@ -2958,9 +3164,9 @@ var agents = {
|
|
|
2958
3164
|
};
|
|
2959
3165
|
|
|
2960
3166
|
// src/commands/completion.ts
|
|
2961
|
-
import { mkdirSync as mkdirSync5, readFileSync as
|
|
3167
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync12, writeFileSync as writeFileSync6 } from "fs";
|
|
2962
3168
|
import { homedir as homedir4 } from "os";
|
|
2963
|
-
import { dirname as dirname3, join as
|
|
3169
|
+
import { dirname as dirname3, join as join7 } from "path";
|
|
2964
3170
|
var GLOBAL_FLAGS = ["--json", "--token", "--lang", "--help", "--version"];
|
|
2965
3171
|
var AGENT_FLAGS = /* @__PURE__ */ new Set(["--agent", "--like"]);
|
|
2966
3172
|
var findSub = (c, name) => Object.entries(c.subs ?? {}).find(([n, l]) => n === name || l.aliases?.includes(name));
|
|
@@ -2997,11 +3203,11 @@ function completionFor(words, current2, commands) {
|
|
|
2997
3203
|
if (spec && spec.index === n) return { kind: "names", ref: spec.kind };
|
|
2998
3204
|
return { kind: "words", words: [] };
|
|
2999
3205
|
}
|
|
3000
|
-
var cacheFile = () =>
|
|
3206
|
+
var cacheFile = () => join7(process.env.XDG_CACHE_HOME || join7(homedir4(), ".cache"), "easybits", "complete.json");
|
|
3001
3207
|
async function namesOf(ctx, kind) {
|
|
3002
3208
|
let cache2 = {};
|
|
3003
3209
|
try {
|
|
3004
|
-
cache2 = JSON.parse(
|
|
3210
|
+
cache2 = JSON.parse(readFileSync12(cacheFile(), "utf8"));
|
|
3005
3211
|
const c = cache2[kind];
|
|
3006
3212
|
if (c && Date.now() - c.at < 5 * 6e4) return c.names;
|
|
3007
3213
|
} catch {
|
|
@@ -3128,16 +3334,16 @@ var docs = {
|
|
|
3128
3334
|
};
|
|
3129
3335
|
|
|
3130
3336
|
// src/commands/doctor.ts
|
|
3131
|
-
import { existsSync as
|
|
3337
|
+
import { existsSync as existsSync11, statSync as statSync7 } from "fs";
|
|
3132
3338
|
import { homedir as homedir6 } from "os";
|
|
3133
|
-
import { join as
|
|
3339
|
+
import { join as join9 } from "path";
|
|
3134
3340
|
import { resolveBaseUrl as resolveBaseUrl6 } from "@easybits.cloud/sdk";
|
|
3135
3341
|
|
|
3136
3342
|
// src/update.ts
|
|
3137
|
-
import { mkdirSync as mkdirSync6, readFileSync as
|
|
3343
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync13, writeFileSync as writeFileSync7 } from "fs";
|
|
3138
3344
|
import { homedir as homedir5 } from "os";
|
|
3139
|
-
import { dirname as dirname4, join as
|
|
3140
|
-
var FILE =
|
|
3345
|
+
import { dirname as dirname4, join as join8 } from "path";
|
|
3346
|
+
var FILE = join8(process.env.XDG_CONFIG_HOME || join8(homedir5(), ".config"), "easybits", "update.json");
|
|
3141
3347
|
var DAY = 24 * 60 * 60 * 1e3;
|
|
3142
3348
|
var UPGRADE = "npm i -g @easybits.cloud/cli@latest";
|
|
3143
3349
|
function newer(a, b) {
|
|
@@ -3149,7 +3355,7 @@ function newer(a, b) {
|
|
|
3149
3355
|
var updateCheckDisabled = () => Boolean(process.env.EASYBITS_NO_UPDATE_CHECK || process.env.CI);
|
|
3150
3356
|
function readCache() {
|
|
3151
3357
|
try {
|
|
3152
|
-
return JSON.parse(
|
|
3358
|
+
return JSON.parse(readFileSync13(FILE, "utf8"));
|
|
3153
3359
|
} catch {
|
|
3154
3360
|
return {};
|
|
3155
3361
|
}
|
|
@@ -3184,7 +3390,7 @@ async function updateNotice(current2) {
|
|
|
3184
3390
|
}
|
|
3185
3391
|
|
|
3186
3392
|
// src/commands/doctor.ts
|
|
3187
|
-
var VERSION = true ? "0.
|
|
3393
|
+
var VERSION = true ? "0.13.0" : "dev";
|
|
3188
3394
|
var doctor2 = {
|
|
3189
3395
|
name: "doctor",
|
|
3190
3396
|
group: "Account",
|
|
@@ -3227,9 +3433,9 @@ var doctor2 = {
|
|
|
3227
3433
|
checks.push({ check: "credential-env", ok: true, detail: t("EASYBITS_API_KEY wins over the saved login in ~/.easybitsrc", "EASYBITS_API_KEY le gana a la sesi\xF3n guardada en ~/.easybitsrc") });
|
|
3228
3434
|
}
|
|
3229
3435
|
}
|
|
3230
|
-
const rcPath =
|
|
3231
|
-
if (
|
|
3232
|
-
const mode =
|
|
3436
|
+
const rcPath = join9(homedir6(), ".easybitsrc");
|
|
3437
|
+
if (existsSync11(rcPath)) {
|
|
3438
|
+
const mode = statSync7(rcPath).mode & 511;
|
|
3233
3439
|
const ok2 = (mode & 63) === 0;
|
|
3234
3440
|
checks.push({ check: "rc-file", ok: ok2, detail: `${rcPath} ${mode.toString(8).padStart(3, "0")}`, ...ok2 ? {} : { hint: t(`It holds tokens. Run: chmod 600 ${rcPath}`, `Guarda tokens. Corre: chmod 600 ${rcPath}`) } });
|
|
3235
3441
|
}
|
|
@@ -3439,7 +3645,7 @@ var HELP_ES = {
|
|
|
3439
3645
|
"Delete a database (irreversible)": "Borra una base de datos (irreversible)",
|
|
3440
3646
|
"asks you to type the name": "te pide teclear el nombre",
|
|
3441
3647
|
"scripts and agents": "scripts y agentes",
|
|
3442
|
-
"List tables with row counts and columns": "Enlista las tablas con
|
|
3648
|
+
"List tables with row counts and columns, and how many links are permanent": "Enlista las tablas con sus filas y columnas, y cu\xE1ntas ligas son permanentes",
|
|
3443
3649
|
"Run one SQL statement (by database id or name)": "Corre una sentencia SQL (por id o nombre de la base)",
|
|
3444
3650
|
"Positional ? parameter (repeatable)": "Par\xE1metro ? posicional (repetible)",
|
|
3445
3651
|
"Storage": "Almacenamiento",
|
|
@@ -3466,7 +3672,15 @@ var HELP_ES = {
|
|
|
3466
3672
|
"Shell completion script (zsh, bash, fish)": "Script de autocompletado para tu shell (zsh, bash, fish)",
|
|
3467
3673
|
"Print the completion script for your shell: commands, flags and your agents, sandboxes and databases": "Imprime el script de autocompletado de tu shell: comandos, flags y tus agentes, sandboxes y bases",
|
|
3468
3674
|
"add to ~/.zshrc, after compinit": "agr\xE9galo a ~/.zshrc, despu\xE9s de compinit",
|
|
3469
|
-
"add to ~/.bashrc": "agr\xE9galo a ~/.bashrc"
|
|
3675
|
+
"add to ~/.bashrc": "agr\xE9galo a ~/.bashrc",
|
|
3676
|
+
"Put catalog photos in a table: SKU-123.jpg \u2192 the row whose key is SKU-123, as a permanent public link": "Pone las fotos del cat\xE1logo en una tabla: SKU-123.jpg \u2192 la fila cuya llave es SKU-123, con liga p\xFAblica permanente",
|
|
3677
|
+
"Table to update": "Tabla a actualizar",
|
|
3678
|
+
"Column that matches the file name (e.g. sku)": "Columna que coincide con el nombre del archivo (p. ej. sku)",
|
|
3679
|
+
"Folder with the photos (JPEG, PNG or WebP, max 10 MB each)": "Carpeta con las fotos (JPEG, PNG o WebP, m\xE1x 10 MB cada una)",
|
|
3680
|
+
"Column for the link (default: image_url, photo_url, imagen\u2026)": "Columna de la liga (default: image_url, photo_url, imagen\u2026)",
|
|
3681
|
+
"Also replace rows that already have a permanent link": "Reemplaza tambi\xE9n las filas que ya tienen liga permanente",
|
|
3682
|
+
"Show the plan; upload nothing": "Ense\xF1a el plan; no sube nada",
|
|
3683
|
+
"how many links are permanent, external or empty": "cu\xE1ntas ligas son permanentes, de fuera o vac\xEDas"
|
|
3470
3684
|
};
|
|
3471
3685
|
|
|
3472
3686
|
// src/help.ts
|
|
@@ -3657,7 +3871,7 @@ function normalizeArgs(input) {
|
|
|
3657
3871
|
}
|
|
3658
3872
|
|
|
3659
3873
|
// src/index.ts
|
|
3660
|
-
var VERSION2 = true ? "0.
|
|
3874
|
+
var VERSION2 = true ? "0.13.0" : "dev";
|
|
3661
3875
|
var GLOBAL_OPTIONS = {
|
|
3662
3876
|
json: { type: "boolean" },
|
|
3663
3877
|
token: { type: "string" },
|
package/package.json
CHANGED