@vibgrate/cli 2026.617.1 → 2026.618.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.
- package/dist/cli.js +432 -387
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
|
|
31
31
|
// src/cli.ts
|
|
32
32
|
import { Command as Command13 } from "commander";
|
|
33
|
-
import
|
|
33
|
+
import chalk15 from "chalk";
|
|
34
34
|
|
|
35
35
|
// src/commands/init.ts
|
|
36
36
|
import * as path from "path";
|
|
@@ -62,7 +62,7 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
|
|
|
62
62
|
// src/commands/scan.ts
|
|
63
63
|
import * as path4 from "path";
|
|
64
64
|
import { Command as Command3 } from "commander";
|
|
65
|
-
import
|
|
65
|
+
import chalk4 from "chalk";
|
|
66
66
|
|
|
67
67
|
// src/version.ts
|
|
68
68
|
import { createRequire } from "module";
|
|
@@ -129,6 +129,9 @@ function dashHostForIngestHost(ingestHost) {
|
|
|
129
129
|
const match = REGIONS.find((r) => r.ingestHost === ingestHost);
|
|
130
130
|
return match?.dashHost ?? "dash.vibgrate.com";
|
|
131
131
|
}
|
|
132
|
+
function ingestHostForRegionId(region) {
|
|
133
|
+
return findRegion(region.toLowerCase())?.ingestHost;
|
|
134
|
+
}
|
|
132
135
|
|
|
133
136
|
// src/commands/dsn.ts
|
|
134
137
|
async function provisionDsn(keyId, secret, workspaceId, ingestHost, region) {
|
|
@@ -261,18 +264,62 @@ function emitIngestIdLine(ingestId, options) {
|
|
|
261
264
|
}
|
|
262
265
|
}
|
|
263
266
|
|
|
267
|
+
// src/utils/upload.ts
|
|
268
|
+
import chalk3 from "chalk";
|
|
269
|
+
function postOnce(input, host) {
|
|
270
|
+
const url = `${input.scheme}://${host}/v1/ingest/scan`;
|
|
271
|
+
return fetch(url, {
|
|
272
|
+
method: "POST",
|
|
273
|
+
headers: {
|
|
274
|
+
"Content-Type": "application/json",
|
|
275
|
+
"Content-Encoding": input.contentEncoding,
|
|
276
|
+
"X-Vibgrate-Timestamp": input.timestamp,
|
|
277
|
+
"Authorization": `VibgrateDSN ${input.keyId}:${input.secret}`,
|
|
278
|
+
"Connection": "close"
|
|
279
|
+
// Prevent keep-alive delays on exit
|
|
280
|
+
},
|
|
281
|
+
body: input.body
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
async function regionRedirectHost(response) {
|
|
285
|
+
try {
|
|
286
|
+
const payload = await response.clone().json();
|
|
287
|
+
if (payload?.code !== "REGION_MISMATCH" || !payload.region) return void 0;
|
|
288
|
+
return ingestHostForRegionId(payload.region);
|
|
289
|
+
} catch {
|
|
290
|
+
return void 0;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
async function uploadScanArtifact(input) {
|
|
294
|
+
let host = input.host;
|
|
295
|
+
let response = await postOnce(input, host);
|
|
296
|
+
if (response.status === 409) {
|
|
297
|
+
const target = await regionRedirectHost(response);
|
|
298
|
+
if (target && target !== host) {
|
|
299
|
+
console.log(
|
|
300
|
+
chalk3.yellow(
|
|
301
|
+
`\u21BB Workspace is pinned to a different region \u2014 retrying upload to ${target}...`
|
|
302
|
+
)
|
|
303
|
+
);
|
|
304
|
+
host = target;
|
|
305
|
+
response = await postOnce(input, host);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return { response, host };
|
|
309
|
+
}
|
|
310
|
+
|
|
264
311
|
// src/commands/scan.ts
|
|
265
312
|
async function autoPush(artifact, rootDir, opts) {
|
|
266
313
|
const dsn = resolveDsn(opts.dsn);
|
|
267
314
|
if (!dsn) {
|
|
268
|
-
console.error(
|
|
269
|
-
console.error(
|
|
315
|
+
console.error(chalk4.red("No DSN provided for push."));
|
|
316
|
+
console.error(chalk4.dim('Run "vibgrate login", set VIBGRATE_DSN, or use the --dsn flag.'));
|
|
270
317
|
if (opts.strict) process.exit(1);
|
|
271
318
|
return;
|
|
272
319
|
}
|
|
273
320
|
const parsed = parseDsn(dsn);
|
|
274
321
|
if (!parsed) {
|
|
275
|
-
console.error(
|
|
322
|
+
console.error(chalk4.red("Invalid DSN format."));
|
|
276
323
|
if (opts.strict) process.exit(1);
|
|
277
324
|
return;
|
|
278
325
|
}
|
|
@@ -283,29 +330,26 @@ async function autoPush(artifact, rootDir, opts) {
|
|
|
283
330
|
try {
|
|
284
331
|
host = resolveIngestHost(opts.region);
|
|
285
332
|
} catch (e) {
|
|
286
|
-
console.error(
|
|
333
|
+
console.error(chalk4.red(e instanceof Error ? e.message : String(e)));
|
|
287
334
|
if (opts.strict) process.exit(1);
|
|
288
335
|
return;
|
|
289
336
|
}
|
|
290
337
|
}
|
|
291
|
-
const url = `${parsed.scheme}://${host}/v1/ingest/scan`;
|
|
292
338
|
const originalSize = JSON.stringify(artifact).length;
|
|
293
339
|
const compressedSize = body.length;
|
|
294
340
|
const ratio = ((1 - compressedSize / originalSize) * 100).toFixed(0);
|
|
295
|
-
console.log(
|
|
341
|
+
console.log(chalk4.dim(`Uploading to ${host}... (${(compressedSize / 1024).toFixed(0)} KB, ${ratio}% smaller)`));
|
|
296
342
|
try {
|
|
297
|
-
const response = await
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
// Prevent keep-alive delays on exit
|
|
306
|
-
},
|
|
307
|
-
body
|
|
343
|
+
const { response, host: uploadedHost } = await uploadScanArtifact({
|
|
344
|
+
scheme: parsed.scheme,
|
|
345
|
+
host,
|
|
346
|
+
keyId: parsed.keyId,
|
|
347
|
+
secret: parsed.secret,
|
|
348
|
+
body,
|
|
349
|
+
contentEncoding,
|
|
350
|
+
timestamp
|
|
308
351
|
});
|
|
352
|
+
host = uploadedHost;
|
|
309
353
|
if (!response.ok) {
|
|
310
354
|
const text = await response.text();
|
|
311
355
|
throw new Error(`HTTP ${response.status}: ${text}`);
|
|
@@ -313,36 +357,36 @@ async function autoPush(artifact, rootDir, opts) {
|
|
|
313
357
|
const result = await response.json();
|
|
314
358
|
if (result.unchanged) {
|
|
315
359
|
console.log(
|
|
316
|
-
|
|
360
|
+
chalk4.green("\u2714") + ` Repository unchanged since ${result.lastScannedAt ?? "last scan"} \u2014 skipped upload (no credit used).`
|
|
317
361
|
);
|
|
318
362
|
if (result.previousIngestId) {
|
|
319
363
|
emitIngestIdLine(result.previousIngestId, { unchanged: true });
|
|
320
|
-
const dashUrl = `https
|
|
321
|
-
console.log(
|
|
364
|
+
const dashUrl = `https://${dashHostForIngestHost(host)}/${parsed.workspaceId}/scan/${result.previousIngestId}`;
|
|
365
|
+
console.log(chalk4.dim(` Previous report: ${dashUrl}`));
|
|
322
366
|
} else if (opts.strict) {
|
|
323
|
-
console.error(
|
|
367
|
+
console.error(chalk4.red("Repository unchanged but no previous ingest id returned."));
|
|
324
368
|
process.exit(1);
|
|
325
369
|
}
|
|
326
370
|
return;
|
|
327
371
|
}
|
|
328
|
-
console.log(
|
|
372
|
+
console.log(chalk4.green("\u2714") + ` Scan queued for processing (${result.ingestId ?? "ok"})`);
|
|
329
373
|
if (result.ingestId) {
|
|
330
374
|
emitIngestIdLine(result.ingestId);
|
|
331
|
-
const dashUrl = `https
|
|
375
|
+
const dashUrl = `https://${dashHostForIngestHost(host)}/${parsed.workspaceId}/scan/${result.ingestId}`;
|
|
332
376
|
const CLEAR_LINE = process.platform === "win32" ? "\x1B[0G\x1B[2K" : "";
|
|
333
377
|
console.log("");
|
|
334
|
-
console.log(CLEAR_LINE +
|
|
378
|
+
console.log(CLEAR_LINE + chalk4.dim(" Processing continues in the background. Results available shortly."));
|
|
335
379
|
console.log("");
|
|
336
|
-
console.log(CLEAR_LINE +
|
|
337
|
-
console.log(CLEAR_LINE +
|
|
338
|
-
console.log(CLEAR_LINE + " " +
|
|
339
|
-
console.log(CLEAR_LINE +
|
|
380
|
+
console.log(CLEAR_LINE + chalk4.cyan("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
381
|
+
console.log(CLEAR_LINE + chalk4.bold(" \u{1F4CA} View Scan Report"));
|
|
382
|
+
console.log(CLEAR_LINE + " " + chalk4.underline.cyan(dashUrl));
|
|
383
|
+
console.log(CLEAR_LINE + chalk4.cyan("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
340
384
|
console.log("");
|
|
341
385
|
console.log("");
|
|
342
386
|
}
|
|
343
387
|
} catch (e) {
|
|
344
388
|
const msg = e instanceof Error ? e.message : String(e);
|
|
345
|
-
console.error(
|
|
389
|
+
console.error(chalk4.red(`Upload failed: ${msg}`));
|
|
346
390
|
if (opts.strict) process.exit(1);
|
|
347
391
|
}
|
|
348
392
|
}
|
|
@@ -365,11 +409,12 @@ var scanCommand = new Command3("scan").description("Scan a project for upgrade d
|
|
|
365
409
|
).option("--concurrency <n>", "Max concurrent npm calls", "8").option("--push", "Auto-push results to Vibgrate API after scan").option("--dsn <dsn>", "DSN token for push (or use VIBGRATE_DSN env)").option("--region <region>", "Override data residency region for push (us, eu)").option("--strict", "Fail on push errors").option("--ui-purpose", "Enable optional UI purpose evidence extraction (slower)").option("--no-local-artifacts", "Do not write .vibgrate JSON artifacts to disk").option("--max-privacy", "Enable strongest privacy mode (minimal scanners, no local artifacts)").option("--offline", "Run without network calls; do not upload results").option("--package-manifest <file>", "Use local package-version manifest JSON/ZIP (for offline mode)").option("--project-scan-timeout <seconds>", "Per-project scan timeout in seconds (default: 180)").option("--drift-budget <score>", "Fail if drift score is above budget (0-100)").option("--drift-worsening <percent>", "Fail if drift worsens by more than % since baseline").action(async (targetPath, opts) => {
|
|
366
410
|
const rootDir = path4.resolve(targetPath);
|
|
367
411
|
if (!await pathExists2(rootDir)) {
|
|
368
|
-
console.error(
|
|
412
|
+
console.error(chalk4.red(`Path does not exist: ${rootDir}`));
|
|
369
413
|
process.exit(1);
|
|
370
414
|
}
|
|
371
415
|
const hasDsn = !!resolveDsn(opts.dsn);
|
|
372
416
|
const willPush = !opts.offline && (opts.push || hasDsn);
|
|
417
|
+
let pinnedRegion;
|
|
373
418
|
if (willPush && hasDsn) {
|
|
374
419
|
const dsn = resolveDsn(opts.dsn);
|
|
375
420
|
const parsed = parseDsn(dsn);
|
|
@@ -383,19 +428,20 @@ var scanCommand = new Command3("scan").description("Scan a project for upgrade d
|
|
|
383
428
|
repositoryName,
|
|
384
429
|
vcsSha: fingerprint.vcsSha
|
|
385
430
|
});
|
|
431
|
+
pinnedRegion = preflight.region;
|
|
386
432
|
if (preflight.vm && !preflight.vm.allowed) {
|
|
387
|
-
console.error(
|
|
433
|
+
console.error(chalk4.red(preflight.error ?? "VM meter usage exhausted"));
|
|
388
434
|
console.error(
|
|
389
|
-
|
|
435
|
+
chalk4.dim(
|
|
390
436
|
`VM minutes: ${preflight.vm.used}/${preflight.vm.limit} (${preflight.plan.label} plan) \u2014 enable overages or upgrade your plan.`
|
|
391
437
|
)
|
|
392
438
|
);
|
|
393
439
|
process.exit(1);
|
|
394
440
|
}
|
|
395
441
|
if (preflight.status === "error" || !preflight.scans.allowed) {
|
|
396
|
-
console.error(
|
|
442
|
+
console.error(chalk4.red(preflight.error ?? "Scan ingestion not allowed for this workspace."));
|
|
397
443
|
console.error(
|
|
398
|
-
|
|
444
|
+
chalk4.dim(
|
|
399
445
|
`Credits: ${preflight.scans.used}/${preflight.scans.limit} (${preflight.plan.label} plan)`
|
|
400
446
|
)
|
|
401
447
|
);
|
|
@@ -403,27 +449,27 @@ var scanCommand = new Command3("scan").description("Scan a project for upgrade d
|
|
|
403
449
|
process.exit(1);
|
|
404
450
|
}
|
|
405
451
|
console.log(
|
|
406
|
-
|
|
452
|
+
chalk4.dim(
|
|
407
453
|
`Plan: ${preflight.plan.label} \u2014 scan credits ${preflight.scans.used}/${preflight.scans.limit} this month`
|
|
408
454
|
)
|
|
409
455
|
);
|
|
410
456
|
if (preflight.repository?.unchanged) {
|
|
411
457
|
console.log(
|
|
412
|
-
|
|
458
|
+
chalk4.green("\u2714") + ` Repository unchanged at ${preflight.repository.lastVcsSha?.slice(0, 7) ?? "same revision"} \u2014 skipping scan.`
|
|
413
459
|
);
|
|
414
460
|
if (preflight.repository.lastIngestId) {
|
|
415
461
|
emitIngestIdLine(preflight.repository.lastIngestId, { unchanged: true });
|
|
416
|
-
const dashUrl = `https
|
|
417
|
-
console.log(
|
|
462
|
+
const dashUrl = `https://${dashHostForIngestHost(ingestHost)}/${parsed.workspaceId}/scan/${preflight.repository.lastIngestId}`;
|
|
463
|
+
console.log(chalk4.dim(` Latest report: ${dashUrl}`));
|
|
418
464
|
} else if (opts.strict) {
|
|
419
|
-
console.error(
|
|
465
|
+
console.error(chalk4.red("Repository unchanged but no previous ingest id available."));
|
|
420
466
|
process.exit(1);
|
|
421
467
|
}
|
|
422
468
|
return;
|
|
423
469
|
}
|
|
424
470
|
} catch (e) {
|
|
425
471
|
const msg = e instanceof Error ? e.message : String(e);
|
|
426
|
-
console.error(
|
|
472
|
+
console.error(chalk4.yellow(`Preflight check failed: ${msg}`));
|
|
427
473
|
if (opts.strict) process.exit(1);
|
|
428
474
|
}
|
|
429
475
|
}
|
|
@@ -439,7 +485,9 @@ var scanCommand = new Command3("scan").description("Scan a project for upgrade d
|
|
|
439
485
|
concurrency: parseInt(opts.concurrency, 10) || 8,
|
|
440
486
|
push: opts.push,
|
|
441
487
|
dsn: opts.dsn,
|
|
442
|
-
region
|
|
488
|
+
// An explicit --region wins; otherwise route to the workspace's pinned
|
|
489
|
+
// region as reported by preflight.
|
|
490
|
+
region: opts.region ?? pinnedRegion,
|
|
443
491
|
strict: opts.strict,
|
|
444
492
|
uiPurpose: opts.uiPurpose,
|
|
445
493
|
noLocalArtifacts: opts.noLocalArtifacts,
|
|
@@ -455,24 +503,24 @@ var scanCommand = new Command3("scan").description("Scan a project for upgrade d
|
|
|
455
503
|
const hasErrors = artifact.findings.some((f) => f.level === "error");
|
|
456
504
|
const hasWarnings = artifact.findings.some((f) => f.level === "warning");
|
|
457
505
|
if (opts.failOn === "error" && hasErrors) {
|
|
458
|
-
console.error(
|
|
506
|
+
console.error(chalk4.red(`
|
|
459
507
|
Failing: ${artifact.findings.filter((f) => f.level === "error").length} error finding(s) detected.`));
|
|
460
508
|
process.exit(2);
|
|
461
509
|
}
|
|
462
510
|
if (opts.failOn === "warn" && (hasErrors || hasWarnings)) {
|
|
463
|
-
console.error(
|
|
511
|
+
console.error(chalk4.red(`
|
|
464
512
|
Failing: findings detected at warn level or above.`));
|
|
465
513
|
process.exit(2);
|
|
466
514
|
}
|
|
467
515
|
}
|
|
468
516
|
if (scanOpts.driftBudget !== void 0 && artifact.drift.score > scanOpts.driftBudget) {
|
|
469
|
-
console.error(
|
|
517
|
+
console.error(chalk4.red(`
|
|
470
518
|
Failing fitness function: drift score ${artifact.drift.score}/100 exceeds budget ${scanOpts.driftBudget}.`));
|
|
471
519
|
process.exit(2);
|
|
472
520
|
}
|
|
473
521
|
if (scanOpts.driftWorseningPercent !== void 0) {
|
|
474
522
|
if (artifact.delta === void 0) {
|
|
475
|
-
console.error(
|
|
523
|
+
console.error(chalk4.red("\nFailing fitness function: --drift-worsening requires --baseline to compare against previous drift."));
|
|
476
524
|
process.exit(2);
|
|
477
525
|
}
|
|
478
526
|
if (artifact.delta > 0) {
|
|
@@ -480,7 +528,7 @@ Failing fitness function: drift score ${artifact.drift.score}/100 exceeds budget
|
|
|
480
528
|
const denominator = Math.max(Math.abs(baselineScore), 1e-4);
|
|
481
529
|
const worseningPercent = artifact.delta / denominator * 100;
|
|
482
530
|
if (worseningPercent > scanOpts.driftWorseningPercent) {
|
|
483
|
-
console.error(
|
|
531
|
+
console.error(chalk4.red(`
|
|
484
532
|
Failing fitness function: drift worsened by ${worseningPercent.toFixed(2)}% (threshold ${scanOpts.driftWorseningPercent}%).`));
|
|
485
533
|
process.exit(2);
|
|
486
534
|
}
|
|
@@ -494,28 +542,28 @@ Failing fitness function: drift worsened by ${worseningPercent.toFixed(2)}% (thr
|
|
|
494
542
|
// src/commands/report.ts
|
|
495
543
|
import * as path5 from "path";
|
|
496
544
|
import { Command as Command4 } from "commander";
|
|
497
|
-
import
|
|
545
|
+
import chalk6 from "chalk";
|
|
498
546
|
|
|
499
547
|
// src/formatters/text.ts
|
|
500
|
-
import
|
|
548
|
+
import chalk5 from "chalk";
|
|
501
549
|
function formatText(artifact) {
|
|
502
550
|
const lines = [];
|
|
503
|
-
const teal =
|
|
504
|
-
const mint =
|
|
551
|
+
const teal = chalk5.hex("#3FB0A4");
|
|
552
|
+
const mint = chalk5.hex("#4FE3C1");
|
|
505
553
|
lines.push("");
|
|
506
554
|
lines.push(" " + teal("\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u256E") + mint("\u279C"));
|
|
507
|
-
lines.push(" " +
|
|
508
|
-
lines.push(" " +
|
|
555
|
+
lines.push(" " + chalk5.dim("\u2524") + teal("\u2502") + " " + mint("\u25FC") + " " + mint("\u25FC") + " " + teal("\u2502") + chalk5.dim("\u251C") + " " + chalk5.bold.white("vibgrate"));
|
|
556
|
+
lines.push(" " + chalk5.dim("\u2524") + teal("\u2502") + " " + chalk5.dim("\u2581\u2581") + " " + teal("\u2502") + chalk5.dim("\u251C") + " " + chalk5.dim(`Drift Intelligence Engine v${VERSION}`));
|
|
509
557
|
lines.push(" " + teal("\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
|
|
510
558
|
lines.push("");
|
|
511
559
|
lines.push(teal("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
512
|
-
lines.push(teal("\u2551 ") +
|
|
560
|
+
lines.push(teal("\u2551 ") + chalk5.bold.white("Vibgrate Drift Report") + teal(" \u2551"));
|
|
513
561
|
lines.push(teal("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
514
562
|
lines.push("");
|
|
515
563
|
for (const project of artifact.projects) {
|
|
516
|
-
lines.push(
|
|
564
|
+
lines.push(chalk5.bold(` \u2500\u2500 ${project.name} `) + chalk5.dim(`(${project.type}) ${project.path}`));
|
|
517
565
|
if (project.runtime) {
|
|
518
|
-
const behindStr = project.runtimeMajorsBehind !== void 0 && project.runtimeMajorsBehind > 0 ?
|
|
566
|
+
const behindStr = project.runtimeMajorsBehind !== void 0 && project.runtimeMajorsBehind > 0 ? chalk5.yellow(` (${project.runtimeMajorsBehind} major${project.runtimeMajorsBehind > 1 ? "s" : ""} behind)`) : chalk5.green(" (current)");
|
|
519
567
|
lines.push(` Runtime: ${project.runtime}${behindStr}`);
|
|
520
568
|
}
|
|
521
569
|
if (project.targetFramework) {
|
|
@@ -524,7 +572,7 @@ function formatText(artifact) {
|
|
|
524
572
|
if (project.frameworks.length > 0) {
|
|
525
573
|
lines.push(" Frameworks:");
|
|
526
574
|
for (const fw of project.frameworks) {
|
|
527
|
-
const lag = fw.majorsBehind !== null ? fw.majorsBehind === 0 ?
|
|
575
|
+
const lag = fw.majorsBehind !== null ? fw.majorsBehind === 0 ? chalk5.green("current") : chalk5.yellow(`${fw.majorsBehind} behind`) : chalk5.dim("unknown");
|
|
528
576
|
lines.push(` ${fw.name}: ${fw.currentVersion ?? "?"} \u2192 ${fw.latestVersion ?? "?"} (${lag})`);
|
|
529
577
|
}
|
|
530
578
|
}
|
|
@@ -532,13 +580,13 @@ function formatText(artifact) {
|
|
|
532
580
|
const total = b.current + b.oneBehind + b.twoPlusBehind + b.unknown;
|
|
533
581
|
if (total > 0) {
|
|
534
582
|
lines.push(" Dependencies:");
|
|
535
|
-
lines.push(` ${
|
|
583
|
+
lines.push(` ${chalk5.green(`${b.current} current`)} ${chalk5.yellow(`${b.oneBehind} 1-behind`)} ${chalk5.red(`${b.twoPlusBehind} 2+ behind`)} ${chalk5.dim(`${b.unknown} unknown`)}`);
|
|
536
584
|
}
|
|
537
585
|
lines.push("");
|
|
538
586
|
}
|
|
539
587
|
if (artifact.delta !== void 0) {
|
|
540
|
-
const deltaStr = artifact.delta > 0 ?
|
|
541
|
-
lines.push(
|
|
588
|
+
const deltaStr = artifact.delta > 0 ? chalk5.green(`+${artifact.delta}`) : artifact.delta < 0 ? chalk5.red(`${artifact.delta}`) : chalk5.dim("0");
|
|
589
|
+
lines.push(chalk5.bold(" Drift Delta: ") + deltaStr + " (vs baseline)");
|
|
542
590
|
lines.push("");
|
|
543
591
|
}
|
|
544
592
|
if (artifact.extended) {
|
|
@@ -549,30 +597,30 @@ function formatText(artifact) {
|
|
|
549
597
|
const warnings = artifact.findings.filter((f) => f.level === "warning");
|
|
550
598
|
const notes = artifact.findings.filter((f) => f.level === "note");
|
|
551
599
|
const summary = [
|
|
552
|
-
errors.length > 0 ?
|
|
553
|
-
warnings.length > 0 ?
|
|
554
|
-
notes.length > 0 ?
|
|
555
|
-
].filter(Boolean).join(
|
|
556
|
-
lines.push(
|
|
600
|
+
errors.length > 0 ? chalk5.red(`${errors.length} error${errors.length !== 1 ? "s" : ""}`) : "",
|
|
601
|
+
warnings.length > 0 ? chalk5.yellow(`${warnings.length} warning${warnings.length !== 1 ? "s" : ""}`) : "",
|
|
602
|
+
notes.length > 0 ? chalk5.blue(`${notes.length} note${notes.length !== 1 ? "s" : ""}`) : ""
|
|
603
|
+
].filter(Boolean).join(chalk5.dim(", "));
|
|
604
|
+
lines.push(chalk5.bold.underline(` Findings`) + chalk5.dim(` (${summary})`));
|
|
557
605
|
for (const f of artifact.findings) {
|
|
558
|
-
const icon = f.level === "error" ?
|
|
606
|
+
const icon = f.level === "error" ? chalk5.red("\u2716") : f.level === "warning" ? chalk5.yellow("\u26A0") : chalk5.blue("\u2139");
|
|
559
607
|
lines.push(` ${icon} ${f.message}`);
|
|
560
|
-
lines.push(
|
|
608
|
+
lines.push(chalk5.dim(` ${f.ruleId} in ${f.location}`));
|
|
561
609
|
}
|
|
562
610
|
lines.push("");
|
|
563
611
|
}
|
|
564
612
|
const actions = generatePriorityActions(artifact);
|
|
565
613
|
if (actions.length > 0) {
|
|
566
|
-
lines.push(
|
|
567
|
-
lines.push(
|
|
568
|
-
lines.push(
|
|
614
|
+
lines.push(chalk5.bold.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
615
|
+
lines.push(chalk5.bold.cyan("\u2551 Top Priority Actions \u2551"));
|
|
616
|
+
lines.push(chalk5.bold.cyan("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
569
617
|
lines.push("");
|
|
570
618
|
for (let i = 0; i < actions.length; i++) {
|
|
571
619
|
const a = actions[i];
|
|
572
|
-
const num =
|
|
573
|
-
lines.push(`${num} ${
|
|
574
|
-
lines.push(
|
|
575
|
-
if (a.impact) lines.push(` Impact: ${
|
|
620
|
+
const num = chalk5.bold.cyan(` ${i + 1}.`);
|
|
621
|
+
lines.push(`${num} ${chalk5.bold(a.title)}`);
|
|
622
|
+
lines.push(chalk5.dim(` ${a.explanation}`));
|
|
623
|
+
if (a.impact) lines.push(` Impact: ${chalk5.green(a.impact)}`);
|
|
576
624
|
lines.push("");
|
|
577
625
|
}
|
|
578
626
|
}
|
|
@@ -580,38 +628,38 @@ function formatText(artifact) {
|
|
|
580
628
|
lines.push(...formatArchitectureDiagram(artifact.extended.architecture));
|
|
581
629
|
}
|
|
582
630
|
if (artifact.solutions && artifact.solutions.length > 0) {
|
|
583
|
-
lines.push(
|
|
584
|
-
lines.push(
|
|
585
|
-
lines.push(
|
|
631
|
+
lines.push(chalk5.bold.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
632
|
+
lines.push(chalk5.bold.cyan("\u2551 Solution Drift Summary \u2551"));
|
|
633
|
+
lines.push(chalk5.bold.cyan("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
586
634
|
lines.push("");
|
|
587
635
|
for (const solution of artifact.solutions) {
|
|
588
636
|
const solScore = solution.drift?.score;
|
|
589
|
-
const color = typeof solScore === "number" ? solScore >= 70 ?
|
|
590
|
-
lines.push(` \u2022 ${solution.name} (${solution.projectPaths.length} projects) \u2014 ${typeof solScore === "number" ? color(`${solScore}/100`) :
|
|
637
|
+
const color = typeof solScore === "number" ? solScore >= 70 ? chalk5.green : solScore >= 40 ? chalk5.yellow : chalk5.red : chalk5.dim;
|
|
638
|
+
lines.push(` \u2022 ${solution.name} (${solution.projectPaths.length} projects) \u2014 ${typeof solScore === "number" ? color(`${solScore}/100`) : chalk5.dim("n/a")}`);
|
|
591
639
|
}
|
|
592
640
|
lines.push("");
|
|
593
641
|
}
|
|
594
|
-
const scoreColor = artifact.drift.score >= 70 ?
|
|
595
|
-
lines.push(
|
|
596
|
-
lines.push(
|
|
597
|
-
lines.push(
|
|
642
|
+
const scoreColor = artifact.drift.score >= 70 ? chalk5.green : artifact.drift.score >= 40 ? chalk5.yellow : chalk5.red;
|
|
643
|
+
lines.push(chalk5.bold.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
644
|
+
lines.push(chalk5.bold.cyan("\u2551 Drift Score Summary \u2551"));
|
|
645
|
+
lines.push(chalk5.bold.cyan("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
598
646
|
lines.push("");
|
|
599
|
-
lines.push(
|
|
600
|
-
lines.push(
|
|
601
|
-
lines.push(
|
|
647
|
+
lines.push(chalk5.bold(" Drift Score: ") + scoreColor.bold(`${artifact.drift.score}/100`));
|
|
648
|
+
lines.push(chalk5.bold(" Risk Level: ") + riskBadge(artifact.drift.riskLevel));
|
|
649
|
+
lines.push(chalk5.bold(" Projects: ") + `${artifact.projects.length}`);
|
|
602
650
|
if (artifact.vcs) {
|
|
603
651
|
const vcsParts = [artifact.vcs.type];
|
|
604
652
|
if (artifact.vcs.branch) vcsParts.push(artifact.vcs.branch);
|
|
605
|
-
if (artifact.vcs.shortSha) vcsParts.push(
|
|
606
|
-
lines.push(
|
|
653
|
+
if (artifact.vcs.shortSha) vcsParts.push(chalk5.dim(artifact.vcs.shortSha));
|
|
654
|
+
lines.push(chalk5.bold(" VCS: ") + vcsParts.join(" "));
|
|
607
655
|
}
|
|
608
656
|
lines.push("");
|
|
609
657
|
const m = new Set(artifact.drift.measured ?? ["runtime", "framework", "dependency", "eol"]);
|
|
610
|
-
lines.push(" " +
|
|
611
|
-
lines.push(` Runtime: ${m.has("runtime") ? scoreBar(artifact.drift.components.runtimeScore) :
|
|
612
|
-
lines.push(` Frameworks: ${m.has("framework") ? scoreBar(artifact.drift.components.frameworkScore) :
|
|
613
|
-
lines.push(` Dependencies: ${m.has("dependency") ? scoreBar(artifact.drift.components.dependencyScore) :
|
|
614
|
-
lines.push(` EOL Risk: ${m.has("eol") ? scoreBar(artifact.drift.components.eolScore) :
|
|
658
|
+
lines.push(" " + chalk5.bold.underline("Score Breakdown"));
|
|
659
|
+
lines.push(` Runtime: ${m.has("runtime") ? scoreBar(artifact.drift.components.runtimeScore) : chalk5.dim("n/a")}`);
|
|
660
|
+
lines.push(` Frameworks: ${m.has("framework") ? scoreBar(artifact.drift.components.frameworkScore) : chalk5.dim("n/a")}`);
|
|
661
|
+
lines.push(` Dependencies: ${m.has("dependency") ? scoreBar(artifact.drift.components.dependencyScore) : chalk5.dim("n/a")}`);
|
|
662
|
+
lines.push(` EOL Risk: ${m.has("eol") ? scoreBar(artifact.drift.components.eolScore) : chalk5.dim("n/a")}`);
|
|
615
663
|
lines.push("");
|
|
616
664
|
const scannedParts = [`Scanned at ${artifact.timestamp}`];
|
|
617
665
|
if (artifact.durationMs !== void 0) {
|
|
@@ -625,18 +673,18 @@ function formatText(artifact) {
|
|
|
625
673
|
scannedParts.push(`${artifact.treeSummary.totalFiles.toLocaleString()} workspace files`);
|
|
626
674
|
scannedParts.push(`${artifact.treeSummary.totalDirs.toLocaleString()} dirs`);
|
|
627
675
|
}
|
|
628
|
-
lines.push(
|
|
676
|
+
lines.push(chalk5.dim(` ${scannedParts.join(" \xB7 ")}`));
|
|
629
677
|
lines.push("");
|
|
630
678
|
return lines.join("\n");
|
|
631
679
|
}
|
|
632
680
|
function riskBadge(level) {
|
|
633
681
|
switch (level) {
|
|
634
682
|
case "low":
|
|
635
|
-
return
|
|
683
|
+
return chalk5.bgGreen.black(" LOW ");
|
|
636
684
|
case "moderate":
|
|
637
|
-
return
|
|
685
|
+
return chalk5.bgYellow.black(" MODERATE ");
|
|
638
686
|
case "high":
|
|
639
|
-
return
|
|
687
|
+
return chalk5.bgRed.white(" HIGH ");
|
|
640
688
|
default:
|
|
641
689
|
return level;
|
|
642
690
|
}
|
|
@@ -645,8 +693,8 @@ function scoreBar(score) {
|
|
|
645
693
|
const width = 20;
|
|
646
694
|
const filled = Math.round(score / 100 * width);
|
|
647
695
|
const empty = width - filled;
|
|
648
|
-
const color = score >= 70 ?
|
|
649
|
-
return color("\u2588".repeat(filled)) +
|
|
696
|
+
const color = score >= 70 ? chalk5.green : score >= 40 ? chalk5.yellow : chalk5.red;
|
|
697
|
+
return color("\u2588".repeat(filled)) + chalk5.dim("\u2591".repeat(empty)) + ` ${Math.round(score)}`;
|
|
650
698
|
}
|
|
651
699
|
var CATEGORY_LABELS = {
|
|
652
700
|
frontend: "Frontend",
|
|
@@ -675,11 +723,11 @@ function formatExtended(ext) {
|
|
|
675
723
|
const inv = ext.toolingInventory;
|
|
676
724
|
const categories = Object.entries(inv).filter(([, items]) => items.length > 0);
|
|
677
725
|
if (categories.length > 0) {
|
|
678
|
-
lines.push(
|
|
726
|
+
lines.push(chalk5.bold.underline(" Tech Stack"));
|
|
679
727
|
for (const [cat, items] of categories) {
|
|
680
728
|
const label = CATEGORY_LABELS[cat] ?? cat;
|
|
681
|
-
const names = items.map((i) =>
|
|
682
|
-
lines.push(` ${
|
|
729
|
+
const names = items.map((i) => chalk5.white(i.name)).join(chalk5.dim(", "));
|
|
730
|
+
lines.push(` ${chalk5.cyan(label)}: ${names}`);
|
|
683
731
|
}
|
|
684
732
|
lines.push("");
|
|
685
733
|
}
|
|
@@ -688,14 +736,14 @@ function formatExtended(ext) {
|
|
|
688
736
|
const svc = ext.serviceDependencies;
|
|
689
737
|
const categories = Object.entries(svc).filter(([, items]) => items.length > 0);
|
|
690
738
|
if (categories.length > 0) {
|
|
691
|
-
lines.push(
|
|
739
|
+
lines.push(chalk5.bold.underline(" Services & Integrations"));
|
|
692
740
|
for (const [cat, items] of categories) {
|
|
693
741
|
const label = CATEGORY_LABELS[cat] ?? cat;
|
|
694
742
|
const names = items.map((i) => {
|
|
695
|
-
const ver = i.version ?
|
|
696
|
-
return
|
|
697
|
-
}).join(
|
|
698
|
-
lines.push(` ${
|
|
743
|
+
const ver = i.version ? chalk5.dim(` ${i.version}`) : "";
|
|
744
|
+
return chalk5.white(i.name) + ver;
|
|
745
|
+
}).join(chalk5.dim(", "));
|
|
746
|
+
lines.push(` ${chalk5.cyan(label)}: ${names}`);
|
|
699
747
|
}
|
|
700
748
|
lines.push("");
|
|
701
749
|
}
|
|
@@ -703,19 +751,19 @@ function formatExtended(ext) {
|
|
|
703
751
|
if (ext.breakingChangeExposure) {
|
|
704
752
|
const bc = ext.breakingChangeExposure;
|
|
705
753
|
if (bc.deprecatedPackages.length > 0 || bc.legacyPolyfills.length > 0) {
|
|
706
|
-
lines.push(
|
|
707
|
-
const exposureColor = bc.exposureScore >= 40 ?
|
|
754
|
+
lines.push(chalk5.bold.underline(" Breaking Change Exposure"));
|
|
755
|
+
const exposureColor = bc.exposureScore >= 40 ? chalk5.red : bc.exposureScore >= 20 ? chalk5.yellow : chalk5.green;
|
|
708
756
|
lines.push(` Exposure Score: ${exposureColor.bold(`${bc.exposureScore}/100`)}`);
|
|
709
757
|
if (bc.deprecatedPackages.length > 0) {
|
|
710
|
-
lines.push(` ${
|
|
758
|
+
lines.push(` ${chalk5.red("Deprecated")}: ${bc.deprecatedPackages.map((p) => chalk5.dim(p)).join(", ")}`);
|
|
711
759
|
}
|
|
712
760
|
if (bc.legacyPolyfills.length > 0) {
|
|
713
|
-
lines.push(` ${
|
|
761
|
+
lines.push(` ${chalk5.yellow("Polyfills")}: ${bc.legacyPolyfills.map((p) => chalk5.dim(p)).join(", ")}`);
|
|
714
762
|
}
|
|
715
763
|
if (bc.peerConflictsDetected) {
|
|
716
|
-
lines.push(` ${
|
|
764
|
+
lines.push(` ${chalk5.red("\u26A0")} Peer dependency conflicts detected`);
|
|
717
765
|
}
|
|
718
|
-
lines.push(` Recommendation: ${
|
|
766
|
+
lines.push(` Recommendation: ${chalk5.bold(bc.overallRecommendation)}`);
|
|
719
767
|
const projectsWithPlans = bc.projectIntelligence.filter((p) => p.packages.length > 0).slice(0, 3);
|
|
720
768
|
if (projectsWithPlans.length > 0) {
|
|
721
769
|
lines.push(" Major Upgrade Intelligence:");
|
|
@@ -731,21 +779,21 @@ function formatExtended(ext) {
|
|
|
731
779
|
}
|
|
732
780
|
if (ext.tsModernity && ext.tsModernity.typescriptVersion) {
|
|
733
781
|
const ts = ext.tsModernity;
|
|
734
|
-
lines.push(
|
|
782
|
+
lines.push(chalk5.bold.underline(" TypeScript"));
|
|
735
783
|
const parts = [];
|
|
736
784
|
parts.push(`v${ts.typescriptVersion}`);
|
|
737
|
-
if (ts.strict === true) parts.push(
|
|
738
|
-
else if (ts.strict === false) parts.push(
|
|
785
|
+
if (ts.strict === true) parts.push(chalk5.green("strict \u2714"));
|
|
786
|
+
else if (ts.strict === false) parts.push(chalk5.yellow("strict \u2716"));
|
|
739
787
|
if (ts.moduleType) parts.push(ts.moduleType.toUpperCase());
|
|
740
788
|
if (ts.target) parts.push(`target: ${ts.target}`);
|
|
741
|
-
lines.push(` ${parts.join(
|
|
789
|
+
lines.push(` ${parts.join(chalk5.dim(" \xB7 "))}`);
|
|
742
790
|
lines.push("");
|
|
743
791
|
}
|
|
744
792
|
if (ext.buildDeploy) {
|
|
745
793
|
const bd = ext.buildDeploy;
|
|
746
794
|
const hasSomething = bd.ci.length > 0 || bd.docker.dockerfileCount > 0 || bd.packageManagers.length > 0;
|
|
747
795
|
if (hasSomething) {
|
|
748
|
-
lines.push(
|
|
796
|
+
lines.push(chalk5.bold.underline(" Build & Deploy"));
|
|
749
797
|
if (bd.ci.length > 0) lines.push(` CI: ${bd.ci.join(", ")}`);
|
|
750
798
|
if (bd.docker.dockerfileCount > 0) {
|
|
751
799
|
lines.push(` Docker: ${bd.docker.dockerfileCount} Dockerfile${bd.docker.dockerfileCount !== 1 ? "s" : ""} (${bd.docker.baseImages.join(", ")})`);
|
|
@@ -758,42 +806,42 @@ function formatExtended(ext) {
|
|
|
758
806
|
}
|
|
759
807
|
if (ext.uiPurpose) {
|
|
760
808
|
const up = ext.uiPurpose;
|
|
761
|
-
lines.push(
|
|
762
|
-
lines.push(` Frameworks: ${up.detectedFrameworks.length > 0 ? up.detectedFrameworks.join(", ") :
|
|
763
|
-
lines.push(` Evidence: ${up.topEvidence.length}${up.capped ?
|
|
809
|
+
lines.push(chalk5.bold.underline(" Product Purpose Signals"));
|
|
810
|
+
lines.push(` Frameworks: ${up.detectedFrameworks.length > 0 ? up.detectedFrameworks.join(", ") : chalk5.dim("unknown")}`);
|
|
811
|
+
lines.push(` Evidence: ${up.topEvidence.length}${up.capped ? chalk5.dim(` of ${up.evidenceCount} (capped)`) : ""}`);
|
|
764
812
|
const top = up.topEvidence.slice(0, 8);
|
|
765
813
|
if (top.length > 0) {
|
|
766
814
|
lines.push(" Top Signals:");
|
|
767
815
|
for (const item of top) {
|
|
768
|
-
lines.push(` - [${item.kind}] ${item.value} ${
|
|
816
|
+
lines.push(` - [${item.kind}] ${item.value} ${chalk5.dim(`(${item.file})`)}`);
|
|
769
817
|
}
|
|
770
818
|
}
|
|
771
819
|
if (up.unknownSignals.length > 0) {
|
|
772
820
|
lines.push(" Unknowns:");
|
|
773
821
|
for (const u of up.unknownSignals.slice(0, 4)) {
|
|
774
|
-
lines.push(` - ${
|
|
822
|
+
lines.push(` - ${chalk5.yellow(u)}`);
|
|
775
823
|
}
|
|
776
824
|
}
|
|
777
825
|
lines.push("");
|
|
778
826
|
}
|
|
779
827
|
if (ext.securityPosture) {
|
|
780
828
|
const sec = ext.securityPosture;
|
|
781
|
-
lines.push(
|
|
829
|
+
lines.push(chalk5.bold.underline(" Security Posture"));
|
|
782
830
|
const checks = [];
|
|
783
|
-
checks.push(sec.lockfilePresent ?
|
|
784
|
-
checks.push(sec.gitignoreCoversEnv ?
|
|
785
|
-
checks.push(sec.gitignoreCoversNodeModules ?
|
|
786
|
-
if (sec.multipleLockfileTypes) checks.push(
|
|
787
|
-
if (sec.envFilesTracked) checks.push(
|
|
788
|
-
lines.push(` ${checks.join(
|
|
831
|
+
checks.push(sec.lockfilePresent ? chalk5.green("Lockfile \u2714") : chalk5.red("Lockfile \u2716"));
|
|
832
|
+
checks.push(sec.gitignoreCoversEnv ? chalk5.green(".env \u2714") : chalk5.red(".env \u2716"));
|
|
833
|
+
checks.push(sec.gitignoreCoversNodeModules ? chalk5.green("node_modules \u2714") : chalk5.yellow("node_modules \u2716"));
|
|
834
|
+
if (sec.multipleLockfileTypes) checks.push(chalk5.yellow("Multiple lockfiles \u26A0"));
|
|
835
|
+
if (sec.envFilesTracked) checks.push(chalk5.red("Env files tracked \u2716"));
|
|
836
|
+
lines.push(` ${checks.join(chalk5.dim(" \xB7 "))}`);
|
|
789
837
|
lines.push("");
|
|
790
838
|
}
|
|
791
839
|
if (ext.platformMatrix) {
|
|
792
840
|
const pm = ext.platformMatrix;
|
|
793
841
|
if (pm.nativeModules.length > 0 || pm.dockerBaseImages.length > 0) {
|
|
794
|
-
lines.push(
|
|
842
|
+
lines.push(chalk5.bold.underline(" Platform"));
|
|
795
843
|
if (pm.nativeModules.length > 0) {
|
|
796
|
-
lines.push(` Native modules: ${pm.nativeModules.map((m) =>
|
|
844
|
+
lines.push(` Native modules: ${pm.nativeModules.map((m) => chalk5.dim(m)).join(", ")}`);
|
|
797
845
|
}
|
|
798
846
|
if (pm.osAssumptions.length > 0) {
|
|
799
847
|
lines.push(` OS assumptions: ${pm.osAssumptions.join(", ")}`);
|
|
@@ -803,25 +851,25 @@ function formatExtended(ext) {
|
|
|
803
851
|
}
|
|
804
852
|
if (ext.codeQuality) {
|
|
805
853
|
const cq = ext.codeQuality;
|
|
806
|
-
lines.push(
|
|
807
|
-
lines.push(` Files: ${
|
|
854
|
+
lines.push(chalk5.bold.underline(" Code Quality"));
|
|
855
|
+
lines.push(` Files: ${chalk5.white(`${cq.filesAnalyzed}`)} \xB7 Functions: ${chalk5.white(`${cq.functionsAnalyzed}`)} \xB7 Avg complexity: ${chalk5.white(`${cq.avgCyclomaticComplexity}`)} \xB7 Avg length: ${chalk5.white(`${cq.avgFunctionLength}`)} lines`);
|
|
808
856
|
lines.push(` Max nesting: ${cq.maxNestingDepth} \xB7 Circular deps: ${cq.circularDependencies} \xB7 Dead code: ${cq.deadCodePercent}%`);
|
|
809
857
|
if (cq.godFiles.length > 0) {
|
|
810
858
|
const preview = cq.godFiles.slice(0, 3).map((f) => `${f.path} (${f.lines} lines)`).join(", ");
|
|
811
|
-
lines.push(` ${
|
|
859
|
+
lines.push(` ${chalk5.yellow("God files")}: ${preview}`);
|
|
812
860
|
}
|
|
813
861
|
lines.push("");
|
|
814
862
|
}
|
|
815
863
|
if (ext.dependencyGraph) {
|
|
816
864
|
const dg = ext.dependencyGraph;
|
|
817
865
|
if (dg.lockfileType) {
|
|
818
|
-
lines.push(
|
|
819
|
-
lines.push(` ${dg.lockfileType}: ${
|
|
866
|
+
lines.push(chalk5.bold.underline(" Dependency Graph"));
|
|
867
|
+
lines.push(` ${dg.lockfileType}: ${chalk5.white(`${dg.totalUnique}`)} unique, ${chalk5.white(`${dg.totalInstalled}`)} installed`);
|
|
820
868
|
if (dg.duplicatedPackages.length > 0) {
|
|
821
|
-
lines.push(` ${
|
|
869
|
+
lines.push(` ${chalk5.yellow(`${dg.duplicatedPackages.length} duplicated`)} packages`);
|
|
822
870
|
}
|
|
823
871
|
if (dg.phantomDependencies.length > 0) {
|
|
824
|
-
lines.push(` ${
|
|
872
|
+
lines.push(` ${chalk5.red(`${dg.phantomDependencies.length} phantom`)} dependencies`);
|
|
825
873
|
}
|
|
826
874
|
lines.push("");
|
|
827
875
|
}
|
|
@@ -830,17 +878,17 @@ function formatExtended(ext) {
|
|
|
830
878
|
}
|
|
831
879
|
function formatArchitectureDiagram(arch) {
|
|
832
880
|
const lines = [];
|
|
833
|
-
lines.push(
|
|
834
|
-
lines.push(
|
|
835
|
-
lines.push(
|
|
881
|
+
lines.push(chalk5.bold.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
882
|
+
lines.push(chalk5.bold.cyan("\u2551 Architecture Layers \u2551"));
|
|
883
|
+
lines.push(chalk5.bold.cyan("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
836
884
|
lines.push("");
|
|
837
|
-
lines.push(
|
|
838
|
-
lines.push(` Files classified: ${arch.totalClassified}` + (arch.unclassified > 0 ?
|
|
885
|
+
lines.push(chalk5.bold(" Archetype: ") + `${arch.archetype}` + chalk5.dim(` (${Math.round(arch.archetypeConfidence * 100)}% confidence)`));
|
|
886
|
+
lines.push(` Files classified: ${arch.totalClassified}` + (arch.unclassified > 0 ? chalk5.dim(` (${arch.unclassified} unclassified)`) : ""));
|
|
839
887
|
lines.push("");
|
|
840
888
|
if (arch.layers.length > 0) {
|
|
841
889
|
for (const layer of arch.layers) {
|
|
842
|
-
const risk = layer.riskLevel === "none" ?
|
|
843
|
-
lines.push(` ${
|
|
890
|
+
const risk = layer.riskLevel === "none" ? chalk5.dim("none") : layer.riskLevel === "low" ? chalk5.green("low") : layer.riskLevel === "moderate" ? chalk5.yellow("moderate") : chalk5.red("high");
|
|
891
|
+
lines.push(` ${chalk5.bold(layer.layer)} ${layer.fileCount} file${layer.fileCount !== 1 ? "s" : ""} drift ${scoreBar(layer.driftScore)} risk ${risk}`);
|
|
844
892
|
}
|
|
845
893
|
lines.push("");
|
|
846
894
|
}
|
|
@@ -1211,8 +1259,8 @@ function formatMarkdown(artifact) {
|
|
|
1211
1259
|
var reportCommand = new Command4("report").description("Generate a drift report from a scan artifact").option("--in <file>", "Input artifact file", ".vibgrate/scan_result.json").option("--format <format>", "Output format (md|text|json)", "text").action(async (opts) => {
|
|
1212
1260
|
const artifactPath = path5.resolve(opts.in);
|
|
1213
1261
|
if (!await pathExists(artifactPath)) {
|
|
1214
|
-
console.error(
|
|
1215
|
-
console.error(
|
|
1262
|
+
console.error(chalk6.red(`Artifact not found: ${artifactPath}`));
|
|
1263
|
+
console.error(chalk6.dim('Run "vibgrate scan" first to generate a scan artifact.'));
|
|
1216
1264
|
process.exit(1);
|
|
1217
1265
|
}
|
|
1218
1266
|
const artifact = await readJsonFile(artifactPath);
|
|
@@ -1232,7 +1280,7 @@ var reportCommand = new Command4("report").description("Generate a drift report
|
|
|
1232
1280
|
|
|
1233
1281
|
// src/commands/login.ts
|
|
1234
1282
|
import { Command as Command5 } from "commander";
|
|
1235
|
-
import
|
|
1283
|
+
import chalk7 from "chalk";
|
|
1236
1284
|
|
|
1237
1285
|
// src/utils/open-url.ts
|
|
1238
1286
|
import { spawn } from "child_process";
|
|
@@ -1268,7 +1316,7 @@ var loginCommand = new Command5("login").description("Authenticate the CLI with
|
|
|
1268
1316
|
try {
|
|
1269
1317
|
ingestHost = resolveIngestHost(opts.region, opts.ingest);
|
|
1270
1318
|
} catch (e) {
|
|
1271
|
-
console.error(
|
|
1319
|
+
console.error(chalk7.red(e instanceof Error ? e.message : String(e)));
|
|
1272
1320
|
process.exit(1);
|
|
1273
1321
|
}
|
|
1274
1322
|
const base = `https://${ingestHost}/v1/auth/device`;
|
|
@@ -1280,28 +1328,28 @@ var loginCommand = new Command5("login").description("Authenticate the CLI with
|
|
|
1280
1328
|
body: "{}"
|
|
1281
1329
|
});
|
|
1282
1330
|
if (!res.ok) {
|
|
1283
|
-
console.error(
|
|
1331
|
+
console.error(chalk7.red(`Failed to start login (HTTP ${res.status}).`));
|
|
1284
1332
|
process.exit(1);
|
|
1285
1333
|
}
|
|
1286
1334
|
start = await res.json();
|
|
1287
1335
|
} catch (e) {
|
|
1288
|
-
console.error(
|
|
1336
|
+
console.error(chalk7.red(`Could not reach ${ingestHost}: ${e instanceof Error ? e.message : String(e)}`));
|
|
1289
1337
|
process.exit(1);
|
|
1290
1338
|
}
|
|
1291
1339
|
console.log("");
|
|
1292
1340
|
console.log("To finish signing in, open this URL and approve the request:");
|
|
1293
1341
|
console.log("");
|
|
1294
|
-
console.log(" " +
|
|
1342
|
+
console.log(" " + chalk7.cyan(start.verificationUri));
|
|
1295
1343
|
console.log("");
|
|
1296
|
-
console.log(" Your code: " +
|
|
1344
|
+
console.log(" Your code: " + chalk7.bold(start.userCode));
|
|
1297
1345
|
console.log("");
|
|
1298
1346
|
if (opts.browser) {
|
|
1299
1347
|
const opened = openUrl(start.verificationUriComplete);
|
|
1300
1348
|
if (opened) {
|
|
1301
|
-
console.log(
|
|
1349
|
+
console.log(chalk7.dim("Opening your browser\u2026 (if it does not open, use the URL above)"));
|
|
1302
1350
|
}
|
|
1303
1351
|
}
|
|
1304
|
-
console.log(
|
|
1352
|
+
console.log(chalk7.dim("Waiting for approval\u2026"));
|
|
1305
1353
|
const intervalMs = Math.max(2, start.interval || 5) * 1e3;
|
|
1306
1354
|
const deadline = Date.now() + (start.expiresIn || 900) * 1e3;
|
|
1307
1355
|
while (Date.now() < deadline) {
|
|
@@ -1327,40 +1375,40 @@ var loginCommand = new Command5("login").description("Authenticate the CLI with
|
|
|
1327
1375
|
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1328
1376
|
});
|
|
1329
1377
|
console.log("");
|
|
1330
|
-
console.log(
|
|
1378
|
+
console.log(chalk7.green("\u2714") + " Logged in.");
|
|
1331
1379
|
if (token.workspaceId) {
|
|
1332
|
-
console.log(" Workspace: " +
|
|
1380
|
+
console.log(" Workspace: " + chalk7.bold(token.workspaceId));
|
|
1333
1381
|
}
|
|
1334
|
-
console.log(
|
|
1335
|
-
console.log(
|
|
1382
|
+
console.log(chalk7.dim(` Credentials saved to ${credentialsPath()}`));
|
|
1383
|
+
console.log(chalk7.dim(' You can now run "vibgrate scan --push".'));
|
|
1336
1384
|
return;
|
|
1337
1385
|
}
|
|
1338
1386
|
if (token.status === "access_denied") {
|
|
1339
|
-
console.error(
|
|
1387
|
+
console.error(chalk7.red("\u2716 Login was denied in the browser."));
|
|
1340
1388
|
process.exit(1);
|
|
1341
1389
|
}
|
|
1342
1390
|
if (token.status === "expired" || token.status === "invalid") {
|
|
1343
|
-
console.error(
|
|
1391
|
+
console.error(chalk7.red('\u2716 Login request expired. Run "vibgrate login" again.'));
|
|
1344
1392
|
process.exit(1);
|
|
1345
1393
|
}
|
|
1346
1394
|
if (token.status === "error") {
|
|
1347
|
-
console.error(
|
|
1395
|
+
console.error(chalk7.red(`\u2716 ${token.error ?? "Login failed."}`));
|
|
1348
1396
|
process.exit(1);
|
|
1349
1397
|
}
|
|
1350
1398
|
}
|
|
1351
|
-
console.error(
|
|
1399
|
+
console.error(chalk7.red('\u2716 Timed out waiting for approval. Run "vibgrate login" again.'));
|
|
1352
1400
|
process.exit(1);
|
|
1353
1401
|
});
|
|
1354
1402
|
|
|
1355
1403
|
// src/commands/logout.ts
|
|
1356
1404
|
import { Command as Command6 } from "commander";
|
|
1357
|
-
import
|
|
1405
|
+
import chalk8 from "chalk";
|
|
1358
1406
|
var logoutCommand = new Command6("logout").description("Clear stored Vibgrate login credentials").action(() => {
|
|
1359
1407
|
const cleared = clearStoredCredentials();
|
|
1360
1408
|
if (cleared) {
|
|
1361
|
-
console.log(
|
|
1409
|
+
console.log(chalk8.green("\u2714") + " Logged out. Stored credentials removed.");
|
|
1362
1410
|
} else {
|
|
1363
|
-
console.log(
|
|
1411
|
+
console.log(chalk8.dim(`No stored credentials found at ${credentialsPath()}.`));
|
|
1364
1412
|
}
|
|
1365
1413
|
});
|
|
1366
1414
|
|
|
@@ -1368,7 +1416,7 @@ var logoutCommand = new Command6("logout").description("Clear stored Vibgrate lo
|
|
|
1368
1416
|
import * as crypto2 from "crypto";
|
|
1369
1417
|
import * as path6 from "path";
|
|
1370
1418
|
import { Command as Command7 } from "commander";
|
|
1371
|
-
import
|
|
1419
|
+
import chalk9 from "chalk";
|
|
1372
1420
|
|
|
1373
1421
|
// src/utils/compact-artifact.ts
|
|
1374
1422
|
import * as zlib from "zlib";
|
|
@@ -1643,22 +1691,22 @@ function computeHmac(body, secret) {
|
|
|
1643
1691
|
var pushCommand = new Command7("push").description("Push scan results to Vibgrate API").option("--dsn <dsn>", "DSN token (or use VIBGRATE_DSN env)").option("--region <region>", `Override data residency region (${availableRegionIds().join(", ")})`).option("--file <file>", "Scan artifact file", ".vibgrate/scan_result.json").option("--strict", "Fail on upload errors").action(async (opts) => {
|
|
1644
1692
|
const dsn = resolveDsn(opts.dsn);
|
|
1645
1693
|
if (!dsn) {
|
|
1646
|
-
console.error(
|
|
1647
|
-
console.error(
|
|
1694
|
+
console.error(chalk9.red("No DSN provided."));
|
|
1695
|
+
console.error(chalk9.dim('Run "vibgrate login", set VIBGRATE_DSN, or use the --dsn flag.'));
|
|
1648
1696
|
if (opts.strict) process.exit(1);
|
|
1649
1697
|
return;
|
|
1650
1698
|
}
|
|
1651
1699
|
const parsed = parseDsn2(dsn);
|
|
1652
1700
|
if (!parsed) {
|
|
1653
|
-
console.error(
|
|
1654
|
-
console.error(
|
|
1701
|
+
console.error(chalk9.red("Invalid DSN format."));
|
|
1702
|
+
console.error(chalk9.dim("Expected: vibgrate+https://<key_id>:<secret>@<host>/<workspace_id>"));
|
|
1655
1703
|
if (opts.strict) process.exit(1);
|
|
1656
1704
|
return;
|
|
1657
1705
|
}
|
|
1658
1706
|
const filePath = path6.resolve(opts.file);
|
|
1659
1707
|
if (!await pathExists(filePath)) {
|
|
1660
|
-
console.error(
|
|
1661
|
-
console.error(
|
|
1708
|
+
console.error(chalk9.red(`Scan artifact not found: ${filePath}`));
|
|
1709
|
+
console.error(chalk9.dim('Run "vibgrate scan" first.'));
|
|
1662
1710
|
if (opts.strict) process.exit(1);
|
|
1663
1711
|
return;
|
|
1664
1712
|
}
|
|
@@ -1670,46 +1718,43 @@ var pushCommand = new Command7("push").description("Push scan results to Vibgrat
|
|
|
1670
1718
|
try {
|
|
1671
1719
|
host = resolveIngestHost(opts.region);
|
|
1672
1720
|
} catch (e) {
|
|
1673
|
-
console.error(
|
|
1721
|
+
console.error(chalk9.red(e instanceof Error ? e.message : String(e)));
|
|
1674
1722
|
if (opts.strict) process.exit(1);
|
|
1675
1723
|
return;
|
|
1676
1724
|
}
|
|
1677
1725
|
}
|
|
1678
|
-
const url = `${parsed.scheme}://${host}/v1/ingest/scan`;
|
|
1679
1726
|
const originalSize = JSON.stringify(artifact).length;
|
|
1680
1727
|
const compressedSize = body.length;
|
|
1681
1728
|
const ratio = ((1 - compressedSize / originalSize) * 100).toFixed(0);
|
|
1682
|
-
console.log(
|
|
1729
|
+
console.log(chalk9.dim(`Uploading to ${host}... (${(compressedSize / 1024).toFixed(0)} KB, ${ratio}% smaller)`));
|
|
1683
1730
|
try {
|
|
1684
|
-
const response = await
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
// Prevent keep-alive delays on exit
|
|
1693
|
-
},
|
|
1694
|
-
body
|
|
1731
|
+
const { response, host: uploadedHost } = await uploadScanArtifact({
|
|
1732
|
+
scheme: parsed.scheme,
|
|
1733
|
+
host,
|
|
1734
|
+
keyId: parsed.keyId,
|
|
1735
|
+
secret: parsed.secret,
|
|
1736
|
+
body,
|
|
1737
|
+
contentEncoding,
|
|
1738
|
+
timestamp
|
|
1695
1739
|
});
|
|
1740
|
+
host = uploadedHost;
|
|
1696
1741
|
if (!response.ok) {
|
|
1697
1742
|
const text = await response.text();
|
|
1698
1743
|
throw new Error(`HTTP ${response.status}: ${text}`);
|
|
1699
1744
|
}
|
|
1700
1745
|
const result = await response.json();
|
|
1701
|
-
console.log(
|
|
1746
|
+
console.log(chalk9.green("\u2714") + ` Scan queued for processing (${result.ingestId ?? "ok"})`);
|
|
1702
1747
|
console.log();
|
|
1703
|
-
console.log(
|
|
1748
|
+
console.log(chalk9.dim("Processing continues in the background. Results available shortly."));
|
|
1704
1749
|
console.log();
|
|
1705
1750
|
if (result.ingestId) {
|
|
1706
1751
|
const dashHost = dashHostForIngestHost(host);
|
|
1707
1752
|
const reportUrl = `https://${dashHost}/${parsed.workspaceId}/scan/${result.ingestId}`;
|
|
1708
|
-
console.log(
|
|
1753
|
+
console.log(chalk9.dim("View report: ") + chalk9.underline(reportUrl));
|
|
1709
1754
|
}
|
|
1710
1755
|
} catch (e) {
|
|
1711
1756
|
const msg = e instanceof Error ? e.message : String(e);
|
|
1712
|
-
console.error(
|
|
1757
|
+
console.error(chalk9.red(`Upload failed: ${msg}`));
|
|
1713
1758
|
if (opts.strict) process.exit(1);
|
|
1714
1759
|
}
|
|
1715
1760
|
});
|
|
@@ -1718,7 +1763,7 @@ var pushCommand = new Command7("push").description("Push scan results to Vibgrat
|
|
|
1718
1763
|
import { execSync } from "child_process";
|
|
1719
1764
|
import * as path8 from "path";
|
|
1720
1765
|
import { Command as Command8 } from "commander";
|
|
1721
|
-
import
|
|
1766
|
+
import chalk10 from "chalk";
|
|
1722
1767
|
|
|
1723
1768
|
// src/utils/update-check.ts
|
|
1724
1769
|
var import_semver = __toESM(require_semver(), 1);
|
|
@@ -1865,21 +1910,21 @@ async function isDevDependency(cwd) {
|
|
|
1865
1910
|
}
|
|
1866
1911
|
}
|
|
1867
1912
|
var updateCommand = new Command8("update").description("Update vibgrate to the latest version").option("--check", "Only check for updates, do not install").option("--pm <manager>", "Package manager to use (npm, pnpm, yarn, bun)").option("--global", "Update global installation").action(async (opts) => {
|
|
1868
|
-
console.log(
|
|
1869
|
-
console.log(
|
|
1913
|
+
console.log(chalk10.dim(`Current version: ${VERSION}`));
|
|
1914
|
+
console.log(chalk10.dim("Checking npm registry..."));
|
|
1870
1915
|
const latest = await fetchLatestVersion();
|
|
1871
1916
|
if (!latest) {
|
|
1872
|
-
console.error(
|
|
1917
|
+
console.error(chalk10.red("Could not reach the npm registry. Check your network connection."));
|
|
1873
1918
|
process.exit(1);
|
|
1874
1919
|
}
|
|
1875
1920
|
const semver2 = await import("./semver-2FJFIYVN.js");
|
|
1876
1921
|
if (!semver2.gt(latest, VERSION)) {
|
|
1877
|
-
console.log(
|
|
1922
|
+
console.log(chalk10.green("\u2714") + ` You are on the latest version (${VERSION}).`);
|
|
1878
1923
|
return;
|
|
1879
1924
|
}
|
|
1880
|
-
console.log(
|
|
1925
|
+
console.log(chalk10.yellow(`Update available: ${VERSION} \u2192 ${latest}`));
|
|
1881
1926
|
if (opts.check) {
|
|
1882
|
-
console.log(
|
|
1927
|
+
console.log(chalk10.dim('Run "vibgrate update" to install.'));
|
|
1883
1928
|
return;
|
|
1884
1929
|
}
|
|
1885
1930
|
const cwd = process.cwd();
|
|
@@ -1889,17 +1934,17 @@ var updateCommand = new Command8("update").description("Update vibgrate to the l
|
|
|
1889
1934
|
let cmd;
|
|
1890
1935
|
if (isGlobal) {
|
|
1891
1936
|
cmd = getGlobalUpdateCommand(pm, "@vibgrate/cli", latest);
|
|
1892
|
-
console.log(
|
|
1937
|
+
console.log(chalk10.dim(`Updating global installation with ${pm}: ${cmd}`));
|
|
1893
1938
|
} else {
|
|
1894
1939
|
const isDev = await isDevDependency(cwd);
|
|
1895
1940
|
cmd = getInstallCommand(pm, "@vibgrate/cli", latest, isDev);
|
|
1896
|
-
console.log(
|
|
1941
|
+
console.log(chalk10.dim(`Using ${pm}: ${cmd}`));
|
|
1897
1942
|
}
|
|
1898
1943
|
try {
|
|
1899
1944
|
execSync(cmd, { cwd, stdio: "inherit" });
|
|
1900
|
-
console.log(
|
|
1945
|
+
console.log(chalk10.green("\u2714") + ` Updated to @vibgrate/cli@${latest}`);
|
|
1901
1946
|
} catch {
|
|
1902
|
-
console.error(
|
|
1947
|
+
console.error(chalk10.red(`Update failed. Run manually: ${cmd}`));
|
|
1903
1948
|
process.exit(1);
|
|
1904
1949
|
}
|
|
1905
1950
|
});
|
|
@@ -1908,7 +1953,7 @@ var updateCommand = new Command8("update").description("Update vibgrate to the l
|
|
|
1908
1953
|
import * as path9 from "path";
|
|
1909
1954
|
import { randomUUID } from "crypto";
|
|
1910
1955
|
import { Command as Command9 } from "commander";
|
|
1911
|
-
import
|
|
1956
|
+
import chalk11 from "chalk";
|
|
1912
1957
|
function flattenDependencies(artifact) {
|
|
1913
1958
|
const rows = [];
|
|
1914
1959
|
for (const project of artifact.projects) {
|
|
@@ -2048,7 +2093,7 @@ function formatDeltaText(base, current) {
|
|
|
2048
2093
|
async function readArtifactOrExit(filePath) {
|
|
2049
2094
|
const absolutePath = path9.resolve(filePath);
|
|
2050
2095
|
if (!await pathExists(absolutePath)) {
|
|
2051
|
-
console.error(
|
|
2096
|
+
console.error(chalk11.red(`Artifact not found: ${absolutePath}`));
|
|
2052
2097
|
process.exit(1);
|
|
2053
2098
|
}
|
|
2054
2099
|
return readJsonFile(absolutePath);
|
|
@@ -2057,14 +2102,14 @@ var exportCommand = new Command9("export").description("Export scan artifact as
|
|
|
2057
2102
|
const artifact = await readArtifactOrExit(opts.in);
|
|
2058
2103
|
const format = opts.format.toLowerCase();
|
|
2059
2104
|
if (format !== "cyclonedx" && format !== "spdx") {
|
|
2060
|
-
console.error(
|
|
2105
|
+
console.error(chalk11.red("Invalid SBOM format. Use cyclonedx or spdx."));
|
|
2061
2106
|
process.exit(1);
|
|
2062
2107
|
}
|
|
2063
2108
|
const sbom = format === "cyclonedx" ? toCycloneDx(artifact) : toSpdx(artifact);
|
|
2064
2109
|
const body = JSON.stringify(sbom, null, 2);
|
|
2065
2110
|
if (opts.out) {
|
|
2066
2111
|
await writeTextFile(path9.resolve(opts.out), body);
|
|
2067
|
-
console.log(
|
|
2112
|
+
console.log(chalk11.green("\u2714") + ` SBOM written to ${opts.out}`);
|
|
2068
2113
|
} else {
|
|
2069
2114
|
console.log(body);
|
|
2070
2115
|
}
|
|
@@ -2075,7 +2120,7 @@ var deltaCommand = new Command9("delta").description("Show SBOM delta between tw
|
|
|
2075
2120
|
const report = formatDeltaText(base, current);
|
|
2076
2121
|
if (opts.out) {
|
|
2077
2122
|
await writeTextFile(path9.resolve(opts.out), report);
|
|
2078
|
-
console.log(
|
|
2123
|
+
console.log(chalk11.green("\u2714") + ` SBOM delta report written to ${opts.out}`);
|
|
2079
2124
|
} else {
|
|
2080
2125
|
console.log(report);
|
|
2081
2126
|
}
|
|
@@ -2089,7 +2134,7 @@ import * as fs4 from "fs/promises";
|
|
|
2089
2134
|
import { existsSync, rmSync as rmSync2 } from "fs";
|
|
2090
2135
|
import { spawn as spawn3, spawnSync as spawnSync2 } from "child_process";
|
|
2091
2136
|
import { Command as Command10 } from "commander";
|
|
2092
|
-
import
|
|
2137
|
+
import chalk12 from "chalk";
|
|
2093
2138
|
|
|
2094
2139
|
// src/behavioural/derive.ts
|
|
2095
2140
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -3577,7 +3622,7 @@ async function runNodeWorker(rootDir, language, opts) {
|
|
|
3577
3622
|
continue;
|
|
3578
3623
|
}
|
|
3579
3624
|
if (opts.verbose) {
|
|
3580
|
-
process.stderr.write(
|
|
3625
|
+
process.stderr.write(chalk12.dim(`[${language}] ${trimmed}
|
|
3581
3626
|
`));
|
|
3582
3627
|
}
|
|
3583
3628
|
if (trimmed.startsWith("[error]")) {
|
|
@@ -3718,7 +3763,7 @@ async function runNativeWorker(rootDir, language, opts) {
|
|
|
3718
3763
|
let stdoutBuf = "";
|
|
3719
3764
|
let killed = false;
|
|
3720
3765
|
if (opts.verbose) {
|
|
3721
|
-
process.stderr.write(
|
|
3766
|
+
process.stderr.write(chalk12.dim(`[${language}] Spawning: ${spec.cmd} ${spec.args.join(" ")}
|
|
3722
3767
|
`));
|
|
3723
3768
|
}
|
|
3724
3769
|
const runtimeReq = NATIVE_RUNTIME_REQUIREMENTS[language];
|
|
@@ -3759,7 +3804,7 @@ async function runNativeWorker(rootDir, language, opts) {
|
|
|
3759
3804
|
const trimmed = line.trim();
|
|
3760
3805
|
if (!trimmed) continue;
|
|
3761
3806
|
if (opts.verbose) {
|
|
3762
|
-
process.stderr.write(
|
|
3807
|
+
process.stderr.write(chalk12.dim(`[${language}] ${trimmed}
|
|
3763
3808
|
`));
|
|
3764
3809
|
}
|
|
3765
3810
|
if (trimmed.startsWith("[error]")) {
|
|
@@ -3789,14 +3834,14 @@ async function runNativeWorker(rootDir, language, opts) {
|
|
|
3789
3834
|
async function pushFacts(facts, dsn, verbose) {
|
|
3790
3835
|
const parsed = parseDsn2(dsn);
|
|
3791
3836
|
if (!parsed) {
|
|
3792
|
-
process.stderr.write(
|
|
3793
|
-
process.stderr.write(
|
|
3837
|
+
process.stderr.write(chalk12.red("Invalid DSN format.\n"));
|
|
3838
|
+
process.stderr.write(chalk12.dim("Expected: vibgrate+https://<key_id>:<secret>@<host>/<workspace_id>\n"));
|
|
3794
3839
|
return false;
|
|
3795
3840
|
}
|
|
3796
3841
|
const body = facts.join("\n") + "\n";
|
|
3797
3842
|
const url = `${parsed.scheme}://${parsed.host}/v1/ingest/hcs`;
|
|
3798
3843
|
if (verbose) {
|
|
3799
|
-
process.stderr.write(
|
|
3844
|
+
process.stderr.write(chalk12.dim(`Pushing ${facts.length} facts to ${parsed.host}...
|
|
3800
3845
|
`));
|
|
3801
3846
|
}
|
|
3802
3847
|
try {
|
|
@@ -3814,17 +3859,17 @@ async function pushFacts(facts, dsn, verbose) {
|
|
|
3814
3859
|
});
|
|
3815
3860
|
if (!response.ok) {
|
|
3816
3861
|
const text = await response.text().catch(() => "");
|
|
3817
|
-
process.stderr.write(
|
|
3862
|
+
process.stderr.write(chalk12.red(`Push failed: HTTP ${response.status} ${text}
|
|
3818
3863
|
`));
|
|
3819
3864
|
return false;
|
|
3820
3865
|
}
|
|
3821
3866
|
if (verbose) {
|
|
3822
|
-
process.stderr.write(
|
|
3867
|
+
process.stderr.write(chalk12.green(`\u2714 Pushed ${facts.length} facts successfully
|
|
3823
3868
|
`));
|
|
3824
3869
|
}
|
|
3825
3870
|
return true;
|
|
3826
3871
|
} catch (err) {
|
|
3827
|
-
process.stderr.write(
|
|
3872
|
+
process.stderr.write(chalk12.red(`Push failed: ${err instanceof Error ? err.message : String(err)}
|
|
3828
3873
|
`));
|
|
3829
3874
|
return false;
|
|
3830
3875
|
}
|
|
@@ -3891,21 +3936,21 @@ var ProgressTracker = class {
|
|
|
3891
3936
|
const parts = [];
|
|
3892
3937
|
for (const [lang, st] of this.langStatus) {
|
|
3893
3938
|
if (st.done) {
|
|
3894
|
-
parts.push(
|
|
3939
|
+
parts.push(chalk12.green(`${lang} \u2713`));
|
|
3895
3940
|
} else if (st.phase === "waiting") {
|
|
3896
|
-
parts.push(
|
|
3941
|
+
parts.push(chalk12.dim(`${lang} \xB7`));
|
|
3897
3942
|
} else if (st.phase === "discovering") {
|
|
3898
|
-
parts.push(
|
|
3943
|
+
parts.push(chalk12.yellow(`${lang} \u2026`));
|
|
3899
3944
|
} else if (st.phase === "scanning" && st.fileCount > 0) {
|
|
3900
3945
|
const pct = Math.round(st.fileIndex / st.fileCount * 100);
|
|
3901
|
-
parts.push(
|
|
3946
|
+
parts.push(chalk12.cyan(`${lang} ${st.fileIndex}/${st.fileCount} ${pct}%`));
|
|
3902
3947
|
} else if (st.phase === "extracting") {
|
|
3903
|
-
parts.push(
|
|
3948
|
+
parts.push(chalk12.cyan(`${lang} extracting`));
|
|
3904
3949
|
} else {
|
|
3905
|
-
parts.push(
|
|
3950
|
+
parts.push(chalk12.dim(`${lang} ${st.phase}`));
|
|
3906
3951
|
}
|
|
3907
3952
|
}
|
|
3908
|
-
const line = ` ${parts.join(" ")} ${
|
|
3953
|
+
const line = ` ${parts.join(" ")} ${chalk12.dim(`${this.totalFacts} facts`)}`;
|
|
3909
3954
|
const padding = Math.max(0, this.lastLineLen - line.length);
|
|
3910
3955
|
process.stderr.write(`\r${line}${" ".repeat(padding)}`);
|
|
3911
3956
|
this.lastLineLen = line.length;
|
|
@@ -3925,7 +3970,7 @@ var extractCommand = new Command10("extract").description("Analyze source code a
|
|
|
3925
3970
|
if (urlArg) {
|
|
3926
3971
|
const cloneTimeoutMins = parseInt(opts.cloneTimeoutMins, 10);
|
|
3927
3972
|
if (isNaN(cloneTimeoutMins) || cloneTimeoutMins < 1) {
|
|
3928
|
-
process.stderr.write(
|
|
3973
|
+
process.stderr.write(chalk12.red("--clone-timeout-mins must be >= 1\n"));
|
|
3929
3974
|
process.exit(EXIT_USAGE_ERROR);
|
|
3930
3975
|
}
|
|
3931
3976
|
manifestRemoteUrl = redactRemoteUrl(urlArg);
|
|
@@ -3937,7 +3982,7 @@ var extractCommand = new Command10("extract").description("Analyze source code a
|
|
|
3937
3982
|
});
|
|
3938
3983
|
rootDir = cloned.dir;
|
|
3939
3984
|
if (opts.keepClone) {
|
|
3940
|
-
process.stderr.write(
|
|
3985
|
+
process.stderr.write(chalk12.dim(`Clone retained at: ${cloned.dir}
|
|
3941
3986
|
`));
|
|
3942
3987
|
} else {
|
|
3943
3988
|
process.once("exit", () => {
|
|
@@ -3949,14 +3994,14 @@ var extractCommand = new Command10("extract").description("Analyze source code a
|
|
|
3949
3994
|
}
|
|
3950
3995
|
} catch (err) {
|
|
3951
3996
|
const msg = err instanceof CloneError ? err.message : err instanceof Error ? err.message : String(err);
|
|
3952
|
-
process.stderr.write(
|
|
3997
|
+
process.stderr.write(chalk12.red(`Clone failed: ${msg}
|
|
3953
3998
|
`));
|
|
3954
3999
|
process.exit(EXIT_USAGE_ERROR);
|
|
3955
4000
|
}
|
|
3956
4001
|
} else {
|
|
3957
4002
|
rootDir = path12.resolve(targetPath);
|
|
3958
4003
|
if (!await pathExists(rootDir)) {
|
|
3959
|
-
process.stderr.write(
|
|
4004
|
+
process.stderr.write(chalk12.red(`Path does not exist: ${rootDir}
|
|
3960
4005
|
`));
|
|
3961
4006
|
process.exit(EXIT_USAGE_ERROR);
|
|
3962
4007
|
}
|
|
@@ -3964,48 +4009,48 @@ var extractCommand = new Command10("extract").description("Analyze source code a
|
|
|
3964
4009
|
try {
|
|
3965
4010
|
stat4 = await fs4.stat(rootDir);
|
|
3966
4011
|
} catch {
|
|
3967
|
-
process.stderr.write(
|
|
4012
|
+
process.stderr.write(chalk12.red(`Cannot read path: ${rootDir}
|
|
3968
4013
|
`));
|
|
3969
4014
|
process.exit(EXIT_USAGE_ERROR);
|
|
3970
4015
|
}
|
|
3971
4016
|
if (!stat4.isDirectory()) {
|
|
3972
|
-
process.stderr.write(
|
|
4017
|
+
process.stderr.write(chalk12.red(`Path must be a directory: ${rootDir}
|
|
3973
4018
|
`));
|
|
3974
4019
|
process.exit(EXIT_USAGE_ERROR);
|
|
3975
4020
|
}
|
|
3976
4021
|
}
|
|
3977
4022
|
const dsn = opts.dsn || process.env.VIBGRATE_DSN;
|
|
3978
4023
|
if (opts.push && !dsn) {
|
|
3979
|
-
process.stderr.write(
|
|
4024
|
+
process.stderr.write(chalk12.red("--push requires --dsn or VIBGRATE_DSN environment variable\n"));
|
|
3980
4025
|
process.exit(EXIT_USAGE_ERROR);
|
|
3981
4026
|
}
|
|
3982
4027
|
if (dsn && !parseDsn2(dsn)) {
|
|
3983
|
-
process.stderr.write(
|
|
3984
|
-
process.stderr.write(
|
|
4028
|
+
process.stderr.write(chalk12.red("Invalid DSN format.\n"));
|
|
4029
|
+
process.stderr.write(chalk12.dim("Expected: vibgrate+https://<key_id>:<secret>@<host>/<workspace_id>\n"));
|
|
3985
4030
|
process.exit(EXIT_USAGE_ERROR);
|
|
3986
4031
|
}
|
|
3987
4032
|
const concurrency = opts.concurrency ? parseInt(opts.concurrency, 10) : os4.cpus().length;
|
|
3988
4033
|
if (isNaN(concurrency) || concurrency < 1) {
|
|
3989
|
-
process.stderr.write(
|
|
4034
|
+
process.stderr.write(chalk12.red("--concurrency must be >= 1\n"));
|
|
3990
4035
|
process.exit(EXIT_USAGE_ERROR);
|
|
3991
4036
|
}
|
|
3992
4037
|
const timeoutMins = parseInt(opts.timeoutMins, 10);
|
|
3993
4038
|
if (isNaN(timeoutMins) || timeoutMins < 1) {
|
|
3994
|
-
process.stderr.write(
|
|
4039
|
+
process.stderr.write(chalk12.red("--timeout-mins must be >= 1\n"));
|
|
3995
4040
|
process.exit(EXIT_USAGE_ERROR);
|
|
3996
4041
|
}
|
|
3997
4042
|
const timeoutMs = timeoutMins * 60 * 1e3;
|
|
3998
4043
|
if (opts.out) {
|
|
3999
4044
|
const outDir = path12.dirname(path12.resolve(opts.out));
|
|
4000
4045
|
if (!await pathExists(outDir)) {
|
|
4001
|
-
process.stderr.write(
|
|
4046
|
+
process.stderr.write(chalk12.red(`Output directory does not exist: ${outDir}
|
|
4002
4047
|
`));
|
|
4003
4048
|
process.exit(EXIT_USAGE_ERROR);
|
|
4004
4049
|
}
|
|
4005
4050
|
try {
|
|
4006
4051
|
const outStat = await fs4.stat(path12.resolve(opts.out));
|
|
4007
4052
|
if (outStat.isDirectory()) {
|
|
4008
|
-
process.stderr.write(
|
|
4053
|
+
process.stderr.write(chalk12.red(`--out cannot be a directory: ${opts.out}
|
|
4009
4054
|
`));
|
|
4010
4055
|
process.exit(EXIT_USAGE_ERROR);
|
|
4011
4056
|
}
|
|
@@ -4016,14 +4061,14 @@ var extractCommand = new Command10("extract").description("Analyze source code a
|
|
|
4016
4061
|
if (opts.feedback) {
|
|
4017
4062
|
const feedbackPath = path12.resolve(opts.feedback);
|
|
4018
4063
|
if (!await pathExists(feedbackPath)) {
|
|
4019
|
-
process.stderr.write(
|
|
4064
|
+
process.stderr.write(chalk12.red(`Feedback file not found: ${feedbackPath}
|
|
4020
4065
|
`));
|
|
4021
4066
|
process.exit(EXIT_USAGE_ERROR);
|
|
4022
4067
|
}
|
|
4023
4068
|
try {
|
|
4024
4069
|
feedbackLines = await loadFeedback(feedbackPath);
|
|
4025
4070
|
} catch (err) {
|
|
4026
|
-
process.stderr.write(
|
|
4071
|
+
process.stderr.write(chalk12.red(`Invalid feedback file: ${err instanceof Error ? err.message : String(err)}
|
|
4027
4072
|
`));
|
|
4028
4073
|
process.exit(EXIT_USAGE_ERROR);
|
|
4029
4074
|
}
|
|
@@ -4033,9 +4078,9 @@ var extractCommand = new Command10("extract").description("Analyze source code a
|
|
|
4033
4078
|
targetLanguages = opts.language.split(",").map((l) => normalizeLanguageToken(l)).filter(Boolean);
|
|
4034
4079
|
for (const lang of targetLanguages) {
|
|
4035
4080
|
if (!SUPPORTED_LANGUAGES.has(lang)) {
|
|
4036
|
-
process.stderr.write(
|
|
4081
|
+
process.stderr.write(chalk12.red(`Unknown language: "${lang}"
|
|
4037
4082
|
`));
|
|
4038
|
-
process.stderr.write(
|
|
4083
|
+
process.stderr.write(chalk12.dim(`Supported: ${[...SUPPORTED_LANGUAGES].sort().join(", ")}
|
|
4039
4084
|
`));
|
|
4040
4085
|
process.exit(EXIT_USAGE_ERROR);
|
|
4041
4086
|
}
|
|
@@ -4043,14 +4088,14 @@ var extractCommand = new Command10("extract").description("Analyze source code a
|
|
|
4043
4088
|
} else {
|
|
4044
4089
|
const detected = await detectLanguages(rootDir, opts.includeTests ?? false);
|
|
4045
4090
|
if (detected.length === 0) {
|
|
4046
|
-
process.stderr.write(
|
|
4091
|
+
process.stderr.write(chalk12.yellow("No supported source files detected.\n"));
|
|
4047
4092
|
process.exit(EXIT_SUCCESS);
|
|
4048
4093
|
}
|
|
4049
4094
|
targetLanguages = detected.map((d) => d.language);
|
|
4050
4095
|
if (opts.verbose) {
|
|
4051
|
-
process.stderr.write(
|
|
4096
|
+
process.stderr.write(chalk12.dim("Detected languages:\n"));
|
|
4052
4097
|
for (const d of detected) {
|
|
4053
|
-
process.stderr.write(
|
|
4098
|
+
process.stderr.write(chalk12.dim(` ${d.language}: ${d.fileCount} files
|
|
4054
4099
|
`));
|
|
4055
4100
|
}
|
|
4056
4101
|
}
|
|
@@ -4058,20 +4103,20 @@ var extractCommand = new Command10("extract").description("Analyze source code a
|
|
|
4058
4103
|
const runnableLanguages = targetLanguages.filter((l) => ALL_WORKER_LANGS.has(l));
|
|
4059
4104
|
const unknownWorkerLangs = targetLanguages.filter((l) => !ALL_WORKER_LANGS.has(l));
|
|
4060
4105
|
if (unknownWorkerLangs.length > 0 && opts.verbose) {
|
|
4061
|
-
process.stderr.write(
|
|
4106
|
+
process.stderr.write(chalk12.dim(
|
|
4062
4107
|
`No worker registered for: ${unknownWorkerLangs.join(", ")} \u2014 skipping.
|
|
4063
4108
|
`
|
|
4064
4109
|
));
|
|
4065
4110
|
}
|
|
4066
4111
|
if (runnableLanguages.length === 0) {
|
|
4067
|
-
process.stderr.write(
|
|
4112
|
+
process.stderr.write(chalk12.yellow("No languages with available HCS workers found.\n"));
|
|
4068
4113
|
process.exit(EXIT_SUCCESS);
|
|
4069
4114
|
}
|
|
4070
4115
|
const startTime = Date.now();
|
|
4071
4116
|
const globalDeadline = startTime + timeoutMs;
|
|
4072
4117
|
process.stderr.write(
|
|
4073
|
-
|
|
4074
|
-
`) +
|
|
4118
|
+
chalk12.bold(`Extracting HCS facts from ${rootDir}
|
|
4119
|
+
`) + chalk12.dim(`Languages: ${runnableLanguages.join(", ")} Concurrency: ${concurrency} Timeout: ${timeoutMins}m
|
|
4075
4120
|
`)
|
|
4076
4121
|
);
|
|
4077
4122
|
const progress = new ProgressTracker(runnableLanguages);
|
|
@@ -4118,7 +4163,7 @@ var extractCommand = new Command10("extract").description("Analyze source code a
|
|
|
4118
4163
|
hasSchemaFailure = true;
|
|
4119
4164
|
allErrors.push(`[${language}] Schema validation: ${validation.error}`);
|
|
4120
4165
|
if (opts.verbose) {
|
|
4121
|
-
process.stderr.write(
|
|
4166
|
+
process.stderr.write(chalk12.red(`[${language}] Invalid fact: ${validation.error}
|
|
4122
4167
|
`));
|
|
4123
4168
|
}
|
|
4124
4169
|
}
|
|
@@ -4155,7 +4200,7 @@ var extractCommand = new Command10("extract").description("Analyze source code a
|
|
|
4155
4200
|
allErrors.push("[behavioural] derived fact failed schema validation");
|
|
4156
4201
|
}
|
|
4157
4202
|
}
|
|
4158
|
-
process.stderr.write(
|
|
4203
|
+
process.stderr.write(chalk12.dim(
|
|
4159
4204
|
`Derived ${summary.assertions} behavioural assertion(s) \xB7 confidence ${summary.behaviouralConfidence} (${summary.assertedFacts}/${summary.surfaceFacts} surface facts)
|
|
4160
4205
|
`
|
|
4161
4206
|
));
|
|
@@ -4190,7 +4235,7 @@ var extractCommand = new Command10("extract").description("Analyze source code a
|
|
|
4190
4235
|
if (opts.out) {
|
|
4191
4236
|
const outPath = path12.resolve(opts.out);
|
|
4192
4237
|
await fs4.writeFile(outPath, ndjsonOutput, "utf-8");
|
|
4193
|
-
process.stderr.write(
|
|
4238
|
+
process.stderr.write(chalk12.green(`\u2714 Wrote ${allFacts.length} facts to ${outPath}
|
|
4194
4239
|
`));
|
|
4195
4240
|
} else {
|
|
4196
4241
|
process.stdout.write(ndjsonOutput);
|
|
@@ -4202,38 +4247,38 @@ var extractCommand = new Command10("extract").description("Analyze source code a
|
|
|
4202
4247
|
}
|
|
4203
4248
|
}
|
|
4204
4249
|
if (opts.verbose) {
|
|
4205
|
-
process.stderr.write(
|
|
4206
|
-
process.stderr.write(
|
|
4250
|
+
process.stderr.write(chalk12.dim("\n\u2500\u2500 Summary \u2500\u2500\n"));
|
|
4251
|
+
process.stderr.write(chalk12.dim(` Facts emitted : ${allFacts.length}
|
|
4207
4252
|
`));
|
|
4208
|
-
process.stderr.write(
|
|
4253
|
+
process.stderr.write(chalk12.dim(` Languages : ${runnableLanguages.join(", ")}
|
|
4209
4254
|
`));
|
|
4210
|
-
process.stderr.write(
|
|
4255
|
+
process.stderr.write(chalk12.dim(` Elapsed : ${elapsed}s
|
|
4211
4256
|
`));
|
|
4212
4257
|
if (allErrors.length > 0) {
|
|
4213
|
-
process.stderr.write(
|
|
4258
|
+
process.stderr.write(chalk12.dim(` Errors : ${allErrors.length}
|
|
4214
4259
|
`));
|
|
4215
4260
|
for (const err of allErrors.slice(0, 10)) {
|
|
4216
|
-
process.stderr.write(
|
|
4261
|
+
process.stderr.write(chalk12.dim(` ${err}
|
|
4217
4262
|
`));
|
|
4218
4263
|
}
|
|
4219
4264
|
if (allErrors.length > 10) {
|
|
4220
|
-
process.stderr.write(
|
|
4265
|
+
process.stderr.write(chalk12.dim(` ... and ${allErrors.length - 10} more
|
|
4221
4266
|
`));
|
|
4222
4267
|
}
|
|
4223
4268
|
}
|
|
4224
|
-
process.stderr.write(
|
|
4269
|
+
process.stderr.write(chalk12.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n"));
|
|
4225
4270
|
}
|
|
4226
4271
|
if (hasTimeout) {
|
|
4227
|
-
process.stderr.write(
|
|
4272
|
+
process.stderr.write(chalk12.red(`Timeout exceeded (${timeoutMins} minutes)
|
|
4228
4273
|
`));
|
|
4229
4274
|
process.exit(EXIT_TIMEOUT);
|
|
4230
4275
|
}
|
|
4231
4276
|
if (hasSchemaFailure) {
|
|
4232
|
-
process.stderr.write(
|
|
4277
|
+
process.stderr.write(chalk12.red("Fact schema validation failures detected\n"));
|
|
4233
4278
|
process.exit(EXIT_SCHEMA_FAILURE);
|
|
4234
4279
|
}
|
|
4235
4280
|
if (hasParseFailure) {
|
|
4236
|
-
process.stderr.write(
|
|
4281
|
+
process.stderr.write(chalk12.red("Parsing failures detected\n"));
|
|
4237
4282
|
process.exit(EXIT_PARSE_FAILURE);
|
|
4238
4283
|
}
|
|
4239
4284
|
});
|
|
@@ -4243,7 +4288,7 @@ import * as path13 from "path";
|
|
|
4243
4288
|
import * as fs5 from "fs/promises";
|
|
4244
4289
|
import * as crypto4 from "crypto";
|
|
4245
4290
|
import { Command as Command11 } from "commander";
|
|
4246
|
-
import
|
|
4291
|
+
import chalk13 from "chalk";
|
|
4247
4292
|
var EXIT_INVALID_PATH = 2;
|
|
4248
4293
|
var EXIT_USAGE_ERROR2 = 5;
|
|
4249
4294
|
var VALID_FORMATS = /* @__PURE__ */ new Set(["ndjson", "json", "patch"]);
|
|
@@ -4476,7 +4521,7 @@ var diffCommand = new Command11("diff").description("Compare two directory trees
|
|
|
4476
4521
|
const genDir = path13.resolve(generatedPath);
|
|
4477
4522
|
for (const [label, dir] of [["original_path", origDir], ["generated_path", genDir]]) {
|
|
4478
4523
|
if (!await pathExists(dir)) {
|
|
4479
|
-
process.stderr.write(
|
|
4524
|
+
process.stderr.write(chalk13.red(`${label} does not exist: ${dir}
|
|
4480
4525
|
`));
|
|
4481
4526
|
process.exit(EXIT_INVALID_PATH);
|
|
4482
4527
|
}
|
|
@@ -4484,25 +4529,25 @@ var diffCommand = new Command11("diff").description("Compare two directory trees
|
|
|
4484
4529
|
try {
|
|
4485
4530
|
stat4 = await fs5.stat(dir);
|
|
4486
4531
|
} catch {
|
|
4487
|
-
process.stderr.write(
|
|
4532
|
+
process.stderr.write(chalk13.red(`Cannot read ${label}: ${dir}
|
|
4488
4533
|
`));
|
|
4489
4534
|
process.exit(EXIT_INVALID_PATH);
|
|
4490
4535
|
}
|
|
4491
4536
|
if (!stat4.isDirectory()) {
|
|
4492
|
-
process.stderr.write(
|
|
4537
|
+
process.stderr.write(chalk13.red(`${label} must be a directory: ${dir}
|
|
4493
4538
|
`));
|
|
4494
4539
|
process.exit(EXIT_INVALID_PATH);
|
|
4495
4540
|
}
|
|
4496
4541
|
}
|
|
4497
4542
|
const format = opts.format.toLowerCase();
|
|
4498
4543
|
if (!VALID_FORMATS.has(format)) {
|
|
4499
|
-
process.stderr.write(
|
|
4544
|
+
process.stderr.write(chalk13.red(`Invalid format: "${opts.format}". Allowed: ndjson, json, patch
|
|
4500
4545
|
`));
|
|
4501
4546
|
process.exit(EXIT_USAGE_ERROR2);
|
|
4502
4547
|
}
|
|
4503
4548
|
const contextLines = parseInt(opts.context, 10);
|
|
4504
4549
|
if (isNaN(contextLines) || contextLines < 0) {
|
|
4505
|
-
process.stderr.write(
|
|
4550
|
+
process.stderr.write(chalk13.red("--context must be >= 0\n"));
|
|
4506
4551
|
process.exit(EXIT_USAGE_ERROR2);
|
|
4507
4552
|
}
|
|
4508
4553
|
const includeGlobs = opts.include ? opts.include.split(",").map((g) => g.trim()).filter(Boolean) : void 0;
|
|
@@ -4514,9 +4559,9 @@ var diffCommand = new Command11("diff").description("Compare two directory trees
|
|
|
4514
4559
|
const origMap = new Map(origFiles.map((f) => [f.relPath, f]));
|
|
4515
4560
|
const genMap = new Map(genFiles.map((f) => [f.relPath, f]));
|
|
4516
4561
|
if (opts.verbose) {
|
|
4517
|
-
process.stderr.write(
|
|
4562
|
+
process.stderr.write(chalk13.dim(`Original: ${origFiles.length} files
|
|
4518
4563
|
`));
|
|
4519
|
-
process.stderr.write(
|
|
4564
|
+
process.stderr.write(chalk13.dim(`Generated: ${genFiles.length} files
|
|
4520
4565
|
`));
|
|
4521
4566
|
}
|
|
4522
4567
|
const deltas = [];
|
|
@@ -4550,7 +4595,7 @@ var diffCommand = new Command11("diff").description("Compare two directory trees
|
|
|
4550
4595
|
}
|
|
4551
4596
|
deltas.push(delta);
|
|
4552
4597
|
if (opts.verbose) {
|
|
4553
|
-
process.stderr.write(
|
|
4598
|
+
process.stderr.write(chalk13.dim(` modified: ${relPath} (+${diffResult.insertions} -${diffResult.deletions})
|
|
4554
4599
|
`));
|
|
4555
4600
|
}
|
|
4556
4601
|
}
|
|
@@ -4614,7 +4659,7 @@ var diffCommand = new Command11("diff").description("Compare two directory trees
|
|
|
4614
4659
|
}
|
|
4615
4660
|
deltas.push(delta);
|
|
4616
4661
|
if (opts.verbose) {
|
|
4617
|
-
process.stderr.write(
|
|
4662
|
+
process.stderr.write(chalk13.dim(
|
|
4618
4663
|
` renamed: ${pair.origPath} \u2192 ${pair.genPath} (${(pair.similarity * 100).toFixed(0)}% similar)
|
|
4619
4664
|
`
|
|
4620
4665
|
));
|
|
@@ -4639,7 +4684,7 @@ var diffCommand = new Command11("diff").description("Compare two directory trees
|
|
|
4639
4684
|
}
|
|
4640
4685
|
deltas.push(delta);
|
|
4641
4686
|
if (opts.verbose) {
|
|
4642
|
-
process.stderr.write(
|
|
4687
|
+
process.stderr.write(chalk13.dim(` removed: ${file.relPath}
|
|
4643
4688
|
`));
|
|
4644
4689
|
}
|
|
4645
4690
|
}
|
|
@@ -4661,7 +4706,7 @@ var diffCommand = new Command11("diff").description("Compare two directory trees
|
|
|
4661
4706
|
}
|
|
4662
4707
|
deltas.push(delta);
|
|
4663
4708
|
if (opts.verbose) {
|
|
4664
|
-
process.stderr.write(
|
|
4709
|
+
process.stderr.write(chalk13.dim(` added: ${file.relPath}
|
|
4665
4710
|
`));
|
|
4666
4711
|
}
|
|
4667
4712
|
}
|
|
@@ -4689,9 +4734,9 @@ var diffCommand = new Command11("diff").description("Compare two directory trees
|
|
|
4689
4734
|
const modified = deltas.filter((d) => d.changeKind === "modified").length;
|
|
4690
4735
|
const renamed = deltas.filter((d) => d.changeKind === "renamed").length;
|
|
4691
4736
|
process.stderr.write(
|
|
4692
|
-
|
|
4737
|
+
chalk13.green(`\u2714 `) + `${deltas.length} changes: ` + chalk13.green(`+${added} added`) + ", " + chalk13.red(`-${removed} removed`) + ", " + chalk13.yellow(`~${modified} modified`) + ", " + chalk13.blue(`\u2192${renamed} renamed`) + "\n"
|
|
4693
4738
|
);
|
|
4694
|
-
process.stderr.write(
|
|
4739
|
+
process.stderr.write(chalk13.dim(` Written to ${outPath}
|
|
4695
4740
|
`));
|
|
4696
4741
|
if (opts.verbose && opts.stats) {
|
|
4697
4742
|
let totalIns = 0;
|
|
@@ -4702,193 +4747,193 @@ var diffCommand = new Command11("diff").description("Compare two directory trees
|
|
|
4702
4747
|
totalDel += d.stats.deletions;
|
|
4703
4748
|
}
|
|
4704
4749
|
}
|
|
4705
|
-
process.stderr.write(
|
|
4750
|
+
process.stderr.write(chalk13.dim(` Total: +${totalIns} insertions, -${totalDel} deletions
|
|
4706
4751
|
`));
|
|
4707
4752
|
}
|
|
4708
4753
|
});
|
|
4709
4754
|
|
|
4710
4755
|
// src/commands/help.ts
|
|
4711
4756
|
import { Command as Command12 } from "commander";
|
|
4712
|
-
import
|
|
4757
|
+
import chalk14 from "chalk";
|
|
4713
4758
|
var HELP_URL = "https://vibgrate.com/help";
|
|
4714
4759
|
function printFooter() {
|
|
4715
4760
|
console.log("");
|
|
4716
|
-
console.log(
|
|
4761
|
+
console.log(chalk14.dim(`See ${HELP_URL} for more guidance`));
|
|
4717
4762
|
}
|
|
4718
4763
|
var detailedHelp = {
|
|
4719
4764
|
scan: () => {
|
|
4720
4765
|
console.log("");
|
|
4721
|
-
console.log(
|
|
4766
|
+
console.log(chalk14.bold.underline("vibgrate scan") + chalk14.dim(" \u2014 Scan a project for upgrade drift"));
|
|
4722
4767
|
console.log("");
|
|
4723
|
-
console.log(
|
|
4768
|
+
console.log(chalk14.bold("Usage:"));
|
|
4724
4769
|
console.log(" vibgrate scan [path] [options]");
|
|
4725
4770
|
console.log("");
|
|
4726
|
-
console.log(
|
|
4727
|
-
console.log(` ${
|
|
4771
|
+
console.log(chalk14.bold("Arguments:"));
|
|
4772
|
+
console.log(` ${chalk14.cyan("[path]")} Path to scan (default: current directory)`);
|
|
4728
4773
|
console.log("");
|
|
4729
|
-
console.log(
|
|
4730
|
-
console.log(` ${
|
|
4731
|
-
console.log(` ${
|
|
4774
|
+
console.log(chalk14.bold("Output options:"));
|
|
4775
|
+
console.log(` ${chalk14.cyan("--format <format>")} Output format: ${chalk14.white("text")} | json | sarif | md (default: text)`);
|
|
4776
|
+
console.log(` ${chalk14.cyan("--out <file>")} Write output to a file instead of stdout`);
|
|
4732
4777
|
console.log("");
|
|
4733
|
-
console.log(
|
|
4734
|
-
console.log(` ${
|
|
4735
|
-
console.log(` ${
|
|
4736
|
-
console.log(` ${
|
|
4737
|
-
console.log(` ${
|
|
4778
|
+
console.log(chalk14.bold("Baseline & gating:"));
|
|
4779
|
+
console.log(` ${chalk14.cyan("--baseline <file>")} Compare results against a saved baseline`);
|
|
4780
|
+
console.log(` ${chalk14.cyan("--drift-budget <score>")} Fail if drift score exceeds this value (0\u2013100)`);
|
|
4781
|
+
console.log(` ${chalk14.cyan("--drift-worsening <percent>")} Fail if drift worsens by more than % since baseline`);
|
|
4782
|
+
console.log(` ${chalk14.cyan("--fail-on <level>")} Fail exit code on warn or error findings`);
|
|
4738
4783
|
console.log("");
|
|
4739
|
-
console.log(
|
|
4740
|
-
console.log(` ${
|
|
4741
|
-
console.log(` ${
|
|
4784
|
+
console.log(chalk14.bold("Performance:"));
|
|
4785
|
+
console.log(` ${chalk14.cyan("--concurrency <n>")} Max concurrent registry calls (default: 8)`);
|
|
4786
|
+
console.log(` ${chalk14.cyan("--changed-only")} Only scan files changed since last git commit`);
|
|
4742
4787
|
console.log("");
|
|
4743
|
-
console.log(
|
|
4744
|
-
console.log(` ${
|
|
4745
|
-
console.log(` ${
|
|
4746
|
-
console.log(` ${
|
|
4747
|
-
console.log(` ${
|
|
4788
|
+
console.log(chalk14.bold("Privacy & offline:"));
|
|
4789
|
+
console.log(` ${chalk14.cyan("--offline")} Run without any network calls; skip result upload`);
|
|
4790
|
+
console.log(` ${chalk14.cyan("--package-manifest <file>")} Use a local package-version manifest (JSON or ZIP) for offline mode`);
|
|
4791
|
+
console.log(` ${chalk14.cyan("--no-local-artifacts")} Do not write .vibgrate JSON artifacts to disk`);
|
|
4792
|
+
console.log(` ${chalk14.cyan("--max-privacy")} Strongest privacy mode: minimal scanners + no local artifacts`);
|
|
4748
4793
|
console.log("");
|
|
4749
|
-
console.log(
|
|
4750
|
-
console.log(` ${
|
|
4751
|
-
console.log(` ${
|
|
4794
|
+
console.log(chalk14.bold("Tooling:"));
|
|
4795
|
+
console.log(` ${chalk14.cyan("--install-tools")} Auto-install missing security scanners via Homebrew`);
|
|
4796
|
+
console.log(` ${chalk14.cyan("--ui-purpose")} Enable UI purpose evidence extraction (slower)`);
|
|
4752
4797
|
console.log("");
|
|
4753
|
-
console.log(
|
|
4754
|
-
console.log(` ${
|
|
4755
|
-
console.log(` ${
|
|
4756
|
-
console.log(` ${
|
|
4757
|
-
console.log(` ${
|
|
4798
|
+
console.log(chalk14.bold("Uploading results:"));
|
|
4799
|
+
console.log(` ${chalk14.cyan("--push")} Auto-push results to Vibgrate API after scan`);
|
|
4800
|
+
console.log(` ${chalk14.cyan("--dsn <dsn>")} DSN token for push (or set VIBGRATE_DSN env var)`);
|
|
4801
|
+
console.log(` ${chalk14.cyan("--region <region>")} Data residency region: us | eu (default: us)`);
|
|
4802
|
+
console.log(` ${chalk14.cyan("--strict")} Fail if the upload to Vibgrate API fails`);
|
|
4758
4803
|
console.log("");
|
|
4759
|
-
console.log(
|
|
4760
|
-
console.log(` ${
|
|
4804
|
+
console.log(chalk14.bold("Examples:"));
|
|
4805
|
+
console.log(` ${chalk14.dim("# Scan the current directory and display a text report")}`);
|
|
4761
4806
|
console.log(" vibgrate scan .");
|
|
4762
4807
|
console.log("");
|
|
4763
|
-
console.log(` ${
|
|
4808
|
+
console.log(` ${chalk14.dim("# Scan, fail if drift score > 40, and write SARIF for GitHub Actions")}`);
|
|
4764
4809
|
console.log(" vibgrate scan . --drift-budget 40 --format sarif --out drift.sarif");
|
|
4765
4810
|
console.log("");
|
|
4766
|
-
console.log(` ${
|
|
4811
|
+
console.log(` ${chalk14.dim("# Scan and automatically upload results via a DSN")}`);
|
|
4767
4812
|
console.log(" vibgrate scan . --push --dsn $VIBGRATE_DSN");
|
|
4768
4813
|
console.log("");
|
|
4769
|
-
console.log(` ${
|
|
4814
|
+
console.log(` ${chalk14.dim("# Offline scan using a pre-downloaded package manifest")}`);
|
|
4770
4815
|
console.log(" vibgrate scan . --offline --package-manifest ./manifest.zip");
|
|
4771
4816
|
},
|
|
4772
4817
|
init: () => {
|
|
4773
4818
|
console.log("");
|
|
4774
|
-
console.log(
|
|
4819
|
+
console.log(chalk14.bold.underline("vibgrate init") + chalk14.dim(" \u2014 Initialise vibgrate in a project directory"));
|
|
4775
4820
|
console.log("");
|
|
4776
|
-
console.log(
|
|
4821
|
+
console.log(chalk14.bold("Usage:"));
|
|
4777
4822
|
console.log(" vibgrate init [path] [options]");
|
|
4778
4823
|
console.log("");
|
|
4779
|
-
console.log(
|
|
4780
|
-
console.log(` ${
|
|
4824
|
+
console.log(chalk14.bold("Arguments:"));
|
|
4825
|
+
console.log(` ${chalk14.cyan("[path]")} Directory to initialise (default: current directory)`);
|
|
4781
4826
|
console.log("");
|
|
4782
|
-
console.log(
|
|
4783
|
-
console.log(` ${
|
|
4784
|
-
console.log(` ${
|
|
4827
|
+
console.log(chalk14.bold("Options:"));
|
|
4828
|
+
console.log(` ${chalk14.cyan("--baseline")} Create an initial drift baseline after init`);
|
|
4829
|
+
console.log(` ${chalk14.cyan("--yes")} Skip all confirmation prompts`);
|
|
4785
4830
|
console.log("");
|
|
4786
|
-
console.log(
|
|
4831
|
+
console.log(chalk14.bold("What it does:"));
|
|
4787
4832
|
console.log(" \u2022 Creates a .vibgrate/ directory");
|
|
4788
4833
|
console.log(" \u2022 Writes a vibgrate.config.ts starter config");
|
|
4789
4834
|
console.log(" \u2022 Optionally runs an initial baseline scan (--baseline)");
|
|
4790
4835
|
console.log("");
|
|
4791
|
-
console.log(
|
|
4836
|
+
console.log(chalk14.bold("Examples:"));
|
|
4792
4837
|
console.log(" vibgrate init");
|
|
4793
4838
|
console.log(" vibgrate init ./my-project --baseline");
|
|
4794
4839
|
},
|
|
4795
4840
|
baseline: () => {
|
|
4796
4841
|
console.log("");
|
|
4797
|
-
console.log(
|
|
4842
|
+
console.log(chalk14.bold.underline("vibgrate baseline") + chalk14.dim(" \u2014 Save a drift baseline snapshot"));
|
|
4798
4843
|
console.log("");
|
|
4799
|
-
console.log(
|
|
4844
|
+
console.log(chalk14.bold("Usage:"));
|
|
4800
4845
|
console.log(" vibgrate baseline [path]");
|
|
4801
4846
|
console.log("");
|
|
4802
|
-
console.log(
|
|
4803
|
-
console.log(` ${
|
|
4847
|
+
console.log(chalk14.bold("Arguments:"));
|
|
4848
|
+
console.log(` ${chalk14.cyan("[path]")} Path to baseline (default: current directory)`);
|
|
4804
4849
|
console.log("");
|
|
4805
|
-
console.log(
|
|
4850
|
+
console.log(chalk14.bold("What it does:"));
|
|
4806
4851
|
console.log(" Runs a full scan and saves the result as .vibgrate/baseline.json.");
|
|
4807
4852
|
console.log(" Future scans can compare against this file using --baseline.");
|
|
4808
4853
|
console.log("");
|
|
4809
|
-
console.log(
|
|
4854
|
+
console.log(chalk14.bold("Examples:"));
|
|
4810
4855
|
console.log(" vibgrate baseline .");
|
|
4811
4856
|
console.log(" vibgrate scan . --baseline .vibgrate/baseline.json --drift-worsening 10");
|
|
4812
4857
|
},
|
|
4813
4858
|
report: () => {
|
|
4814
4859
|
console.log("");
|
|
4815
|
-
console.log(
|
|
4860
|
+
console.log(chalk14.bold.underline("vibgrate report") + chalk14.dim(" \u2014 Generate a report from a saved scan artifact"));
|
|
4816
4861
|
console.log("");
|
|
4817
|
-
console.log(
|
|
4862
|
+
console.log(chalk14.bold("Usage:"));
|
|
4818
4863
|
console.log(" vibgrate report [options]");
|
|
4819
4864
|
console.log("");
|
|
4820
|
-
console.log(
|
|
4821
|
-
console.log(` ${
|
|
4822
|
-
console.log(` ${
|
|
4865
|
+
console.log(chalk14.bold("Options:"));
|
|
4866
|
+
console.log(` ${chalk14.cyan("--in <file>")} Input artifact file (default: .vibgrate/scan_result.json)`);
|
|
4867
|
+
console.log(` ${chalk14.cyan("--format <format>")} Output format: ${chalk14.white("text")} | md | json (default: text)`);
|
|
4823
4868
|
console.log("");
|
|
4824
|
-
console.log(
|
|
4869
|
+
console.log(chalk14.bold("Examples:"));
|
|
4825
4870
|
console.log(" vibgrate report");
|
|
4826
4871
|
console.log(" vibgrate report --format md > DRIFT-REPORT.md");
|
|
4827
4872
|
console.log(" vibgrate report --in ./ci/scan_result.json --format json");
|
|
4828
4873
|
},
|
|
4829
4874
|
sbom: () => {
|
|
4830
4875
|
console.log("");
|
|
4831
|
-
console.log(
|
|
4876
|
+
console.log(chalk14.bold.underline("vibgrate sbom") + chalk14.dim(" \u2014 Export a Software Bill of Materials from a scan artifact"));
|
|
4832
4877
|
console.log("");
|
|
4833
|
-
console.log(
|
|
4878
|
+
console.log(chalk14.bold("Usage:"));
|
|
4834
4879
|
console.log(" vibgrate sbom [options]");
|
|
4835
4880
|
console.log("");
|
|
4836
|
-
console.log(
|
|
4837
|
-
console.log(` ${
|
|
4838
|
-
console.log(` ${
|
|
4839
|
-
console.log(` ${
|
|
4881
|
+
console.log(chalk14.bold("Options:"));
|
|
4882
|
+
console.log(` ${chalk14.cyan("--in <file>")} Input artifact (default: .vibgrate/scan_result.json)`);
|
|
4883
|
+
console.log(` ${chalk14.cyan("--format <format>")} SBOM format: ${chalk14.white("cyclonedx")} | spdx (default: cyclonedx)`);
|
|
4884
|
+
console.log(` ${chalk14.cyan("--out <file>")} Write SBOM to file instead of stdout`);
|
|
4840
4885
|
console.log("");
|
|
4841
|
-
console.log(
|
|
4886
|
+
console.log(chalk14.bold("Examples:"));
|
|
4842
4887
|
console.log(" vibgrate sbom --format cyclonedx --out sbom.json");
|
|
4843
4888
|
console.log(" vibgrate sbom --format spdx --out sbom.spdx.json");
|
|
4844
4889
|
},
|
|
4845
4890
|
push: () => {
|
|
4846
4891
|
console.log("");
|
|
4847
|
-
console.log(
|
|
4892
|
+
console.log(chalk14.bold.underline("vibgrate push") + chalk14.dim(" \u2014 Upload a scan artifact to the Vibgrate API"));
|
|
4848
4893
|
console.log("");
|
|
4849
|
-
console.log(
|
|
4894
|
+
console.log(chalk14.bold("Usage:"));
|
|
4850
4895
|
console.log(" vibgrate push [options]");
|
|
4851
4896
|
console.log("");
|
|
4852
|
-
console.log(
|
|
4853
|
-
console.log(` ${
|
|
4854
|
-
console.log(` ${
|
|
4855
|
-
console.log(` ${
|
|
4856
|
-
console.log(` ${
|
|
4897
|
+
console.log(chalk14.bold("Options:"));
|
|
4898
|
+
console.log(` ${chalk14.cyan("--dsn <dsn>")} DSN token (or set VIBGRATE_DSN env var)`);
|
|
4899
|
+
console.log(` ${chalk14.cyan("--file <file>")} Artifact to upload (default: .vibgrate/scan_result.json)`);
|
|
4900
|
+
console.log(` ${chalk14.cyan("--region <region>")} Override data residency region: us | eu`);
|
|
4901
|
+
console.log(` ${chalk14.cyan("--strict")} Fail with non-zero exit code on upload error`);
|
|
4857
4902
|
console.log("");
|
|
4858
|
-
console.log(
|
|
4903
|
+
console.log(chalk14.bold("Examples:"));
|
|
4859
4904
|
console.log(" vibgrate push --dsn $VIBGRATE_DSN");
|
|
4860
4905
|
console.log(" vibgrate push --file ./ci/scan_result.json --strict");
|
|
4861
4906
|
},
|
|
4862
4907
|
dsn: () => {
|
|
4863
4908
|
console.log("");
|
|
4864
|
-
console.log(
|
|
4909
|
+
console.log(chalk14.bold.underline("vibgrate dsn") + chalk14.dim(" \u2014 Manage DSN tokens for API authentication"));
|
|
4865
4910
|
console.log("");
|
|
4866
|
-
console.log(
|
|
4867
|
-
console.log(` ${
|
|
4911
|
+
console.log(chalk14.bold("Subcommands:"));
|
|
4912
|
+
console.log(` ${chalk14.cyan("vibgrate dsn create")} Generate a new DSN token`);
|
|
4868
4913
|
console.log("");
|
|
4869
|
-
console.log(
|
|
4870
|
-
console.log(` ${
|
|
4871
|
-
console.log(` ${
|
|
4872
|
-
console.log(` ${
|
|
4873
|
-
console.log(` ${
|
|
4914
|
+
console.log(chalk14.bold("dsn create options:"));
|
|
4915
|
+
console.log(` ${chalk14.cyan("--workspace <id>")} Workspace ID or "new" to auto-generate ${chalk14.red("(required)")}`);
|
|
4916
|
+
console.log(` ${chalk14.cyan("--region <region>")} Data residency region: us | eu (default: us)`);
|
|
4917
|
+
console.log(` ${chalk14.cyan("--ingest <url>")} Override ingest API URL`);
|
|
4918
|
+
console.log(` ${chalk14.cyan("--write <path>")} Write the DSN to a file (add to .gitignore!)`);
|
|
4874
4919
|
console.log("");
|
|
4875
|
-
console.log(
|
|
4920
|
+
console.log(chalk14.bold("Examples:"));
|
|
4876
4921
|
console.log(" vibgrate dsn create --workspace new");
|
|
4877
4922
|
console.log(" vibgrate dsn create --workspace abc123");
|
|
4878
4923
|
console.log(" vibgrate dsn create --workspace new --region eu --write .vibgrate/.dsn");
|
|
4879
4924
|
},
|
|
4880
4925
|
update: () => {
|
|
4881
4926
|
console.log("");
|
|
4882
|
-
console.log(
|
|
4927
|
+
console.log(chalk14.bold.underline("vibgrate update") + chalk14.dim(" \u2014 Update the vibgrate CLI to the latest version"));
|
|
4883
4928
|
console.log("");
|
|
4884
|
-
console.log(
|
|
4929
|
+
console.log(chalk14.bold("Usage:"));
|
|
4885
4930
|
console.log(" vibgrate update [options]");
|
|
4886
4931
|
console.log("");
|
|
4887
|
-
console.log(
|
|
4888
|
-
console.log(` ${
|
|
4889
|
-
console.log(` ${
|
|
4932
|
+
console.log(chalk14.bold("Options:"));
|
|
4933
|
+
console.log(` ${chalk14.cyan("--check")} Check for a newer version without installing`);
|
|
4934
|
+
console.log(` ${chalk14.cyan("--pm <manager>")} Force a package manager: npm | pnpm | yarn | bun`);
|
|
4890
4935
|
console.log("");
|
|
4891
|
-
console.log(
|
|
4936
|
+
console.log(chalk14.bold("Examples:"));
|
|
4892
4937
|
console.log(" vibgrate update");
|
|
4893
4938
|
console.log(" vibgrate update --check");
|
|
4894
4939
|
console.log(" vibgrate update --pm pnpm");
|
|
@@ -4896,33 +4941,33 @@ var detailedHelp = {
|
|
|
4896
4941
|
};
|
|
4897
4942
|
function printSummaryHelp() {
|
|
4898
4943
|
console.log("");
|
|
4899
|
-
console.log(
|
|
4944
|
+
console.log(chalk14.bold("vibgrate") + chalk14.dim(" \u2014 Continuous Drift Intelligence"));
|
|
4900
4945
|
console.log("");
|
|
4901
|
-
console.log(
|
|
4946
|
+
console.log(chalk14.bold("Usage:"));
|
|
4902
4947
|
console.log(" vibgrate <command> [options]");
|
|
4903
4948
|
console.log(" vibgrate help [command] Show detailed help for a command");
|
|
4904
4949
|
console.log("");
|
|
4905
|
-
console.log(
|
|
4906
|
-
console.log(` ${
|
|
4907
|
-
console.log(` ${
|
|
4908
|
-
console.log(` ${
|
|
4950
|
+
console.log(chalk14.bold("Getting started:"));
|
|
4951
|
+
console.log(` ${chalk14.cyan("init")} Initialise vibgrate in a project (creates config & .vibgrate/ dir)`);
|
|
4952
|
+
console.log(` ${chalk14.cyan("login")} Sign the CLI into your workspace via the browser`);
|
|
4953
|
+
console.log(` ${chalk14.cyan("logout")} Clear stored login credentials`);
|
|
4909
4954
|
console.log("");
|
|
4910
|
-
console.log(
|
|
4911
|
-
console.log(` ${
|
|
4912
|
-
console.log(` ${
|
|
4955
|
+
console.log(chalk14.bold("Core scanning:"));
|
|
4956
|
+
console.log(` ${chalk14.cyan("scan")} Scan a project for upgrade drift and generate a report`);
|
|
4957
|
+
console.log(` ${chalk14.cyan("baseline")} Save a baseline snapshot to compare future scans against`);
|
|
4913
4958
|
console.log("");
|
|
4914
|
-
console.log(
|
|
4915
|
-
console.log(` ${
|
|
4916
|
-
console.log(` ${
|
|
4959
|
+
console.log(chalk14.bold("Reporting & export:"));
|
|
4960
|
+
console.log(` ${chalk14.cyan("report")} Re-generate a report from a previously saved scan artifact`);
|
|
4961
|
+
console.log(` ${chalk14.cyan("sbom")} Export a Software Bill of Materials (CycloneDX or SPDX)`);
|
|
4917
4962
|
console.log("");
|
|
4918
|
-
console.log(
|
|
4919
|
-
console.log(` ${
|
|
4920
|
-
console.log(` ${
|
|
4963
|
+
console.log(chalk14.bold("CI/CD integration:"));
|
|
4964
|
+
console.log(` ${chalk14.cyan("push")} Upload a scan artifact to the Vibgrate API`);
|
|
4965
|
+
console.log(` ${chalk14.cyan("dsn")} Create and manage DSN tokens for API authentication`);
|
|
4921
4966
|
console.log("");
|
|
4922
|
-
console.log(
|
|
4923
|
-
console.log(` ${
|
|
4967
|
+
console.log(chalk14.bold("Maintenance:"));
|
|
4968
|
+
console.log(` ${chalk14.cyan("update")} Update the vibgrate CLI to the latest version`);
|
|
4924
4969
|
console.log("");
|
|
4925
|
-
console.log(
|
|
4970
|
+
console.log(chalk14.dim("Run") + ` ${chalk14.cyan("vibgrate help <command>")} ` + chalk14.dim("for detailed options, e.g.") + ` ${chalk14.cyan("vibgrate help scan")}`);
|
|
4926
4971
|
}
|
|
4927
4972
|
var helpCommand = new Command12("help").description("Show help for vibgrate commands").argument("[command]", "Command to show detailed help for").helpOption(false).action((cmd) => {
|
|
4928
4973
|
const name = cmd?.toLowerCase().trim();
|
|
@@ -4930,8 +4975,8 @@ var helpCommand = new Command12("help").description("Show help for vibgrate comm
|
|
|
4930
4975
|
detailedHelp[name]();
|
|
4931
4976
|
} else if (name) {
|
|
4932
4977
|
console.log("");
|
|
4933
|
-
console.log(
|
|
4934
|
-
console.log(
|
|
4978
|
+
console.log(chalk14.red(`Unknown command: ${name}`));
|
|
4979
|
+
console.log(chalk14.dim(`Available commands: ${Object.keys(detailedHelp).join(", ")}`));
|
|
4935
4980
|
printSummaryHelp();
|
|
4936
4981
|
} else {
|
|
4937
4982
|
printSummaryHelp();
|
|
@@ -4959,8 +5004,8 @@ function notifyIfUpdateAvailable() {
|
|
|
4959
5004
|
void checkForUpdate().then((update) => {
|
|
4960
5005
|
if (!update?.updateAvailable) return;
|
|
4961
5006
|
console.error("");
|
|
4962
|
-
console.error(
|
|
4963
|
-
console.error(
|
|
5007
|
+
console.error(chalk15.yellow(` Update available: ${update.current} \u2192 ${update.latest}`));
|
|
5008
|
+
console.error(chalk15.dim(' Run "vibgrate update" to install the latest version.'));
|
|
4964
5009
|
console.error("");
|
|
4965
5010
|
}).catch(() => {
|
|
4966
5011
|
});
|