@easybits.cloud/cli 0.20.0 → 0.22.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 +6 -0
  2. package/dist/index.js +199 -9
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -54,6 +54,9 @@ easybits sandboxes bg kill sb_abc123 exec_123 # the whole process group
54
54
  easybits sandboxes git clone sb_abc123 --repo https://github.com/you/app.git --dir /data/work/app --git-token '$secret:GITHUB_TOKEN'
55
55
  easybits sandboxes git status sb_abc123 --dir /data/work/app
56
56
  easybits sandboxes git commit sb_abc123 --dir /data/work/app --all -m "fix: checkout"
57
+ easybits sandboxes codex login sb_abc123 # Codex on YOUR ChatGPT plan: shows a link + code, you sign in, saved to your vault
58
+ easybits sandboxes codex connect sb_def456 # puts that session in another box (installs Codex if missing)
59
+ easybits sandboxes codex save sb_abc123 # saves the renewed session back to the vault
57
60
  easybits sandboxes git push sb_abc123 --dir /data/work/app --git-token '$secret:GITHUB_TOKEN'
58
61
  easybits sandboxes git pull|checkout|log sb_abc123 --dir /data/work/app …
59
62
  easybits sandboxes suspend sb_abc123
@@ -74,6 +77,7 @@ easybits machines secrets ls sb_abc123
74
77
  easybits machines secrets set sb_abc123 --dotenv .env.production # - reads stdin
75
78
  easybits machines secrets unset sb_abc123 DATABASE_URL
76
79
  easybits machines restart shop # restart the app with the current secrets/runspec (no build)
80
+ easybits machines adopt sb_abc123 --name shop-prod --unit app --import-env /app/secrets.env --dry-run # register a box that already runs; its secrets move to the vault
77
81
  easybits machines get shop # tier, size, MXN/month, whether it sleeps
78
82
  easybits machines tiers # the catalog: sizes and prices in MXN
79
83
  easybits machines runspec get shop
@@ -212,6 +216,8 @@ easybits sites ls # your site-builder sites (/dash/sitios): k
212
216
  easybits sites create --prompt "Landing for my dental clinic" # web app (TanStack) in its own machine
213
217
  easybits sites create --prompt "Task tracker with login" --stack next --json
214
218
  easybits sites create --name "Promo" --kind static # CDN website instead of a machine
219
+ easybits sites model my-clinic # engine + model that build it (● = current)
220
+ easybits sites model my-clinic claude-sonnet-5-5 # the model applies to all your sites with that engine
215
221
  easybits sites theme get my-clinic # site by name or id
216
222
  easybits sites theme set my-clinic --theme dracula # or --colors theme.json (every token)
217
223
  easybits websites ls # every static website (also documents and agent-published pages)
package/dist/index.js CHANGED
@@ -1754,6 +1754,14 @@ function printGit(op, r) {
1754
1754
  async function sandboxAt(ctx, i, usage2) {
1755
1755
  return (await getClient(ctx)).sandboxes.get(need(ctx, i, "sandbox", usage2));
1756
1756
  }
1757
+ var CODEX_USAGE = "easybits sandboxes codex <login|connect|save> <sandbox> [--force] [--no-install]";
1758
+ function parseDeviceAuth(out) {
1759
+ const plain = out.replace(/\x1b\[[0-9;]*m/g, "");
1760
+ return {
1761
+ url: plain.match(/https:\/\/auth\.openai\.com\/\S+/)?.[0],
1762
+ code: plain.match(/\b[A-Z0-9]{4}-[A-Z0-9]{4,6}\b/)?.[0]
1763
+ };
1764
+ }
1757
1765
  var FILES_USAGE = "easybits sandboxes files <ls|read|write|rm|mv|mkdir> <sandbox> <path> [local-file|new-path]";
1758
1766
  var sandboxes = {
1759
1767
  name: "sandboxes",
@@ -2066,6 +2074,89 @@ var sandboxes = {
2066
2074
  }
2067
2075
  }
2068
2076
  },
2077
+ codex: {
2078
+ summary: "Codex on YOUR ChatGPT plan in a box: login once, then connect any box from your vault",
2079
+ usage: CODEX_USAGE,
2080
+ options: {
2081
+ force: { type: "boolean", description: "connect: replace the session the box already has" },
2082
+ "no-install": { type: "boolean", description: "connect/login: don't install Codex if it's missing" }
2083
+ },
2084
+ examples: [
2085
+ "easybits sb codex login sb_abc123 # shows a link + code; you sign in; the session goes to your vault",
2086
+ "easybits sb codex connect sb_def456 # puts that session in another box (installs Codex if missing)",
2087
+ "easybits sb codex save sb_abc123 # saves the box's (renewed) session back to the vault"
2088
+ ],
2089
+ async run(ctx) {
2090
+ const action = need(ctx, 0, "login|connect|save", CODEX_USAGE);
2091
+ const id = need(ctx, 1, "sandbox", CODEX_USAGE);
2092
+ const path = `/sandboxes/${encodeURIComponent(id)}`;
2093
+ if (action === "connect") {
2094
+ const r2 = await api(ctx, "POST", `${path}/codex-connect`, {
2095
+ force: bool(ctx, "force"),
2096
+ install: !bool(ctx, "no-install")
2097
+ });
2098
+ return emit(ctx, r2, () => {
2099
+ console.log(
2100
+ r2.session === "kept" ? t("The box already had a Codex session: kept it (use --force to replace it).", "La caja ya ten\xEDa sesi\xF3n de Codex: se conserv\xF3 (usa --force para reemplazarla).") : t("Codex session from your vault is in the box.", "La sesi\xF3n de Codex de tu vault ya est\xE1 en la caja.")
2101
+ );
2102
+ if (r2.codex === "no_npm") console.log(t("Codex isn't installed and the box has no npm: use a node template.", "Codex no est\xE1 instalado y la caja no tiene npm: usa un template node."));
2103
+ console.log(t(`Try it: easybits sb exec ${id} -- codex exec "hola"`, `Pru\xE9balo: easybits sb exec ${id} -- codex exec "hola"`));
2104
+ });
2105
+ }
2106
+ if (action === "save") {
2107
+ const r2 = await api(ctx, "POST", `${path}/codex-save`, {});
2108
+ return emit(
2109
+ ctx,
2110
+ r2,
2111
+ () => console.log(r2.saved ? t("Saved to your vault as CODEX_AUTH.", "Guardada en tu vault como CODEX_AUTH.") : t(`Not saved: ${r2.reason}`, `No se guard\xF3: ${r2.reason}`))
2112
+ );
2113
+ }
2114
+ if (action !== "login") throw usageError(t(`Unknown codex action "${action}".`, `Acci\xF3n de codex desconocida "${action}".`), CODEX_USAGE);
2115
+ const sb = await sandboxAt(ctx, 1, CODEX_USAGE);
2116
+ if (!bool(ctx, "no-install")) {
2117
+ const i = await sb.exec("command -v codex >/dev/null 2>&1 || npm i -g @openai/codex >/dev/null 2>&1; command -v codex", { timeoutSeconds: 240 });
2118
+ if (!i.stdout.trim()) throw new CliError(t("Couldn't install Codex in the box (needs npm: use a node template).", "No se pudo instalar Codex en la caja (necesita npm: usa un template node)."));
2119
+ }
2120
+ const bg = await sb.execBackground("codex login --device-auth");
2121
+ const started = Date.now();
2122
+ let shown = false;
2123
+ for (; ; ) {
2124
+ await new Promise((r2) => setTimeout(r2, 2e3));
2125
+ const st = await sb.bgStatus(bg.execId);
2126
+ const { url, code } = parseDeviceAuth(`${st.stdout ?? ""}
2127
+ ${st.stderr ?? ""}`);
2128
+ if (!shown && url && code) {
2129
+ shown = true;
2130
+ if (ctx.json) console.log(JSON.stringify({ url, code, expiresInMinutes: 15 }));
2131
+ else {
2132
+ console.log(t("1. Open: ", "1. Abre: ") + url);
2133
+ console.log(t("2. Code: ", "2. C\xF3digo: ") + code + t(" (15 min)", " (15 min)"));
2134
+ console.log(t("Waiting for you to sign in with your ChatGPT account\u2026", "Esperando a que inicies sesi\xF3n con tu cuenta de ChatGPT\u2026"));
2135
+ }
2136
+ }
2137
+ if (st.status !== "running") {
2138
+ if (st.exitCode !== 0) {
2139
+ const tail = `${st.stderr ?? ""}${st.stdout ?? ""}`.replace(/\x1b\[[0-9;]*m/g, "").trim().split("\n").slice(-3).join(" ");
2140
+ throw new CliError(t(`Codex login failed: ${tail}`, `El login de Codex fall\xF3: ${tail}`));
2141
+ }
2142
+ break;
2143
+ }
2144
+ if (Date.now() - started > 16 * 6e4) {
2145
+ await sb.bgKill(bg.execId).catch(() => void 0);
2146
+ throw new CliError(t("The code expired (15 min). Run it again.", "El c\xF3digo venci\xF3 (15 min). Vuelve a correrlo."));
2147
+ }
2148
+ }
2149
+ const r = await api(ctx, "POST", `${path}/codex-save`, {});
2150
+ return emit(
2151
+ ctx,
2152
+ { signedIn: true, ...r },
2153
+ () => console.log(t(
2154
+ `Signed in. Session saved to your vault (CODEX_AUTH): connect any other box with easybits sb codex connect <sandbox>`,
2155
+ `Listo. Sesi\xF3n guardada en tu vault (CODEX_AUTH): conecta cualquier otra caja con easybits sb codex connect <sandbox>`
2156
+ ))
2157
+ );
2158
+ }
2159
+ },
2069
2160
  git: {
2070
2161
  summary: "Git inside a sandbox: clone, status, commit, push, pull, checkout, log",
2071
2162
  usage: GIT_USAGE,
@@ -2198,6 +2289,7 @@ var REF_ARGS = {
2198
2289
  "db query": { index: 0, kind: "db" },
2199
2290
  "db photos": { index: 1, kind: "db" },
2200
2291
  "sites theme": { index: 1, kind: "site" },
2292
+ "sites model": { index: 0, kind: "site" },
2201
2293
  "fleet get": { index: 0, kind: "fleet" },
2202
2294
  "fleet capabilities": { index: 0, kind: "fleet" },
2203
2295
  "fleet configure": { index: 0, kind: "fleet" },
@@ -2534,6 +2626,39 @@ var machines = {
2534
2626
  emit(ctx, m, () => printMachine(m));
2535
2627
  }
2536
2628
  },
2629
+ adopt: {
2630
+ summary: "Register a box that already runs (e.g. migrated by hand) so its secrets live in the vault",
2631
+ usage: "easybits machines adopt <sandbox-id> --name <name> [--unit <unit>] [--import-env <path-in-box>] [--tier <tier>] [--dry-run]",
2632
+ options: {
2633
+ name: { type: "string", value: "name", description: "Label for the machine" },
2634
+ unit: { type: "string", value: "unit", description: "systemd unit that `machines restart` restarts" },
2635
+ "import-env": { type: "string", value: "path", description: "dotenv file INSIDE the box to copy into the vault (values never leave the server)" },
2636
+ tier: { type: "string", value: "tier", description: "Tier to show for it (default: the host's, or mini)" },
2637
+ "dry-run": { type: "boolean", description: "Show what would happen without writing anything" }
2638
+ },
2639
+ examples: [
2640
+ "easybits machines adopt sb_abc123 --name shop-prod --unit app --import-env /app/secrets.env --dry-run",
2641
+ "easybits machines adopt sb_abc123 --name shop-prod --unit app --import-env /app/secrets.env"
2642
+ ],
2643
+ async run(ctx) {
2644
+ const id = need(ctx, 0, "sandbox-id", this.usage);
2645
+ const name = str(ctx, "name");
2646
+ if (!name) throw usageError(t("Give --name <name>.", "Pasa --name <nombre>."), this.usage);
2647
+ const dryRun = bool(ctx, "dry-run");
2648
+ const r = await api(
2649
+ ctx,
2650
+ "POST",
2651
+ "/machines/adopt",
2652
+ { sandboxId: id, name, unit: str(ctx, "unit"), importEnvFrom: str(ctx, "import-env"), tier: str(ctx, "tier"), dryRun }
2653
+ );
2654
+ emit(ctx, r, () => {
2655
+ console.log(
2656
+ r.dryRun ? t(`Would adopt ${r.sandboxId} as ${r.name} (${r.tier}). Nothing was written.`, `Adoptar\xEDa ${r.sandboxId} como ${r.name} (${r.tier}). No se escribi\xF3 nada.`) : t(`Adopted ${r.sandboxId} as ${r.name}. EasyBits keeps its secrets and restarts it; it never redeploys or releases it.`, `Adoptada ${r.sandboxId} como ${r.name}. EasyBits guarda sus secretos y la reinicia; nunca la redespliega ni la libera.`)
2657
+ );
2658
+ if (r.imported.length) console.log(`${t("Secrets", "Secretos")}: ${r.imported.join(", ")}`);
2659
+ });
2660
+ }
2661
+ },
2537
2662
  restart: {
2538
2663
  summary: "Restart the app's process with the current secrets and runspec (seconds, no build)",
2539
2664
  usage: "easybits machines restart <machine>",
@@ -2705,7 +2830,10 @@ var machines = {
2705
2830
  secrets: {
2706
2831
  summary: "App env vars stored encrypted in your vault",
2707
2832
  usage: SECRETS_USAGE,
2708
- options: { ...ENV_FILE_OPTION },
2833
+ options: {
2834
+ ...ENV_FILE_OPTION,
2835
+ "no-restart": { type: "boolean", description: "Store them without restarting the app (apply later with machines restart)" }
2836
+ },
2709
2837
  examples: [
2710
2838
  "easybits machines secrets ls sb_abc123",
2711
2839
  "easybits machines secrets set sb_abc123 --dotenv .env.production",
@@ -2726,7 +2854,7 @@ var machines = {
2726
2854
  const file = str(ctx, "dotenv");
2727
2855
  const values = { ...file ? await readEnvFile(file, SECRETS_USAGE) : {}, ...pairs(ctx.args.slice(2), SECRETS_USAGE) };
2728
2856
  if (!Object.keys(values).length) throw usageError(t("Give --dotenv <path> (or - for stdin).", "Pasa --dotenv <ruta> (o - para stdin)."), SECRETS_USAGE);
2729
- const r = await eb.machines.setSecrets(id, values);
2857
+ const r = bool(ctx, "no-restart") ? await api(ctx, "PUT", `/machines/${encodeURIComponent(id)}/secrets?restart=false`, values) : await eb.machines.setSecrets(id, values);
2730
2858
  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(", ")}`)));
2731
2859
  } else if (action === "unset") {
2732
2860
  const name = need(ctx, 2, "NAME", SECRETS_USAGE);
@@ -2773,9 +2901,10 @@ function createBody(o, usage2) {
2773
2901
  }
2774
2902
  if (!o.prompt && !o.name) throw usageError(t("Give --prompt (what to build) or --name.", "Pasa --prompt (qu\xE9 construir) o --name."), usage2);
2775
2903
  const body = { kind };
2776
- for (const k of ["prompt", "name", "stack", "template"]) if (o[k]) body[k] = o[k];
2904
+ for (const k of ["prompt", "name", "stack", "template", "engine", "model"]) if (o[k]) body[k] = o[k];
2777
2905
  return body;
2778
2906
  }
2907
+ var MODEL_USAGE = "easybits sites model <site> [<model>] [--engine <id>] [--key <value>]";
2779
2908
  var THEME_USAGE = "easybits sites theme <get|set> <site> [--theme <id> | --colors <file.json|->]";
2780
2909
  async function readColors(path) {
2781
2910
  let text;
@@ -2798,7 +2927,7 @@ var sites = {
2798
2927
  aliases: ["site"],
2799
2928
  group: "Hosting",
2800
2929
  summary: "Your site-builder sites (/dash/sitios): static and web apps",
2801
- synopsis: "sites <ls|create|theme>",
2930
+ synopsis: "sites <ls|create|model|theme>",
2802
2931
  subs: {
2803
2932
  ls: {
2804
2933
  aliases: ["list"],
@@ -2821,13 +2950,15 @@ var sites = {
2821
2950
  create: {
2822
2951
  aliases: ["new"],
2823
2952
  summary: "Create a site from a prompt; its builder agent picks it up in the editor",
2824
- usage: "easybits sites create (--prompt <text> | --name <name>) [--kind webapp|static] [--stack <stack>] [--template <key>]",
2953
+ usage: "easybits sites create (--prompt <text> | --name <name>) [--kind webapp|static] [--stack <stack>] [--template <key>] [--engine <id>] [--model <id>]",
2825
2954
  options: {
2826
2955
  prompt: { type: "string", value: "text", description: "What to build; also names the site if --name is missing" },
2827
2956
  name: { type: "string", value: "name", description: "Site name" },
2828
2957
  kind: { type: "string", value: "kind", description: "webapp (default: its own machine, published later) or static (CDN website)" },
2829
2958
  stack: { type: "string", value: "stack", description: "Web app stack: tanstack (default), next, react-router, remix, nuxt, sveltekit, astro, elysia" },
2830
- template: { type: "string", value: "key", description: "Starting template (tanstack/react-router): consultorio, despacho, portafolio, lanzamiento" }
2959
+ template: { type: "string", value: "key", description: "Starting template (tanstack/react-router): consultorio, despacho, portafolio, lanzamiento" },
2960
+ engine: { type: "string", value: "id", description: "What builds it: claude (your Claude), easybits (included)\u2026 see sites model" },
2961
+ model: { type: "string", value: "id", description: "Model of that engine; applies to all your sites with that engine" }
2831
2962
  },
2832
2963
  examples: [
2833
2964
  'easybits sites create --prompt "Landing for my dental clinic in Puebla"',
@@ -2836,7 +2967,7 @@ var sites = {
2836
2967
  ],
2837
2968
  async run(ctx) {
2838
2969
  const body = createBody(
2839
- { prompt: str(ctx, "prompt"), name: str(ctx, "name"), kind: str(ctx, "kind"), stack: str(ctx, "stack"), template: str(ctx, "template") },
2970
+ { prompt: str(ctx, "prompt"), name: str(ctx, "name"), kind: str(ctx, "kind"), stack: str(ctx, "stack"), template: str(ctx, "template"), engine: str(ctx, "engine"), model: str(ctx, "model") },
2840
2971
  this.usage
2841
2972
  );
2842
2973
  const site = await api(ctx, "POST", "/sites", body);
@@ -2851,6 +2982,46 @@ var sites = {
2851
2982
  });
2852
2983
  }
2853
2984
  },
2985
+ model: {
2986
+ aliases: ["engine"],
2987
+ summary: "See or change what builds a site: engine and model (the model applies to all your sites with that engine)",
2988
+ usage: MODEL_USAGE,
2989
+ options: {
2990
+ engine: { type: "string", value: "id", description: "Switch the site to this engine" },
2991
+ key: { type: "string", value: "value", description: "The engine's key, if your account doesn't have it yet" }
2992
+ },
2993
+ examples: [
2994
+ "easybits sites model my-shop # engines, models and the current one",
2995
+ "easybits sites model my-shop claude-sonnet-5-5",
2996
+ "easybits sites model my-shop --engine easybits"
2997
+ ],
2998
+ async run(ctx) {
2999
+ const id = need(ctx, 0, "site", MODEL_USAGE);
3000
+ const model = ctx.args[1];
3001
+ const engine = str(ctx, "engine");
3002
+ if (!model && !engine) {
3003
+ const [{ items }, { items: mine }] = await Promise.all([
3004
+ api(ctx, "GET", "/sites/engines"),
3005
+ api(ctx, "GET", "/sites")
3006
+ ]);
3007
+ const site = mine.find((s) => s.id === id);
3008
+ emit(ctx, { site: site ? { id: site.id, engine: site.engine } : null, engines: items }, () => {
3009
+ if (site) console.log(t(`${site.name} builds with: ${site.engine}`, `${site.name} se construye con: ${site.engine}`));
3010
+ table(
3011
+ items.flatMap((e) => e.models.map((m) => ({ engine: e.id, model: m.id, label: m.label, now: m.id === e.currentModel ? "\u25CF" : "", key: e.needsSecret ? t("needs key", "pide llave") : "" }))),
3012
+ [["engine", "ENGINE"], ["model", "MODEL"], ["label", "LABEL"], ["now", "NOW"], ["key", "KEY"]]
3013
+ );
3014
+ });
3015
+ return;
3016
+ }
3017
+ const r = await api(ctx, "POST", `/sites/${encodeURIComponent(id)}/engine`, {
3018
+ ...engine ? { engine } : {},
3019
+ ...model ? { model } : {},
3020
+ ...str(ctx, "key") ? { secretValue: str(ctx, "key") } : {}
3021
+ });
3022
+ emit(ctx, r, () => console.log(t(`Now builds with ${r.label} \xB7 ${r.model ?? "default"} (from the next message, on all your ${r.label} sites).`, `Ahora construye con ${r.label} \xB7 ${r.model ?? "default"} (desde el siguiente mensaje, en todos tus sitios con ${r.label}).`)));
3023
+ }
3024
+ },
2854
3025
  theme: {
2855
3026
  summary: "Read or change a site's color theme without a model turn (the \xABTema\xBB button)",
2856
3027
  usage: THEME_USAGE,
@@ -4940,7 +5111,7 @@ async function updateNotice(current2) {
4940
5111
  }
4941
5112
 
4942
5113
  // src/commands/doctor.ts
4943
- var VERSION = true ? "0.20.0" : "dev";
5114
+ var VERSION = true ? "0.22.0" : "dev";
4944
5115
  var doctor2 = {
4945
5116
  name: "doctor",
4946
5117
  group: "Account",
@@ -7280,12 +7451,25 @@ var HELP_ES = {
7280
7451
  "webapp (default: its own machine, published later) or static (CDN website)": "webapp (default: su propia m\xE1quina, se publica despu\xE9s) o static (website en el CDN)",
7281
7452
  "Web app stack: tanstack (default), next, react-router, remix, nuxt, sveltekit, astro, elysia": "Stack de la web app: tanstack (default), next, react-router, remix, nuxt, sveltekit, astro, elysia",
7282
7453
  "Starting template (tanstack/react-router): consultorio, despacho, portafolio, lanzamiento": "Plantilla de partida (tanstack/react-router): consultorio, despacho, portafolio, lanzamiento",
7454
+ "What builds it: claude (your Claude), easybits (included)\u2026 see sites model": "Qu\xE9 lo construye: claude (tu Claude), easybits (incluido)\u2026 ver sites model",
7455
+ "Model of that engine; applies to all your sites with that engine": "Modelo de ese motor; vale para todos tus sitios con ese motor",
7456
+ "See or change what builds a site: engine and model (the model applies to all your sites with that engine)": "Ve o cambia qu\xE9 construye un sitio: motor y modelo (el modelo vale para todos tus sitios con ese motor)",
7457
+ "Switch the site to this engine": "Cambia el sitio a este motor",
7458
+ "The engine's key, if your account doesn't have it yet": "La llave del motor, si tu cuenta todav\xEDa no la tiene",
7459
+ "engines, models and the current one": "motores, modelos y el actual",
7283
7460
  "Read or change a site's color theme without a model turn (the \xABTema\xBB button)": "Lee o cambia el tema de colores de un sitio sin turno del modelo (el bot\xF3n \xABTema\xBB)",
7284
7461
  "Palette id: minimal, calido, oceano, noche, dracula, nord, tokyo-night, github-light\u2026": "Id de la paleta: minimal, calido, oceano, noche, dracula, nord, tokyo-night, github-light\u2026",
7285
7462
  "JSON with every token as #rrggbb (- reads stdin)": "JSON con cada token en #rrggbb (- lee stdin)",
7286
7463
  "edit, then:": "ed\xEDtalo y luego:",
7287
7464
  // machines get/restart/redeploy/backups/backup/runspec/rm/tiers
7288
7465
  "Show a machine: tier, size, price, status and whether it sleeps": "Muestra una m\xE1quina: tier, tama\xF1o, precio, estado y si duerme",
7466
+ "Register a box that already runs (e.g. migrated by hand) so its secrets live in the vault": "Registra una caja que ya corre (p. ej. migrada a mano) para que sus secretos vivan en el vault",
7467
+ "Label for the machine": "Etiqueta de la m\xE1quina",
7468
+ "systemd unit that `machines restart` restarts": "Unit de systemd que reinicia `machines restart`",
7469
+ "dotenv file INSIDE the box to copy into the vault (values never leave the server)": "Archivo dotenv DENTRO de la caja que se copia al vault (los valores no salen del servidor)",
7470
+ "Tier to show for it (default: the host's, or mini)": "Tier con que se muestra (default: el del host, o mini)",
7471
+ "Show what would happen without writing anything": "Muestra qu\xE9 har\xEDa sin escribir nada",
7472
+ "Store them without restarting the app (apply later with machines restart)": "Los guarda sin reiniciar la app (apl\xEDcalos despu\xE9s con machines restart)",
7289
7473
  "Restart the app's process with the current secrets and runspec (seconds, no build)": "Reinicia el proceso de la app con los secretos y el runspec actuales (segundos, sin build)",
7290
7474
  "Build a NEW machine from a release: recover a lost one, or change its tier (resize)": "Construye una m\xE1quina NUEVA desde un release: recupera una perdida o cambia su tier (resize)",
7291
7475
  "Release to build from (default: the machine's newest)": "Release de d\xF3nde construir (default: el m\xE1s nuevo de la m\xE1quina)",
@@ -7377,6 +7561,12 @@ var HELP_ES = {
7377
7561
  "Long-running processes: start one in the background, list, poll its output, kill it": "Procesos largos: arranca uno en segundo plano, enl\xEDstalos, lee su salida, detenlo",
7378
7562
  "kill: seconds between SIGTERM and SIGKILL (default 5)": "kill: segundos entre SIGTERM y SIGKILL (default 5)",
7379
7563
  "status, exit code, stdout/stderr": "estado, c\xF3digo de salida, stdout/stderr",
7564
+ "Codex on YOUR ChatGPT plan in a box: login once, then connect any box from your vault": "Codex con TU plan de ChatGPT en una caja: inicia sesi\xF3n una vez y conecta cualquier caja desde tu vault",
7565
+ "connect: replace the session the box already has": "connect: reemplaza la sesi\xF3n que ya tenga la caja",
7566
+ "shows a link + code; you sign in; the session goes to your vault": "muestra un link y un c\xF3digo; inicias sesi\xF3n; la sesi\xF3n se guarda en tu vault",
7567
+ "puts that session in another box (installs Codex if missing)": "pone esa sesi\xF3n en otra caja (instala Codex si falta)",
7568
+ "saves the box's (renewed) session back to the vault": "devuelve al vault la sesi\xF3n (renovada) de la caja",
7569
+ "connect/login: don't install Codex if it's missing": "connect/login: no instalar Codex si falta",
7380
7570
  "Git inside a sandbox: clone, status, commit, push, pull, checkout, log": "Git dentro de un sandbox: clone, status, commit, push, pull, checkout, log",
7381
7571
  "Repo folder inside the sandbox (required)": "Carpeta del repo dentro del sandbox (obligatoria)",
7382
7572
  "clone: repo URL (clean; the token goes in --git-token)": "clone: URL del repo (limpia; el token va en --git-token)",
@@ -7680,7 +7870,7 @@ function normalizeArgs(input) {
7680
7870
  }
7681
7871
 
7682
7872
  // src/index.ts
7683
- var VERSION2 = true ? "0.20.0" : "dev";
7873
+ var VERSION2 = true ? "0.22.0" : "dev";
7684
7874
  var GLOBAL_OPTIONS = {
7685
7875
  json: { type: "boolean" },
7686
7876
  token: { type: "string" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@easybits.cloud/cli",
3
- "version": "0.20.0",
3
+ "version": "0.22.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": {