@kb-labs/cli-commands 2.4.0 → 2.6.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/index.js CHANGED
@@ -10,7 +10,7 @@ import { glob } from 'glob';
10
10
  import { defineSystemCommand, defineSystemCommandGroup } from '@kb-labs/shared-command-kit';
11
11
  import { createRegistry, formatDiagnosticReport } from '@kb-labs/core-registry';
12
12
  import { readMarketplaceLock, DiagnosticCollector } from '@kb-labs/core-discovery';
13
- import { platform } from '@kb-labs/core-runtime';
13
+ import { platform, platformSync } from '@kb-labs/core-runtime';
14
14
  import { CredentialsManager } from '@kb-labs/cli-runtime/gateway';
15
15
  import Ajv from 'ajv';
16
16
  import { createRequire } from 'module';
@@ -167,7 +167,8 @@ var discover_exports = {};
167
167
  __export(discover_exports, {
168
168
  __test: () => __test,
169
169
  discoverManifests: () => discoverManifests,
170
- discoverManifestsByNamespace: () => discoverManifestsByNamespace
170
+ discoverManifestsByNamespace: () => discoverManifestsByNamespace,
171
+ resetInProcCache: () => resetInProcCache
171
172
  });
172
173
  function createManifestV3Loader(commandId) {
173
174
  return async () => {
@@ -207,8 +208,12 @@ function createUnavailableManifest(pkgName, error) {
207
208
  throw new Error(`Cannot load ${pkgName} CLI manifest. ${rawMsg}`);
208
209
  }
209
210
  };
211
+ manifest._synthetic = true;
210
212
  return manifest;
211
213
  }
214
+ function resetInProcCache() {
215
+ inProcDiscoveryCache = null;
216
+ }
212
217
  async function computeManifestHash(manifestPath) {
213
218
  try {
214
219
  const content = await promises.readFile(manifestPath, "utf8");
@@ -551,7 +556,7 @@ async function discoverNodeModules(cwd) {
551
556
  const packageInfos = [];
552
557
  const scanPromises = [];
553
558
  for (const entry of entries) {
554
- if (!entry.isDirectory()) {
559
+ if (!entry.isDirectory() && !entry.isSymbolicLink()) {
555
560
  continue;
556
561
  }
557
562
  const scanEntry = async () => {
@@ -561,7 +566,7 @@ async function discoverNodeModules(cwd) {
561
566
  const scopeDir = path.join(nmDir, entry.name);
562
567
  try {
563
568
  const scopedDirs = await promises.readdir(scopeDir, { withFileTypes: true });
564
- for (const scopedEntry of scopedDirs.filter((d) => d.isDirectory())) {
569
+ for (const scopedEntry of scopedDirs.filter((d) => d.isDirectory() || d.isSymbolicLink())) {
565
570
  pkgRoot = path.join(scopeDir, scopedEntry.name);
566
571
  pkg = await readPackageJson(path.join(pkgRoot, PACKAGE_JSON));
567
572
  if (!pkg) {
@@ -791,6 +796,11 @@ async function saveCache(cwd, results) {
791
796
  const now = Date.now();
792
797
  const stateHasher = createHash("sha256");
793
798
  for (const result of results) {
799
+ const allSynthetic = result.manifests.length > 0 && result.manifests.every((m) => m._synthetic === true);
800
+ if (allSynthetic) {
801
+ log("debug", `[plugins][cache] Skipping synthetic unavailable manifest for ${result.packageName}`);
802
+ continue;
803
+ }
794
804
  try {
795
805
  const manifestHash = await computeManifestHash(result.manifestPath);
796
806
  const pkgJsonPath = path.join(result.pkgRoot.split("/").join(path.sep), PACKAGE_JSON);
@@ -851,7 +861,7 @@ async function saveCache(cwd, results) {
851
861
  log("debug", `Failed to save cache: ${err.message}`);
852
862
  }
853
863
  }
854
- async function discoverManifests(cwd, noCache = false) {
864
+ async function discoverManifests(cwd, noCache = false, options = {}) {
855
865
  const startTime = Date.now();
856
866
  const timings = {};
857
867
  if (noCache) {
@@ -927,7 +937,8 @@ async function discoverManifests(cwd, noCache = false) {
927
937
  }
928
938
  }
929
939
  const nmStart = Date.now();
930
- const installed = await discoverNodeModules(cwd);
940
+ const nodeModulesRoot = options.platformRoot ?? cwd;
941
+ const installed = await discoverNodeModules(nodeModulesRoot);
931
942
  timings.nodeModules = Date.now() - nmStart;
932
943
  if (installed.length > 0) {
933
944
  log("info", `Discovered ${installed.length} installed packages with CLI manifests`);
@@ -981,6 +992,7 @@ var init_discover = __esm({
981
992
  __test = {
982
993
  ensureManifestLoader,
983
994
  createManifestV3Loader
995
+ // resetInProcCache is exported directly (not via __test) since it's part of the public API
984
996
  };
985
997
  PACKAGE_JSON = "package.json";
986
998
  MANIFEST_LOAD_TIMEOUT = 1500;
@@ -1524,7 +1536,8 @@ var diag = defineSystemCommand({
1524
1536
  details: { nodeVersion, cliVersion, platform: platform11, arch }
1525
1537
  });
1526
1538
  try {
1527
- const discovered = await discoverManifests(cwd, false);
1539
+ const platformRoot = process.env.KB_PLATFORM_ROOT;
1540
+ const discovered = await discoverManifests(cwd, false, { platformRoot });
1528
1541
  const manifests = registry.listManifests();
1529
1542
  const enabled = manifests.filter((m) => m.available && !m.shadowed).length;
1530
1543
  const disabled = manifests.filter((m) => !m.available).length;
@@ -1764,6 +1777,9 @@ var registryDiagnostics = defineSystemCommand({
1764
1777
  }
1765
1778
  }
1766
1779
  });
1780
+
1781
+ // src/registry/plugins-state.ts
1782
+ init_discover();
1767
1783
  async function clearCache(cwd, options) {
1768
1784
  const cleared = [];
1769
1785
  const cacheDir = path.join(cwd, ".kb", "cache");
@@ -1792,6 +1808,7 @@ async function clearCache(cwd, options) {
1792
1808
  }
1793
1809
  } catch {
1794
1810
  }
1811
+ resetInProcCache();
1795
1812
  const modulesCleared = [];
1796
1813
  if (options?.deep) {
1797
1814
  try {
@@ -3140,6 +3157,146 @@ var authCreateServiceAccount = defineSystemCommand({
3140
3157
  return { ok: true, clientId: data.clientId, clientSecret: data.clientSecret, hostId: data.hostId };
3141
3158
  }
3142
3159
  });
3160
+ function isMode(value) {
3161
+ return value === "validate" || value === "reconcile" || value === "auto";
3162
+ }
3163
+ var platformSyncCommand = defineSystemCommand({
3164
+ name: "provision",
3165
+ description: "Reconcile .kb/marketplace.lock with installed adapters & plugins",
3166
+ longDescription: "Reads .kb/marketplace.lock as the declarative source of truth for which adapters and plugins this installation needs, and makes the filesystem match. In monorepo/dev mode it only validates. In prod/Docker build mode it installs missing marketplace packages via pnpm.",
3167
+ category: "platform",
3168
+ examples: generateExamples("platform provision", "kb", [
3169
+ { flags: {} },
3170
+ { flags: { mode: "validate" } },
3171
+ { flags: { mode: "reconcile", json: true } },
3172
+ { flags: { "dry-run": true } }
3173
+ ]),
3174
+ flags: {
3175
+ json: { type: "boolean", description: "Output machine-readable JSON" },
3176
+ mode: {
3177
+ type: "string",
3178
+ description: "Sync mode: validate | reconcile | auto (default: auto)"
3179
+ },
3180
+ "dry-run": {
3181
+ type: "boolean",
3182
+ description: "Never install, fail on drift"
3183
+ },
3184
+ root: {
3185
+ type: "string",
3186
+ description: "Workspace root (default: current working directory)"
3187
+ }
3188
+ },
3189
+ analytics: {
3190
+ command: "platform.sync",
3191
+ startEvent: "PLATFORM_SYNC_STARTED",
3192
+ finishEvent: "PLATFORM_SYNC_FINISHED"
3193
+ },
3194
+ async handler(ctx, _argv, flags) {
3195
+ const cwd = getContextCwd(ctx);
3196
+ const root = flags.root ?? cwd;
3197
+ const modeFlag = flags.mode;
3198
+ const mode = isMode(modeFlag) ? modeFlag : "auto";
3199
+ const dryRun = Boolean(flags["dry-run"]);
3200
+ const logger = ctx.platform?.logger;
3201
+ logger?.info("platform sync started", { root, mode, dryRun });
3202
+ const result = await platformSync({
3203
+ root,
3204
+ mode,
3205
+ dryRun,
3206
+ logger
3207
+ });
3208
+ logger?.info("platform sync finished", {
3209
+ ok: result.ok,
3210
+ mode: result.mode,
3211
+ checked: result.checked,
3212
+ missing: result.missing.length,
3213
+ mismatched: result.mismatched.length,
3214
+ installed: result.installed.length,
3215
+ errors: result.errors.length
3216
+ });
3217
+ return {
3218
+ ok: result.ok,
3219
+ status: result.ok ? "success" : "error",
3220
+ sync: result
3221
+ };
3222
+ },
3223
+ formatter(result, ctx, flags) {
3224
+ if (flags.json) {
3225
+ console.log(JSON.stringify(result.sync ?? result, null, 2));
3226
+ return;
3227
+ }
3228
+ const sync = result.sync;
3229
+ if (!sync) {
3230
+ ctx.ui.error("Platform Sync", {
3231
+ sections: [{ header: "Error", items: [result.error ?? "Unknown error"] }]
3232
+ });
3233
+ return;
3234
+ }
3235
+ const summary = [
3236
+ `Mode: ${sync.mode}${sync.mode !== flags.mode && flags.mode !== "auto" ? " (auto-detected)" : ""}`,
3237
+ `Checked: ${sync.checked}`
3238
+ ];
3239
+ if (sync.lockMissing) {
3240
+ summary.push("Lock: not found (nothing to reconcile)");
3241
+ }
3242
+ if (sync.installed.length) {
3243
+ summary.push(`Installed: ${sync.installed.length}`);
3244
+ }
3245
+ if (sync.missing.length) {
3246
+ summary.push(`Missing: ${sync.missing.length}`);
3247
+ }
3248
+ if (sync.mismatched.length) {
3249
+ summary.push(`Integrity mismatch: ${sync.mismatched.length}`);
3250
+ }
3251
+ if (sync.errors.length) {
3252
+ summary.push(`Errors: ${sync.errors.length}`);
3253
+ }
3254
+ const sections = [
3255
+ { header: "Summary", items: summary }
3256
+ ];
3257
+ if (sync.installed.length) {
3258
+ sections.push({
3259
+ header: "Installed",
3260
+ items: sync.installed.map((id) => `\u2022 ${id}`)
3261
+ });
3262
+ }
3263
+ if (sync.missing.length) {
3264
+ sections.push({
3265
+ header: "Missing",
3266
+ items: sync.missing.map((id) => `\u2022 ${id}`)
3267
+ });
3268
+ }
3269
+ if (sync.mismatched.length) {
3270
+ sections.push({
3271
+ header: "Integrity mismatch",
3272
+ items: sync.mismatched.map((id) => `\u2022 ${id}`)
3273
+ });
3274
+ }
3275
+ if (sync.errors.length) {
3276
+ sections.push({
3277
+ header: "Errors",
3278
+ items: sync.errors.map((e) => `\u2022 ${e.packageId}: ${e.message}`)
3279
+ });
3280
+ }
3281
+ const nextSteps = [];
3282
+ if (!sync.ok) {
3283
+ if (sync.missing.length && sync.mode === "validate") {
3284
+ nextSteps.push("kb platform provision --mode reconcile Install missing packages");
3285
+ }
3286
+ if (sync.mismatched.length) {
3287
+ nextSteps.push("kb marketplace install <pkg> Re-install mismatched packages");
3288
+ }
3289
+ }
3290
+ if (nextSteps.length) {
3291
+ sections.push({ header: "Next Steps", items: nextSteps });
3292
+ }
3293
+ if (sync.ok) {
3294
+ ctx.ui.success("Platform Sync", { sections });
3295
+ } else {
3296
+ ctx.ui.error("Platform Sync", { sections });
3297
+ }
3298
+ }
3299
+ });
3143
3300
 
3144
3301
  // src/commands/system/groups.ts
3145
3302
  var infoGroup = defineSystemCommandGroup("info", "System information commands", [
@@ -3172,6 +3329,9 @@ var authGroup = defineSystemCommandGroup("auth", "Gateway authentication command
3172
3329
  authStatus,
3173
3330
  authCreateServiceAccount
3174
3331
  ]);
3332
+ var platformGroup = defineSystemCommandGroup("platform", "Platform lifecycle commands", [
3333
+ platformSyncCommand
3334
+ ]);
3175
3335
  var req = createRequire(import.meta.url);
3176
3336
  var __filename$1 = fileURLToPath(import.meta.url);
3177
3337
  var __dirname$1 = path.dirname(__filename$1);
@@ -3655,12 +3815,15 @@ async function registerBuiltinCommands(input = {}) {
3655
3815
  registry.registerGroup(docsGroup);
3656
3816
  registry.registerGroup(logsGroup);
3657
3817
  registry.registerGroup(authGroup);
3818
+ registry.registerGroup(platformGroup);
3658
3819
  try {
3659
3820
  const cwd = getContextCwd({ cwd: input.cwd });
3660
3821
  const env = input.env ?? process.env;
3661
3822
  const noCache = process.argv.includes("--no-cache") || env.KB_PLUGIN_NO_CACHE === "1";
3662
3823
  const { discoverManifests: discoverManifests2 } = await Promise.resolve().then(() => (init_discover(), discover_exports));
3663
- const discovered = await discoverManifests2(cwd, noCache);
3824
+ const discovered = await discoverManifests2(cwd, noCache, {
3825
+ platformRoot: input.platformRoot
3826
+ });
3664
3827
  if (discovered.length > 0) {
3665
3828
  log3.info(`Discovered ${discovered.length} packages with CLI manifests`);
3666
3829
  const { valid: readyToRegister, skipped: preflightSkipped } = preflightManifests(discovered, log3);