komplian 0.6.0 → 0.7.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 +5 -9
- package/komplian-onboard.mjs +9 -3
- package/komplian-postman.mjs +121 -27
- package/komplian-setup.mjs +477 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -6,26 +6,22 @@
|
|
|
6
6
|
2. Browser login: `gh auth login -h github.com -s repo -s read:org -w`
|
|
7
7
|
3. `npx komplian onboard --yes` (sin `@versión`: usa `latest` del registry público).
|
|
8
8
|
|
|
9
|
-
**Orden típico del equipo:**
|
|
9
|
+
**Orden típico del equipo:** **`npx komplian setup`** (todo en uno; formulario local en navegador si hace falta) **o** paso a paso: onboard → **`postman --yes`** → `mcp-tools --yes` → **`db:all:dev`** → `localhost --yes`. Ver **`ONBOARDING.md`**.
|
|
10
10
|
|
|
11
|
-
**`npm ERR! ETARGET` / “No matching version”** (también si falló **`postman`**, no solo `onboard`): es el mismo paquete npm. Usa **`npx komplian postman
|
|
11
|
+
**`npm ERR! ETARGET` / “No matching version”** (también si falló **`postman`**, no solo `onboard`): es el mismo paquete npm. Usa **`npx komplian postman --yes`** **sin** `@0.4.x`; comprueba `npm config get registry` → `https://registry.npmjs.org/`; `npm cache clean --force`. Detalle: `ONBOARDING.md` en el monorepo.
|
|
12
12
|
|
|
13
13
|
### Postman (colección + entornos)
|
|
14
14
|
|
|
15
15
|
1. En [Postman](https://postman.com) usa una cuenta con email **`@komplian.com`** (o añade ese email a tu perfil).
|
|
16
16
|
2. Crea una **API key**: Settings → **API keys** → Generate.
|
|
17
|
-
3. **
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
npx komplian postman login
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
4. Cuando quieras sincronizar la colección:
|
|
17
|
+
3. **Sincronizar** (la primera vez en terminal interactiva **te pide** la API key y la guarda en `~/.komplian/postman-api-key`; no hace falta `export` después):
|
|
24
18
|
|
|
25
19
|
```bash
|
|
26
20
|
npx komplian postman --yes
|
|
27
21
|
```
|
|
28
22
|
|
|
23
|
+
Opcional: **`npx komplian postman login`** solo para guardar o rotar la clave sin ejecutar el sync.
|
|
24
|
+
|
|
29
25
|
Opcional: `export POSTMAN_API_KEY=…` solo para la sesión actual (tiene prioridad sobre el archivo).
|
|
30
26
|
|
|
31
27
|
El comando llama a `GET https://api.getpostman.com/me` y **solo continúa** si el email de la cuenta es `@komplian.com`. **Si ya existen** la colección **Komplian API** y los entornos con el mismo nombre en ese workspace, se **actualizan**; si no, se crean.
|
package/komplian-onboard.mjs
CHANGED
|
@@ -394,10 +394,11 @@ function npmInstallEach(workspace) {
|
|
|
394
394
|
}
|
|
395
395
|
|
|
396
396
|
function usage() {
|
|
397
|
-
log(`Uso: komplian onboard | postman | mcp-tools | db:all:dev | localhost | db …`);
|
|
398
|
-
log(` ${c.bold}
|
|
397
|
+
log(`Uso: komplian setup | onboard | postman | mcp-tools | db:all:dev | localhost | db …`);
|
|
398
|
+
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}`);
|
|
399
|
+
log(` ${c.bold}Setup por pasos:${c.reset}`);
|
|
399
400
|
log(` 1. npx komplian onboard --yes`);
|
|
400
|
-
log(` 2. npx komplian postman
|
|
401
|
+
log(` 2. npx komplian postman --yes ${c.dim}(@komplian.com)${c.reset}`);
|
|
401
402
|
log(` 3. npx komplian mcp-tools --yes`);
|
|
402
403
|
log(` 4. npx komplian db:all:dev ${c.dim}(Postman + 3 URLs dev → ~/.komplian + .env.local)${c.reset}`);
|
|
403
404
|
log(` 5. npx komplian localhost --yes`);
|
|
@@ -486,6 +487,11 @@ function normalizeArgv(argv) {
|
|
|
486
487
|
|
|
487
488
|
async function main() {
|
|
488
489
|
const rawArgv = process.argv.slice(2);
|
|
490
|
+
if (rawArgv[0] === "setup") {
|
|
491
|
+
const { runSetup } = await import("./komplian-setup.mjs");
|
|
492
|
+
await runSetup(rawArgv.slice(1));
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
489
495
|
if (rawArgv[0] === "postman") {
|
|
490
496
|
const { runPostman } = await import("./komplian-postman.mjs");
|
|
491
497
|
await runPostman(rawArgv.slice(1));
|
package/komplian-postman.mjs
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Komplian Postman — API key + email @komplian.com (Postman /me), luego colección + entornos.
|
|
4
4
|
*
|
|
5
|
-
* Requisitos: Node 18+. Primera vez: npx komplian postman
|
|
5
|
+
* Requisitos: Node 18+. Primera vez: ejecuta `npx komplian postman --yes` (o `postman` sin clave):
|
|
6
|
+
* en terminal interactiva te pide la API key una vez y la guarda; o `npx komplian postman login`.
|
|
6
7
|
*
|
|
7
|
-
* Clave: POSTMAN_API_KEY
|
|
8
|
+
* Clave: POSTMAN_API_KEY, ~/.komplian/postman-api-key, o prompt la primera vez (TTY).
|
|
8
9
|
* Seguridad: no registrar secretos; errores API redactados; login sin eco (TTY); ~/.komplian 700 + key 600.
|
|
9
10
|
* Opcional: POSTMAN_WORKSPACE_ID, KOMPLIAN_EMAIL_DOMAIN, KOMPLIAN_POSTMAN_KEY_FILE, KOMPLIAN_DEBUG
|
|
10
11
|
*/
|
|
@@ -215,7 +216,7 @@ function defaultKeyPath() {
|
|
|
215
216
|
/**
|
|
216
217
|
* Orden: POSTMAN_API_KEY → ~/.komplian/postman-api-key (o KOMPLIAN_POSTMAN_KEY_FILE).
|
|
217
218
|
*/
|
|
218
|
-
function resolveApiKey() {
|
|
219
|
+
export function resolveApiKey() {
|
|
219
220
|
const env = process.env.POSTMAN_API_KEY?.trim();
|
|
220
221
|
if (env) return { key: env, source: "POSTMAN_API_KEY" };
|
|
221
222
|
const path = defaultKeyPath();
|
|
@@ -226,20 +227,90 @@ function resolveApiKey() {
|
|
|
226
227
|
return { key: "", source: null };
|
|
227
228
|
}
|
|
228
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
|
+
|
|
229
245
|
function printMissingKeyHelp() {
|
|
230
246
|
log(`${c.red}✗${c.reset} No hay API key de Postman.`);
|
|
231
247
|
log(``);
|
|
232
|
-
log(` ${c.bold}
|
|
233
|
-
log(
|
|
234
|
-
|
|
248
|
+
log(` ${c.bold}Terminal interactiva (primera vez):${c.reset}`);
|
|
249
|
+
log(
|
|
250
|
+
` ${c.cyan}npx komplian postman --yes${c.reset} (pide la clave y sigue con el sync)`
|
|
251
|
+
);
|
|
252
|
+
log(
|
|
253
|
+
` ${c.cyan}npx komplian postman login${c.reset} (solo guardar clave; luego postman --yes)`
|
|
254
|
+
);
|
|
235
255
|
log(``);
|
|
236
|
-
log(` ${c.
|
|
256
|
+
log(` ${c.bold}Sin TTY (CI, pipes):${c.reset}`);
|
|
257
|
+
log(` ${c.dim}export POSTMAN_API_KEY=…${c.reset}`);
|
|
237
258
|
log(
|
|
238
|
-
`${c.dim} Archivo
|
|
259
|
+
`${c.dim} Archivo: ${formatHomePath(defaultKeyPath())} (permiso 600)${c.reset}`
|
|
239
260
|
);
|
|
240
261
|
process.exit(1);
|
|
241
262
|
}
|
|
242
263
|
|
|
264
|
+
/**
|
|
265
|
+
* POSTMAN_API_KEY o ~/.komplian/postman-api-key. Si falta y hay TTY, pide la clave
|
|
266
|
+
* (misma validación que `postman login`), guarda y devuelve la resolución.
|
|
267
|
+
*/
|
|
268
|
+
async function resolveApiKeyWithOptionalInteractiveSetup(domain) {
|
|
269
|
+
let r = resolveApiKey();
|
|
270
|
+
if (r.key) return r;
|
|
271
|
+
if (!input.isTTY) {
|
|
272
|
+
printMissingKeyHelp();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
log(`${c.cyan}━━ Primera vez: Postman API key ━━${c.reset}`);
|
|
276
|
+
log(
|
|
277
|
+
`${c.dim}Cuenta @${domain}. Postman → Settings → API keys → Generate. Se guarda en ${formatHomePath(defaultKeyPath())}.${c.reset}`
|
|
278
|
+
);
|
|
279
|
+
log("");
|
|
280
|
+
|
|
281
|
+
const raw = await readPasswordLine(
|
|
282
|
+
"Postman API key (no se muestra al escribir; Settings → API keys): "
|
|
283
|
+
);
|
|
284
|
+
const k = (raw || "").trim();
|
|
285
|
+
if (!k) {
|
|
286
|
+
log(`${c.red}✗${c.reset} Clave vacía.`);
|
|
287
|
+
process.exit(1);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
await verifyKomplianEmail(k, domain, { quietSuccess: true });
|
|
291
|
+
|
|
292
|
+
const keyPath = defaultKeyPath();
|
|
293
|
+
ensureSecureKomplianDir();
|
|
294
|
+
writeFileSync(keyPath, `${k}\n`, { encoding: "utf8", mode: 0o600 });
|
|
295
|
+
try {
|
|
296
|
+
chmodSync(keyPath, 0o600);
|
|
297
|
+
} catch {
|
|
298
|
+
/* Windows u otros */
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
log("");
|
|
302
|
+
log(
|
|
303
|
+
`${c.green}✓${c.reset} Guardada en ${c.bold}${formatHomePath(keyPath)}${c.reset} — continuando…`
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
r = resolveApiKey();
|
|
307
|
+
if (!r.key) {
|
|
308
|
+
log(`${c.red}✗${c.reset} No se pudo leer la clave guardada.`);
|
|
309
|
+
process.exit(1);
|
|
310
|
+
}
|
|
311
|
+
return r;
|
|
312
|
+
}
|
|
313
|
+
|
|
243
314
|
/** Parseo mínimo .env (sin dependencia dotenv). */
|
|
244
315
|
function parseEnvFile(text) {
|
|
245
316
|
const out = {};
|
|
@@ -600,7 +671,34 @@ async function pickWorkspaceId(apiKey, explicit) {
|
|
|
600
671
|
return w.id;
|
|
601
672
|
}
|
|
602
673
|
|
|
603
|
-
|
|
674
|
+
/** Validación sin `process.exit` (p. ej. formulario web local en `komplian setup`). */
|
|
675
|
+
export async function validatePostmanApiKeyForKomplian(apiKey, domain) {
|
|
676
|
+
const d = (domain || "komplian.com").trim().replace(/^@/, "");
|
|
677
|
+
const { ok, status, body } = await pmFetch(apiKey, "/me");
|
|
678
|
+
if (!ok) {
|
|
679
|
+
return {
|
|
680
|
+
ok: false,
|
|
681
|
+
error: `Postman rechazó la clave (HTTP ${status}).`,
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
const email = extractEmail(body);
|
|
685
|
+
if (!email) {
|
|
686
|
+
return {
|
|
687
|
+
ok: false,
|
|
688
|
+
error: "La API de Postman no devolvió email en /me.",
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
if (!emailAllowed(email, d)) {
|
|
692
|
+
return {
|
|
693
|
+
ok: false,
|
|
694
|
+
error: `Se requiere una cuenta con email @${d}.`,
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
return { ok: true };
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
async function verifyKomplianEmail(apiKey, domain, opts = {}) {
|
|
701
|
+
const quietOk = opts.quietSuccess === true;
|
|
604
702
|
const { ok, status, body } = await pmFetch(apiKey, "/me");
|
|
605
703
|
if (!ok) {
|
|
606
704
|
log(
|
|
@@ -624,9 +722,11 @@ async function verifyKomplianEmail(apiKey, domain) {
|
|
|
624
722
|
);
|
|
625
723
|
process.exit(1);
|
|
626
724
|
}
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
725
|
+
if (!quietOk) {
|
|
726
|
+
log(
|
|
727
|
+
`${c.green}✓${c.reset} Postman: ${c.bold}${maskEmail(email)}${c.reset} (${c.dim}dominio permitido${c.reset})`
|
|
728
|
+
);
|
|
729
|
+
}
|
|
630
730
|
return email;
|
|
631
731
|
}
|
|
632
732
|
|
|
@@ -759,7 +859,7 @@ function usage() {
|
|
|
759
859
|
log(` Clave: ${c.dim}POSTMAN_API_KEY${c.reset} o archivo ${c.dim}~/.komplian/postman-api-key${c.reset} (véase ${c.cyan}postman login${c.reset})`);
|
|
760
860
|
log(` Dominio email: solo @komplian.com (GET /me)`);
|
|
761
861
|
log(``);
|
|
762
|
-
log(` -y, --yes Sin prompts extra`);
|
|
862
|
+
log(` -y, --yes Sin prompts extra (si falta API key y hay TTY, pide la clave una vez y guarda)`);
|
|
763
863
|
log(` --export-only Solo escribe JSON en disco (no llama a la API de Postman)`);
|
|
764
864
|
log(` --out <dir> Carpeta para export (por defecto: ./komplian-postman)`);
|
|
765
865
|
log(` --dotenv <ruta> .env extra (además de api/.env, .env, KOMPLIAN_DOTENV)`);
|
|
@@ -771,8 +871,8 @@ function usage() {
|
|
|
771
871
|
|
|
772
872
|
function usageLogin() {
|
|
773
873
|
log(`Uso: komplian postman login`);
|
|
774
|
-
log(` Guarda la API key
|
|
775
|
-
log(`
|
|
874
|
+
log(` Guarda o sustituye la API key en ${c.dim}~/.komplian/postman-api-key${c.reset} (permiso 600).`);
|
|
875
|
+
log(` Opcional: ${c.cyan}npx komplian postman --yes${c.reset} ya pide la clave la primera vez si falta.`);
|
|
776
876
|
log(``);
|
|
777
877
|
log(` Postman → Settings (avatar) → API keys → Generate`);
|
|
778
878
|
}
|
|
@@ -841,15 +941,13 @@ export async function runPostman(argv) {
|
|
|
841
941
|
return;
|
|
842
942
|
}
|
|
843
943
|
|
|
844
|
-
const
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
}
|
|
944
|
+
const domain = (process.env.KOMPLIAN_EMAIL_DOMAIN || "komplian.com").trim();
|
|
945
|
+
const { key: apiKey, source } =
|
|
946
|
+
await resolveApiKeyWithOptionalInteractiveSetup(domain);
|
|
848
947
|
if (source && source !== "POSTMAN_API_KEY") {
|
|
849
948
|
log(`${c.dim}→ API key Postman desde: ${formatHomePath(source)}${c.reset}`);
|
|
850
949
|
}
|
|
851
950
|
|
|
852
|
-
const domain = (process.env.KOMPLIAN_EMAIL_DOMAIN || "komplian.com").trim();
|
|
853
951
|
await verifyKomplianEmail(apiKey, domain);
|
|
854
952
|
|
|
855
953
|
const collection = buildCollection();
|
|
@@ -940,16 +1038,12 @@ export async function runPostman(argv) {
|
|
|
940
1038
|
}
|
|
941
1039
|
|
|
942
1040
|
/**
|
|
943
|
-
*
|
|
944
|
-
* Requiere `npx komplian postman login` (o POSTMAN_API_KEY en la sesión).
|
|
1041
|
+
* GET /me y dominio @komplian.com. Si no hay clave y hay TTY, pide guardarla (como `postman --yes`).
|
|
945
1042
|
*/
|
|
946
1043
|
export async function assertKomplianEmployeePostman() {
|
|
947
1044
|
const domain = (process.env.KOMPLIAN_EMAIL_DOMAIN || "komplian.com").trim();
|
|
948
|
-
const { key, source } =
|
|
949
|
-
|
|
950
|
-
printMissingKeyHelp();
|
|
951
|
-
process.exit(1);
|
|
952
|
-
}
|
|
1045
|
+
const { key, source } =
|
|
1046
|
+
await resolveApiKeyWithOptionalInteractiveSetup(domain);
|
|
953
1047
|
if (source && source !== "POSTMAN_API_KEY") {
|
|
954
1048
|
log(`${c.dim}→ Postman API key desde: ${formatHomePath(source)}${c.reset}`);
|
|
955
1049
|
}
|
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Komplian setup — un solo comando: onboard → postman → mcp-tools → db:all:dev → localhost.
|
|
4
|
+
* Por defecto abre el navegador en localhost para datos sensibles (Postman API key, URLs Neon dev)
|
|
5
|
+
* con texto de ayuda; alternativa: --terminal-only (sin navegador).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { spawnSync } from "node:child_process";
|
|
9
|
+
import { createServer } from "node:http";
|
|
10
|
+
import { randomBytes } from "node:crypto";
|
|
11
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
12
|
+
import { dirname, join, resolve } from "node:path";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
import { homedir } from "node:os";
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
resolveApiKey,
|
|
18
|
+
validatePostmanApiKeyForKomplian,
|
|
19
|
+
savePostmanApiKeyToKomplianHome,
|
|
20
|
+
runPostman,
|
|
21
|
+
} from "./komplian-postman.mjs";
|
|
22
|
+
import { runDbAllDev } from "./komplian-db-all-dev.mjs";
|
|
23
|
+
import { runMcpTools } from "./komplian-mcp-tools.mjs";
|
|
24
|
+
import { runLocalhost, findWorkspaceRoot } from "./komplian-localhost.mjs";
|
|
25
|
+
import { loadHomeDevDatabases } from "./komplian-db.mjs";
|
|
26
|
+
|
|
27
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
28
|
+
const IS_WIN = process.platform === "win32";
|
|
29
|
+
|
|
30
|
+
function spawnWin(extra = {}) {
|
|
31
|
+
return IS_WIN ? { ...extra, shell: true } : extra;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const c = {
|
|
35
|
+
reset: "\x1b[0m",
|
|
36
|
+
dim: "\x1b[2m",
|
|
37
|
+
bold: "\x1b[1m",
|
|
38
|
+
cyan: "\x1b[36m",
|
|
39
|
+
green: "\x1b[32m",
|
|
40
|
+
red: "\x1b[31m",
|
|
41
|
+
yellow: "\x1b[33m",
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
function log(s = "") {
|
|
45
|
+
console.log(s);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const PLACEHOLDER = "komplian_localhost_placeholder";
|
|
49
|
+
|
|
50
|
+
function isValidPostgresUrl(s) {
|
|
51
|
+
const t = (s || "").trim();
|
|
52
|
+
if (!t.startsWith("postgresql://") && !t.startsWith("postgres://")) return false;
|
|
53
|
+
if (t.includes(PLACEHOLDER)) return false;
|
|
54
|
+
try {
|
|
55
|
+
const u = new URL(t);
|
|
56
|
+
return !!u.hostname;
|
|
57
|
+
} catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function isValidTriplet(d) {
|
|
63
|
+
return (
|
|
64
|
+
isValidPostgresUrl(d.app) &&
|
|
65
|
+
isValidPostgresUrl(d.admin) &&
|
|
66
|
+
isValidPostgresUrl(d.web)
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function collectUrlsFromEnv() {
|
|
71
|
+
return {
|
|
72
|
+
app: process.env.KOMPLIAN_DEV_APP_DATABASE_URL?.trim() || "",
|
|
73
|
+
admin: process.env.KOMPLIAN_DEV_ADMIN_DATABASE_URL?.trim() || "",
|
|
74
|
+
web: process.env.KOMPLIAN_DEV_WEB_DATABASE_URL?.trim() || "",
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function openBrowserSync(url) {
|
|
79
|
+
try {
|
|
80
|
+
if (process.platform === "darwin") {
|
|
81
|
+
spawnSync("open", [url], { stdio: "ignore" });
|
|
82
|
+
} else if (process.platform === "win32") {
|
|
83
|
+
spawnSync("cmd", ["/c", "start", "", url], { ...spawnWin(), stdio: "ignore" });
|
|
84
|
+
} else {
|
|
85
|
+
spawnSync("xdg-open", [url], { stdio: "ignore" });
|
|
86
|
+
}
|
|
87
|
+
} catch {
|
|
88
|
+
log(`${c.yellow}○${c.reset} No se pudo abrir el navegador. Abre manualmente: ${c.bold}${url}${c.reset}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function buildSetupPageHtml(token, { needsPostmanKey, needsDbUrls, emailDomain }) {
|
|
93
|
+
const esc = (s) =>
|
|
94
|
+
String(s)
|
|
95
|
+
.replace(/&/g, "&")
|
|
96
|
+
.replace(/</g, "<")
|
|
97
|
+
.replace(/"/g, """);
|
|
98
|
+
const postmanBlock = needsPostmanKey
|
|
99
|
+
? `
|
|
100
|
+
<section class="card">
|
|
101
|
+
<h2>1. Postman API key</h2>
|
|
102
|
+
<p class="help">Crea una clave en Postman → <strong>Settings</strong> → <strong>API keys</strong> → Generate. Cuenta con email <strong>@${esc(emailDomain)}</strong>. Se guarda en <code>~/.komplian/postman-api-key</code>. Si la dejas vacía, la terminal te la pedirá en el paso Postman.</p>
|
|
103
|
+
<label for="postman_api_key">API key (opcional aquí)</label>
|
|
104
|
+
<input type="password" id="postman_api_key" name="postman_api_key" autocomplete="off" placeholder="PMAK-…" />
|
|
105
|
+
</section>`
|
|
106
|
+
: "";
|
|
107
|
+
const dbBlock = needsDbUrls
|
|
108
|
+
? `
|
|
109
|
+
<section class="card">
|
|
110
|
+
<h2>${needsPostmanKey ? "2" : "1"}. Bases de datos (solo desarrollo)</h2>
|
|
111
|
+
<p class="help">Pega las connection strings <strong>postgresql://…</strong> de Neon (rama <em>development</em> u homólogo). Tres bases: <strong>app</strong> (también usa la API), <strong>admin</strong> y <strong>web</strong> (pilot). Se escriben en <code>~/.komplian/dev-databases.json</code> y en cada <code>.env.local</code> del monorepo.</p>
|
|
112
|
+
<label for="db_app">URL APP (app + API)</label>
|
|
113
|
+
<textarea id="db_app" name="db_app" rows="2" placeholder="postgresql://…"></textarea>
|
|
114
|
+
<label for="db_admin">URL ADMIN</label>
|
|
115
|
+
<textarea id="db_admin" name="db_admin" rows="2" placeholder="postgresql://…"></textarea>
|
|
116
|
+
<label for="db_web">URL WEB (pilot)</label>
|
|
117
|
+
<textarea id="db_web" name="db_web" rows="2" placeholder="postgresql://…"></textarea>
|
|
118
|
+
</section>`
|
|
119
|
+
: "";
|
|
120
|
+
return `<!DOCTYPE html>
|
|
121
|
+
<html lang="es">
|
|
122
|
+
<head>
|
|
123
|
+
<meta charset="utf-8" />
|
|
124
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
125
|
+
<title>Komplian setup</title>
|
|
126
|
+
<style>
|
|
127
|
+
:root { font-family: system-ui, sans-serif; background: #0a0a0a; color: #e5e5e5; }
|
|
128
|
+
body { max-width: 640px; margin: 2rem auto; padding: 0 1rem; }
|
|
129
|
+
h1 { font-size: 1.25rem; font-weight: 600; }
|
|
130
|
+
.card { background: #171717; border: 1px solid #262626; border-radius: 10px; padding: 1.25rem; margin-bottom: 1.25rem; }
|
|
131
|
+
.help { color: #a3a3a3; font-size: 0.9rem; line-height: 1.5; margin: 0 0 1rem; }
|
|
132
|
+
label { display: block; font-size: 0.8rem; color: #737373; margin: 0.75rem 0 0.35rem; }
|
|
133
|
+
input, textarea { width: 100%; box-sizing: border-box; padding: 0.6rem 0.75rem; border-radius: 8px; border: 1px solid #404040; background: #0a0a0a; color: #fafafa; font-family: ui-monospace, monospace; font-size: 0.85rem; }
|
|
134
|
+
button { margin-top: 1.25rem; width: 100%; padding: 0.85rem; border: none; border-radius: 8px; background: #fafafa; color: #0a0a0a; font-weight: 600; cursor: pointer; font-size: 1rem; }
|
|
135
|
+
button:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
136
|
+
.err { color: #f87171; font-size: 0.9rem; margin-top: 0.75rem; white-space: pre-wrap; }
|
|
137
|
+
.ok { color: #4ade80; margin-top: 1rem; }
|
|
138
|
+
code { font-size: 0.8em; background: #262626; padding: 0.1em 0.35em; border-radius: 4px; }
|
|
139
|
+
</style>
|
|
140
|
+
</head>
|
|
141
|
+
<body>
|
|
142
|
+
<h1>Komplian — asistente de setup</h1>
|
|
143
|
+
<p class="help">Esta ventana es local (solo tu ordenador). Rellena lo que pida el asistente y pulsa <strong>Continuar</strong>. Puedes cerrar la pestaña después del mensaje de éxito.</p>
|
|
144
|
+
${postmanBlock}
|
|
145
|
+
${dbBlock}
|
|
146
|
+
<div id="err" class="err" style="display:none"></div>
|
|
147
|
+
<div id="ok" class="ok" style="display:none"></div>
|
|
148
|
+
<button type="button" id="go">Continuar</button>
|
|
149
|
+
<script>
|
|
150
|
+
const TOKEN = ${JSON.stringify(token)};
|
|
151
|
+
const needsPostman = ${JSON.stringify(needsPostmanKey)};
|
|
152
|
+
const needsDb = ${JSON.stringify(needsDbUrls)};
|
|
153
|
+
document.getElementById("go").onclick = async () => {
|
|
154
|
+
const err = document.getElementById("err");
|
|
155
|
+
const ok = document.getElementById("ok");
|
|
156
|
+
err.style.display = "none";
|
|
157
|
+
ok.style.display = "none";
|
|
158
|
+
const body = { token: TOKEN };
|
|
159
|
+
if (needsPostman) body.postman_api_key = (document.getElementById("postman_api_key") || {}).value || "";
|
|
160
|
+
if (needsDb) {
|
|
161
|
+
body.db_app = (document.getElementById("db_app") || {}).value || "";
|
|
162
|
+
body.db_admin = (document.getElementById("db_admin") || {}).value || "";
|
|
163
|
+
body.db_web = (document.getElementById("db_web") || {}).value || "";
|
|
164
|
+
}
|
|
165
|
+
const btn = document.getElementById("go");
|
|
166
|
+
btn.disabled = true;
|
|
167
|
+
try {
|
|
168
|
+
const r = await fetch("/submit", {
|
|
169
|
+
method: "POST",
|
|
170
|
+
headers: { "Content-Type": "application/json" },
|
|
171
|
+
body: JSON.stringify(body),
|
|
172
|
+
});
|
|
173
|
+
const j = await r.json().catch(() => ({}));
|
|
174
|
+
if (!r.ok || !j.ok) {
|
|
175
|
+
err.textContent = j.error || "Error al validar. Revisa los datos.";
|
|
176
|
+
err.style.display = "block";
|
|
177
|
+
btn.disabled = false;
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
ok.textContent = "Listo. Vuelve a la terminal; puedes cerrar esta pestaña.";
|
|
181
|
+
ok.style.display = "block";
|
|
182
|
+
} catch (e) {
|
|
183
|
+
err.textContent = "No se pudo enviar. ¿Sigues en la misma red local?";
|
|
184
|
+
err.style.display = "block";
|
|
185
|
+
btn.disabled = false;
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
</script>
|
|
189
|
+
</body>
|
|
190
|
+
</html>`;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Servidor solo en 127.0.0.1. Devuelve { postmanKey?, db } según lo pedido.
|
|
195
|
+
*/
|
|
196
|
+
function runSetupBrowserForm(opts) {
|
|
197
|
+
const {
|
|
198
|
+
needsPostmanKey,
|
|
199
|
+
needsDbUrls,
|
|
200
|
+
emailDomain,
|
|
201
|
+
timeoutMs = 600_000,
|
|
202
|
+
} = opts;
|
|
203
|
+
|
|
204
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
205
|
+
const token = randomBytes(24).toString("hex");
|
|
206
|
+
const server = createServer(async (req, res) => {
|
|
207
|
+
const url = new URL(req.url || "/", `http://127.0.0.1`);
|
|
208
|
+
|
|
209
|
+
if (req.method === "GET" && url.pathname === "/") {
|
|
210
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
211
|
+
res.end(
|
|
212
|
+
buildSetupPageHtml(token, {
|
|
213
|
+
needsPostmanKey,
|
|
214
|
+
needsDbUrls,
|
|
215
|
+
emailDomain,
|
|
216
|
+
})
|
|
217
|
+
);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (req.method === "POST" && url.pathname === "/submit") {
|
|
222
|
+
let raw = "";
|
|
223
|
+
for await (const ch of req) raw += ch;
|
|
224
|
+
let data;
|
|
225
|
+
try {
|
|
226
|
+
data = JSON.parse(raw || "{}");
|
|
227
|
+
} catch {
|
|
228
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
229
|
+
res.end(JSON.stringify({ ok: false, error: "JSON inválido." }));
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
if (data.token !== token) {
|
|
233
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
234
|
+
res.end(JSON.stringify({ ok: false, error: "Token inválido." }));
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const out = { postmanKey: "", db: null };
|
|
239
|
+
|
|
240
|
+
if (needsPostmanKey) {
|
|
241
|
+
const pk = String(data.postman_api_key || "").trim();
|
|
242
|
+
if (pk) {
|
|
243
|
+
const v = await validatePostmanApiKeyForKomplian(pk, emailDomain);
|
|
244
|
+
if (!v.ok) {
|
|
245
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
246
|
+
res.end(JSON.stringify({ ok: false, error: v.error || "Postman inválido." }));
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
out.postmanKey = pk;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (needsDbUrls) {
|
|
254
|
+
const triplet = {
|
|
255
|
+
app: String(data.db_app || "").trim(),
|
|
256
|
+
admin: String(data.db_admin || "").trim(),
|
|
257
|
+
web: String(data.db_web || "").trim(),
|
|
258
|
+
};
|
|
259
|
+
if (!isValidTriplet(triplet)) {
|
|
260
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
261
|
+
res.end(
|
|
262
|
+
JSON.stringify({
|
|
263
|
+
ok: false,
|
|
264
|
+
error:
|
|
265
|
+
"Las tres URLs deben ser postgresql:// o postgres:// válidas (sin placeholder).",
|
|
266
|
+
})
|
|
267
|
+
);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
out.db = triplet;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
274
|
+
res.end(JSON.stringify({ ok: true }));
|
|
275
|
+
|
|
276
|
+
server.close(() => {
|
|
277
|
+
clearTimeout(timer);
|
|
278
|
+
resolvePromise(out);
|
|
279
|
+
});
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
res.writeHead(404);
|
|
284
|
+
res.end();
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
const timer = setTimeout(() => {
|
|
288
|
+
server.close();
|
|
289
|
+
rejectPromise(new Error("Tiempo agotado esperando el formulario en el navegador."));
|
|
290
|
+
}, timeoutMs);
|
|
291
|
+
|
|
292
|
+
server.listen(0, "127.0.0.1", () => {
|
|
293
|
+
const addr = server.address();
|
|
294
|
+
const port = typeof addr === "object" && addr ? addr.port : 0;
|
|
295
|
+
const openUrl = `http://127.0.0.1:${port}/`;
|
|
296
|
+
log("");
|
|
297
|
+
log(
|
|
298
|
+
`${c.cyan}━━ Navegador (formulario local) ━━${c.reset} ${c.bold}${openUrl}${c.reset}`
|
|
299
|
+
);
|
|
300
|
+
log(
|
|
301
|
+
`${c.dim}Solo escucha en tu máquina (127.0.0.1). Cierra la pestaña tras el mensaje de éxito.${c.reset}`
|
|
302
|
+
);
|
|
303
|
+
openBrowserSync(openUrl);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
server.on("error", (e) => {
|
|
307
|
+
clearTimeout(timer);
|
|
308
|
+
rejectPromise(e);
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function parseArgs(argv) {
|
|
314
|
+
const out = {
|
|
315
|
+
terminalOnly: false,
|
|
316
|
+
workspace: "",
|
|
317
|
+
help: false,
|
|
318
|
+
team: "",
|
|
319
|
+
ssh: false,
|
|
320
|
+
allRepos: false,
|
|
321
|
+
noInstall: false,
|
|
322
|
+
};
|
|
323
|
+
for (let i = 0; i < argv.length; i++) {
|
|
324
|
+
const a = argv[i];
|
|
325
|
+
if (a === "--terminal-only" || a === "--no-browser")
|
|
326
|
+
out.terminalOnly = true;
|
|
327
|
+
else if (a === "-w" || a === "--workspace") out.workspace = argv[++i] || "";
|
|
328
|
+
else if (a === "-t" || a === "--team") out.team = argv[++i] || "";
|
|
329
|
+
else if (a === "--ssh") out.ssh = true;
|
|
330
|
+
else if (a === "--all-repos") out.allRepos = true;
|
|
331
|
+
else if (a === "--no-install") out.noInstall = true;
|
|
332
|
+
else if (a === "-h" || a === "--help") out.help = true;
|
|
333
|
+
else if (a.startsWith("-")) {
|
|
334
|
+
log(`${c.red}✗${c.reset} Opción desconocida: ${a}`);
|
|
335
|
+
process.exit(1);
|
|
336
|
+
} else if (!out.workspace) out.workspace = a;
|
|
337
|
+
}
|
|
338
|
+
return out;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function usage() {
|
|
342
|
+
log(`Uso: npx komplian setup [opciones] [carpeta-workspace]`);
|
|
343
|
+
log(``);
|
|
344
|
+
log(
|
|
345
|
+
` Orden: ${c.bold}onboard${c.reset} → ${c.bold}postman${c.reset} → ${c.bold}mcp-tools${c.reset} → ${c.bold}db:all:dev${c.reset} → ${c.bold}localhost${c.reset}`
|
|
346
|
+
);
|
|
347
|
+
log(
|
|
348
|
+
` Por defecto abre el ${c.bold}navegador${c.reset} para Postman API key (si falta) y las 3 URLs Neon dev (si faltan).`
|
|
349
|
+
);
|
|
350
|
+
log(``);
|
|
351
|
+
log(` --terminal-only Sin formulario web; postman/db usan terminal o env`);
|
|
352
|
+
log(` -w, --workspace Raíz del monorepo (destino del clone)`);
|
|
353
|
+
log(` -t, --team Equipo (komplian-team-repos.json)`);
|
|
354
|
+
log(` --all-repos Clonar todos los repos del JSON`);
|
|
355
|
+
log(` --ssh Clonar por SSH`);
|
|
356
|
+
log(` --no-install No npm install tras clone`);
|
|
357
|
+
log(` -h, --help`);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function runOnboardChild(args) {
|
|
361
|
+
const script = join(__dirname, "komplian-onboard.mjs");
|
|
362
|
+
const argv = ["onboard", ...args];
|
|
363
|
+
const r = spawnSync(process.execPath, [script, ...argv], {
|
|
364
|
+
stdio: "inherit",
|
|
365
|
+
windowsHide: true,
|
|
366
|
+
});
|
|
367
|
+
return r.status === 0;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
export async function runSetup(argv) {
|
|
371
|
+
const opts = parseArgs(argv);
|
|
372
|
+
if (opts.help) {
|
|
373
|
+
usage();
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const nodeMajor = Number(process.versions.node.split(".")[0], 10);
|
|
378
|
+
if (nodeMajor < 18) {
|
|
379
|
+
log(`${c.red}✗${c.reset} Hace falta Node 18+.`);
|
|
380
|
+
process.exit(1);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
let workspaceArg = (opts.workspace || "").trim();
|
|
384
|
+
if (!workspaceArg) workspaceArg = process.cwd();
|
|
385
|
+
const workspaceAbs = resolve(
|
|
386
|
+
workspaceArg.replace(/^~(?=$|[/\\])/, homedir())
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
mkdirSync(workspaceAbs, { recursive: true });
|
|
390
|
+
|
|
391
|
+
log(`${c.cyan}${c.bold}━━ Komplian setup ━━${c.reset}`);
|
|
392
|
+
log(`${c.dim}Workspace:${c.reset} ${workspaceAbs}`);
|
|
393
|
+
log("");
|
|
394
|
+
|
|
395
|
+
const onboardArgs = ["--yes"];
|
|
396
|
+
if (opts.team) onboardArgs.push("-t", opts.team);
|
|
397
|
+
if (opts.allRepos) onboardArgs.push("--all-repos");
|
|
398
|
+
if (opts.ssh) onboardArgs.push("--ssh");
|
|
399
|
+
if (opts.noInstall) onboardArgs.push("--no-install");
|
|
400
|
+
onboardArgs.push(workspaceAbs);
|
|
401
|
+
|
|
402
|
+
log(`${c.cyan}━━ 1/5 Onboard ━━${c.reset}`);
|
|
403
|
+
if (!runOnboardChild(onboardArgs)) {
|
|
404
|
+
log(`${c.red}✗${c.reset} Onboard falló. Revisa GitHub CLI (gh) y permisos.`);
|
|
405
|
+
process.exit(1);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
let monorepoRoot = workspaceAbs;
|
|
409
|
+
if (!existsSync(join(monorepoRoot, "api", "package.json"))) {
|
|
410
|
+
monorepoRoot = findWorkspaceRoot(workspaceAbs);
|
|
411
|
+
}
|
|
412
|
+
if (!existsSync(join(monorepoRoot, "api", "package.json"))) {
|
|
413
|
+
log(
|
|
414
|
+
`${c.red}✗${c.reset} No se encontró monorepo con api/package.json bajo ${workspaceAbs}.`
|
|
415
|
+
);
|
|
416
|
+
process.exit(1);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
process.chdir(monorepoRoot);
|
|
420
|
+
|
|
421
|
+
const emailDomain = (process.env.KOMPLIAN_EMAIL_DOMAIN || "komplian.com").trim();
|
|
422
|
+
const useBrowser =
|
|
423
|
+
!opts.terminalOnly && process.env.KOMPLIAN_SETUP_NO_BROWSER !== "1";
|
|
424
|
+
|
|
425
|
+
const envTriplet = collectUrlsFromEnv();
|
|
426
|
+
const envDbOk = isValidTriplet(envTriplet);
|
|
427
|
+
const saved = loadHomeDevDatabases();
|
|
428
|
+
const savedDbOk = saved && isValidTriplet(saved);
|
|
429
|
+
const { key: existingPmKey } = resolveApiKey();
|
|
430
|
+
const needsPostmanKey = !existingPmKey;
|
|
431
|
+
const needsDbUrls = !envDbOk && !savedDbOk;
|
|
432
|
+
|
|
433
|
+
if (useBrowser && (needsPostmanKey || needsDbUrls)) {
|
|
434
|
+
log(`${c.cyan}━━ Formulario en el navegador ━━${c.reset}`);
|
|
435
|
+
try {
|
|
436
|
+
const form = await runSetupBrowserForm({
|
|
437
|
+
needsPostmanKey,
|
|
438
|
+
needsDbUrls,
|
|
439
|
+
emailDomain,
|
|
440
|
+
});
|
|
441
|
+
if (form.postmanKey) {
|
|
442
|
+
savePostmanApiKeyToKomplianHome(form.postmanKey);
|
|
443
|
+
log(`${c.green}✓${c.reset} Postman API key guardada en ~/.komplian/`);
|
|
444
|
+
}
|
|
445
|
+
if (form.db) {
|
|
446
|
+
process.env.KOMPLIAN_DEV_APP_DATABASE_URL = form.db.app;
|
|
447
|
+
process.env.KOMPLIAN_DEV_ADMIN_DATABASE_URL = form.db.admin;
|
|
448
|
+
process.env.KOMPLIAN_DEV_WEB_DATABASE_URL = form.db.web;
|
|
449
|
+
log(`${c.green}✓${c.reset} URLs de desarrollo recibidas desde el formulario.`);
|
|
450
|
+
}
|
|
451
|
+
} catch (e) {
|
|
452
|
+
log(
|
|
453
|
+
`${c.red}✗${c.reset} Formulario web: ${e?.message || e}. Prueba ${c.bold}--terminal-only${c.reset} o define variables de entorno.`
|
|
454
|
+
);
|
|
455
|
+
process.exit(1);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
log(`${c.cyan}━━ 2/5 Postman ━━${c.reset}`);
|
|
460
|
+
await runPostman(["--yes"]);
|
|
461
|
+
|
|
462
|
+
log(`${c.cyan}━━ 3/5 MCP tools ━━${c.reset}`);
|
|
463
|
+
await runMcpTools(["--yes"]);
|
|
464
|
+
|
|
465
|
+
log(`${c.cyan}━━ 4/5 Bases de datos (development) ━━${c.reset}`);
|
|
466
|
+
const dbArgs = ["--yes", "-w", monorepoRoot];
|
|
467
|
+
await runDbAllDev(dbArgs);
|
|
468
|
+
|
|
469
|
+
log(`${c.cyan}━━ 5/5 Localhost ━━${c.reset}`);
|
|
470
|
+
await runLocalhost(["--yes"]);
|
|
471
|
+
|
|
472
|
+
log("");
|
|
473
|
+
log(`${c.green}✓${c.reset} ${c.bold}Setup completado.${c.reset}`);
|
|
474
|
+
log(
|
|
475
|
+
`${c.dim}Monorepo:${c.reset} ${monorepoRoot} ${c.dim}· Cursor: File → Open Folder${c.reset}`
|
|
476
|
+
);
|
|
477
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "komplian",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Komplian CLI: onboard, Postman, localhost, mcp-tools, db (psql). Node 18+.
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "Komplian CLI: setup (todo en uno), onboard, Postman, localhost, mcp-tools, db (psql). Node 18+.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=18"
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
"files": [
|
|
13
13
|
"komplian-onboard.mjs",
|
|
14
|
+
"komplian-setup.mjs",
|
|
14
15
|
"komplian-postman.mjs",
|
|
15
16
|
"komplian-localhost.mjs",
|
|
16
17
|
"komplian-mcp-tools.mjs",
|
|
@@ -23,7 +24,7 @@
|
|
|
23
24
|
"access": "public"
|
|
24
25
|
},
|
|
25
26
|
"scripts": {
|
|
26
|
-
"prepublishOnly": "node --check komplian-onboard.mjs && node --check komplian-postman.mjs && node --check komplian-localhost.mjs && node --check komplian-mcp-tools.mjs && node --check komplian-db.mjs && node --check komplian-db-all-dev.mjs"
|
|
27
|
+
"prepublishOnly": "node --check komplian-onboard.mjs && node --check komplian-setup.mjs && node --check komplian-postman.mjs && node --check komplian-localhost.mjs && node --check komplian-mcp-tools.mjs && node --check komplian-db.mjs && node --check komplian-db-all-dev.mjs"
|
|
27
28
|
},
|
|
28
29
|
"keywords": [
|
|
29
30
|
"komplian",
|