komplian 0.7.1 → 0.7.2
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 +1 -1
- package/komplian-onboard.mjs +220 -116
- package/komplian-postman.mjs +20 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ Opcional: `export POSTMAN_API_KEY=…` solo para la sesión actual (tiene priori
|
|
|
26
26
|
|
|
27
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.
|
|
28
28
|
|
|
29
|
-
**Variables de la API Komplian** (`apiKey`, `adminApiKey`, `workspaceId`, …) se rellenan en Postman automáticamente si están en `process.env` o en archivos `.env` (busca `api/.env`, `.env`, etc.; o `KOMPLIAN_DOTENV` / `--dotenv ruta`). Nombres típicos: `API_KEY`, `ADMIN_API_KEY`, `KOMPLIAN_WORKSPACE_ID`. Los JSON exportados en
|
|
29
|
+
**Variables de la API Komplian** (`apiKey`, `adminApiKey`, `workspaceId`, …) se rellenan en Postman automáticamente si están en `process.env` o en archivos `.env` (busca `api/.env`, `.env`, etc.; o `KOMPLIAN_DOTENV` / `--dotenv ruta`). Nombres típicos: `API_KEY`, `ADMIN_API_KEY`, `KOMPLIAN_WORKSPACE_ID`. Los JSON exportados en `~/.komplian/postman-export/` (o `--out`) **no** incluyen secretos (para no commitearlos).
|
|
30
30
|
|
|
31
31
|
- Solo exportar archivos (sin subir por API): `npx komplian postman --yes --export-only`
|
|
32
32
|
- Otro workspace: `POSTMAN_WORKSPACE_ID=<id>`
|
package/komplian-onboard.mjs
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* npx komplian onboard --yes
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { spawnSync } from "node:child_process";
|
|
11
|
+
import { spawnSync, spawn } from "node:child_process";
|
|
12
12
|
import { existsSync, readFileSync, mkdirSync, cpSync, rmSync, readdirSync, statSync } from "node:fs";
|
|
13
13
|
import { dirname, join, resolve, normalize } from "node:path";
|
|
14
14
|
import { fileURLToPath } from "node:url";
|
|
@@ -41,8 +41,13 @@ function log(s = "") {
|
|
|
41
41
|
console.log(s);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/** Branding and repo progress: always visible (even when KOMPLIAN_CLI_QUIET=1). */
|
|
45
|
+
function ux(s = "") {
|
|
46
|
+
console.log(s);
|
|
47
|
+
}
|
|
48
|
+
|
|
44
49
|
function banner() {
|
|
45
|
-
|
|
50
|
+
ux(
|
|
46
51
|
[
|
|
47
52
|
`${c.cyan}${c.bold}`,
|
|
48
53
|
"██╗ ██╗ ██████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ █████╗ ███╗ ██╗",
|
|
@@ -52,12 +57,52 @@ function banner() {
|
|
|
52
57
|
"██║ ██╗ ╚██████╔╝ ██║ ╚═╝ ██║ ██║ ███████╗ ██║ ██║ ██║ ██║ ╚████║",
|
|
53
58
|
"╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═══╝",
|
|
54
59
|
`${c.reset}`,
|
|
55
|
-
`${c.dim} Secure setup · GitHub CLI · git clone
|
|
60
|
+
`${c.dim} Secure setup · GitHub CLI · git clone${c.reset}`,
|
|
56
61
|
"",
|
|
57
62
|
].join("\n")
|
|
58
63
|
);
|
|
59
64
|
}
|
|
60
65
|
|
|
66
|
+
function startRepoSpinner(repoName) {
|
|
67
|
+
const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
68
|
+
const cloud = `${c.blue}☁${c.reset}`;
|
|
69
|
+
let i = 0;
|
|
70
|
+
const id = setInterval(() => {
|
|
71
|
+
const fr = frames[i++ % frames.length];
|
|
72
|
+
process.stdout.write(
|
|
73
|
+
`\r ${cloud} ${c.cyan}${fr}${c.reset} ${c.bold}${repoName}${c.reset} ${c.dim}…${c.reset} `
|
|
74
|
+
);
|
|
75
|
+
}, 90);
|
|
76
|
+
return {
|
|
77
|
+
stop() {
|
|
78
|
+
clearInterval(id);
|
|
79
|
+
process.stdout.write("\r\x1b[K");
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function runSpawn(cmd, args, cwd) {
|
|
85
|
+
return new Promise((resolve) => {
|
|
86
|
+
const child = spawn(cmd, args, {
|
|
87
|
+
...spawnWin({
|
|
88
|
+
cwd,
|
|
89
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
90
|
+
}),
|
|
91
|
+
});
|
|
92
|
+
let stderr = "";
|
|
93
|
+
child.stderr?.setEncoding?.("utf8");
|
|
94
|
+
child.stderr?.on("data", (d) => {
|
|
95
|
+
stderr += String(d);
|
|
96
|
+
});
|
|
97
|
+
child.on("close", (code) => {
|
|
98
|
+
resolve({ status: code === 0 ? 0 : code ?? 1, stderr });
|
|
99
|
+
});
|
|
100
|
+
child.on("error", (err) => {
|
|
101
|
+
resolve({ status: 1, stderr: String(err?.message || err) });
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
61
106
|
function ghOk() {
|
|
62
107
|
const r = spawnSync(
|
|
63
108
|
"gh",
|
|
@@ -71,8 +116,8 @@ function ghOk() {
|
|
|
71
116
|
}
|
|
72
117
|
|
|
73
118
|
function runGhAuth() {
|
|
74
|
-
|
|
75
|
-
`${c.yellow}→${c.reset}
|
|
119
|
+
ux(
|
|
120
|
+
`${c.yellow}→${c.reset} Opening ${c.bold}GitHub${c.reset} in your browser (OAuth). Your password is not shown here.`
|
|
76
121
|
);
|
|
77
122
|
const r = spawnSync(
|
|
78
123
|
"gh",
|
|
@@ -102,32 +147,32 @@ function verifyOrgMembership(org) {
|
|
|
102
147
|
try {
|
|
103
148
|
const j = JSON.parse(mem.stdout);
|
|
104
149
|
if (j.state === "active") return;
|
|
105
|
-
|
|
106
|
-
`${c.red}✗${c.reset}
|
|
150
|
+
console.error(
|
|
151
|
+
`${c.red}✗${c.reset} Org ${c.bold}${org}${c.reset} membership not active (state: ${j.state || "?"}).`
|
|
107
152
|
);
|
|
108
153
|
process.exit(1);
|
|
109
154
|
} catch {
|
|
110
|
-
|
|
155
|
+
console.error(`${c.red}✗${c.reset} Invalid response while verifying org membership.`);
|
|
111
156
|
process.exit(1);
|
|
112
157
|
}
|
|
113
158
|
}
|
|
114
159
|
const hint = (mem.stderr + mem.stdout).toLowerCase();
|
|
115
160
|
if (hint.includes("404") || hint.includes("not found")) {
|
|
116
|
-
|
|
117
|
-
`${c.red}✗${c.reset}
|
|
161
|
+
console.error(
|
|
162
|
+
`${c.red}✗${c.reset} This account is ${c.bold}not a member${c.reset} of org ${c.bold}${org}${c.reset}.`
|
|
118
163
|
);
|
|
119
|
-
|
|
120
|
-
`${c.dim}
|
|
164
|
+
console.error(
|
|
165
|
+
`${c.dim} Wrong account? gh auth logout && gh auth login -h github.com -s repo -s read:org -w${c.reset}`
|
|
121
166
|
);
|
|
122
167
|
process.exit(1);
|
|
123
168
|
}
|
|
124
169
|
if (hint.includes("403") || hint.includes("read:org") || hint.includes("scope")) {
|
|
125
|
-
|
|
126
|
-
|
|
170
|
+
console.error(`${c.red}✗${c.reset} GitHub CLI needs ${c.bold}read:org${c.reset} scope.`);
|
|
171
|
+
console.error(`${c.dim} gh auth refresh -h github.com -s repo -s read:org${c.reset}`);
|
|
127
172
|
process.exit(1);
|
|
128
173
|
}
|
|
129
|
-
|
|
130
|
-
`${c.red}✗${c.reset}
|
|
174
|
+
console.error(
|
|
175
|
+
`${c.red}✗${c.reset} Could not verify org (exit ${mem.status}):\n${c.dim}${(mem.stderr || mem.stdout).trim()}${c.reset}`
|
|
131
176
|
);
|
|
132
177
|
process.exit(1);
|
|
133
178
|
}
|
|
@@ -159,10 +204,12 @@ function needCmd(name, hint = "") {
|
|
|
159
204
|
? canRun("git", ["--version"])
|
|
160
205
|
: canRun(name, ["--version"]);
|
|
161
206
|
if (!ok) {
|
|
162
|
-
|
|
207
|
+
console.error(
|
|
208
|
+
`${c.red}✗${c.reset} Cannot run ${c.bold}${name}${c.reset} (PATH or install?).${hint ? ` ${hint}` : ""}`
|
|
209
|
+
);
|
|
163
210
|
if (IS_WIN) {
|
|
164
|
-
|
|
165
|
-
`${c.dim} Windows:
|
|
211
|
+
console.error(
|
|
212
|
+
`${c.dim} Windows: restart the terminal after installing gh/git, or reboot to refresh PATH.${c.reset}`
|
|
166
213
|
);
|
|
167
214
|
}
|
|
168
215
|
process.exit(1);
|
|
@@ -180,7 +227,7 @@ function ghRepoList(org) {
|
|
|
180
227
|
spawnWin({ encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] })
|
|
181
228
|
);
|
|
182
229
|
if (r.status !== 0) {
|
|
183
|
-
|
|
230
|
+
console.error(`${c.red}✗${c.reset} gh repo list failed:\n${r.stderr || r.stdout}`);
|
|
184
231
|
process.exit(1);
|
|
185
232
|
}
|
|
186
233
|
const rows = JSON.parse(r.stdout || "[]");
|
|
@@ -209,8 +256,8 @@ function resolveRepos(cfg, org, teamArg, allRepos) {
|
|
|
209
256
|
|
|
210
257
|
const teams = cfg.teams || {};
|
|
211
258
|
if (!teams[team]) {
|
|
212
|
-
|
|
213
|
-
`${c.red}✗${c.reset}
|
|
259
|
+
console.error(
|
|
260
|
+
`${c.red}✗${c.reset} Unknown team ${c.bold}${team}${c.reset}. Valid: ${Object.keys(teams).sort().join(", ")}`
|
|
214
261
|
);
|
|
215
262
|
process.exit(2);
|
|
216
263
|
}
|
|
@@ -236,47 +283,50 @@ function isSafeTargetDir(abs) {
|
|
|
236
283
|
function cloudLine(org, name, status) {
|
|
237
284
|
const cloud = `${c.blue}☁${c.reset}`;
|
|
238
285
|
if (status === "ok") {
|
|
239
|
-
return ` ${cloud} ${c.green}✓${c.reset} ${c.bold}${org}/${name}${c.reset} ${c.dim}
|
|
286
|
+
return ` ${cloud} ${c.green}✓${c.reset} ${c.bold}${org}/${name}${c.reset} ${c.dim}cloned${c.reset}`;
|
|
240
287
|
}
|
|
241
288
|
if (status === "skip") {
|
|
242
|
-
return ` ${cloud} ${c.yellow}○${c.reset} ${org}/${name} ${c.dim}(
|
|
289
|
+
return ` ${cloud} ${c.yellow}○${c.reset} ${org}/${name} ${c.dim}(already present)${c.reset}`;
|
|
243
290
|
}
|
|
244
291
|
if (status === "fail") {
|
|
245
|
-
return ` ${cloud} ${c.red}✗${c.reset} ${org}/${name} ${c.dim}
|
|
292
|
+
return ` ${cloud} ${c.red}✗${c.reset} ${org}/${name} ${c.dim}failed${c.reset}`;
|
|
246
293
|
}
|
|
247
|
-
return ` ${cloud} ${c.cyan}…${c.reset} ${org}/${name} ${c.dim}
|
|
294
|
+
return ` ${cloud} ${c.cyan}…${c.reset} ${org}/${name} ${c.dim}cloning…${c.reset}`;
|
|
248
295
|
}
|
|
249
296
|
|
|
250
|
-
function
|
|
297
|
+
async function cloneOneAsync(org, name, workspace, useSsh) {
|
|
251
298
|
const q = process.env.KOMPLIAN_CLI_QUIET === "1";
|
|
252
299
|
const gitDir = join(workspace, name, ".git");
|
|
253
300
|
if (existsSync(gitDir)) {
|
|
254
|
-
|
|
255
|
-
else log(cloudLine(org, name, "skip"));
|
|
301
|
+
ux(cloudLine(org, name, "skip"));
|
|
256
302
|
return true;
|
|
257
303
|
}
|
|
258
|
-
|
|
304
|
+
|
|
259
305
|
const args = useSsh
|
|
260
306
|
? ["clone", `git@github.com:${org}/${name}.git`, name]
|
|
261
307
|
: ["repo", "clone", `${org}/${name}`, name];
|
|
262
308
|
const cmd = useSsh ? "git" : "gh";
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
309
|
+
|
|
310
|
+
const tty = process.stdout.isTTY;
|
|
311
|
+
const spin = tty ? startRepoSpinner(name) : null;
|
|
312
|
+
if (!tty) {
|
|
313
|
+
ux(
|
|
314
|
+
`${c.blue}☁${c.reset} ${c.cyan}…${c.reset} ${c.bold}${name}${c.reset} ${c.dim}cloning…${c.reset}`
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const r = await runSpawn(cmd, args, workspace);
|
|
319
|
+
if (spin) spin.stop();
|
|
320
|
+
|
|
271
321
|
if (r.status === 0) {
|
|
272
|
-
|
|
273
|
-
else log(cloudLine(org, name, "ok"));
|
|
322
|
+
ux(cloudLine(org, name, "ok"));
|
|
274
323
|
return true;
|
|
275
324
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
if (
|
|
325
|
+
ux(cloudLine(org, name, "fail"));
|
|
326
|
+
const err = (r.stderr || "").trim();
|
|
327
|
+
if (err) {
|
|
328
|
+
if (q) console.error(`${c.dim}${err.slice(0, 500)}${c.reset}`);
|
|
329
|
+
else log(`${c.dim}${err}${c.reset}`);
|
|
280
330
|
}
|
|
281
331
|
return false;
|
|
282
332
|
}
|
|
@@ -286,7 +336,7 @@ function copyCursorPack(workspace, cursorRepoUrl) {
|
|
|
286
336
|
if (cursorRepoUrl && String(cursorRepoUrl).trim()) {
|
|
287
337
|
const tmp = join(workspace, ".cursor-bootstrap-tmp");
|
|
288
338
|
rmSync(tmp, { recursive: true, force: true });
|
|
289
|
-
|
|
339
|
+
ux(`${c.dim}→${c.reset} Cursor rules pack…`);
|
|
290
340
|
const r = spawnSync(
|
|
291
341
|
"git",
|
|
292
342
|
["clone", "--depth", "1", String(cursorRepoUrl).trim(), tmp],
|
|
@@ -300,15 +350,18 @@ function copyCursorPack(workspace, cursorRepoUrl) {
|
|
|
300
350
|
rmSync(rootCursor, { recursive: true, force: true });
|
|
301
351
|
cpSync(join(tmp, ".cursor"), rootCursor, { recursive: true });
|
|
302
352
|
rmSync(tmp, { recursive: true, force: true });
|
|
303
|
-
|
|
353
|
+
ux(`${c.green}✓${c.reset} ${c.bold}.cursor${c.reset} ${c.dim}(KOMPLIAN_CURSOR_REPO)${c.reset}`);
|
|
304
354
|
return;
|
|
305
355
|
}
|
|
306
356
|
rmSync(tmp, { recursive: true, force: true });
|
|
307
|
-
|
|
357
|
+
ux(
|
|
358
|
+
`${c.yellow}○${c.reset} KOMPLIAN_CURSOR_REPO: clone failed or no .cursor/ at repo root`
|
|
359
|
+
);
|
|
360
|
+
} else if (!process.env.KOMPLIAN_CLI_QUIET) {
|
|
361
|
+
log(
|
|
362
|
+
`${c.dim}○ No .cursor/ (optional: KOMPLIAN_CURSOR_REPO=<git url>).${c.reset}`
|
|
363
|
+
);
|
|
308
364
|
}
|
|
309
|
-
log(
|
|
310
|
-
`${c.dim}○ Sin .cursor/ en el workspace (opcional: KOMPLIAN_CURSOR_REPO=<url git>).${c.reset}`
|
|
311
|
-
);
|
|
312
365
|
}
|
|
313
366
|
|
|
314
367
|
/** Sin esto, `npm install` crea o retoca package-lock.json y git muestra cambios sin querer. */
|
|
@@ -322,8 +375,8 @@ function npmInstallOneRepo(dir, name) {
|
|
|
322
375
|
const pkg = join(dir, "package.json");
|
|
323
376
|
if (!existsSync(pkg)) return { ok: true, skipped: true };
|
|
324
377
|
|
|
325
|
-
const
|
|
326
|
-
|
|
378
|
+
const q = process.env.KOMPLIAN_CLI_QUIET === "1";
|
|
379
|
+
const stdio = q ? "ignore" : "inherit";
|
|
327
380
|
|
|
328
381
|
const yarnLock = join(dir, "yarn.lock");
|
|
329
382
|
const pnpmLock = join(dir, "pnpm-lock.yaml");
|
|
@@ -332,11 +385,17 @@ function npmInstallOneRepo(dir, name) {
|
|
|
332
385
|
if (existsSync(yarnLock)) {
|
|
333
386
|
if (!canRun("yarn", ["--version"])) {
|
|
334
387
|
log(
|
|
335
|
-
`${c.yellow}○${c.reset} ${name} ${c.dim}(yarn.lock;
|
|
388
|
+
`${c.yellow}○${c.reset} ${name} ${c.dim}(yarn.lock; install yarn or run yarn install manually)${c.reset}`
|
|
336
389
|
);
|
|
337
390
|
return { ok: true, skipped: true };
|
|
338
391
|
}
|
|
339
|
-
|
|
392
|
+
if (q) {
|
|
393
|
+
ux(
|
|
394
|
+
` ${c.blue}☁${c.reset} ${c.cyan}…${c.reset} ${c.bold}${name}${c.reset} ${c.dim}yarn install…${c.reset}`
|
|
395
|
+
);
|
|
396
|
+
} else {
|
|
397
|
+
log(`${c.dim}→${c.reset} ${name} ${c.dim}(yarn)${c.reset}`);
|
|
398
|
+
}
|
|
340
399
|
const r = spawnSync(
|
|
341
400
|
"yarn",
|
|
342
401
|
["install", "--frozen-lockfile"],
|
|
@@ -348,11 +407,17 @@ function npmInstallOneRepo(dir, name) {
|
|
|
348
407
|
if (existsSync(pnpmLock)) {
|
|
349
408
|
if (!canRun("pnpm", ["--version"])) {
|
|
350
409
|
log(
|
|
351
|
-
`${c.yellow}○${c.reset} ${name} ${c.dim}(pnpm-lock;
|
|
410
|
+
`${c.yellow}○${c.reset} ${name} ${c.dim}(pnpm-lock; install pnpm or run pnpm install manually)${c.reset}`
|
|
352
411
|
);
|
|
353
412
|
return { ok: true, skipped: true };
|
|
354
413
|
}
|
|
355
|
-
|
|
414
|
+
if (q) {
|
|
415
|
+
ux(
|
|
416
|
+
` ${c.blue}☁${c.reset} ${c.cyan}…${c.reset} ${c.bold}${name}${c.reset} ${c.dim}pnpm install…${c.reset}`
|
|
417
|
+
);
|
|
418
|
+
} else {
|
|
419
|
+
log(`${c.dim}→${c.reset} ${name} ${c.dim}(pnpm)${c.reset}`);
|
|
420
|
+
}
|
|
356
421
|
const r = spawnSync(
|
|
357
422
|
"pnpm",
|
|
358
423
|
["install", "--frozen-lockfile"],
|
|
@@ -362,23 +427,35 @@ function npmInstallOneRepo(dir, name) {
|
|
|
362
427
|
}
|
|
363
428
|
|
|
364
429
|
if (!canRun("npm", ["--version"])) {
|
|
365
|
-
log(`${c.yellow}○${c.reset} npm
|
|
430
|
+
log(`${c.yellow}○${c.reset} npm not in PATH — skipping ${name}`);
|
|
366
431
|
return { ok: true, skipped: true };
|
|
367
432
|
}
|
|
368
433
|
|
|
369
434
|
const quiet = npmQuietFlags();
|
|
370
435
|
|
|
371
436
|
if (existsSync(npmLock)) {
|
|
372
|
-
|
|
437
|
+
if (q) {
|
|
438
|
+
ux(
|
|
439
|
+
` ${c.blue}☁${c.reset} ${c.cyan}…${c.reset} ${c.bold}${name}${c.reset} ${c.dim}npm ci…${c.reset}`
|
|
440
|
+
);
|
|
441
|
+
} else {
|
|
442
|
+
log(`${c.dim}→${c.reset} ${name} ${c.dim}(npm ci)${c.reset}`);
|
|
443
|
+
}
|
|
373
444
|
const r = spawnSync("npm", ["ci", ...quiet], spawnWin({ cwd: dir, stdio }));
|
|
374
445
|
if (r.status === 0) return { ok: true, skipped: false };
|
|
375
446
|
log(
|
|
376
|
-
`${c.yellow}○${c.reset} ${name}: npm ci
|
|
447
|
+
`${c.yellow}○${c.reset} ${name}: npm ci failed (lock out of sync?). ${c.dim}Try npm install in that repo.${c.reset}`
|
|
377
448
|
);
|
|
378
449
|
return { ok: false, skipped: false };
|
|
379
450
|
}
|
|
380
451
|
|
|
381
|
-
|
|
452
|
+
if (q) {
|
|
453
|
+
ux(
|
|
454
|
+
` ${c.blue}☁${c.reset} ${c.cyan}…${c.reset} ${c.bold}${name}${c.reset} ${c.dim}npm install…${c.reset}`
|
|
455
|
+
);
|
|
456
|
+
} else {
|
|
457
|
+
log(`${c.dim}→${c.reset} ${name} ${c.dim}(npm install — no new lockfile)${c.reset}`);
|
|
458
|
+
}
|
|
382
459
|
const r = spawnSync(
|
|
383
460
|
"npm",
|
|
384
461
|
["install", ...quiet, "--no-package-lock"],
|
|
@@ -392,6 +469,9 @@ function npmInstallEach(workspace) {
|
|
|
392
469
|
if (!q) {
|
|
393
470
|
log("");
|
|
394
471
|
log(`${c.cyan}━━ dependencies ━━${c.reset}`);
|
|
472
|
+
} else {
|
|
473
|
+
ux("");
|
|
474
|
+
ux(`${c.cyan}Dependencies${c.reset}`);
|
|
395
475
|
}
|
|
396
476
|
for (const ent of readdirSync(workspace)) {
|
|
397
477
|
const d = join(workspace, ent);
|
|
@@ -399,7 +479,12 @@ function npmInstallEach(workspace) {
|
|
|
399
479
|
const { ok, skipped } = npmInstallOneRepo(d, ent);
|
|
400
480
|
if (skipped) continue;
|
|
401
481
|
if (q) {
|
|
402
|
-
|
|
482
|
+
const cloud = `${c.blue}☁${c.reset}`;
|
|
483
|
+
if (ok) {
|
|
484
|
+
ux(` ${cloud} ${c.green}✓${c.reset} ${c.bold}${ent}${c.reset} ${c.dim}deps${c.reset}`);
|
|
485
|
+
} else {
|
|
486
|
+
ux(` ${cloud} ${c.yellow}○${c.reset} ${c.bold}${ent}${c.reset} ${c.dim}deps${c.reset}`);
|
|
487
|
+
}
|
|
403
488
|
} else if (ok) {
|
|
404
489
|
log(`${c.green}✓${c.reset} ${ent}`);
|
|
405
490
|
} else {
|
|
@@ -409,30 +494,34 @@ function npmInstallEach(workspace) {
|
|
|
409
494
|
}
|
|
410
495
|
|
|
411
496
|
function usage() {
|
|
412
|
-
log(`
|
|
413
|
-
log(
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
log(`
|
|
417
|
-
log(`
|
|
418
|
-
log(`
|
|
419
|
-
log(`
|
|
420
|
-
log(
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
log(`
|
|
424
|
-
log(
|
|
425
|
-
log(
|
|
426
|
-
log(
|
|
427
|
-
log(`
|
|
428
|
-
log(`
|
|
429
|
-
log(
|
|
430
|
-
log(`
|
|
431
|
-
log(`
|
|
432
|
-
log(` --
|
|
433
|
-
log(` --
|
|
434
|
-
log(`
|
|
435
|
-
log(` -
|
|
497
|
+
console.log(`Usage: komplian setup | onboard | postman | mcp-tools | db:all:dev | localhost | db …`);
|
|
498
|
+
console.log(
|
|
499
|
+
` ${c.bold}All-in-one:${c.reset} ${c.cyan}npx komplian setup${c.reset} ${c.dim}(onboard+postman+mcp+db+localhost; browser form if needed)${c.reset}`
|
|
500
|
+
);
|
|
501
|
+
console.log(` ${c.bold}Step by step:${c.reset}`);
|
|
502
|
+
console.log(` 1. npx komplian onboard --yes`);
|
|
503
|
+
console.log(` 2. npx komplian postman --yes ${c.dim}(@komplian.com)${c.reset}`);
|
|
504
|
+
console.log(` 3. npx komplian mcp-tools --yes`);
|
|
505
|
+
console.log(
|
|
506
|
+
` 4. npx komplian db:all:dev ${c.dim}(Postman + 3 dev URLs → ~/.komplian + .env.local)${c.reset}`
|
|
507
|
+
);
|
|
508
|
+
console.log(` 5. npx komplian localhost --yes`);
|
|
509
|
+
console.log(``);
|
|
510
|
+
console.log(` npx komplian db:app:development ${c.dim}(psql one DB)${c.reset}`);
|
|
511
|
+
console.log(``);
|
|
512
|
+
console.log(` Once: gh auth login -h github.com -s repo -s read:org -w`);
|
|
513
|
+
console.log(` Requires: Node 18+, git, GitHub CLI (gh)`);
|
|
514
|
+
console.log(``);
|
|
515
|
+
console.log(` onboard implies --install unless --no-install`);
|
|
516
|
+
console.log(` [folder] Target (default: cwd, not ~/komplian)`);
|
|
517
|
+
console.log(` -y, --yes Non-interactive (default team from JSON)`);
|
|
518
|
+
console.log(` -t, --team <slug> Team in komplian-team-repos.json`);
|
|
519
|
+
console.log(` -i, --install npm install in each repo with package.json`);
|
|
520
|
+
console.log(` --no-install Skip npm install`);
|
|
521
|
+
console.log(` --all-repos All visible repos (minus exclude_from_all)`);
|
|
522
|
+
console.log(` --ssh Clone with git@github.com:…`);
|
|
523
|
+
console.log(` --list-teams Print team slugs (JSON only) and exit`);
|
|
524
|
+
console.log(` -h, --help`);
|
|
436
525
|
}
|
|
437
526
|
|
|
438
527
|
function parseArgs(argv) {
|
|
@@ -459,8 +548,8 @@ function parseArgs(argv) {
|
|
|
459
548
|
else if (a === "-h" || a === "--help") out.help = true;
|
|
460
549
|
else if (a === "-t" || a === "--team") {
|
|
461
550
|
out.team = argv[++i] || "";
|
|
462
|
-
}
|
|
463
|
-
|
|
551
|
+
} else if (a.startsWith("-")) {
|
|
552
|
+
console.error(`${c.red}✗${c.reset} Unknown option: ${a}`);
|
|
464
553
|
usage();
|
|
465
554
|
process.exit(1);
|
|
466
555
|
} else rest.push(a);
|
|
@@ -560,20 +649,22 @@ async function main() {
|
|
|
560
649
|
}
|
|
561
650
|
|
|
562
651
|
if (!existsSync(configPath)) {
|
|
563
|
-
|
|
652
|
+
console.error(`${c.red}✗${c.reset} Missing config: ${configPath}`);
|
|
564
653
|
process.exit(1);
|
|
565
654
|
}
|
|
566
655
|
|
|
567
656
|
const cfg = loadConfig(configPath);
|
|
568
657
|
|
|
569
658
|
if (args.listTeams) {
|
|
570
|
-
log(Object.keys(cfg.teams || {}).sort().join("\n"));
|
|
659
|
+
console.log(Object.keys(cfg.teams || {}).sort().join("\n"));
|
|
571
660
|
return;
|
|
572
661
|
}
|
|
573
662
|
|
|
574
663
|
const nodeMajor = Number(process.versions.node.split(".")[0], 10);
|
|
575
664
|
if (nodeMajor < 18) {
|
|
576
|
-
|
|
665
|
+
console.error(
|
|
666
|
+
`${c.red}✗${c.reset} Node 18+ required. You have ${process.versions.node}.`
|
|
667
|
+
);
|
|
577
668
|
process.exit(1);
|
|
578
669
|
}
|
|
579
670
|
|
|
@@ -593,18 +684,16 @@ async function main() {
|
|
|
593
684
|
process.exit(1);
|
|
594
685
|
}
|
|
595
686
|
}
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
}
|
|
687
|
+
|
|
688
|
+
banner();
|
|
689
|
+
ux(`${c.green}✓${c.reset} GitHub CLI`);
|
|
599
690
|
|
|
600
691
|
const org = process.env.KOMPLIAN_ORG || cfg.org || "Komplian";
|
|
601
692
|
verifyOrgMembership(org);
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
);
|
|
606
|
-
|
|
607
|
-
banner();
|
|
693
|
+
if (!process.env.KOMPLIAN_CLI_QUIET) {
|
|
694
|
+
logGhIdentity();
|
|
695
|
+
}
|
|
696
|
+
ux(`${c.green}✓${c.reset} Org ${c.bold}${org}${c.reset}`);
|
|
608
697
|
|
|
609
698
|
let team = args.team;
|
|
610
699
|
if (!team && !args.yes && !args.allRepos) {
|
|
@@ -613,7 +702,9 @@ async function main() {
|
|
|
613
702
|
|
|
614
703
|
const repos = resolveRepos(cfg, org, team, args.allRepos);
|
|
615
704
|
if (repos.length === 0) {
|
|
616
|
-
|
|
705
|
+
console.error(
|
|
706
|
+
`${c.red}✗${c.reset} No repositories to clone (permissions / team / org).`
|
|
707
|
+
);
|
|
617
708
|
process.exit(1);
|
|
618
709
|
}
|
|
619
710
|
|
|
@@ -623,21 +714,28 @@ async function main() {
|
|
|
623
714
|
}
|
|
624
715
|
const abs = resolve(workspace.replace(/^~(?=$|[/\\])/, homedir()));
|
|
625
716
|
if (!isSafeTargetDir(abs)) {
|
|
626
|
-
|
|
717
|
+
console.error(`${c.red}✗${c.reset} Unsafe destination folder: ${abs}`);
|
|
627
718
|
process.exit(1);
|
|
628
719
|
}
|
|
629
720
|
|
|
630
721
|
mkdirSync(abs, { recursive: true });
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
722
|
+
const q = process.env.KOMPLIAN_CLI_QUIET === "1";
|
|
723
|
+
ux("");
|
|
724
|
+
if (!q) {
|
|
725
|
+
log(`${c.cyan}━━ Workspace ━━${c.reset} ${c.bold}${abs}${c.reset}`);
|
|
726
|
+
log(`${c.cyan}━━ Org ━━${c.reset} ${c.bold}${org}${c.reset}`);
|
|
727
|
+
if (team) log(`${c.cyan}━━ Team ━━${c.reset} ${c.bold}${team}${c.reset}`);
|
|
728
|
+
log(`${c.cyan}━━ Repos (${repos.length}) ━━${c.reset}`);
|
|
729
|
+
} else {
|
|
730
|
+
ux(`${c.dim}Workspace:${c.reset} ${abs}`);
|
|
731
|
+
if (team) ux(`${c.dim}Team:${c.reset} ${team}`);
|
|
732
|
+
ux(`${c.cyan}Repositories${c.reset} ${c.dim}(${repos.length})${c.reset}`);
|
|
733
|
+
}
|
|
734
|
+
ux("");
|
|
637
735
|
|
|
638
736
|
let failed = 0;
|
|
639
737
|
for (const name of repos) {
|
|
640
|
-
const ok =
|
|
738
|
+
const ok = await cloneOneAsync(org, name, abs, args.ssh);
|
|
641
739
|
if (!ok) failed += 1;
|
|
642
740
|
}
|
|
643
741
|
|
|
@@ -647,16 +745,22 @@ async function main() {
|
|
|
647
745
|
npmInstallEach(abs);
|
|
648
746
|
}
|
|
649
747
|
|
|
650
|
-
|
|
651
|
-
|
|
748
|
+
ux("");
|
|
749
|
+
ux(`${c.cyan}━━ Done ━━${c.reset}`);
|
|
652
750
|
if (failed > 0) {
|
|
653
|
-
|
|
751
|
+
ux(
|
|
752
|
+
`${c.yellow}○${c.reset} ${failed} repo(s) failed — check access and retry.`
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
ux(`${c.green}✓${c.reset} Open in Cursor: ${c.bold}File → Open Folder → ${abs}${c.reset}`);
|
|
756
|
+
if (!q) {
|
|
757
|
+
log(
|
|
758
|
+
`${c.dim} package-lock: npm ci. No lock: npm install --no-package-lock. yarn/pnpm: frozen lockfile. KOMPLIAN_NPM_AUDIT=1 enables npm audit.${c.reset}`
|
|
759
|
+
);
|
|
760
|
+
log(
|
|
761
|
+
`${c.dim} Copy .env.example → .env per project; secrets in 1Password — never commit.${c.reset}`
|
|
762
|
+
);
|
|
654
763
|
}
|
|
655
|
-
log(`${c.green}✓${c.reset} Cursor: ${c.bold}File → Open Folder → ${abs}${c.reset}`);
|
|
656
|
-
log(
|
|
657
|
-
`${c.dim} Con package-lock.json: npm ci (no retoca el lock). Sin lock: npm install --no-package-lock. yarn/pnpm: lock congelado. KOMPLIAN_NPM_AUDIT=1 activa auditoría en npm.${c.reset}`
|
|
658
|
-
);
|
|
659
|
-
log(`${c.dim} .env.example → .env por proyecto; secretos en 1Password — nunca commit.${c.reset}`);
|
|
660
764
|
}
|
|
661
765
|
|
|
662
766
|
main().catch((e) => {
|
package/komplian-postman.mjs
CHANGED
|
@@ -46,6 +46,11 @@ function formatHomePath(absPath) {
|
|
|
46
46
|
return absPath;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
/** JSON exports never go into the monorepo root (avoids accidental commits). */
|
|
50
|
+
function defaultPostmanExportDir() {
|
|
51
|
+
return join(homedir(), ".komplian", "postman-export");
|
|
52
|
+
}
|
|
53
|
+
|
|
49
54
|
function maskEmail(email) {
|
|
50
55
|
if (!email || typeof email !== "string" || !email.includes("@")) {
|
|
51
56
|
return "[sin email]";
|
|
@@ -855,7 +860,7 @@ function usage() {
|
|
|
855
860
|
log(``);
|
|
856
861
|
log(` -y, --yes Sin prompts extra (si falta API key y hay TTY, pide la clave una vez y guarda)`);
|
|
857
862
|
log(` --export-only Solo escribe JSON en disco (no llama a la API de Postman)`);
|
|
858
|
-
log(` --out <dir>
|
|
863
|
+
log(` --out <dir> Export folder (default: ~/.komplian/postman-export)`);
|
|
859
864
|
log(` --dotenv <ruta> .env extra (además de api/.env, .env, KOMPLIAN_DOTENV)`);
|
|
860
865
|
log(` -h, --help`);
|
|
861
866
|
log(``);
|
|
@@ -965,23 +970,29 @@ export async function runPostman(argv) {
|
|
|
965
970
|
apiKey,
|
|
966
971
|
process.env.POSTMAN_WORKSPACE_ID
|
|
967
972
|
);
|
|
968
|
-
|
|
969
|
-
`${c.green}✓${c.reset}
|
|
970
|
-
|
|
973
|
+
if (!process.env.KOMPLIAN_CLI_QUIET) {
|
|
974
|
+
log(`${c.green}✓${c.reset} Postman workspace: ${c.bold}${workspaceId}${c.reset}`);
|
|
975
|
+
}
|
|
971
976
|
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
: resolve(process.cwd(), "komplian-postman");
|
|
977
|
+
ensureSecureKomplianDir();
|
|
978
|
+
const outBase = args.outDir ? resolve(args.outDir) : defaultPostmanExportDir();
|
|
975
979
|
mkdirSync(outBase, { recursive: true });
|
|
976
980
|
|
|
977
981
|
const collPath = join(outBase, "Komplian-API.postman_collection.json");
|
|
978
982
|
writeFileSync(collPath, JSON.stringify(collection, null, 2), "utf8");
|
|
979
|
-
|
|
983
|
+
const quiet = process.env.KOMPLIAN_CLI_QUIET === "1";
|
|
984
|
+
if (!quiet) {
|
|
985
|
+
log(`${c.green}✓${c.reset} Export: ${c.dim}${collPath}${c.reset}`);
|
|
986
|
+
}
|
|
980
987
|
|
|
981
988
|
for (const env of envsForExport) {
|
|
982
989
|
const safe = env.name.replace(/[\\/]/g, "-") + ".postman_environment.json";
|
|
983
990
|
writeFileSync(join(outBase, safe), JSON.stringify({ ...env }, null, 2), "utf8");
|
|
984
|
-
|
|
991
|
+
if (!quiet) {
|
|
992
|
+
log(
|
|
993
|
+
`${c.green}✓${c.reset} Export: ${c.dim}${join(outBase, safe)}${c.reset} ${c.dim}(no secrets)${c.reset}`
|
|
994
|
+
);
|
|
995
|
+
}
|
|
985
996
|
}
|
|
986
997
|
|
|
987
998
|
if (args.exportOnly) {
|