@easybits.cloud/cli 0.15.0 → 0.15.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.
Files changed (3) hide show
  1. package/README.md +9 -0
  2. package/dist/index.js +59 -12
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -168,6 +168,15 @@ Host *.ghosty
168
168
 
169
169
  Full reference: https://www.easybits.cloud/docs#cli · https://www.easybits.cloud/en/docs/cli.md
170
170
 
171
+ ## Changes in 0.15.1
172
+
173
+ - After a `--dry-run`, the last line is the real command (`To apply: …`); without a terminal it
174
+ already carries `--yes` when the command asks for confirmation.
175
+ - A 404 with a reason prints the reason; a bare «Not found» says it may also be missing access
176
+ (another account's resource) and points to `easybits whoami`.
177
+ - The «new version» notice checks npm every hour (it was once a day), and asks again when the
178
+ cache is older than what you have installed.
179
+
171
180
  ## Changes in 0.15.0
172
181
 
173
182
  - `db rename <db> <new-name> [--skip-prompts] [--dry-run]` (alias `mv`): renames the database (the
package/dist/index.js CHANGED
@@ -108,6 +108,17 @@ function notLoggedIn() {
108
108
  "not_logged_in"
109
109
  );
110
110
  }
111
+ function isGenericNotFound(body) {
112
+ let msg = body;
113
+ try {
114
+ const j = JSON.parse(body);
115
+ msg = j?.message ?? j?.error ?? j?.detail ?? "";
116
+ } catch {
117
+ }
118
+ if (typeof msg !== "string") return false;
119
+ const s = msg.trim();
120
+ return !s || s.startsWith("<") || /^(404\s*:?\s*)?not found\.?$/i.test(s);
121
+ }
111
122
  function apiMessage(body) {
112
123
  try {
113
124
  const j = JSON.parse(body);
@@ -142,6 +153,14 @@ function toCliError(err) {
142
153
  401
143
154
  );
144
155
  }
156
+ if (err.status === 404) {
157
+ const hint404 = t(
158
+ "Check the id (list what you have with the matching `ls` command) and that your key is from the account that owns it (easybits whoami).",
159
+ "Revisa el id (enlista lo que tienes con el `ls` que corresponda) y que tu key sea de la cuenta due\xF1a (easybits whoami)."
160
+ );
161
+ if (isGenericNotFound(err.body)) return new CliError(t("Not found \u2014 or you don't have access to it.", "No se encontr\xF3, o no tienes acceso."), EXIT.API, hint404, "not_found", 404);
162
+ return new CliError(message2, EXIT.API, (kind && KIND_HINTS()[kind]) ?? hint404, "not_found", 404);
163
+ }
145
164
  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
165
  return new CliError(`${t("API error", "Error de la API")} ${err.status}: ${message2}`, EXIT.API, hint, "api_error", err.status);
147
166
  }
@@ -683,9 +702,18 @@ import { createInterface } from "readline/promises";
683
702
  var YES_OPTION = {
684
703
  yes: { type: "boolean", short: "y", description: "Skip the confirmation (required without a terminal or with --json)" }
685
704
  };
686
- function sameCommand() {
687
- const argv2 = process.argv.slice(2).map((a) => /^[\w@%+=:,./-]+$/.test(a) ? a : `'${a.replace(/'/g, `'\\''`)}'`);
688
- return `easybits ${argv2.join(" ")}`;
705
+ function sameCommand(argv2 = process.argv.slice(2)) {
706
+ const quoted = argv2.map((a) => /^[\w@%+=:,./-]+$/.test(a) ? a : `'${a.replace(/'/g, `'\\''`)}'`);
707
+ return `easybits ${quoted.join(" ")}`;
708
+ }
709
+ function applyCommand(argv2, opts) {
710
+ const rest = argv2.filter((a) => a !== "--dry-run");
711
+ const hasYes = rest.some((a) => a === "--yes" || a === "-y");
712
+ return `${sameCommand(rest)}${opts.needsYes && opts.headless && !hasYes ? " --yes" : ""}`;
713
+ }
714
+ function applyHint(ctx, opts = {}) {
715
+ const cmd = applyCommand(process.argv.slice(2), { headless: !interactive(ctx), needsYes: opts.needsYes });
716
+ return t(`To apply: ${cmd}`, `Para aplicarlo: ${cmd}`);
689
717
  }
690
718
  async function ask(question) {
691
719
  const rl = createInterface({ input: process.stdin, output: process.stderr });
@@ -2138,6 +2166,7 @@ var set = {
2138
2166
  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."));
2139
2167
  for (const [k, v] of Object.entries(changes)) console.log(`${k}: ${v.from} \u2192 ${v.to}`);
2140
2168
  console.log(t("(dry run: nothing changed)", "(simulaci\xF3n: no cambi\xF3 nada)"));
2169
+ console.log(applyHint(ctx));
2141
2170
  });
2142
2171
  return;
2143
2172
  }
@@ -2204,6 +2233,7 @@ var files2 = {
2204
2233
  emit(ctx, { agentId: id, dryRun: true, upload: plan }, () => {
2205
2234
  for (const p of plan) console.log(`${p.local} \u2192 ${WORK_DIR}/${p.path} (${fmtBytes(p.bytes)})`);
2206
2235
  console.log(t("(dry run: nothing uploaded)", "(simulaci\xF3n: no se subi\xF3 nada)"));
2236
+ console.log(applyHint(ctx));
2207
2237
  });
2208
2238
  return;
2209
2239
  }
@@ -2220,6 +2250,7 @@ var files2 = {
2220
2250
  if (bool(ctx, "dry-run")) {
2221
2251
  emit(ctx, { agentId: id, dryRun: true, delete: rest }, () => {
2222
2252
  for (const p of rest) console.log(`${t("would delete", "borrar\xEDa")} ${WORK_DIR}/${p}`);
2253
+ console.log(applyHint(ctx, { needsYes: true }));
2223
2254
  });
2224
2255
  return;
2225
2256
  }
@@ -2312,9 +2343,12 @@ var skills = {
2312
2343
  const assets = dir ? readSkillDir(dir) : [];
2313
2344
  const plan = { agentId: id, slug, files: ["SKILL.md", ...assets.map((a) => a.name)], restart: bool(ctx, "restart") };
2314
2345
  if (bool(ctx, "dry-run")) {
2315
- emit(ctx, { ...plan, dryRun: true }, () => console.log(t(`would add skill ${slug} (${plan.files.length} file(s))${plan.restart ? " and restart" : ""}
2346
+ emit(ctx, { ...plan, dryRun: true }, () => {
2347
+ console.log(t(`would add skill ${slug} (${plan.files.length} file(s))${plan.restart ? " and restart" : ""}
2316
2348
  (dry run: nothing changed)`, `agregar\xEDa la skill ${slug} (${plan.files.length} archivo(s))${plan.restart ? " y reiniciar\xEDa" : ""}
2317
- (simulaci\xF3n: no cambi\xF3 nada)`)));
2349
+ (simulaci\xF3n: no cambi\xF3 nada)`));
2350
+ console.log(applyHint(ctx));
2351
+ });
2318
2352
  return;
2319
2353
  }
2320
2354
  const r = await api(ctx, "PUT", `/agents/${id}/skills/${slug}`, { markdown, assets });
@@ -2327,7 +2361,10 @@ var skills = {
2327
2361
  const slugs = ctx.args.slice(2);
2328
2362
  if (!slugs.length) throw usageError(t("Give one or more slugs.", "Pasa uno o m\xE1s slugs."), SKILLS_USAGE);
2329
2363
  if (bool(ctx, "dry-run")) {
2330
- emit(ctx, { agentId: id, dryRun: true, delete: slugs }, () => slugs.forEach((s) => console.log(`${t("would remove skill", "quitar\xEDa la skill")} ${s}`)));
2364
+ emit(ctx, { agentId: id, dryRun: true, delete: slugs }, () => {
2365
+ slugs.forEach((s) => console.log(`${t("would remove skill", "quitar\xEDa la skill")} ${s}`));
2366
+ console.log(applyHint(ctx, { needsYes: true }));
2367
+ });
2331
2368
  return;
2332
2369
  }
2333
2370
  requireYesIfHeadless(ctx);
@@ -2391,6 +2428,7 @@ var mcp2 = {
2391
2428
  removed: ${diff.removed.join(", ") || "-"}
2392
2429
  changed: ${diff.changed.join(", ") || "-"}`);
2393
2430
  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)"));
2431
+ console.log(applyHint(ctx));
2394
2432
  });
2395
2433
  return;
2396
2434
  }
@@ -2720,6 +2758,7 @@ var db = {
2720
2758
  console.log(t(`${hit2.name} (${hit2.id}): ${tables.length} table(s), ${rows} row(s)`, `${hit2.name} (${hit2.id}): ${tables.length} tabla(s), ${rows} fila(s)`));
2721
2759
  for (const x of tables) console.log(` ${x.name}: ${x.rows}`);
2722
2760
  console.log(t("(dry run: nothing deleted)", "(simulaci\xF3n: no se borr\xF3 nada)"));
2761
+ console.log(applyHint(ctx, { needsYes: true }));
2723
2762
  });
2724
2763
  return;
2725
2764
  }
@@ -2773,6 +2812,7 @@ var db = {
2773
2812
  console.log(`${from} \u2192 ${to}`);
2774
2813
  printRefs();
2775
2814
  console.log(t("(dry run: nothing changed; without --dry-run those prompts are rewritten too)", "(simulaci\xF3n: no cambi\xF3 nada; sin --dry-run tambi\xE9n se reescriben esos prompts)"));
2815
+ console.log(applyHint(ctx));
2776
2816
  });
2777
2817
  return;
2778
2818
  }
@@ -3115,6 +3155,7 @@ async function applyTarget(ctx, target, o) {
3115
3155
  for (const s of Object.keys(uploads.skills ?? {})) console.log(`+ skill ${s}`);
3116
3156
  for (const f of Object.keys(uploads.files ?? {})) console.log(`+ file ${f}`);
3117
3157
  console.log(t("(dry run: nothing created)", "(simulaci\xF3n: no se cre\xF3 nada)"));
3158
+ console.log(applyHint(ctx));
3118
3159
  });
3119
3160
  return;
3120
3161
  }
@@ -3155,6 +3196,7 @@ async function applyTarget(ctx, target, o) {
3155
3196
  console.log(
3156
3197
  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.")
3157
3198
  );
3199
+ if (o.dryRun && actionable.length) console.log(applyHint(ctx, { needsYes: true }));
3158
3200
  });
3159
3201
  return;
3160
3202
  }
@@ -3320,6 +3362,7 @@ var agents = {
3320
3362
  for (const [k, v] of Object.entries(plan)) console.log(`${k.padEnd(12)} ${Array.isArray(v) ? v.join(", ") || "-" : v ?? "-"}`);
3321
3363
  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."));
3322
3364
  console.log(t("(dry run: nothing created)", "(simulaci\xF3n: no se cre\xF3 nada)"));
3365
+ console.log(applyHint(ctx));
3323
3366
  });
3324
3367
  return;
3325
3368
  }
@@ -3575,7 +3618,7 @@ import { mkdirSync as mkdirSync6, readFileSync as readFileSync14, writeFileSync
3575
3618
  import { homedir as homedir5 } from "os";
3576
3619
  import { dirname as dirname4, join as join8 } from "path";
3577
3620
  var FILE = join8(process.env.XDG_CONFIG_HOME || join8(homedir5(), ".config"), "easybits", "update.json");
3578
- var DAY = 24 * 60 * 60 * 1e3;
3621
+ var TTL = 60 * 60 * 1e3;
3579
3622
  var UPGRADE = "npm i -g @easybits.cloud/cli@latest";
3580
3623
  function newer(a, b) {
3581
3624
  const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
@@ -3603,10 +3646,14 @@ async function fetchLatest(timeoutMs = 1500) {
3603
3646
  return null;
3604
3647
  }
3605
3648
  }
3606
- async function latestVersion() {
3649
+ function cacheFresh(c, current2, now = Date.now()) {
3650
+ if (!c.latest || !c.checkedAt || now - c.checkedAt >= TTL) return false;
3651
+ return !(current2 && current2 !== "dev" && newer(current2, c.latest));
3652
+ }
3653
+ async function latestVersion(current2) {
3607
3654
  if (updateCheckDisabled()) return null;
3608
3655
  const c = readCache();
3609
- if (c.latest && c.checkedAt && Date.now() - c.checkedAt < DAY) return c.latest;
3656
+ if (cacheFresh(c, current2)) return c.latest;
3610
3657
  return await fetchLatest() ?? c.latest ?? null;
3611
3658
  }
3612
3659
  function cachedNewer(current2) {
@@ -3616,12 +3663,12 @@ function cachedNewer(current2) {
3616
3663
  }
3617
3664
  async function updateNotice(current2) {
3618
3665
  if (current2 === "dev") return null;
3619
- const latest = await latestVersion();
3666
+ const latest = await latestVersion(current2);
3620
3667
  return latest && newer(latest, current2) ? t(`Update available: ${current2} \u2192 ${latest}. Run: ${UPGRADE}`, `Hay versi\xF3n nueva: ${current2} \u2192 ${latest}. Corre: ${UPGRADE}`) : null;
3621
3668
  }
3622
3669
 
3623
3670
  // src/commands/doctor.ts
3624
- var VERSION = true ? "0.15.0" : "dev";
3671
+ var VERSION = true ? "0.15.1" : "dev";
3625
3672
  var doctor2 = {
3626
3673
  name: "doctor",
3627
3674
  group: "Account",
@@ -4125,7 +4172,7 @@ function normalizeArgs(input) {
4125
4172
  }
4126
4173
 
4127
4174
  // src/index.ts
4128
- var VERSION2 = true ? "0.15.0" : "dev";
4175
+ var VERSION2 = true ? "0.15.1" : "dev";
4129
4176
  var GLOBAL_OPTIONS = {
4130
4177
  json: { type: "boolean" },
4131
4178
  token: { type: "string" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@easybits.cloud/cli",
3
- "version": "0.15.0",
3
+ "version": "0.15.1",
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": {