@easybits.cloud/cli 0.21.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.
- package/README.md +3 -0
- package/dist/index.js +102 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -77,6 +77,7 @@ easybits machines secrets ls sb_abc123
|
|
|
77
77
|
easybits machines secrets set sb_abc123 --dotenv .env.production # - reads stdin
|
|
78
78
|
easybits machines secrets unset sb_abc123 DATABASE_URL
|
|
79
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
|
|
80
81
|
easybits machines get shop # tier, size, MXN/month, whether it sleeps
|
|
81
82
|
easybits machines tiers # the catalog: sizes and prices in MXN
|
|
82
83
|
easybits machines runspec get shop
|
|
@@ -215,6 +216,8 @@ easybits sites ls # your site-builder sites (/dash/sitios): k
|
|
|
215
216
|
easybits sites create --prompt "Landing for my dental clinic" # web app (TanStack) in its own machine
|
|
216
217
|
easybits sites create --prompt "Task tracker with login" --stack next --json
|
|
217
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
|
|
218
221
|
easybits sites theme get my-clinic # site by name or id
|
|
219
222
|
easybits sites theme set my-clinic --theme dracula # or --colors theme.json (every token)
|
|
220
223
|
easybits websites ls # every static website (also documents and agent-published pages)
|
package/dist/index.js
CHANGED
|
@@ -2289,6 +2289,7 @@ var REF_ARGS = {
|
|
|
2289
2289
|
"db query": { index: 0, kind: "db" },
|
|
2290
2290
|
"db photos": { index: 1, kind: "db" },
|
|
2291
2291
|
"sites theme": { index: 1, kind: "site" },
|
|
2292
|
+
"sites model": { index: 0, kind: "site" },
|
|
2292
2293
|
"fleet get": { index: 0, kind: "fleet" },
|
|
2293
2294
|
"fleet capabilities": { index: 0, kind: "fleet" },
|
|
2294
2295
|
"fleet configure": { index: 0, kind: "fleet" },
|
|
@@ -2625,6 +2626,39 @@ var machines = {
|
|
|
2625
2626
|
emit(ctx, m, () => printMachine(m));
|
|
2626
2627
|
}
|
|
2627
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
|
+
},
|
|
2628
2662
|
restart: {
|
|
2629
2663
|
summary: "Restart the app's process with the current secrets and runspec (seconds, no build)",
|
|
2630
2664
|
usage: "easybits machines restart <machine>",
|
|
@@ -2796,7 +2830,10 @@ var machines = {
|
|
|
2796
2830
|
secrets: {
|
|
2797
2831
|
summary: "App env vars stored encrypted in your vault",
|
|
2798
2832
|
usage: SECRETS_USAGE,
|
|
2799
|
-
options: {
|
|
2833
|
+
options: {
|
|
2834
|
+
...ENV_FILE_OPTION,
|
|
2835
|
+
"no-restart": { type: "boolean", description: "Store them without restarting the app (apply later with machines restart)" }
|
|
2836
|
+
},
|
|
2800
2837
|
examples: [
|
|
2801
2838
|
"easybits machines secrets ls sb_abc123",
|
|
2802
2839
|
"easybits machines secrets set sb_abc123 --dotenv .env.production",
|
|
@@ -2817,7 +2854,7 @@ var machines = {
|
|
|
2817
2854
|
const file = str(ctx, "dotenv");
|
|
2818
2855
|
const values = { ...file ? await readEnvFile(file, SECRETS_USAGE) : {}, ...pairs(ctx.args.slice(2), SECRETS_USAGE) };
|
|
2819
2856
|
if (!Object.keys(values).length) throw usageError(t("Give --dotenv <path> (or - for stdin).", "Pasa --dotenv <ruta> (o - para stdin)."), SECRETS_USAGE);
|
|
2820
|
-
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);
|
|
2821
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(", ")}`)));
|
|
2822
2859
|
} else if (action === "unset") {
|
|
2823
2860
|
const name = need(ctx, 2, "NAME", SECRETS_USAGE);
|
|
@@ -2864,9 +2901,10 @@ function createBody(o, usage2) {
|
|
|
2864
2901
|
}
|
|
2865
2902
|
if (!o.prompt && !o.name) throw usageError(t("Give --prompt (what to build) or --name.", "Pasa --prompt (qu\xE9 construir) o --name."), usage2);
|
|
2866
2903
|
const body = { kind };
|
|
2867
|
-
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];
|
|
2868
2905
|
return body;
|
|
2869
2906
|
}
|
|
2907
|
+
var MODEL_USAGE = "easybits sites model <site> [<model>] [--engine <id>] [--key <value>]";
|
|
2870
2908
|
var THEME_USAGE = "easybits sites theme <get|set> <site> [--theme <id> | --colors <file.json|->]";
|
|
2871
2909
|
async function readColors(path) {
|
|
2872
2910
|
let text;
|
|
@@ -2889,7 +2927,7 @@ var sites = {
|
|
|
2889
2927
|
aliases: ["site"],
|
|
2890
2928
|
group: "Hosting",
|
|
2891
2929
|
summary: "Your site-builder sites (/dash/sitios): static and web apps",
|
|
2892
|
-
synopsis: "sites <ls|create|theme>",
|
|
2930
|
+
synopsis: "sites <ls|create|model|theme>",
|
|
2893
2931
|
subs: {
|
|
2894
2932
|
ls: {
|
|
2895
2933
|
aliases: ["list"],
|
|
@@ -2912,13 +2950,15 @@ var sites = {
|
|
|
2912
2950
|
create: {
|
|
2913
2951
|
aliases: ["new"],
|
|
2914
2952
|
summary: "Create a site from a prompt; its builder agent picks it up in the editor",
|
|
2915
|
-
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>]",
|
|
2916
2954
|
options: {
|
|
2917
2955
|
prompt: { type: "string", value: "text", description: "What to build; also names the site if --name is missing" },
|
|
2918
2956
|
name: { type: "string", value: "name", description: "Site name" },
|
|
2919
2957
|
kind: { type: "string", value: "kind", description: "webapp (default: its own machine, published later) or static (CDN website)" },
|
|
2920
2958
|
stack: { type: "string", value: "stack", description: "Web app stack: tanstack (default), next, react-router, remix, nuxt, sveltekit, astro, elysia" },
|
|
2921
|
-
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" }
|
|
2922
2962
|
},
|
|
2923
2963
|
examples: [
|
|
2924
2964
|
'easybits sites create --prompt "Landing for my dental clinic in Puebla"',
|
|
@@ -2927,7 +2967,7 @@ var sites = {
|
|
|
2927
2967
|
],
|
|
2928
2968
|
async run(ctx) {
|
|
2929
2969
|
const body = createBody(
|
|
2930
|
-
{ 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") },
|
|
2931
2971
|
this.usage
|
|
2932
2972
|
);
|
|
2933
2973
|
const site = await api(ctx, "POST", "/sites", body);
|
|
@@ -2942,6 +2982,46 @@ var sites = {
|
|
|
2942
2982
|
});
|
|
2943
2983
|
}
|
|
2944
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
|
+
},
|
|
2945
3025
|
theme: {
|
|
2946
3026
|
summary: "Read or change a site's color theme without a model turn (the \xABTema\xBB button)",
|
|
2947
3027
|
usage: THEME_USAGE,
|
|
@@ -5031,7 +5111,7 @@ async function updateNotice(current2) {
|
|
|
5031
5111
|
}
|
|
5032
5112
|
|
|
5033
5113
|
// src/commands/doctor.ts
|
|
5034
|
-
var VERSION = true ? "0.
|
|
5114
|
+
var VERSION = true ? "0.22.0" : "dev";
|
|
5035
5115
|
var doctor2 = {
|
|
5036
5116
|
name: "doctor",
|
|
5037
5117
|
group: "Account",
|
|
@@ -7371,12 +7451,25 @@ var HELP_ES = {
|
|
|
7371
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)",
|
|
7372
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",
|
|
7373
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",
|
|
7374
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)",
|
|
7375
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",
|
|
7376
7462
|
"JSON with every token as #rrggbb (- reads stdin)": "JSON con cada token en #rrggbb (- lee stdin)",
|
|
7377
7463
|
"edit, then:": "ed\xEDtalo y luego:",
|
|
7378
7464
|
// machines get/restart/redeploy/backups/backup/runspec/rm/tiers
|
|
7379
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)",
|
|
7380
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)",
|
|
7381
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)",
|
|
7382
7475
|
"Release to build from (default: the machine's newest)": "Release de d\xF3nde construir (default: el m\xE1s nuevo de la m\xE1quina)",
|
|
@@ -7777,7 +7870,7 @@ function normalizeArgs(input) {
|
|
|
7777
7870
|
}
|
|
7778
7871
|
|
|
7779
7872
|
// src/index.ts
|
|
7780
|
-
var VERSION2 = true ? "0.
|
|
7873
|
+
var VERSION2 = true ? "0.22.0" : "dev";
|
|
7781
7874
|
var GLOBAL_OPTIONS = {
|
|
7782
7875
|
json: { type: "boolean" },
|
|
7783
7876
|
token: { type: "string" },
|
package/package.json
CHANGED