@easybits.cloud/cli 0.15.1 → 0.15.3

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 +3 -0
  2. package/dist/index.js +98 -38
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -105,8 +105,11 @@ easybits agents create --from helper.json --name helper-3
105
105
  # Files, websites, account
106
106
  easybits files ls
107
107
  easybits files upload ./report.pdf
108
+ easybits files upload ./logo.png --public # open CDN URL; private by default
108
109
  easybits files rm FILE_ID --yes
109
110
  easybits websites ls
111
+ easybits websites create "My shop" --slug my-shop # slug optional
112
+ easybits websites slug my-shop my-store # change it; the old URL 301s
110
113
  easybits providers # storage provider (Tigris by default)
111
114
  easybits usage
112
115
 
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@
4
4
  import { parseArgs } from "util";
5
5
 
6
6
  // src/commands/account.ts
7
- import { resolveBaseUrl as resolveBaseUrl3 } from "@easybits.cloud/sdk";
7
+ import { resolveBaseUrl as resolveBaseUrl4 } from "@easybits.cloud/sdk";
8
8
 
9
9
  // src/args.ts
10
10
  import { existsSync, readFileSync } from "fs";
@@ -303,6 +303,9 @@ function maskSecret(v) {
303
303
  return v.length <= 8 ? "***" : `${v.slice(0, 4)}\u2026***`;
304
304
  }
305
305
 
306
+ // src/api.ts
307
+ import { EasybitsError as EasybitsError2, resolveBaseUrl as resolveBaseUrl3 } from "@easybits.cloud/sdk";
308
+
306
309
  // src/client.ts
307
310
  import { EasybitsClient, resolveBaseUrl as resolveBaseUrl2 } from "@easybits.cloud/sdk";
308
311
 
@@ -500,6 +503,32 @@ async function getClient(ctx) {
500
503
  return new EasybitsClient({ apiKey, baseUrl: await resolveBaseUrl2() });
501
504
  }
502
505
 
506
+ // src/api.ts
507
+ async function api(ctx, method, path, body, opts = {}) {
508
+ const token = await resolveCredential(ctx);
509
+ const base = (await resolveBaseUrl3()).replace(/\/$/, "");
510
+ const headers = { Authorization: `Bearer ${token}` };
511
+ let payload;
512
+ if (body !== void 0) {
513
+ if (opts.raw) {
514
+ payload = body;
515
+ headers["Content-Type"] = "application/octet-stream";
516
+ } else {
517
+ payload = JSON.stringify(body);
518
+ headers["Content-Type"] = "application/json";
519
+ }
520
+ }
521
+ const res = await fetch(`${base}/api/v2${path}`, { method, headers, body: payload });
522
+ const text = await res.text();
523
+ if (!res.ok) throw new EasybitsError2(res.status, text);
524
+ try {
525
+ return JSON.parse(text);
526
+ } catch {
527
+ return text;
528
+ }
529
+ }
530
+ var encodePath = (p) => p.replace(/^\/+/, "").split("/").map(encodeURIComponent).join("/");
531
+
503
532
  // src/commands/account.ts
504
533
  var login = {
505
534
  name: "login",
@@ -592,8 +621,8 @@ Webhooks: ${u.counts.webhooks}`
592
621
  var websites = {
593
622
  name: "websites",
594
623
  group: "Storage",
595
- summary: "List your static websites",
596
- synopsis: "websites",
624
+ summary: "Static websites: list, create, change slug",
625
+ synopsis: "websites <ls|create|slug>",
597
626
  subs: {
598
627
  ls: {
599
628
  aliases: ["list"],
@@ -613,10 +642,55 @@ var websites = {
613
642
  )
614
643
  );
615
644
  }
645
+ },
646
+ create: {
647
+ summary: "Create an empty static website (optionally with your own slug)",
648
+ usage: "easybits websites create <name> [--slug <slug>]",
649
+ options: { slug: { type: "string", value: "slug", description: "Public slug: /s/<slug>/ (a-z, 0-9, hyphens, 3-60 chars)" } },
650
+ examples: ['easybits websites create "Mi tienda" --slug mi-tienda'],
651
+ async run(ctx) {
652
+ const name = need(ctx, 0, "name", this.usage);
653
+ const slug = str(ctx, "slug");
654
+ const { website } = await api(ctx, "POST", "/websites", { name, ...slug ? { slug } : {} });
655
+ emit(ctx, website, () => console.log(t(`Created ${website.slug} \u2192 ${website.url}`, `Creado ${website.slug} \u2192 ${website.url}`)));
656
+ }
657
+ },
658
+ slug: {
659
+ summary: "Change a website's slug (the old URL redirects 301 to the new one)",
660
+ usage: "easybits websites slug <websiteId|slug> <new-slug>",
661
+ examples: ["easybits websites slug rio-durmiente mi-tienda"],
662
+ async run(ctx) {
663
+ const ref = need(ctx, 0, "website", this.usage);
664
+ const slug = need(ctx, 1, "new-slug", this.usage);
665
+ const websiteId = await resolveWebsiteId(ctx, ref);
666
+ const { website } = await api(ctx, "PATCH", `/websites/${websiteId}`, { slug });
667
+ emit(ctx, website, () => console.log(t(`Slug changed \u2192 ${website.url}`, `Slug cambiado \u2192 ${website.url}`)));
668
+ }
616
669
  }
617
670
  },
618
671
  defaultSub: "ls"
619
672
  };
673
+ function matchWebsite(ref, items) {
674
+ if (/^[0-9a-f]{24}$/i.test(ref)) return ref;
675
+ const r = ref.trim().toLowerCase();
676
+ return items.find((w) => w.slug === r)?.id ?? null;
677
+ }
678
+ async function resolveWebsiteId(ctx, ref) {
679
+ if (/^[0-9a-f]{24}$/i.test(ref)) return ref;
680
+ for (let offset = 0; ; offset += 100) {
681
+ const page = await api(ctx, "GET", `/websites?limit=100&offset=${offset}`);
682
+ const id = matchWebsite(ref, page.items);
683
+ if (id) return id;
684
+ if (page.items.length < 100 || offset + 100 >= page.total) break;
685
+ }
686
+ throw new CliError(
687
+ t(`No website with id or slug "${ref}".`, `No hay sitio con id o slug "${ref}".`),
688
+ EXIT.API,
689
+ `${t("List them with", "Enl\xEDstalos con")}: easybits websites ls`,
690
+ "not_found",
691
+ 404
692
+ );
693
+ }
620
694
  var providers = {
621
695
  name: "providers",
622
696
  group: "Storage",
@@ -660,7 +734,7 @@ var mcp = {
660
734
  return;
661
735
  }
662
736
  const apiKey = resolveApiKey(ctx);
663
- const baseUrl = await resolveBaseUrl3();
737
+ const baseUrl = await resolveBaseUrl4();
664
738
  const config = {
665
739
  mcpServers: {
666
740
  easybits: {
@@ -688,7 +762,7 @@ var whoami = {
688
762
  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;
689
763
  if (!source) throw notLoggedIn();
690
764
  const email = await fetchEmail(await resolveCredential(ctx));
691
- emit(ctx, { email, source, baseUrl: await resolveBaseUrl3() }, () => console.log(`${email ?? t("(unknown email)", "(correo desconocido)")} ${t("via", "por")} ${source}`));
765
+ emit(ctx, { email, source, baseUrl: await resolveBaseUrl4() }, () => console.log(`${email ?? t("(unknown email)", "(correo desconocido)")} ${t("via", "por")} ${source}`));
692
766
  }
693
767
  }
694
768
  };
@@ -792,22 +866,30 @@ var files = {
792
866
  },
793
867
  upload: {
794
868
  summary: "Upload a local file",
795
- usage: "easybits files upload <path>",
796
- examples: ["easybits files upload ./report.pdf", "easybits files upload ./logo.png --json"],
869
+ usage: "easybits files upload <path> [--public]",
870
+ options: {
871
+ public: { type: "boolean", description: "Public file with an open CDN URL (default: private)" }
872
+ },
873
+ examples: ["easybits files upload ./report.pdf", "easybits files upload ./logo.png --public --json"],
797
874
  async run(ctx) {
798
875
  const path = need(ctx, 0, "path", this.usage);
799
876
  if (!existsSync2(path)) throw usageError(t(`File not found: ${path}`, `No existe el archivo: ${path}`), this.usage);
800
877
  const size = statSync(path).size;
801
878
  const contentType = MIME[extname(path).slice(1).toLowerCase()] || "application/octet-stream";
802
879
  const eb = await getClient(ctx);
803
- const data = await eb.uploadFile({ fileName: basename(path), contentType, size });
880
+ const access = bool(ctx, "public") ? "public" : void 0;
881
+ const data = await eb.uploadFile({ fileName: basename(path), contentType, size, access });
804
882
  const res = await fetch(data.putUrl, {
805
883
  method: "PUT",
806
884
  body: readFileSync3(path),
807
885
  headers: { "Content-Type": contentType }
808
886
  });
809
887
  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);
810
- emit(ctx, data.file, () => console.log(`${t("Uploaded", "Subido")}: ${data.file.id}`));
888
+ const url = data.file.url;
889
+ emit(ctx, data.file, () => {
890
+ console.log(`${t("Uploaded", "Subido")}: ${data.file.id}`);
891
+ if (url) console.log(`URL: ${url}`);
892
+ });
811
893
  }
812
894
  },
813
895
  rm: {
@@ -1901,33 +1983,6 @@ function parseLinkStats(columns, row) {
1901
1983
  return columns.map((column, i) => ({ column, permanent: n(i * 4), expiring: n(i * 4 + 1), external: n(i * 4 + 2), empty: n(i * 4 + 3) })).filter((s) => s.permanent + s.expiring + s.external > 0);
1902
1984
  }
1903
1985
 
1904
- // src/api.ts
1905
- import { EasybitsError as EasybitsError2, resolveBaseUrl as resolveBaseUrl4 } from "@easybits.cloud/sdk";
1906
- async function api(ctx, method, path, body, opts = {}) {
1907
- const token = await resolveCredential(ctx);
1908
- const base = (await resolveBaseUrl4()).replace(/\/$/, "");
1909
- const headers = { Authorization: `Bearer ${token}` };
1910
- let payload;
1911
- if (body !== void 0) {
1912
- if (opts.raw) {
1913
- payload = body;
1914
- headers["Content-Type"] = "application/octet-stream";
1915
- } else {
1916
- payload = JSON.stringify(body);
1917
- headers["Content-Type"] = "application/json";
1918
- }
1919
- }
1920
- const res = await fetch(`${base}/api/v2${path}`, { method, headers, body: payload });
1921
- const text = await res.text();
1922
- if (!res.ok) throw new EasybitsError2(res.status, text);
1923
- try {
1924
- return JSON.parse(text);
1925
- } catch {
1926
- return text;
1927
- }
1928
- }
1929
- var encodePath = (p) => p.replace(/^\/+/, "").split("/").map(encodeURIComponent).join("/");
1930
-
1931
1986
  // src/commands/agents-config.ts
1932
1987
  import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync8, readdirSync as readdirSync2, statSync as statSync4, writeFileSync as writeFileSync4 } from "fs";
1933
1988
  import { basename as basename3, dirname, join as join4, relative, resolve } from "path";
@@ -3668,7 +3723,7 @@ async function updateNotice(current2) {
3668
3723
  }
3669
3724
 
3670
3725
  // src/commands/doctor.ts
3671
- var VERSION = true ? "0.15.1" : "dev";
3726
+ var VERSION = true ? "0.15.3" : "dev";
3672
3727
  var doctor2 = {
3673
3728
  name: "doctor",
3674
3729
  group: "Account",
@@ -3953,8 +4008,13 @@ var HELP_ES = {
3953
4008
  "Your files in EasyBits storage": "Tus archivos en el almacenamiento de EasyBits",
3954
4009
  "List your files": "Enlista tus archivos",
3955
4010
  "Upload a local file": "Sube un archivo local",
4011
+ "Public file with an open CDN URL (default: private)": "Archivo p\xFAblico con URL abierta en el CDN (por defecto: privado)",
3956
4012
  "Delete a file (soft delete, 7-day trash)": "Borra un archivo (a la papelera 7 d\xEDas)",
3957
4013
  "List your static websites": "Enlista tus sitios est\xE1ticos",
4014
+ "Static websites: list, create, change slug": "Sitios est\xE1ticos: enlistar, crear, cambiar slug",
4015
+ "Create an empty static website (optionally with your own slug)": "Crea un sitio est\xE1tico vac\xEDo (con tu propio slug si quieres)",
4016
+ "Public slug: /s/<slug>/ (a-z, 0-9, hyphens, 3-60 chars)": "Slug p\xFAblico: /s/<slug>/ (a-z, 0-9, guiones, 3-60 caracteres)",
4017
+ "Change a website's slug (the old URL redirects 301 to the new one)": "Cambia el slug de un sitio (la URL vieja redirige 301 a la nueva)",
3958
4018
  "Show storage providers": "Muestra los proveedores de almacenamiento",
3959
4019
  "MCP": "MCP",
3960
4020
  "Print the MCP config JSON for your agent or editor": "Imprime el JSON de configuraci\xF3n MCP para tu agente o editor",
@@ -4172,7 +4232,7 @@ function normalizeArgs(input) {
4172
4232
  }
4173
4233
 
4174
4234
  // src/index.ts
4175
- var VERSION2 = true ? "0.15.1" : "dev";
4235
+ var VERSION2 = true ? "0.15.3" : "dev";
4176
4236
  var GLOBAL_OPTIONS = {
4177
4237
  json: { type: "boolean" },
4178
4238
  token: { type: "string" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@easybits.cloud/cli",
3
- "version": "0.15.1",
3
+ "version": "0.15.3",
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": {