korext 0.9.9 → 0.9.11
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/bin/korext.js +112 -17
- package/package.json +1 -1
package/bin/korext.js
CHANGED
|
@@ -367,7 +367,7 @@ program
|
|
|
367
367
|
program
|
|
368
368
|
.command('login [token]')
|
|
369
369
|
.description('Authenticate with the Korext platform using a personal access token')
|
|
370
|
-
.action((tokenArg) => {
|
|
370
|
+
.action(async (tokenArg) => {
|
|
371
371
|
let token = tokenArg;
|
|
372
372
|
if (!token) {
|
|
373
373
|
console.log(`\nTo authenticate, sign in at ${chalk.cyan('https://app.korext.com')} and use your session token.`);
|
|
@@ -377,10 +377,31 @@ program
|
|
|
377
377
|
console.log(`\nThen run: ${chalk.green('korext login <your-token>')}\n`);
|
|
378
378
|
process.exit(1);
|
|
379
379
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
380
|
+
// Validate token with the server before saving
|
|
381
|
+
const spinner = ora('Validating token...').start();
|
|
382
|
+
try {
|
|
383
|
+
const res = await fetch(`${API_URL}/api/ide/status`, {
|
|
384
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
385
|
+
signal: AbortSignal.timeout(8000),
|
|
386
|
+
});
|
|
387
|
+
if (res.ok) {
|
|
388
|
+
const config = getConfig();
|
|
389
|
+
config.token = token;
|
|
390
|
+
saveConfig(config);
|
|
391
|
+
const data = await res.json().catch(() => ({}));
|
|
392
|
+
spinner.succeed(chalk.green(`Authenticated as ${data.workspace || 'developer'}. Token saved.`));
|
|
393
|
+
} else {
|
|
394
|
+
spinner.fail(chalk.red('Invalid or expired token. Not saved.'));
|
|
395
|
+
console.log(chalk.dim(`\nSign in at ${chalk.cyan('https://app.korext.com')} to get a valid token.`));
|
|
396
|
+
process.exit(1);
|
|
397
|
+
}
|
|
398
|
+
} catch (e) {
|
|
399
|
+
// Network error: save token optimistically but warn
|
|
400
|
+
const config = getConfig();
|
|
401
|
+
config.token = token;
|
|
402
|
+
saveConfig(config);
|
|
403
|
+
spinner.warn(chalk.yellow('Could not reach server to validate token. Token saved (will be verified on next command).'));
|
|
404
|
+
}
|
|
384
405
|
});
|
|
385
406
|
|
|
386
407
|
program
|
|
@@ -609,18 +630,65 @@ program
|
|
|
609
630
|
});
|
|
610
631
|
|
|
611
632
|
console.log();
|
|
612
|
-
const industryInput = await ask(chalk.bold(' Select industries (
|
|
633
|
+
const industryInput = await ask(chalk.bold(' Select industries or packs (numbers, IDs, or names): '));
|
|
613
634
|
|
|
614
|
-
// Parse user input
|
|
615
|
-
const
|
|
616
|
-
|
|
635
|
+
// Parse user input: accept numbers, industry IDs, industry names (fuzzy), or pack IDs
|
|
636
|
+
const rawTokens = industryInput.split(',').map(s => s.trim()).filter(Boolean);
|
|
637
|
+
const selectedIndustries = [];
|
|
638
|
+
const directPacks = [];
|
|
639
|
+
|
|
640
|
+
for (const token of rawTokens) {
|
|
641
|
+
const lower = token.toLowerCase();
|
|
642
|
+
|
|
643
|
+
// Strategy 1: Number (e.g., "8" for Finance)
|
|
644
|
+
const num = parseInt(token, 10);
|
|
617
645
|
if (!isNaN(num) && num >= 1 && num <= sortedIndustries.length) {
|
|
618
|
-
|
|
646
|
+
selectedIndustries.push(sortedIndustries[num - 1][0]);
|
|
647
|
+
continue;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// Strategy 2: Industry ID exact match (e.g., "finance")
|
|
651
|
+
if (taxonomy.industries[lower]) {
|
|
652
|
+
selectedIndustries.push(lower);
|
|
653
|
+
continue;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// Strategy 3: Industry label fuzzy match (e.g., "Finance", "health", "aero")
|
|
657
|
+
const labelMatch = sortedIndustries.find(([id, group]) =>
|
|
658
|
+
group.label.toLowerCase().startsWith(lower)
|
|
659
|
+
);
|
|
660
|
+
if (labelMatch) {
|
|
661
|
+
selectedIndustries.push(labelMatch[0]);
|
|
662
|
+
console.log(chalk.dim(` "${token}" matched industry: ${labelMatch[1].label}`));
|
|
663
|
+
continue;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// Strategy 4: Direct pack ID (e.g., "web", "pci-dss-v1", "security")
|
|
667
|
+
const allPackIds = Object.keys(defs.packs);
|
|
668
|
+
if (allPackIds.includes(lower)) {
|
|
669
|
+
directPacks.push(lower);
|
|
670
|
+
console.log(chalk.dim(` "${token}" is a pack ID, adding directly.`));
|
|
671
|
+
continue;
|
|
619
672
|
}
|
|
620
|
-
return s.toLowerCase();
|
|
621
|
-
}).filter(id => taxonomy.industries[id]);
|
|
622
673
|
|
|
623
|
-
|
|
674
|
+
// Nothing matched
|
|
675
|
+
console.log(chalk.yellow(` Unknown input: "${token}" (not an industry, pack ID, or name)`));
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// If user typed only pack IDs (e.g., "web" or "web, security"), skip region prompt
|
|
679
|
+
if (selectedIndustries.length === 0 && directPacks.length > 0) {
|
|
680
|
+
const config = {
|
|
681
|
+
targetPacks: directPacks,
|
|
682
|
+
exclude: ['node_modules', 'dist', 'build', '.next']
|
|
683
|
+
};
|
|
684
|
+
fs.writeFileSync(outputPath, JSON.stringify(config, null, 2));
|
|
685
|
+
console.log(chalk.green(`\n Created ${outputPath} with packs: ${directPacks.join(', ')}\n`));
|
|
686
|
+
console.log(chalk.dim(` Run: ${chalk.green('korext enforce .')} to enforce these packs.\n`));
|
|
687
|
+
rl.close();
|
|
688
|
+
process.exit(0);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
if (selectedIndustries.length === 0 && directPacks.length === 0) {
|
|
624
692
|
console.log(chalk.yellow('\n No valid industries selected. Using default: web\n'));
|
|
625
693
|
const config = {
|
|
626
694
|
targetPacks: ['web'],
|
|
@@ -655,22 +723,25 @@ program
|
|
|
655
723
|
}
|
|
656
724
|
}
|
|
657
725
|
|
|
658
|
-
// Resolve packs
|
|
726
|
+
// Resolve packs from industries
|
|
659
727
|
const resolvedPacks = resolvePacksForIndustryRegion(
|
|
660
728
|
selectedIndustries.join(','),
|
|
661
729
|
selectedRegion,
|
|
662
730
|
taxonomy
|
|
663
731
|
);
|
|
664
732
|
|
|
665
|
-
|
|
666
|
-
|
|
733
|
+
// Merge any direct pack IDs the user typed alongside industries
|
|
734
|
+
const allPacks = [...new Set([...resolvedPacks, ...directPacks])];
|
|
735
|
+
|
|
736
|
+
console.log(chalk.green(`\n Resolved ${allPacks.length} packs for ${selectedIndustries.map(id => INDUSTRY_LABELS[id]).join(', ')}${selectedRegion ? ` (${REGION_LABELS[selectedRegion] || selectedRegion})` : ''}${directPacks.length > 0 ? ` + ${directPacks.join(', ')}` : ''}:`));
|
|
737
|
+
for (const pid of allPacks) {
|
|
667
738
|
console.log(` ${chalk.cyan(pid)}`);
|
|
668
739
|
}
|
|
669
740
|
|
|
670
741
|
const config = {
|
|
671
742
|
industry: selectedIndustries.join(','),
|
|
672
743
|
...(selectedRegion && { region: selectedRegion }),
|
|
673
|
-
targetPacks:
|
|
744
|
+
targetPacks: allPacks,
|
|
674
745
|
exclude: ['node_modules', 'dist', 'build', '.next']
|
|
675
746
|
};
|
|
676
747
|
|
|
@@ -823,6 +894,22 @@ program
|
|
|
823
894
|
// Priority 1: User explicitly passed --pack
|
|
824
895
|
packIds = options.pack.split(',').map(s => s.trim()).filter(Boolean);
|
|
825
896
|
packSource = 'flag';
|
|
897
|
+
|
|
898
|
+
// Validate pack IDs against cached definitions
|
|
899
|
+
const valDefs = getRuleDefinitionsCache();
|
|
900
|
+
if (valDefs && valDefs.packs) {
|
|
901
|
+
const unknownPacks = packIds.filter(pid => !valDefs.packs[pid]);
|
|
902
|
+
if (unknownPacks.length > 0) {
|
|
903
|
+
if (isText) {
|
|
904
|
+
console.error(chalk.red(`\n✖ Unknown pack ID${unknownPacks.length > 1 ? 's' : ''}: ${unknownPacks.join(', ')}`));
|
|
905
|
+
console.error(chalk.dim(` Run ${chalk.green('korext packs list')} to see available packs.`));
|
|
906
|
+
console.error(chalk.dim(` Run ${chalk.green('korext rules sync')} to update cached definitions.\n`));
|
|
907
|
+
} else {
|
|
908
|
+
console.error(JSON.stringify({ error: `Unknown pack ID(s): ${unknownPacks.join(', ')}` }));
|
|
909
|
+
}
|
|
910
|
+
process.exit(1);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
826
913
|
} else if (options.industry) {
|
|
827
914
|
// Priority 2: --industry (and optional --region) flags
|
|
828
915
|
const taxonomy = tryBuildTaxonomy();
|
|
@@ -958,6 +1045,14 @@ program
|
|
|
958
1045
|
console.log(chalk.dim(`Run ${chalk.green('korext login')} to authenticate for unlimited CI/CD analytics.\n`));
|
|
959
1046
|
}
|
|
960
1047
|
|
|
1048
|
+
// BUG-002 fix: Warn when --sign is used without valid auth
|
|
1049
|
+
if (options.sign && !token) {
|
|
1050
|
+
if (isText) {
|
|
1051
|
+
console.log(chalk.red('⚠ --sign requires authentication. Proof bundles will not be signed.'));
|
|
1052
|
+
console.log(chalk.dim(` Run ${chalk.green('korext login <token>')} first, then retry with --sign.\n`));
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
|
|
961
1056
|
const report = {
|
|
962
1057
|
version,
|
|
963
1058
|
packId: Array.isArray(pack) ? pack[0] : pack,
|