komplian 0.4.4 → 0.4.6
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/komplian-localhost.mjs +230 -113
- package/package.json +2 -2
package/komplian-localhost.mjs
CHANGED
|
@@ -1,22 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* Komplian localhost — genera `.env.local`
|
|
4
|
-
* api, app, web, admin, docs
|
|
3
|
+
* Komplian localhost — genera `.env.local` completos a partir de cada proyecto `/.env.example`
|
|
4
|
+
* (misma estructura y comentarios) + secretos aleatorios (crypto). Arranca api, app, web, admin, docs.
|
|
5
5
|
*
|
|
6
|
-
* Seguridad:
|
|
7
|
-
* No imprime
|
|
6
|
+
* Seguridad: los `.env*` no van en el paquete npm `komplian` (solo *.mjs + JSON + README).
|
|
7
|
+
* No imprime secretos. Tras generar, muestra tabla de URLs locales (puertos fijos).
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* npx komplian localhost --yes --minimal # solo app + web + docs (sin DB real)
|
|
12
|
-
* npx komplian localhost --env-only --force # solo escribir env
|
|
13
|
-
*
|
|
14
|
-
* URLs de Neon (opcional, una o tres):
|
|
15
|
-
* KOMPLIAN_LOCALHOST_DATABASE_URL # misma para app/admin/web/api
|
|
16
|
-
* KOMPLIAN_LOCALHOST_APP_DATABASE_URL
|
|
17
|
-
* KOMPLIAN_LOCALHOST_ADMIN_DATABASE_URL
|
|
18
|
-
* KOMPLIAN_LOCALHOST_WEB_DATABASE_URL
|
|
19
|
-
* O archivo (gitignored): komplian-localhost.secrets.env o .komplian/localhost-secrets.env
|
|
9
|
+
* Neon: KOMPLIAN_LOCALHOST_APP_DATABASE_URL, …_ADMIN_…, …_WEB_… o KOMPLIAN_LOCALHOST_SECRETS.env
|
|
10
|
+
* (plantilla en la raíz del monorepo: KOMPLIAN_LOCALHOST_SECRETS.env.example).
|
|
20
11
|
*/
|
|
21
12
|
|
|
22
13
|
import { randomBytes } from "node:crypto";
|
|
@@ -72,6 +63,8 @@ function parseEnvFile(content) {
|
|
|
72
63
|
|
|
73
64
|
function loadSecretsFromDisk(workspaceRoot) {
|
|
74
65
|
const paths = [
|
|
66
|
+
join(workspaceRoot, "KOMPLIAN_LOCALHOST_SECRETS.env"),
|
|
67
|
+
join(workspaceRoot, ".komplian", "KOMPLIAN_LOCALHOST_SECRETS.env"),
|
|
75
68
|
join(workspaceRoot, "komplian-localhost.secrets.env"),
|
|
76
69
|
join(workspaceRoot, ".komplian", "localhost-secrets.env"),
|
|
77
70
|
];
|
|
@@ -153,120 +146,240 @@ function buildShared() {
|
|
|
153
146
|
}
|
|
154
147
|
|
|
155
148
|
function header(project) {
|
|
156
|
-
return `#
|
|
157
|
-
#
|
|
149
|
+
return `# KOMPLIAN — generado por komplian localhost (no commitear) — ${project}
|
|
150
|
+
# Regenerar: npx komplian localhost --yes --force
|
|
151
|
+
# Los .env / .env.local no se publican en el paquete npm \`komplian\` (solo scripts/*.mjs).
|
|
158
152
|
|
|
159
153
|
`;
|
|
160
154
|
}
|
|
161
155
|
|
|
156
|
+
/** Serializa valor para KEY= en .env (comillas si hace falta). */
|
|
157
|
+
function formatEnvValue(val) {
|
|
158
|
+
if (val === undefined || val === null) return '""';
|
|
159
|
+
const s = String(val);
|
|
160
|
+
if (s === "") return '""';
|
|
161
|
+
if (/[\s#"']/.test(s) || s.includes("\n")) {
|
|
162
|
+
return `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
163
|
+
}
|
|
164
|
+
return s;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function collectKeysFromEnvContent(content) {
|
|
168
|
+
const keys = new Set();
|
|
169
|
+
for (const line of content.split(/\r?\n/)) {
|
|
170
|
+
const t = line.trim();
|
|
171
|
+
if (!t || t.startsWith("#")) continue;
|
|
172
|
+
const eq = line.indexOf("=");
|
|
173
|
+
if (eq === -1) continue;
|
|
174
|
+
keys.add(line.slice(0, eq).trim());
|
|
175
|
+
}
|
|
176
|
+
return keys;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Sustituye claves presentes en el contenido y añade al final las que faltan.
|
|
181
|
+
* Mantiene comentarios al final de línea (# ...).
|
|
182
|
+
*/
|
|
183
|
+
function applyOverridesToEnvContent(content, overrides) {
|
|
184
|
+
const present = collectKeysFromEnvContent(content);
|
|
185
|
+
const lines = content.split(/\r?\n/);
|
|
186
|
+
const out = lines.map((line) => {
|
|
187
|
+
const trimmed = line.trim();
|
|
188
|
+
if (!trimmed || trimmed.startsWith("#")) return line;
|
|
189
|
+
const eq = line.indexOf("=");
|
|
190
|
+
if (eq === -1) return line;
|
|
191
|
+
const key = line.slice(0, eq).trim();
|
|
192
|
+
if (!Object.prototype.hasOwnProperty.call(overrides, key)) return line;
|
|
193
|
+
const rest = line.slice(eq + 1);
|
|
194
|
+
const hashIdx = rest.search(/\s+#/);
|
|
195
|
+
const suffix = hashIdx >= 0 ? rest.slice(hashIdx) : "";
|
|
196
|
+
return `${key}=${formatEnvValue(overrides[key])}${suffix}`;
|
|
197
|
+
});
|
|
198
|
+
const append = [];
|
|
199
|
+
for (const [key, val] of Object.entries(overrides)) {
|
|
200
|
+
if (!present.has(key)) append.push(`${key}=${formatEnvValue(val)}`);
|
|
201
|
+
}
|
|
202
|
+
const tail =
|
|
203
|
+
append.length > 0
|
|
204
|
+
? `\n\n# --- KOMPLIAN localhost (claves que no estaban en .env.example) ---\n${append.join("\n")}`
|
|
205
|
+
: "";
|
|
206
|
+
return out.join("\n") + tail;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function buildAppOverrides(s, db, opts) {
|
|
210
|
+
const appUrl = db.app || PLACEHOLDER_DB;
|
|
211
|
+
const useNoDb = opts.minimal || String(appUrl).includes("placeholder");
|
|
212
|
+
/** @type {Record<string, string>} */
|
|
213
|
+
const o = {
|
|
214
|
+
DATABASE_URL: appUrl,
|
|
215
|
+
NEXTAUTH_SECRET: s.nextAuthSecret,
|
|
216
|
+
NEXTAUTH_URL: "http://localhost:3001",
|
|
217
|
+
CSRF_SECRET: s.csrfSecret,
|
|
218
|
+
NEXT_PUBLIC_APP_URL: "http://localhost:3001",
|
|
219
|
+
KOMPLIAN_API_URL: "http://localhost:4000",
|
|
220
|
+
KOMPLIAN_API_KEY: s.apiKey,
|
|
221
|
+
KOMPLIAN_INTERNAL_WEBHOOK_SECRET: s.internalWebhook,
|
|
222
|
+
ENCRYPTION_KEY: s.encryptionKey,
|
|
223
|
+
OPENAI_API_KEY: "",
|
|
224
|
+
MAILBOX_OAUTH_SECRET: s.nextAuthSecret,
|
|
225
|
+
NEXT_PUBLIC_API_URL: "http://localhost:4000",
|
|
226
|
+
AUTH_SECRET: s.nextAuthSecret,
|
|
227
|
+
TWO_FACTOR_ENCRYPTION_KEY: s.twoFactorEnc,
|
|
228
|
+
HASH_SECRET: s.hashSecret,
|
|
229
|
+
};
|
|
230
|
+
if (useNoDb) o.DEV_MODE_NO_DB = "true";
|
|
231
|
+
return o;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function buildApiOverrides(s, db) {
|
|
235
|
+
return {
|
|
236
|
+
APP_DATABASE_URL: db.apiApp || PLACEHOLDER_DB,
|
|
237
|
+
ADMIN_DATABASE_URL: db.apiAdmin || PLACEHOLDER_DB,
|
|
238
|
+
WEB_DATABASE_URL: db.apiWeb || PLACEHOLDER_DB,
|
|
239
|
+
API_KEY: s.apiKey,
|
|
240
|
+
ADMIN_API_KEY: s.adminApiKey,
|
|
241
|
+
KOMPLIAN_INTERNAL_WEBHOOK_SECRET: s.internalWebhook,
|
|
242
|
+
JWT_SECRET: s.jwtSecret,
|
|
243
|
+
ENCRYPTION_KEY: s.encryptionKey,
|
|
244
|
+
OPENAI_API_KEY: "",
|
|
245
|
+
RESEND_API_KEY: "",
|
|
246
|
+
CRON_SECRET: s.cronSecret,
|
|
247
|
+
APP_URL: "http://localhost:3001",
|
|
248
|
+
WEB_URL: "http://localhost:3003",
|
|
249
|
+
ADMIN_URL: "http://localhost:3002",
|
|
250
|
+
API_BASE_URL: "http://localhost:4000",
|
|
251
|
+
PORT: "4000",
|
|
252
|
+
NODE_ENV: "development",
|
|
253
|
+
LOG_LEVEL: "debug",
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function buildWebOverrides(s, db) {
|
|
258
|
+
return {
|
|
259
|
+
DATABASE_URL: db.web || PLACEHOLDER_DB,
|
|
260
|
+
KOMPLIAN_API_URL: "http://localhost:4000",
|
|
261
|
+
KOMPLIAN_API_KEY: s.apiKey,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function buildAdminOverrides(s, db) {
|
|
266
|
+
return {
|
|
267
|
+
DATABASE_URL: db.admin || PLACEHOLDER_DB,
|
|
268
|
+
NEXTAUTH_URL: "http://localhost:3002",
|
|
269
|
+
NEXTAUTH_SECRET: s.nextAuthAdmin,
|
|
270
|
+
AUTH_SECRET: s.nextAuthAdmin,
|
|
271
|
+
API_URL: "http://localhost:4000",
|
|
272
|
+
ADMIN_API_KEY: s.adminApiKey,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function buildDocsOverrides() {
|
|
277
|
+
return {
|
|
278
|
+
NODE_ENV: "development",
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function envFromOverridesOnly(overrides) {
|
|
283
|
+
return Object.entries(overrides)
|
|
284
|
+
.map(([k, v]) => `${k}=${formatEnvValue(v)}`)
|
|
285
|
+
.join("\n");
|
|
286
|
+
}
|
|
287
|
+
|
|
162
288
|
function writeAppEnv(root, s, db, opts) {
|
|
163
289
|
const p = join(root, "app", ".env.local");
|
|
164
290
|
if (existsSync(p) && !opts.force) return { path: p, skipped: true };
|
|
165
|
-
const
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
"",
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
`CSRF_SECRET=${s.csrfSecret}`,
|
|
175
|
-
"",
|
|
176
|
-
`NEXT_PUBLIC_APP_URL=http://localhost:3001`,
|
|
177
|
-
`NEXT_PUBLIC_API_URL=http://localhost:4000`,
|
|
178
|
-
"",
|
|
179
|
-
`KOMPLIAN_API_URL=http://localhost:4000`,
|
|
180
|
-
`KOMPLIAN_API_KEY=${s.apiKey}`,
|
|
181
|
-
`KOMPLIAN_INTERNAL_WEBHOOK_SECRET=${s.internalWebhook}`,
|
|
182
|
-
"",
|
|
183
|
-
`ENCRYPTION_KEY=${s.encryptionKey}`,
|
|
184
|
-
`TWO_FACTOR_ENCRYPTION_KEY=${s.twoFactorEnc}`,
|
|
185
|
-
`HASH_SECRET=${s.hashSecret}`,
|
|
186
|
-
"",
|
|
187
|
-
"# Optional: OPENAI_API_KEY= Stripe, Shopify, Google OAuth — ver app/.env.example",
|
|
188
|
-
];
|
|
189
|
-
writeAtomic(p, lines.join("\n"));
|
|
291
|
+
const examplePath = join(root, "app", ".env.example");
|
|
292
|
+
const overrides = buildAppOverrides(s, db, opts);
|
|
293
|
+
let body;
|
|
294
|
+
if (existsSync(examplePath)) {
|
|
295
|
+
body = applyOverridesToEnvContent(readFileSync(examplePath, "utf8"), overrides);
|
|
296
|
+
} else {
|
|
297
|
+
body = envFromOverridesOnly(overrides);
|
|
298
|
+
}
|
|
299
|
+
writeAtomic(p, header("app") + body);
|
|
190
300
|
return { path: p, skipped: false };
|
|
191
301
|
}
|
|
192
302
|
|
|
193
303
|
function writeApiEnv(root, s, db, opts) {
|
|
194
304
|
const p = join(root, "api", ".env.local");
|
|
195
305
|
if (existsSync(p) && !opts.force) return { path: p, skipped: true };
|
|
196
|
-
const
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
`API_KEY=${s.apiKey}`,
|
|
206
|
-
`ADMIN_API_KEY=${s.adminApiKey}`,
|
|
207
|
-
`KOMPLIAN_INTERNAL_WEBHOOK_SECRET=${s.internalWebhook}`,
|
|
208
|
-
`JWT_SECRET=${s.jwtSecret}`,
|
|
209
|
-
`ENCRYPTION_KEY=${s.encryptionKey}`,
|
|
210
|
-
"",
|
|
211
|
-
"# OPENAI_API_KEY= RESEND_API_KEY= — ver api/.env.example",
|
|
212
|
-
"",
|
|
213
|
-
`CRON_SECRET=${s.cronSecret}`,
|
|
214
|
-
"",
|
|
215
|
-
`APP_URL=http://localhost:3001`,
|
|
216
|
-
`WEB_URL=http://localhost:3003`,
|
|
217
|
-
`ADMIN_URL=http://localhost:3002`,
|
|
218
|
-
`API_BASE_URL=http://localhost:4000`,
|
|
219
|
-
"",
|
|
220
|
-
`PORT=4000`,
|
|
221
|
-
`NODE_ENV=development`,
|
|
222
|
-
`LOG_LEVEL=debug`,
|
|
223
|
-
];
|
|
224
|
-
writeAtomic(p, lines.join("\n"));
|
|
306
|
+
const examplePath = join(root, "api", ".env.example");
|
|
307
|
+
const overrides = buildApiOverrides(s, db);
|
|
308
|
+
let body;
|
|
309
|
+
if (existsSync(examplePath)) {
|
|
310
|
+
body = applyOverridesToEnvContent(readFileSync(examplePath, "utf8"), overrides);
|
|
311
|
+
} else {
|
|
312
|
+
body = envFromOverridesOnly(overrides);
|
|
313
|
+
}
|
|
314
|
+
writeAtomic(p, header("api") + body);
|
|
225
315
|
return { path: p, skipped: false };
|
|
226
316
|
}
|
|
227
317
|
|
|
228
318
|
function writeWebEnv(root, s, db, opts) {
|
|
229
319
|
const p = join(root, "web", ".env.local");
|
|
230
320
|
if (existsSync(p) && !opts.force) return { path: p, skipped: true };
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
writeAtomic(p,
|
|
321
|
+
const examplePath = join(root, "web", ".env.example");
|
|
322
|
+
const overrides = buildWebOverrides(s, db);
|
|
323
|
+
let body;
|
|
324
|
+
if (existsSync(examplePath)) {
|
|
325
|
+
body = applyOverridesToEnvContent(readFileSync(examplePath, "utf8"), overrides);
|
|
326
|
+
} else {
|
|
327
|
+
body = envFromOverridesOnly(overrides);
|
|
328
|
+
}
|
|
329
|
+
writeAtomic(p, header("web") + body);
|
|
240
330
|
return { path: p, skipped: false };
|
|
241
331
|
}
|
|
242
332
|
|
|
243
333
|
function writeAdminEnv(root, s, db, opts) {
|
|
244
334
|
const p = join(root, "admin", ".env.local");
|
|
245
335
|
if (existsSync(p) && !opts.force) return { path: p, skipped: true };
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
writeAtomic(p, lines.join("\n"));
|
|
336
|
+
const examplePath = join(root, "admin", ".env.example");
|
|
337
|
+
const overrides = buildAdminOverrides(s, db);
|
|
338
|
+
let body;
|
|
339
|
+
if (existsSync(examplePath)) {
|
|
340
|
+
body = applyOverridesToEnvContent(readFileSync(examplePath, "utf8"), overrides);
|
|
341
|
+
} else {
|
|
342
|
+
body = envFromOverridesOnly(overrides);
|
|
343
|
+
}
|
|
344
|
+
writeAtomic(p, header("admin") + body);
|
|
256
345
|
return { path: p, skipped: false };
|
|
257
346
|
}
|
|
258
347
|
|
|
259
348
|
function writeDocsEnv(root, opts) {
|
|
260
349
|
const p = join(root, "docs", ".env.local");
|
|
261
350
|
if (existsSync(p) && !opts.force) return { path: p, skipped: true };
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
351
|
+
const examplePath = join(root, "docs", ".env.example");
|
|
352
|
+
if (!existsSync(examplePath)) {
|
|
353
|
+
writeAtomic(
|
|
354
|
+
p,
|
|
355
|
+
`${header("docs")}# Sin variables obligatorias para next dev.\n`
|
|
356
|
+
);
|
|
357
|
+
return { path: p, skipped: false };
|
|
358
|
+
}
|
|
359
|
+
const overrides = buildDocsOverrides();
|
|
360
|
+
let content = readFileSync(examplePath, "utf8");
|
|
361
|
+
content = applyOverridesToEnvContent(content, overrides);
|
|
362
|
+
writeAtomic(p, header("docs") + content);
|
|
267
363
|
return { path: p, skipped: false };
|
|
268
364
|
}
|
|
269
365
|
|
|
366
|
+
function printLocalUrls(minimal) {
|
|
367
|
+
log("");
|
|
368
|
+
log(`${c.bold}Local — abre en el navegador${c.reset}`);
|
|
369
|
+
if (!minimal) {
|
|
370
|
+
log(` ${c.cyan}API${c.reset} http://localhost:4000 ${c.dim}(/health)${c.reset}`);
|
|
371
|
+
}
|
|
372
|
+
log(` ${c.cyan}App${c.reset} http://localhost:3001`);
|
|
373
|
+
if (!minimal) {
|
|
374
|
+
log(` ${c.cyan}Admin${c.reset} http://localhost:3002`);
|
|
375
|
+
log(` ${c.cyan}Web${c.reset} http://localhost:3003`);
|
|
376
|
+
} else {
|
|
377
|
+
log(` ${c.cyan}Web${c.reset} http://localhost:3003`);
|
|
378
|
+
}
|
|
379
|
+
log(` ${c.cyan}Docs${c.reset} http://localhost:3004`);
|
|
380
|
+
log("");
|
|
381
|
+
}
|
|
382
|
+
|
|
270
383
|
async function confirmOverwrite(yes) {
|
|
271
384
|
if (yes) return true;
|
|
272
385
|
const rl = createInterface({ input, output });
|
|
@@ -307,14 +420,14 @@ function parseLocalhostArgs(argv) {
|
|
|
307
420
|
function usageLocalhost() {
|
|
308
421
|
log(`Uso: npx komplian localhost [opciones] [carpeta-monorepo]`);
|
|
309
422
|
log(``);
|
|
310
|
-
log(` Genera .env.local
|
|
311
|
-
log(` ${c.dim}Neon:
|
|
423
|
+
log(` Genera .env.local completos (desde cada .env.example + secretos KOMPLIAN) y arranca todos los dev servers.`);
|
|
424
|
+
log(` ${c.dim}Neon: KOMPLIAN_LOCALHOST_APP_DATABASE_URL, …_ADMIN_…, …_WEB_… o archivo KOMPLIAN_LOCALHOST_SECRETS.env${c.reset}`);
|
|
312
425
|
log(``);
|
|
313
|
-
log(` -y, --yes Sin confirmación
|
|
314
|
-
log(` --force
|
|
315
|
-
log(` --minimal Solo app + web + docs
|
|
316
|
-
log(` --env-only Solo escribir env
|
|
317
|
-
log(` -w, --workspace Ruta al monorepo
|
|
426
|
+
log(` -y, --yes Sin confirmación`);
|
|
427
|
+
log(` --force Regenerar .env.local`);
|
|
428
|
+
log(` --minimal Solo app + web + docs`);
|
|
429
|
+
log(` --env-only Solo escribir .env.local`);
|
|
430
|
+
log(` -w, --workspace Ruta al monorepo`);
|
|
318
431
|
log(` -h, --help`);
|
|
319
432
|
}
|
|
320
433
|
|
|
@@ -383,22 +496,24 @@ export async function runLocalhost(argv) {
|
|
|
383
496
|
written.push(writeDocsEnv(workspaceRoot, opts));
|
|
384
497
|
|
|
385
498
|
log("");
|
|
386
|
-
log(`${c.cyan}━━ .env.local ━━${c.reset} ${c.dim}(
|
|
499
|
+
log(`${c.cyan}━━ .env.local ━━${c.reset} ${c.dim}(600, no se publican en npm)${c.reset}`);
|
|
387
500
|
for (const w of written) {
|
|
388
501
|
const st = w.skipped ? `${c.dim}sin cambios${c.reset}` : `${c.green}ok${c.reset}`;
|
|
389
502
|
log(` ${st} ${w.path}`);
|
|
390
503
|
}
|
|
391
504
|
|
|
505
|
+
printLocalUrls(opts.minimal);
|
|
506
|
+
|
|
392
507
|
if (!db.hasReal && !opts.minimal) {
|
|
508
|
+
log(
|
|
509
|
+
`${c.yellow}⚠${c.reset} Sin Neon: exporta ${c.bold}KOMPLIAN_LOCALHOST_APP_DATABASE_URL${c.reset} / ${c.bold}ADMIN${c.reset} / ${c.bold}WEB${c.reset} o usa ${c.bold}KOMPLIAN_LOCALHOST_SECRETS.env${c.reset} (plantilla: KOMPLIAN_LOCALHOST_SECRETS.env.example).`
|
|
510
|
+
);
|
|
511
|
+
log(`${c.dim} O ${c.bold}--minimal${c.reset} (solo app + web + docs).${c.reset}`);
|
|
393
512
|
log("");
|
|
394
|
-
log(`${c.yellow}⚠${c.reset} Sin URLs Neon reales: api/admin/web usarán un placeholder local.`);
|
|
395
|
-
log(` ${c.dim}Define KOMPLIAN_LOCALHOST_DATABASE_URL o komplian-localhost.secrets.env${c.reset}`);
|
|
396
|
-
log(` ${c.dim}O usa ${c.bold}--minimal${c.reset} para solo app + web + docs.${c.reset}`);
|
|
397
513
|
}
|
|
398
514
|
|
|
399
515
|
if (opts.envOnly) {
|
|
400
|
-
log(
|
|
401
|
-
log(`${c.green}✓${c.reset} Solo entorno. Arranca con: ${c.bold}npx komplian localhost --yes${c.reset}`);
|
|
516
|
+
log(`${c.green}✓${c.reset} Entorno listo. Arranca: ${c.bold}npx komplian localhost --yes${c.reset}`);
|
|
402
517
|
return;
|
|
403
518
|
}
|
|
404
519
|
|
|
@@ -423,18 +538,20 @@ export async function runLocalhost(argv) {
|
|
|
423
538
|
return `npm run dev --prefix ${dir}`;
|
|
424
539
|
});
|
|
425
540
|
|
|
426
|
-
log(
|
|
427
|
-
log(`${c.cyan}━━ Servicios (${services.length}) ━━${c.reset} ${workspaceRoot}`);
|
|
428
|
-
log(`${c.dim}Ctrl+C detiene todos.${c.reset}`);
|
|
541
|
+
log(`${c.cyan}━━ Arranque (${services.length}) ━━${c.reset} ${c.dim}Ctrl+C detiene todo${c.reset}`);
|
|
429
542
|
log("");
|
|
430
543
|
|
|
544
|
+
// shell: false — con shell:true Node concatena args para /bin/sh -c y concurrently
|
|
545
|
+
// recibe un número de comandos/names incoherente → TypeError en logger (prev.replace).
|
|
546
|
+
const npx = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
431
547
|
const child = spawn(
|
|
432
|
-
|
|
548
|
+
npx,
|
|
433
549
|
["--yes", "concurrently@9", "-c", colors, "-n", names, ...scripts],
|
|
434
550
|
{
|
|
435
551
|
cwd: workspaceRoot,
|
|
436
552
|
stdio: "inherit",
|
|
437
|
-
shell:
|
|
553
|
+
shell: false,
|
|
554
|
+
windowsHide: true,
|
|
438
555
|
env: { ...process.env },
|
|
439
556
|
}
|
|
440
557
|
);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "komplian",
|
|
3
|
-
"version": "0.4.
|
|
4
|
-
"description": "Komplian
|
|
3
|
+
"version": "0.4.6",
|
|
4
|
+
"description": "Komplian CLI: clone repos (onboard), Postman sync, localhost (.env.local + dev servers). Node 18+. No env files in the published package.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=18"
|