create-kontrolia-auth 2.0.1 → 2.2.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 CHANGED
@@ -20,8 +20,12 @@ Eso hace, en orden:
20
20
  contenedores por ti** (`docker compose up -d`) y aplica las migraciones
21
21
  en cuanto Postgres está listo.
22
22
  4. **Primera aplicación** (opcional): registra tu app y su catálogo de permisos.
23
- 5. **Despliegue**: genera los `.env.local` de `auth-server` y `admin-panel` y te
24
- da los pasos exactos para Docker, Vercel, Railway, Render, Coolify o manual.
23
+ 5. **Despliegue**: genera los `.env.local` de `auth-server` y `admin-panel`. Si
24
+ eliges Vercel y el repo ya está en GitHub, puede **crear los dos proyectos
25
+ por ti** vía la API de Vercel (carpeta y variables de entorno correctas de
26
+ una vez, sin tocar el dashboard ni la CLI de Vercel) — solo pide un API
27
+ token de vercel.com/account/tokens. Para Docker, Railway, Render o
28
+ Coolify te da los pasos exactos.
25
29
 
26
30
  Si ya estás **dentro del repo** (desarrollo), el instalador lo detecta y se
27
31
  salta la descarga:
@@ -36,6 +40,7 @@ pnpm --filter create-kontrolia-auth dev
36
40
  | ------------------------------------- | ----------------------------------------------------------------------------- |
37
41
  | `npx create-kontrolia-auth [carpeta]` | Instalación completa (el flujo de arriba). |
38
42
  | `npx create-kontrolia-auth update` | Descarga el código nuevo (`git pull`) sobre una copia ya instalada, instala dependencias, y aplica las migraciones nuevas. |
43
+ | `npx create-kontrolia-auth deploy` | Va directo al paso de despliegue (URLs, cliente OAuth, `.env.local`, creación automática en Vercel) sin repetir las preguntas de base de datos ni de aplicación — para cuando ya tienes todo corriendo y solo quieres (re)conectar auth-server o admin-panel a un nuevo destino. |
39
44
  | `npx create-kontrolia-auth migrate` | Aplica/re-aplica las migraciones contra una base existente. |
40
45
  | `npx create-kontrolia-auth doctor` | Solo revisa que tengas Node, pnpm, git y Docker. |
41
46
 
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ import { askDatabaseStep } from "./steps/database.js";
6
6
  import { askDeploymentStep } from "./steps/deployment.js";
7
7
  import { bringUpAndMigrate } from "./utils/docker.js";
8
8
  import { runPreflight } from "./utils/preflight.js";
9
+ import { textOrExit } from "./utils/prompts.js";
9
10
  import { ensureRepo, isInsideRepo } from "./utils/scaffold.js";
10
11
  import { pullLatest } from "./utils/update.js";
11
12
  /** Applies the schema against an already-reachable database, with a spinner. */
@@ -86,6 +87,31 @@ async function runUpdateCommand() {
86
87
  ? "Listo. Si despliegas con Docker: `docker compose -f docker/docker-compose.yml up -d --build`. Si usas Vercel/Railway/Render/Coolify, vuelve a desplegar (push a tu rama, o su CLI) para que tomen el código nuevo."
87
88
  : "El código ya está actualizado, pero revisa el error de las migraciones antes de redesplegar.");
88
89
  }
90
+ /**
91
+ * `create-kontrolia-auth deploy` — jumps straight to the deployment step
92
+ * (URLs, OAuth client, .env.local generation, and the Vercel auto-create
93
+ * offer) without repeating the database/application questions. For anyone
94
+ * who already has everything running and just wants to (re)connect a
95
+ * deploy target — e.g. adding admin-panel after auth-server was already
96
+ * deployed. Doesn't persist Supabase credentials anywhere between runs, so
97
+ * it still has to ask for them once here.
98
+ */
99
+ async function runDeployCommand() {
100
+ console.clear();
101
+ p.intro("KontrolIA Auth — desplegar auth-server / admin-panel");
102
+ if (!isInsideRepo(process.cwd())) {
103
+ p.log.error("Corre esto dentro de la carpeta donde instalaste KontrolIA Auth (la que tiene docker/docker-compose.yml).");
104
+ p.outro("Nada que hacer aquí.");
105
+ process.exitCode = 1;
106
+ return;
107
+ }
108
+ const supabaseUrl = await textOrExit("URL de tu proyecto Supabase", "https://tu-proyecto.supabase.co");
109
+ const anonKey = await textOrExit("Anon/public key");
110
+ const serviceRoleKey = await textOrExit("Service role key (server-only, nunca la expongas al navegador)");
111
+ const db = { mode: "existing", databaseUrl: "", supabaseUrl, anonKey, serviceRoleKey };
112
+ await askDeploymentStep(process.cwd(), db);
113
+ p.outro("Listo.");
114
+ }
89
115
  /** Default flow: preflight → (scaffold if outside repo) → DB → migrate → app → deploy. */
90
116
  async function runInstall(targetDirArg) {
91
117
  console.clear();
@@ -145,6 +171,8 @@ async function main() {
145
171
  return runDoctor();
146
172
  if (first === "update")
147
173
  return runUpdateCommand();
174
+ if (first === "deploy")
175
+ return runDeployCommand();
148
176
  // `create <dir>` / `install <dir>` / `<dir>` / (nothing) all reach install.
149
177
  const dir = first === "create" || first === "install" ? args[1] : first;
150
178
  return runInstall(dir);
@@ -1 +1 @@
1
- {"version":3,"file":"application.d.ts","sourceRoot":"","sources":["../../src/steps/application.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAYpD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAgG1E"}
1
+ {"version":3,"file":"application.d.ts","sourceRoot":"","sources":["../../src/steps/application.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAYpD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAgG1E"}
@@ -1,5 +1,6 @@
1
1
  import * as p from "@clack/prompts";
2
2
  import { registerApplication } from "@kontrolia/db";
3
+ import { textOrExit } from "../utils/prompts.js";
3
4
  function slugify(name) {
4
5
  return name
5
6
  .trim()
@@ -95,11 +96,3 @@ export async function askApplicationStep(db) {
95
96
  }
96
97
  p.note("Para habilitarla en una organización, entra a admin-panel → \"Aplicaciones\" — cualquier owner/admin puede activarla ahí con un clic.", "Siguiente paso");
97
98
  }
98
- async function textOrExit(message, placeholder) {
99
- const value = await p.text({ message, placeholder });
100
- if (p.isCancel(value)) {
101
- p.cancel("Instalación cancelada.");
102
- process.exit(0);
103
- }
104
- return value;
105
- }
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/steps/database.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,UAAU,GAAG,iBAAiB,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAmE/E"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/steps/database.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,UAAU,GAAG,iBAAiB,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAmE/E"}
@@ -2,6 +2,7 @@ import * as p from "@clack/prompts";
2
2
  import { generateJwtSecret } from "../utils/secrets.js";
3
3
  import { generateSupabaseKeys } from "../utils/supabase-keys.js";
4
4
  import { writeEnvFile } from "../utils/files.js";
5
+ import { textOrExit } from "../utils/prompts.js";
5
6
  /**
6
7
  * Question A from the architecture plan: where does Postgres + GoTrue come
7
8
  * from? Two fully independent paths — neither assumes Docker.
@@ -60,11 +61,3 @@ export async function askDatabaseStep(repoRoot) {
60
61
  serviceRoleKey,
61
62
  };
62
63
  }
63
- async function textOrExit(message, placeholder) {
64
- const value = await p.text({ message, placeholder });
65
- if (p.isCancel(value)) {
66
- p.cancel("Instalación cancelada.");
67
- process.exit(0);
68
- }
69
- return value;
70
- }
@@ -1 +1 @@
1
- {"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../../src/steps/deployment.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAoCpD;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAkI3F"}
1
+ {"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../../src/steps/deployment.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AA6GpD;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAwI3F"}
@@ -1,6 +1,7 @@
1
1
  import * as p from "@clack/prompts";
2
2
  import { updateEnvValue, writeEnvFile } from "../utils/files.js";
3
3
  import { registerOAuthClient } from "../utils/oauth-client.js";
4
+ import { createVercelProject, detectGitHubRepo } from "../utils/vercel-api.js";
4
5
  const NEXT_STEPS = {
5
6
  docker: "Ya está: los servicios auth-server y admin-panel viven en el mismo docker/docker-compose.yml. Corre `docker compose -f docker/docker-compose.yml up -d` para tenerlos arriba.",
6
7
  vercel: "Lo más confiable con un monorepo pnpm es conectar el repo desde el dashboard (no desplegar carpetas sueltas por CLI):\n" +
@@ -26,6 +27,63 @@ const NEXT_STEPS = {
26
27
  coolify: "Crea dos recursos 'Next.js App' en Coolify apuntando a apps/auth-server y apps/admin-panel, y pega en cada uno las variables de su .env.local.",
27
28
  manual: "Corre `pnpm build && pnpm start` en apps/auth-server y apps/admin-panel en el servidor/K8s de tu elección, usando los .env.local generados.",
28
29
  };
30
+ /**
31
+ * Offers to create both Vercel projects via the REST API instead of walking
32
+ * the user through the dashboard/CLI by hand — the same three fields that
33
+ * are easy to get wrong doing this manually (gitRepository, rootDirectory,
34
+ * environmentVariables) are just fields in one request body here. Returns
35
+ * true only if both projects were created, so the caller knows whether to
36
+ * still show the manual fallback instructions.
37
+ */
38
+ async function tryAutoCreateVercelProjects(repoRoot, authServerEnv, adminPanelEnv) {
39
+ const repo = await detectGitHubRepo(repoRoot);
40
+ if (!repo) {
41
+ p.note("No encontré un remoto de GitHub en este repo (`git remote get-url origin`) — sin eso no puedo crear los " +
42
+ "proyectos de Vercel por ti. Sube el repo a GitHub y vuelve a correr el instalador, o sigue los pasos " +
43
+ "manuales de abajo.", "Creación automática no disponible");
44
+ return false;
45
+ }
46
+ const auto = await p.confirm({
47
+ message: `¿Quieres que cree los dos proyectos de Vercel automáticamente (conectados a ${repo}, con sus variables ya puestas)? Solo necesito un API token de Vercel.`,
48
+ initialValue: true,
49
+ });
50
+ if (p.isCancel(auto) || !auto)
51
+ return false;
52
+ const token = await p.password({
53
+ message: "API token de Vercel (vercel.com/account/tokens — no se guarda, solo se usa ahora)",
54
+ validate: (value) => (value.trim() ? undefined : "Requerido"),
55
+ });
56
+ if (p.isCancel(token))
57
+ return false;
58
+ const namePrefix = repo.split("/")[1] ?? "kontrolia-auth";
59
+ const authSpinner = p.spinner();
60
+ authSpinner.start("Creando proyecto de Vercel para auth-server");
61
+ const authResult = await createVercelProject({
62
+ token,
63
+ repo,
64
+ name: `${namePrefix}-auth-server`,
65
+ rootDirectory: "apps/auth-server",
66
+ env: authServerEnv,
67
+ });
68
+ authSpinner.stop(authResult.ok ? "Proyecto auth-server creado" : `Falló auth-server: ${authResult.error}`);
69
+ const adminSpinner = p.spinner();
70
+ adminSpinner.start("Creando proyecto de Vercel para admin-panel");
71
+ const adminResult = await createVercelProject({
72
+ token,
73
+ repo,
74
+ name: `${namePrefix}-admin-panel`,
75
+ rootDirectory: "apps/admin-panel",
76
+ env: adminPanelEnv,
77
+ });
78
+ adminSpinner.stop(adminResult.ok ? "Proyecto admin-panel creado" : `Falló admin-panel: ${adminResult.error}`);
79
+ if (authResult.ok && adminResult.ok) {
80
+ p.note("Los dos proyectos ya están conectados a tu repo, con la carpeta y las variables correctas. Entra a " +
81
+ "vercel.com/dashboard y dale \"Deploy\" a cada uno (o empuja un commit a tu rama principal) para el primer despliegue.", "Listo");
82
+ return true;
83
+ }
84
+ p.note("Algo falló creando los proyectos — usa los pasos manuales de abajo para lo que no se haya creado.", "Revisa el error de arriba");
85
+ return false;
86
+ }
29
87
  /**
30
88
  * Question B from the architecture plan: where do auth-server/admin-panel
31
89
  * run? Fully independent from the database question above — this only
@@ -106,21 +164,23 @@ export async function askDeploymentStep(repoRoot, db) {
106
164
  }
107
165
  const s = p.spinner();
108
166
  s.start("Generando apps/auth-server/.env.local y apps/admin-panel/.env.local");
109
- await writeEnvFile(`${repoRoot}/apps/auth-server/.env.local`, {
167
+ const authServerEnv = {
110
168
  NEXT_PUBLIC_SUPABASE_URL: db.supabaseUrl,
111
169
  NEXT_PUBLIC_SUPABASE_ANON_KEY: db.anonKey,
112
170
  SUPABASE_URL: db.supabaseUrl,
113
171
  SUPABASE_SERVICE_ROLE_KEY: db.serviceRoleKey,
114
172
  NEXT_PUBLIC_ADMIN_PANEL_URL: adminPanelUrl,
115
173
  NEXT_PUBLIC_COOKIE_DOMAIN: cookieDomain,
116
- });
117
- await writeEnvFile(`${repoRoot}/apps/admin-panel/.env.local`, {
174
+ };
175
+ await writeEnvFile(`${repoRoot}/apps/auth-server/.env.local`, authServerEnv);
176
+ const adminPanelEnv = {
118
177
  NEXT_PUBLIC_SUPABASE_URL: db.supabaseUrl,
119
178
  NEXT_PUBLIC_SUPABASE_ANON_KEY: db.anonKey,
120
179
  NEXT_PUBLIC_AUTH_SERVER_URL: authServerUrl,
121
180
  NEXT_PUBLIC_COOKIE_DOMAIN: cookieDomain,
122
181
  NEXT_PUBLIC_OAUTH_CLIENT_ID: oauthClientId ?? "",
123
- });
182
+ };
183
+ await writeEnvFile(`${repoRoot}/apps/admin-panel/.env.local`, adminPanelEnv);
124
184
  if (db.mode === "new-self-hosted") {
125
185
  // database.ts wrote SITE_URL="http://localhost:3000" as a fixed default
126
186
  // *before* this step ever asked where auth-server actually lives —
@@ -141,5 +201,8 @@ export async function askDeploymentStep(repoRoot, db) {
141
201
  p.note("docker/.env se actualizó con las URLs reales — como el contenedor de auth (GoTrue) ya estaba arriba con los " +
142
202
  "valores por defecto, corre `docker compose -f docker/docker-compose.yml up -d` una vez más para que los tome.", "Reinicia el docker-compose");
143
203
  }
144
- p.note(NEXT_STEPS[target], `Despliegue: ${target}`);
204
+ const autoCreated = target === "vercel" && (await tryAutoCreateVercelProjects(repoRoot, authServerEnv, adminPanelEnv));
205
+ if (!autoCreated) {
206
+ p.note(NEXT_STEPS[target], `Despliegue: ${target}`);
207
+ }
145
208
  }
@@ -0,0 +1,3 @@
1
+ /** A required text prompt — exits cleanly on Ctrl+C instead of returning a cancel symbol to every call site. */
2
+ export declare function textOrExit(message: string, placeholder?: string): Promise<string>;
3
+ //# sourceMappingURL=prompts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/utils/prompts.ts"],"names":[],"mappings":"AAEA,gHAAgH;AAChH,wBAAsB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAOvF"}
@@ -0,0 +1,10 @@
1
+ import * as p from "@clack/prompts";
2
+ /** A required text prompt — exits cleanly on Ctrl+C instead of returning a cancel symbol to every call site. */
3
+ export async function textOrExit(message, placeholder) {
4
+ const value = await p.text({ message, placeholder });
5
+ if (p.isCancel(value)) {
6
+ p.cancel("Instalación cancelada.");
7
+ process.exit(0);
8
+ }
9
+ return value;
10
+ }
@@ -0,0 +1,35 @@
1
+ import type { EnvValues } from "./files.js";
2
+ /**
3
+ * Resolves "owner/repo" from the git remote, so the wizard can offer to
4
+ * create Vercel projects automatically instead of walking the user through
5
+ * `vercel`/dashboard steps by hand. Only understands GitHub remotes (https
6
+ * or ssh) — Vercel's own gitRepository.type also supports gitlab/bitbucket,
7
+ * but this product's docs and CLI only ever assume GitHub.
8
+ */
9
+ export declare function detectGitHubRepo(repoRoot: string): Promise<string | null>;
10
+ export interface CreateVercelProjectOptions {
11
+ token: string;
12
+ /** "owner/repo" on GitHub. */
13
+ repo: string;
14
+ name: string;
15
+ rootDirectory: string;
16
+ env: EnvValues;
17
+ }
18
+ export interface CreateVercelProjectResult {
19
+ ok: boolean;
20
+ /** Only present when ok is false. */
21
+ error?: string;
22
+ }
23
+ /**
24
+ * Creates a Vercel project via the REST API — connected to the GitHub repo,
25
+ * scoped to one app's folder in this monorepo, with its environment
26
+ * variables already set. This is what makes automatic setup possible: the
27
+ * same three things (gitRepository, rootDirectory, environmentVariables)
28
+ * that are easy to get wrong doing this by hand (see the "vercel CLI run
29
+ * from inside the app folder" failure mode) are just fields in one request
30
+ * body here.
31
+ *
32
+ * https://vercel.com/docs/rest-api/reference/endpoints/projects/create-a-new-project
33
+ */
34
+ export declare function createVercelProject(options: CreateVercelProjectOptions): Promise<CreateVercelProjectResult>;
35
+ //# sourceMappingURL=vercel-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vercel-api.d.ts","sourceRoot":"","sources":["../../src/utils/vercel-api.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQ/E;AAED,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,GAAG,EAAE,SAAS,CAAC;CAChB;AAED,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,OAAO,CAAC;IACZ,qCAAqC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAgCjH"}
@@ -0,0 +1,61 @@
1
+ import { run } from "./exec.js";
2
+ /**
3
+ * Resolves "owner/repo" from the git remote, so the wizard can offer to
4
+ * create Vercel projects automatically instead of walking the user through
5
+ * `vercel`/dashboard steps by hand. Only understands GitHub remotes (https
6
+ * or ssh) — Vercel's own gitRepository.type also supports gitlab/bitbucket,
7
+ * but this product's docs and CLI only ever assume GitHub.
8
+ */
9
+ export async function detectGitHubRepo(repoRoot) {
10
+ try {
11
+ const url = (await run("git", ["remote", "get-url", "origin"], { cwd: repoRoot })).trim();
12
+ const match = url.match(/github\.com[:/]([^/]+)\/(.+?)(\.git)?$/);
13
+ return match ? `${match[1]}/${match[2]}` : null;
14
+ }
15
+ catch {
16
+ return null;
17
+ }
18
+ }
19
+ /**
20
+ * Creates a Vercel project via the REST API — connected to the GitHub repo,
21
+ * scoped to one app's folder in this monorepo, with its environment
22
+ * variables already set. This is what makes automatic setup possible: the
23
+ * same three things (gitRepository, rootDirectory, environmentVariables)
24
+ * that are easy to get wrong doing this by hand (see the "vercel CLI run
25
+ * from inside the app folder" failure mode) are just fields in one request
26
+ * body here.
27
+ *
28
+ * https://vercel.com/docs/rest-api/reference/endpoints/projects/create-a-new-project
29
+ */
30
+ export async function createVercelProject(options) {
31
+ const environmentVariables = Object.entries(options.env).map(([key, value]) => ({
32
+ key,
33
+ value,
34
+ type: "encrypted",
35
+ target: ["production", "preview", "development"],
36
+ }));
37
+ let response;
38
+ try {
39
+ response = await fetch("https://api.vercel.com/v11/projects", {
40
+ method: "POST",
41
+ headers: {
42
+ Authorization: `Bearer ${options.token}`,
43
+ "Content-Type": "application/json",
44
+ },
45
+ body: JSON.stringify({
46
+ name: options.name,
47
+ framework: "nextjs",
48
+ gitRepository: { type: "github", repo: options.repo },
49
+ rootDirectory: options.rootDirectory,
50
+ environmentVariables,
51
+ }),
52
+ });
53
+ }
54
+ catch (error) {
55
+ return { ok: false, error: error.message };
56
+ }
57
+ if (response.ok)
58
+ return { ok: true };
59
+ const body = (await response.json().catch(() => null));
60
+ return { ok: false, error: body?.error?.message ?? `HTTP ${response.status}` };
61
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-kontrolia-auth",
3
- "version": "2.0.1",
3
+ "version": "2.2.0",
4
4
  "license": "MIT",
5
5
  "description": "Turnkey installer for KontrolIA Auth. `npx create-kontrolia-auth mi-app` checks requirements, downloads the repo, installs deps, and runs a wizard: (1) connect to an existing Supabase project or create a new self-hosted one via Docker; (2) where to deploy auth-server/admin-panel (Docker, Vercel, Railway, Render, Coolify). Also exposes `update`, `migrate` and `doctor` subcommands.",
6
6
  "keywords": [