komplian 0.6.1 → 0.7.1

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.
@@ -37,6 +37,7 @@ const c = {
37
37
  };
38
38
 
39
39
  function log(s = "") {
40
+ if (process.env.KOMPLIAN_CLI_QUIET === "1") return;
40
41
  console.log(s);
41
42
  }
42
43
 
@@ -247,12 +248,14 @@ function cloudLine(org, name, status) {
247
248
  }
248
249
 
249
250
  function cloneOne(org, name, workspace, useSsh) {
251
+ const q = process.env.KOMPLIAN_CLI_QUIET === "1";
250
252
  const gitDir = join(workspace, name, ".git");
251
253
  if (existsSync(gitDir)) {
252
- log(cloudLine(org, name, "skip"));
254
+ if (q) console.log(`${c.yellow}○${c.reset} ${name}`);
255
+ else log(cloudLine(org, name, "skip"));
253
256
  return true;
254
257
  }
255
- process.stdout.write(`\r${cloudLine(org, name, "pending")}`);
258
+ if (!q) process.stdout.write(`\r${cloudLine(org, name, "pending")}`);
256
259
  const args = useSsh
257
260
  ? ["clone", `git@github.com:${org}/${name}.git`, name]
258
261
  : ["repo", "clone", `${org}/${name}`, name];
@@ -264,13 +267,17 @@ function cloneOne(org, name, workspace, useSsh) {
264
267
  stdio: ["ignore", "pipe", "pipe"],
265
268
  }),
266
269
  });
267
- process.stdout.write("\r\x1b[K");
270
+ if (!q) process.stdout.write("\r\x1b[K");
268
271
  if (r.status === 0) {
269
- log(cloudLine(org, name, "ok"));
272
+ if (q) console.log(`${c.green}✓${c.reset} ${name}`);
273
+ else log(cloudLine(org, name, "ok"));
270
274
  return true;
271
275
  }
272
- log(cloudLine(org, name, "fail"));
273
- if (r.stderr) log(`${c.dim}${r.stderr.trim()}${c.reset}`);
276
+ if (q) console.error(`${c.red}✗${c.reset} ${name}`);
277
+ else {
278
+ log(cloudLine(org, name, "fail"));
279
+ if (r.stderr) log(`${c.dim}${r.stderr.trim()}${c.reset}`);
280
+ }
274
281
  return false;
275
282
  }
276
283
 
@@ -315,6 +322,9 @@ function npmInstallOneRepo(dir, name) {
315
322
  const pkg = join(dir, "package.json");
316
323
  if (!existsSync(pkg)) return { ok: true, skipped: true };
317
324
 
325
+ const stdio =
326
+ process.env.KOMPLIAN_CLI_QUIET === "1" ? "ignore" : "inherit";
327
+
318
328
  const yarnLock = join(dir, "yarn.lock");
319
329
  const pnpmLock = join(dir, "pnpm-lock.yaml");
320
330
  const npmLock = join(dir, "package-lock.json");
@@ -330,7 +340,7 @@ function npmInstallOneRepo(dir, name) {
330
340
  const r = spawnSync(
331
341
  "yarn",
332
342
  ["install", "--frozen-lockfile"],
333
- spawnWin({ cwd: dir, stdio: "inherit" })
343
+ spawnWin({ cwd: dir, stdio })
334
344
  );
335
345
  return { ok: r.status === 0, skipped: false };
336
346
  }
@@ -346,7 +356,7 @@ function npmInstallOneRepo(dir, name) {
346
356
  const r = spawnSync(
347
357
  "pnpm",
348
358
  ["install", "--frozen-lockfile"],
349
- spawnWin({ cwd: dir, stdio: "inherit" })
359
+ spawnWin({ cwd: dir, stdio })
350
360
  );
351
361
  return { ok: r.status === 0, skipped: false };
352
362
  }
@@ -360,7 +370,7 @@ function npmInstallOneRepo(dir, name) {
360
370
 
361
371
  if (existsSync(npmLock)) {
362
372
  log(`${c.dim}→${c.reset} ${name} ${c.dim}(npm ci — lock sin cambios)${c.reset}`);
363
- const r = spawnSync("npm", ["ci", ...quiet], spawnWin({ cwd: dir, stdio: "inherit" }));
373
+ const r = spawnSync("npm", ["ci", ...quiet], spawnWin({ cwd: dir, stdio }));
364
374
  if (r.status === 0) return { ok: true, skipped: false };
365
375
  log(
366
376
  `${c.yellow}○${c.reset} ${name}: npm ci falló (¿lock desincronizado?). ${c.dim}Revisa con npm install en ese repo.${c.reset}`
@@ -372,20 +382,25 @@ function npmInstallOneRepo(dir, name) {
372
382
  const r = spawnSync(
373
383
  "npm",
374
384
  ["install", ...quiet, "--no-package-lock"],
375
- spawnWin({ cwd: dir, stdio: "inherit" })
385
+ spawnWin({ cwd: dir, stdio })
376
386
  );
377
387
  return { ok: r.status === 0, skipped: false };
378
388
  }
379
389
 
380
390
  function npmInstallEach(workspace) {
381
- log("");
382
- log(`${c.cyan}━━ Dependencias por repo ━━${c.reset}`);
391
+ const q = process.env.KOMPLIAN_CLI_QUIET === "1";
392
+ if (!q) {
393
+ log("");
394
+ log(`${c.cyan}━━ dependencies ━━${c.reset}`);
395
+ }
383
396
  for (const ent of readdirSync(workspace)) {
384
397
  const d = join(workspace, ent);
385
398
  if (!statSync(d).isDirectory()) continue;
386
399
  const { ok, skipped } = npmInstallOneRepo(d, ent);
387
400
  if (skipped) continue;
388
- if (ok) {
401
+ if (q) {
402
+ console.log(`${ok ? c.green + "✓" + c.reset : c.yellow + "○" + c.reset} ${ent}`);
403
+ } else if (ok) {
389
404
  log(`${c.green}✓${c.reset} ${ent}`);
390
405
  } else {
391
406
  log(`${c.yellow}○${c.reset} ${ent}`);
@@ -394,10 +409,11 @@ function npmInstallEach(workspace) {
394
409
  }
395
410
 
396
411
  function usage() {
397
- log(`Uso: komplian onboard | postman | mcp-tools | db:all:dev | localhost | db …`);
398
- log(` ${c.bold}Setup estándar (orden):${c.reset}`);
412
+ log(`Uso: komplian setup | onboard | postman | mcp-tools | db:all:dev | localhost | db …`);
413
+ log(` ${c.bold}Todo en uno:${c.reset} ${c.cyan}npx komplian setup${c.reset} ${c.dim}(onboard+postman+mcp+db+localhost; formulario en navegador si hace falta)${c.reset}`);
414
+ log(` ${c.bold}Setup por pasos:${c.reset}`);
399
415
  log(` 1. npx komplian onboard --yes`);
400
- log(` 2. npx komplian postman login → npx komplian postman --yes ${c.dim}(@komplian.com)${c.reset}`);
416
+ log(` 2. npx komplian postman --yes ${c.dim}(@komplian.com)${c.reset}`);
401
417
  log(` 3. npx komplian mcp-tools --yes`);
402
418
  log(` 4. npx komplian db:all:dev ${c.dim}(Postman + 3 URLs dev → ~/.komplian + .env.local)${c.reset}`);
403
419
  log(` 5. npx komplian localhost --yes`);
@@ -486,6 +502,11 @@ function normalizeArgv(argv) {
486
502
 
487
503
  async function main() {
488
504
  const rawArgv = process.argv.slice(2);
505
+ if (rawArgv[0] === "setup") {
506
+ const { runSetup } = await import("./komplian-setup.mjs");
507
+ await runSetup(rawArgv.slice(1));
508
+ return;
509
+ }
489
510
  if (rawArgv[0] === "postman") {
490
511
  const { runPostman } = await import("./komplian-postman.mjs");
491
512
  await runPostman(rawArgv.slice(1));
@@ -566,13 +587,15 @@ async function main() {
566
587
  );
567
588
 
568
589
  if (!ghOk()) {
569
- log(`${c.red}✗${c.reset} No hay sesión en GitHub CLI.`);
590
+ console.error(`${c.red}✗${c.reset} GitHub CLI not logged in.`);
570
591
  if (!runGhAuth()) {
571
- log(`${c.red}✗${c.reset} Autenticación cancelada.`);
592
+ console.error(`${c.red}✗${c.reset} GitHub auth cancelled.`);
572
593
  process.exit(1);
573
594
  }
574
595
  }
575
- log(`${c.green}✓${c.reset} GitHub CLI OK`);
596
+ if (!process.env.KOMPLIAN_CLI_QUIET) {
597
+ log(`${c.green}✓${c.reset} GitHub CLI OK`);
598
+ }
576
599
 
577
600
  const org = process.env.KOMPLIAN_ORG || cfg.org || "Komplian";
578
601
  verifyOrgMembership(org);
@@ -35,6 +35,7 @@ const c = {
35
35
  };
36
36
 
37
37
  function log(s = "") {
38
+ if (process.env.KOMPLIAN_CLI_QUIET === "1") return;
38
39
  console.log(s);
39
40
  }
40
41
 
@@ -126,9 +127,8 @@ function logApiFailure(context, status, body) {
126
127
  extra = " [debug: no serializable]";
127
128
  }
128
129
  }
129
- log(
130
- `${c.yellow}○${c.reset} ${context}: HTTP ${status}${hint ? ` — ${hint}` : ""}${extra}`
131
- );
130
+ const line = `${c.yellow}○${c.reset} ${context}: HTTP ${status}${hint ? ` — ${hint}` : ""}${extra}`;
131
+ console.error(line);
132
132
  }
133
133
 
134
134
  function ensureSecureKomplianDir() {
@@ -216,7 +216,7 @@ function defaultKeyPath() {
216
216
  /**
217
217
  * Orden: POSTMAN_API_KEY → ~/.komplian/postman-api-key (o KOMPLIAN_POSTMAN_KEY_FILE).
218
218
  */
219
- function resolveApiKey() {
219
+ export function resolveApiKey() {
220
220
  const env = process.env.POSTMAN_API_KEY?.trim();
221
221
  if (env) return { key: env, source: "POSTMAN_API_KEY" };
222
222
  const path = defaultKeyPath();
@@ -227,22 +227,31 @@ function resolveApiKey() {
227
227
  return { key: "", source: null };
228
228
  }
229
229
 
230
+ /** Guarda la clave en ~/.komplian/postman-api-key (uso: `komplian setup` vía navegador). */
231
+ export function savePostmanApiKeyToKomplianHome(apiKey) {
232
+ const k = (apiKey || "").trim();
233
+ if (!k) return false;
234
+ const keyPath = defaultKeyPath();
235
+ ensureSecureKomplianDir();
236
+ writeFileSync(keyPath, `${k}\n`, { encoding: "utf8", mode: 0o600 });
237
+ try {
238
+ chmodSync(keyPath, 0o600);
239
+ } catch {
240
+ /* Windows u otros */
241
+ }
242
+ return true;
243
+ }
244
+
230
245
  function printMissingKeyHelp() {
231
- log(`${c.red}✗${c.reset} No hay API key de Postman.`);
232
- log(``);
233
- log(` ${c.bold}Terminal interactiva (primera vez):${c.reset}`);
234
- log(
235
- ` ${c.cyan}npx komplian postman --yes${c.reset} (pide la clave y sigue con el sync)`
236
- );
237
- log(
238
- ` ${c.cyan}npx komplian postman login${c.reset} (solo guardar clave; luego postman --yes)`
239
- );
240
- log(``);
241
- log(` ${c.bold}Sin TTY (CI, pipes):${c.reset}`);
242
- log(` ${c.dim}export POSTMAN_API_KEY=…${c.reset}`);
243
- log(
244
- `${c.dim} Archivo: ${formatHomePath(defaultKeyPath())} (permiso 600)${c.reset}`
245
- );
246
+ const w = (s) => console.error(s);
247
+ w(`${c.red}✗${c.reset} No Postman API key.`);
248
+ w(``);
249
+ w(` ${c.bold}Interactive terminal:${c.reset}`);
250
+ w(` ${c.cyan}npx komplian postman --yes${c.reset}`);
251
+ w(` ${c.cyan}npx komplian postman login${c.reset}`);
252
+ w(``);
253
+ w(` ${c.bold}CI / no TTY:${c.reset} ${c.dim}POSTMAN_API_KEY${c.reset}`);
254
+ w(`${c.dim} ${formatHomePath(defaultKeyPath())}${c.reset}`);
246
255
  process.exit(1);
247
256
  }
248
257
 
@@ -656,6 +665,32 @@ async function pickWorkspaceId(apiKey, explicit) {
656
665
  return w.id;
657
666
  }
658
667
 
668
+ /** Validación sin `process.exit` (p. ej. formulario web local en `komplian setup`). */
669
+ export async function validatePostmanApiKeyForKomplian(apiKey, domain) {
670
+ const d = (domain || "komplian.com").trim().replace(/^@/, "");
671
+ const { ok, status, body } = await pmFetch(apiKey, "/me");
672
+ if (!ok) {
673
+ return {
674
+ ok: false,
675
+ error: `Postman rechazó la clave (HTTP ${status}).`,
676
+ };
677
+ }
678
+ const email = extractEmail(body);
679
+ if (!email) {
680
+ return {
681
+ ok: false,
682
+ error: "La API de Postman no devolvió email en /me.",
683
+ };
684
+ }
685
+ if (!emailAllowed(email, d)) {
686
+ return {
687
+ ok: false,
688
+ error: `Se requiere una cuenta con email @${d}.`,
689
+ };
690
+ }
691
+ return { ok: true };
692
+ }
693
+
659
694
  async function verifyKomplianEmail(apiKey, domain, opts = {}) {
660
695
  const quietOk = opts.quietSuccess === true;
661
696
  const { ok, status, body } = await pmFetch(apiKey, "/me");
@@ -994,6 +1029,9 @@ export async function runPostman(argv) {
994
1029
  `${c.dim} Para rellenar apiKey: exporta API_KEY o pon api/.env y vuelve a ejecutar este comando.${c.reset}`
995
1030
  );
996
1031
  }
1032
+ if (process.env.KOMPLIAN_CLI_QUIET === "1") {
1033
+ console.log(`${c.green}✓${c.reset} postman`);
1034
+ }
997
1035
  }
998
1036
 
999
1037
  /**