@easybits.cloud/cli 0.17.0 → 0.18.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.
Files changed (3) hide show
  1. package/README.md +23 -0
  2. package/dist/index.js +301 -3
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -123,6 +123,24 @@ easybits secrets set --dotenv .env.production # KEY=VALUE lines; - reads stdin
123
123
  printf '%s' "$TOKEN" | easybits secrets set GITHUB_TOKEN # one value from stdin
124
124
  easybits secrets rm GITHUB_TOKEN --yes
125
125
 
126
+ # Your crons: call a public https URL or message one of your agents (at most every 5 min)
127
+ easybits crons create "Study reminder" --cron "30 8 * * *" --url https://www.ghosty.studio/api/v2/me/push \
128
+ --method POST --body '{"title":"Study time"}' --secret GHOSTY_PUSH_TOKEN
129
+ easybits crons create "Weekly report" --cron "0 9 * * 1" --agent <agentId> --prompt "Send me the sales summary"
130
+ easybits crons ls
131
+ easybits crons update "Study reminder" --cron "0 7 * * 1-5"
132
+ easybits crons run "Study reminder" # id or exact name
133
+ easybits crons runs "Study reminder"
134
+ easybits crons pause "Study reminder"
135
+ easybits crons rm "Study reminder" --yes
136
+
137
+ # Platform crons (platform admins only; key with the ADMIN scope)
138
+ easybits crons ls --platform
139
+ easybits crons runs backup-machines
140
+ easybits crons run purge-notifications
141
+ easybits crons pause check-hosting
142
+ easybits crons resume check-hosting
143
+
126
144
  # Databases
127
145
  easybits db ls
128
146
  easybits db create leads
@@ -275,6 +293,11 @@ Host *.ghosty
275
293
 
276
294
  Full reference: https://www.easybits.cloud/docs#cli · https://www.easybits.cloud/en/docs/cli.md
277
295
 
296
+ ## Changes in 0.17.1
297
+
298
+ - `easybits crons ls|runs|run|pause|resume`: EasyBits' scheduled jobs, the same as the
299
+ /dash/admin/crons panel (platform admins only; key with the ADMIN scope).
300
+
278
301
  ## Changes in 0.17.0
279
302
 
280
303
  - `sites create --prompt … [--kind webapp|static] [--stack …] [--template …]` and
package/dist/index.js CHANGED
@@ -305,7 +305,15 @@ var HEADERS_ES = {
305
305
  SCENES: "ESCENAS",
306
306
  DURATION: "DURACI\xD3N",
307
307
  NARRATION: "NARRACI\xD3N",
308
- PROMPT: "PROMPT"
308
+ PROMPT: "PROMPT",
309
+ JOB: "TAREA",
310
+ SCHEDULE: "HORARIO",
311
+ "LAST RUN": "\xDALTIMA",
312
+ "NEXT RUN": "PR\xD3XIMA",
313
+ RESULT: "RESULTADO",
314
+ TOOK: "TARD\xD3",
315
+ TRIGGER: "ORIGEN",
316
+ SUMMARY: "RESUMEN"
309
317
  };
310
318
  function table(rows, cols, empty = t("Nothing here yet.", "Todav\xEDa no hay nada.")) {
311
319
  if (lang() === "es") cols = cols.map(([k, l]) => [k, HEADERS_ES[l] ?? l]);
@@ -4910,7 +4918,7 @@ async function updateNotice(current2) {
4910
4918
  }
4911
4919
 
4912
4920
  // src/commands/doctor.ts
4913
- var VERSION = true ? "0.17.0" : "dev";
4921
+ var VERSION = true ? "0.18.0" : "dev";
4914
4922
  var doctor2 = {
4915
4923
  name: "doctor",
4916
4924
  group: "Account",
@@ -5416,6 +5424,272 @@ var secrets = {
5416
5424
  defaultSub: "ls"
5417
5425
  };
5418
5426
 
5427
+ // src/commands/crons.ts
5428
+ var HEX24 = /^[a-f0-9]{24}$/;
5429
+ var isUserCronId = (ref) => HEX24.test(ref);
5430
+ function describeCronAction(a) {
5431
+ if (a.type === "http") return `${a.method} ${a.url}${a.secretName ? ` (+${a.secretName})` : ""}`;
5432
+ return `agent ${a.fleetAgentId}: ${a.prompt.length > 40 ? a.prompt.slice(0, 39) + "\u2026" : a.prompt}`;
5433
+ }
5434
+ async function resolveUserCron(ctx, ref) {
5435
+ const { items } = await api(ctx, "GET", "/crons");
5436
+ const byId = items.find((c) => c.id === ref);
5437
+ if (byId) return byId;
5438
+ const byName = items.filter((c) => c.name.toLowerCase() === ref.trim().toLowerCase());
5439
+ if (byName.length > 1) {
5440
+ throw new CliError(t(`"${ref}" matches ${byName.length} crons. Use the id.`, `"${ref}" coincide con ${byName.length} crons. Usa el id.`), EXIT.USAGE);
5441
+ }
5442
+ return byName[0] ?? null;
5443
+ }
5444
+ async function userCronOrNull(ctx, ref) {
5445
+ if (isUserCronId(ref)) return await resolveUserCron(ctx, ref) ?? { id: ref };
5446
+ return resolveUserCron(ctx, ref);
5447
+ }
5448
+ var ACTION_OPTIONS = {
5449
+ url: { type: "string", value: "https-url", description: "Call this public https URL on each run" },
5450
+ method: { type: "string", value: "GET|POST|PUT|PATCH|DELETE", description: "HTTP method (default GET)" },
5451
+ body: { type: "string", value: "json|text", description: "Request body (not with GET)" },
5452
+ secret: { type: "string", value: "NAME", description: "Vault secret sent as a header (default Authorization: Bearer)" },
5453
+ "secret-header": { type: "string", value: "header", description: "Header for --secret" },
5454
+ agent: { type: "string", value: "id", description: "Send a turn to this fleet agent instead of calling a URL" },
5455
+ prompt: { type: "string", value: "text", description: "What the agent is told on each run (with --agent)" }
5456
+ };
5457
+ function actionFromFlags(f) {
5458
+ if (f.url && f.agent) return { error: "--url and --agent are exclusive" };
5459
+ if (f.url) {
5460
+ return {
5461
+ action: {
5462
+ type: "http",
5463
+ url: f.url,
5464
+ method: (f.method ?? "GET").toUpperCase(),
5465
+ ...f.body ? { body: f.body } : {},
5466
+ ...f.secret ? { secretName: f.secret, ...f.secretHeader ? { secretHeader: f.secretHeader } : {} } : {}
5467
+ }
5468
+ };
5469
+ }
5470
+ if (f.agent) {
5471
+ if (!f.prompt) return { error: "--agent needs --prompt" };
5472
+ return { action: { type: "agent_turn", fleetAgentId: f.agent, prompt: f.prompt } };
5473
+ }
5474
+ return {};
5475
+ }
5476
+ var flagsOf = (ctx) => ({
5477
+ url: str(ctx, "url"),
5478
+ method: str(ctx, "method"),
5479
+ body: str(ctx, "body"),
5480
+ secret: str(ctx, "secret"),
5481
+ secretHeader: str(ctx, "secret-header"),
5482
+ agent: str(ctx, "agent"),
5483
+ prompt: str(ctx, "prompt")
5484
+ });
5485
+ var STATUS = {
5486
+ "al-dia": ["up to date", "al d\xEDa"],
5487
+ tarde: ["late", "tarde"],
5488
+ fallo: ["failed", "fall\xF3"],
5489
+ pausado: ["paused", "pausado"],
5490
+ nuevo: ["no runs yet", "sin corridas"]
5491
+ };
5492
+ function cronStatus(c) {
5493
+ if (c.running) return t("running", "corriendo");
5494
+ const s = STATUS[c.status];
5495
+ return s ? t(s[0], s[1]) : c.status;
5496
+ }
5497
+ function duration(ms) {
5498
+ if (ms == null) return "\u2014";
5499
+ return ms < 1e3 ? `${ms} ms` : `${(ms / 1e3).toFixed(1)} s`;
5500
+ }
5501
+ var crons = {
5502
+ name: "crons",
5503
+ aliases: ["cron"],
5504
+ group: "Account",
5505
+ summary: "Your scheduled jobs (call a URL or message an agent) and, for admins, the platform's",
5506
+ synopsis: "crons <ls|create|update|rm|run|runs|pause|resume>",
5507
+ subs: {
5508
+ ls: {
5509
+ aliases: ["list"],
5510
+ summary: "List your crons (or, with --platform, the platform's scheduled jobs)",
5511
+ usage: "easybits crons ls [--platform]",
5512
+ options: { platform: { type: "boolean", description: "Platform jobs (backups, purges\u2026; platform admins only)" } },
5513
+ examples: ["easybits crons ls", "easybits crons ls --json", "easybits crons ls --platform"],
5514
+ async run(ctx) {
5515
+ if (!bool(ctx, "platform")) {
5516
+ const { items: items2 } = await api(ctx, "GET", "/crons");
5517
+ emit(
5518
+ ctx,
5519
+ { items: items2 },
5520
+ () => table(
5521
+ items2.map((c) => ({
5522
+ id: c.id,
5523
+ name: c.name,
5524
+ schedule: c.enabled ? c.schedule : `${c.schedule} (${t("paused", "en pausa")})`,
5525
+ does: describeCronAction(c.action),
5526
+ last: c.lastRunAt ? `${fmtDate(c.lastRunAt)} ${c.lastOk === false ? t("failed", "fall\xF3") : "ok"}` : "\u2014",
5527
+ next: c.enabled ? fmtDate(c.nextRunAt) : "\u2014"
5528
+ })),
5529
+ [["id", "ID"], ["name", "NAME"], ["schedule", "SCHEDULE"], ["does", "DOES"], ["last", "LAST RUN"], ["next", "NEXT RUN"]],
5530
+ t(
5531
+ 'No crons. Create one: easybits crons create "Ping" --cron "*/15 * * * *" --url https://example.com/ping',
5532
+ 'Sin crons. Crea uno: easybits crons create "Ping" --cron "*/15 * * * *" --url https://ejemplo.com/ping'
5533
+ )
5534
+ )
5535
+ );
5536
+ return;
5537
+ }
5538
+ const { items } = await api(ctx, "GET", "/admin/crons");
5539
+ emit(
5540
+ ctx,
5541
+ { items },
5542
+ () => table(
5543
+ items.map((c) => ({
5544
+ key: c.key,
5545
+ status: cronStatus(c),
5546
+ schedule: c.schedule,
5547
+ last: c.lastRun ? `${fmtDate(c.lastRun.startedAt)} (${duration(c.lastRun.durationMs)})` : "\u2014",
5548
+ next: c.paused ? "\u2014" : fmtDate(c.nextRunAt)
5549
+ })),
5550
+ [["key", "JOB"], ["status", "STATUS"], ["schedule", "SCHEDULE"], ["last", "LAST RUN"], ["next", "NEXT RUN"]]
5551
+ )
5552
+ );
5553
+ }
5554
+ },
5555
+ runs: {
5556
+ summary: "Show the last runs of one of your crons (id or name) or of a platform job, with the full error",
5557
+ usage: "easybits crons runs <id|name|job>",
5558
+ examples: ["easybits crons runs 66f0c2a1b2c3d4e5f6a7b8c9", "easybits crons runs backup-machines --json"],
5559
+ async run(ctx) {
5560
+ const key = need(ctx, 0, "id|name|job", this.usage);
5561
+ const mine = await userCronOrNull(ctx, key);
5562
+ const path = mine ? `/crons/${mine.id}/runs?limit=50` : `/admin/crons/${encodeURIComponent(key)}/runs`;
5563
+ const { items } = await api(ctx, "GET", path);
5564
+ emit(ctx, { items }, () => {
5565
+ table(
5566
+ items.map((r) => ({
5567
+ at: fmtDate(r.startedAt),
5568
+ ok: r.ok == null ? t("running", "corriendo") : r.ok ? "ok" : t("failed", "fall\xF3"),
5569
+ status: r.status ?? "\u2014",
5570
+ took: duration(r.durationMs),
5571
+ trigger: r.trigger,
5572
+ summary: (r.error ?? r.summary ?? "").slice(0, 80)
5573
+ })),
5574
+ [["at", "STARTED"], ["ok", "RESULT"], ["status", "HTTP"], ["took", "TOOK"], ["trigger", "TRIGGER"], ["summary", "SUMMARY"]],
5575
+ t("No runs yet.", "Todav\xEDa no hay corridas.")
5576
+ );
5577
+ const failed = items.find((r) => r.ok === false && r.error);
5578
+ if (failed) console.log(`
5579
+ ${t("Last error", "\xDAltimo error")} (${fmtDate(failed.startedAt)}):
5580
+ ${failed.error}`);
5581
+ });
5582
+ }
5583
+ },
5584
+ run: {
5585
+ summary: "Run one of your crons or a platform job now, in the background (trigger manual)",
5586
+ usage: "easybits crons run <id|name|job>",
5587
+ examples: ["easybits crons run 66f0c2a1b2c3d4e5f6a7b8c9", "easybits crons run purge-notifications --json"],
5588
+ async run(ctx) {
5589
+ const key = need(ctx, 0, "id|name|job", this.usage);
5590
+ const mine = await userCronOrNull(ctx, key);
5591
+ const r = await api(ctx, "POST", mine ? `/crons/${mine.id}/run` : `/admin/crons/${encodeURIComponent(key)}/run`);
5592
+ emit(
5593
+ ctx,
5594
+ r,
5595
+ () => console.log(t(`Started ${key}. Follow it with: easybits crons runs ${key}`, `Arranc\xF3 ${key}. S\xEDguela con: easybits crons runs ${key}`))
5596
+ );
5597
+ }
5598
+ },
5599
+ pause: {
5600
+ summary: "Pause one of your crons or a platform job until you resume it",
5601
+ usage: "easybits crons pause <id|name|job>",
5602
+ examples: ["easybits crons pause 66f0c2a1b2c3d4e5f6a7b8c9", "easybits crons pause check-hosting"],
5603
+ async run(ctx) {
5604
+ const key = need(ctx, 0, "id|name|job", this.usage);
5605
+ const mine = await userCronOrNull(ctx, key);
5606
+ const r = mine ? await api(ctx, "PATCH", `/crons/${mine.id}`, { enabled: false }) : await api(ctx, "POST", `/admin/crons/${encodeURIComponent(key)}/pause`, { paused: true });
5607
+ emit(ctx, r, () => console.log(t(`Paused ${key}.`, `${key} en pausa.`)));
5608
+ }
5609
+ },
5610
+ resume: {
5611
+ summary: "Resume one of your crons or a platform job",
5612
+ usage: "easybits crons resume <id|name|job>",
5613
+ examples: ["easybits crons resume 66f0c2a1b2c3d4e5f6a7b8c9", "easybits crons resume check-hosting"],
5614
+ async run(ctx) {
5615
+ const key = need(ctx, 0, "id|name|job", this.usage);
5616
+ const mine = await userCronOrNull(ctx, key);
5617
+ const r = mine ? await api(ctx, "PATCH", `/crons/${mine.id}`, { enabled: true }) : await api(ctx, "POST", `/admin/crons/${encodeURIComponent(key)}/pause`, { paused: false });
5618
+ emit(ctx, r, () => console.log(t(`Resumed ${key}.`, `${key} reanudada.`)));
5619
+ }
5620
+ },
5621
+ create: {
5622
+ aliases: ["add"],
5623
+ summary: "Create a cron: call a public https URL or send a turn to one of your agents (at most every 5 min)",
5624
+ usage: 'easybits crons create <name> --cron "<5 fields>" [--tz <IANA>] (--url <https-url> [--method] [--body] [--secret NAME] | --agent <id> --prompt <text>)',
5625
+ options: {
5626
+ cron: { type: "string", value: "expr", description: 'Cron of 5 fields, e.g. "30 8 * * *" = daily 8:30' },
5627
+ tz: { type: "string", value: "zone", description: "IANA time zone (default America/Mexico_City)" },
5628
+ ...ACTION_OPTIONS
5629
+ },
5630
+ examples: [
5631
+ `easybits crons create "Study reminder" --cron "30 8 * * *" --url https://www.ghosty.studio/api/v2/me/push --method POST --body '{"title":"Study time"}' --secret GHOSTY_PUSH_TOKEN`,
5632
+ 'easybits crons create "Weekly report" --cron "0 9 * * 1" --agent 6a3c046d2cf7d7f4163f739d --prompt "Send me the weekly sales summary"'
5633
+ ],
5634
+ async run(ctx) {
5635
+ const name = need(ctx, 0, "name", this.usage);
5636
+ const cron = str(ctx, "cron");
5637
+ if (!cron) throw usageError(t("Missing --cron.", "Falta --cron."), this.usage);
5638
+ const a = actionFromFlags(flagsOf(ctx));
5639
+ if ("error" in a) throw usageError(a.error, this.usage);
5640
+ if (!a.action) throw usageError(t("Missing --url or --agent.", "Falta --url o --agent."), this.usage);
5641
+ const c = await api(ctx, "POST", "/crons", { name, cron, ...str(ctx, "tz") ? { timezone: str(ctx, "tz") } : {}, action: a.action });
5642
+ emit(ctx, c, () => console.log(t(`Created ${c.id} \xAB${c.name}\xBB \u2014 ${c.schedule}; next ${fmtDate(c.nextRunAt)}`, `Creado ${c.id} \xAB${c.name}\xBB \u2014 ${c.schedule}; pr\xF3xima ${fmtDate(c.nextRunAt)}`)));
5643
+ }
5644
+ },
5645
+ update: {
5646
+ aliases: ["edit", "set"],
5647
+ summary: "Change one of your crons (only what you pass)",
5648
+ usage: 'easybits crons update <id|name> [--name <n>] [--cron "<expr>"] [--tz <IANA>] [--url \u2026 | --agent \u2026 --prompt \u2026]',
5649
+ options: {
5650
+ name: { type: "string", value: "name", description: "New name" },
5651
+ cron: { type: "string", value: "expr", description: "New schedule" },
5652
+ tz: { type: "string", value: "zone", description: "New IANA time zone" },
5653
+ ...ACTION_OPTIONS
5654
+ },
5655
+ examples: ['easybits crons update 66f0c2a1b2c3d4e5f6a7b8c9 --cron "0 7 * * 1-5"'],
5656
+ async run(ctx) {
5657
+ const ref = need(ctx, 0, "id|name", this.usage);
5658
+ const mine = await resolveUserCron(ctx, ref);
5659
+ if (!mine) throw new CliError(t(`You have no cron "${ref}".`, `No tienes un cron "${ref}".`), EXIT.API, "easybits crons ls", "not_found", 404);
5660
+ const a = actionFromFlags(flagsOf(ctx));
5661
+ if ("error" in a) throw usageError(a.error, this.usage);
5662
+ const patch = {
5663
+ ...str(ctx, "name") ? { name: str(ctx, "name") } : {},
5664
+ ...str(ctx, "cron") ? { cron: str(ctx, "cron") } : {},
5665
+ ...str(ctx, "tz") ? { timezone: str(ctx, "tz") } : {},
5666
+ ...a.action ? { action: a.action } : {}
5667
+ };
5668
+ if (!Object.keys(patch).length) throw usageError(t("Nothing to change.", "No hay nada que cambiar."), this.usage);
5669
+ const c = await api(ctx, "PATCH", `/crons/${mine.id}`, patch);
5670
+ emit(ctx, c, () => console.log(t(`Updated \xAB${c.name}\xBB \u2014 ${c.schedule}`, `Actualizado \xAB${c.name}\xBB \u2014 ${c.schedule}`)));
5671
+ }
5672
+ },
5673
+ rm: {
5674
+ aliases: ["remove", "delete"],
5675
+ summary: "Delete one of your crons",
5676
+ usage: "easybits crons rm <id|name> [--yes]",
5677
+ options: { ...YES_OPTION },
5678
+ examples: ["easybits crons rm 66f0c2a1b2c3d4e5f6a7b8c9 --yes"],
5679
+ async run(ctx) {
5680
+ const ref = need(ctx, 0, "id|name", this.usage);
5681
+ const mine = await resolveUserCron(ctx, ref);
5682
+ if (!mine) throw new CliError(t(`You have no cron "${ref}".`, `No tienes un cron "${ref}".`), EXIT.API, "easybits crons ls", "not_found", 404);
5683
+ requireYesIfHeadless(ctx);
5684
+ await confirm(ctx, t(`Delete cron \xAB${mine.name}\xBB? It stops running right away.`, `\xBFBorrar el cron \xAB${mine.name}\xBB? Deja de correr de inmediato.`));
5685
+ const r = await api(ctx, "DELETE", `/crons/${mine.id}`);
5686
+ emit(ctx, r, () => console.log(t(`Deleted \xAB${mine.name}\xBB`, `Borrado \xAB${mine.name}\xBB`)));
5687
+ }
5688
+ }
5689
+ },
5690
+ defaultSub: "ls"
5691
+ };
5692
+
5419
5693
  // src/commands/documents.ts
5420
5694
  import { existsSync as existsSync16, mkdirSync as mkdirSync7, readFileSync as readFileSync18, statSync as statSync11, writeFileSync as writeFileSync10 } from "fs";
5421
5695
  import { join as join11 } from "path";
@@ -6203,6 +6477,7 @@ var COMMANDS = [
6203
6477
  doctor2,
6204
6478
  usage,
6205
6479
  secrets,
6480
+ crons,
6206
6481
  sandboxes,
6207
6482
  agents,
6208
6483
  fleet,
@@ -6543,6 +6818,29 @@ var HELP_ES = {
6543
6818
  "Path kept out of the release (repeatable)": "Ruta que no entra al release (repetible)",
6544
6819
  "Release a machine: stops billing, takes a last backup and destroys it": "Libera una m\xE1quina: deja de cobrarse, toma un \xFAltimo respaldo y la destruye",
6545
6820
  "The hosting catalog: sizes and monthly prices in MXN": "El cat\xE1logo de hosting: tama\xF1os y precio mensual en MXN",
6821
+ // crons (tuyos y de la plataforma)
6822
+ "Your scheduled jobs (call a URL or message an agent) and, for admins, the platform's": "Tus tareas programadas (pegarle a una URL o escribirle a un agente) y, para admins, las de la plataforma",
6823
+ "List your crons (or, with --platform, the platform's scheduled jobs)": "Enlista tus crons (o, con --platform, las tareas de la plataforma)",
6824
+ "Platform jobs (backups, purges\u2026; platform admins only)": "Tareas de la plataforma (respaldos, purgas\u2026; s\xF3lo admins)",
6825
+ "Show the last runs of one of your crons (id or name) or of a platform job, with the full error": "Muestra las \xFAltimas corridas de un cron tuyo (id o nombre) o de una tarea de la plataforma, con el error completo",
6826
+ "Run one of your crons or a platform job now, in the background (trigger manual)": "Corre ya un cron tuyo o una tarea de la plataforma, en segundo plano (manual)",
6827
+ "Pause one of your crons or a platform job until you resume it": "Pausa un cron tuyo o una tarea de la plataforma hasta que lo reanudes",
6828
+ "Resume one of your crons or a platform job": "Reanuda un cron tuyo o una tarea de la plataforma",
6829
+ "Create a cron: call a public https URL or send a turn to one of your agents (at most every 5 min)": "Crea un cron: pegarle a una URL https p\xFAblica o mandarle un turno a uno de tus agentes (como mucho cada 5 min)",
6830
+ 'Cron of 5 fields, e.g. "30 8 * * *" = daily 8:30': 'Cron de 5 campos, p. ej. "30 8 * * *" = diario 8:30',
6831
+ "IANA time zone (default America/Mexico_City)": "Zona horaria IANA (default America/Mexico_City)",
6832
+ "Call this public https URL on each run": "Pegarle a esta URL https p\xFAblica en cada corrida",
6833
+ "HTTP method (default GET)": "M\xE9todo HTTP (default GET)",
6834
+ "Request body (not with GET)": "Cuerpo de la petici\xF3n (no con GET)",
6835
+ "Vault secret sent as a header (default Authorization: Bearer)": "Secreto de la b\xF3veda que viaja como header (default Authorization: Bearer)",
6836
+ "Header for --secret": "Header para --secret",
6837
+ "Send a turn to this fleet agent instead of calling a URL": "Mandarle un turno a este agente de la flota en vez de pegarle a una URL",
6838
+ "What the agent is told on each run (with --agent)": "Lo que se le dice al agente en cada corrida (con --agent)",
6839
+ "Change one of your crons (only what you pass)": "Cambia un cron tuyo (s\xF3lo lo que pases)",
6840
+ "New name": "Nombre nuevo",
6841
+ "New schedule": "Horario nuevo",
6842
+ "New IANA time zone": "Zona horaria IANA nueva",
6843
+ "Delete one of your crons": "Borra un cron tuyo",
6546
6844
  // secrets (vault de la cuenta)
6547
6845
  "Your account's secrets vault: names only; used as $secret:NAME": "La b\xF3veda de secretos de tu cuenta: s\xF3lo nombres; se usan como $secret:NOMBRE",
6548
6846
  "List the names in your vault (values never come back)": "Enlista los nombres de tu b\xF3veda (los valores nunca regresan)",
@@ -6857,7 +7155,7 @@ function normalizeArgs(input) {
6857
7155
  }
6858
7156
 
6859
7157
  // src/index.ts
6860
- var VERSION2 = true ? "0.17.0" : "dev";
7158
+ var VERSION2 = true ? "0.18.0" : "dev";
6861
7159
  var GLOBAL_OPTIONS = {
6862
7160
  json: { type: "boolean" },
6863
7161
  token: { type: "string" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@easybits.cloud/cli",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "Official CLI for EasyBits, the cloud for AI agents — sandboxes, hosting, domains, databases, agents and files from your terminal",
5
5
  "type": "module",
6
6
  "bin": {