@kb-labs/cli-commands 1.0.0 → 1.1.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 +98 -354
- package/dist/index.js.map +1 -1
- package/package.json +17 -19
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { safeColors, safeSymbols, getContextCwd, formatTable, toPosixPath, TimingTracker, sideBorderBox, formatCommandHelp } from '@kb-labs/shared-cli-ui';
|
|
2
2
|
export { TimingTracker } from '@kb-labs/shared-cli-ui';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import { promises, existsSync } from 'fs';
|
|
5
|
-
import path2 from 'path';
|
|
6
4
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
5
|
+
import { promises, existsSync } from 'fs';
|
|
7
6
|
import { createHash } from 'crypto';
|
|
7
|
+
import path from 'path';
|
|
8
8
|
import { parse } from 'yaml';
|
|
9
9
|
import { glob } from 'glob';
|
|
10
10
|
import { defineSystemCommand, defineSystemCommandGroup } from '@kb-labs/shared-command-kit';
|
|
@@ -161,225 +161,6 @@ var init_schema = __esm({
|
|
|
161
161
|
}
|
|
162
162
|
});
|
|
163
163
|
|
|
164
|
-
// src/commands/system/plugin-setup-rollback.ts
|
|
165
|
-
var plugin_setup_rollback_exports = {};
|
|
166
|
-
__export(plugin_setup_rollback_exports, {
|
|
167
|
-
createPluginSetupRollbackCommand: () => createPluginSetupRollbackCommand
|
|
168
|
-
});
|
|
169
|
-
function createPluginSetupRollbackCommand(options) {
|
|
170
|
-
const { namespace } = options;
|
|
171
|
-
return {
|
|
172
|
-
name: `${namespace}:setup:rollback`,
|
|
173
|
-
describe: `Rollback setup changes applied by ${namespace}`,
|
|
174
|
-
category: namespace,
|
|
175
|
-
examples: [
|
|
176
|
-
`kb ${namespace} setup:rollback --list`,
|
|
177
|
-
`kb ${namespace} setup:rollback --log .kb/logs/setup/${namespace}-<id>.json --yes`
|
|
178
|
-
],
|
|
179
|
-
flags: ROLLBACK_COMMAND_FLAGS,
|
|
180
|
-
async run(ctx, _argv, rawFlags) {
|
|
181
|
-
const presenter = ctx.presenter ?? {};
|
|
182
|
-
const output = ctx.output;
|
|
183
|
-
const logger = ctx.platform?.logger;
|
|
184
|
-
const cwd = getContextCwd(ctx) ?? process.cwd();
|
|
185
|
-
logger?.info("Plugin setup rollback started", { namespace });
|
|
186
|
-
const logsDir = path2.join(cwd, ".kb", "logs", "setup");
|
|
187
|
-
const listOnly = rawFlags.list === true;
|
|
188
|
-
const autoConfirm = rawFlags.yes === true;
|
|
189
|
-
const logFlag = typeof rawFlags.log === "string" ? rawFlags.log : void 0;
|
|
190
|
-
try {
|
|
191
|
-
await promises.mkdir(logsDir, { recursive: true });
|
|
192
|
-
if (listOnly) {
|
|
193
|
-
await listLogs(logsDir, namespace, output || presenter, logger);
|
|
194
|
-
return 0;
|
|
195
|
-
}
|
|
196
|
-
const resolvedLogPath = logFlag ? resolveLogPath(logsDir, logFlag, cwd) : await findLatestLog(logsDir, namespace);
|
|
197
|
-
if (!resolvedLogPath) {
|
|
198
|
-
logger?.warn("No setup log found", { namespace, logFlag });
|
|
199
|
-
(output || presenter)?.error?.(
|
|
200
|
-
`\u041D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D \u043D\u0438 \u043E\u0434\u0438\u043D \u043B\u043E\u0433 setup \u0434\u043B\u044F ${namespace}. \u0418\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435 --log \u0434\u043B\u044F \u0443\u043A\u0430\u0437\u0430\u043D\u0438\u044F \u0444\u0430\u0439\u043B\u0430.`
|
|
201
|
-
);
|
|
202
|
-
return 1;
|
|
203
|
-
}
|
|
204
|
-
logger?.info("Using setup log", { logPath: resolvedLogPath });
|
|
205
|
-
(output || presenter)?.info?.(`~ \u0418\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u043C \u043B\u043E\u0433: ${path2.relative(cwd, resolvedLogPath)}`);
|
|
206
|
-
if (!autoConfirm) {
|
|
207
|
-
(output || presenter)?.warn?.("\u0414\u043E\u0431\u0430\u0432\u044C\u0442\u0435 \u0444\u043B\u0430\u0433 --yes \u0434\u043B\u044F \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F \u043E\u0442\u043A\u0430\u0442\u0430.");
|
|
208
|
-
return 1;
|
|
209
|
-
}
|
|
210
|
-
const entries = await readJournalEntries(resolvedLogPath);
|
|
211
|
-
if (entries.length === 0) {
|
|
212
|
-
logger?.warn("Setup log is empty", { logPath: resolvedLogPath });
|
|
213
|
-
(output || presenter)?.warn?.("\u041B\u043E\u0433 \u043F\u0443\u0441\u0442\u043E\u0439, \u043E\u0442\u043A\u0430\u0442\u044B\u0432\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E.");
|
|
214
|
-
return 0;
|
|
215
|
-
}
|
|
216
|
-
logger?.info("Applying rollback", { entriesCount: entries.length });
|
|
217
|
-
await applyRollback(entries, cwd, output || presenter, logger);
|
|
218
|
-
logger?.info("Rollback completed");
|
|
219
|
-
(output || presenter)?.info?.("\u2713 \u041E\u0442\u043A\u0430\u0442 \u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043D.");
|
|
220
|
-
return 0;
|
|
221
|
-
} catch (error) {
|
|
222
|
-
logger?.error("Rollback failed", {
|
|
223
|
-
error: error instanceof Error ? error.message : String(error)
|
|
224
|
-
});
|
|
225
|
-
(output || presenter)?.error?.(error);
|
|
226
|
-
return 1;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
function resolveLogPath(logsDir, inputPath, cwd) {
|
|
232
|
-
const candidate = path2.isAbsolute(inputPath) ? inputPath : path2.resolve(cwd, inputPath);
|
|
233
|
-
if (candidate.startsWith(logsDir)) {
|
|
234
|
-
return candidate;
|
|
235
|
-
}
|
|
236
|
-
return candidate;
|
|
237
|
-
}
|
|
238
|
-
async function listLogs(logsDir, namespace, output, logger) {
|
|
239
|
-
const files = await safeReadDir(logsDir);
|
|
240
|
-
const filtered = files.filter((file) => file.startsWith(namespace) && file.endsWith(".json")).sort().reverse();
|
|
241
|
-
logger?.info("Listing setup logs", { namespace, count: filtered.length });
|
|
242
|
-
if (filtered.length === 0) {
|
|
243
|
-
output?.info?.("\u041B\u043E\u0433\u0438 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u044B.");
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
output?.info?.("\u0414\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0435 \u043B\u043E\u0433\u0438 setup:");
|
|
247
|
-
for (const file of filtered) {
|
|
248
|
-
output?.info?.(` \u2022 ${file}`);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
async function safeReadDir(dir) {
|
|
252
|
-
try {
|
|
253
|
-
return await promises.readdir(dir);
|
|
254
|
-
} catch (error) {
|
|
255
|
-
if (error?.code === "ENOENT") {
|
|
256
|
-
return [];
|
|
257
|
-
}
|
|
258
|
-
throw error;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
async function findLatestLog(logsDir, namespace) {
|
|
262
|
-
const entries = await safeReadDir(logsDir);
|
|
263
|
-
const candidates = await Promise.all(
|
|
264
|
-
entries.filter((file) => file.startsWith(namespace) && file.endsWith(".json")).map(async (file) => {
|
|
265
|
-
const fullPath = path2.join(logsDir, file);
|
|
266
|
-
const stat = await promises.stat(fullPath);
|
|
267
|
-
return { path: fullPath, mtime: stat.mtimeMs };
|
|
268
|
-
})
|
|
269
|
-
);
|
|
270
|
-
if (candidates.length === 0) {
|
|
271
|
-
return null;
|
|
272
|
-
}
|
|
273
|
-
candidates.sort((a, b) => b.mtime - a.mtime);
|
|
274
|
-
return candidates[0]?.path ?? null;
|
|
275
|
-
}
|
|
276
|
-
async function readJournalEntries(logPath) {
|
|
277
|
-
const content = await promises.readFile(logPath, "utf8");
|
|
278
|
-
const parsed = JSON.parse(content);
|
|
279
|
-
if (!Array.isArray(parsed)) {
|
|
280
|
-
throw new Error("\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0444\u043E\u0440\u043C\u0430\u0442 \u043B\u043E\u0433\u0430 setup.");
|
|
281
|
-
}
|
|
282
|
-
return parsed;
|
|
283
|
-
}
|
|
284
|
-
async function applyRollback(entries, cwd, output, logger) {
|
|
285
|
-
logger?.info("Applying rollback operations", { entriesCount: entries.length });
|
|
286
|
-
for (const entry of [...entries].reverse()) {
|
|
287
|
-
const operation = entry.operation?.operation;
|
|
288
|
-
if (!operation) {
|
|
289
|
-
continue;
|
|
290
|
-
}
|
|
291
|
-
switch (operation.kind) {
|
|
292
|
-
case "file":
|
|
293
|
-
case "config":
|
|
294
|
-
case "script":
|
|
295
|
-
await restoreFileLikeOperation(entry, cwd, output, logger);
|
|
296
|
-
break;
|
|
297
|
-
default:
|
|
298
|
-
logger?.warn("Skipping operation (rollback not implemented)", { kind: operation.kind });
|
|
299
|
-
output?.warn?.(
|
|
300
|
-
`\u041F\u0440\u043E\u043F\u0443\u0441\u043A \u043E\u043F\u0435\u0440\u0430\u0446\u0438\u0438 ${operation.kind} (\u043E\u0442\u043A\u0430\u0442 \u043D\u0435 \u0440\u0435\u0430\u043B\u0438\u0437\u043E\u0432\u0430\u043D).`
|
|
301
|
-
);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
async function restoreFileLikeOperation(entry, cwd, output, logger) {
|
|
306
|
-
const operation = entry.operation.operation;
|
|
307
|
-
const relativePath = operation.kind === "script" ? operation.file : operation.path;
|
|
308
|
-
const targetPath = resolveWithinWorkspace(cwd, relativePath);
|
|
309
|
-
if (entry.backupPath) {
|
|
310
|
-
await copyBackup(entry.backupPath, targetPath, output, logger);
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
if (entry.before?.exists === false) {
|
|
314
|
-
await promises.rm(targetPath, { force: true, recursive: false });
|
|
315
|
-
logger?.info("File removed during rollback", { path: targetPath });
|
|
316
|
-
output?.info?.(`\u2212 \u0423\u0434\u0430\u043B\u0451\u043D ${path2.relative(cwd, targetPath)}`);
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
if (typeof entry.before?.content === "string") {
|
|
320
|
-
if (entry.before.content.startsWith("<truncated")) {
|
|
321
|
-
logger?.warn("Skipping truncated file", { path: targetPath });
|
|
322
|
-
output?.warn?.(
|
|
323
|
-
`\u041F\u0440\u043E\u043F\u0443\u0441\u043A ${path2.relative(cwd, targetPath)} \u2014 \u0441\u043E\u0434\u0435\u0440\u0436\u0438\u043C\u043E\u0435 \u043B\u043E\u0433\u0430 \u0443\u0441\u0435\u0447\u0435\u043D\u043E.`
|
|
324
|
-
);
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
327
|
-
await promises.mkdir(path2.dirname(targetPath), { recursive: true });
|
|
328
|
-
await promises.writeFile(targetPath, entry.before.content, "utf8");
|
|
329
|
-
logger?.info("File restored from log", { path: targetPath });
|
|
330
|
-
output?.info?.(`\u21BA \u0412\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D ${path2.relative(cwd, targetPath)}`);
|
|
331
|
-
return;
|
|
332
|
-
}
|
|
333
|
-
logger?.warn("Skipping file (no restore data)", { path: targetPath });
|
|
334
|
-
output?.warn?.(
|
|
335
|
-
`\u041F\u0440\u043E\u043F\u0443\u0441\u043A ${path2.relative(cwd, targetPath)} \u2014 \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044E\u0442 \u0434\u0430\u043D\u043D\u044B\u0435 \u0434\u043B\u044F \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F.`
|
|
336
|
-
);
|
|
337
|
-
}
|
|
338
|
-
function resolveWithinWorkspace(workspace, target) {
|
|
339
|
-
const resolvedWorkspace = path2.resolve(workspace);
|
|
340
|
-
const resolvedTarget = path2.resolve(resolvedWorkspace, target);
|
|
341
|
-
if (!resolvedTarget.startsWith(resolvedWorkspace)) {
|
|
342
|
-
throw new Error(`\u041F\u0443\u0442\u044C ${target} \u0432\u044B\u0445\u043E\u0434\u0438\u0442 \u0437\u0430 \u043F\u0440\u0435\u0434\u0435\u043B\u044B \u0440\u0430\u0431\u043E\u0447\u0435\u0439 \u0434\u0438\u0440\u0435\u043A\u0442\u043E\u0440\u0438\u0438.`);
|
|
343
|
-
}
|
|
344
|
-
return resolvedTarget;
|
|
345
|
-
}
|
|
346
|
-
async function copyBackup(backupPath, destination, output, logger) {
|
|
347
|
-
try {
|
|
348
|
-
await promises.access(backupPath);
|
|
349
|
-
} catch {
|
|
350
|
-
logger?.warn("Backup not found", { backupPath });
|
|
351
|
-
output?.warn?.(`\u041F\u0440\u043E\u043F\u0443\u0441\u043A \u2014 backup ${backupPath} \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D.`);
|
|
352
|
-
return;
|
|
353
|
-
}
|
|
354
|
-
await promises.mkdir(path2.dirname(destination), { recursive: true });
|
|
355
|
-
await promises.copyFile(backupPath, destination);
|
|
356
|
-
logger?.info("File restored from backup", { backupPath, destination });
|
|
357
|
-
output?.info?.(`\u21BA \u0412\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D \u0438\u0437 backup ${path2.basename(destination)}`);
|
|
358
|
-
}
|
|
359
|
-
var ROLLBACK_COMMAND_FLAGS;
|
|
360
|
-
var init_plugin_setup_rollback = __esm({
|
|
361
|
-
"src/commands/system/plugin-setup-rollback.ts"() {
|
|
362
|
-
ROLLBACK_COMMAND_FLAGS = [
|
|
363
|
-
{
|
|
364
|
-
name: "log",
|
|
365
|
-
type: "string",
|
|
366
|
-
description: "Path to a setup change log JSON file. Defaults to latest log for the namespace."
|
|
367
|
-
},
|
|
368
|
-
{
|
|
369
|
-
name: "list",
|
|
370
|
-
type: "boolean",
|
|
371
|
-
description: "List available setup change logs and exit."
|
|
372
|
-
},
|
|
373
|
-
{
|
|
374
|
-
name: "yes",
|
|
375
|
-
type: "boolean",
|
|
376
|
-
alias: "y",
|
|
377
|
-
description: "Apply rollback without confirmation prompt."
|
|
378
|
-
}
|
|
379
|
-
];
|
|
380
|
-
}
|
|
381
|
-
});
|
|
382
|
-
|
|
383
164
|
// src/registry/discover.ts
|
|
384
165
|
var discover_exports = {};
|
|
385
166
|
__export(discover_exports, {
|
|
@@ -394,41 +175,9 @@ function createManifestV3Loader(commandId) {
|
|
|
394
175
|
);
|
|
395
176
|
};
|
|
396
177
|
}
|
|
397
|
-
async function loadSetupRollbackCommandModule({
|
|
398
|
-
manifestV2,
|
|
399
|
-
namespace,
|
|
400
|
-
pkgName,
|
|
401
|
-
pkgRoot
|
|
402
|
-
}) {
|
|
403
|
-
const module = await Promise.resolve().then(() => (init_plugin_setup_rollback(), plugin_setup_rollback_exports));
|
|
404
|
-
if (typeof module.createPluginSetupRollbackCommand !== "function") {
|
|
405
|
-
throw new Error("Failed to load plugin setup rollback command factory");
|
|
406
|
-
}
|
|
407
|
-
const command = module.createPluginSetupRollbackCommand({
|
|
408
|
-
namespace,
|
|
409
|
-
packageName: pkgName,
|
|
410
|
-
pkgRoot
|
|
411
|
-
});
|
|
412
|
-
return {
|
|
413
|
-
run: async (ctx, argv, flags) => {
|
|
414
|
-
const result = await command.run(ctx, argv, flags);
|
|
415
|
-
return typeof result === "number" ? result : void 0;
|
|
416
|
-
}
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
178
|
function ensureManifestLoader(manifest) {
|
|
420
179
|
if (typeof manifest.loader !== "function") {
|
|
421
180
|
const commandId = manifest.id || manifest.group || "unknown";
|
|
422
|
-
if (manifest.isSetupRollback) {
|
|
423
|
-
log("debug", `[plugins][cache] Rehydrated setup rollback loader for ${commandId}`);
|
|
424
|
-
manifest.loader = () => loadSetupRollbackCommandModule({
|
|
425
|
-
manifestV2: manifest.manifestV2,
|
|
426
|
-
namespace: manifest.namespace || manifest.group || deriveNamespace(manifest.package || commandId),
|
|
427
|
-
pkgName: manifest.package || commandId,
|
|
428
|
-
pkgRoot: manifest.pkgRoot
|
|
429
|
-
});
|
|
430
|
-
return;
|
|
431
|
-
}
|
|
432
181
|
log("debug", `[plugins][cache] Rehydrated loader for ${commandId}`);
|
|
433
182
|
manifest.loader = createManifestV3Loader(commandId);
|
|
434
183
|
}
|
|
@@ -468,7 +217,7 @@ async function computeManifestHash(manifestPath) {
|
|
|
468
217
|
}
|
|
469
218
|
}
|
|
470
219
|
async function computeLockfileHash(cwd) {
|
|
471
|
-
const lockfilePath =
|
|
220
|
+
const lockfilePath = path.join(cwd, "pnpm-lock.yaml");
|
|
472
221
|
try {
|
|
473
222
|
const content = await promises.readFile(lockfilePath, "utf8");
|
|
474
223
|
return createHash("sha256").update(content).digest("hex");
|
|
@@ -477,7 +226,7 @@ async function computeLockfileHash(cwd) {
|
|
|
477
226
|
}
|
|
478
227
|
}
|
|
479
228
|
async function computeConfigHash(cwd) {
|
|
480
|
-
const configPath =
|
|
229
|
+
const configPath = path.join(cwd, ".kb", "kb.config.json");
|
|
481
230
|
try {
|
|
482
231
|
const content = await promises.readFile(configPath, "utf8");
|
|
483
232
|
return createHash("sha256").update(content).digest("hex");
|
|
@@ -486,7 +235,7 @@ async function computeConfigHash(cwd) {
|
|
|
486
235
|
}
|
|
487
236
|
}
|
|
488
237
|
async function computePluginsStateHash(cwd) {
|
|
489
|
-
const pluginsPath =
|
|
238
|
+
const pluginsPath = path.join(cwd, ".kb", "plugins.json");
|
|
490
239
|
try {
|
|
491
240
|
const content = await promises.readFile(pluginsPath, "utf8");
|
|
492
241
|
return createHash("sha256").update(content).digest("hex");
|
|
@@ -499,7 +248,7 @@ async function detectNewWorkspacePackages(cwd, cachedPackages) {
|
|
|
499
248
|
return true;
|
|
500
249
|
}
|
|
501
250
|
try {
|
|
502
|
-
const workspaceYaml =
|
|
251
|
+
const workspaceYaml = path.join(cwd, "pnpm-workspace.yaml");
|
|
503
252
|
const content = await promises.readFile(workspaceYaml, "utf8");
|
|
504
253
|
const parsed = parse(content);
|
|
505
254
|
if (!Array.isArray(parsed.packages)) {
|
|
@@ -507,15 +256,15 @@ async function detectNewWorkspacePackages(cwd, cachedPackages) {
|
|
|
507
256
|
}
|
|
508
257
|
const knownPackages = new Set(Object.keys(cachedPackages));
|
|
509
258
|
for (const pattern of parsed.packages) {
|
|
510
|
-
const pkgPattern =
|
|
259
|
+
const pkgPattern = path.join(pattern, PACKAGE_JSON);
|
|
511
260
|
const pkgFiles = await glob(pkgPattern, {
|
|
512
261
|
cwd,
|
|
513
262
|
absolute: false,
|
|
514
263
|
ignore: [".kb/**", "node_modules/**", "**/node_modules/**"]
|
|
515
264
|
});
|
|
516
265
|
for (const pkgFile of pkgFiles) {
|
|
517
|
-
const pkgRoot =
|
|
518
|
-
const pkg = await readPackageJson(
|
|
266
|
+
const pkgRoot = path.dirname(path.join(cwd, pkgFile));
|
|
267
|
+
const pkg = await readPackageJson(path.join(cwd, pkgFile));
|
|
519
268
|
if (!pkg || !pkg.name) {
|
|
520
269
|
continue;
|
|
521
270
|
}
|
|
@@ -558,11 +307,6 @@ function getNamespaceFromManifest(manifestV2, packageName) {
|
|
|
558
307
|
const last = parts[parts.length - 1] || packageName;
|
|
559
308
|
return last.replace(/^@/, "");
|
|
560
309
|
}
|
|
561
|
-
function deriveNamespace(packageName) {
|
|
562
|
-
const parts = packageName.split("/");
|
|
563
|
-
const lastPart = parts[parts.length - 1] || packageName;
|
|
564
|
-
return lastPart.replace(/^@/, "");
|
|
565
|
-
}
|
|
566
310
|
async function loadManifest(manifestPath, pkgName, pkgRoot) {
|
|
567
311
|
const fileUrl = pathToFileURL(manifestPath).href;
|
|
568
312
|
const mod = await import(fileUrl);
|
|
@@ -571,7 +315,7 @@ async function loadManifest(manifestPath, pkgName, pkgRoot) {
|
|
|
571
315
|
throw new Error(`Unsupported manifest format in ${pkgName}. Only kb.plugin/3 schema is supported.`);
|
|
572
316
|
}
|
|
573
317
|
const namespace = getNamespaceFromManifest(manifest, pkgName);
|
|
574
|
-
const manifestDir =
|
|
318
|
+
const manifestDir = path.dirname(manifestPath);
|
|
575
319
|
const baseRoot = pkgRoot || manifestDir;
|
|
576
320
|
const cliCommands = Array.isArray(manifest.cli?.commands) ? manifest.cli.commands : [];
|
|
577
321
|
if (cliCommands.length === 0 && !manifest.setup) {
|
|
@@ -616,7 +360,7 @@ async function readPackageJson(pkgPath) {
|
|
|
616
360
|
}
|
|
617
361
|
}
|
|
618
362
|
async function loadConfig(cwd) {
|
|
619
|
-
const configPath =
|
|
363
|
+
const configPath = path.join(cwd, ".kb", "kb.config.json");
|
|
620
364
|
try {
|
|
621
365
|
const content = await promises.readFile(configPath, "utf8");
|
|
622
366
|
const config = JSON.parse(content);
|
|
@@ -631,7 +375,7 @@ async function loadConfig(cwd) {
|
|
|
631
375
|
}
|
|
632
376
|
async function findManifestPath(pkgRoot, pkg) {
|
|
633
377
|
if (pkg.kb?.manifest) {
|
|
634
|
-
const manifestPath =
|
|
378
|
+
const manifestPath = path.join(pkgRoot, pkg.kb.manifest);
|
|
635
379
|
try {
|
|
636
380
|
await promises.access(manifestPath);
|
|
637
381
|
return { path: manifestPath, deprecated: false };
|
|
@@ -643,7 +387,7 @@ async function findManifestPath(pkgRoot, pkg) {
|
|
|
643
387
|
const exportPath = pkg.exports["./kb/commands"];
|
|
644
388
|
const manifestPath = typeof exportPath === "string" ? exportPath : exportPath.default || exportPath.import;
|
|
645
389
|
if (manifestPath) {
|
|
646
|
-
const resolved =
|
|
390
|
+
const resolved = path.resolve(pkgRoot, manifestPath);
|
|
647
391
|
try {
|
|
648
392
|
await promises.access(resolved);
|
|
649
393
|
return { path: resolved, deprecated: false };
|
|
@@ -688,7 +432,7 @@ function validateUniqueIds(manifests, pkgName) {
|
|
|
688
432
|
}
|
|
689
433
|
}
|
|
690
434
|
async function discoverWorkspace(cwd) {
|
|
691
|
-
const workspaceYaml =
|
|
435
|
+
const workspaceYaml = path.join(cwd, "pnpm-workspace.yaml");
|
|
692
436
|
const content = await promises.readFile(workspaceYaml, "utf8");
|
|
693
437
|
const parsed = parse(content);
|
|
694
438
|
if (!parsed.packages || !Array.isArray(parsed.packages)) {
|
|
@@ -696,7 +440,7 @@ async function discoverWorkspace(cwd) {
|
|
|
696
440
|
}
|
|
697
441
|
const packageInfos = [];
|
|
698
442
|
for (const pattern of parsed.packages) {
|
|
699
|
-
const pkgPattern =
|
|
443
|
+
const pkgPattern = path.join(pattern, PACKAGE_JSON);
|
|
700
444
|
const pkgFiles = await glob(pkgPattern, {
|
|
701
445
|
cwd,
|
|
702
446
|
absolute: false,
|
|
@@ -704,8 +448,8 @@ async function discoverWorkspace(cwd) {
|
|
|
704
448
|
// Ignore .kb, node_modules
|
|
705
449
|
});
|
|
706
450
|
for (const pkgFile of pkgFiles) {
|
|
707
|
-
const pkgRoot =
|
|
708
|
-
const pkg = await readPackageJson(
|
|
451
|
+
const pkgRoot = path.dirname(path.join(cwd, pkgFile));
|
|
452
|
+
const pkg = await readPackageJson(path.join(cwd, pkgFile));
|
|
709
453
|
if (!pkg) {
|
|
710
454
|
continue;
|
|
711
455
|
}
|
|
@@ -770,7 +514,7 @@ async function discoverWorkspace(cwd) {
|
|
|
770
514
|
}
|
|
771
515
|
async function discoverCurrentPackage(cwd) {
|
|
772
516
|
try {
|
|
773
|
-
const pkg = await readPackageJson(
|
|
517
|
+
const pkg = await readPackageJson(path.join(cwd, PACKAGE_JSON));
|
|
774
518
|
if (!pkg) {
|
|
775
519
|
return null;
|
|
776
520
|
}
|
|
@@ -798,7 +542,7 @@ async function discoverCurrentPackage(cwd) {
|
|
|
798
542
|
return null;
|
|
799
543
|
}
|
|
800
544
|
async function discoverNodeModules(cwd) {
|
|
801
|
-
const nmDir =
|
|
545
|
+
const nmDir = path.join(cwd, "node_modules");
|
|
802
546
|
const config = await loadConfig(cwd);
|
|
803
547
|
try {
|
|
804
548
|
const entries = await promises.readdir(nmDir, { withFileTypes: true });
|
|
@@ -812,12 +556,12 @@ async function discoverNodeModules(cwd) {
|
|
|
812
556
|
let pkgRoot;
|
|
813
557
|
let pkg;
|
|
814
558
|
if (entry.name.startsWith("@")) {
|
|
815
|
-
const scopeDir =
|
|
559
|
+
const scopeDir = path.join(nmDir, entry.name);
|
|
816
560
|
try {
|
|
817
561
|
const scopedDirs = await promises.readdir(scopeDir, { withFileTypes: true });
|
|
818
562
|
for (const scopedEntry of scopedDirs.filter((d) => d.isDirectory())) {
|
|
819
|
-
pkgRoot =
|
|
820
|
-
pkg = await readPackageJson(
|
|
563
|
+
pkgRoot = path.join(scopeDir, scopedEntry.name);
|
|
564
|
+
pkg = await readPackageJson(path.join(pkgRoot, PACKAGE_JSON));
|
|
821
565
|
if (!pkg) {
|
|
822
566
|
continue;
|
|
823
567
|
}
|
|
@@ -851,8 +595,8 @@ async function discoverNodeModules(cwd) {
|
|
|
851
595
|
} catch {
|
|
852
596
|
}
|
|
853
597
|
} else {
|
|
854
|
-
pkgRoot =
|
|
855
|
-
pkg = await readPackageJson(
|
|
598
|
+
pkgRoot = path.join(nmDir, entry.name);
|
|
599
|
+
pkg = await readPackageJson(path.join(pkgRoot, PACKAGE_JSON));
|
|
856
600
|
if (!pkg) {
|
|
857
601
|
return;
|
|
858
602
|
}
|
|
@@ -951,7 +695,7 @@ function deduplicateManifests(all) {
|
|
|
951
695
|
return Array.from(byPackageName.values());
|
|
952
696
|
}
|
|
953
697
|
async function loadCache(cwd) {
|
|
954
|
-
const cachePath =
|
|
698
|
+
const cachePath = path.join(cwd, ".kb", "cache", "cli-manifests.json");
|
|
955
699
|
try {
|
|
956
700
|
const content = await promises.readFile(cachePath, "utf8");
|
|
957
701
|
const cache = JSON.parse(content);
|
|
@@ -1001,8 +745,8 @@ async function loadCache(cwd) {
|
|
|
1001
745
|
}
|
|
1002
746
|
}
|
|
1003
747
|
async function isPackageCacheStale(entry, options) {
|
|
1004
|
-
const manifestFsPath = entry.manifestPath.split("/").join(
|
|
1005
|
-
const pkgJsonPath =
|
|
748
|
+
const manifestFsPath = entry.manifestPath.split("/").join(path.sep);
|
|
749
|
+
const pkgJsonPath = path.join(entry.result.pkgRoot.split("/").join(path.sep), PACKAGE_JSON);
|
|
1006
750
|
try {
|
|
1007
751
|
const pkgStat = await promises.stat(pkgJsonPath);
|
|
1008
752
|
if (pkgStat.mtimeMs !== entry.pkgJsonMtime) {
|
|
@@ -1039,19 +783,19 @@ async function isPackageCacheStale(entry, options) {
|
|
|
1039
783
|
return false;
|
|
1040
784
|
}
|
|
1041
785
|
async function saveCache(cwd, results) {
|
|
1042
|
-
const cachePath =
|
|
1043
|
-
await promises.mkdir(
|
|
786
|
+
const cachePath = path.join(cwd, ".kb", "cache", "cli-manifests.json");
|
|
787
|
+
await promises.mkdir(path.dirname(cachePath), { recursive: true });
|
|
1044
788
|
const packages = {};
|
|
1045
789
|
const now = Date.now();
|
|
1046
790
|
const stateHasher = createHash("sha256");
|
|
1047
791
|
for (const result of results) {
|
|
1048
792
|
try {
|
|
1049
793
|
const manifestHash = await computeManifestHash(result.manifestPath);
|
|
1050
|
-
const pkgJsonPath =
|
|
794
|
+
const pkgJsonPath = path.join(result.pkgRoot.split("/").join(path.sep), PACKAGE_JSON);
|
|
1051
795
|
const pkgStat = await promises.stat(pkgJsonPath);
|
|
1052
796
|
const pkg = await readPackageJson(pkgJsonPath);
|
|
1053
797
|
const version2 = pkg?.version || "0.1.0";
|
|
1054
|
-
const manifestStat = await promises.stat(result.manifestPath.split("/").join(
|
|
798
|
+
const manifestStat = await promises.stat(result.manifestPath.split("/").join(path.sep));
|
|
1055
799
|
const manifestsForCache = result.manifests.map((manifest) => {
|
|
1056
800
|
const manifestCopy = { ...manifest };
|
|
1057
801
|
delete manifestCopy.loader;
|
|
@@ -1255,7 +999,7 @@ __export(plugins_state_exports, {
|
|
|
1255
999
|
unlinkPlugin: () => unlinkPlugin
|
|
1256
1000
|
});
|
|
1257
1001
|
function getPluginsStatePath(cwd) {
|
|
1258
|
-
return
|
|
1002
|
+
return path.join(cwd, ".kb", "plugins.json");
|
|
1259
1003
|
}
|
|
1260
1004
|
async function loadPluginsState(cwd) {
|
|
1261
1005
|
const statePath = getPluginsStatePath(cwd);
|
|
@@ -1278,7 +1022,7 @@ async function loadPluginsState(cwd) {
|
|
|
1278
1022
|
}
|
|
1279
1023
|
async function savePluginsState(cwd, state) {
|
|
1280
1024
|
const statePath = getPluginsStatePath(cwd);
|
|
1281
|
-
const dir =
|
|
1025
|
+
const dir = path.dirname(statePath);
|
|
1282
1026
|
await promises.mkdir(dir, { recursive: true });
|
|
1283
1027
|
state.lastUpdated = Date.now();
|
|
1284
1028
|
await promises.writeFile(statePath, JSON.stringify(state, null, 2), "utf8");
|
|
@@ -1310,7 +1054,7 @@ async function disablePlugin(cwd, packageName) {
|
|
|
1310
1054
|
}
|
|
1311
1055
|
async function linkPlugin(cwd, pluginPath) {
|
|
1312
1056
|
const state = await loadPluginsState(cwd);
|
|
1313
|
-
const absPath =
|
|
1057
|
+
const absPath = path.resolve(cwd, pluginPath);
|
|
1314
1058
|
if (!state.linked.includes(absPath)) {
|
|
1315
1059
|
state.linked.push(absPath);
|
|
1316
1060
|
}
|
|
@@ -1318,7 +1062,7 @@ async function linkPlugin(cwd, pluginPath) {
|
|
|
1318
1062
|
}
|
|
1319
1063
|
async function unlinkPlugin(cwd, pluginPath) {
|
|
1320
1064
|
const state = await loadPluginsState(cwd);
|
|
1321
|
-
const absPath =
|
|
1065
|
+
const absPath = path.resolve(cwd, pluginPath);
|
|
1322
1066
|
state.linked = state.linked.filter((p) => p !== absPath);
|
|
1323
1067
|
await savePluginsState(cwd, state);
|
|
1324
1068
|
}
|
|
@@ -1345,7 +1089,7 @@ async function recordCrash(cwd, packageName) {
|
|
|
1345
1089
|
}
|
|
1346
1090
|
async function computePackageIntegrity(pkgRoot) {
|
|
1347
1091
|
try {
|
|
1348
|
-
const pkgJsonPath =
|
|
1092
|
+
const pkgJsonPath = path.join(pkgRoot, "package.json");
|
|
1349
1093
|
const content = await promises.readFile(pkgJsonPath, "utf8");
|
|
1350
1094
|
const hash = createHash("sha256").update(content).digest("base64");
|
|
1351
1095
|
return `sha256-${hash}`;
|
|
@@ -1355,12 +1099,12 @@ async function computePackageIntegrity(pkgRoot) {
|
|
|
1355
1099
|
}
|
|
1356
1100
|
async function clearCache(cwd, options) {
|
|
1357
1101
|
const cleared = [];
|
|
1358
|
-
const cacheDir =
|
|
1102
|
+
const cacheDir = path.join(cwd, ".kb", "cache");
|
|
1359
1103
|
try {
|
|
1360
1104
|
const entries = await promises.readdir(cacheDir);
|
|
1361
1105
|
for (const entry of entries) {
|
|
1362
1106
|
if (entry.includes("manifest") || entry.includes("plugin")) {
|
|
1363
|
-
const entryPath =
|
|
1107
|
+
const entryPath = path.join(cacheDir, entry);
|
|
1364
1108
|
await promises.unlink(entryPath);
|
|
1365
1109
|
cleared.push(entry);
|
|
1366
1110
|
}
|
|
@@ -1925,7 +1669,7 @@ var diag = defineSystemCommand({
|
|
|
1925
1669
|
});
|
|
1926
1670
|
}
|
|
1927
1671
|
try {
|
|
1928
|
-
const cachePath =
|
|
1672
|
+
const cachePath = path.join(cwd, ".kb", "cache", "cli-manifests.json");
|
|
1929
1673
|
const cacheExists = await promises.access(cachePath).then(() => true).catch(() => false);
|
|
1930
1674
|
if (cacheExists) {
|
|
1931
1675
|
const cache = JSON.parse(await promises.readFile(cachePath, "utf8"));
|
|
@@ -2135,7 +1879,7 @@ var pluginsList = defineSystemCommand({
|
|
|
2135
1879
|
let version2 = "unknown";
|
|
2136
1880
|
if (cmd.pkgRoot) {
|
|
2137
1881
|
try {
|
|
2138
|
-
const pkgPath =
|
|
1882
|
+
const pkgPath = path.join(cmd.pkgRoot, "package.json");
|
|
2139
1883
|
const pkgJson = JSON.parse(await promises.readFile(pkgPath, "utf8"));
|
|
2140
1884
|
version2 = pkgJson.version || "unknown";
|
|
2141
1885
|
} catch {
|
|
@@ -2143,22 +1887,22 @@ var pluginsList = defineSystemCommand({
|
|
|
2143
1887
|
}
|
|
2144
1888
|
if (version2 === "unknown") {
|
|
2145
1889
|
try {
|
|
2146
|
-
const pkgPath =
|
|
1890
|
+
const pkgPath = path.join(cwd, "node_modules", pkgName, "package.json");
|
|
2147
1891
|
const pkgJson = JSON.parse(await promises.readFile(pkgPath, "utf8"));
|
|
2148
1892
|
version2 = pkgJson.version || "unknown";
|
|
2149
1893
|
} catch {
|
|
2150
1894
|
try {
|
|
2151
|
-
const pkgPath =
|
|
1895
|
+
const pkgPath = path.join(cwd, "packages", namespace.replace("@kb-labs/", "").replace("-cli", ""), "package.json");
|
|
2152
1896
|
const pkgJson = JSON.parse(await promises.readFile(pkgPath, "utf8"));
|
|
2153
1897
|
version2 = pkgJson.version || "unknown";
|
|
2154
1898
|
} catch {
|
|
2155
1899
|
try {
|
|
2156
|
-
const workspaceRoot =
|
|
2157
|
-
const pkgDirs = await promises.readdir(
|
|
1900
|
+
const workspaceRoot = path.resolve(cwd, "..");
|
|
1901
|
+
const pkgDirs = await promises.readdir(path.join(workspaceRoot, "packages"), { withFileTypes: true });
|
|
2158
1902
|
for (const dir of pkgDirs) {
|
|
2159
1903
|
if (dir.isDirectory()) {
|
|
2160
1904
|
try {
|
|
2161
|
-
const pkgPath =
|
|
1905
|
+
const pkgPath = path.join(workspaceRoot, "packages", dir.name, "package.json");
|
|
2162
1906
|
const pkgJson = JSON.parse(await promises.readFile(pkgPath, "utf8"));
|
|
2163
1907
|
if (pkgJson.name === pkgName) {
|
|
2164
1908
|
version2 = pkgJson.version || "unknown";
|
|
@@ -2600,9 +2344,9 @@ var pluginsLink = defineSystemCommand({
|
|
|
2600
2344
|
throw new Error("Please specify a plugin path to link");
|
|
2601
2345
|
}
|
|
2602
2346
|
const cwd = getContextCwd(ctx);
|
|
2603
|
-
const absPath =
|
|
2347
|
+
const absPath = path.resolve(cwd, pluginPath);
|
|
2604
2348
|
ctx.platform?.logger?.info("Linking plugin", { pluginPath, absPath });
|
|
2605
|
-
const pkgJsonPath =
|
|
2349
|
+
const pkgJsonPath = path.join(absPath, "package.json");
|
|
2606
2350
|
await promises.access(pkgJsonPath);
|
|
2607
2351
|
const pkgJson = JSON.parse(await promises.readFile(pkgJsonPath, "utf8"));
|
|
2608
2352
|
const hasManifest = pkgJson.kb?.commandsManifest || pkgJson.exports?.["./kb/commands"];
|
|
@@ -2613,7 +2357,7 @@ var pluginsLink = defineSystemCommand({
|
|
|
2613
2357
|
ctx.platform?.logger?.info("Plugin linked", { packageName: pkgJson.name, absPath });
|
|
2614
2358
|
let configUpdated = false;
|
|
2615
2359
|
try {
|
|
2616
|
-
const configPath =
|
|
2360
|
+
const configPath = path.join(cwd, ".kb", "kb.config.json");
|
|
2617
2361
|
const config = JSON.parse(await promises.readFile(configPath, "utf8"));
|
|
2618
2362
|
if (!config.plugins) {
|
|
2619
2363
|
config.plugins = {};
|
|
@@ -2679,7 +2423,7 @@ var pluginsUnlink = defineSystemCommand({
|
|
|
2679
2423
|
ctx.platform?.logger?.info("Unlinking plugin", { identifier });
|
|
2680
2424
|
let absPath;
|
|
2681
2425
|
try {
|
|
2682
|
-
absPath =
|
|
2426
|
+
absPath = path.resolve(cwd, identifier);
|
|
2683
2427
|
await promises.access(absPath);
|
|
2684
2428
|
} catch {
|
|
2685
2429
|
const matched = state.linked.find((p) => p.includes(identifier) || p.endsWith(identifier));
|
|
@@ -2709,12 +2453,12 @@ var pluginsUnlink = defineSystemCommand({
|
|
|
2709
2453
|
}
|
|
2710
2454
|
});
|
|
2711
2455
|
function detectRepoRoot(start = process.cwd()) {
|
|
2712
|
-
let cur =
|
|
2456
|
+
let cur = path.resolve(start);
|
|
2713
2457
|
while (true) {
|
|
2714
|
-
if (existsSync(
|
|
2458
|
+
if (existsSync(path.join(cur, ".git"))) {
|
|
2715
2459
|
return cur;
|
|
2716
2460
|
}
|
|
2717
|
-
const parent =
|
|
2461
|
+
const parent = path.dirname(cur);
|
|
2718
2462
|
if (parent === cur) {
|
|
2719
2463
|
return start;
|
|
2720
2464
|
}
|
|
@@ -2723,7 +2467,7 @@ function detectRepoRoot(start = process.cwd()) {
|
|
|
2723
2467
|
}
|
|
2724
2468
|
async function findRestApiManifestPath(pkgRoot, pkg) {
|
|
2725
2469
|
if (pkg.kbLabs?.manifest) {
|
|
2726
|
-
const manifestPath =
|
|
2470
|
+
const manifestPath = path.isAbsolute(pkg.kbLabs.manifest) ? pkg.kbLabs.manifest : path.join(pkgRoot, pkg.kbLabs.manifest);
|
|
2727
2471
|
try {
|
|
2728
2472
|
await promises.access(manifestPath);
|
|
2729
2473
|
return manifestPath;
|
|
@@ -2732,7 +2476,7 @@ async function findRestApiManifestPath(pkgRoot, pkg) {
|
|
|
2732
2476
|
}
|
|
2733
2477
|
}
|
|
2734
2478
|
if (pkg.kb?.manifest) {
|
|
2735
|
-
const manifestPath =
|
|
2479
|
+
const manifestPath = path.isAbsolute(pkg.kb.manifest) ? pkg.kb.manifest : path.join(pkgRoot, pkg.kb.manifest);
|
|
2736
2480
|
try {
|
|
2737
2481
|
await promises.access(manifestPath);
|
|
2738
2482
|
return manifestPath;
|
|
@@ -2742,7 +2486,7 @@ async function findRestApiManifestPath(pkgRoot, pkg) {
|
|
|
2742
2486
|
}
|
|
2743
2487
|
if (Array.isArray(pkg.kbLabs?.plugins)) {
|
|
2744
2488
|
for (const pluginPath of pkg.kbLabs.plugins) {
|
|
2745
|
-
const manifestPath =
|
|
2489
|
+
const manifestPath = path.isAbsolute(pluginPath) ? pluginPath : path.join(pkgRoot, pluginPath);
|
|
2746
2490
|
try {
|
|
2747
2491
|
await promises.access(manifestPath);
|
|
2748
2492
|
return manifestPath;
|
|
@@ -2753,7 +2497,7 @@ async function findRestApiManifestPath(pkgRoot, pkg) {
|
|
|
2753
2497
|
}
|
|
2754
2498
|
if (Array.isArray(pkg.kb?.plugins)) {
|
|
2755
2499
|
for (const pluginPath of pkg.kb.plugins) {
|
|
2756
|
-
const manifestPath =
|
|
2500
|
+
const manifestPath = path.isAbsolute(pluginPath) ? pluginPath : path.join(pkgRoot, pluginPath);
|
|
2757
2501
|
try {
|
|
2758
2502
|
await promises.access(manifestPath);
|
|
2759
2503
|
return manifestPath;
|
|
@@ -2762,22 +2506,22 @@ async function findRestApiManifestPath(pkgRoot, pkg) {
|
|
|
2762
2506
|
}
|
|
2763
2507
|
}
|
|
2764
2508
|
}
|
|
2765
|
-
const pluginsDir =
|
|
2509
|
+
const pluginsDir = path.join(pkgRoot, ".kblabs", "plugins");
|
|
2766
2510
|
try {
|
|
2767
2511
|
const entries = await promises.readdir(pluginsDir, { withFileTypes: true });
|
|
2768
2512
|
for (const entry of entries) {
|
|
2769
2513
|
if (entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".js"))) {
|
|
2770
|
-
return
|
|
2514
|
+
return path.join(pluginsDir, entry.name);
|
|
2771
2515
|
}
|
|
2772
2516
|
}
|
|
2773
2517
|
} catch {
|
|
2774
2518
|
}
|
|
2775
2519
|
const conventionalPaths = [
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2520
|
+
path.join(pkgRoot, "dist", "manifest.v2.js"),
|
|
2521
|
+
path.join(pkgRoot, "dist", "manifest.v2.ts"),
|
|
2522
|
+
path.join(pkgRoot, "src", "manifest.v2.ts"),
|
|
2523
|
+
path.join(pkgRoot, "manifest.v2.ts"),
|
|
2524
|
+
path.join(pkgRoot, "manifest.v2.js")
|
|
2781
2525
|
];
|
|
2782
2526
|
for (const manifestPath of conventionalPaths) {
|
|
2783
2527
|
try {
|
|
@@ -2791,7 +2535,7 @@ async function findRestApiManifestPath(pkgRoot, pkg) {
|
|
|
2791
2535
|
}
|
|
2792
2536
|
async function discoverRestApiPlugins(repoRoot) {
|
|
2793
2537
|
const results = [];
|
|
2794
|
-
const workspaceYaml =
|
|
2538
|
+
const workspaceYaml = path.join(repoRoot, "pnpm-workspace.yaml");
|
|
2795
2539
|
let workspacePatterns = [];
|
|
2796
2540
|
try {
|
|
2797
2541
|
const content = await promises.readFile(workspaceYaml, "utf8");
|
|
@@ -2803,19 +2547,19 @@ async function discoverRestApiPlugins(repoRoot) {
|
|
|
2803
2547
|
if (workspacePatterns.length === 0) {
|
|
2804
2548
|
let currentDir = repoRoot;
|
|
2805
2549
|
for (let i = 0; i < 3; i++) {
|
|
2806
|
-
const parentYaml =
|
|
2550
|
+
const parentYaml = path.join(path.dirname(currentDir), "pnpm-workspace.yaml");
|
|
2807
2551
|
try {
|
|
2808
2552
|
const content = await promises.readFile(parentYaml, "utf8");
|
|
2809
2553
|
const parsed = parse(content);
|
|
2810
2554
|
if (parsed.packages && parsed.packages.length > 0) {
|
|
2811
2555
|
workspacePatterns = parsed.packages;
|
|
2812
|
-
repoRoot =
|
|
2556
|
+
repoRoot = path.dirname(currentDir);
|
|
2813
2557
|
break;
|
|
2814
2558
|
}
|
|
2815
2559
|
} catch {
|
|
2816
2560
|
}
|
|
2817
|
-
currentDir =
|
|
2818
|
-
if (currentDir ===
|
|
2561
|
+
currentDir = path.dirname(currentDir);
|
|
2562
|
+
if (currentDir === path.dirname(currentDir)) {
|
|
2819
2563
|
break;
|
|
2820
2564
|
}
|
|
2821
2565
|
}
|
|
@@ -2835,7 +2579,7 @@ async function discoverRestApiPlugins(repoRoot) {
|
|
|
2835
2579
|
}
|
|
2836
2580
|
const uniquePaths = Array.from(new Set(packageJsonPaths));
|
|
2837
2581
|
for (const pkgJsonPath of uniquePaths) {
|
|
2838
|
-
const pkgRoot =
|
|
2582
|
+
const pkgRoot = path.dirname(pkgJsonPath);
|
|
2839
2583
|
try {
|
|
2840
2584
|
const pkgContent = await promises.readFile(pkgJsonPath, "utf8");
|
|
2841
2585
|
const pkg = JSON.parse(pkgContent);
|
|
@@ -2850,7 +2594,7 @@ async function discoverRestApiPlugins(repoRoot) {
|
|
|
2850
2594
|
const manifestV2 = manifest;
|
|
2851
2595
|
results.push({
|
|
2852
2596
|
manifest: manifestV2,
|
|
2853
|
-
manifestPath:
|
|
2597
|
+
manifestPath: path.resolve(manifestPath),
|
|
2854
2598
|
pluginRoot: pkgRoot
|
|
2855
2599
|
});
|
|
2856
2600
|
}
|
|
@@ -3041,7 +2785,7 @@ var pluginsDoctor = defineSystemCommand({
|
|
|
3041
2785
|
}
|
|
3042
2786
|
if (cmd.manifest.engine?.module) {
|
|
3043
2787
|
const required = cmd.manifest.engine.module;
|
|
3044
|
-
const pkgJsonPath =
|
|
2788
|
+
const pkgJsonPath = path.join(cmd.pkgRoot || cwd, "package.json");
|
|
3045
2789
|
try {
|
|
3046
2790
|
const pkgJson = JSON.parse(await promises.readFile(pkgJsonPath, "utf8"));
|
|
3047
2791
|
const isESM = pkgJson.type === "module";
|
|
@@ -3189,7 +2933,7 @@ var pluginsScaffold = defineSystemCommand({
|
|
|
3189
2933
|
const moduleType = isESM ? "module" : "commonjs";
|
|
3190
2934
|
const tsupFormat = isESM ? "esm" : "cjs";
|
|
3191
2935
|
const baseDir = getContextCwd(ctx);
|
|
3192
|
-
const dir =
|
|
2936
|
+
const dir = path.join(baseDir, pluginName);
|
|
3193
2937
|
try {
|
|
3194
2938
|
await promises.access(dir);
|
|
3195
2939
|
ctx.platform?.logger?.warn("Directory already exists", { dir });
|
|
@@ -3200,9 +2944,9 @@ var pluginsScaffold = defineSystemCommand({
|
|
|
3200
2944
|
}
|
|
3201
2945
|
}
|
|
3202
2946
|
await promises.mkdir(dir, { recursive: true });
|
|
3203
|
-
await promises.mkdir(
|
|
3204
|
-
await promises.mkdir(
|
|
3205
|
-
await promises.mkdir(
|
|
2947
|
+
await promises.mkdir(path.join(dir, "src"), { recursive: true });
|
|
2948
|
+
await promises.mkdir(path.join(dir, "src", "kb"), { recursive: true });
|
|
2949
|
+
await promises.mkdir(path.join(dir, "src", "commands"), { recursive: true });
|
|
3206
2950
|
const packageJson = {
|
|
3207
2951
|
name: `@your-scope/${pluginName}-cli`,
|
|
3208
2952
|
version: "1.0.0",
|
|
@@ -3232,7 +2976,7 @@ var pluginsScaffold = defineSystemCommand({
|
|
|
3232
2976
|
typescript: "^5.6.3"
|
|
3233
2977
|
}
|
|
3234
2978
|
};
|
|
3235
|
-
await promises.writeFile(
|
|
2979
|
+
await promises.writeFile(path.join(dir, "package.json"), JSON.stringify(packageJson, null, 2), "utf8");
|
|
3236
2980
|
const tsconfig = {
|
|
3237
2981
|
extends: "@kb-labs/devkit/tsconfig/lib.json",
|
|
3238
2982
|
compilerOptions: {
|
|
@@ -3241,7 +2985,7 @@ var pluginsScaffold = defineSystemCommand({
|
|
|
3241
2985
|
},
|
|
3242
2986
|
include: ["src/**/*"]
|
|
3243
2987
|
};
|
|
3244
|
-
await promises.writeFile(
|
|
2988
|
+
await promises.writeFile(path.join(dir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2), "utf8");
|
|
3245
2989
|
const manifestContent = `import type { ManifestV2 } from '@kb-labs/plugin-contracts';
|
|
3246
2990
|
|
|
3247
2991
|
export const manifest: ManifestV2 = {
|
|
@@ -3274,7 +3018,7 @@ export const manifest: ManifestV2 = {
|
|
|
3274
3018
|
}
|
|
3275
3019
|
};
|
|
3276
3020
|
`;
|
|
3277
|
-
await promises.writeFile(
|
|
3021
|
+
await promises.writeFile(path.join(dir, "src", "kb", `manifest.${extension}`), manifestContent, "utf8");
|
|
3278
3022
|
const commandContent = `import { defineCommand, type CommandResult } from '@kb-labs/shared-command-kit';
|
|
3279
3023
|
|
|
3280
3024
|
// Define flag types for type safety
|
|
@@ -3344,7 +3088,7 @@ export const run = defineCommand<${pluginName.charAt(0).toUpperCase() + pluginNa
|
|
|
3344
3088
|
// Remove both type parameters for quick prototyping:
|
|
3345
3089
|
// export const run = defineCommand({ ... });
|
|
3346
3090
|
`;
|
|
3347
|
-
await promises.writeFile(
|
|
3091
|
+
await promises.writeFile(path.join(dir, "src", "commands", `hello.${extension}`), commandContent, "utf8");
|
|
3348
3092
|
const tsupConfig = `import config from "@kb-labs/devkit/tsup/node.js";
|
|
3349
3093
|
|
|
3350
3094
|
export default {
|
|
@@ -3354,7 +3098,7 @@ export default {
|
|
|
3354
3098
|
dts: true,
|
|
3355
3099
|
};
|
|
3356
3100
|
`;
|
|
3357
|
-
await promises.writeFile(
|
|
3101
|
+
await promises.writeFile(path.join(dir, "tsup.config.ts"), tsupConfig, "utf8");
|
|
3358
3102
|
const readme = `# ${pluginName} CLI Plugin
|
|
3359
3103
|
|
|
3360
3104
|
KB Labs CLI plugin for ${pluginName}.
|
|
@@ -3384,13 +3128,13 @@ pnpm dev
|
|
|
3384
3128
|
kb plugins link ./${pluginName}
|
|
3385
3129
|
\`\`\`
|
|
3386
3130
|
`;
|
|
3387
|
-
await promises.writeFile(
|
|
3131
|
+
await promises.writeFile(path.join(dir, "README.md"), readme, "utf8");
|
|
3388
3132
|
const gitignore = `node_modules
|
|
3389
3133
|
dist
|
|
3390
3134
|
*.log
|
|
3391
3135
|
.DS_Store
|
|
3392
3136
|
`;
|
|
3393
|
-
await promises.writeFile(
|
|
3137
|
+
await promises.writeFile(path.join(dir, ".gitignore"), gitignore, "utf8");
|
|
3394
3138
|
ctx.platform?.logger?.info("Plugin scaffold created", { pluginName, dir });
|
|
3395
3139
|
return {
|
|
3396
3140
|
ok: true,
|
|
@@ -3423,7 +3167,7 @@ dist
|
|
|
3423
3167
|
});
|
|
3424
3168
|
async function validateManifestAgainstContracts(manifest, contractsPath) {
|
|
3425
3169
|
try {
|
|
3426
|
-
const contractsModule = await import(
|
|
3170
|
+
const contractsModule = await import(path.resolve(contractsPath));
|
|
3427
3171
|
const contracts = contractsModule.pluginContractsManifest || contractsModule.default;
|
|
3428
3172
|
if (!contracts) {
|
|
3429
3173
|
return { ok: false, issues: ["Contracts file does not export pluginContractsManifest"] };
|
|
@@ -3477,7 +3221,7 @@ var pluginValidate = defineSystemCommand({
|
|
|
3477
3221
|
},
|
|
3478
3222
|
async handler(ctx, argv, flags) {
|
|
3479
3223
|
const cwd = getContextCwd(ctx);
|
|
3480
|
-
const manifestPath =
|
|
3224
|
+
const manifestPath = path.resolve(cwd, flags.manifest || "manifest.v2.ts");
|
|
3481
3225
|
ctx.ui.write(`Validating manifest: ${manifestPath}
|
|
3482
3226
|
`);
|
|
3483
3227
|
let manifestContent;
|
|
@@ -3513,7 +3257,7 @@ var pluginValidate = defineSystemCommand({
|
|
|
3513
3257
|
}
|
|
3514
3258
|
ctx.ui.success("\u2705 Manifest structure is valid!\n");
|
|
3515
3259
|
if (flags.contracts) {
|
|
3516
|
-
const contractsPath =
|
|
3260
|
+
const contractsPath = path.resolve(cwd, flags.contracts);
|
|
3517
3261
|
ctx.ui.write(`
|
|
3518
3262
|
Validating against contracts: ${contractsPath}
|
|
3519
3263
|
`);
|
|
@@ -3630,7 +3374,7 @@ var docsGenerateCliReference = defineSystemCommand({
|
|
|
3630
3374
|
// eslint-disable-next-line sonarjs/cognitive-complexity -- Complex markdown generation with nested loops for product groups, commands, and formatting
|
|
3631
3375
|
async handler(ctx, argv, flags) {
|
|
3632
3376
|
const cwd = getContextCwd(ctx);
|
|
3633
|
-
const outputPath = flags.output ||
|
|
3377
|
+
const outputPath = flags.output || path.join(cwd, "CLI-REFERENCE.md");
|
|
3634
3378
|
const commands = registry.list();
|
|
3635
3379
|
const productGroups = registry.listProductGroups();
|
|
3636
3380
|
ctx.platform?.logger?.info("Generating CLI reference", {
|
|
@@ -4657,15 +4401,15 @@ var logsGroup = defineSystemCommandGroup("logs", "Log viewing and analysis comma
|
|
|
4657
4401
|
]);
|
|
4658
4402
|
var req = createRequire(import.meta.url);
|
|
4659
4403
|
var __filename$1 = fileURLToPath(import.meta.url);
|
|
4660
|
-
var __dirname$1 =
|
|
4404
|
+
var __dirname$1 = path.dirname(__filename$1);
|
|
4661
4405
|
function resolveFromCwd(spec, cwd) {
|
|
4662
|
-
const cliRoot =
|
|
4406
|
+
const cliRoot = path.resolve(__dirname$1, "../../..");
|
|
4663
4407
|
let monorepoRoot = cwd;
|
|
4664
|
-
while (monorepoRoot !==
|
|
4665
|
-
if (existsSync(
|
|
4408
|
+
while (monorepoRoot !== path.dirname(monorepoRoot)) {
|
|
4409
|
+
if (existsSync(path.join(monorepoRoot, "pnpm-workspace.yaml"))) {
|
|
4666
4410
|
break;
|
|
4667
4411
|
}
|
|
4668
|
-
monorepoRoot =
|
|
4412
|
+
monorepoRoot = path.dirname(monorepoRoot);
|
|
4669
4413
|
}
|
|
4670
4414
|
const pathsToTry = [
|
|
4671
4415
|
cwd,
|
|
@@ -4674,9 +4418,9 @@ function resolveFromCwd(spec, cwd) {
|
|
|
4674
4418
|
// CLI installation directory
|
|
4675
4419
|
monorepoRoot,
|
|
4676
4420
|
// Monorepo root (if found)
|
|
4677
|
-
|
|
4421
|
+
path.join(monorepoRoot, "../kb-labs-cli"),
|
|
4678
4422
|
// Explicit kb-labs-cli path
|
|
4679
|
-
|
|
4423
|
+
path.join(monorepoRoot, "../kb-labs-mind")
|
|
4680
4424
|
// Explicit kb-labs-mind path
|
|
4681
4425
|
].filter(Boolean);
|
|
4682
4426
|
for (const searchPath of pathsToTry) {
|
|
@@ -4694,12 +4438,12 @@ function checkRequires(manifest, options = {}) {
|
|
|
4694
4438
|
}
|
|
4695
4439
|
let isInMonorepo = false;
|
|
4696
4440
|
let monorepoRoot = cwd;
|
|
4697
|
-
while (monorepoRoot !==
|
|
4698
|
-
if (existsSync(
|
|
4441
|
+
while (monorepoRoot !== path.dirname(monorepoRoot)) {
|
|
4442
|
+
if (existsSync(path.join(monorepoRoot, "pnpm-workspace.yaml"))) {
|
|
4699
4443
|
isInMonorepo = true;
|
|
4700
4444
|
break;
|
|
4701
4445
|
}
|
|
4702
|
-
monorepoRoot =
|
|
4446
|
+
monorepoRoot = path.dirname(monorepoRoot);
|
|
4703
4447
|
}
|
|
4704
4448
|
for (const dep of manifest.requires) {
|
|
4705
4449
|
try {
|
|
@@ -4723,8 +4467,8 @@ function checkRequires(manifest, options = {}) {
|
|
|
4723
4467
|
if (!packageName) {
|
|
4724
4468
|
continue;
|
|
4725
4469
|
}
|
|
4726
|
-
const cliRoot =
|
|
4727
|
-
const packagePath = scope ?
|
|
4470
|
+
const cliRoot = path.resolve(__dirname$1, "../../..");
|
|
4471
|
+
const packagePath = scope ? path.join(cliRoot, "node_modules", "@" + scope, packageName, "package.json") : path.join(cliRoot, "node_modules", packageName, "package.json");
|
|
4728
4472
|
if (existsSync(packagePath)) {
|
|
4729
4473
|
continue;
|
|
4730
4474
|
}
|