@easybits.cloud/cli 0.10.0 → 0.11.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 +8 -0
- package/dist/index.js +637 -291
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -11,6 +11,73 @@ import { existsSync, readFileSync } from "fs";
|
|
|
11
11
|
|
|
12
12
|
// src/errors.ts
|
|
13
13
|
import { EasybitsError } from "@easybits.cloud/sdk";
|
|
14
|
+
|
|
15
|
+
// src/i18n.ts
|
|
16
|
+
var current = null;
|
|
17
|
+
function detectLang(flag, env = process.env) {
|
|
18
|
+
const pick = (v) => v && /^es\b|^es[_-]/i.test(v) ? "es" : v && /^en\b|^en[_-]|^c$|^posix$/i.test(v) ? "en" : null;
|
|
19
|
+
return pick(flag) ?? pick(env.EASYBITS_LANG) ?? pick(env.LC_ALL) ?? pick(env.LC_MESSAGES) ?? pick(env.LANG) ?? "en";
|
|
20
|
+
}
|
|
21
|
+
function setLang(l) {
|
|
22
|
+
current = l;
|
|
23
|
+
}
|
|
24
|
+
var lang = () => current ?? (current = detectLang());
|
|
25
|
+
var t = (en, es) => lang() === "es" ? es : en;
|
|
26
|
+
function langFlag(argv2) {
|
|
27
|
+
const end = argv2.indexOf("--");
|
|
28
|
+
const head = end === -1 ? argv2 : argv2.slice(0, end);
|
|
29
|
+
const i = head.indexOf("--lang");
|
|
30
|
+
if (i !== -1) return head[i + 1];
|
|
31
|
+
return head.find((a) => a.startsWith("--lang="))?.slice(7);
|
|
32
|
+
}
|
|
33
|
+
var FROM_ES = [
|
|
34
|
+
["ruta inv\xE1lida", "invalid path"],
|
|
35
|
+
["cuerpo vac\xEDo", "empty body"],
|
|
36
|
+
["archivo demasiado grande (m\xE1x 10 MB)", "file too large (max 10 MB)"],
|
|
37
|
+
["skill demasiado grande (m\xE1x 25 MB)", "skill too large (max 25 MB)"],
|
|
38
|
+
["slug inv\xE1lido ([a-z0-9-], m\xE1x 64)", "invalid slug ([a-z0-9-], max 64)"],
|
|
39
|
+
["slug inv\xE1lido", "invalid slug"],
|
|
40
|
+
["SKILL.md sin frontmatter (--- name / description ---)", "SKILL.md has no frontmatter (--- name / description ---)"],
|
|
41
|
+
["frontmatter sin `{}:`", "frontmatter has no `{}:`"],
|
|
42
|
+
["`{}:` vac\xEDo", "`{}:` is empty"],
|
|
43
|
+
['`{}:` lleva un ": " sin comillas; entrecomilla el valor o la skill se descarta', '`{}:` has an unquoted ": "; quote the value or the skill is dropped'],
|
|
44
|
+
["asset con nombre inv\xE1lido: {}", "asset with an invalid name: {}"],
|
|
45
|
+
["asset {} sin contentBase64", "asset {} has no contentBase64"],
|
|
46
|
+
["este agente es anterior al revive: recr\xE9alo", "this agent predates revive: recreate it"],
|
|
47
|
+
["falta markdown (el contenido de SKILL.md)", "missing markdown (the SKILL.md content)"],
|
|
48
|
+
["nada que cambiar: systemPrompt y/o systemPromptMode", "nothing to change: systemPrompt and/or systemPromptMode"],
|
|
49
|
+
['agente_sin_maquina: template "{}" no expone el system prompt por archivo', 'agent without a machine: template "{}" does not expose its system prompt as a file'],
|
|
50
|
+
["el cerebro la ve tras el siguiente reinicio: POST \u2026/restart", "the engine sees it after the next restart: POST \u2026/restart"],
|
|
51
|
+
["deja de verla tras el siguiente reinicio: POST \u2026/restart", "it stops seeing it after the next restart: POST \u2026/restart"]
|
|
52
|
+
];
|
|
53
|
+
var FROM_EN = [
|
|
54
|
+
["agent not found", "agente no encontrado"],
|
|
55
|
+
["sandbox not found", "sandbox no encontrado"],
|
|
56
|
+
["Method not allowed", "M\xE9todo no permitido"],
|
|
57
|
+
["template and env required", "faltan template y env"],
|
|
58
|
+
["the file has no template", "el archivo no trae template"],
|
|
59
|
+
["missing values for {}: export them in your environment (a new agent has nothing to keep)", "faltan valores para {}: exp\xF3rtalos en tu entorno (un agente nuevo no tiene nada que conservar)"],
|
|
60
|
+
["agent failed to start: {}", "el agente no arranc\xF3: {}"],
|
|
61
|
+
["Unauthorized", "No autorizado"],
|
|
62
|
+
["Forbidden", "Prohibido"],
|
|
63
|
+
["Rate limit exceeded", "L\xEDmite de peticiones excedido"]
|
|
64
|
+
];
|
|
65
|
+
var compiled = {};
|
|
66
|
+
var escape = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
67
|
+
var compile = (s) => new RegExp(`^${s.split("{}").map(escape).join("(.+?)")}$`, "s");
|
|
68
|
+
function serverText(msg, l = lang()) {
|
|
69
|
+
if (!msg) return msg;
|
|
70
|
+
compiled[l] ??= (l === "en" ? FROM_ES : FROM_EN).map(([from, to]) => ({ re: compile(from), to }));
|
|
71
|
+
for (const c of compiled[l]) {
|
|
72
|
+
const m = c.re.exec(msg);
|
|
73
|
+
if (!m) continue;
|
|
74
|
+
let i = 1;
|
|
75
|
+
return c.to.replace(/\{\}/g, () => m[i++] ?? "");
|
|
76
|
+
}
|
|
77
|
+
return msg;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/errors.ts
|
|
14
81
|
var EXIT = { OK: 0, API: 1, USAGE: 2, AUTH: 3 };
|
|
15
82
|
var CliError = class extends Error {
|
|
16
83
|
constructor(message, exitCode = EXIT.API, hint, code = "error", status) {
|
|
@@ -29,54 +96,54 @@ function usageError(message, usage2) {
|
|
|
29
96
|
return new CliError(
|
|
30
97
|
message,
|
|
31
98
|
EXIT.USAGE,
|
|
32
|
-
usage2 ?
|
|
99
|
+
usage2 ? `${t("Usage", "Uso")}: ${usage2}` : t("Run: easybits --help", "Corre: easybits --help"),
|
|
33
100
|
"usage"
|
|
34
101
|
);
|
|
35
102
|
}
|
|
36
103
|
function notLoggedIn() {
|
|
37
104
|
return new CliError(
|
|
38
|
-
"Not logged in.",
|
|
105
|
+
t("Not logged in.", "No has iniciado sesi\xF3n."),
|
|
39
106
|
EXIT.AUTH,
|
|
40
|
-
"Run: easybits login (or: easybits login - < key-file, or set EASYBITS_API_KEY)",
|
|
107
|
+
t("Run: easybits login (or: easybits login - < key-file, or set EASYBITS_API_KEY)", "Corre: easybits login (o: easybits login - < archivo-con-la-key, o define EASYBITS_API_KEY)"),
|
|
41
108
|
"not_logged_in"
|
|
42
109
|
);
|
|
43
110
|
}
|
|
44
111
|
function apiMessage(body) {
|
|
45
112
|
try {
|
|
46
113
|
const j = JSON.parse(body);
|
|
47
|
-
if (typeof j.error === "string" && typeof j.message === "string") return { message: `${j.error}: ${j.message}`, kind: j.error };
|
|
48
|
-
if (typeof j.code === "string" && typeof j.error === "string") return { message: `${j.code}: ${j.error}`, kind: j.code };
|
|
114
|
+
if (typeof j.error === "string" && typeof j.message === "string") return { message: `${j.error}: ${serverText(j.message)}`, kind: j.error };
|
|
115
|
+
if (typeof j.code === "string" && typeof j.error === "string") return { message: `${j.code}: ${serverText(j.error)}`, kind: j.code };
|
|
49
116
|
const msg = j.message ?? j.error ?? j.detail;
|
|
50
|
-
if (typeof msg === "string") return { message: msg };
|
|
117
|
+
if (typeof msg === "string") return { message: serverText(msg) };
|
|
51
118
|
if (msg) return { message: JSON.stringify(msg) };
|
|
52
119
|
} catch {
|
|
53
120
|
}
|
|
54
|
-
return { message: body.trim() || "empty response" };
|
|
121
|
+
return { message: serverText(body.trim()) || t("empty response", "respuesta vac\xEDa") };
|
|
55
122
|
}
|
|
56
|
-
var KIND_HINTS = {
|
|
57
|
-
SandboxBusy: "A snapshot or fork is running on this sandbox. Wait a few minutes and retry (easybits sb get <id> shows `activity`).",
|
|
58
|
-
SandboxUnreachable: "The sandbox is not answering inside. Wait and retry; if it persists, destroy it.",
|
|
59
|
-
SandboxHostTimeout: "The operation may still be running. Check `easybits sb get <id>` and retry in a few minutes.",
|
|
60
|
-
SandboxHostError: "The sandbox host failed. Retry; if it persists, contact support.",
|
|
61
|
-
SQL_ERROR: "Fix the SQL statement.",
|
|
62
|
-
DATABASE_STORAGE_MISSING: "This database has no storage; its data is unavailable. Delete it (easybits db rm <db> --yes) and create a new one.",
|
|
63
|
-
DATABASE_BACKEND_ERROR: "The database backend failed. Retry in a moment."
|
|
64
|
-
};
|
|
123
|
+
var KIND_HINTS = () => ({
|
|
124
|
+
SandboxBusy: t("A snapshot or fork is running on this sandbox. Wait a few minutes and retry (easybits sb get <id> shows `activity`).", "Hay un snapshot o fork en curso en este sandbox. Espera unos minutos y reintenta (easybits sb get <id> ense\xF1a `activity`)."),
|
|
125
|
+
SandboxUnreachable: t("The sandbox is not answering inside. Wait and retry; if it persists, destroy it.", "El sandbox no contesta por dentro. Espera y reintenta; si sigue, destr\xFAyelo."),
|
|
126
|
+
SandboxHostTimeout: t("The operation may still be running. Check `easybits sb get <id>` and retry in a few minutes.", "La operaci\xF3n puede seguir en curso. Revisa `easybits sb get <id>` y reintenta en unos minutos."),
|
|
127
|
+
SandboxHostError: t("The sandbox host failed. Retry; if it persists, contact support.", "Fall\xF3 el host de sandboxes. Reintenta; si sigue, contacta a soporte."),
|
|
128
|
+
SQL_ERROR: t("Fix the SQL statement.", "Corrige la sentencia SQL."),
|
|
129
|
+
DATABASE_STORAGE_MISSING: t("This database has no storage; its data is unavailable. Delete it (easybits db rm <db> --yes) and create a new one.", "Esta base no tiene almacenamiento; sus datos no est\xE1n disponibles. B\xF3rrala (easybits db rm <db> --yes) y crea otra."),
|
|
130
|
+
DATABASE_BACKEND_ERROR: t("The database backend failed. Retry in a moment.", "Fall\xF3 el backend de la base. Reintenta en un momento.")
|
|
131
|
+
});
|
|
65
132
|
function toCliError(err) {
|
|
66
133
|
if (err instanceof CliError) return err;
|
|
67
134
|
if (err instanceof EasybitsError) {
|
|
68
135
|
const { message: message2, kind } = apiMessage(err.body);
|
|
69
136
|
if (err.status === 401) {
|
|
70
137
|
return new CliError(
|
|
71
|
-
|
|
138
|
+
`${t("Credentials rejected", "Credenciales rechazadas")} (401): ${message2}`,
|
|
72
139
|
EXIT.AUTH,
|
|
73
|
-
"Run: easybits login (or pass a valid API key from https://www.easybits.cloud/dash/developer)",
|
|
140
|
+
t("Run: easybits login (or pass a valid API key from https://www.easybits.cloud/dash/developer)", "Corre: easybits login (o pasa una API key v\xE1lida de https://www.easybits.cloud/dash/developer)"),
|
|
74
141
|
"unauthorized",
|
|
75
142
|
401
|
|
76
143
|
);
|
|
77
144
|
}
|
|
78
|
-
const hint = (kind && KIND_HINTS[kind]) ?? (err.status === 403 ? "Your key may lack the scope for this action (READ/WRITE/DELETE)." : err.status === 404 ? "Check the id. List what you have with the matching `ls` command." : err.status === 402 ? "Plan limit reached. See: easybits usage" : void 0);
|
|
79
|
-
return new CliError(
|
|
145
|
+
const hint = (kind && KIND_HINTS()[kind]) ?? (err.status === 403 ? t("Your key may lack the scope for this action (READ/WRITE/DELETE).", "Tu key quiz\xE1 no tiene el permiso para esto (READ/WRITE/DELETE).") : err.status === 404 ? t("Check the id. List what you have with the matching `ls` command.", "Revisa el id. Enlista lo que tienes con el `ls` que corresponda.") : err.status === 402 ? t("Plan limit reached. See: easybits usage", "Llegaste al l\xEDmite de tu plan. Mira: easybits usage") : void 0);
|
|
146
|
+
return new CliError(`${t("API error", "Error de la API")} ${err.status}: ${message2}`, EXIT.API, hint, "api_error", err.status);
|
|
80
147
|
}
|
|
81
148
|
const message = err instanceof Error ? err.message : String(err);
|
|
82
149
|
return new CliError(message, EXIT.API);
|
|
@@ -85,7 +152,7 @@ function toCliError(err) {
|
|
|
85
152
|
// src/args.ts
|
|
86
153
|
function need(ctx, i, name, usage2) {
|
|
87
154
|
const v = ctx.args[i];
|
|
88
|
-
if (v == null || v === "") throw usageError(`Missing <${name}>.`, usage2);
|
|
155
|
+
if (v == null || v === "") throw usageError(t(`Missing <${name}>.`, `Falta <${name}>.`), usage2);
|
|
89
156
|
return v;
|
|
90
157
|
}
|
|
91
158
|
function str(ctx, key) {
|
|
@@ -103,14 +170,14 @@ function int(ctx, key, usage2) {
|
|
|
103
170
|
const v = str(ctx, key);
|
|
104
171
|
if (v == null) return void 0;
|
|
105
172
|
const n = Number(v);
|
|
106
|
-
if (!Number.isInteger(n) || n < 0) throw usageError(`--${key} must be a non-negative integer.`, usage2);
|
|
173
|
+
if (!Number.isInteger(n) || n < 0) throw usageError(t(`--${key} must be a non-negative integer.`, `--${key} debe ser un entero no negativo.`), usage2);
|
|
107
174
|
return n;
|
|
108
175
|
}
|
|
109
176
|
function pairs(items, usage2) {
|
|
110
177
|
const out = {};
|
|
111
178
|
for (const item of items) {
|
|
112
179
|
const eq = item.indexOf("=");
|
|
113
|
-
if (eq < 1) throw usageError(`Expected KEY=VALUE, got "${item}".`, usage2);
|
|
180
|
+
if (eq < 1) throw usageError(t(`Expected KEY=VALUE, got "${item}".`, `Se esperaba KEY=VALUE y lleg\xF3 "${item}".`), usage2);
|
|
114
181
|
out[item.slice(0, eq)] = item.slice(eq + 1);
|
|
115
182
|
}
|
|
116
183
|
return out;
|
|
@@ -126,10 +193,10 @@ var ENV_FILE_OPTION = {
|
|
|
126
193
|
async function readEnvFile(path, usage2) {
|
|
127
194
|
let text;
|
|
128
195
|
if (path === "-") {
|
|
129
|
-
if (process.stdin.isTTY) throw usageError("--dotenv - expects KEY=VALUE lines on stdin.", usage2);
|
|
196
|
+
if (process.stdin.isTTY) throw usageError(t("--dotenv - expects KEY=VALUE lines on stdin.", "--dotenv - espera renglones KEY=VALUE por stdin."), usage2);
|
|
130
197
|
text = (await readStdin()).toString("utf8");
|
|
131
198
|
} else {
|
|
132
|
-
if (!existsSync(path)) throw usageError(`File not found: ${path}`, usage2);
|
|
199
|
+
if (!existsSync(path)) throw usageError(t(`File not found: ${path}`, `No existe el archivo: ${path}`), usage2);
|
|
133
200
|
text = readFileSync(path, "utf8");
|
|
134
201
|
}
|
|
135
202
|
const out = {};
|
|
@@ -137,7 +204,7 @@ async function readEnvFile(path, usage2) {
|
|
|
137
204
|
const line2 = raw.trim();
|
|
138
205
|
if (!line2 || line2.startsWith("#")) continue;
|
|
139
206
|
const m = /^(?:export\s+)?([A-Za-z_][A-Za-z0-9_.-]*)\s*=\s*(.*)$/.exec(line2);
|
|
140
|
-
if (!m) throw usageError(`${path === "-" ? "stdin" : path}:${n + 1}: expected KEY=VALUE
|
|
207
|
+
if (!m) throw usageError(`${path === "-" ? "stdin" : path}:${n + 1}: ${t("expected KEY=VALUE.", "se esperaba KEY=VALUE.")}`, usage2);
|
|
141
208
|
let v = m[2];
|
|
142
209
|
if (v.length >= 2 && (v[0] === '"' || v[0] === "'") && v.endsWith(v[0])) v = v.slice(1, -1);
|
|
143
210
|
out[m[1]] = v;
|
|
@@ -166,7 +233,29 @@ function fmtDate(s) {
|
|
|
166
233
|
const d = new Date(s);
|
|
167
234
|
return Number.isNaN(d.getTime()) ? s : d.toISOString().slice(0, 16).replace("T", " ");
|
|
168
235
|
}
|
|
169
|
-
|
|
236
|
+
var HEADERS_ES = {
|
|
237
|
+
COLUMNS: "COLUMNAS",
|
|
238
|
+
CREATED: "CREADO",
|
|
239
|
+
DESCRIPTION: "DESCRIPCI\xD3N",
|
|
240
|
+
DOMAIN: "DOMINIO",
|
|
241
|
+
EXPIRES: "VENCE",
|
|
242
|
+
FILES: "ARCHIVOS",
|
|
243
|
+
MESSAGE: "MENSAJE",
|
|
244
|
+
MODIFIED: "MODIFICADO",
|
|
245
|
+
"MXN/MONTH": "MXN/MES",
|
|
246
|
+
NAME: "NOMBRE",
|
|
247
|
+
PATH: "RUTA",
|
|
248
|
+
PORT: "PUERTO",
|
|
249
|
+
ROWS: "FILAS",
|
|
250
|
+
SIZE: "TAMA\xD1O",
|
|
251
|
+
STATUS: "ESTADO",
|
|
252
|
+
TABLE: "TABLA",
|
|
253
|
+
TYPE: "TIPO",
|
|
254
|
+
"URL / COMMAND": "URL / COMANDO",
|
|
255
|
+
VERSION: "VERSI\xD3N"
|
|
256
|
+
};
|
|
257
|
+
function table(rows, cols, empty = t("Nothing here yet.", "Todav\xEDa no hay nada.")) {
|
|
258
|
+
if (lang() === "es") cols = cols.map(([k, l]) => [k, HEADERS_ES[l] ?? l]);
|
|
170
259
|
if (rows.length === 0) {
|
|
171
260
|
console.log(empty);
|
|
172
261
|
return;
|
|
@@ -235,7 +324,7 @@ async function tokenRequest(base, form) {
|
|
|
235
324
|
});
|
|
236
325
|
const body = await res.json().catch(() => ({}));
|
|
237
326
|
if (!res.ok || !body.access_token) {
|
|
238
|
-
throw new CliError(`Login failed (${body.error ?? res.status}).`, EXIT.AUTH, "Run: easybits login", "login_failed", res.status);
|
|
327
|
+
throw new CliError(t(`Login failed (${body.error ?? res.status}).`, `No se pudo entrar (${body.error ?? res.status}).`), EXIT.AUTH, t("Run: easybits login", "Corre: easybits login"), "login_failed", res.status);
|
|
239
328
|
}
|
|
240
329
|
return body;
|
|
241
330
|
}
|
|
@@ -249,7 +338,7 @@ async function fetchEmail(token) {
|
|
|
249
338
|
const base = (await resolveBaseUrl()).replace(/\/+$/, "");
|
|
250
339
|
const res = await fetch(`${base}/api/v2/me`, { headers: { authorization: `Bearer ${token}` } });
|
|
251
340
|
if (res.status === 401) {
|
|
252
|
-
throw new CliError("Credentials rejected (401).", EXIT.AUTH, "Check the key at https://www.easybits.cloud/dash/developer", "unauthorized", 401);
|
|
341
|
+
throw new CliError(t("Credentials rejected (401).", "Credenciales rechazadas (401)."), EXIT.AUTH, t("Check the key at https://www.easybits.cloud/dash/developer", "Revisa la key en https://www.easybits.cloud/dash/developer"), "unauthorized", 401);
|
|
253
342
|
}
|
|
254
343
|
if (!res.ok) return void 0;
|
|
255
344
|
return (await res.json().catch(() => ({}))).email;
|
|
@@ -276,7 +365,7 @@ async function oauthLogin(ctx, opts = {}) {
|
|
|
276
365
|
});
|
|
277
366
|
const code = await new Promise((resolve3, reject) => {
|
|
278
367
|
const timer = setTimeout(
|
|
279
|
-
() => reject(new CliError("Login timed out after 5 minutes.", EXIT.AUTH, "Run: easybits login", "login_timeout")),
|
|
368
|
+
() => reject(new CliError(t("Login timed out after 5 minutes.", "El login se venci\xF3 a los 5 minutos."), EXIT.AUTH, t("Run: easybits login", "Corre: easybits login"), "login_timeout")),
|
|
280
369
|
LOGIN_TIMEOUT_MS
|
|
281
370
|
);
|
|
282
371
|
server.on("request", (req, res) => {
|
|
@@ -300,12 +389,13 @@ async function oauthLogin(ctx, opts = {}) {
|
|
|
300
389
|
});
|
|
301
390
|
if (ctx.json) process.stdout.write(JSON.stringify({ event: "login_url", url }) + "\n");
|
|
302
391
|
if (opts.openBrowser !== false) {
|
|
303
|
-
info("Opening your browser to sign in to EasyBits\u2026");
|
|
392
|
+
info(t("Opening your browser to sign in to EasyBits\u2026", "Abriendo tu navegador para entrar a EasyBits\u2026"));
|
|
304
393
|
openUrl(url);
|
|
305
394
|
}
|
|
306
|
-
info(`If it doesn't open, visit:
|
|
307
|
-
${url}`
|
|
308
|
-
|
|
395
|
+
info(t(`If it doesn't open, visit:
|
|
396
|
+
${url}`, `Si no se abre, entra a:
|
|
397
|
+
${url}`));
|
|
398
|
+
info(t("Waiting for the browser\u2026", "Esperando al navegador\u2026"));
|
|
309
399
|
});
|
|
310
400
|
const tok = await tokenRequest(base, {
|
|
311
401
|
grant_type: "authorization_code",
|
|
@@ -370,14 +460,14 @@ async function resolveCredential(ctx) {
|
|
|
370
460
|
fromSession = false;
|
|
371
461
|
if (rc.apiKey) return rc.apiKey;
|
|
372
462
|
if (!interactive(ctx)) {
|
|
373
|
-
throw new CliError("Your session expired.", EXIT.AUTH, "Run: easybits login (or set EASYBITS_API_KEY)", "session_expired");
|
|
463
|
+
throw new CliError(t("Your session expired.", "Tu sesi\xF3n venci\xF3."), EXIT.AUTH, t("Run: easybits login (or set EASYBITS_API_KEY)", "Corre: easybits login (o define EASYBITS_API_KEY)"), "session_expired");
|
|
374
464
|
}
|
|
375
465
|
} else if (rc.apiKey) {
|
|
376
466
|
return rc.apiKey;
|
|
377
467
|
} else if (!interactive(ctx)) {
|
|
378
468
|
throw notLoggedIn();
|
|
379
469
|
}
|
|
380
|
-
process.stderr.write("Not logged in yet \u2014 let's fix that.\n");
|
|
470
|
+
process.stderr.write(t("Not logged in yet \u2014 let's fix that.\n", "Todav\xEDa no inicias sesi\xF3n \u2014 vamos a arreglarlo.\n"));
|
|
381
471
|
fromSession = true;
|
|
382
472
|
return (await oauthLogin(ctx)).accessToken;
|
|
383
473
|
}
|
|
@@ -409,22 +499,22 @@ var login = {
|
|
|
409
499
|
async run(ctx) {
|
|
410
500
|
let key = ctx.args[0];
|
|
411
501
|
if (key === "-" || bool(ctx, "with-token")) {
|
|
412
|
-
if (process.stdin.isTTY) throw usageError("Pipe the API key on stdin: easybits login - < key.txt", this.usage);
|
|
502
|
+
if (process.stdin.isTTY) throw usageError(t("Pipe the API key on stdin: easybits login - < key.txt", "Pasa la API key por stdin: easybits login - < key.txt"), this.usage);
|
|
413
503
|
key = (await readStdin()).toString("utf8").trim();
|
|
414
|
-
if (!key) throw usageError("Empty API key on stdin.", this.usage);
|
|
504
|
+
if (!key) throw usageError(t("Empty API key on stdin.", "La API key lleg\xF3 vac\xEDa por stdin."), this.usage);
|
|
415
505
|
}
|
|
416
506
|
if (key) {
|
|
417
507
|
const email2 = await fetchEmail(key);
|
|
418
508
|
const { oauth: _drop, ...rest } = readRc();
|
|
419
509
|
writeRc({ ...rest, apiKey: key });
|
|
420
510
|
if (ctx.json) process.stdout.write(JSON.stringify({ event: "logged_in", email: email2, method: "apiKey" }) + "\n");
|
|
421
|
-
else console.log(`Logged in${email2 ? ` as ${email2}` : ""}. API key saved to ~/.easybitsrc`);
|
|
511
|
+
else console.log(t(`Logged in${email2 ? ` as ${email2}` : ""}. API key saved to ~/.easybitsrc`, `Sesi\xF3n iniciada${email2 ? ` como ${email2}` : ""}. API key guardada en ~/.easybitsrc`));
|
|
422
512
|
return;
|
|
423
513
|
}
|
|
424
514
|
const session = await oauthLogin(ctx, { openBrowser: !bool(ctx, "no-browser") });
|
|
425
515
|
const email = await fetchEmail(session.accessToken);
|
|
426
516
|
if (ctx.json) process.stdout.write(JSON.stringify({ event: "logged_in", email, method: "oauth" }) + "\n");
|
|
427
|
-
else console.log(`Logged in${email ? ` as ${email}` : ""}. Session saved to ~/.easybitsrc`);
|
|
517
|
+
else console.log(t(`Logged in${email ? ` as ${email}` : ""}. Session saved to ~/.easybitsrc`, `Sesi\xF3n iniciada${email ? ` como ${email}` : ""}. Sesi\xF3n guardada en ~/.easybitsrc`));
|
|
428
518
|
}
|
|
429
519
|
}
|
|
430
520
|
};
|
|
@@ -440,7 +530,7 @@ var logout = {
|
|
|
440
530
|
async run(ctx) {
|
|
441
531
|
const { oauth: _o, apiKey: _k, ...rest } = readRc();
|
|
442
532
|
writeRc(rest);
|
|
443
|
-
emit(ctx, { ok: true }, () => console.log("Logged out. Removed credentials from ~/.easybitsrc"));
|
|
533
|
+
emit(ctx, { ok: true }, () => console.log(t("Logged out. Removed credentials from ~/.easybitsrc", "Sesi\xF3n cerrada. Se quitaron las credenciales de ~/.easybitsrc")));
|
|
444
534
|
}
|
|
445
535
|
}
|
|
446
536
|
};
|
|
@@ -459,12 +549,17 @@ var usage = {
|
|
|
459
549
|
emit(ctx, u, () => {
|
|
460
550
|
console.log(`Plan: ${u.plan}`);
|
|
461
551
|
console.log(
|
|
462
|
-
`Storage: ${fmtBytes(u.storage.usedBytes)} of ${fmtBytes(u.storage.maxBytes)} (${u.storage.percentUsed}%)`
|
|
552
|
+
t(`Storage: ${fmtBytes(u.storage.usedBytes)} of ${fmtBytes(u.storage.maxBytes)} (${u.storage.percentUsed}%)`, `Espacio: ${fmtBytes(u.storage.usedBytes)} de ${fmtBytes(u.storage.maxBytes)} (${u.storage.percentUsed}%)`)
|
|
463
553
|
);
|
|
464
554
|
console.log(
|
|
465
|
-
|
|
555
|
+
t(
|
|
556
|
+
`Files: ${u.counts.files} (trash: ${u.counts.deletedFiles})
|
|
466
557
|
Websites: ${u.counts.websites}
|
|
558
|
+
Webhooks: ${u.counts.webhooks}`,
|
|
559
|
+
`Archivos: ${u.counts.files} (papelera: ${u.counts.deletedFiles})
|
|
560
|
+
Sitios: ${u.counts.websites}
|
|
467
561
|
Webhooks: ${u.counts.webhooks}`
|
|
562
|
+
)
|
|
468
563
|
);
|
|
469
564
|
});
|
|
470
565
|
}
|
|
@@ -490,7 +585,7 @@ var websites = {
|
|
|
490
585
|
() => table(
|
|
491
586
|
items.map((w) => ({ ...w, size: fmtBytes(w.totalSize), createdAt: fmtDate(w.createdAt) })),
|
|
492
587
|
[["id", "ID"], ["name", "NAME"], ["status", "STATUS"], ["fileCount", "FILES"], ["size", "SIZE"], ["url", "URL"]],
|
|
493
|
-
"No websites yet."
|
|
588
|
+
t("No websites yet.", "Todav\xEDa no hay sitios.")
|
|
494
589
|
)
|
|
495
590
|
);
|
|
496
591
|
}
|
|
@@ -513,8 +608,8 @@ var providers = {
|
|
|
513
608
|
note: "Use the Developer Dashboard to add custom providers."
|
|
514
609
|
};
|
|
515
610
|
emit(ctx, data, () => {
|
|
516
|
-
console.log("Default provider: Tigris (platform)");
|
|
517
|
-
console.log("Use the Developer Dashboard to add custom providers.");
|
|
611
|
+
console.log(t("Default provider: Tigris (platform)", "Proveedor por default: Tigris (plataforma)"));
|
|
612
|
+
console.log(t("Use the Developer Dashboard to add custom providers.", "Agrega proveedores propios en el Developer Dashboard."));
|
|
518
613
|
});
|
|
519
614
|
}
|
|
520
615
|
}
|
|
@@ -569,7 +664,7 @@ var whoami = {
|
|
|
569
664
|
const source = process.env.EASYBITS_API_KEY ? "env EASYBITS_API_KEY" : ctx.token ? "--token" : readRc().oauth?.accessToken ? "browser session (~/.easybitsrc)" : readRc().apiKey ? "API key (~/.easybitsrc)" : void 0;
|
|
570
665
|
if (!source) throw notLoggedIn();
|
|
571
666
|
const email = await fetchEmail(await resolveCredential(ctx));
|
|
572
|
-
emit(ctx, { email, source, baseUrl: await resolveBaseUrl3() }, () => console.log(`${email ?? "(unknown email)"} via ${source}`));
|
|
667
|
+
emit(ctx, { email, source, baseUrl: await resolveBaseUrl3() }, () => console.log(`${email ?? t("(unknown email)", "(correo desconocido)")} ${t("via", "por")} ${source}`));
|
|
573
668
|
}
|
|
574
669
|
}
|
|
575
670
|
};
|
|
@@ -600,21 +695,21 @@ async function ask(question) {
|
|
|
600
695
|
}
|
|
601
696
|
function requireYesIfHeadless(ctx) {
|
|
602
697
|
if (bool(ctx, "yes") || interactive(ctx)) return;
|
|
603
|
-
throw new CliError("--yes required when not running interactively.", EXIT.USAGE,
|
|
698
|
+
throw new CliError(t("--yes required when not running interactively.", "Sin terminal hace falta --yes."), EXIT.USAGE, `${t("Run", "Corre")}: ${sameCommand()} --yes`, "usage");
|
|
604
699
|
}
|
|
605
700
|
async function confirm(ctx, action, opts = {}) {
|
|
606
701
|
if (bool(ctx, "yes")) return;
|
|
607
702
|
requireYesIfHeadless(ctx);
|
|
608
703
|
if (opts.typeName) {
|
|
609
704
|
process.stderr.write(`${action}
|
|
610
|
-
This cannot be undone.
|
|
705
|
+
${t("This cannot be undone.", "No se puede deshacer.")}
|
|
611
706
|
`);
|
|
612
|
-
const answer2 = await ask(`Type "${opts.typeName}" to confirm: `);
|
|
613
|
-
if (answer2 !== opts.typeName) throw new CliError("Aborted: the name did not match.", EXIT.USAGE, void 0, "aborted");
|
|
707
|
+
const answer2 = await ask(t(`Type "${opts.typeName}" to confirm: `, `Teclea "${opts.typeName}" para confirmar: `));
|
|
708
|
+
if (answer2 !== opts.typeName) throw new CliError(t("Aborted: the name did not match.", "Cancelado: el nombre no coincide."), EXIT.USAGE, void 0, "aborted");
|
|
614
709
|
return;
|
|
615
710
|
}
|
|
616
|
-
const answer = await ask(`${action} [y/N] `);
|
|
617
|
-
if (!/^y(es)
|
|
711
|
+
const answer = await ask(`${action} ${t("[y/N]", "[s/N]")} `);
|
|
712
|
+
if (!/^(y(es)?|s[ií]?)$/i.test(answer)) throw new CliError(t("Aborted.", "Cancelado."), EXIT.USAGE, void 0, "aborted");
|
|
618
713
|
}
|
|
619
714
|
|
|
620
715
|
// src/commands/files.ts
|
|
@@ -657,7 +752,7 @@ var files = {
|
|
|
657
752
|
() => table(
|
|
658
753
|
data.items.map((f) => ({ ...f, size: fmtBytes(f.size) })),
|
|
659
754
|
[["name", "NAME"], ["size", "SIZE"], ["status", "STATUS"], ["id", "ID"]],
|
|
660
|
-
"No files found"
|
|
755
|
+
t("No files found", "No hay archivos")
|
|
661
756
|
)
|
|
662
757
|
);
|
|
663
758
|
}
|
|
@@ -668,7 +763,7 @@ var files = {
|
|
|
668
763
|
examples: ["easybits files upload ./report.pdf", "easybits files upload ./logo.png --json"],
|
|
669
764
|
async run(ctx) {
|
|
670
765
|
const path = need(ctx, 0, "path", this.usage);
|
|
671
|
-
if (!existsSync2(path)) throw usageError(`File not found: ${path}`, this.usage);
|
|
766
|
+
if (!existsSync2(path)) throw usageError(t(`File not found: ${path}`, `No existe el archivo: ${path}`), this.usage);
|
|
672
767
|
const size = statSync(path).size;
|
|
673
768
|
const contentType = MIME[extname(path).slice(1).toLowerCase()] || "application/octet-stream";
|
|
674
769
|
const eb = await getClient(ctx);
|
|
@@ -678,8 +773,8 @@ var files = {
|
|
|
678
773
|
body: readFileSync3(path),
|
|
679
774
|
headers: { "Content-Type": contentType }
|
|
680
775
|
});
|
|
681
|
-
if (!res.ok) throw new CliError(`Upload failed: storage answered ${res.status}`, 1, void 0, "upload_failed", res.status);
|
|
682
|
-
emit(ctx, data.file, () => console.log(
|
|
776
|
+
if (!res.ok) throw new CliError(t(`Upload failed: storage answered ${res.status}`, `Fall\xF3 la subida: el almacenamiento contest\xF3 ${res.status}`), 1, void 0, "upload_failed", res.status);
|
|
777
|
+
emit(ctx, data.file, () => console.log(`${t("Uploaded", "Subido")}: ${data.file.id}`));
|
|
683
778
|
}
|
|
684
779
|
},
|
|
685
780
|
rm: {
|
|
@@ -691,10 +786,10 @@ var files = {
|
|
|
691
786
|
async run(ctx) {
|
|
692
787
|
const id = need(ctx, 0, "file-id", this.usage);
|
|
693
788
|
requireYesIfHeadless(ctx);
|
|
694
|
-
await confirm(ctx, `Delete file ${id}? (recoverable from trash for 7 days)`);
|
|
789
|
+
await confirm(ctx, t(`Delete file ${id}? (recoverable from trash for 7 days)`, `\xBFBorrar el archivo ${id}? (se recupera de la papelera por 7 d\xEDas)`));
|
|
695
790
|
const eb = await getClient(ctx);
|
|
696
791
|
const r = await eb.deleteFile(id);
|
|
697
|
-
emit(ctx, r, () => console.log("Deleted"));
|
|
792
|
+
emit(ctx, r, () => console.log(t("Deleted", "Borrado")));
|
|
698
793
|
}
|
|
699
794
|
}
|
|
700
795
|
}
|
|
@@ -719,7 +814,7 @@ var init = {
|
|
|
719
814
|
const appDir = str(ctx, "app-dir") ?? "/srv/app";
|
|
720
815
|
const port = str(ctx, "port") ?? "3000";
|
|
721
816
|
if (!existsSync3("package.json")) {
|
|
722
|
-
throw usageError("No package.json here. Run this at the root of your app's repo.", this.usage);
|
|
817
|
+
throw usageError(t("No package.json here. Run this at the root of your app's repo.", "Aqu\xED no hay package.json. C\xF3rrelo en la ra\xEDz del repo de tu app."), this.usage);
|
|
723
818
|
}
|
|
724
819
|
const workflow = `# Despliegue a EasyBits en cada push a main.
|
|
725
820
|
#
|
|
@@ -857,7 +952,7 @@ jobs:
|
|
|
857
952
|
writeFileSync2(".github/workflows/easybits-deploy.yml", workflow);
|
|
858
953
|
writeFileSync2(".github/scripts/easybits-deploy.mjs", script);
|
|
859
954
|
const written = [".github/workflows/easybits-deploy.yml", ".github/scripts/easybits-deploy.mjs"];
|
|
860
|
-
emit(ctx, { written, appDir, port: Number(port) }, () => console.log(`Done. Wrote:
|
|
955
|
+
emit(ctx, { written, appDir, port: Number(port) }, () => console.log(t(`Done. Wrote:
|
|
861
956
|
.github/workflows/easybits-deploy.yml
|
|
862
957
|
.github/scripts/easybits-deploy.mjs
|
|
863
958
|
|
|
@@ -887,7 +982,37 @@ One-time setup left:
|
|
|
887
982
|
|
|
888
983
|
easybits machines secrets set SANDBOX_ID --dotenv .env.production
|
|
889
984
|
|
|
890
|
-
From then on, every push to main deploys
|
|
985
|
+
From then on, every push to main deploys.`, `Listo. Escrib\xED:
|
|
986
|
+
.github/workflows/easybits-deploy.yml
|
|
987
|
+
.github/scripts/easybits-deploy.mjs
|
|
988
|
+
|
|
989
|
+
Falta configurar una sola vez:
|
|
990
|
+
|
|
991
|
+
1. Crea la m\xE1quina (si todav\xEDa no tienes una):
|
|
992
|
+
|
|
993
|
+
curl -X POST https://www.easybits.cloud/api/v2/machines/launch \\
|
|
994
|
+
-H "Authorization: Bearer $EASYBITS_API_KEY" \\
|
|
995
|
+
-H "Content-Type: application/json" \\
|
|
996
|
+
-d '{"repo":"https://github.com/TU/REPO.git","branch":"main",
|
|
997
|
+
"tier":"micro","template":"node","appDir":"${appDir}","port":${port}}'
|
|
998
|
+
|
|
999
|
+
Repo privado: deja la URL limpia y pasa el token aparte,
|
|
1000
|
+
"repoToken":"github_pat_\u2026"
|
|
1001
|
+
(un token dentro de la URL se rechaza).
|
|
1002
|
+
|
|
1003
|
+
\xBFTu build cabe en la m\xE1quina? Entonces no necesitas este workflow:
|
|
1004
|
+
POST /machines/SANDBOX_ID/push-deploy te da un webhook de GitHub
|
|
1005
|
+
y cada push se despliega solo. Docs: https://www.easybits.cloud/docs
|
|
1006
|
+
|
|
1007
|
+
2. Agrega estos secretos del repo (Settings \u2192 Secrets and variables \u2192 Actions):
|
|
1008
|
+
EASYBITS_API_KEY tu key
|
|
1009
|
+
EASYBITS_SANDBOX_ID el id que devolvi\xF3 el paso 1
|
|
1010
|
+
|
|
1011
|
+
3. Si tu app usa variables de entorno secretas:
|
|
1012
|
+
|
|
1013
|
+
easybits machines secrets set SANDBOX_ID --dotenv .env.production
|
|
1014
|
+
|
|
1015
|
+
Desde ah\xED, cada push a main despliega.`)));
|
|
891
1016
|
}
|
|
892
1017
|
}
|
|
893
1018
|
};
|
|
@@ -918,7 +1043,7 @@ var sshKey = {
|
|
|
918
1043
|
execFileSync("ssh-keygen", ["-t", "ed25519", "-N", "", "-f", key, "-C", "easybits"], {
|
|
919
1044
|
stdio: ["ignore", "ignore", "inherit"]
|
|
920
1045
|
});
|
|
921
|
-
console.error(`easybits: key created at ${key}`);
|
|
1046
|
+
console.error(t(`easybits: key created at ${key}`, `easybits: llave creada en ${key}`));
|
|
922
1047
|
}
|
|
923
1048
|
const publicKey = readFileSync4(pub, "utf8").trim();
|
|
924
1049
|
if (ctx.json) process.stdout.write(JSON.stringify({ publicKey, path: pub }, null, 2) + "\n");
|
|
@@ -941,9 +1066,9 @@ var sshProxy = {
|
|
|
941
1066
|
const eb = await getClient(ctx);
|
|
942
1067
|
if (!sandboxId.startsWith("sb_")) {
|
|
943
1068
|
const hits = (await eb.sandboxes.list()).filter((s) => s.name === sandboxId);
|
|
944
|
-
if (hits.length === 0) throw usageError(`No sandbox named "${sandboxId}".`, this.usage);
|
|
1069
|
+
if (hits.length === 0) throw usageError(t(`No sandbox named "${sandboxId}".`, `No hay un sandbox llamado "${sandboxId}".`), this.usage);
|
|
945
1070
|
if (hits.length > 1)
|
|
946
|
-
throw usageError(`"${sandboxId}" is ambiguous: ${hits.length} sandboxes use it; pass the sandboxId.`, this.usage);
|
|
1071
|
+
throw usageError(t(`"${sandboxId}" is ambiguous: ${hits.length} sandboxes use it; pass the sandboxId.`, `"${sandboxId}" es ambiguo: lo usan ${hits.length} sandboxes; pasa el sandboxId.`), this.usage);
|
|
947
1072
|
sandboxId = hits[0].sandboxId;
|
|
948
1073
|
}
|
|
949
1074
|
const sb = await eb.sandboxes.get(sandboxId);
|
|
@@ -952,10 +1077,10 @@ var sshProxy = {
|
|
|
952
1077
|
ws.binaryType = "arraybuffer";
|
|
953
1078
|
await new Promise((resolve3, reject) => {
|
|
954
1079
|
ws.onopen = () => resolve3();
|
|
955
|
-
ws.onerror = () => reject(new CliError("easybits ssh-proxy: could not open the tunnel"));
|
|
1080
|
+
ws.onerror = () => reject(new CliError(t("easybits ssh-proxy: could not open the tunnel", "easybits ssh-proxy: no se pudo abrir el t\xFAnel")));
|
|
956
1081
|
});
|
|
957
1082
|
ws.onerror = () => {
|
|
958
|
-
console.error("easybits ssh-proxy: tunnel error");
|
|
1083
|
+
console.error(t("easybits ssh-proxy: tunnel error", "easybits ssh-proxy: error del t\xFAnel"));
|
|
959
1084
|
process.exit(1);
|
|
960
1085
|
};
|
|
961
1086
|
ws.onclose = () => process.exit(0);
|
|
@@ -991,13 +1116,13 @@ function sandboxRecord(s) {
|
|
|
991
1116
|
}
|
|
992
1117
|
function printSandbox(s) {
|
|
993
1118
|
console.log(`ID: ${s.sandboxId}`);
|
|
994
|
-
if (s.name) console.log(
|
|
1119
|
+
if (s.name) console.log(`${t("Name: ", "Nombre: ")} ${s.name}`);
|
|
995
1120
|
console.log(`Template: ${s.template}`);
|
|
996
|
-
console.log(
|
|
997
|
-
if (s.activity) console.log(`Activity: ${s.activity} (busy: exec/suspend/destroy answer 409 SandboxBusy until it ends)`);
|
|
998
|
-
console.log(
|
|
999
|
-
console.log(
|
|
1000
|
-
if (s.tier) console.log(`Tier: ${s.tier}${s.monthlyMxn != null ? ` ($${s.monthlyMxn} MXN
|
|
1121
|
+
console.log(`${t("Status: ", "Estado: ")} ${s.status}`);
|
|
1122
|
+
if (s.activity) console.log(t(`Activity: ${s.activity} (busy: exec/suspend/destroy answer 409 SandboxBusy until it ends)`, `Actividad: ${s.activity} (ocupado: exec/suspend/destroy contestan 409 SandboxBusy hasta que termine)`));
|
|
1123
|
+
console.log(`${t("Created: ", "Creado: ")} ${fmtDate(s.createdAt)}`);
|
|
1124
|
+
console.log(`${t("Expires: ", "Vence: ")} ${s.persistent ? t("never (permanent)", "nunca (permanente)") : fmtDate(s.expiresAt)}`);
|
|
1125
|
+
if (s.tier) console.log(`Tier: ${s.tier}${s.monthlyMxn != null ? ` ($${s.monthlyMxn} MXN/${t("month", "mes")})` : ""}`);
|
|
1001
1126
|
}
|
|
1002
1127
|
async function sandbox(ctx, usage2) {
|
|
1003
1128
|
const id = need(ctx, 0, "sandbox", usage2);
|
|
@@ -1026,7 +1151,7 @@ var sandboxes = {
|
|
|
1026
1151
|
() => table(
|
|
1027
1152
|
rows.map((r) => ({ ...r, expires: r.persistent ? "permanent" : fmtDate(r.expiresAt) })),
|
|
1028
1153
|
[["sandboxId", "ID"], ["name", "NAME"], ["template", "TEMPLATE"], ["status", "STATUS"], ["expires", "EXPIRES"]],
|
|
1029
|
-
"No sandboxes. Create one: easybits sandboxes create --template node"
|
|
1154
|
+
t("No sandboxes. Create one: easybits sandboxes create --template node", "No hay sandboxes. Crea uno: easybits sandboxes create --template node")
|
|
1030
1155
|
)
|
|
1031
1156
|
);
|
|
1032
1157
|
}
|
|
@@ -1051,7 +1176,7 @@ var sandboxes = {
|
|
|
1051
1176
|
],
|
|
1052
1177
|
async run(ctx) {
|
|
1053
1178
|
const size = str(ctx, "size");
|
|
1054
|
-
if (size && !["s", "m", "l", "xl"].includes(size)) throw usageError("--size must be s, m, l or xl.", this.usage);
|
|
1179
|
+
if (size && !["s", "m", "l", "xl"].includes(size)) throw usageError(t("--size must be s, m, l or xl.", "--size debe ser s, m, l o xl."), this.usage);
|
|
1055
1180
|
const env = await envFrom(ctx, this.usage);
|
|
1056
1181
|
const eb = await getClient(ctx);
|
|
1057
1182
|
const sb = await eb.sandboxes.create({
|
|
@@ -1094,7 +1219,7 @@ var sandboxes = {
|
|
|
1094
1219
|
async run(ctx) {
|
|
1095
1220
|
need(ctx, 0, "sandbox", this.usage);
|
|
1096
1221
|
const command = ctx.args.slice(1).join(" ");
|
|
1097
|
-
if (!command) throw usageError("Missing <command>.", this.usage);
|
|
1222
|
+
if (!command) throw usageError(t("Missing <command>.", "Falta <command>."), this.usage);
|
|
1098
1223
|
const env = await envFrom(ctx, this.usage);
|
|
1099
1224
|
const sb = await sandbox(ctx, this.usage);
|
|
1100
1225
|
const r = await sb.exec(command, {
|
|
@@ -1109,7 +1234,7 @@ var sandboxes = {
|
|
|
1109
1234
|
}
|
|
1110
1235
|
if (r.stdout) process.stdout.write(r.stdout);
|
|
1111
1236
|
if (r.stderr) process.stderr.write(r.stderr);
|
|
1112
|
-
if (r.truncated) console.error("easybits: output truncated");
|
|
1237
|
+
if (r.truncated) console.error(t("easybits: output truncated", "easybits: salida recortada"));
|
|
1113
1238
|
process.exitCode = r.exitCode;
|
|
1114
1239
|
}
|
|
1115
1240
|
},
|
|
@@ -1170,7 +1295,7 @@ var sandboxes = {
|
|
|
1170
1295
|
if (out) {
|
|
1171
1296
|
const r = await sb.files.read(path, { encoding: "base64" });
|
|
1172
1297
|
writeFileSync3(out, Buffer.from(r.content, "base64"));
|
|
1173
|
-
emit(ctx, { path, out, size: r.size }, () => console.error(`Saved ${fmtBytes(r.size)} to ${out}`));
|
|
1298
|
+
emit(ctx, { path, out, size: r.size }, () => console.error(t(`Saved ${fmtBytes(r.size)} to ${out}`, `Guard\xE9 ${fmtBytes(r.size)} en ${out}`)));
|
|
1174
1299
|
} else {
|
|
1175
1300
|
const r = await sb.files.read(path);
|
|
1176
1301
|
emit(ctx, r, () => process.stdout.write(r.content));
|
|
@@ -1181,14 +1306,14 @@ var sandboxes = {
|
|
|
1181
1306
|
let buf;
|
|
1182
1307
|
if (inline != null) buf = Buffer.from(inline, "utf8");
|
|
1183
1308
|
else if (local) {
|
|
1184
|
-
if (!existsSync5(local)) throw usageError(`File not found: ${local}`, FILES_USAGE);
|
|
1309
|
+
if (!existsSync5(local)) throw usageError(t(`File not found: ${local}`, `No existe el archivo: ${local}`), FILES_USAGE);
|
|
1185
1310
|
buf = readFileSync5(local);
|
|
1186
1311
|
} else if (!process.stdin.isTTY) buf = await readStdin();
|
|
1187
|
-
else throw usageError("Give a local file, --content, or pipe data on stdin.", FILES_USAGE);
|
|
1312
|
+
else throw usageError(t("Give a local file, --content, or pipe data on stdin.", "Pasa un archivo local, --content, o datos por stdin."), FILES_USAGE);
|
|
1188
1313
|
const r = await sb.files.write(path, buf.toString("base64"), { encoding: "base64" });
|
|
1189
|
-
emit(ctx, { path, ...r }, () => console.log(`Wrote ${fmtBytes(r.bytes)} to ${path}`));
|
|
1314
|
+
emit(ctx, { path, ...r }, () => console.log(t(`Wrote ${fmtBytes(r.bytes)} to ${path}`, `Escrib\xED ${fmtBytes(r.bytes)} en ${path}`)));
|
|
1190
1315
|
} else {
|
|
1191
|
-
throw usageError(`Unknown files action "${action}".`, FILES_USAGE);
|
|
1316
|
+
throw usageError(t(`Unknown files action "${action}".`, `Acci\xF3n de files desconocida "${action}".`), FILES_USAGE);
|
|
1192
1317
|
}
|
|
1193
1318
|
}
|
|
1194
1319
|
},
|
|
@@ -1198,7 +1323,7 @@ var sandboxes = {
|
|
|
1198
1323
|
examples: ["easybits sandboxes suspend sb_abc123"],
|
|
1199
1324
|
async run(ctx) {
|
|
1200
1325
|
const r = await (await sandbox(ctx, this.usage)).suspend();
|
|
1201
|
-
emit(ctx, r, () => console.log(`Suspended ${r.sandboxId ?? ctx.args[0]} (${r.status ?? "suspended"})`));
|
|
1326
|
+
emit(ctx, r, () => console.log(t(`Suspended ${r.sandboxId ?? ctx.args[0]} (${r.status ?? "suspended"})`, `Suspendido ${r.sandboxId ?? ctx.args[0]} (${r.status ?? "suspended"})`)));
|
|
1202
1327
|
}
|
|
1203
1328
|
},
|
|
1204
1329
|
resume: {
|
|
@@ -1212,7 +1337,7 @@ var sandboxes = {
|
|
|
1212
1337
|
async run(ctx) {
|
|
1213
1338
|
const env = await envFrom(ctx, this.usage);
|
|
1214
1339
|
const r = await (await sandbox(ctx, this.usage)).resume(Object.keys(env).length ? { env } : {});
|
|
1215
|
-
emit(ctx, r, () => console.log(`Resumed ${r.sandboxId ?? ctx.args[0]} (${r.status ?? "running"})`));
|
|
1340
|
+
emit(ctx, r, () => console.log(t(`Resumed ${r.sandboxId ?? ctx.args[0]} (${r.status ?? "running"})`, `Despierto ${r.sandboxId ?? ctx.args[0]} (${r.status ?? "running"})`)));
|
|
1216
1341
|
}
|
|
1217
1342
|
},
|
|
1218
1343
|
destroy: {
|
|
@@ -1227,11 +1352,11 @@ var sandboxes = {
|
|
|
1227
1352
|
const sb = await sandbox(ctx, this.usage);
|
|
1228
1353
|
await confirm(
|
|
1229
1354
|
ctx,
|
|
1230
|
-
sb.persistent ? `Destroy permanent sandbox ${sb.sandboxId}${sb.name ? ` (${sb.name})` : ""} and its disk?` : `Destroy sandbox ${sb.sandboxId}?`,
|
|
1355
|
+
sb.persistent ? t(`Destroy permanent sandbox ${sb.sandboxId}${sb.name ? ` (${sb.name})` : ""} and its disk?`, `\xBFDestruir el sandbox permanente ${sb.sandboxId}${sb.name ? ` (${sb.name})` : ""} y su disco?`) : t(`Destroy sandbox ${sb.sandboxId}?`, `\xBFDestruir el sandbox ${sb.sandboxId}?`),
|
|
1231
1356
|
sb.persistent ? { typeName: sb.sandboxId } : {}
|
|
1232
1357
|
);
|
|
1233
1358
|
const r = await sb.destroy();
|
|
1234
|
-
emit(ctx, { ...r, sandboxId: ctx.args[0] }, () => console.log(`Destroyed ${ctx.args[0]}`));
|
|
1359
|
+
emit(ctx, { ...r, sandboxId: ctx.args[0] }, () => console.log(t(`Destroyed ${ctx.args[0]}`, `Destruido ${ctx.args[0]}`)));
|
|
1235
1360
|
}
|
|
1236
1361
|
},
|
|
1237
1362
|
snapshot: {
|
|
@@ -1270,7 +1395,7 @@ var machines = {
|
|
|
1270
1395
|
() => table(
|
|
1271
1396
|
rows,
|
|
1272
1397
|
[["sandboxId", "ID"], ["name", "NAME"], ["tier", "TIER"], ["status", "STATUS"], ["monthlyMxn", "MXN/MONTH"]],
|
|
1273
|
-
"No permanent machines. See: easybits docs hosting"
|
|
1398
|
+
t("No permanent machines. See: easybits docs hosting", "No hay m\xE1quinas permanentes. Mira: easybits docs hosting")
|
|
1274
1399
|
)
|
|
1275
1400
|
);
|
|
1276
1401
|
}
|
|
@@ -1302,7 +1427,7 @@ var machines = {
|
|
|
1302
1427
|
() => table(
|
|
1303
1428
|
r.items.map((x) => ({ ...x, size: fmtBytes(x.sizeBytes), createdAt: fmtDate(x.createdAt) })),
|
|
1304
1429
|
[["version", "VERSION"], ["releaseId", "ID"], ["status", "STATUS"], ["size", "SIZE"], ["createdAt", "CREATED"], ["message", "MESSAGE"]],
|
|
1305
|
-
"No releases yet. Publish one: easybits machines deploy <machine>"
|
|
1430
|
+
t("No releases yet. Publish one: easybits machines deploy <machine>", "Todav\xEDa no hay releases. Publica uno: easybits deploy <m\xE1quina>")
|
|
1306
1431
|
)
|
|
1307
1432
|
);
|
|
1308
1433
|
}
|
|
@@ -1331,7 +1456,7 @@ var machines = {
|
|
|
1331
1456
|
const rel = need(ctx, 1, "release-id", this.usage);
|
|
1332
1457
|
const eb = await getClient(ctx);
|
|
1333
1458
|
const r = await eb.machines.rollback(id, rel);
|
|
1334
|
-
emit(ctx, r, () => console.log(`Rolled back to v${r.version} (exit ${r.exitCode})`));
|
|
1459
|
+
emit(ctx, r, () => console.log(t(`Rolled back to v${r.version} (exit ${r.exitCode})`, `De vuelta en v${r.version} (salida ${r.exitCode})`)));
|
|
1335
1460
|
if (r.exitCode !== 0) process.exitCode = 1;
|
|
1336
1461
|
}
|
|
1337
1462
|
},
|
|
@@ -1352,21 +1477,21 @@ var machines = {
|
|
|
1352
1477
|
if (action === "ls" || action === "list") {
|
|
1353
1478
|
const r = await eb.machines.secrets(id);
|
|
1354
1479
|
emit(ctx, r, () => {
|
|
1355
|
-
console.log(
|
|
1356
|
-
console.log(
|
|
1480
|
+
console.log(`${t("Injected", "Inyectados")}: ${r.secretNames.length ? r.secretNames.join(", ") : t("(none)", "(ninguno)")}`);
|
|
1481
|
+
console.log(`${t("In vault", "En el vault")}: ${r.inVault.length ? r.inVault.join(", ") : t("(none)", "(ninguno)")}`);
|
|
1357
1482
|
});
|
|
1358
1483
|
} else if (action === "set") {
|
|
1359
1484
|
const file = str(ctx, "dotenv");
|
|
1360
1485
|
const values = { ...file ? await readEnvFile(file, SECRETS_USAGE) : {}, ...pairs(ctx.args.slice(2), SECRETS_USAGE) };
|
|
1361
|
-
if (!Object.keys(values).length) throw usageError("Give --dotenv <path> (or - for stdin).", SECRETS_USAGE);
|
|
1486
|
+
if (!Object.keys(values).length) throw usageError(t("Give --dotenv <path> (or - for stdin).", "Pasa --dotenv <ruta> (o - para stdin)."), SECRETS_USAGE);
|
|
1362
1487
|
const r = await eb.machines.setSecrets(id, values);
|
|
1363
|
-
emit(ctx, r, () => console.log(`Set ${Object.keys(values).join(", ")}. Injected now: ${r.secretNames.join(", ")}`));
|
|
1488
|
+
emit(ctx, r, () => console.log(t(`Set ${Object.keys(values).join(", ")}. Injected now: ${r.secretNames.join(", ")}`, `Guardados ${Object.keys(values).join(", ")}. Inyectados ahora: ${r.secretNames.join(", ")}`)));
|
|
1364
1489
|
} else if (action === "unset") {
|
|
1365
1490
|
const name = need(ctx, 2, "NAME", SECRETS_USAGE);
|
|
1366
1491
|
const r = await eb.machines.unsetSecret(id, name);
|
|
1367
|
-
emit(ctx, r, () => console.log(`Unset ${name} (value kept in the vault)`));
|
|
1492
|
+
emit(ctx, r, () => console.log(t(`Unset ${name} (value kept in the vault)`, `Quitado ${name} (el valor sigue en el vault)`)));
|
|
1368
1493
|
} else {
|
|
1369
|
-
throw usageError(`Unknown secrets action "${action}".`, SECRETS_USAGE);
|
|
1494
|
+
throw usageError(t(`Unknown secrets action "${action}".`, `Acci\xF3n de secrets desconocida "${action}".`), SECRETS_USAGE);
|
|
1370
1495
|
}
|
|
1371
1496
|
}
|
|
1372
1497
|
}
|
|
@@ -1392,7 +1517,7 @@ var domains = {
|
|
|
1392
1517
|
examples: ["easybits domains ls sb_abc123"],
|
|
1393
1518
|
async run(ctx) {
|
|
1394
1519
|
const items = await (await sandbox2(ctx, this.usage)).listDomains();
|
|
1395
|
-
emit(ctx, items, () => table(items, [["domain", "DOMAIN"], ["port", "PORT"]], "No custom domains."));
|
|
1520
|
+
emit(ctx, items, () => table(items, [["domain", "DOMAIN"], ["port", "PORT"]], t("No custom domains.", "Sin dominios propios.")));
|
|
1396
1521
|
}
|
|
1397
1522
|
},
|
|
1398
1523
|
add: {
|
|
@@ -1403,12 +1528,12 @@ var domains = {
|
|
|
1403
1528
|
async run(ctx) {
|
|
1404
1529
|
const domain = need(ctx, 1, "domain", this.usage);
|
|
1405
1530
|
const port = int(ctx, "port", this.usage);
|
|
1406
|
-
if (port == null) throw usageError("Missing --port.", this.usage);
|
|
1531
|
+
if (port == null) throw usageError(t("Missing --port.", "Falta --port."), this.usage);
|
|
1407
1532
|
const r = await (await sandbox2(ctx, this.usage)).addDomain(domain, port);
|
|
1408
1533
|
emit(ctx, r, () => {
|
|
1409
|
-
console.log(`Attached ${r.domain} \u2192 port ${r.port}`);
|
|
1410
|
-
console.log(`Create this DNS record: ${JSON.stringify(r.dns)}`);
|
|
1411
|
-
console.log(`Then run: easybits domains verify ${ctx.args[0]} ${r.domain}`);
|
|
1534
|
+
console.log(t(`Attached ${r.domain} \u2192 port ${r.port}`, `Ligado ${r.domain} \u2192 puerto ${r.port}`));
|
|
1535
|
+
console.log(t(`Create this DNS record: ${JSON.stringify(r.dns)}`, `Crea este registro DNS: ${JSON.stringify(r.dns)}`));
|
|
1536
|
+
console.log(t(`Then run: easybits domains verify ${ctx.args[0]} ${r.domain}`, `Luego corre: easybits domains verify ${ctx.args[0]} ${r.domain}`));
|
|
1412
1537
|
});
|
|
1413
1538
|
}
|
|
1414
1539
|
},
|
|
@@ -1420,8 +1545,8 @@ var domains = {
|
|
|
1420
1545
|
const domain = need(ctx, 1, "domain", this.usage);
|
|
1421
1546
|
const r = await (await sandbox2(ctx, this.usage)).verifyDomain(domain);
|
|
1422
1547
|
emit(ctx, r, () => {
|
|
1423
|
-
console.log(`${r.domain}: ${r.ready ? "ready" : "not ready yet"}`);
|
|
1424
|
-
console.log(`DNS resolved: ${r.dns.resolved} HTTPS ok: ${r.https.ok}`);
|
|
1548
|
+
console.log(`${r.domain}: ${r.ready ? t("ready", "listo") : t("not ready yet", "todav\xEDa no est\xE1 listo")}`);
|
|
1549
|
+
console.log(t(`DNS resolved: ${r.dns.resolved} HTTPS ok: ${r.https.ok}`, `DNS resuelve: ${r.dns.resolved} HTTPS ok: ${r.https.ok}`));
|
|
1425
1550
|
if (r.hint) console.log(r.hint);
|
|
1426
1551
|
});
|
|
1427
1552
|
if (!r.ready) process.exitCode = 1;
|
|
@@ -1437,9 +1562,9 @@ var domains = {
|
|
|
1437
1562
|
need(ctx, 0, "sandbox", this.usage);
|
|
1438
1563
|
const domain = need(ctx, 1, "domain", this.usage);
|
|
1439
1564
|
requireYesIfHeadless(ctx);
|
|
1440
|
-
await confirm(ctx, `Detach ${domain} from ${ctx.args[0]}? It stops serving right away.`);
|
|
1565
|
+
await confirm(ctx, t(`Detach ${domain} from ${ctx.args[0]}? It stops serving right away.`, `\xBFDesligar ${domain} de ${ctx.args[0]}? Deja de servir de inmediato.`));
|
|
1441
1566
|
const r = await (await sandbox2(ctx, this.usage)).removeDomain(domain);
|
|
1442
|
-
emit(ctx, r, () => console.log(`Removed ${domain}`));
|
|
1567
|
+
emit(ctx, r, () => console.log(t(`Removed ${domain}`, `Quitado ${domain}`)));
|
|
1443
1568
|
}
|
|
1444
1569
|
}
|
|
1445
1570
|
}
|
|
@@ -1503,7 +1628,8 @@ var LS_HINT = {
|
|
|
1503
1628
|
sandbox: "easybits sandboxes ls",
|
|
1504
1629
|
db: "easybits db ls"
|
|
1505
1630
|
};
|
|
1506
|
-
var NOUN = { agent: "agent", sandbox: "sandbox or machine", db: "database" };
|
|
1631
|
+
var NOUN = () => ({ agent: t("agent", "agente"), sandbox: t("sandbox or machine", "sandbox o m\xE1quina"), db: t("database", "base de datos") });
|
|
1632
|
+
var NOUNS = () => ({ agent: t("agents", "agentes"), sandbox: t("sandboxes", "sandboxes"), db: t("databases", "bases de datos") });
|
|
1507
1633
|
var cache = /* @__PURE__ */ new Map();
|
|
1508
1634
|
function listRefs(ctx, kind) {
|
|
1509
1635
|
let p = cache.get(kind);
|
|
@@ -1524,15 +1650,15 @@ async function resolveRef(ctx, kind, ref) {
|
|
|
1524
1650
|
const r = matchRef(kind, ref, await listRefs(ctx, kind));
|
|
1525
1651
|
if (r.ok) return r.id;
|
|
1526
1652
|
if (r.reason === "not_found") {
|
|
1527
|
-
throw new CliError(`No ${NOUN[kind]} with id or name "${ref}".`, EXIT.API,
|
|
1653
|
+
throw new CliError(t(`No ${NOUN()[kind]} with id or name "${ref}".`, `No hay ${NOUN()[kind]} con id o nombre "${ref}".`), EXIT.API, `${t("List them with", "Enl\xEDstalos con")}: ${LS_HINT[kind]}`, "not_found", 404);
|
|
1528
1654
|
}
|
|
1529
1655
|
const shown = r.matches.slice(0, 10).map((m) => ` ${m.id}${m.status ? ` (${m.status})` : ""}`);
|
|
1530
|
-
if (r.matches.length > 10) shown.push(` \u2026and ${r.matches.length - 10} more`);
|
|
1656
|
+
if (r.matches.length > 10) shown.push(t(` \u2026and ${r.matches.length - 10} more`, ` \u2026y ${r.matches.length - 10} m\xE1s`));
|
|
1531
1657
|
throw new CliError(
|
|
1532
|
-
`"${ref}" matches ${r.matches.length} ${kind
|
|
1658
|
+
t(`"${ref}" matches ${r.matches.length} ${NOUNS()[kind]}. Use the id:`, `"${ref}" coincide con ${r.matches.length} ${NOUNS()[kind]}. Usa el id:`),
|
|
1533
1659
|
EXIT.USAGE,
|
|
1534
1660
|
`${shown.join("\n")}
|
|
1535
|
-
List them with: ${LS_HINT[kind]}`,
|
|
1661
|
+
${t("List them with", "Enl\xEDstalos con")}: ${LS_HINT[kind]}`,
|
|
1536
1662
|
"ambiguous"
|
|
1537
1663
|
);
|
|
1538
1664
|
}
|
|
@@ -1546,7 +1672,7 @@ async function resolveRefs(ctx, key) {
|
|
|
1546
1672
|
// src/commands/db.ts
|
|
1547
1673
|
async function findDb(ctx, id) {
|
|
1548
1674
|
const hit = (await listRefs(ctx, "db")).find((d) => d.id === id);
|
|
1549
|
-
if (!hit) throw new CliError(`No database "${id}".`, 1, "List them with: easybits db ls", "not_found", 404);
|
|
1675
|
+
if (!hit) throw new CliError(t(`No database "${id}".`, `No existe la base "${id}".`), 1, t("List them with: easybits db ls", "Enl\xEDstalas con: easybits db ls"), "not_found", 404);
|
|
1550
1676
|
return { id, name: hit.name ?? id };
|
|
1551
1677
|
}
|
|
1552
1678
|
var db = {
|
|
@@ -1570,7 +1696,7 @@ var db = {
|
|
|
1570
1696
|
() => table(
|
|
1571
1697
|
items.map((d) => ({ ...d, createdAt: fmtDate(d.createdAt) })),
|
|
1572
1698
|
[["id", "ID"], ["name", "NAME"], ["createdAt", "CREATED"], ["description", "DESCRIPTION"]],
|
|
1573
|
-
"No databases. Create one: easybits db create <name>"
|
|
1699
|
+
t("No databases. Create one: easybits db create <name>", "No hay bases. Crea una: easybits db create <nombre>")
|
|
1574
1700
|
)
|
|
1575
1701
|
);
|
|
1576
1702
|
}
|
|
@@ -1584,7 +1710,7 @@ var db = {
|
|
|
1584
1710
|
const name = need(ctx, 0, "name", this.usage);
|
|
1585
1711
|
const eb = await getClient(ctx);
|
|
1586
1712
|
const d = await eb.createDatabase({ name, description: str(ctx, "description") });
|
|
1587
|
-
emit(ctx, d, () => console.log(`Created ${d.name} (${d.id})`));
|
|
1713
|
+
emit(ctx, d, () => console.log(t(`Created ${d.name} (${d.id})`, `Creada ${d.name} (${d.id})`)));
|
|
1588
1714
|
}
|
|
1589
1715
|
},
|
|
1590
1716
|
rm: {
|
|
@@ -1598,9 +1724,9 @@ var db = {
|
|
|
1598
1724
|
requireYesIfHeadless(ctx);
|
|
1599
1725
|
const eb = await getClient(ctx);
|
|
1600
1726
|
const hit = await findDb(ctx, ref);
|
|
1601
|
-
await confirm(ctx, `Delete database ${hit.name} (${hit.id}) and all its data?`, { typeName: hit.name });
|
|
1727
|
+
await confirm(ctx, t(`Delete database ${hit.name} (${hit.id}) and all its data?`, `\xBFBorrar la base ${hit.name} (${hit.id}) con todos sus datos?`), { typeName: hit.name });
|
|
1602
1728
|
const r = await eb.deleteDatabase(hit.id);
|
|
1603
|
-
emit(ctx, { ...r, id: hit.id, name: hit.name }, () => console.log(`Deleted ${hit.name} (${hit.id})`));
|
|
1729
|
+
emit(ctx, { ...r, id: hit.id, name: hit.name }, () => console.log(t(`Deleted ${hit.name} (${hit.id})`, `Borrada ${hit.name} (${hit.id})`)));
|
|
1604
1730
|
}
|
|
1605
1731
|
},
|
|
1606
1732
|
tables: {
|
|
@@ -1628,10 +1754,10 @@ var db = {
|
|
|
1628
1754
|
ctx,
|
|
1629
1755
|
tables,
|
|
1630
1756
|
() => table(
|
|
1631
|
-
tables.map((
|
|
1632
|
-
name:
|
|
1633
|
-
rows:
|
|
1634
|
-
columns:
|
|
1757
|
+
tables.map((t2) => ({
|
|
1758
|
+
name: t2.name,
|
|
1759
|
+
rows: t2.rows,
|
|
1760
|
+
columns: t2.columns.map((c) => `${c.name}${c.type ? ` ${c.type}` : ""}${c.pk ? " PK" : ""}`).join(", ")
|
|
1635
1761
|
})),
|
|
1636
1762
|
[["name", "TABLE"], ["rows", "ROWS"], ["columns", "COLUMNS"]],
|
|
1637
1763
|
`No tables in ${hit.name}. Create one: easybits db query ${hit.name} 'CREATE TABLE \u2026'`
|
|
@@ -1664,7 +1790,12 @@ var db = {
|
|
|
1664
1790
|
"(0 rows)"
|
|
1665
1791
|
);
|
|
1666
1792
|
} else {
|
|
1667
|
-
console.log(
|
|
1793
|
+
console.log(
|
|
1794
|
+
t(
|
|
1795
|
+
`OK, ${r.affected_row_count} row(s) affected${r.last_insert_rowid ? `, last id ${r.last_insert_rowid}` : ""}`,
|
|
1796
|
+
`OK, ${r.affected_row_count} fila(s) afectadas${r.last_insert_rowid ? `, \xFAltimo id ${r.last_insert_rowid}` : ""}`
|
|
1797
|
+
)
|
|
1798
|
+
);
|
|
1668
1799
|
}
|
|
1669
1800
|
});
|
|
1670
1801
|
}
|
|
@@ -1719,15 +1850,15 @@ async function agentBox(eb, id) {
|
|
|
1719
1850
|
}
|
|
1720
1851
|
function skillFrontmatter(md) {
|
|
1721
1852
|
const m = /^---\r?\n([\s\S]*?)\r?\n---/.exec(md);
|
|
1722
|
-
if (!m) return { error: "SKILL.md has no frontmatter (--- name / description ---)" };
|
|
1853
|
+
if (!m) return { error: t("SKILL.md has no frontmatter (--- name / description ---)", "SKILL.md no tiene frontmatter (--- name / description ---)") };
|
|
1723
1854
|
const out = {};
|
|
1724
1855
|
for (const key of ["name", "description"]) {
|
|
1725
1856
|
const line2 = m[1].split(/\r?\n/).find((l) => l.startsWith(`${key}:`));
|
|
1726
|
-
if (!line2) return { ...out, error: `frontmatter has no \`${key}:\`` };
|
|
1857
|
+
if (!line2) return { ...out, error: t(`frontmatter has no \`${key}:\``, `el frontmatter no tiene \`${key}:\``) };
|
|
1727
1858
|
const value = line2.slice(key.length + 1).trim();
|
|
1728
|
-
if (!value) return { ...out, error: `\`${key}:\` is empty` };
|
|
1859
|
+
if (!value) return { ...out, error: t(`\`${key}:\` is empty`, `\`${key}:\` est\xE1 vac\xEDo`) };
|
|
1729
1860
|
const quoted = /^(["']).*\1$/.test(value);
|
|
1730
|
-
if (!quoted && value.includes(": ")) return { ...out, error: `\`${key}:\` has an unquoted ": " \u2014 quote the value or the skill is dropped` };
|
|
1861
|
+
if (!quoted && value.includes(": ")) return { ...out, error: t(`\`${key}:\` has an unquoted ": " \u2014 quote the value or the skill is dropped`, `\`${key}:\` lleva un ": " sin comillas \u2014 entrecomilla el valor o la skill se descarta`) };
|
|
1731
1862
|
out[key] = quoted ? value.slice(1, -1) : value;
|
|
1732
1863
|
}
|
|
1733
1864
|
return out;
|
|
@@ -1780,10 +1911,10 @@ async function soft(fn) {
|
|
|
1780
1911
|
}
|
|
1781
1912
|
function readLocalText(path, usage2) {
|
|
1782
1913
|
if (path === "-") {
|
|
1783
|
-
if (process.stdin.isTTY) throw usageError("Expected the content on stdin.", usage2);
|
|
1914
|
+
if (process.stdin.isTTY) throw usageError(t("Expected the content on stdin.", "Se esperaba el contenido por stdin."), usage2);
|
|
1784
1915
|
return readStdin().then((b) => b.toString("utf8"));
|
|
1785
1916
|
}
|
|
1786
|
-
if (!existsSync6(path)) throw usageError(`File not found: ${path}`, usage2);
|
|
1917
|
+
if (!existsSync6(path)) throw usageError(t(`File not found: ${path}`, `No existe el archivo: ${path}`), usage2);
|
|
1787
1918
|
return readFileSync6(path, "utf8");
|
|
1788
1919
|
}
|
|
1789
1920
|
function parseMcpFile(text, usage2) {
|
|
@@ -1791,10 +1922,10 @@ function parseMcpFile(text, usage2) {
|
|
|
1791
1922
|
try {
|
|
1792
1923
|
j = JSON.parse(text);
|
|
1793
1924
|
} catch (e) {
|
|
1794
|
-
throw usageError(`servers file is not JSON: ${e.message}`, usage2);
|
|
1925
|
+
throw usageError(t(`servers file is not JSON: ${e.message}`, `el archivo de servers no es JSON: ${e.message}`), usage2);
|
|
1795
1926
|
}
|
|
1796
1927
|
const list2 = Array.isArray(j) ? j : j?.servers ?? j?.mcpServers;
|
|
1797
|
-
if (!Array.isArray(list2)) throw usageError("Expected an array of MCP servers, or { servers: [...] }.", usage2);
|
|
1928
|
+
if (!Array.isArray(list2)) throw usageError(t("Expected an array of MCP servers, or { servers: [...] }.", "Se esperaba un arreglo de servidores MCP, o { servers: [...] }."), usage2);
|
|
1798
1929
|
return list2;
|
|
1799
1930
|
}
|
|
1800
1931
|
var hasMasked = (servers) => servers.some((s) => [...s.env ?? [], ...s.headers ?? []].some((x) => /…\*\*\*$|^\*\*\*$/.test(x.value)));
|
|
@@ -1832,7 +1963,7 @@ var get = {
|
|
|
1832
1963
|
const id = need(ctx, 0, "agent", this.usage);
|
|
1833
1964
|
const fields = str(ctx, "fields")?.split(",").map((f) => f.trim()).filter(Boolean);
|
|
1834
1965
|
const bad = fields?.filter((f) => !GET_FIELDS.includes(f));
|
|
1835
|
-
if (bad?.length) throw usageError(`Unknown field(s): ${bad.join(", ")}. Valid: ${GET_FIELDS.join(", ")}`, this.usage);
|
|
1966
|
+
if (bad?.length) throw usageError(t(`Unknown field(s): ${bad.join(", ")}. Valid: ${GET_FIELDS.join(", ")}`, `Campo(s) desconocido(s): ${bad.join(", ")}. V\xE1lidos: ${GET_FIELDS.join(", ")}`), this.usage);
|
|
1836
1967
|
const promptOut = str(ctx, "prompt-out");
|
|
1837
1968
|
const want = (f) => !fields || fields.includes(f);
|
|
1838
1969
|
const eb = await getClient(ctx);
|
|
@@ -1841,12 +1972,12 @@ var get = {
|
|
|
1841
1972
|
const notes = [];
|
|
1842
1973
|
const wantsConfig = ["systemPrompt", "systemPromptMode", "mcpServers", "files", "skills"].some(want) || !!promptOut;
|
|
1843
1974
|
let machine = wantsConfig && a.status !== "lost";
|
|
1844
|
-
if (wantsConfig && !machine) notes.push(`its sandbox is lost: prompt, MCP, skills and files are unavailable (easybits agents doctor ${id})`);
|
|
1975
|
+
if (wantsConfig && !machine) notes.push(t(`its sandbox is lost: prompt, MCP, skills and files are unavailable (easybits agents doctor ${id})`, `su sandbox se perdi\xF3: prompt, MCP, skills y archivos no est\xE1n disponibles (easybits agents doctor ${id})`));
|
|
1845
1976
|
if (machine) {
|
|
1846
1977
|
const p = await soft(() => getPrompt(ctx, id));
|
|
1847
1978
|
if (!p.ok && /agente_sin_maquina|no expone/.test(p.error)) {
|
|
1848
1979
|
machine = false;
|
|
1849
|
-
notes.push(`template "${a.template}" has no machine: prompt, MCP, skills and files apply only to ghosty-lite and goose`);
|
|
1980
|
+
notes.push(t(`template "${a.template}" has no machine: prompt, MCP, skills and files apply only to ghosty-lite and goose`, `el template "${a.template}" no tiene m\xE1quina: prompt, MCP, skills y archivos s\xF3lo aplican a ghosty-lite y goose`));
|
|
1850
1981
|
} else if (p.ok) {
|
|
1851
1982
|
rec.systemPromptMode = p.value.systemPromptMode;
|
|
1852
1983
|
if (promptOut) {
|
|
@@ -1880,12 +2011,12 @@ var get = {
|
|
|
1880
2011
|
printAgent(a);
|
|
1881
2012
|
if (rec.systemPromptMode) console.log(`Prompt: ${rec.systemPromptMode}${promptOut ? ` \u2192 ${resolve(promptOut)}` : ""}`);
|
|
1882
2013
|
const mcp3 = rec.mcpServers;
|
|
1883
|
-
if (mcp3) console.log(`MCP: ${mcp3.length ? mcp3.map((s) => s.name).join(", ") : "none"}`);
|
|
2014
|
+
if (mcp3) console.log(`MCP: ${mcp3.length ? mcp3.map((s) => s.name).join(", ") : t("none", "ninguno")}`);
|
|
1884
2015
|
const skills2 = rec.skills;
|
|
1885
|
-
if (skills2) console.log(`Skills: ${skills2.length ? skills2.map((s) => s.slug).join(", ") : "none"}`);
|
|
2016
|
+
if (skills2) console.log(`Skills: ${skills2.length ? skills2.map((s) => s.slug).join(", ") : t("none", "ninguna")}`);
|
|
1886
2017
|
const files3 = rec.files;
|
|
1887
|
-
if (files3) console.log(
|
|
1888
|
-
for (const e of errors) console.error(
|
|
2018
|
+
if (files3) console.log(`${t("Files: ", "Archivos: ")} ${files3.length} ${t("in", "en")} ${WORK_DIR}`);
|
|
2019
|
+
for (const e of errors) console.error(`${t("note", "nota")}: ${e}`);
|
|
1889
2020
|
if (typeof rec.systemPrompt === "string" && rec.systemPrompt) console.log(`
|
|
1890
2021
|
${rec.systemPrompt.trimEnd()}`);
|
|
1891
2022
|
});
|
|
@@ -1894,16 +2025,16 @@ ${rec.systemPrompt.trimEnd()}`);
|
|
|
1894
2025
|
var startError = (a) => a.status === "error" ? a.lastError ?? null : null;
|
|
1895
2026
|
function printAgent(a) {
|
|
1896
2027
|
console.log(`ID: ${a.agentId}`);
|
|
1897
|
-
if (a.name) console.log(
|
|
2028
|
+
if (a.name) console.log(`${t("Name: ", "Nombre: ")} ${a.name}`);
|
|
1898
2029
|
console.log(`Template: ${a.template}`);
|
|
1899
|
-
console.log(
|
|
2030
|
+
console.log(`${t("Status: ", "Estado: ")} ${a.status}`);
|
|
1900
2031
|
const reason = startError(a);
|
|
1901
2032
|
if (reason) console.log(`Error: ${reason}`);
|
|
1902
2033
|
console.log(`Sandbox: ${a.sandboxId}`);
|
|
1903
2034
|
console.log(`URL: ${a.agentUrl}`);
|
|
1904
2035
|
console.log(`Token: ${maskSecret(a.embedToken ?? "")} (full value: --fields embedToken --json)`);
|
|
1905
|
-
console.log(
|
|
1906
|
-
console.log(
|
|
2036
|
+
console.log(`${t("Created: ", "Creado: ")} ${fmtDate(a.createdAt)}`);
|
|
2037
|
+
console.log(`${t("Expires: ", "Vence: ")} ${a.expiresAt ? fmtDate(a.expiresAt) : t("never", "nunca")}`);
|
|
1907
2038
|
}
|
|
1908
2039
|
var set = {
|
|
1909
2040
|
summary: "Change the system prompt (ghosty-lite, goose); takes effect without a reboot",
|
|
@@ -1923,11 +2054,11 @@ var set = {
|
|
|
1923
2054
|
const id = need(ctx, 0, "agent", this.usage);
|
|
1924
2055
|
const inline = str(ctx, "prompt");
|
|
1925
2056
|
const file = str(ctx, "prompt-file");
|
|
1926
|
-
if (inline != null && file) throw usageError("Use --prompt or --prompt-file, not both.", this.usage);
|
|
2057
|
+
if (inline != null && file) throw usageError(t("Use --prompt or --prompt-file, not both.", "Usa --prompt o --prompt-file, no los dos."), this.usage);
|
|
1927
2058
|
const mode = str(ctx, "prompt-mode");
|
|
1928
|
-
if (mode && mode !== "append" && mode !== "replace") throw usageError("--prompt-mode must be append or replace.", this.usage);
|
|
2059
|
+
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);
|
|
1929
2060
|
const prompt = inline ?? (file ? await readLocalText(file, this.usage) : void 0);
|
|
1930
|
-
if (prompt == null && !mode) throw usageError("Nothing to change: pass --prompt, --prompt-file or --prompt-mode.", this.usage);
|
|
2061
|
+
if (prompt == null && !mode) throw usageError(t("Nothing to change: pass --prompt, --prompt-file or --prompt-mode.", "Nada que cambiar: pasa --prompt, --prompt-file o --prompt-mode."), this.usage);
|
|
1931
2062
|
const cur = await getPrompt(ctx, id);
|
|
1932
2063
|
const changes = {};
|
|
1933
2064
|
if (prompt != null && prompt !== cur.systemPrompt) {
|
|
@@ -1937,9 +2068,9 @@ var set = {
|
|
|
1937
2068
|
if (bool(ctx, "dry-run") || !Object.keys(changes).length) {
|
|
1938
2069
|
const dry = { agentId: id, dryRun: bool(ctx, "dry-run"), changes };
|
|
1939
2070
|
emit(ctx, dry, () => {
|
|
1940
|
-
if (!Object.keys(changes).length) return console.log("No changes: the agent already has that prompt.");
|
|
2071
|
+
if (!Object.keys(changes).length) return console.log(t("No changes: the agent already has that prompt.", "Sin cambios: el agente ya tiene ese prompt."));
|
|
1941
2072
|
for (const [k, v] of Object.entries(changes)) console.log(`${k}: ${v.from} \u2192 ${v.to}`);
|
|
1942
|
-
console.log("(dry run: nothing changed)");
|
|
2073
|
+
console.log(t("(dry run: nothing changed)", "(simulaci\xF3n: no cambi\xF3 nada)"));
|
|
1943
2074
|
});
|
|
1944
2075
|
return;
|
|
1945
2076
|
}
|
|
@@ -1950,7 +2081,7 @@ var set = {
|
|
|
1950
2081
|
emit(ctx, { agentId: id, changes, ...r }, () => {
|
|
1951
2082
|
for (const [k, v] of Object.entries(changes)) console.log(`${k}: ${v.from} \u2192 ${v.to}`);
|
|
1952
2083
|
if (r.hooks) console.log(r.hooks);
|
|
1953
|
-
console.log(`Check it: easybits agents try ${id} "who are you?"`);
|
|
2084
|
+
console.log(t(`Check it: easybits agents try ${id} "who are you?"`, `Rev\xEDsalo: easybits agents try ${id} "\xBFqui\xE9n eres?"`));
|
|
1954
2085
|
});
|
|
1955
2086
|
}
|
|
1956
2087
|
};
|
|
@@ -1979,7 +2110,7 @@ var files2 = {
|
|
|
1979
2110
|
emit(
|
|
1980
2111
|
ctx,
|
|
1981
2112
|
r,
|
|
1982
|
-
() => table(r.files.map((f) => ({ ...f, size: fmtBytes(f.size) })), [["path", "PATH"], ["size", "SIZE"]], `No files in ${r.dir}. Upload: easybits agents files put ${id} <file>`)
|
|
2113
|
+
() => table(r.files.map((f) => ({ ...f, size: fmtBytes(f.size) })), [["path", "PATH"], ["size", "SIZE"]], t(`No files in ${r.dir}. Upload: easybits agents files put ${id} <file>`, `No hay archivos en ${r.dir}. Sube: easybits agents files put ${id} <archivo>`))
|
|
1983
2114
|
);
|
|
1984
2115
|
} else if (action === "get" || action === "read" || action === "cat") {
|
|
1985
2116
|
const path = need(ctx, 2, "path", FILES_USAGE2);
|
|
@@ -1990,22 +2121,22 @@ var files2 = {
|
|
|
1990
2121
|
if (out) {
|
|
1991
2122
|
const r = await box.files.read(full, { encoding: "base64" });
|
|
1992
2123
|
writeFileSync4(out, Buffer.from(r.content, "base64"));
|
|
1993
|
-
emit(ctx, { path, out: resolve(out), size: r.size }, () => console.error(`Saved ${fmtBytes(r.size)} to ${resolve(out)}`));
|
|
2124
|
+
emit(ctx, { path, out: resolve(out), size: r.size }, () => console.error(t(`Saved ${fmtBytes(r.size)} to ${resolve(out)}`, `Guard\xE9 ${fmtBytes(r.size)} en ${resolve(out)}`)));
|
|
1994
2125
|
} else {
|
|
1995
2126
|
const r = await box.files.read(full);
|
|
1996
2127
|
emit(ctx, r, () => process.stdout.write(r.content));
|
|
1997
2128
|
}
|
|
1998
2129
|
} else if (action === "put" || action === "upload") {
|
|
1999
|
-
if (!rest.length) throw usageError("Give one or more local files.", FILES_USAGE2);
|
|
2130
|
+
if (!rest.length) throw usageError(t("Give one or more local files.", "Pasa uno o m\xE1s archivos locales."), FILES_USAGE2);
|
|
2000
2131
|
const to = (str(ctx, "to") ?? "").replace(/^\/+|\/+$/g, "");
|
|
2001
2132
|
const plan = rest.map((local) => {
|
|
2002
|
-
if (!existsSync6(local) || !statSync2(local).isFile()) throw usageError(`File not found: ${local}`, FILES_USAGE2);
|
|
2133
|
+
if (!existsSync6(local) || !statSync2(local).isFile()) throw usageError(t(`File not found: ${local}`, `No existe el archivo: ${local}`), FILES_USAGE2);
|
|
2003
2134
|
return { local, path: to ? `${to}/${basename2(local)}` : basename2(local), bytes: statSync2(local).size };
|
|
2004
2135
|
});
|
|
2005
2136
|
if (bool(ctx, "dry-run")) {
|
|
2006
2137
|
emit(ctx, { agentId: id, dryRun: true, upload: plan }, () => {
|
|
2007
2138
|
for (const p of plan) console.log(`${p.local} \u2192 ${WORK_DIR}/${p.path} (${fmtBytes(p.bytes)})`);
|
|
2008
|
-
console.log("(dry run: nothing uploaded)");
|
|
2139
|
+
console.log(t("(dry run: nothing uploaded)", "(simulaci\xF3n: no se subi\xF3 nada)"));
|
|
2009
2140
|
});
|
|
2010
2141
|
return;
|
|
2011
2142
|
}
|
|
@@ -2013,24 +2144,24 @@ var files2 = {
|
|
|
2013
2144
|
for (const p of plan) {
|
|
2014
2145
|
const r = await api(ctx, "PUT", `/agents/${id}/files/${encodePath(p.path)}`, readFileSync6(p.local), { raw: true });
|
|
2015
2146
|
done.push({ local: p.local, ...r });
|
|
2016
|
-
if (!ctx.json) console.log(
|
|
2147
|
+
if (!ctx.json) console.log(`${t("Uploaded", "Subido")} ${p.local} \u2192 ${WORK_DIR}/${p.path} (${fmtBytes(p.bytes)})`);
|
|
2017
2148
|
}
|
|
2018
2149
|
if (ctx.json) emit(ctx, done, () => {
|
|
2019
2150
|
});
|
|
2020
2151
|
} else if (action === "rm" || action === "delete") {
|
|
2021
|
-
if (!rest.length) throw usageError("Give one or more paths.", FILES_USAGE2);
|
|
2152
|
+
if (!rest.length) throw usageError(t("Give one or more paths.", "Pasa una o m\xE1s rutas."), FILES_USAGE2);
|
|
2022
2153
|
if (bool(ctx, "dry-run")) {
|
|
2023
2154
|
emit(ctx, { agentId: id, dryRun: true, delete: rest }, () => {
|
|
2024
|
-
for (const p of rest) console.log(
|
|
2155
|
+
for (const p of rest) console.log(`${t("would delete", "borrar\xEDa")} ${WORK_DIR}/${p}`);
|
|
2025
2156
|
});
|
|
2026
2157
|
return;
|
|
2027
2158
|
}
|
|
2028
2159
|
requireYesIfHeadless(ctx);
|
|
2029
|
-
await confirm(ctx, `Delete ${rest.length === 1 ? rest[0] : `${rest.length} files`} from agent ${id}?`);
|
|
2160
|
+
await confirm(ctx, t(`Delete ${rest.length === 1 ? rest[0] : `${rest.length} files`} from agent ${id}?`, `\xBFBorrar ${rest.length === 1 ? rest[0] : `${rest.length} archivos`} del agente ${id}?`));
|
|
2030
2161
|
const done = [];
|
|
2031
2162
|
for (const p of rest) done.push(await api(ctx, "DELETE", `/agents/${id}/files/${encodePath(p)}`));
|
|
2032
|
-
emit(ctx, done, () => done.forEach((d) => console.log(
|
|
2033
|
-
} else throw usageError(`Unknown files action "${action}".`, FILES_USAGE2);
|
|
2163
|
+
emit(ctx, done, () => done.forEach((d) => console.log(`${t("Deleted", "Borrado")} ${d.deleted}`)));
|
|
2164
|
+
} else throw usageError(t(`Unknown files action "${action}".`, `Acci\xF3n de files desconocida "${action}".`), FILES_USAGE2);
|
|
2034
2165
|
}
|
|
2035
2166
|
};
|
|
2036
2167
|
var SKILLS_USAGE = "easybits agents skills <ls|get|add|rm> <agent> [slug...] [--file SKILL.md | --dir <folder>] [--restart]";
|
|
@@ -2078,14 +2209,14 @@ var skills = {
|
|
|
2078
2209
|
emit(
|
|
2079
2210
|
ctx,
|
|
2080
2211
|
list2,
|
|
2081
|
-
() => table(list2.map((s) => ({ ...s, files: s.files.length })), [["slug", "SLUG"], ["files", "FILES"], ["description", "DESCRIPTION"]], `No skills. Add one: easybits agents skills add ${id} --dir <folder>`)
|
|
2212
|
+
() => table(list2.map((s) => ({ ...s, files: s.files.length })), [["slug", "SLUG"], ["files", "FILES"], ["description", "DESCRIPTION"]], t(`No skills. Add one: easybits agents skills add ${id} --dir <folder>`, `No hay skills. Agrega una: easybits agents skills add ${id} --dir <carpeta>`))
|
|
2082
2213
|
);
|
|
2083
2214
|
} else if (action === "get" || action === "read") {
|
|
2084
2215
|
const slug = need(ctx, 2, "slug", SKILLS_USAGE);
|
|
2085
2216
|
const eb = await getClient(ctx);
|
|
2086
2217
|
const { box } = await agentBox(eb, id);
|
|
2087
2218
|
const entry = (await listSkills(box)).find((s2) => s2.slug === slug);
|
|
2088
|
-
if (!entry) throw new CliError(`No skill "${slug}" on agent ${id}.`, EXIT.API, `List them: easybits agents skills ls ${id}`, "not_found", 404);
|
|
2219
|
+
if (!entry) throw new CliError(t(`No skill "${slug}" on agent ${id}.`, `El agente ${id} no tiene la skill "${slug}".`), EXIT.API, t(`List them: easybits agents skills ls ${id}`, `Enl\xEDstalas: easybits agents skills ls ${id}`), "not_found", 404);
|
|
2089
2220
|
const out = str(ctx, "out");
|
|
2090
2221
|
if (!out) {
|
|
2091
2222
|
const md = (await box.files.read(`${SKILLS_DIR}/${slug}/SKILL.md`)).content;
|
|
@@ -2099,48 +2230,49 @@ var skills = {
|
|
|
2099
2230
|
mkdirSync3(dirname(join3(out, a.name)), { recursive: true });
|
|
2100
2231
|
writeFileSync4(join3(out, a.name), Buffer.from(a.contentBase64, "base64"));
|
|
2101
2232
|
}
|
|
2102
|
-
emit(ctx, { slug, out: resolve(out), files: entry.files }, () => console.log(`Saved ${entry.files.length} file(s) to ${resolve(out)}`));
|
|
2233
|
+
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)}`)));
|
|
2103
2234
|
} else if (action === "add" || action === "put") {
|
|
2104
2235
|
const file = str(ctx, "file");
|
|
2105
2236
|
const dir = str(ctx, "dir");
|
|
2106
|
-
if (!!file === !!dir) throw usageError("Pass --file SKILL.md or --dir <folder>.", SKILLS_USAGE);
|
|
2237
|
+
if (!!file === !!dir) throw usageError(t("Pass --file SKILL.md or --dir <folder>.", "Pasa --file SKILL.md o --dir <carpeta>."), SKILLS_USAGE);
|
|
2107
2238
|
const mdPath = file ?? join3(dir, "SKILL.md");
|
|
2108
|
-
if (!existsSync6(mdPath)) throw usageError(`File not found: ${mdPath}`, SKILLS_USAGE);
|
|
2239
|
+
if (!existsSync6(mdPath)) throw usageError(t(`File not found: ${mdPath}`, `No existe el archivo: ${mdPath}`), SKILLS_USAGE);
|
|
2109
2240
|
const markdown = readFileSync6(mdPath, "utf8");
|
|
2110
2241
|
const fm = skillFrontmatter(markdown);
|
|
2111
2242
|
if (fm.error) throw usageError(`${mdPath}: ${fm.error}`, SKILLS_USAGE);
|
|
2112
2243
|
const slug = str(ctx, "slug") ?? (fm.name && SLUG_RE.test(fm.name) ? fm.name : basename2(resolve(dir ?? dirname(mdPath))).toLowerCase());
|
|
2113
|
-
if (!SLUG_RE.test(slug)) throw usageError(`Invalid slug "${slug}" ([a-z0-9-], max 64). Pass --slug.`, SKILLS_USAGE);
|
|
2244
|
+
if (!SLUG_RE.test(slug)) throw usageError(t(`Invalid slug "${slug}" ([a-z0-9-], max 64). Pass --slug.`, `Slug inv\xE1lido "${slug}" ([a-z0-9-], m\xE1x 64). Pasa --slug.`), SKILLS_USAGE);
|
|
2114
2245
|
const assets = dir ? readSkillDir(dir) : [];
|
|
2115
2246
|
const plan = { agentId: id, slug, files: ["SKILL.md", ...assets.map((a) => a.name)], restart: bool(ctx, "restart") };
|
|
2116
2247
|
if (bool(ctx, "dry-run")) {
|
|
2117
|
-
emit(ctx, { ...plan, dryRun: true }, () => console.log(`would add skill ${slug} (${plan.files.length} file(s))${plan.restart ? " and restart" : ""}
|
|
2118
|
-
(dry run: nothing changed)`))
|
|
2248
|
+
emit(ctx, { ...plan, dryRun: true }, () => console.log(t(`would add skill ${slug} (${plan.files.length} file(s))${plan.restart ? " and restart" : ""}
|
|
2249
|
+
(dry run: nothing changed)`, `agregar\xEDa la skill ${slug} (${plan.files.length} archivo(s))${plan.restart ? " y reiniciar\xEDa" : ""}
|
|
2250
|
+
(simulaci\xF3n: no cambi\xF3 nada)`)));
|
|
2119
2251
|
return;
|
|
2120
2252
|
}
|
|
2121
2253
|
const r = await api(ctx, "PUT", `/agents/${id}/skills/${slug}`, { markdown, assets });
|
|
2122
2254
|
const restarted = plan.restart ? await restartAgent(ctx, id) : null;
|
|
2123
2255
|
emit(ctx, { ...r, restarted: !!restarted }, () => {
|
|
2124
|
-
console.log(`Added skill ${slug} (${r.files.length} file(s), ${fmtBytes(r.bytes)})`);
|
|
2125
|
-
console.log(restarted ? "Restarted: the agent sees it now." : `It takes effect after: easybits agents restart ${id}`);
|
|
2256
|
+
console.log(t(`Added skill ${slug} (${r.files.length} file(s), ${fmtBytes(r.bytes)})`, `Skill ${slug} agregada (${r.files.length} archivo(s), ${fmtBytes(r.bytes)})`));
|
|
2257
|
+
console.log(restarted ? t("Restarted: the agent sees it now.", "Reiniciado: el agente ya la ve.") : t(`It takes effect after: easybits agents restart ${id}`, `Entra despu\xE9s de: easybits agents restart ${id}`));
|
|
2126
2258
|
});
|
|
2127
2259
|
} else if (action === "rm" || action === "delete") {
|
|
2128
2260
|
const slugs = ctx.args.slice(2);
|
|
2129
|
-
if (!slugs.length) throw usageError("Give one or more slugs.", SKILLS_USAGE);
|
|
2261
|
+
if (!slugs.length) throw usageError(t("Give one or more slugs.", "Pasa uno o m\xE1s slugs."), SKILLS_USAGE);
|
|
2130
2262
|
if (bool(ctx, "dry-run")) {
|
|
2131
|
-
emit(ctx, { agentId: id, dryRun: true, delete: slugs }, () => slugs.forEach((s) => console.log(
|
|
2263
|
+
emit(ctx, { agentId: id, dryRun: true, delete: slugs }, () => slugs.forEach((s) => console.log(`${t("would remove skill", "quitar\xEDa la skill")} ${s}`)));
|
|
2132
2264
|
return;
|
|
2133
2265
|
}
|
|
2134
2266
|
requireYesIfHeadless(ctx);
|
|
2135
|
-
await confirm(ctx, `Remove skill${slugs.length > 1 ? "s" : ""} ${slugs.join(", ")} from agent ${id}?`);
|
|
2267
|
+
await confirm(ctx, t(`Remove skill${slugs.length > 1 ? "s" : ""} ${slugs.join(", ")} from agent ${id}?`, `\xBFQuitar la(s) skill(s) ${slugs.join(", ")} del agente ${id}?`));
|
|
2136
2268
|
const done = [];
|
|
2137
2269
|
for (const s of slugs) done.push(await api(ctx, "DELETE", `/agents/${id}/skills/${s}`));
|
|
2138
2270
|
const restarted = bool(ctx, "restart") ? await restartAgent(ctx, id) : null;
|
|
2139
2271
|
emit(ctx, { deleted: done.map((d) => d.deleted), restarted: !!restarted }, () => {
|
|
2140
|
-
done.forEach((d) => console.log(
|
|
2141
|
-
console.log(restarted ? "Restarted." : `It takes effect after: easybits agents restart ${id}`);
|
|
2272
|
+
done.forEach((d) => console.log(`${t("Removed", "Quitada")} ${d.deleted}`));
|
|
2273
|
+
console.log(restarted ? t("Restarted.", "Reiniciado.") : t(`It takes effect after: easybits agents restart ${id}`, `Entra despu\xE9s de: easybits agents restart ${id}`));
|
|
2142
2274
|
});
|
|
2143
|
-
} else throw usageError(`Unknown skills action "${action}".`, SKILLS_USAGE);
|
|
2275
|
+
} else throw usageError(t(`Unknown skills action "${action}".`, `Acci\xF3n de skills desconocida "${action}".`), SKILLS_USAGE);
|
|
2144
2276
|
}
|
|
2145
2277
|
};
|
|
2146
2278
|
var MCP_USAGE = "easybits agents mcp <get|set> <agent> [--file <servers.json|->] [--dry-run]";
|
|
@@ -2167,15 +2299,15 @@ var mcp2 = {
|
|
|
2167
2299
|
emit(
|
|
2168
2300
|
ctx,
|
|
2169
2301
|
{ servers: shown },
|
|
2170
|
-
() => table(shown.map((s) => ({ name: s.name, type: s.type ?? "stdio", target: s.url ?? [s.command, ...s.args ?? []].join(" ") })), [["name", "NAME"], ["type", "TYPE"], ["target", "URL / COMMAND"]], "No MCP servers.")
|
|
2302
|
+
() => table(shown.map((s) => ({ name: s.name, type: s.type ?? "stdio", target: s.url ?? [s.command, ...s.args ?? []].join(" ") })), [["name", "NAME"], ["type", "TYPE"], ["target", "URL / COMMAND"]], t("No MCP servers.", "Sin servidores MCP."))
|
|
2171
2303
|
);
|
|
2172
2304
|
return;
|
|
2173
2305
|
}
|
|
2174
|
-
if (action !== "set") throw usageError(`Unknown mcp action "${action}".`, MCP_USAGE);
|
|
2306
|
+
if (action !== "set") throw usageError(t(`Unknown mcp action "${action}".`, `Acci\xF3n de mcp desconocida "${action}".`), MCP_USAGE);
|
|
2175
2307
|
const file = str(ctx, "file");
|
|
2176
|
-
if (!file) throw usageError("Missing --file.", MCP_USAGE);
|
|
2308
|
+
if (!file) throw usageError(t("Missing --file.", "Falta --file."), MCP_USAGE);
|
|
2177
2309
|
const next = parseMcpFile(await readLocalText(file, MCP_USAGE), MCP_USAGE);
|
|
2178
|
-
if (hasMasked(next)) throw usageError("The file has masked values (\u2026***). Export with --show-secrets or use $secret:NAME references.", MCP_USAGE);
|
|
2310
|
+
if (hasMasked(next)) throw usageError(t("The file has masked values (\u2026***). Export with --show-secrets or use $secret:NAME references.", "El archivo trae valores tapados (\u2026***). Exporta con --show-secrets o usa referencias $secret:NOMBRE."), MCP_USAGE);
|
|
2179
2311
|
const cur = await getMcp(ctx, id);
|
|
2180
2312
|
const names = (l) => new Set(l.map((s) => s.name));
|
|
2181
2313
|
const diff = {
|
|
@@ -2191,7 +2323,7 @@ var mcp2 = {
|
|
|
2191
2323
|
console.log(`added: ${diff.added.join(", ") || "-"}
|
|
2192
2324
|
removed: ${diff.removed.join(", ") || "-"}
|
|
2193
2325
|
changed: ${diff.changed.join(", ") || "-"}`);
|
|
2194
|
-
console.log("(dry run: nothing changed; a real set restarts the agent)");
|
|
2326
|
+
console.log(t("(dry run: nothing changed; a real set restarts the agent)", "(simulaci\xF3n: no cambi\xF3 nada; un set de verdad reinicia el agente)"));
|
|
2195
2327
|
});
|
|
2196
2328
|
return;
|
|
2197
2329
|
}
|
|
@@ -2199,7 +2331,7 @@ changed: ${diff.changed.join(", ") || "-"}`);
|
|
|
2199
2331
|
emit(
|
|
2200
2332
|
ctx,
|
|
2201
2333
|
{ agentId: id, ...diff, servers: maskMcp(r.servers), restarted: true },
|
|
2202
|
-
() => console.log(`MCP servers: ${r.servers.map((s) => s.name).join(", ") || "none"} (agent restarted, new session)`)
|
|
2334
|
+
() => console.log(t(`MCP servers: ${r.servers.map((s) => s.name).join(", ") || "none"} (agent restarted, new session)`, `Servidores MCP: ${r.servers.map((s) => s.name).join(", ") || "ninguno"} (agente reiniciado, sesi\xF3n nueva)`))
|
|
2203
2335
|
);
|
|
2204
2336
|
}
|
|
2205
2337
|
};
|
|
@@ -2210,7 +2342,7 @@ var restart = {
|
|
|
2210
2342
|
async run(ctx) {
|
|
2211
2343
|
const id = need(ctx, 0, "agent", this.usage);
|
|
2212
2344
|
const r = await restartAgent(ctx, id);
|
|
2213
|
-
emit(ctx, { agentId: id, restarted: true, sandboxId: r.sandboxId }, () => console.log(`Restarted ${id}`));
|
|
2345
|
+
emit(ctx, { agentId: id, restarted: true, sandboxId: r.sandboxId }, () => console.log(t(`Restarted ${id}`, `Reiniciado ${id}`)));
|
|
2214
2346
|
}
|
|
2215
2347
|
};
|
|
2216
2348
|
var tryTurn = {
|
|
@@ -2224,19 +2356,19 @@ var tryTurn = {
|
|
|
2224
2356
|
async run(ctx) {
|
|
2225
2357
|
const id = need(ctx, 0, "agent", this.usage);
|
|
2226
2358
|
const text = ctx.args.slice(1).join(" ");
|
|
2227
|
-
if (!text) throw usageError("Missing <text>.", this.usage);
|
|
2359
|
+
if (!text) throw usageError(t("Missing <text>.", "Falta <text>."), this.usage);
|
|
2228
2360
|
let r;
|
|
2229
2361
|
try {
|
|
2230
2362
|
r = await api(ctx, "POST", `/agents/${id}/try`, { text, session: str(ctx, "session"), reset: bool(ctx, "reset") });
|
|
2231
2363
|
} catch (e) {
|
|
2232
2364
|
const err = toCliError(e);
|
|
2233
|
-
if (err.status === 409) err.hint = "A turn is already running in that session. Wait, or use another --session.";
|
|
2365
|
+
if (err.status === 409) err.hint = t("A turn is already running in that session. Wait, or use another --session.", "Ya hay un turno corriendo en esa sesi\xF3n. Espera, o usa otra --session.");
|
|
2234
2366
|
throw err;
|
|
2235
2367
|
}
|
|
2236
2368
|
emit(ctx, r, () => {
|
|
2237
|
-
console.log(r.text || "(no text)");
|
|
2369
|
+
console.log(r.text || t("(no text)", "(sin texto)"));
|
|
2238
2370
|
if (r.error) console.error(`error: ${r.error}`);
|
|
2239
|
-
console.error(r.session ?
|
|
2371
|
+
console.error(r.session ? `${t("session", "sesi\xF3n")}: ${r.session} \xB7 ${(r.ms / 1e3).toFixed(1)}s (${t("continue", "para seguir")}: --session ${r.session})` : `${(r.ms / 1e3).toFixed(1)}s`);
|
|
2240
2372
|
});
|
|
2241
2373
|
}
|
|
2242
2374
|
};
|
|
@@ -2272,28 +2404,28 @@ var doctor = {
|
|
|
2272
2404
|
check: "status",
|
|
2273
2405
|
ok: a.status === "running" || a.status === "suspended",
|
|
2274
2406
|
detail: startError(a) ? `error: ${startError(a)}` : a.status,
|
|
2275
|
-
...a.status === "error" ? { hint: `Its runtime did not start. Fix the cause, then: easybits agents restart ${id}` } : {},
|
|
2276
|
-
...a.status === "lost" ? { hint: `Its sandbox is gone. Destroy it (easybits agents destroy ${id}) or recreate it (easybits agents create --from <export.json>).` } : {}
|
|
2407
|
+
...a.status === "error" ? { hint: t(`Its runtime did not start. Fix the cause, then: easybits agents restart ${id}`, `Su runtime no arranc\xF3. Corrige la causa y luego: easybits agents restart ${id}`) } : {},
|
|
2408
|
+
...a.status === "lost" ? { hint: t(`Its sandbox is gone. Destroy it (easybits agents destroy ${id}) or recreate it (easybits agents create --from <export.json>).`, `Su sandbox ya no existe. Destr\xFAyelo (easybits agents destroy ${id}) o recr\xE9alo (easybits apply <archivo> --create).`) } : {}
|
|
2277
2409
|
});
|
|
2278
2410
|
if (a.expiresAt) {
|
|
2279
2411
|
const left = new Date(a.expiresAt).getTime() - Date.now();
|
|
2280
|
-
add({ check: "expires", ok: left > 0, detail: left > 0 ? `in ${Math.round(left / 6e4)} min` : `expired ${fmtDate(a.expiresAt)}` });
|
|
2412
|
+
add({ check: "expires", ok: left > 0, detail: left > 0 ? t(`in ${Math.round(left / 6e4)} min`, `en ${Math.round(left / 6e4)} min`) : t(`expired ${fmtDate(a.expiresAt)}`, `venci\xF3 ${fmtDate(a.expiresAt)}`) });
|
|
2281
2413
|
}
|
|
2282
2414
|
const sb = await soft(() => eb.sandboxes.get(a.sandboxId));
|
|
2283
2415
|
add(sb.ok ? { check: "sandbox", ok: sb.value.status !== "error", detail: `${a.sandboxId} ${sb.value.status}` } : { check: "sandbox", ok: false, detail: sb.error });
|
|
2284
2416
|
if (sb.ok && sb.value.status === "running") {
|
|
2285
2417
|
const p = await soft(() => getPrompt(ctx, id));
|
|
2286
2418
|
if (!p.ok && /agente_sin_maquina|no expone/.test(p.error)) {
|
|
2287
|
-
add({ check: "machine", ok: true, detail: `template ${a.template} has no machine config (prompt/skills/MCP): nothing more to check` });
|
|
2419
|
+
add({ check: "machine", ok: true, detail: t(`template ${a.template} has no machine config (prompt/skills/MCP): nothing more to check`, `el template ${a.template} no tiene config de m\xE1quina (prompt/skills/MCP): nada m\xE1s que revisar`) });
|
|
2288
2420
|
} else {
|
|
2289
|
-
add(p.ok ? { check: "prompt", ok: p.value.systemPrompt.trim().length > 0, detail: `${Buffer.byteLength(p.value.systemPrompt)} bytes, ${p.value.systemPromptMode}`, ...p.value.systemPrompt.trim() ? {} : { hint: `Set one: easybits agents set ${id} --prompt-file PROMPT.md` } } : { check: "prompt", ok: false, detail: p.error });
|
|
2421
|
+
add(p.ok ? { check: "prompt", ok: p.value.systemPrompt.trim().length > 0, detail: `${Buffer.byteLength(p.value.systemPrompt)} bytes, ${p.value.systemPromptMode}`, ...p.value.systemPrompt.trim() ? {} : { hint: t(`Set one: easybits agents set ${id} --prompt-file PROMPT.md`, `Ponle uno: easybits agents set ${id} --prompt-file PROMPT.md`) } } : { check: "prompt", ok: false, detail: p.error });
|
|
2290
2422
|
const s = await soft(() => listSkills(sb.value));
|
|
2291
2423
|
if (s.ok) {
|
|
2292
2424
|
add({ check: "skills", ok: true, detail: s.value.length ? s.value.map((x) => x.slug).join(", ") : "none" });
|
|
2293
2425
|
for (const skill of s.value) {
|
|
2294
2426
|
const md = await soft(() => sb.value.files.read(`${SKILLS_DIR}/${skill.slug}/SKILL.md`));
|
|
2295
2427
|
const fm = md.ok ? skillFrontmatter(md.value.content) : { error: "no SKILL.md" };
|
|
2296
|
-
if (fm.error) add({ check: `skill ${skill.slug}`, ok: false, detail: fm.error, hint: "The engine drops this skill silently. Fix SKILL.md and add it again." });
|
|
2428
|
+
if (fm.error) add({ check: `skill ${skill.slug}`, ok: false, detail: fm.error, hint: t("The engine drops this skill silently. Fix SKILL.md and add it again.", "El motor descarta esta skill en silencio. Corrige SKILL.md y vuelve a agregarla.") });
|
|
2297
2429
|
}
|
|
2298
2430
|
} else add({ check: "skills", ok: false, detail: s.error });
|
|
2299
2431
|
const m = await soft(() => getMcp(ctx, id));
|
|
@@ -2303,10 +2435,10 @@ var doctor = {
|
|
|
2303
2435
|
const ok = checks.every((c) => c.ok);
|
|
2304
2436
|
emit(ctx, { agentId: id, ok, checks }, () => {
|
|
2305
2437
|
for (const c of checks) {
|
|
2306
|
-
console.log(`${c.ok ? "ok " : "FAIL"} ${c.check.padEnd(14)} ${c.detail}`);
|
|
2438
|
+
console.log(`${c.ok ? "ok " : t("FAIL", "MAL ")} ${c.check.padEnd(14)} ${c.detail}`);
|
|
2307
2439
|
if (!c.ok && c.hint) console.log(` ${"".padEnd(14)} \u2192 ${c.hint}`);
|
|
2308
2440
|
}
|
|
2309
|
-
console.log(ok ? "\nNo problems found." : "\nProblems found.");
|
|
2441
|
+
console.log(ok ? t("\nNo problems found.", "\nSin problemas.") : t("\nProblems found.", "\nHay problemas."));
|
|
2310
2442
|
});
|
|
2311
2443
|
if (!ok) process.exitCode = EXIT.API;
|
|
2312
2444
|
}
|
|
@@ -2324,7 +2456,7 @@ async function buildExport(ctx, id, opts) {
|
|
|
2324
2456
|
const p = await soft(() => getPrompt(ctx, id));
|
|
2325
2457
|
if (!p.ok) {
|
|
2326
2458
|
if (/agente_sin_maquina|no expone/.test(p.error)) return out;
|
|
2327
|
-
throw new CliError(`Could not read the agent's config: ${p.error}`, EXIT.API, `Is it running? easybits agents doctor ${id}`);
|
|
2459
|
+
throw new CliError(t(`Could not read the agent's config: ${p.error}`, `No se pudo leer la config del agente: ${p.error}`), EXIT.API, t(`Is it running? easybits agents doctor ${id}`, `\xBFEst\xE1 corriendo? easybits agents doctor ${id}`));
|
|
2328
2460
|
}
|
|
2329
2461
|
out.systemPrompt = p.value.systemPrompt;
|
|
2330
2462
|
out.systemPromptMode = p.value.systemPromptMode;
|
|
@@ -2358,31 +2490,31 @@ var exportAgent = {
|
|
|
2358
2490
|
if (out) {
|
|
2359
2491
|
writeFileSync4(out, JSON.stringify(x, null, 2) + "\n", { mode: bool(ctx, "show-secrets") ? 384 : 420 });
|
|
2360
2492
|
const summary = { out: resolve(out), template: x.template, skills: x.skills?.length ?? 0, mcpServers: x.mcpServers?.length ?? 0, files: x.files?.length ?? 0 };
|
|
2361
|
-
emit(ctx, summary, () => console.log(`Exported ${id} to ${summary.out} (${summary.skills} skill(s), ${summary.mcpServers} MCP, ${summary.files} file(s))`));
|
|
2493
|
+
emit(ctx, summary, () => console.log(t(`Exported ${id} to ${summary.out} (${summary.skills} skill(s), ${summary.mcpServers} MCP, ${summary.files} file(s))`, `Exportado ${id} a ${summary.out} (${summary.skills} skill(s), ${summary.mcpServers} MCP, ${summary.files} archivo(s))`)));
|
|
2362
2494
|
return;
|
|
2363
2495
|
}
|
|
2364
2496
|
process.stdout.write(JSON.stringify(x, null, 2) + "\n");
|
|
2365
2497
|
}
|
|
2366
2498
|
};
|
|
2367
2499
|
function readExport(path, usage2) {
|
|
2368
|
-
if (!existsSync6(path)) throw usageError(`File not found: ${path}`, usage2);
|
|
2500
|
+
if (!existsSync6(path)) throw usageError(t(`File not found: ${path}`, `No existe el archivo: ${path}`), usage2);
|
|
2369
2501
|
let x;
|
|
2370
2502
|
try {
|
|
2371
2503
|
x = JSON.parse(readFileSync6(path, "utf8"));
|
|
2372
2504
|
} catch (e) {
|
|
2373
|
-
throw usageError(`${path} is not JSON: ${e.message}`, usage2);
|
|
2505
|
+
throw usageError(t(`${path} is not JSON: ${e.message}`, `${path} no es JSON: ${e.message}`), usage2);
|
|
2374
2506
|
}
|
|
2375
|
-
if (x?.kind !== "easybits.agent" || !x.template) throw usageError(`${path} is not an agent export (easybits agents export).`, usage2);
|
|
2507
|
+
if (x?.kind !== "easybits.agent" || !x.template) throw usageError(t(`${path} is not an agent export (easybits agents export).`, `${path} no es un export de agente (easybits agents export).`), usage2);
|
|
2376
2508
|
return x;
|
|
2377
2509
|
}
|
|
2378
2510
|
async function waitMachine(ctx, id, ms = 12e4) {
|
|
2379
2511
|
const until = Date.now() + ms;
|
|
2380
|
-
if (!ctx.json) process.stderr.write("Waiting for the agent's machine");
|
|
2512
|
+
if (!ctx.json) process.stderr.write(t("Waiting for the agent's machine", "Esperando la m\xE1quina del agente"));
|
|
2381
2513
|
let last = "";
|
|
2382
2514
|
while (Date.now() < until) {
|
|
2383
2515
|
const p = await soft(() => getPrompt(ctx, id));
|
|
2384
2516
|
if (p.ok) {
|
|
2385
|
-
if (!ctx.json) process.stderr.write(" ready\n");
|
|
2517
|
+
if (!ctx.json) process.stderr.write(t(" ready\n", " lista\n"));
|
|
2386
2518
|
return;
|
|
2387
2519
|
}
|
|
2388
2520
|
last = p.error;
|
|
@@ -2390,7 +2522,7 @@ async function waitMachine(ctx, id, ms = 12e4) {
|
|
|
2390
2522
|
await new Promise((r) => setTimeout(r, 3e3));
|
|
2391
2523
|
}
|
|
2392
2524
|
if (!ctx.json) process.stderr.write("\n");
|
|
2393
|
-
throw new CliError(`Agent ${id} was created but its machine did not answer in ${ms / 1e3}s: ${last}`, EXIT.API, `Check it: easybits agents doctor ${id}`);
|
|
2525
|
+
throw new CliError(t(`Agent ${id} was created but its machine did not answer in ${ms / 1e3}s: ${last}`, `El agente ${id} se cre\xF3 pero su m\xE1quina no contest\xF3 en ${ms / 1e3}s: ${last}`), EXIT.API, t(`Check it: easybits agents doctor ${id}`, `Rev\xEDsalo: easybits agents doctor ${id}`));
|
|
2394
2526
|
}
|
|
2395
2527
|
async function applyExport(ctx, id, x, opts) {
|
|
2396
2528
|
const copied = { skills: [], files: [], skippedFiles: [] };
|
|
@@ -2488,8 +2620,9 @@ var exportSpec = {
|
|
|
2488
2620
|
}
|
|
2489
2621
|
if (isFileTarget(out)) {
|
|
2490
2622
|
writeFileSync5(out, /\.json$/i.test(out) ? JSON.stringify(await getJson(), null, 2) + "\n" : await getYaml());
|
|
2491
|
-
emit(ctx, { saved: resolve2(out) }, () => console.log(`Saved ${resolve2(out)}
|
|
2492
|
-
Edit it, then: easybits apply ${out} --dry-run`)
|
|
2623
|
+
emit(ctx, { saved: resolve2(out) }, () => console.log(t(`Saved ${resolve2(out)}
|
|
2624
|
+
Edit it, then: easybits apply ${out} --dry-run`, `Guardado ${resolve2(out)}
|
|
2625
|
+
Ed\xEDtalo y luego: easybits apply ${out} --dry-run`)));
|
|
2493
2626
|
return;
|
|
2494
2627
|
}
|
|
2495
2628
|
const yaml = await getYaml();
|
|
@@ -2511,17 +2644,17 @@ Edit it, then: easybits apply ${out} --dry-run`));
|
|
|
2511
2644
|
ctx,
|
|
2512
2645
|
summary,
|
|
2513
2646
|
() => console.log(
|
|
2514
|
-
|
|
2515
|
-
Edit it, then: easybits apply ${out} --dry-run`
|
|
2647
|
+
`${t("Saved", "Guardado")} ${dir}/agent.yaml${summary.skills.length ? ` + skills/ (${summary.skills.join(", ")})` : ""}${summary.files.length ? ` + files/ (${summary.files.length})` : ""}
|
|
2648
|
+
${t("Edit it, then", "Ed\xEDtalo y luego")}: easybits apply ${out} --dry-run`
|
|
2516
2649
|
)
|
|
2517
2650
|
);
|
|
2518
2651
|
}
|
|
2519
2652
|
};
|
|
2520
2653
|
function readTarget(target, usage2) {
|
|
2521
|
-
if (!existsSync8(target)) throw usageError(`Not found: ${target}`, usage2);
|
|
2654
|
+
if (!existsSync8(target)) throw usageError(t(`Not found: ${target}`, `No existe: ${target}`), usage2);
|
|
2522
2655
|
if (statSync3(target).isDirectory()) {
|
|
2523
2656
|
const name = SPEC_NAMES.find((n) => existsSync8(join5(target, n)));
|
|
2524
|
-
if (!name) throw usageError(`${target} has no agent.yaml.`, usage2);
|
|
2657
|
+
if (!name) throw usageError(t(`${target} has no agent.yaml.`, `${target} no tiene agent.yaml.`), usage2);
|
|
2525
2658
|
return { text: readFileSync8(join5(target, name), "utf8"), dir: target };
|
|
2526
2659
|
}
|
|
2527
2660
|
return { text: readFileSync8(target, "utf8") };
|
|
@@ -2540,7 +2673,7 @@ async function applyTarget(ctx, target, o) {
|
|
|
2540
2673
|
const { secrets, missing } = secretsFor(text);
|
|
2541
2674
|
const uploads = dir ? readUploads(dir) : {};
|
|
2542
2675
|
if (o.create) {
|
|
2543
|
-
if (o.agent) throw usageError("Use --agent or --create, not both.", o.usage);
|
|
2676
|
+
if (o.agent) throw usageError(t("Use --agent or --create, not both.", "Usa --agent o --create, no los dos."), o.usage);
|
|
2544
2677
|
const body2 = { spec: text, secrets, ...o.name ? { name: o.name } : {} };
|
|
2545
2678
|
const plan2 = await api(ctx, "POST", "/agents/apply", { ...body2, dryRun: true });
|
|
2546
2679
|
if (o.dryRun) {
|
|
@@ -2548,14 +2681,15 @@ async function applyTarget(ctx, target, o) {
|
|
|
2548
2681
|
printPlan(plan2.plan);
|
|
2549
2682
|
for (const s of Object.keys(uploads.skills ?? {})) console.log(`+ skill ${s}`);
|
|
2550
2683
|
for (const f of Object.keys(uploads.files ?? {})) console.log(`+ file ${f}`);
|
|
2551
|
-
console.log("(dry run: nothing created)");
|
|
2684
|
+
console.log(t("(dry run: nothing created)", "(simulaci\xF3n: no se cre\xF3 nada)"));
|
|
2552
2685
|
});
|
|
2553
2686
|
return;
|
|
2554
2687
|
}
|
|
2555
2688
|
const created = await api(ctx, "POST", "/agents/apply", body2);
|
|
2556
2689
|
const id2 = created.agentId;
|
|
2557
|
-
if (!ctx.json) process.stderr.write(`Created ${id2}; applying the rest\u2026
|
|
2558
|
-
`
|
|
2690
|
+
if (!ctx.json) process.stderr.write(t(`Created ${id2}; applying the rest\u2026
|
|
2691
|
+
`, `Creado ${id2}; aplicando lo dem\xE1s\u2026
|
|
2692
|
+
`));
|
|
2559
2693
|
let rest;
|
|
2560
2694
|
if (/\n?(skills|files):/m.test(text) || uploads.skills || uploads.files) {
|
|
2561
2695
|
await waitMachine(ctx, id2).catch(() => {
|
|
@@ -2566,17 +2700,18 @@ async function applyTarget(ctx, target, o) {
|
|
|
2566
2700
|
const res = { agentId: id2, sandboxId: created.sandboxId, plan: [...created.plan, ...extra], applied: rest?.applied, failed: rest?.failed };
|
|
2567
2701
|
emit(ctx, res, () => {
|
|
2568
2702
|
printPlan(res.plan);
|
|
2569
|
-
console.log(`Created ${id2}.${res.failed ? "" : ` Try it: easybits agents try ${id2} "hello"`}`);
|
|
2570
|
-
if (res.failed) console.error(`Stopped at \xAB${res.failed.what}\xBB: ${res.failed.error}`);
|
|
2703
|
+
console.log(t(`Created ${id2}.${res.failed ? "" : ` Try it: easybits agents try ${id2} "hello"`}`, `Creado ${id2}.${res.failed ? "" : ` Pru\xE9balo: easybits agents try ${id2} "hola"`}`));
|
|
2704
|
+
if (res.failed) console.error(t(`Stopped at \xAB${res.failed.what}\xBB: ${res.failed.error}`, `Se detuvo en \xAB${res.failed.what}\xBB: ${serverText(res.failed.error)}`));
|
|
2571
2705
|
});
|
|
2572
2706
|
if (res.failed) process.exitCode = EXIT.API;
|
|
2573
2707
|
return;
|
|
2574
2708
|
}
|
|
2575
2709
|
const ref = o.agent ?? specId(text);
|
|
2576
|
-
if (!ref) throw usageError("The file has no agent id.", "Point it at one with --agent <name|id>, or make a new one with --create.");
|
|
2710
|
+
if (!ref) throw usageError(t("The file has no agent id.", "El archivo no trae id de agente."), t("Point it at one with --agent <name|id>, or make a new one with --create.", "Ap\xFAntalo a uno con --agent <nombre|id>, o crea uno nuevo con --create."));
|
|
2577
2711
|
const id = await resolveRef(ctx, "agent", ref);
|
|
2578
|
-
if (missing.length && !ctx.json) process.stderr.write(`Not in your environment (keep their current value): ${missing.join(", ")}
|
|
2579
|
-
`)
|
|
2712
|
+
if (missing.length && !ctx.json) process.stderr.write(t(`Not in your environment (keep their current value): ${missing.join(", ")}
|
|
2713
|
+
`, `No est\xE1n en tu entorno (conservan su valor de hoy): ${missing.join(", ")}
|
|
2714
|
+
`));
|
|
2580
2715
|
const path = `/agents/${id}/apply`;
|
|
2581
2716
|
const body = { spec: text, secrets, ...uploads, ...o.prune ? { prune: true } : {} };
|
|
2582
2717
|
const plan = await api(ctx, "POST", path, { ...body, dryRun: true });
|
|
@@ -2584,20 +2719,22 @@ async function applyTarget(ctx, target, o) {
|
|
|
2584
2719
|
if (o.dryRun || !actionable.length) {
|
|
2585
2720
|
emit(ctx, plan, () => {
|
|
2586
2721
|
if (plan.plan.length) printPlan(plan.plan);
|
|
2587
|
-
console.log(
|
|
2722
|
+
console.log(
|
|
2723
|
+
actionable.length ? t("(dry run: nothing changed)", "(simulaci\xF3n: no cambi\xF3 nada)") : plan.plan.length ? t("Nothing to apply (see the ! lines).", "Nada que aplicar (mira los renglones con !).") : t("No changes: the agent already matches the file.", "Sin cambios: el agente ya coincide con el archivo.")
|
|
2724
|
+
);
|
|
2588
2725
|
});
|
|
2589
2726
|
return;
|
|
2590
2727
|
}
|
|
2591
2728
|
requireYesIfHeadless(ctx);
|
|
2592
2729
|
await confirm(ctx, `${plan.plan.map(line).join("\n")}
|
|
2593
|
-
Apply ${actionable.length} change(s) to ${id}?`);
|
|
2730
|
+
${t(`Apply ${actionable.length} change(s) to ${id}?`, `\xBFAplicar ${actionable.length} cambio(s) a ${id}?`)}`);
|
|
2594
2731
|
const r = await api(ctx, "POST", path, body);
|
|
2595
2732
|
const backup = r.before ? saveBackup(id, r.before) : void 0;
|
|
2596
2733
|
const { before: _b, ...shown } = r;
|
|
2597
2734
|
emit(ctx, { ...shown, ...backup ? { backup } : {} }, () => {
|
|
2598
|
-
console.log(`Applied ${r.applied?.length ?? 0} change(s) to ${id}.`);
|
|
2599
|
-
if (backup) console.log(`Before (to roll back: easybits apply ${backup} --agent ${id}): ${backup}`);
|
|
2600
|
-
if (r.failed) console.error(`Stopped at \xAB${r.failed.what}\xBB: ${r.failed.error}`);
|
|
2735
|
+
console.log(t(`Applied ${r.applied?.length ?? 0} change(s) to ${id}.`, `${r.applied?.length ?? 0} cambio(s) aplicados a ${id}.`));
|
|
2736
|
+
if (backup) console.log(t(`Before (to roll back: easybits apply ${backup} --agent ${id}): ${backup}`, `Lo de antes (para volver: easybits apply ${backup} --agent ${id}): ${backup}`));
|
|
2737
|
+
if (r.failed) console.error(t(`Stopped at \xAB${r.failed.what}\xBB: ${r.failed.error}`, `Se detuvo en \xAB${r.failed.what}\xBB: ${serverText(r.failed.error)}`));
|
|
2601
2738
|
});
|
|
2602
2739
|
if (r.failed) process.exitCode = EXIT.API;
|
|
2603
2740
|
}
|
|
@@ -2667,7 +2804,7 @@ var agents = {
|
|
|
2667
2804
|
() => table(
|
|
2668
2805
|
items.map((a) => ({ ...a, createdAt: fmtDate(a.createdAt) })),
|
|
2669
2806
|
[["agentId", "ID"], ["name", "NAME"], ["template", "TEMPLATE"], ["status", "STATUS"], ["createdAt", "CREATED"]],
|
|
2670
|
-
"No agents. Create one: easybits agents create --template <template>"
|
|
2807
|
+
t("No agents. Create one: easybits agents create --template <template>", "No hay agentes. Crea uno: easybits agents create --template <template>")
|
|
2671
2808
|
)
|
|
2672
2809
|
);
|
|
2673
2810
|
}
|
|
@@ -2702,36 +2839,36 @@ var agents = {
|
|
|
2702
2839
|
async run(ctx) {
|
|
2703
2840
|
const like = str(ctx, "like");
|
|
2704
2841
|
const from = str(ctx, "from");
|
|
2705
|
-
if (like && from) throw usageError("Use --like or --from, not both.", this.usage);
|
|
2842
|
+
if (like && from) throw usageError(t("Use --like or --from, not both.", "Usa --like o --from, no los dos."), this.usage);
|
|
2706
2843
|
const promptInline = str(ctx, "prompt");
|
|
2707
2844
|
const promptFile = str(ctx, "prompt-file");
|
|
2708
|
-
if (promptInline != null && promptFile) throw usageError("Use --prompt or --prompt-file, not both.", this.usage);
|
|
2845
|
+
if (promptInline != null && promptFile) throw usageError(t("Use --prompt or --prompt-file, not both.", "Usa --prompt o --prompt-file, no los dos."), this.usage);
|
|
2709
2846
|
const mode = str(ctx, "prompt-mode");
|
|
2710
|
-
if (mode && mode !== "append" && mode !== "replace") throw usageError("--prompt-mode must be append or replace.", this.usage);
|
|
2711
|
-
if (promptFile && !existsSync9(promptFile)) throw usageError(`File not found: ${promptFile}`, this.usage);
|
|
2847
|
+
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 && !existsSync9(promptFile)) throw usageError(t(`File not found: ${promptFile}`, `No existe el archivo: ${promptFile}`), this.usage);
|
|
2712
2849
|
const mcpFile = str(ctx, "mcp-file");
|
|
2713
|
-
if (mcpFile && !existsSync9(mcpFile)) throw usageError(`File not found: ${mcpFile}`, this.usage);
|
|
2714
|
-
if (bool(ctx, "copy-files") && !like && !from) throw usageError("--copy-files needs --like or --from.", this.usage);
|
|
2850
|
+
if (mcpFile && !existsSync9(mcpFile)) throw usageError(t(`File not found: ${mcpFile}`, `No existe el archivo: ${mcpFile}`), this.usage);
|
|
2851
|
+
if (bool(ctx, "copy-files") && !like && !from) throw usageError(t("--copy-files needs --like or --from.", "--copy-files necesita --like o --from."), this.usage);
|
|
2715
2852
|
if (from && !isLegacyExport(from)) {
|
|
2716
2853
|
await applyTarget(ctx, from, { create: true, name: str(ctx, "name"), dryRun: bool(ctx, "dry-run"), usage: this.usage });
|
|
2717
2854
|
return;
|
|
2718
2855
|
}
|
|
2719
2856
|
const source = from ? readExport(from, this.usage) : like ? await buildExport(ctx, await resolveRef(ctx, "agent", like), { secrets: true, fileContents: bool(ctx, "copy-files") }) : void 0;
|
|
2720
2857
|
const template = str(ctx, "template") ?? source?.template;
|
|
2721
|
-
if (!template) throw usageError("Missing --template (or --like / --from).", this.usage);
|
|
2858
|
+
if (!template) throw usageError(t("Missing --template (or --like / --from).", "Falta --template (o --like / --from)."), this.usage);
|
|
2722
2859
|
const env = await envFrom(ctx, this.usage);
|
|
2723
2860
|
const prompt = promptInline ?? (promptFile ? readFileSync9(promptFile, "utf8") : source?.systemPrompt);
|
|
2724
2861
|
const promptMode = mode ?? source?.systemPromptMode;
|
|
2725
2862
|
const multiline = !!prompt && /[\r\n]/.test(prompt.trimEnd());
|
|
2726
2863
|
const promptAfter = multiline ? prompt : void 0;
|
|
2727
2864
|
if (multiline && !MACHINE_TEMPLATES.has(template)) {
|
|
2728
|
-
throw usageError(`A multi-line prompt needs a template with a machine (${[...MACHINE_TEMPLATES].join(", ")}).`, this.usage);
|
|
2865
|
+
throw usageError(t(`A multi-line prompt needs a template with a machine (${[...MACHINE_TEMPLATES].join(", ")}).`, `Un prompt de varios renglones necesita un template con m\xE1quina (${[...MACHINE_TEMPLATES].join(", ")}).`), this.usage);
|
|
2729
2866
|
}
|
|
2730
2867
|
if (prompt && !multiline) env.SYSTEM_PROMPT = prompt.trimEnd();
|
|
2731
2868
|
if (promptMode && (prompt || mode) && !multiline) env.SYSTEM_PROMPT_MODE = promptMode;
|
|
2732
2869
|
const mcpServers = mcpFile ? parseMcpFile(readFileSync9(mcpFile, "utf8"), this.usage) : source?.mcpServers;
|
|
2733
2870
|
if (mcpServers && hasMasked(mcpServers)) {
|
|
2734
|
-
throw usageError(`${from ?? "the source"} has masked MCP values (\u2026***).`, "Re-export with --show-secrets, or pass --mcp-file");
|
|
2871
|
+
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"));
|
|
2735
2872
|
}
|
|
2736
2873
|
const name = str(ctx, "name") ?? (source?.name ? `${source.name}-copy` : void 0);
|
|
2737
2874
|
const plan = {
|
|
@@ -2748,8 +2885,8 @@ var agents = {
|
|
|
2748
2885
|
if (bool(ctx, "dry-run")) {
|
|
2749
2886
|
emit(ctx, { dryRun: true, ...plan }, () => {
|
|
2750
2887
|
for (const [k, v] of Object.entries(plan)) console.log(`${k.padEnd(12)} ${Array.isArray(v) ? v.join(", ") || "-" : v ?? "-"}`);
|
|
2751
|
-
if (source) console.log("Env is never copied: pass the engine's keys with --dotenv.");
|
|
2752
|
-
console.log("(dry run: nothing created)");
|
|
2888
|
+
if (source) console.log(t("Env is never copied: pass the engine's keys with --dotenv.", "El env nunca se copia: pasa las llaves del motor con --dotenv."));
|
|
2889
|
+
console.log(t("(dry run: nothing created)", "(simulaci\xF3n: no se cre\xF3 nada)"));
|
|
2753
2890
|
});
|
|
2754
2891
|
return;
|
|
2755
2892
|
}
|
|
@@ -2764,11 +2901,11 @@ var agents = {
|
|
|
2764
2901
|
const copied = source ? await applyExport(ctx, a.agentId, source, { files: bool(ctx, "copy-files") }) : void 0;
|
|
2765
2902
|
if (promptAfter) await api(ctx, "PATCH", `/agents/${a.agentId}`, { systemPrompt: promptAfter, ...promptMode ? { systemPromptMode: promptMode } : {} });
|
|
2766
2903
|
emit(ctx, { ...a, ...copied ? { copied } : {} }, () => {
|
|
2767
|
-
console.log(
|
|
2904
|
+
console.log(`${t("Agent: ", "Agente: ")} ${a.agentId}`);
|
|
2768
2905
|
console.log(`Sandbox: ${a.sandboxId}`);
|
|
2769
2906
|
console.log(`URL: ${a.agentUrl}`);
|
|
2770
|
-
if (copied) console.log(`Copied: ${copied.skills.length} skill(s), ${copied.files.length} file(s)${copied.skippedFiles.length ? ` (${copied.skippedFiles.length} skipped: export without --with-files)` : ""}`);
|
|
2771
|
-
console.log(`Talk to it: easybits agents try ${a.agentId} "hello"`);
|
|
2907
|
+
if (copied) console.log(t(`Copied: ${copied.skills.length} skill(s), ${copied.files.length} file(s)${copied.skippedFiles.length ? ` (${copied.skippedFiles.length} skipped: export without --with-files)` : ""}`, `Copiado: ${copied.skills.length} skill(s), ${copied.files.length} archivo(s)${copied.skippedFiles.length ? ` (${copied.skippedFiles.length} sin copiar: export sin --with-files)` : ""}`));
|
|
2908
|
+
console.log(t(`Talk to it: easybits agents try ${a.agentId} "hello"`, `H\xE1blale: easybits agents try ${a.agentId} "hola"`));
|
|
2772
2909
|
});
|
|
2773
2910
|
}
|
|
2774
2911
|
},
|
|
@@ -2781,7 +2918,7 @@ var agents = {
|
|
|
2781
2918
|
async run(ctx) {
|
|
2782
2919
|
const id = need(ctx, 0, "agent", this.usage);
|
|
2783
2920
|
const content = ctx.args.slice(1).join(" ");
|
|
2784
|
-
if (!content) throw usageError("Missing <text>.", this.usage);
|
|
2921
|
+
if (!content) throw usageError(t("Missing <text>.", "Falta <text>."), this.usage);
|
|
2785
2922
|
const eb = await getClient(ctx);
|
|
2786
2923
|
const params = { content, sessionId: str(ctx, "session") };
|
|
2787
2924
|
if (ctx.json) {
|
|
@@ -2802,10 +2939,10 @@ var agents = {
|
|
|
2802
2939
|
async run(ctx) {
|
|
2803
2940
|
const id = need(ctx, 0, "agent", this.usage);
|
|
2804
2941
|
requireYesIfHeadless(ctx);
|
|
2805
|
-
await confirm(ctx, `Destroy agent ${id} and its sandbox (including /data)?`, { typeName: id });
|
|
2942
|
+
await confirm(ctx, t(`Destroy agent ${id} and its sandbox (including /data)?`, `\xBFDestruir el agente ${id} y su sandbox (con /data)?`), { typeName: id });
|
|
2806
2943
|
const eb = await getClient(ctx);
|
|
2807
2944
|
const r = await eb.destroyAgent(id);
|
|
2808
|
-
emit(ctx, { ...r, agentId: id }, () => console.log(`Destroyed ${id}`));
|
|
2945
|
+
emit(ctx, { ...r, agentId: id }, () => console.log(t(`Destroyed ${id}`, `Destruido ${id}`)));
|
|
2809
2946
|
}
|
|
2810
2947
|
},
|
|
2811
2948
|
set,
|
|
@@ -2853,9 +2990,9 @@ var docs = {
|
|
|
2853
2990
|
const res = await fetch(url);
|
|
2854
2991
|
if (!res.ok) {
|
|
2855
2992
|
throw new CliError(
|
|
2856
|
-
`Docs section "${section}" not found (${res.status}).`,
|
|
2993
|
+
t(`Docs section "${section}" not found (${res.status}).`, `No existe la secci\xF3n "${section}" de los docs (${res.status}).`),
|
|
2857
2994
|
2,
|
|
2858
|
-
`See the section list: ${base}/llms.txt`,
|
|
2995
|
+
t(`See the section list: ${base}/llms.txt`, `Mira la lista de secciones: ${base}/llms.txt`),
|
|
2859
2996
|
"usage",
|
|
2860
2997
|
res.status
|
|
2861
2998
|
);
|
|
@@ -2911,19 +3048,19 @@ async function latestVersion() {
|
|
|
2911
3048
|
if (c.latest && c.checkedAt && Date.now() - c.checkedAt < DAY) return c.latest;
|
|
2912
3049
|
return await fetchLatest() ?? c.latest ?? null;
|
|
2913
3050
|
}
|
|
2914
|
-
function cachedNewer(
|
|
2915
|
-
if (updateCheckDisabled() ||
|
|
3051
|
+
function cachedNewer(current2) {
|
|
3052
|
+
if (updateCheckDisabled() || current2 === "dev") return null;
|
|
2916
3053
|
const l = readCache().latest;
|
|
2917
|
-
return l && newer(l,
|
|
3054
|
+
return l && newer(l, current2) ? l : null;
|
|
2918
3055
|
}
|
|
2919
|
-
async function updateNotice(
|
|
2920
|
-
if (
|
|
3056
|
+
async function updateNotice(current2) {
|
|
3057
|
+
if (current2 === "dev") return null;
|
|
2921
3058
|
const latest = await latestVersion();
|
|
2922
|
-
return latest && newer(latest,
|
|
3059
|
+
return latest && newer(latest, current2) ? t(`Update available: ${current2} \u2192 ${latest}. Run: ${UPGRADE}`, `Hay versi\xF3n nueva: ${current2} \u2192 ${latest}. Corre: ${UPGRADE}`) : null;
|
|
2923
3060
|
}
|
|
2924
3061
|
|
|
2925
3062
|
// src/commands/doctor.ts
|
|
2926
|
-
var VERSION = true ? "0.
|
|
3063
|
+
var VERSION = true ? "0.11.0" : "dev";
|
|
2927
3064
|
var doctor2 = {
|
|
2928
3065
|
name: "doctor",
|
|
2929
3066
|
group: "Account",
|
|
@@ -2936,49 +3073,49 @@ var doctor2 = {
|
|
|
2936
3073
|
async run(ctx) {
|
|
2937
3074
|
const checks = [];
|
|
2938
3075
|
const major = Number(process.versions.node.split(".")[0]);
|
|
2939
|
-
checks.push({ check: "node", ok: major >= 22, detail: process.versions.node, ...major >= 22 ? {} : { hint: "The CLI needs Node 22 or newer." } });
|
|
2940
|
-
if (updateCheckDisabled()) checks.push({ check: "version", ok: true, detail: `${VERSION} (update check off)` });
|
|
3076
|
+
checks.push({ check: "node", ok: major >= 22, detail: process.versions.node, ...major >= 22 ? {} : { hint: t("The CLI needs Node 22 or newer.", "El CLI necesita Node 22 o m\xE1s nuevo.") } });
|
|
3077
|
+
if (updateCheckDisabled()) checks.push({ check: "version", ok: true, detail: t(`${VERSION} (update check off)`, `${VERSION} (sin revisar versiones)`) });
|
|
2941
3078
|
else {
|
|
2942
3079
|
const latest = await fetchLatest(3e3);
|
|
2943
3080
|
const old = latest && VERSION !== "dev" && newer(latest, VERSION);
|
|
2944
3081
|
checks.push({
|
|
2945
3082
|
check: "version",
|
|
2946
3083
|
ok: !old,
|
|
2947
|
-
detail: latest ? `${VERSION}${old ? ` (latest ${latest})` : " (latest)"}` : `${VERSION} (npm unreachable)`,
|
|
2948
|
-
...old ? { hint:
|
|
3084
|
+
detail: latest ? `${VERSION}${old ? ` (${t("latest", "la m\xE1s nueva")} ${latest})` : t(" (latest)", " (la m\xE1s nueva)")}` : t(`${VERSION} (npm unreachable)`, `${VERSION} (npm no contesta)`),
|
|
3085
|
+
...old ? { hint: `${t("Run", "Corre")}: ${UPGRADE}` } : {}
|
|
2949
3086
|
});
|
|
2950
3087
|
}
|
|
2951
3088
|
const base = await resolveBaseUrl6();
|
|
2952
3089
|
const health = await fetch(`${base.replace(/\/+$/, "")}/api/health`, { signal: AbortSignal.timeout(5e3) }).then((r) => ({ ok: r.ok, detail: `${base} (${r.status})` })).catch((e) => ({ ok: false, detail: `${base}: ${e.message}` }));
|
|
2953
|
-
checks.push({ check: "api", ...health, ...health.ok ? {} : { hint: "Check your network or EASYBITS_URL." } });
|
|
3090
|
+
checks.push({ check: "api", ...health, ...health.ok ? {} : { hint: t("Check your network or EASYBITS_URL.", "Revisa tu red o EASYBITS_URL.") } });
|
|
2954
3091
|
const rc = readRc();
|
|
2955
3092
|
const source = process.env.EASYBITS_API_KEY ? "env EASYBITS_API_KEY" : ctx.token ? "--token" : rc.oauth?.accessToken ? "browser session (~/.easybitsrc)" : rc.apiKey ? "API key (~/.easybitsrc)" : null;
|
|
2956
|
-
if (!source) checks.push({ check: "credential", ok: false, detail: "not logged in", hint: "Run: easybits login (or set EASYBITS_API_KEY)" });
|
|
3093
|
+
if (!source) checks.push({ check: "credential", ok: false, detail: t("not logged in", "sin sesi\xF3n"), hint: t("Run: easybits login (or set EASYBITS_API_KEY)", "Corre: easybits login (o define EASYBITS_API_KEY)") });
|
|
2957
3094
|
else {
|
|
2958
3095
|
try {
|
|
2959
3096
|
const email = await fetchEmail(await resolveCredential(ctx));
|
|
2960
|
-
checks.push({ check: "credential", ok: true, detail: `${email ?? "(unknown email)"} via ${source}` });
|
|
3097
|
+
checks.push({ check: "credential", ok: true, detail: `${email ?? t("(unknown email)", "(correo desconocido)")} ${t("via", "por")} ${source}` });
|
|
2961
3098
|
} catch (e) {
|
|
2962
3099
|
const err = toCliError(e);
|
|
2963
3100
|
checks.push({ check: "credential", ok: false, detail: `${source}: ${err.message}`, hint: err.hint });
|
|
2964
3101
|
}
|
|
2965
3102
|
if (process.env.EASYBITS_API_KEY && (rc.apiKey || rc.oauth)) {
|
|
2966
|
-
checks.push({ check: "credential-env", ok: true, detail: "EASYBITS_API_KEY wins over the saved login in ~/.easybitsrc" });
|
|
3103
|
+
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") });
|
|
2967
3104
|
}
|
|
2968
3105
|
}
|
|
2969
3106
|
const rcPath = join7(homedir5(), ".easybitsrc");
|
|
2970
3107
|
if (existsSync10(rcPath)) {
|
|
2971
3108
|
const mode = statSync5(rcPath).mode & 511;
|
|
2972
3109
|
const ok2 = (mode & 63) === 0;
|
|
2973
|
-
checks.push({ check: "rc-file", ok: ok2, detail: `${rcPath} ${mode.toString(8).padStart(3, "0")}`, ...ok2 ? {} : { hint: `It holds tokens. Run: chmod 600 ${rcPath}` } });
|
|
3110
|
+
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}`) } });
|
|
2974
3111
|
}
|
|
2975
3112
|
const ok = checks.every((c) => c.ok);
|
|
2976
3113
|
emit(ctx, { ok, checks }, () => {
|
|
2977
3114
|
for (const c of checks) {
|
|
2978
|
-
console.log(`${c.ok ? "ok " : "FAIL"} ${c.check.padEnd(14)} ${c.detail}`);
|
|
3115
|
+
console.log(`${c.ok ? "ok " : t("FAIL", "MAL ")} ${c.check.padEnd(14)} ${c.detail}`);
|
|
2979
3116
|
if (!c.ok && c.hint) console.log(` ${"".padEnd(14)} \u2192 ${c.hint}`);
|
|
2980
3117
|
}
|
|
2981
|
-
console.log(ok ? "\nAll good." : "\nProblems found.");
|
|
3118
|
+
console.log(ok ? t("\nAll good.", "\nTodo bien.") : t("\nProblems found.", "\nHay problemas."));
|
|
2982
3119
|
});
|
|
2983
3120
|
if (!ok) process.exitCode = EXIT.API;
|
|
2984
3121
|
}
|
|
@@ -3033,13 +3170,187 @@ function didYouMean(input, candidates, prefix, canonical = (n) => n) {
|
|
|
3033
3170
|
const hits = [...new Set(suggest(input, candidates).map(canonical))].slice(0, 3);
|
|
3034
3171
|
if (!hits.length) return void 0;
|
|
3035
3172
|
return `Did you mean this?
|
|
3036
|
-
${hits.map((
|
|
3173
|
+
${hits.map((h2) => ` ${prefix}${h2}`).join("\n")}`;
|
|
3037
3174
|
}
|
|
3038
3175
|
|
|
3176
|
+
// src/help.es.ts
|
|
3177
|
+
var HELP_ES = {
|
|
3178
|
+
"Account": "Cuenta",
|
|
3179
|
+
"Sign in with your browser (or save an API key)": "Entra con tu navegador (o guarda una API key)",
|
|
3180
|
+
"Sign in with your browser (OAuth2 + PKCE), or save an API key instead": "Entra con tu navegador (OAuth2 + PKCE), o guarda una API key en su lugar",
|
|
3181
|
+
"Only print the sign-in URL (e.g. for an agent to relay it)": "S\xF3lo imprime la URL para entrar (p. ej. para que un agente te la pase)",
|
|
3182
|
+
"Read an API key from stdin instead of the browser (same as -)": "Lee una API key por stdin en vez del navegador (igual que -)",
|
|
3183
|
+
"opens the browser; waits for you": "abre el navegador y te espera",
|
|
3184
|
+
'agents: prints {"event":"login_url"} first': 'agentes: primero imprime {"event":"login_url"}',
|
|
3185
|
+
"API key from stdin (validated first)": "API key por stdin (se valida antes)",
|
|
3186
|
+
"CI/agents: the env var needs no login at all": "CI/agentes: con la variable no hace falta login",
|
|
3187
|
+
"Forget the saved session and API key": "Olvida la sesi\xF3n y la API key guardadas",
|
|
3188
|
+
"Remove the browser session and the API key from ~/.easybitsrc": "Quita la sesi\xF3n del navegador y la API key de ~/.easybitsrc",
|
|
3189
|
+
"Show which account and credential the CLI is using": "Muestra qu\xE9 cuenta y qu\xE9 credencial usa el CLI",
|
|
3190
|
+
"Show the account email and where the credential comes from": "Muestra el correo de la cuenta y de d\xF3nde sale la credencial",
|
|
3191
|
+
"Check the CLI setup: Node, version, credential, API; exits 1 on a problem": "Revisa el CLI: Node, versi\xF3n, credencial y API; sale con 1 si hay un problema",
|
|
3192
|
+
"Check Node, the CLI version, your credential and the API; exits 1 on a problem": "Revisa Node, la versi\xF3n del CLI, tu credencial y la API; sale con 1 si hay un problema",
|
|
3193
|
+
"one agent": "un agente",
|
|
3194
|
+
"Show your plan, storage and resource counts": "Muestra tu plan, almacenamiento y cu\xE1ntos recursos tienes",
|
|
3195
|
+
"Compute": "C\xF3mputo",
|
|
3196
|
+
"Firecracker microVMs (alias: sb)": "MicroVMs Firecracker (alias: sb)",
|
|
3197
|
+
"List your sandboxes": "Enlista tus sandboxes",
|
|
3198
|
+
"Create a sandbox and wait until it is running": "Crea un sandbox y espera a que est\xE9 corriendo",
|
|
3199
|
+
"Base template (default ubuntu): ubuntu, python, node, code-interpreter\u2026": "Template base (default ubuntu): ubuntu, python, node, code-interpreter\u2026",
|
|
3200
|
+
"Human label (lets you `ssh <name>.ghosty`)": "Nombre para personas (permite `ssh <nombre>.ghosty`)",
|
|
3201
|
+
"Time to live before auto-destroy (was --timeout)": "Tiempo de vida antes de autodestruirse (antes --timeout)",
|
|
3202
|
+
"VM size class (gated by plan)": "Tama\xF1o de la VM (seg\xFAn tu plan)",
|
|
3203
|
+
"Environment variable, non-secret (repeatable)": "Variable de entorno, no secreta (repetible)",
|
|
3204
|
+
"KEY=VALUE lines (dotenv); - reads stdin. Use it for secrets": "Renglones KEY=VALUE (dotenv); - lee stdin. \xDAsalo para secretos",
|
|
3205
|
+
"Return right away instead of waiting for running": "Regresa de inmediato en vez de esperar a que corra",
|
|
3206
|
+
"Show one sandbox": "Muestra un sandbox",
|
|
3207
|
+
"Run a shell command inside a sandbox": "Corre un comando de shell dentro de un sandbox",
|
|
3208
|
+
"Working directory": "Directorio de trabajo",
|
|
3209
|
+
"Kill the command after this long": "Mata el comando despu\xE9s de este tiempo",
|
|
3210
|
+
"Read the sandbox journal (optionally one systemd unit)": "Lee el journal del sandbox (opcionalmente de una unit de systemd)",
|
|
3211
|
+
"systemd unit to filter": "Unit de systemd a filtrar",
|
|
3212
|
+
"Last N lines (default 200)": "\xDAltimos N renglones (default 200)",
|
|
3213
|
+
'journalctl time spec, e.g. "10 min ago"': 'Tiempo en sintaxis de journalctl, p. ej. "10 min ago"',
|
|
3214
|
+
"Only lines matching": "S\xF3lo los renglones que coincidan",
|
|
3215
|
+
"List, read or write files inside a sandbox": "Enlista, lee o escribe archivos dentro de un sandbox",
|
|
3216
|
+
"write: inline content instead of a local file": "write: contenido en l\xEDnea en vez de un archivo local",
|
|
3217
|
+
"read: save to a local file (binary-safe)": "read: guarda en un archivo local (seguro con binarios)",
|
|
3218
|
+
"Suspend to disk (frees CPU; TTL paused)": "Lo suspende a disco (libera CPU; el TTL se pausa)",
|
|
3219
|
+
"Wake a suspended sandbox": "Despierta un sandbox suspendido",
|
|
3220
|
+
"Rewrite env on wake, non-secret (repeatable)": "Reescribe el env al despertar, no secreto (repetible)",
|
|
3221
|
+
"Destroy a sandbox (irreversible)": "Destruye un sandbox (irreversible)",
|
|
3222
|
+
"Skip the confirmation (required without a terminal or with --json)": "Salta la confirmaci\xF3n (obligatorio sin terminal o con --json)",
|
|
3223
|
+
"Capture a copy-on-write snapshot without stopping the box": "Toma un snapshot copy-on-write sin detener la caja",
|
|
3224
|
+
"Snapshot label": "Nombre del snapshot",
|
|
3225
|
+
"Agents running in their own sandbox": "Agentes que corren en su propio sandbox",
|
|
3226
|
+
"List your agents": "Enlista tus agentes",
|
|
3227
|
+
"Show one agent: record, prompt, MCP servers, skills and files": "Muestra un agente: registro, prompt, servidores MCP, skills y archivos",
|
|
3228
|
+
"Only these fields: agentId, name, template, status, lastError, sandboxId, agentUrl, embedToken, createdAt, expiresAt, systemPrompt, systemPromptMode, mcpServers, skills, files": "S\xF3lo estos campos: agentId, name, template, status, lastError, sandboxId, agentUrl, embedToken, createdAt, expiresAt, systemPrompt, systemPromptMode, mcpServers, skills, files",
|
|
3229
|
+
"Write the system prompt to a file instead of printing it": "Escribe el system prompt en un archivo en vez de imprimirlo",
|
|
3230
|
+
"edit, then: agents set helper --prompt-file PROMPT.md": "ed\xEDtalo y luego: agents set helper --prompt-file PROMPT.md",
|
|
3231
|
+
"Create an agent from a template, or clone one (--like / --from)": "Crea un agente desde un template, o clona uno (--like / --from)",
|
|
3232
|
+
"Agent template (see: easybits docs agents)": "Template del agente (ver: easybits docs agents)",
|
|
3233
|
+
"Clone another agent's setup: template, prompt, MCP, skills (never its env)": "Clona la configuraci\xF3n de otro agente: template, prompt, MCP, skills (nunca su env)",
|
|
3234
|
+
"Clone from `easybits agents export` output": "Clona desde lo que dio `easybits agents export`",
|
|
3235
|
+
"Label": "Nombre",
|
|
3236
|
+
"System prompt (ghosty-lite, goose)": "System prompt (ghosty-lite, goose)",
|
|
3237
|
+
"System prompt from a file": "System prompt desde un archivo",
|
|
3238
|
+
"append (default) or replace the engine's prompt": "append (default) o reemplaza el prompt del motor",
|
|
3239
|
+
"MCP servers: JSON array or { servers } (ACP templates)": "Servidores MCP: arreglo JSON o { servers } (templates ACP)",
|
|
3240
|
+
"--like/--from: also copy the knowledge files in /data/work": "--like/--from: copia tambi\xE9n los archivos de conocimiento de /data/work",
|
|
3241
|
+
"Env for the agent, non-secret (repeatable)": "Env del agente, no secreto (repetible)",
|
|
3242
|
+
"Lifetime before auto-destroy (was --timeout)": "Vida antes de autodestruirse (antes --timeout)",
|
|
3243
|
+
"Print the plan; create nothing": "Imprime el plan; no crea nada",
|
|
3244
|
+
"Send a message (agent by id or name); streams the reply": "Manda un mensaje (agente por id o nombre); la respuesta llega en streaming",
|
|
3245
|
+
"Continue a conversation": "Sigue una conversaci\xF3n",
|
|
3246
|
+
"Destroy an agent and its sandbox": "Destruye un agente y su sandbox",
|
|
3247
|
+
"asks you to type the id": "te pide teclear el id",
|
|
3248
|
+
"Change the system prompt (ghosty-lite, goose); takes effect without a reboot": "Cambia el system prompt (ghosty-lite, goose); entra sin reiniciar",
|
|
3249
|
+
"New system prompt": "System prompt nuevo",
|
|
3250
|
+
"Read the prompt from a file (- = stdin)": "Lee el prompt de un archivo (- = stdin)",
|
|
3251
|
+
"append (to the engine's own) or replace it": "append (al del motor) o lo reemplaza",
|
|
3252
|
+
"Show what would change; change nothing": "Ense\xF1a qu\xE9 cambiar\xEDa; no cambia nada",
|
|
3253
|
+
"Knowledge files in /data/work: list, read, upload (several at once) or delete": "Archivos de conocimiento en /data/work: enlista, lee, sube (varios a la vez) o borra",
|
|
3254
|
+
"get: save to a local file (binary-safe)": "get: guarda en un archivo local (seguro con binarios)",
|
|
3255
|
+
"put: folder inside /data/work (default: its root)": "put: carpeta dentro de /data/work (default: la ra\xEDz)",
|
|
3256
|
+
"Skills in /data/agent/skills: list, read, add from a file or folder, remove": "Skills en /data/agent/skills: enlista, lee, agrega desde un archivo o carpeta, quita",
|
|
3257
|
+
"add: a single SKILL.md": "add: un solo SKILL.md",
|
|
3258
|
+
"add: a folder with SKILL.md and its assets": "add: una carpeta con SKILL.md y sus archivos",
|
|
3259
|
+
"add: slug (default: the frontmatter name, else the folder)": "add: slug (default: el name del frontmatter, si no, la carpeta)",
|
|
3260
|
+
"get: download the whole skill into this folder": "get: baja la skill entera a esta carpeta",
|
|
3261
|
+
"add/rm: restart the agent so it sees the change (new session)": "add/rm: reinicia el agente para que vea el cambio (sesi\xF3n nueva)",
|
|
3262
|
+
"The agent's own MCP servers: show them, or replace the whole list (restarts it)": "Los servidores MCP propios del agente: verlos o reemplazar la lista entera (lo reinicia)",
|
|
3263
|
+
"set: JSON array (or { servers }); - = stdin. [] removes all": "set: arreglo JSON (o { servers }); - = stdin. [] quita todos",
|
|
3264
|
+
"get: print env/header values unmasked": "get: imprime los valores de env/headers sin tapar",
|
|
3265
|
+
"Restart the agent (picks up skills, MCP and prompt mode; starts a new session)": "Reinicia el agente (toma skills, MCP y modo del prompt; abre sesi\xF3n nueva)",
|
|
3266
|
+
"One full turn as text (no stream): verify what you just configured": "Un turno completo como texto (sin streaming): verifica lo que acabas de configurar",
|
|
3267
|
+
"Continue this session (printed after each turn)": "Sigue esta sesi\xF3n (se imprime despu\xE9s de cada turno)",
|
|
3268
|
+
"Start a fresh session": "Empieza una sesi\xF3n nueva",
|
|
3269
|
+
"Read the journal of the agent's sandbox": "Lee el journal del sandbox del agente",
|
|
3270
|
+
"Check an agent: status, sandbox, prompt, skills frontmatter, MCP; exits 1 on a problem": "Revisa un agente: estado, sandbox, prompt, frontmatter de skills, MCP; sale con 1 si hay un problema",
|
|
3271
|
+
"Export an agent as a file you can edit and apply (secrets as ${NAME}, never their value)": "Exporta un agente como archivo que puedes editar y aplicar (secretos como ${NOMBRE}, nunca su valor)",
|
|
3272
|
+
"A folder (agent.yaml + skills/ + files/) or a .yaml/.json file": "Una carpeta (agent.yaml + skills/ + files/) o un archivo .yaml/.json",
|
|
3273
|
+
"Legacy clone JSON (0.9) with MCP values unmasked": "JSON de clonado de 0.9 con los valores del MCP sin tapar",
|
|
3274
|
+
"Legacy clone JSON (0.9) with the file contents": "JSON de clonado de 0.9 con el contenido de los archivos",
|
|
3275
|
+
"agent.yaml + skills/ + files/, versionable in git": "agent.yaml + skills/ + files/, versionable en git",
|
|
3276
|
+
"after editing: see the plan": "despu\xE9s de editarlo: mira el plan",
|
|
3277
|
+
"Make an agent match a file from `agents export` (plan, then apply)": "Hace que un agente coincida con un archivo de `agents export` (plan y luego aplica)",
|
|
3278
|
+
"Apply an agent file or folder: shows the plan (+ add, ~ change, - remove, ! skipped), then applies it": "Aplica un archivo o carpeta de agente: ense\xF1a el plan (+ agrega, ~ cambia, - quita, ! se salta) y luego lo aplica",
|
|
3279
|
+
"Apply to this agent (default: the id in the file)": "Aplicarlo a este agente (default: el id del archivo)",
|
|
3280
|
+
"Make a new agent from the file instead": "Crea un agente nuevo desde el archivo",
|
|
3281
|
+
"--create: name for the new agent": "--create: nombre del agente nuevo",
|
|
3282
|
+
"Also remove what the file doesn't list (env, MCP, skills, files)": "Quita tambi\xE9n lo que el archivo no enlista (env, MCP, skills, archivos)",
|
|
3283
|
+
"Show the plan; change nothing": "Ense\xF1a el plan; no cambia nada",
|
|
3284
|
+
"${ANTHROPIC_API_KEY} in the file gets this value": "${ANTHROPIC_API_KEY} del archivo toma este valor",
|
|
3285
|
+
"a copy": "una copia",
|
|
3286
|
+
"what would be removed": "lo que se quitar\xEDa",
|
|
3287
|
+
"Hosting": "Hosting",
|
|
3288
|
+
"Permanent machines and their releases (easybits deploy <machine> publishes)": "M\xE1quinas permanentes y sus releases (easybits deploy <m\xE1quina> publica)",
|
|
3289
|
+
"List your permanent machines": "Enlista tus m\xE1quinas permanentes",
|
|
3290
|
+
"Publish the machine's current app code as a new release": "Publica el c\xF3digo actual de la app como un release nuevo",
|
|
3291
|
+
"Release note": "Nota del release",
|
|
3292
|
+
"same thing, shorter": "lo mismo, m\xE1s corto",
|
|
3293
|
+
"List releases, newest first": "Enlista los releases, el m\xE1s nuevo primero",
|
|
3294
|
+
"How many": "Cu\xE1ntos",
|
|
3295
|
+
"Tail the app's own log": "Los \xFAltimos renglones del log de la app",
|
|
3296
|
+
"Last N lines": "\xDAltimos N renglones",
|
|
3297
|
+
"Put a previous release back on the same machine (data untouched)": "Vuelve a poner un release anterior en la misma m\xE1quina (datos intactos)",
|
|
3298
|
+
"App env vars stored encrypted in your vault": "Variables de entorno de la app, cifradas en tu vault",
|
|
3299
|
+
"Custom domains with auto TLS on a sandbox or machine": "Dominios propios con TLS autom\xE1tico en un sandbox o m\xE1quina",
|
|
3300
|
+
"List domains attached to a sandbox": "Enlista los dominios de un sandbox",
|
|
3301
|
+
"Attach a domain to a port; prints the DNS record to create": "Liga un dominio a un puerto; imprime el registro DNS que hay que crear",
|
|
3302
|
+
"Port inside the sandbox (required)": "Puerto dentro del sandbox (obligatorio)",
|
|
3303
|
+
"Check DNS + HTTPS for a domain": "Revisa DNS + HTTPS de un dominio",
|
|
3304
|
+
"Detach a domain": "Desliga un dominio",
|
|
3305
|
+
"Write the GitHub Actions deploy workflow for this repo": "Escribe el workflow de GitHub Actions que despliega este repo",
|
|
3306
|
+
"Write the GitHub Actions workflow that deploys this repo on every push to main": "Escribe el workflow de GitHub Actions que despliega este repo en cada push a main",
|
|
3307
|
+
"Where the app lives in the machine (default /srv/app)": "D\xF3nde vive la app en la m\xE1quina (default /srv/app)",
|
|
3308
|
+
"Port your app listens on (default 3000)": "Puerto en el que escucha tu app (default 3000)",
|
|
3309
|
+
"Data": "Datos",
|
|
3310
|
+
"SQL databases (libSQL)": "Bases de datos SQL (libSQL)",
|
|
3311
|
+
"List your databases": "Enlista tus bases de datos",
|
|
3312
|
+
"Create a database": "Crea una base de datos",
|
|
3313
|
+
"What it is for": "Para qu\xE9 es",
|
|
3314
|
+
"Delete a database (irreversible)": "Borra una base de datos (irreversible)",
|
|
3315
|
+
"asks you to type the name": "te pide teclear el nombre",
|
|
3316
|
+
"scripts and agents": "scripts y agentes",
|
|
3317
|
+
"List tables with row counts and columns": "Enlista las tablas con su n\xFAmero de filas y columnas",
|
|
3318
|
+
"Run one SQL statement (by database id or name)": "Corre una sentencia SQL (por id o nombre de la base)",
|
|
3319
|
+
"Positional ? parameter (repeatable)": "Par\xE1metro ? posicional (repetible)",
|
|
3320
|
+
"Storage": "Almacenamiento",
|
|
3321
|
+
"Your files in EasyBits storage": "Tus archivos en el almacenamiento de EasyBits",
|
|
3322
|
+
"List your files": "Enlista tus archivos",
|
|
3323
|
+
"Upload a local file": "Sube un archivo local",
|
|
3324
|
+
"Delete a file (soft delete, 7-day trash)": "Borra un archivo (a la papelera 7 d\xEDas)",
|
|
3325
|
+
"List your static websites": "Enlista tus sitios est\xE1ticos",
|
|
3326
|
+
"Show storage providers": "Muestra los proveedores de almacenamiento",
|
|
3327
|
+
"MCP": "MCP",
|
|
3328
|
+
"Print the MCP config JSON for your agent or editor": "Imprime el JSON de configuraci\xF3n MCP para tu agente o editor",
|
|
3329
|
+
"Print MCP config JSON: streamable HTTP with your key, or stdio (npx @easybits.cloud/mcp)": "Imprime el JSON de configuraci\xF3n MCP: streamable HTTP con tu key, o stdio (npx @easybits.cloud/mcp)",
|
|
3330
|
+
"stdio config (npx @easybits.cloud/mcp) instead of streamable HTTP": "Configuraci\xF3n stdio (npx @easybits.cloud/mcp) en vez de streamable HTTP",
|
|
3331
|
+
"SSH": "SSH",
|
|
3332
|
+
"Print your public key for sandbox SSH (creates it once)": "Imprime tu llave p\xFAblica para SSH a un sandbox (la crea una vez)",
|
|
3333
|
+
"Print your public key for sandbox_ssh_enable (creates ~/.ssh/easybits_ed25519 once)": "Imprime tu llave p\xFAblica para sandbox_ssh_enable (crea ~/.ssh/easybits_ed25519 una vez)",
|
|
3334
|
+
"SSH tunnel over 443 (use as ssh ProxyCommand)": "T\xFAnel SSH por el 443 (\xFAsalo como ProxyCommand de ssh)",
|
|
3335
|
+
"SSH tunnel over 443; meant to be the ProxyCommand in ~/.ssh/config": "T\xFAnel SSH por el 443; pensado como el ProxyCommand de ~/.ssh/config",
|
|
3336
|
+
"with the ~/.ssh/config snippet from `easybits --help`": "con el bloque de ~/.ssh/config de `easybits --help`",
|
|
3337
|
+
"Help": "Ayuda",
|
|
3338
|
+
"Print the docs URL, or a docs section as markdown": "Imprime la URL de los docs, o una secci\xF3n de los docs en markdown",
|
|
3339
|
+
"English docs": "Docs en ingl\xE9s",
|
|
3340
|
+
"Open the docs in your browser": "Abre los docs en tu navegador"
|
|
3341
|
+
};
|
|
3342
|
+
|
|
3039
3343
|
// src/help.ts
|
|
3040
|
-
var
|
|
3344
|
+
var h = (en) => en && lang() === "es" ? HELP_ES[en] ?? en : en ?? "";
|
|
3345
|
+
function example(line2) {
|
|
3346
|
+
const i = line2.indexOf("# ");
|
|
3347
|
+
if (i === -1 || lang() !== "es") return line2;
|
|
3348
|
+
return line2.slice(0, i + 2) + h(line2.slice(i + 2).trim());
|
|
3349
|
+
}
|
|
3350
|
+
var GLOBAL_FLAGS_EN = `Global flags:
|
|
3041
3351
|
--json Machine output: JSON on stdout, errors included
|
|
3042
3352
|
--token <key> API key for this call (prefer EASYBITS_API_KEY: argv leaks to ps)
|
|
3353
|
+
--lang <es|en> Language (default: from LANG; also EASYBITS_LANG)
|
|
3043
3354
|
-h, --help Help (also: easybits <command> --help)
|
|
3044
3355
|
-v, --version Print the CLI version
|
|
3045
3356
|
|
|
@@ -3057,15 +3368,43 @@ Env: EASYBITS_API_KEY (API key), EASYBITS_URL (API server, default https://www.e
|
|
|
3057
3368
|
EASYBITS_NO_UPDATE_CHECK=1 (no daily "update available" notice on stderr)
|
|
3058
3369
|
|
|
3059
3370
|
Exit codes: 0 ok, 1 API error, 2 usage error, 3 not logged in or bad key`;
|
|
3371
|
+
var GLOBAL_FLAGS_ES = `Flags globales:
|
|
3372
|
+
--json Salida para m\xE1quinas: JSON en stdout, errores incluidos
|
|
3373
|
+
--token <key> API key para esta llamada (mejor EASYBITS_API_KEY: argv se ve en ps)
|
|
3374
|
+
--lang <es|en> Idioma (default: el de LANG; tambi\xE9n EASYBITS_LANG)
|
|
3375
|
+
-h, --help Ayuda (tambi\xE9n: easybits <comando> --help)
|
|
3376
|
+
-v, --version Imprime la versi\xF3n del CLI
|
|
3377
|
+
|
|
3378
|
+
Nombres (como gh): sustantivo y luego verbo \u2014 easybits <sustantivo> <verbo>. Las formas
|
|
3379
|
+
viejas siguen funcionando y avisan la nueva en stderr (nunca con --json).
|
|
3380
|
+
|
|
3381
|
+
Nombres o ids: agentes, sandboxes/m\xE1quinas y bases aceptan cualquiera de los dos
|
|
3382
|
+
(exacto, sin distinguir may\xFAsculas; dos con el mismo nombre \u2192 sale con 2 y los ids).
|
|
3383
|
+
|
|
3384
|
+
Los borrados (sandboxes destroy, agents destroy, db rm, domains rm, files rm, agents files|skills rm)
|
|
3385
|
+
preguntan antes; sin terminal o con --json pasa -y/--yes, o salen con 2.
|
|
3386
|
+
Lo que escribe en un agente (agents set|create|files|skills|mcp) acepta --dry-run: ense\xF1a el
|
|
3387
|
+
cambio y no toca nada.
|
|
3388
|
+
|
|
3389
|
+
Env: EASYBITS_API_KEY (API key), EASYBITS_URL (servidor de la API, default https://www.easybits.cloud),
|
|
3390
|
+
EASYBITS_NO_UPDATE_CHECK=1 (sin el aviso diario de versi\xF3n nueva en stderr)
|
|
3391
|
+
|
|
3392
|
+
C\xF3digos de salida: 0 ok, 1 error de la API, 2 error de uso, 3 sin sesi\xF3n o key inv\xE1lida`;
|
|
3393
|
+
var globalFlags = () => lang() === "es" ? GLOBAL_FLAGS_ES : GLOBAL_FLAGS_EN;
|
|
3060
3394
|
var COL = 17;
|
|
3061
3395
|
function globalHelp(version) {
|
|
3062
3396
|
const groups = /* @__PURE__ */ new Map();
|
|
3063
3397
|
for (const c of COMMANDS) groups.set(c.group, [...groups.get(c.group) ?? [], c]);
|
|
3064
|
-
const lines = [
|
|
3398
|
+
const lines = [
|
|
3399
|
+
`easybits ${version} \u2014 ${t("the cloud for AI agents, from your terminal", "la nube para agentes de IA, desde tu terminal")}`,
|
|
3400
|
+
"",
|
|
3401
|
+
t("Usage: easybits <command> [subcommand] [args] [flags]", "Uso: easybits <comando> [subcomando] [args] [flags]"),
|
|
3402
|
+
""
|
|
3403
|
+
];
|
|
3065
3404
|
for (const [group, cmds] of groups) {
|
|
3066
|
-
lines.push(`${group}:`);
|
|
3405
|
+
lines.push(`${h(group)}:`);
|
|
3067
3406
|
for (const c of cmds) {
|
|
3068
|
-
lines.push(` ${c.synopsis.padEnd(COL)}${c.summary}`);
|
|
3407
|
+
lines.push(` ${c.synopsis.padEnd(COL)}${h(c.summary)}`);
|
|
3069
3408
|
if (c.subs) {
|
|
3070
3409
|
let row = "";
|
|
3071
3410
|
for (const n of Object.keys(c.subs)) {
|
|
@@ -3079,36 +3418,36 @@ function globalHelp(version) {
|
|
|
3079
3418
|
}
|
|
3080
3419
|
lines.push("");
|
|
3081
3420
|
}
|
|
3082
|
-
lines.push(
|
|
3083
|
-
lines.push("Docs: https://www.easybits.cloud/docs/cli.md (or: easybits docs cli)");
|
|
3421
|
+
lines.push(globalFlags(), "", t("SSH to a sandbox \u2014 add to ~/.ssh/config:", "SSH a un sandbox \u2014 agrega a ~/.ssh/config:"), indent(SSH_CONFIG_SNIPPET, 2), "");
|
|
3422
|
+
lines.push(t("Docs: https://www.easybits.cloud/en/docs/cli.md (or: easybits docs cli --en)", "Docs: https://www.easybits.cloud/docs/cli.md (o: easybits docs cli)"));
|
|
3084
3423
|
return lines.join("\n");
|
|
3085
3424
|
}
|
|
3086
3425
|
function commandHelp(c) {
|
|
3087
3426
|
if (c.leaf) return leafHelp(c.leaf);
|
|
3088
3427
|
const subs = Object.entries(c.subs ?? {});
|
|
3089
3428
|
const width = Math.max(...subs.map(([n]) => n.length)) + 2;
|
|
3090
|
-
const lines = [`easybits ${c.name} \u2014 ${c.summary}`, "", `Usage: easybits ${c.name} <subcommand> [args] [flags]`, ""];
|
|
3091
|
-
if (c.aliases?.length) lines.push(
|
|
3092
|
-
lines.push("Subcommands:");
|
|
3093
|
-
for (const [n, l] of subs) lines.push(` ${n.padEnd(width)}${l.summary}`);
|
|
3094
|
-
lines.push("", `Run: easybits ${c.name} <subcommand> --help`);
|
|
3429
|
+
const lines = [`easybits ${c.name} \u2014 ${h(c.summary)}`, "", t(`Usage: easybits ${c.name} <subcommand> [args] [flags]`, `Uso: easybits ${c.name} <subcomando> [args] [flags]`), ""];
|
|
3430
|
+
if (c.aliases?.length) lines.push(`${t("Aliases", "Alias")}: ${c.aliases.join(", ")}`, "");
|
|
3431
|
+
lines.push(t("Subcommands:", "Subcomandos:"));
|
|
3432
|
+
for (const [n, l] of subs) lines.push(` ${n.padEnd(width)}${h(l.summary)}`);
|
|
3433
|
+
lines.push("", t(`Run: easybits ${c.name} <subcommand> --help`, `Corre: easybits ${c.name} <subcomando> --help`));
|
|
3095
3434
|
return lines.join("\n");
|
|
3096
3435
|
}
|
|
3097
3436
|
function leafHelp(l) {
|
|
3098
|
-
const lines = [l.summary, "",
|
|
3099
|
-
if (l.aliases?.length) lines.push(
|
|
3437
|
+
const lines = [h(l.summary), "", `${t("Usage", "Uso")}: ${l.usage}`];
|
|
3438
|
+
if (l.aliases?.length) lines.push(`${t("Aliases", "Alias")}: ${l.aliases.join(", ")}`);
|
|
3100
3439
|
const opts = Object.entries(l.options ?? {});
|
|
3101
3440
|
if (opts.length) {
|
|
3102
3441
|
const flag = ([n, o]) => `${o.short ? `-${o.short}, ` : ""}--${n}${o.type === "string" ? ` <${o.value ?? "value"}>` : ""}`;
|
|
3103
3442
|
const width = Math.max(...opts.map((o) => flag(o).length)) + 2;
|
|
3104
3443
|
lines.push("", "Flags:");
|
|
3105
|
-
for (const o of opts) lines.push(` ${flag(o).padEnd(width)}${o[1].description
|
|
3444
|
+
for (const o of opts) lines.push(` ${flag(o).padEnd(width)}${h(o[1].description)}`);
|
|
3106
3445
|
}
|
|
3107
3446
|
if (l.examples?.length) {
|
|
3108
|
-
lines.push("", "Examples:");
|
|
3109
|
-
for (const e of l.examples) lines.push(` ${e}`);
|
|
3447
|
+
lines.push("", t("Examples:", "Ejemplos:"));
|
|
3448
|
+
for (const e of l.examples) lines.push(` ${example(e)}`);
|
|
3110
3449
|
}
|
|
3111
|
-
lines.push("", "Add --json for machine output.");
|
|
3450
|
+
lines.push("", t("Add --json for machine output.", "Agrega --json para salida de m\xE1quina."));
|
|
3112
3451
|
return lines.join("\n");
|
|
3113
3452
|
}
|
|
3114
3453
|
function indent(s, n) {
|
|
@@ -3127,7 +3466,7 @@ function positionals(argv2) {
|
|
|
3127
3466
|
for (let i = 0; i < argv2.length; i++) {
|
|
3128
3467
|
const a = argv2[i];
|
|
3129
3468
|
if (a === "--") break;
|
|
3130
|
-
if (a === "--token") {
|
|
3469
|
+
if (a === "--token" || a === "--lang") {
|
|
3131
3470
|
i++;
|
|
3132
3471
|
continue;
|
|
3133
3472
|
}
|
|
@@ -3149,20 +3488,20 @@ function normalizeArgs(input) {
|
|
|
3149
3488
|
const at = (n) => pos[n] != null ? argv2[pos[n]] : void 0;
|
|
3150
3489
|
if (at(0) === "config") {
|
|
3151
3490
|
argv2.splice(pos[0], 1, "mcp", "config");
|
|
3152
|
-
notes.push("`easybits config` is now `easybits mcp config`.");
|
|
3491
|
+
notes.push(["`easybits config` is now `easybits mcp config`.", "`easybits config` ahora es `easybits mcp config`."]);
|
|
3153
3492
|
} else if (at(0) === "mcp" && at(1) == null && !help) {
|
|
3154
3493
|
argv2.splice(pos[0] + 1, 0, "config");
|
|
3155
3494
|
argv2.push("--stdio");
|
|
3156
|
-
notes.push("`easybits mcp` is now `easybits mcp config --stdio`.");
|
|
3495
|
+
notes.push(["`easybits mcp` is now `easybits mcp config --stdio`.", "`easybits mcp` ahora es `easybits mcp config --stdio`."]);
|
|
3157
3496
|
}
|
|
3158
3497
|
if (at(0) === "deploy") {
|
|
3159
3498
|
const sub = at(1);
|
|
3160
3499
|
if (sub && MACHINE_SUBS.includes(sub) && sub !== "deploy") {
|
|
3161
3500
|
argv2[pos[0]] = "machines";
|
|
3162
|
-
notes.push(`\`easybits deploy ${sub}\` is now \`easybits machines ${sub}\`.`);
|
|
3501
|
+
notes.push([`\`easybits deploy ${sub}\` is now \`easybits machines ${sub}\`.`, `\`easybits deploy ${sub}\` ahora es \`easybits machines ${sub}\`.`]);
|
|
3163
3502
|
} else if (sub === "deploy") {
|
|
3164
3503
|
argv2[pos[0]] = "machines";
|
|
3165
|
-
notes.push("`easybits deploy deploy` is now `easybits deploy <machine>`.");
|
|
3504
|
+
notes.push(["`easybits deploy deploy` is now `easybits deploy <machine>`.", "`easybits deploy deploy` ahora es `easybits deploy <m\xE1quina>`."]);
|
|
3166
3505
|
} else {
|
|
3167
3506
|
argv2.splice(pos[0], 1, "machines", "deploy");
|
|
3168
3507
|
}
|
|
@@ -3170,7 +3509,7 @@ function normalizeArgs(input) {
|
|
|
3170
3509
|
}
|
|
3171
3510
|
if ((at(0) === "machines" || at(0) === "machine") && at(1) === "release") {
|
|
3172
3511
|
argv2[pos[1]] = "deploy";
|
|
3173
|
-
notes.push("`machines release` is now `machines deploy`.");
|
|
3512
|
+
notes.push(["`machines release` is now `machines deploy`.", "`machines release` ahora es `machines deploy`."]);
|
|
3174
3513
|
}
|
|
3175
3514
|
const noun = SANDBOX_NAMES.includes(at(0) ?? "") ? "sandboxes" : AGENT_NAMES.includes(at(0) ?? "") ? "agents" : null;
|
|
3176
3515
|
if (noun && (at(1) === "create" || at(1) === "new")) {
|
|
@@ -3178,7 +3517,10 @@ function normalizeArgs(input) {
|
|
|
3178
3517
|
for (let i = 0; i < (stop === -1 ? argv2.length : stop); i++) {
|
|
3179
3518
|
if (argv2[i] === "--timeout" || argv2[i].startsWith("--timeout=")) {
|
|
3180
3519
|
argv2[i] = argv2[i].replace("--timeout", "--ttl");
|
|
3181
|
-
notes.push(
|
|
3520
|
+
notes.push([
|
|
3521
|
+
`\`${noun} create --timeout\` is now \`--ttl\`${noun === "sandboxes" ? " (`--timeout` stays for `exec`)" : ""}.`,
|
|
3522
|
+
`\`${noun} create --timeout\` ahora es \`--ttl\`${noun === "sandboxes" ? " (`--timeout` sigue en `exec`)" : ""}.`
|
|
3523
|
+
]);
|
|
3182
3524
|
}
|
|
3183
3525
|
}
|
|
3184
3526
|
}
|
|
@@ -3186,10 +3528,11 @@ function normalizeArgs(input) {
|
|
|
3186
3528
|
}
|
|
3187
3529
|
|
|
3188
3530
|
// src/index.ts
|
|
3189
|
-
var VERSION2 = true ? "0.
|
|
3531
|
+
var VERSION2 = true ? "0.11.0" : "dev";
|
|
3190
3532
|
var GLOBAL_OPTIONS = {
|
|
3191
3533
|
json: { type: "boolean" },
|
|
3192
3534
|
token: { type: "string" },
|
|
3535
|
+
lang: { type: "string" },
|
|
3193
3536
|
help: { type: "boolean", short: "h" },
|
|
3194
3537
|
version: { type: "boolean", short: "v" }
|
|
3195
3538
|
};
|
|
@@ -3206,7 +3549,7 @@ function positionalIndexes(argv2) {
|
|
|
3206
3549
|
for (let i = 0; i < argv2.length; i++) {
|
|
3207
3550
|
const a = argv2[i];
|
|
3208
3551
|
if (a === "--") break;
|
|
3209
|
-
if (a === "--token") {
|
|
3552
|
+
if (a === "--token" || a === "--lang") {
|
|
3210
3553
|
i++;
|
|
3211
3554
|
continue;
|
|
3212
3555
|
}
|
|
@@ -3233,27 +3576,29 @@ function findSub(cmd, name) {
|
|
|
3233
3576
|
return Object.entries(cmd.subs ?? {}).find(([n, l]) => n === name || l.aliases?.includes(name));
|
|
3234
3577
|
}
|
|
3235
3578
|
function unknownCommand(name) {
|
|
3236
|
-
const e = usageError(`Unknown command "${name}".`);
|
|
3579
|
+
const e = usageError(t(`Unknown command "${name}".`, `Comando desconocido "${name}".`));
|
|
3237
3580
|
const names = COMMANDS.flatMap((c) => [c.name, ...c.aliases ?? []]);
|
|
3238
3581
|
const hint = didYouMean(name, names, "easybits ", (n) => findCommand(n)?.name ?? n);
|
|
3239
|
-
if (hint) e.hint = `${hint}
|
|
3240
|
-
Run: easybits --help`;
|
|
3582
|
+
if (hint) e.hint = `${localHint(hint)}
|
|
3583
|
+
${t("Run", "Corre")}: easybits --help`;
|
|
3241
3584
|
return e;
|
|
3242
3585
|
}
|
|
3243
3586
|
function unknownSub(cmd, name) {
|
|
3244
|
-
const e = usageError(`Unknown subcommand "${cmd.name} ${name}".`, `easybits ${cmd.name} <${Object.keys(cmd.subs ?? {}).join("|")}>`);
|
|
3587
|
+
const e = usageError(t(`Unknown subcommand "${cmd.name} ${name}".`, `Subcomando desconocido "${cmd.name} ${name}".`), `easybits ${cmd.name} <${Object.keys(cmd.subs ?? {}).join("|")}>`);
|
|
3245
3588
|
const names = Object.entries(cmd.subs ?? {}).flatMap(([n, l]) => [n, ...l.aliases ?? []]);
|
|
3246
3589
|
const hint = didYouMean(name, names, `easybits ${cmd.name} `, (n) => findSub(cmd, n)?.[0] ?? n);
|
|
3247
|
-
if (hint) e.hint = `${hint}
|
|
3590
|
+
if (hint) e.hint = `${localHint(hint)}
|
|
3248
3591
|
${e.hint}`;
|
|
3249
3592
|
return e;
|
|
3250
3593
|
}
|
|
3251
|
-
function aliasNote(
|
|
3594
|
+
function aliasNote([en, es]) {
|
|
3595
|
+
const note = t(en, es);
|
|
3252
3596
|
const dim = process.stderr.isTTY && !process.env.NO_COLOR;
|
|
3253
3597
|
process.stderr.write(dim ? `\x1B[2m${note}\x1B[0m
|
|
3254
3598
|
` : `${note}
|
|
3255
3599
|
`);
|
|
3256
3600
|
}
|
|
3601
|
+
var localHint = (hint) => hint.replace("Did you mean this?", t("Did you mean this?", "\xBFQuisiste decir esto?"));
|
|
3257
3602
|
async function main(input) {
|
|
3258
3603
|
const { argv: argv2, notes } = normalizeArgs(input);
|
|
3259
3604
|
if (!wantsJson(argv2)) notes.forEach(aliasNote);
|
|
@@ -3301,7 +3646,7 @@ async function main(input) {
|
|
|
3301
3646
|
leaf = cmd.subs[cmd.defaultSub];
|
|
3302
3647
|
subKey = cmd.defaultSub;
|
|
3303
3648
|
} else if (!argv2.includes("--help") && !argv2.includes("-h")) {
|
|
3304
|
-
throw usageError(`Missing subcommand for "${cmd.name}".`, `easybits ${cmd.name} <${Object.keys(cmd.subs).join("|")}>`);
|
|
3649
|
+
throw usageError(t(`Missing subcommand for "${cmd.name}".`, `Falta el subcomando de "${cmd.name}".`), `easybits ${cmd.name} <${Object.keys(cmd.subs).join("|")}>`);
|
|
3305
3650
|
}
|
|
3306
3651
|
}
|
|
3307
3652
|
const { values, positionals: positionals2 } = parse(argv2, skip, leaf);
|
|
@@ -3336,11 +3681,12 @@ async function main(input) {
|
|
|
3336
3681
|
}
|
|
3337
3682
|
}
|
|
3338
3683
|
var argv = process.argv.slice(2);
|
|
3684
|
+
setLang(detectLang(langFlag(argv)));
|
|
3339
3685
|
var notice = process.stderr.isTTY && !wantsJson(argv) && !argv.includes("doctor") ? updateNotice(VERSION2) : null;
|
|
3340
3686
|
main(argv).catch((err) => {
|
|
3341
3687
|
const e = toCliError(err);
|
|
3342
3688
|
const latest = e.code === "usage" ? cachedNewer(VERSION2) : null;
|
|
3343
|
-
if (latest) e.hint = `${e.hint ? e.hint + "\n" : ""}You have ${VERSION2}; ${latest} is out and may have it. Run: ${UPGRADE}`;
|
|
3689
|
+
if (latest) e.hint = `${e.hint ? e.hint + "\n" : ""}${t(`You have ${VERSION2}; ${latest} is out and may have it. Run: ${UPGRADE}`, `Tienes ${VERSION2}; ya sali\xF3 ${latest} y quiz\xE1 lo trae. Corre: ${UPGRADE}`)}`;
|
|
3344
3690
|
if (wantsJson(argv)) {
|
|
3345
3691
|
process.stdout.write(JSON.stringify({ error: e.message, code: e.exitCode, ...e.hint ? { hint: e.hint } : {} }) + "\n");
|
|
3346
3692
|
} else {
|