@easybits.cloud/cli 0.19.0 → 0.19.1
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 +5 -2
- package/dist/index.js +89 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -131,7 +131,8 @@ easybits crons create "Heads-up" --at 23:00 --url https://my-app.com/api/notice
|
|
|
131
131
|
easybits crons create "Heads-up" --in 2h --url https://my-app.com/api/notice --method POST # once; --until/--times end repeating ones
|
|
132
132
|
easybits crons create "Review" --at 23:00 --run "python3 asc.py submit" --file scripts/asc.py \
|
|
133
133
|
--secret-file ASC_P8:~/.appstoreconnect/private_keys/AuthKey_X.p8 # a command in a throwaway box; the .p8 only this cron reads
|
|
134
|
-
easybits crons
|
|
134
|
+
easybits crons get "Review" # what it does, how it notifies you, next run (--json: all of it)
|
|
135
|
+
easybits crons runs "Review" --logs --limit 5 # exit code + stdout/stderr
|
|
135
136
|
easybits crons wait "Review" --timeout 2h # blocks until it runs; exits with its exit code
|
|
136
137
|
easybits crons update "Review" --notify push,email --notify-on always # push to the Ghosty app / email to you
|
|
137
138
|
easybits crons ls
|
|
@@ -145,7 +146,9 @@ easybits crons rm "Study reminder" --yes
|
|
|
145
146
|
easybits inbox create builds # prints the URL (anyone with it can write)
|
|
146
147
|
easybits inbox ls
|
|
147
148
|
easybits inbox listen builds # one JSON line per event, live (Claude Code: run it with Monitor)
|
|
148
|
-
easybits inbox read builds # the unread ones, marked read
|
|
149
|
+
easybits inbox read builds # the unread ones, marked read (--since <seq>: from a cursor)
|
|
150
|
+
easybits inbox update builds --retention 30 # rename (--name) or keep events longer; the URL stays
|
|
151
|
+
easybits inbox rotate builds --yes # new URL; the old one stops working (if it leaked)
|
|
149
152
|
easybits inbox rm builds --yes
|
|
150
153
|
|
|
151
154
|
# Platform crons (platform admins only; key with the ADMIN scope)
|
package/dist/index.js
CHANGED
|
@@ -4918,7 +4918,7 @@ async function updateNotice(current2) {
|
|
|
4918
4918
|
}
|
|
4919
4919
|
|
|
4920
4920
|
// src/commands/doctor.ts
|
|
4921
|
-
var VERSION = true ? "0.19.
|
|
4921
|
+
var VERSION = true ? "0.19.1" : "dev";
|
|
4922
4922
|
var doctor2 = {
|
|
4923
4923
|
name: "doctor",
|
|
4924
4924
|
group: "Account",
|
|
@@ -5438,6 +5438,19 @@ function describeCronAction(a) {
|
|
|
5438
5438
|
}
|
|
5439
5439
|
return `agent ${a.fleetAgentId}: ${a.prompt.length > 40 ? a.prompt.slice(0, 39) + "\u2026" : a.prompt}`;
|
|
5440
5440
|
}
|
|
5441
|
+
function describeCron(c) {
|
|
5442
|
+
const n = c.notify;
|
|
5443
|
+
const chans = n ? [n.push ? "push" : "", n.email ? "email" : "", n.inbox ? `inbox:${n.inbox}` : ""].filter(Boolean).join(", ") : "";
|
|
5444
|
+
const state = c.enabled ? t("active", "activo") : c.done ? t("done", "terminado") : t("paused", "en pausa");
|
|
5445
|
+
return [
|
|
5446
|
+
`${c.id} \xAB${c.name}\xBB (${state})`,
|
|
5447
|
+
`${t("Schedule", "Horario")}: ${c.schedule} (${c.timezone})${c.until ? ` ${t("until", "hasta")} ${c.until}` : ""}${c.timesLeft != null ? ` \xB7 ${t("runs left", "corridas restantes")}: ${c.timesLeft}` : ""}`,
|
|
5448
|
+
`${t("Next run", "Pr\xF3xima")}: ${c.enabled && c.nextRunLocal ? `${c.nextRunLocal} (${c.in})` : "\u2014"}`,
|
|
5449
|
+
`${t("Does", "Hace")}: ${describeCronAction(c.action)}`,
|
|
5450
|
+
`${t("Notifies", "Avisa")}: ${chans ? `${chans} (${n.on === "always" ? t("always", "siempre") : t("on failure", "si falla")})` : "\u2014"}`,
|
|
5451
|
+
`${t("Last run", "\xDAltima")}: ${c.lastRunAt ? `${fmtDate(c.lastRunAt)} ${c.lastOk === false ? t("failed", "fall\xF3") : "ok"}${c.lastError ? ` \u2014 ${c.lastError}` : ""}` : "\u2014"}`
|
|
5452
|
+
];
|
|
5453
|
+
}
|
|
5441
5454
|
async function resolveUserCron(ctx, ref2) {
|
|
5442
5455
|
const { items } = await api(ctx, "GET", "/crons");
|
|
5443
5456
|
const byId = items.find((c) => c.id === ref2);
|
|
@@ -5636,7 +5649,7 @@ var crons = {
|
|
|
5636
5649
|
aliases: ["cron"],
|
|
5637
5650
|
group: "Account",
|
|
5638
5651
|
summary: "Your scheduled jobs (call a URL, message an agent or run a command; repeating or once) and, for admins, the platform's",
|
|
5639
|
-
synopsis: "crons <ls|create|update|rm|run|runs|wait|pause|resume>",
|
|
5652
|
+
synopsis: "crons <ls|get|create|update|rm|run|runs|wait|pause|resume>",
|
|
5640
5653
|
subs: {
|
|
5641
5654
|
ls: {
|
|
5642
5655
|
aliases: ["list"],
|
|
@@ -5685,16 +5698,35 @@ var crons = {
|
|
|
5685
5698
|
);
|
|
5686
5699
|
}
|
|
5687
5700
|
},
|
|
5701
|
+
get: {
|
|
5702
|
+
aliases: ["show"],
|
|
5703
|
+
summary: "Show one of your crons: what it does, how it notifies you and when it runs (--json: the whole object)",
|
|
5704
|
+
usage: "easybits crons get <id|name> [--json]",
|
|
5705
|
+
examples: ['easybits crons get "Review 1.0.10"', "easybits crons get 66f0c2a1b2c3d4e5f6a7b8c9 --json"],
|
|
5706
|
+
async run(ctx) {
|
|
5707
|
+
const ref2 = need(ctx, 0, "id|name", this.usage);
|
|
5708
|
+
const c = await resolveUserCron(ctx, ref2);
|
|
5709
|
+
if (!c) throw new CliError(t(`You have no cron "${ref2}".`, `No tienes un cron "${ref2}".`), EXIT.API, "easybits crons ls", "not_found", 404);
|
|
5710
|
+
emit(ctx, c, () => {
|
|
5711
|
+
for (const line2 of describeCron(c)) console.log(line2);
|
|
5712
|
+
});
|
|
5713
|
+
}
|
|
5714
|
+
},
|
|
5688
5715
|
runs: {
|
|
5689
5716
|
summary: "Show the last runs of one of your crons (id or name) or of a platform job, with the full error",
|
|
5690
|
-
usage: "easybits crons runs <id|name|job> [--logs]",
|
|
5691
|
-
options: {
|
|
5717
|
+
usage: "easybits crons runs <id|name|job> [--limit N] [--logs]",
|
|
5718
|
+
options: {
|
|
5719
|
+
limit: { type: "string", value: "N", description: "At most N runs of one of your crons (1-50, default 50)" },
|
|
5720
|
+
logs: { type: "boolean", description: "Also print stdout/stderr of --run crons (last 64 KB per run)" }
|
|
5721
|
+
},
|
|
5692
5722
|
examples: ["easybits crons runs 66f0c2a1b2c3d4e5f6a7b8c9", 'easybits crons runs "Review 1.0.10" --logs', "easybits crons runs backup-machines --json"],
|
|
5693
5723
|
async run(ctx) {
|
|
5694
5724
|
const key = need(ctx, 0, "id|name|job", this.usage);
|
|
5695
5725
|
const mine = await userCronOrNull(ctx, key);
|
|
5696
5726
|
const withLogs = bool(ctx, "logs");
|
|
5697
|
-
const
|
|
5727
|
+
const limit = str(ctx, "limit") ?? "50";
|
|
5728
|
+
if (!/^\d+$/.test(limit) || Number(limit) < 1 || Number(limit) > 50) throw usageError(t("--limit is 1 to 50.", "--limit va de 1 a 50."), this.usage);
|
|
5729
|
+
const path = mine ? `/crons/${mine.id}/runs?limit=${limit}${withLogs ? "&logs=1" : ""}` : `/admin/crons/${encodeURIComponent(key)}/runs`;
|
|
5698
5730
|
const { items } = await api(ctx, "GET", path);
|
|
5699
5731
|
emit(ctx, { items }, () => {
|
|
5700
5732
|
table(
|
|
@@ -5846,7 +5878,7 @@ ${failed.error}`);
|
|
|
5846
5878
|
update: {
|
|
5847
5879
|
aliases: ["edit", "set"],
|
|
5848
5880
|
summary: "Change one of your crons (only what you pass)",
|
|
5849
|
-
usage: 'easybits crons update <id|name> [--name <n>] [--cron "<expr>" | --at <when> | --in <duration>] [--until <when>] [--times N] [--tz <IANA>] [--url \u2026 | --agent \u2026 --prompt \u2026]',
|
|
5881
|
+
usage: 'easybits crons update <id|name> [--name <n>] [--cron "<expr>" | --at <when> | --in <duration>] [--until <when>] [--times N] [--tz <IANA>] [--url \u2026 | --agent \u2026 --prompt \u2026 | --run <cmd> [--file f]\u2026 [--secret NAME]\u2026] [--notify push,email,inbox:<name>|none] [--notify-on failure|always]',
|
|
5850
5882
|
options: {
|
|
5851
5883
|
name: { type: "string", value: "name", description: "New name" },
|
|
5852
5884
|
cron: { type: "string", value: "expr", description: "New schedule" },
|
|
@@ -5987,7 +6019,7 @@ var inbox = {
|
|
|
5987
6019
|
aliases: ["inboxes"],
|
|
5988
6020
|
group: "Account",
|
|
5989
6021
|
summary: "Event inboxes: your own webhook URL whose events reach your terminal live or wait for you to read them",
|
|
5990
|
-
synopsis: "inbox <ls|create|listen|read|rm>",
|
|
6022
|
+
synopsis: "inbox <ls|create|listen|read|update|rotate|rm>",
|
|
5991
6023
|
subs: {
|
|
5992
6024
|
ls: {
|
|
5993
6025
|
aliases: ["list"],
|
|
@@ -6040,17 +6072,22 @@ var inbox = {
|
|
|
6040
6072
|
},
|
|
6041
6073
|
read: {
|
|
6042
6074
|
summary: "Show the events you haven't read yet and mark them read (the next read brings only new ones)",
|
|
6043
|
-
usage: "easybits inbox read <id|name> [--all] [--no-ack] [--limit N]",
|
|
6075
|
+
usage: "easybits inbox read <id|name> [--all | --since <seq>] [--no-ack] [--limit N]",
|
|
6044
6076
|
options: {
|
|
6045
6077
|
all: { type: "boolean", description: "Everything still kept, read or not" },
|
|
6078
|
+
since: { type: "string", value: "seq", description: "Events after this one, read or not (the cursor of a previous read)" },
|
|
6046
6079
|
"no-ack": { type: "boolean", description: "Just look; don't mark them read" },
|
|
6047
6080
|
limit: { type: "string", value: "N", description: "At most N events (default 50, max 200)" }
|
|
6048
6081
|
},
|
|
6049
|
-
examples: ["easybits inbox read builds", "easybits inbox read builds --all --no-ack --json"],
|
|
6082
|
+
examples: ["easybits inbox read builds", "easybits inbox read builds --all --no-ack --json", "easybits inbox read builds --since 12 --no-ack"],
|
|
6050
6083
|
async run(ctx) {
|
|
6051
6084
|
const name = need(ctx, 0, "id|name", this.usage);
|
|
6085
|
+
const since = str(ctx, "since");
|
|
6086
|
+
if (since != null && !/^\d+$/.test(since)) throw usageError(t("--since is an event number.", "--since es un n\xFAmero de evento."), this.usage);
|
|
6087
|
+
if (since != null && bool(ctx, "all")) throw usageError(t("--all and --since are exclusive.", "--all y --since no van juntos."), this.usage);
|
|
6052
6088
|
const q = new URLSearchParams();
|
|
6053
6089
|
if (bool(ctx, "all")) q.set("since", "0");
|
|
6090
|
+
if (since != null) q.set("since", since);
|
|
6054
6091
|
if (bool(ctx, "no-ack")) q.set("ack", "0");
|
|
6055
6092
|
if (str(ctx, "limit")) q.set("limit", str(ctx, "limit"));
|
|
6056
6093
|
const r = await api(ctx, "GET", `/inboxes/${ref(name)}/events${q.size ? `?${q}` : ""}`);
|
|
@@ -6063,6 +6100,41 @@ Quedan ${r.unread} sin leer: easybits inbox read ${name}`));
|
|
|
6063
6100
|
});
|
|
6064
6101
|
}
|
|
6065
6102
|
},
|
|
6103
|
+
update: {
|
|
6104
|
+
aliases: ["edit", "set"],
|
|
6105
|
+
summary: "Rename an inbox or change how long it keeps events (its URL stays the same)",
|
|
6106
|
+
usage: "easybits inbox update <id|name> [--name <new>] [--retention <days>]",
|
|
6107
|
+
options: {
|
|
6108
|
+
name: { type: "string", value: "new", description: "New name, unique in your account" },
|
|
6109
|
+
retention: { type: "string", value: "days", description: "Days events are kept (1-30)" }
|
|
6110
|
+
},
|
|
6111
|
+
examples: ["easybits inbox update builds --retention 30", "easybits inbox update builds --name ci --json"],
|
|
6112
|
+
async run(ctx) {
|
|
6113
|
+
const name = need(ctx, 0, "id|name", this.usage);
|
|
6114
|
+
const days = str(ctx, "retention");
|
|
6115
|
+
if (days != null && !/^\d+$/.test(days)) throw usageError(t("--retention is whole days.", "--retention son d\xEDas enteros."), this.usage);
|
|
6116
|
+
const patch = { ...str(ctx, "name") ? { name: str(ctx, "name") } : {}, ...days ? { retentionDays: Number(days) } : {} };
|
|
6117
|
+
if (!Object.keys(patch).length) throw usageError(t("Nothing to change: pass --name or --retention.", "No hay nada que cambiar: pasa --name o --retention."), this.usage);
|
|
6118
|
+
const b = await api(ctx, "PATCH", `/inboxes/${ref(name)}`, patch);
|
|
6119
|
+
emit(ctx, b, () => console.log(t(`Updated \xAB${b.name}\xBB: keeps events ${b.retentionDays} days.`, `Actualizado \xAB${b.name}\xBB: guarda los eventos ${b.retentionDays} d\xEDas.`)));
|
|
6120
|
+
}
|
|
6121
|
+
},
|
|
6122
|
+
rotate: {
|
|
6123
|
+
summary: "Give an inbox a new URL and print it; the old one stops accepting writes right away (stored events stay)",
|
|
6124
|
+
usage: "easybits inbox rotate <id|name> [--yes]",
|
|
6125
|
+
options: { ...YES_OPTION },
|
|
6126
|
+
examples: ["easybits inbox rotate builds --yes", "easybits inbox rotate builds --yes --json"],
|
|
6127
|
+
async run(ctx) {
|
|
6128
|
+
const name = need(ctx, 0, "id|name", this.usage);
|
|
6129
|
+
requireYesIfHeadless(ctx);
|
|
6130
|
+
await confirm(ctx, t(`New URL for \xAB${name}\xBB? Whoever uses the old one stops getting through.`, `\xBFURL nueva para \xAB${name}\xBB? Quien use la vieja deja de poder escribir.`));
|
|
6131
|
+
const b = await api(ctx, "POST", `/inboxes/${ref(name)}/rotate`);
|
|
6132
|
+
emit(ctx, b, () => {
|
|
6133
|
+
console.log(t(`New URL for \xAB${b.name}\xBB (the old one no longer works):`, `URL nueva de \xAB${b.name}\xBB (la vieja ya no sirve):`));
|
|
6134
|
+
console.log(` ${b.url}`);
|
|
6135
|
+
});
|
|
6136
|
+
}
|
|
6137
|
+
},
|
|
6066
6138
|
rm: {
|
|
6067
6139
|
aliases: ["remove", "delete"],
|
|
6068
6140
|
summary: "Delete an inbox and its events; its URL stops accepting writes",
|
|
@@ -7241,6 +7313,13 @@ var HELP_ES = {
|
|
|
7241
7313
|
"Just look; don't mark them read": "S\xF3lo mirar; no marcarlos como le\xEDdos",
|
|
7242
7314
|
"At most N events (default 50, max 200)": "Como m\xE1ximo N eventos (default 50, tope 200)",
|
|
7243
7315
|
"Delete an inbox and its events; its URL stops accepting writes": "Borra un buz\xF3n y sus eventos; su URL deja de aceptar escrituras",
|
|
7316
|
+
"Events after this one, read or not (the cursor of a previous read)": "Los eventos despu\xE9s de \xE9ste, le\xEDdos o no (el cursor de una lectura anterior)",
|
|
7317
|
+
"Rename an inbox or change how long it keeps events (its URL stays the same)": "Renombra un buz\xF3n o cambia cu\xE1nto guarda los eventos (su URL no cambia)",
|
|
7318
|
+
"New name, unique in your account": "Nombre nuevo, \xFAnico en tu cuenta",
|
|
7319
|
+
"Days events are kept (1-30)": "D\xEDas que se guardan los eventos (1-30)",
|
|
7320
|
+
"Give an inbox a new URL and print it; the old one stops accepting writes right away (stored events stay)": "Le da al buz\xF3n una URL nueva y la imprime; la vieja deja de aceptar escrituras de inmediato (los eventos guardados se quedan)",
|
|
7321
|
+
"Show one of your crons: what it does, how it notifies you and when it runs (--json: the whole object)": "Muestra un cron tuyo: qu\xE9 hace, c\xF3mo te avisa y cu\xE1ndo corre (--json: el objeto completo)",
|
|
7322
|
+
"At most N runs of one of your crons (1-50, default 50)": "Como m\xE1ximo N corridas de un cron tuyo (1-50, default 50)",
|
|
7244
7323
|
"When to notify (default failure)": "Cu\xE1ndo avisar (default failure: s\xF3lo si falla)",
|
|
7245
7324
|
'Run ONCE at this time: ISO ("2026-10-18T23:00", read in --tz) or "23:00" (today)': 'Correr UNA SOLA VEZ a esta hora: ISO ("2026-10-18T23:00", en la zona de --tz) o "23:00" (hoy)',
|
|
7246
7325
|
"Run ONCE after this long: 10m, 2h, 1h30m, 1d": "Correr UNA SOLA VEZ dentro de: 10m, 2h, 1h30m, 1d",
|
|
@@ -7574,7 +7653,7 @@ function normalizeArgs(input) {
|
|
|
7574
7653
|
}
|
|
7575
7654
|
|
|
7576
7655
|
// src/index.ts
|
|
7577
|
-
var VERSION2 = true ? "0.19.
|
|
7656
|
+
var VERSION2 = true ? "0.19.1" : "dev";
|
|
7578
7657
|
var GLOBAL_OPTIONS = {
|
|
7579
7658
|
json: { type: "boolean" },
|
|
7580
7659
|
token: { type: "string" },
|
package/package.json
CHANGED