@kb-labs/cli-commands 2.31.0 → 2.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{discover-DhqYgS-M.d.ts → discover-Cz7X5bQp.d.ts} +17 -1
- package/dist/index.d.ts +9 -2
- package/dist/index.js +101 -39
- package/dist/index.js.map +1 -1
- package/dist/registry/index.d.ts +2 -2
- package/dist/registry/index.js +97 -37
- package/dist/registry/index.js.map +1 -1
- package/package.json +22 -22
|
@@ -46,6 +46,7 @@ interface RegisteredCommand {
|
|
|
46
46
|
unavailableReason?: string;
|
|
47
47
|
hint?: string;
|
|
48
48
|
source: 'workspace' | 'node_modules' | 'linked' | 'builtin';
|
|
49
|
+
scope?: 'platform' | 'project';
|
|
49
50
|
shadowed: boolean;
|
|
50
51
|
pkgRoot?: string;
|
|
51
52
|
packageName?: string;
|
|
@@ -55,6 +56,13 @@ interface CommandModule {
|
|
|
55
56
|
}
|
|
56
57
|
interface DiscoveryResult {
|
|
57
58
|
source: 'workspace' | 'node_modules' | 'linked' | 'builtin';
|
|
59
|
+
/**
|
|
60
|
+
* Scope this result came from. Platform-wide discovery (monorepo workspace,
|
|
61
|
+
* platform node_modules) reports `platform`; discovery from
|
|
62
|
+
* `<projectRoot>/.kb/plugins/` reports `project`. On collision (same
|
|
63
|
+
* packageName in both scopes) the platform entry wins — see ADR-0012.
|
|
64
|
+
*/
|
|
65
|
+
scope: 'platform' | 'project';
|
|
58
66
|
packageName: string;
|
|
59
67
|
manifestPath: string;
|
|
60
68
|
pkgRoot: string;
|
|
@@ -88,7 +96,14 @@ interface CacheFile {
|
|
|
88
96
|
lockfileHash?: string;
|
|
89
97
|
configHash?: string;
|
|
90
98
|
pluginsStateHash?: string;
|
|
91
|
-
|
|
99
|
+
/** Hash of <platformRoot>/.kb/marketplace.lock. Invalidates when global plugins change. */
|
|
100
|
+
platformMarketplaceLockHash?: string;
|
|
101
|
+
/** Hash of <projectRoot>/.kb/marketplace.lock. Invalidates when project-scoped plugins change. */
|
|
102
|
+
projectMarketplaceLockHash?: string;
|
|
103
|
+
/** Absolute platform root at the time of caching — cache is invalid if it differs on next run. */
|
|
104
|
+
platformRoot?: string;
|
|
105
|
+
/** Absolute project root at the time of caching. */
|
|
106
|
+
projectRoot?: string;
|
|
92
107
|
packages: Record<string, PackageCacheEntry>;
|
|
93
108
|
}
|
|
94
109
|
type AvailabilityCheck = {
|
|
@@ -261,6 +276,7 @@ declare function resetInProcCache(): void;
|
|
|
261
276
|
*/
|
|
262
277
|
declare function discoverManifests(cwd: string, noCache?: boolean, options?: {
|
|
263
278
|
platformRoot?: string;
|
|
279
|
+
projectRoot?: string;
|
|
264
280
|
}): Promise<DiscoveryResult[]>;
|
|
265
281
|
/**
|
|
266
282
|
* Lazy load manifests for a specific namespace
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as CommandGroup, P as ProductGroup, a as Command, R as RegisteredCommand } from './discover-
|
|
2
|
-
export { A as AvailabilityCheck, b as CacheFile, c as CommandLookupResult, d as CommandManifest, e as CommandModule, f as CommandType, D as DiscoveryResult, F as FlagDefinition, G as GlobalFlags, g as PackageCacheEntry, h as discoverManifests, i as discoverManifestsByNamespace, j as findCommand, k as findCommandWithType, r as registry } from './discover-
|
|
1
|
+
import { C as CommandGroup, P as ProductGroup, a as Command, R as RegisteredCommand } from './discover-Cz7X5bQp.js';
|
|
2
|
+
export { A as AvailabilityCheck, b as CacheFile, c as CommandLookupResult, d as CommandManifest, e as CommandModule, f as CommandType, D as DiscoveryResult, F as FlagDefinition, G as GlobalFlags, g as PackageCacheEntry, h as discoverManifests, i as discoverManifestsByNamespace, j as findCommand, k as findCommandWithType, r as registry } from './discover-Cz7X5bQp.js';
|
|
3
3
|
import { ILogger } from '@kb-labs/core-platform';
|
|
4
4
|
export { TimingTracker } from '@kb-labs/shared-cli-ui';
|
|
5
5
|
import * as _kb_labs_shared_command_kit from '@kb-labs/shared-command-kit';
|
|
@@ -17,6 +17,13 @@ interface RegisterBuiltinCommandsInput {
|
|
|
17
17
|
* dev-mode behavior unchanged.
|
|
18
18
|
*/
|
|
19
19
|
platformRoot?: string;
|
|
20
|
+
/**
|
|
21
|
+
* The user's project root (directory containing `.kb/kb.config.{json,jsonc}`).
|
|
22
|
+
* Discovery uses this to pick up project-scope plugins from
|
|
23
|
+
* `<projectRoot>/.kb/plugins/` and to invalidate the cache when the
|
|
24
|
+
* project's `marketplace.lock` changes. When omitted, falls back to `cwd`.
|
|
25
|
+
*/
|
|
26
|
+
projectRoot?: string;
|
|
20
27
|
}
|
|
21
28
|
declare function registerBuiltinCommands(input?: RegisterBuiltinCommandsInput): Promise<void>;
|
|
22
29
|
|
package/dist/index.js
CHANGED
|
@@ -249,8 +249,8 @@ async function computePluginsStateHash(cwd) {
|
|
|
249
249
|
return "";
|
|
250
250
|
}
|
|
251
251
|
}
|
|
252
|
-
async function
|
|
253
|
-
const lockPath = path.join(
|
|
252
|
+
async function computeMarketplaceLockHashAt(root) {
|
|
253
|
+
const lockPath = path.join(root, ".kb", "marketplace.lock");
|
|
254
254
|
try {
|
|
255
255
|
const content = await promises.readFile(lockPath, "utf8");
|
|
256
256
|
return createHash("sha256").update(content).digest("hex");
|
|
@@ -479,22 +479,19 @@ async function discoverWorkspace(cwd) {
|
|
|
479
479
|
}
|
|
480
480
|
}
|
|
481
481
|
}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
PACKAGE_JSON
|
|
489
|
-
);
|
|
490
|
-
const localPluginFiles = await glob(localPluginPattern, {
|
|
491
|
-
cwd,
|
|
482
|
+
return loadManifestsForPackages(packageInfos, "workspace", "platform");
|
|
483
|
+
}
|
|
484
|
+
async function discoverProjectLocalPlugins(projectRoot) {
|
|
485
|
+
const pattern = path.join(".kb", "plugins", "*", "packages", "*-entry", PACKAGE_JSON);
|
|
486
|
+
const files = await glob(pattern, {
|
|
487
|
+
cwd: projectRoot,
|
|
492
488
|
absolute: false,
|
|
493
489
|
ignore: ["**/node_modules/**"]
|
|
494
490
|
});
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
const
|
|
491
|
+
const packageInfos = [];
|
|
492
|
+
for (const pkgFile of files) {
|
|
493
|
+
const pkgRoot = path.dirname(path.join(projectRoot, pkgFile));
|
|
494
|
+
const pkg = await readPackageJson(path.join(projectRoot, pkgFile));
|
|
498
495
|
if (!pkg) {
|
|
499
496
|
continue;
|
|
500
497
|
}
|
|
@@ -503,6 +500,9 @@ async function discoverWorkspace(cwd) {
|
|
|
503
500
|
packageInfos.push({ pkgRoot, pkg, manifestPath: manifestInfo.path });
|
|
504
501
|
}
|
|
505
502
|
}
|
|
503
|
+
return loadManifestsForPackages(packageInfos, "linked", "project");
|
|
504
|
+
}
|
|
505
|
+
async function loadManifestsForPackages(packageInfos, source, scope) {
|
|
506
506
|
const loadPromises = packageInfos.map(async ({ pkgRoot, pkg, manifestPath }) => {
|
|
507
507
|
const pkgStart = Date.now();
|
|
508
508
|
try {
|
|
@@ -515,7 +515,8 @@ async function discoverWorkspace(cwd) {
|
|
|
515
515
|
validateUniqueIds(manifests, pkg.name);
|
|
516
516
|
return {
|
|
517
517
|
manifests,
|
|
518
|
-
source
|
|
518
|
+
source,
|
|
519
|
+
scope,
|
|
519
520
|
packageName: pkg.name,
|
|
520
521
|
manifestPath: toPosixPath(manifestPath),
|
|
521
522
|
pkgRoot: toPosixPath(pkgRoot)
|
|
@@ -536,7 +537,8 @@ async function discoverWorkspace(cwd) {
|
|
|
536
537
|
const synthetic = createUnavailableManifest(pkg.name, err);
|
|
537
538
|
return {
|
|
538
539
|
manifests: [synthetic],
|
|
539
|
-
source
|
|
540
|
+
source,
|
|
541
|
+
scope,
|
|
540
542
|
packageName: pkg.name,
|
|
541
543
|
manifestPath: toPosixPath(manifestPath),
|
|
542
544
|
pkgRoot: toPosixPath(pkgRoot)
|
|
@@ -570,6 +572,9 @@ async function discoverCurrentPackage(cwd) {
|
|
|
570
572
|
return {
|
|
571
573
|
manifests,
|
|
572
574
|
source: "workspace",
|
|
575
|
+
// Current-package fallback fires in installed mode when the user's
|
|
576
|
+
// project itself exposes a manifest. That maps to the project scope.
|
|
577
|
+
scope: "project",
|
|
573
578
|
packageName: pkg.name,
|
|
574
579
|
manifestPath: toPosixPath(manifestInfo.path),
|
|
575
580
|
pkgRoot: toPosixPath(cwd)
|
|
@@ -674,6 +679,10 @@ async function discoverNodeModules(cwd) {
|
|
|
674
679
|
return {
|
|
675
680
|
manifests,
|
|
676
681
|
source: isLinked ? "linked" : "node_modules",
|
|
682
|
+
// node_modules discovery runs at the platform root. In dev mode
|
|
683
|
+
// platformRoot === projectRoot so either label is correct; we use
|
|
684
|
+
// platform to reflect where the manifest physically lives.
|
|
685
|
+
scope: "platform",
|
|
677
686
|
packageName: pkg.name,
|
|
678
687
|
manifestPath: toPosixPath(manifestPath),
|
|
679
688
|
pkgRoot: toPosixPath(pkgRoot)
|
|
@@ -693,6 +702,7 @@ async function discoverNodeModules(cwd) {
|
|
|
693
702
|
return {
|
|
694
703
|
manifests: [synthetic],
|
|
695
704
|
source: isLinked ? "linked" : "node_modules",
|
|
705
|
+
scope: "platform",
|
|
696
706
|
packageName: pkg.name,
|
|
697
707
|
manifestPath: toPosixPath(manifestPath),
|
|
698
708
|
pkgRoot: toPosixPath(pkgRoot)
|
|
@@ -724,17 +734,35 @@ function deduplicateManifests(all) {
|
|
|
724
734
|
const existing = byPackageName.get(result.packageName);
|
|
725
735
|
if (!existing) {
|
|
726
736
|
byPackageName.set(result.packageName, result);
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
if (
|
|
731
|
-
|
|
737
|
+
continue;
|
|
738
|
+
}
|
|
739
|
+
if (existing.scope !== result.scope) {
|
|
740
|
+
if (existing.pkgRoot === result.pkgRoot) {
|
|
741
|
+
const projectEntry = existing.scope === "project" ? existing : result;
|
|
742
|
+
byPackageName.set(result.packageName, projectEntry);
|
|
743
|
+
continue;
|
|
732
744
|
}
|
|
745
|
+
const winner = existing.scope === "platform" ? existing : result;
|
|
746
|
+
const loser = existing.scope === "platform" ? result : existing;
|
|
747
|
+
log("warn", JSON.stringify({
|
|
748
|
+
code: "DISCOVERY_SCOPE_COLLISION",
|
|
749
|
+
packageName: result.packageName,
|
|
750
|
+
platformPath: winner.pkgRoot,
|
|
751
|
+
projectPath: loser.pkgRoot,
|
|
752
|
+
message: "Package exists in both platform and project scopes \u2014 platform wins."
|
|
753
|
+
}));
|
|
754
|
+
byPackageName.set(result.packageName, winner);
|
|
755
|
+
continue;
|
|
756
|
+
}
|
|
757
|
+
const existingPriority = priority[existing.source] ?? 0;
|
|
758
|
+
const newPriority = priority[result.source] ?? 0;
|
|
759
|
+
if (newPriority > existingPriority) {
|
|
760
|
+
byPackageName.set(result.packageName, result);
|
|
733
761
|
}
|
|
734
762
|
}
|
|
735
763
|
return Array.from(byPackageName.values());
|
|
736
764
|
}
|
|
737
|
-
async function loadCache(cwd) {
|
|
765
|
+
async function loadCache(cwd, roots) {
|
|
738
766
|
const cachePath = path.join(cwd, ".kb", "cache", "cli-manifests.json");
|
|
739
767
|
try {
|
|
740
768
|
const content = await promises.readFile(cachePath, "utf8");
|
|
@@ -771,11 +799,26 @@ async function loadCache(cwd) {
|
|
|
771
799
|
log("debug", "Cache invalidated: .kb/plugins.json changed");
|
|
772
800
|
return null;
|
|
773
801
|
}
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
802
|
+
if (cache.platformRoot && cache.platformRoot !== roots.platformRoot) {
|
|
803
|
+
log("debug", `Cache invalidated: platformRoot changed (${cache.platformRoot} \u2192 ${roots.platformRoot})`);
|
|
804
|
+
return null;
|
|
805
|
+
}
|
|
806
|
+
if (cache.projectRoot && cache.projectRoot !== roots.projectRoot) {
|
|
807
|
+
log("debug", `Cache invalidated: projectRoot changed (${cache.projectRoot} \u2192 ${roots.projectRoot})`);
|
|
808
|
+
return null;
|
|
809
|
+
}
|
|
810
|
+
const currentPlatformLockHash = await computeMarketplaceLockHashAt(roots.platformRoot);
|
|
811
|
+
if (currentPlatformLockHash && cache.platformMarketplaceLockHash && cache.platformMarketplaceLockHash !== currentPlatformLockHash) {
|
|
812
|
+
log("debug", `Cache invalidated: ${roots.platformRoot}/.kb/marketplace.lock changed`);
|
|
777
813
|
return null;
|
|
778
814
|
}
|
|
815
|
+
if (roots.projectRoot !== roots.platformRoot) {
|
|
816
|
+
const currentProjectLockHash = await computeMarketplaceLockHashAt(roots.projectRoot);
|
|
817
|
+
if (currentProjectLockHash && cache.projectMarketplaceLockHash && cache.projectMarketplaceLockHash !== currentProjectLockHash) {
|
|
818
|
+
log("debug", `Cache invalidated: ${roots.projectRoot}/.kb/marketplace.lock changed`);
|
|
819
|
+
return null;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
779
822
|
const parsedCache = cache;
|
|
780
823
|
parsedCache.ttlMs = parsedCache.ttlMs ?? DISK_CACHE_TTL_MS;
|
|
781
824
|
for (const entry of Object.values(parsedCache.packages)) {
|
|
@@ -827,7 +870,7 @@ async function isPackageCacheStale(entry, options) {
|
|
|
827
870
|
}
|
|
828
871
|
return false;
|
|
829
872
|
}
|
|
830
|
-
async function saveCache(cwd, results) {
|
|
873
|
+
async function saveCache(cwd, results, roots) {
|
|
831
874
|
const cachePath = path.join(cwd, ".kb", "cache", "cli-manifests.json");
|
|
832
875
|
await promises.mkdir(path.dirname(cachePath), { recursive: true });
|
|
833
876
|
const packages = {};
|
|
@@ -873,7 +916,8 @@ async function saveCache(cwd, results) {
|
|
|
873
916
|
const lockfileHash = await computeLockfileHash(cwd);
|
|
874
917
|
const configHash = await computeConfigHash(cwd);
|
|
875
918
|
const pluginsStateHash = await computePluginsStateHash(cwd);
|
|
876
|
-
const
|
|
919
|
+
const platformMarketplaceLockHash = await computeMarketplaceLockHashAt(roots.platformRoot);
|
|
920
|
+
const projectMarketplaceLockHash = roots.projectRoot !== roots.platformRoot ? await computeMarketplaceLockHashAt(roots.projectRoot) : "";
|
|
877
921
|
if (lockfileHash) {
|
|
878
922
|
stateHasher.update(lockfileHash);
|
|
879
923
|
}
|
|
@@ -883,9 +927,14 @@ async function saveCache(cwd, results) {
|
|
|
883
927
|
if (pluginsStateHash) {
|
|
884
928
|
stateHasher.update(pluginsStateHash);
|
|
885
929
|
}
|
|
886
|
-
if (
|
|
887
|
-
stateHasher.update(
|
|
930
|
+
if (platformMarketplaceLockHash) {
|
|
931
|
+
stateHasher.update(platformMarketplaceLockHash);
|
|
932
|
+
}
|
|
933
|
+
if (projectMarketplaceLockHash) {
|
|
934
|
+
stateHasher.update(projectMarketplaceLockHash);
|
|
888
935
|
}
|
|
936
|
+
stateHasher.update(roots.platformRoot);
|
|
937
|
+
stateHasher.update(roots.projectRoot);
|
|
889
938
|
const cache = {
|
|
890
939
|
version: process.version,
|
|
891
940
|
cliVersion: process.env.CLI_VERSION || "0.1.0",
|
|
@@ -895,7 +944,10 @@ async function saveCache(cwd, results) {
|
|
|
895
944
|
lockfileHash: lockfileHash || void 0,
|
|
896
945
|
configHash: configHash || void 0,
|
|
897
946
|
pluginsStateHash: pluginsStateHash || void 0,
|
|
898
|
-
|
|
947
|
+
platformMarketplaceLockHash: platformMarketplaceLockHash || void 0,
|
|
948
|
+
projectMarketplaceLockHash: projectMarketplaceLockHash || void 0,
|
|
949
|
+
platformRoot: roots.platformRoot,
|
|
950
|
+
projectRoot: roots.projectRoot,
|
|
899
951
|
packages
|
|
900
952
|
};
|
|
901
953
|
try {
|
|
@@ -907,6 +959,9 @@ async function saveCache(cwd, results) {
|
|
|
907
959
|
async function discoverManifests(cwd, noCache = false, options = {}) {
|
|
908
960
|
const startTime = Date.now();
|
|
909
961
|
const timings = {};
|
|
962
|
+
const platformRoot = options.platformRoot ?? cwd;
|
|
963
|
+
const projectRoot = options.projectRoot ?? cwd;
|
|
964
|
+
const roots = { platformRoot, projectRoot };
|
|
910
965
|
if (noCache) {
|
|
911
966
|
inProcDiscoveryCache = null;
|
|
912
967
|
} else if (inProcDiscoveryCache) {
|
|
@@ -923,7 +978,7 @@ async function discoverManifests(cwd, noCache = false, options = {}) {
|
|
|
923
978
|
}
|
|
924
979
|
if (!noCache) {
|
|
925
980
|
const cacheStart = Date.now();
|
|
926
|
-
const cached = await loadCache(cwd);
|
|
981
|
+
const cached = await loadCache(cwd, roots);
|
|
927
982
|
timings.cacheLoad = Date.now() - cacheStart;
|
|
928
983
|
if (cached) {
|
|
929
984
|
log("debug", "Using cached manifests");
|
|
@@ -966,7 +1021,7 @@ async function discoverManifests(cwd, noCache = false, options = {}) {
|
|
|
966
1021
|
let workspace = [];
|
|
967
1022
|
try {
|
|
968
1023
|
const wsStart = Date.now();
|
|
969
|
-
workspace = await discoverWorkspace(
|
|
1024
|
+
workspace = await discoverWorkspace(platformRoot);
|
|
970
1025
|
timings.workspace = Date.now() - wsStart;
|
|
971
1026
|
log("info", `Discovered ${workspace.length} workspace packages with CLI manifests`);
|
|
972
1027
|
} catch (_err) {
|
|
@@ -980,17 +1035,22 @@ async function discoverManifests(cwd, noCache = false, options = {}) {
|
|
|
980
1035
|
}
|
|
981
1036
|
}
|
|
982
1037
|
const nmStart = Date.now();
|
|
983
|
-
const
|
|
984
|
-
const installed = await discoverNodeModules(nodeModulesRoot);
|
|
1038
|
+
const installed = await discoverNodeModules(platformRoot);
|
|
985
1039
|
timings.nodeModules = Date.now() - nmStart;
|
|
986
1040
|
if (installed.length > 0) {
|
|
987
1041
|
log("info", `Discovered ${installed.length} installed packages with CLI manifests`);
|
|
988
1042
|
}
|
|
1043
|
+
const plStart = Date.now();
|
|
1044
|
+
const projectLocal = await discoverProjectLocalPlugins(projectRoot);
|
|
1045
|
+
timings.projectLocal = Date.now() - plStart;
|
|
1046
|
+
if (projectLocal.length > 0) {
|
|
1047
|
+
log("info", `Discovered ${projectLocal.length} project-local plugins`);
|
|
1048
|
+
}
|
|
989
1049
|
const dedupStart = Date.now();
|
|
990
|
-
const results = deduplicateManifests([...workspace, ...installed]);
|
|
1050
|
+
const results = deduplicateManifests([...workspace, ...installed, ...projectLocal]);
|
|
991
1051
|
timings.deduplicate = Date.now() - dedupStart;
|
|
992
1052
|
const saveStart = Date.now();
|
|
993
|
-
await saveCache(cwd, results);
|
|
1053
|
+
await saveCache(cwd, results, roots);
|
|
994
1054
|
timings.cacheSave = Date.now() - saveStart;
|
|
995
1055
|
const totalTime = Date.now() - startTime;
|
|
996
1056
|
const sourceCounts = results.reduce((acc, r) => {
|
|
@@ -1690,7 +1750,8 @@ var diag = defineSystemCommand({
|
|
|
1690
1750
|
});
|
|
1691
1751
|
try {
|
|
1692
1752
|
const platformRoot = process.env.KB_PLATFORM_ROOT;
|
|
1693
|
-
const
|
|
1753
|
+
const projectRoot = process.env.KB_PROJECT_ROOT;
|
|
1754
|
+
const discovered = await discoverManifests(cwd, false, { platformRoot, projectRoot });
|
|
1694
1755
|
const manifests = registry.listManifests();
|
|
1695
1756
|
const enabled = manifests.filter((m) => m.available && !m.shadowed).length;
|
|
1696
1757
|
const disabled = manifests.filter((m) => !m.available).length;
|
|
@@ -3981,7 +4042,8 @@ async function registerBuiltinCommands(input = {}) {
|
|
|
3981
4042
|
const noCache = process.argv.includes("--no-cache") || env.KB_PLUGIN_NO_CACHE === "1";
|
|
3982
4043
|
const { discoverManifests: discoverManifests2 } = await Promise.resolve().then(() => (init_discover(), discover_exports));
|
|
3983
4044
|
const discovered = await discoverManifests2(cwd, noCache, {
|
|
3984
|
-
platformRoot: input.platformRoot
|
|
4045
|
+
platformRoot: input.platformRoot,
|
|
4046
|
+
projectRoot: input.projectRoot
|
|
3985
4047
|
});
|
|
3986
4048
|
if (discovered.length > 0) {
|
|
3987
4049
|
log3.info(`Discovered ${discovered.length} packages with CLI manifests`);
|