@lark-apaas/fullstack-cli 1.1.6-alpha.12 → 1.1.6-alpha.14
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 +1065 -183
- package/package.json +4 -3
- package/templates/scripts/dev.sh +223 -32
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import fs18 from "fs";
|
|
3
|
+
import path16 from "path";
|
|
4
4
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
5
5
|
import { config as dotenvConfig } from "dotenv";
|
|
6
6
|
|
|
@@ -168,15 +168,15 @@ async function run(options = {}) {
|
|
|
168
168
|
console.error("[gen-db-schema] schema.ts not generated");
|
|
169
169
|
throw new Error("drizzle-kit introspect failed to generate schema.ts");
|
|
170
170
|
}
|
|
171
|
-
fs.mkdirSync(path.dirname(SCHEMA_FILE), { recursive: true });
|
|
172
|
-
fs.copyFileSync(generatedSchema, SCHEMA_FILE);
|
|
173
|
-
console.log(`[gen-db-schema] \u2713 Copied to ${outputPath}`);
|
|
174
171
|
const { postprocessDrizzleSchema } = require2("@lark-apaas/devtool-kits");
|
|
175
|
-
const stats = postprocessDrizzleSchema(
|
|
172
|
+
const stats = postprocessDrizzleSchema(generatedSchema);
|
|
176
173
|
if (stats?.unmatchedUnknown?.length) {
|
|
177
174
|
console.warn("[gen-db-schema] Unmatched custom types detected:", stats.unmatchedUnknown);
|
|
178
175
|
}
|
|
179
176
|
console.log("[gen-db-schema] \u2713 Postprocessed schema");
|
|
177
|
+
fs.mkdirSync(path.dirname(SCHEMA_FILE), { recursive: true });
|
|
178
|
+
fs.copyFileSync(generatedSchema, SCHEMA_FILE);
|
|
179
|
+
console.log(`[gen-db-schema] \u2713 Copied to ${outputPath}`);
|
|
180
180
|
try {
|
|
181
181
|
if (options.enableNestModuleGenerate) {
|
|
182
182
|
const { parseAndGenerateNestResourceTemplate } = require2("@lark-apaas/devtool-kits");
|
|
@@ -216,8 +216,8 @@ var genDbSchemaCommand = {
|
|
|
216
216
|
};
|
|
217
217
|
|
|
218
218
|
// src/commands/sync/run.handler.ts
|
|
219
|
-
import
|
|
220
|
-
import
|
|
219
|
+
import path3 from "path";
|
|
220
|
+
import fs3 from "fs";
|
|
221
221
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
222
222
|
|
|
223
223
|
// src/config/sync.ts
|
|
@@ -254,6 +254,12 @@ var syncConfig = {
|
|
|
254
254
|
type: "delete-directory",
|
|
255
255
|
to: ".swc"
|
|
256
256
|
// 删除 .swc 目录(如果存在)
|
|
257
|
+
},
|
|
258
|
+
// 4. 从 .gitignore 中移除 package-lock.json
|
|
259
|
+
{
|
|
260
|
+
type: "remove-line",
|
|
261
|
+
to: ".gitignore",
|
|
262
|
+
pattern: "package-lock.json"
|
|
257
263
|
}
|
|
258
264
|
],
|
|
259
265
|
// 文件权限设置
|
|
@@ -274,18 +280,39 @@ function genSyncConfig(perms = {}) {
|
|
|
274
280
|
return syncConfig;
|
|
275
281
|
}
|
|
276
282
|
|
|
283
|
+
// src/utils/file-ops.ts
|
|
284
|
+
import fs2 from "fs";
|
|
285
|
+
import path2 from "path";
|
|
286
|
+
function removeLineFromFile(filePath, pattern) {
|
|
287
|
+
if (!fs2.existsSync(filePath)) {
|
|
288
|
+
console.log(`[fullstack-cli] \u25CB ${path2.basename(filePath)} (not found)`);
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
292
|
+
const lines = content.split("\n");
|
|
293
|
+
const trimmedPattern = pattern.trim();
|
|
294
|
+
const filteredLines = lines.filter((line) => line.trim() !== trimmedPattern);
|
|
295
|
+
if (filteredLines.length === lines.length) {
|
|
296
|
+
console.log(`[fullstack-cli] \u25CB ${path2.basename(filePath)} (pattern not found: ${pattern})`);
|
|
297
|
+
return false;
|
|
298
|
+
}
|
|
299
|
+
fs2.writeFileSync(filePath, filteredLines.join("\n"));
|
|
300
|
+
console.log(`[fullstack-cli] \u2713 ${path2.basename(filePath)} (removed: ${pattern})`);
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
|
|
277
304
|
// src/commands/sync/run.handler.ts
|
|
278
305
|
async function run2(options) {
|
|
279
306
|
const userProjectRoot = process.env.INIT_CWD || process.cwd();
|
|
280
307
|
const __filename = fileURLToPath2(import.meta.url);
|
|
281
|
-
const __dirname2 =
|
|
282
|
-
const pluginRoot =
|
|
308
|
+
const __dirname2 = path3.dirname(__filename);
|
|
309
|
+
const pluginRoot = path3.resolve(__dirname2, "..");
|
|
283
310
|
if (userProjectRoot === pluginRoot) {
|
|
284
311
|
console.log("[fullstack-cli] Skip syncing (installing plugin itself)");
|
|
285
312
|
process.exit(0);
|
|
286
313
|
}
|
|
287
|
-
const userPackageJson =
|
|
288
|
-
if (!
|
|
314
|
+
const userPackageJson = path3.join(userProjectRoot, "package.json");
|
|
315
|
+
if (!fs3.existsSync(userPackageJson)) {
|
|
289
316
|
console.log("[fullstack-cli] Skip syncing (not a valid npm project)");
|
|
290
317
|
process.exit(0);
|
|
291
318
|
}
|
|
@@ -313,7 +340,7 @@ async function run2(options) {
|
|
|
313
340
|
}
|
|
314
341
|
async function syncRule(rule, pluginRoot, userProjectRoot) {
|
|
315
342
|
if (rule.type === "delete-file" || rule.type === "delete-directory") {
|
|
316
|
-
const destPath2 =
|
|
343
|
+
const destPath2 = path3.join(userProjectRoot, rule.to);
|
|
317
344
|
if (rule.type === "delete-file") {
|
|
318
345
|
deleteFile(destPath2);
|
|
319
346
|
} else {
|
|
@@ -321,12 +348,17 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
|
|
|
321
348
|
}
|
|
322
349
|
return;
|
|
323
350
|
}
|
|
351
|
+
if (rule.type === "remove-line") {
|
|
352
|
+
const destPath2 = path3.join(userProjectRoot, rule.to);
|
|
353
|
+
removeLineFromFile(destPath2, rule.pattern);
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
324
356
|
if (!("from" in rule)) {
|
|
325
357
|
return;
|
|
326
358
|
}
|
|
327
|
-
const srcPath =
|
|
328
|
-
const destPath =
|
|
329
|
-
if (!
|
|
359
|
+
const srcPath = path3.join(pluginRoot, rule.from);
|
|
360
|
+
const destPath = path3.join(userProjectRoot, rule.to);
|
|
361
|
+
if (!fs3.existsSync(srcPath)) {
|
|
330
362
|
console.warn(`[fullstack-cli] Source not found: ${rule.from}`);
|
|
331
363
|
return;
|
|
332
364
|
}
|
|
@@ -343,64 +375,64 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
|
|
|
343
375
|
}
|
|
344
376
|
}
|
|
345
377
|
function syncFile(src, dest, overwrite = true) {
|
|
346
|
-
const destDir =
|
|
347
|
-
if (!
|
|
348
|
-
|
|
378
|
+
const destDir = path3.dirname(dest);
|
|
379
|
+
if (!fs3.existsSync(destDir)) {
|
|
380
|
+
fs3.mkdirSync(destDir, { recursive: true });
|
|
349
381
|
}
|
|
350
|
-
if (
|
|
351
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
382
|
+
if (fs3.existsSync(dest) && !overwrite) {
|
|
383
|
+
console.log(`[fullstack-cli] \u25CB ${path3.basename(dest)} (skipped, already exists)`);
|
|
352
384
|
return;
|
|
353
385
|
}
|
|
354
|
-
|
|
355
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
386
|
+
fs3.copyFileSync(src, dest);
|
|
387
|
+
console.log(`[fullstack-cli] \u2713 ${path3.basename(dest)}`);
|
|
356
388
|
}
|
|
357
389
|
function syncDirectory(src, dest, overwrite = true) {
|
|
358
|
-
if (!
|
|
359
|
-
|
|
390
|
+
if (!fs3.existsSync(dest)) {
|
|
391
|
+
fs3.mkdirSync(dest, { recursive: true });
|
|
360
392
|
}
|
|
361
|
-
const files =
|
|
393
|
+
const files = fs3.readdirSync(src);
|
|
362
394
|
let count = 0;
|
|
363
395
|
files.forEach((file) => {
|
|
364
|
-
const srcFile =
|
|
365
|
-
const destFile =
|
|
366
|
-
const stats =
|
|
396
|
+
const srcFile = path3.join(src, file);
|
|
397
|
+
const destFile = path3.join(dest, file);
|
|
398
|
+
const stats = fs3.statSync(srcFile);
|
|
367
399
|
if (stats.isDirectory()) {
|
|
368
400
|
syncDirectory(srcFile, destFile, overwrite);
|
|
369
401
|
} else {
|
|
370
|
-
if (overwrite || !
|
|
371
|
-
|
|
372
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
402
|
+
if (overwrite || !fs3.existsSync(destFile)) {
|
|
403
|
+
fs3.copyFileSync(srcFile, destFile);
|
|
404
|
+
console.log(`[fullstack-cli] \u2713 ${path3.relative(dest, destFile)}`);
|
|
373
405
|
count++;
|
|
374
406
|
}
|
|
375
407
|
}
|
|
376
408
|
});
|
|
377
409
|
if (count > 0) {
|
|
378
|
-
console.log(`[fullstack-cli] Synced ${count} files to ${
|
|
410
|
+
console.log(`[fullstack-cli] Synced ${count} files to ${path3.basename(dest)}/`);
|
|
379
411
|
}
|
|
380
412
|
}
|
|
381
413
|
function appendToFile(src, dest) {
|
|
382
|
-
const content =
|
|
414
|
+
const content = fs3.readFileSync(src, "utf-8");
|
|
383
415
|
let existingContent = "";
|
|
384
|
-
if (
|
|
385
|
-
existingContent =
|
|
416
|
+
if (fs3.existsSync(dest)) {
|
|
417
|
+
existingContent = fs3.readFileSync(dest, "utf-8");
|
|
386
418
|
}
|
|
387
419
|
if (existingContent.includes(content.trim())) {
|
|
388
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
420
|
+
console.log(`[fullstack-cli] \u25CB ${path3.basename(dest)} (already contains content)`);
|
|
389
421
|
return;
|
|
390
422
|
}
|
|
391
|
-
|
|
392
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
423
|
+
fs3.appendFileSync(dest, content);
|
|
424
|
+
console.log(`[fullstack-cli] \u2713 ${path3.basename(dest)} (appended)`);
|
|
393
425
|
}
|
|
394
426
|
function setPermissions(permissions, projectRoot) {
|
|
395
427
|
for (const [pattern, mode] of Object.entries(permissions)) {
|
|
396
428
|
if (pattern === "**/*.sh") {
|
|
397
|
-
const scriptsDir =
|
|
398
|
-
if (
|
|
399
|
-
const files =
|
|
429
|
+
const scriptsDir = path3.join(projectRoot, "scripts");
|
|
430
|
+
if (fs3.existsSync(scriptsDir)) {
|
|
431
|
+
const files = fs3.readdirSync(scriptsDir);
|
|
400
432
|
files.forEach((file) => {
|
|
401
433
|
if (file.endsWith(".sh")) {
|
|
402
|
-
const filePath =
|
|
403
|
-
|
|
434
|
+
const filePath = path3.join(scriptsDir, file);
|
|
435
|
+
fs3.chmodSync(filePath, mode);
|
|
404
436
|
}
|
|
405
437
|
});
|
|
406
438
|
}
|
|
@@ -408,19 +440,19 @@ function setPermissions(permissions, projectRoot) {
|
|
|
408
440
|
}
|
|
409
441
|
}
|
|
410
442
|
function deleteFile(filePath) {
|
|
411
|
-
if (
|
|
412
|
-
|
|
413
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
443
|
+
if (fs3.existsSync(filePath)) {
|
|
444
|
+
fs3.unlinkSync(filePath);
|
|
445
|
+
console.log(`[fullstack-cli] \u2713 ${path3.basename(filePath)} (deleted)`);
|
|
414
446
|
} else {
|
|
415
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
447
|
+
console.log(`[fullstack-cli] \u25CB ${path3.basename(filePath)} (not found)`);
|
|
416
448
|
}
|
|
417
449
|
}
|
|
418
450
|
function deleteDirectory(dirPath) {
|
|
419
|
-
if (
|
|
420
|
-
|
|
421
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
451
|
+
if (fs3.existsSync(dirPath)) {
|
|
452
|
+
fs3.rmSync(dirPath, { recursive: true });
|
|
453
|
+
console.log(`[fullstack-cli] \u2713 ${path3.basename(dirPath)} (deleted)`);
|
|
422
454
|
} else {
|
|
423
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
455
|
+
console.log(`[fullstack-cli] \u25CB ${path3.basename(dirPath)} (not found)`);
|
|
424
456
|
}
|
|
425
457
|
}
|
|
426
458
|
|
|
@@ -436,8 +468,8 @@ var syncCommand = {
|
|
|
436
468
|
};
|
|
437
469
|
|
|
438
470
|
// src/commands/action-plugin/utils.ts
|
|
439
|
-
import
|
|
440
|
-
import
|
|
471
|
+
import fs4 from "fs";
|
|
472
|
+
import path4 from "path";
|
|
441
473
|
import { spawnSync as spawnSync2, execSync } from "child_process";
|
|
442
474
|
function parsePluginName(input) {
|
|
443
475
|
const match = input.match(/^(@[^/]+\/[^@]+)(?:@(.+))?$/);
|
|
@@ -455,18 +487,18 @@ function getProjectRoot() {
|
|
|
455
487
|
return process.cwd();
|
|
456
488
|
}
|
|
457
489
|
function getPackageJsonPath() {
|
|
458
|
-
return
|
|
490
|
+
return path4.join(getProjectRoot(), "package.json");
|
|
459
491
|
}
|
|
460
492
|
function getPluginPath(pluginName) {
|
|
461
|
-
return
|
|
493
|
+
return path4.join(getProjectRoot(), "node_modules", pluginName);
|
|
462
494
|
}
|
|
463
495
|
function readPackageJson() {
|
|
464
496
|
const pkgPath = getPackageJsonPath();
|
|
465
|
-
if (!
|
|
497
|
+
if (!fs4.existsSync(pkgPath)) {
|
|
466
498
|
throw new Error("package.json not found in current directory");
|
|
467
499
|
}
|
|
468
500
|
try {
|
|
469
|
-
const content =
|
|
501
|
+
const content = fs4.readFileSync(pkgPath, "utf-8");
|
|
470
502
|
return JSON.parse(content);
|
|
471
503
|
} catch {
|
|
472
504
|
throw new Error("Failed to parse package.json");
|
|
@@ -474,7 +506,7 @@ function readPackageJson() {
|
|
|
474
506
|
}
|
|
475
507
|
function writePackageJson(pkg2) {
|
|
476
508
|
const pkgPath = getPackageJsonPath();
|
|
477
|
-
|
|
509
|
+
fs4.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
|
|
478
510
|
}
|
|
479
511
|
function readActionPlugins() {
|
|
480
512
|
const pkg2 = readPackageJson();
|
|
@@ -507,12 +539,12 @@ function npmInstall(tgzPath) {
|
|
|
507
539
|
}
|
|
508
540
|
}
|
|
509
541
|
function getPackageVersion(pluginName) {
|
|
510
|
-
const pkgJsonPath =
|
|
511
|
-
if (!
|
|
542
|
+
const pkgJsonPath = path4.join(getPluginPath(pluginName), "package.json");
|
|
543
|
+
if (!fs4.existsSync(pkgJsonPath)) {
|
|
512
544
|
return null;
|
|
513
545
|
}
|
|
514
546
|
try {
|
|
515
|
-
const content =
|
|
547
|
+
const content = fs4.readFileSync(pkgJsonPath, "utf-8");
|
|
516
548
|
const pkg2 = JSON.parse(content);
|
|
517
549
|
return pkg2.version || null;
|
|
518
550
|
} catch {
|
|
@@ -520,49 +552,49 @@ function getPackageVersion(pluginName) {
|
|
|
520
552
|
}
|
|
521
553
|
}
|
|
522
554
|
function readPluginPackageJson(pluginPath) {
|
|
523
|
-
const pkgJsonPath =
|
|
524
|
-
if (!
|
|
555
|
+
const pkgJsonPath = path4.join(pluginPath, "package.json");
|
|
556
|
+
if (!fs4.existsSync(pkgJsonPath)) {
|
|
525
557
|
return null;
|
|
526
558
|
}
|
|
527
559
|
try {
|
|
528
|
-
const content =
|
|
560
|
+
const content = fs4.readFileSync(pkgJsonPath, "utf-8");
|
|
529
561
|
return JSON.parse(content);
|
|
530
562
|
} catch {
|
|
531
563
|
return null;
|
|
532
564
|
}
|
|
533
565
|
}
|
|
534
566
|
function extractTgzToNodeModules(tgzPath, pluginName) {
|
|
535
|
-
const nodeModulesPath =
|
|
536
|
-
const targetDir =
|
|
537
|
-
const scopeDir =
|
|
538
|
-
if (!
|
|
539
|
-
|
|
567
|
+
const nodeModulesPath = path4.join(getProjectRoot(), "node_modules");
|
|
568
|
+
const targetDir = path4.join(nodeModulesPath, pluginName);
|
|
569
|
+
const scopeDir = path4.dirname(targetDir);
|
|
570
|
+
if (!fs4.existsSync(scopeDir)) {
|
|
571
|
+
fs4.mkdirSync(scopeDir, { recursive: true });
|
|
540
572
|
}
|
|
541
|
-
if (
|
|
542
|
-
|
|
573
|
+
if (fs4.existsSync(targetDir)) {
|
|
574
|
+
fs4.rmSync(targetDir, { recursive: true });
|
|
543
575
|
}
|
|
544
|
-
const tempDir =
|
|
545
|
-
if (
|
|
546
|
-
|
|
576
|
+
const tempDir = path4.join(nodeModulesPath, ".cache", "fullstack-cli", "extract-temp");
|
|
577
|
+
if (fs4.existsSync(tempDir)) {
|
|
578
|
+
fs4.rmSync(tempDir, { recursive: true });
|
|
547
579
|
}
|
|
548
|
-
|
|
580
|
+
fs4.mkdirSync(tempDir, { recursive: true });
|
|
549
581
|
try {
|
|
550
582
|
execSync(`tar -xzf "${tgzPath}" -C "${tempDir}"`, { stdio: "pipe" });
|
|
551
|
-
const extractedDir =
|
|
552
|
-
if (
|
|
553
|
-
|
|
583
|
+
const extractedDir = path4.join(tempDir, "package");
|
|
584
|
+
if (fs4.existsSync(extractedDir)) {
|
|
585
|
+
fs4.renameSync(extractedDir, targetDir);
|
|
554
586
|
} else {
|
|
555
|
-
const files =
|
|
587
|
+
const files = fs4.readdirSync(tempDir);
|
|
556
588
|
if (files.length === 1) {
|
|
557
|
-
|
|
589
|
+
fs4.renameSync(path4.join(tempDir, files[0]), targetDir);
|
|
558
590
|
} else {
|
|
559
591
|
throw new Error("Unexpected tgz structure");
|
|
560
592
|
}
|
|
561
593
|
}
|
|
562
594
|
return targetDir;
|
|
563
595
|
} finally {
|
|
564
|
-
if (
|
|
565
|
-
|
|
596
|
+
if (fs4.existsSync(tempDir)) {
|
|
597
|
+
fs4.rmSync(tempDir, { recursive: true });
|
|
566
598
|
}
|
|
567
599
|
}
|
|
568
600
|
}
|
|
@@ -571,10 +603,10 @@ function checkMissingPeerDeps(peerDeps) {
|
|
|
571
603
|
return [];
|
|
572
604
|
}
|
|
573
605
|
const missing = [];
|
|
574
|
-
const nodeModulesPath =
|
|
606
|
+
const nodeModulesPath = path4.join(getProjectRoot(), "node_modules");
|
|
575
607
|
for (const [depName, _version] of Object.entries(peerDeps)) {
|
|
576
|
-
const depPath =
|
|
577
|
-
if (!
|
|
608
|
+
const depPath = path4.join(nodeModulesPath, depName);
|
|
609
|
+
if (!fs4.existsSync(depPath)) {
|
|
578
610
|
missing.push(depName);
|
|
579
611
|
}
|
|
580
612
|
}
|
|
@@ -598,16 +630,16 @@ function installMissingDeps(deps) {
|
|
|
598
630
|
}
|
|
599
631
|
function removePluginDirectory(pluginName) {
|
|
600
632
|
const pluginPath = getPluginPath(pluginName);
|
|
601
|
-
if (
|
|
602
|
-
|
|
633
|
+
if (fs4.existsSync(pluginPath)) {
|
|
634
|
+
fs4.rmSync(pluginPath, { recursive: true });
|
|
603
635
|
console.log(`[action-plugin] Removed ${pluginName}`);
|
|
604
636
|
}
|
|
605
637
|
}
|
|
606
638
|
|
|
607
639
|
// src/commands/action-plugin/api-client.ts
|
|
608
640
|
import { HttpClient as HttpClient2 } from "@lark-apaas/http-client";
|
|
609
|
-
import
|
|
610
|
-
import
|
|
641
|
+
import fs5 from "fs";
|
|
642
|
+
import path5 from "path";
|
|
611
643
|
|
|
612
644
|
// src/utils/http-client.ts
|
|
613
645
|
import { HttpClient } from "@lark-apaas/http-client";
|
|
@@ -692,19 +724,19 @@ async function downloadFromPublic(downloadURL) {
|
|
|
692
724
|
return Buffer.from(arrayBuffer);
|
|
693
725
|
}
|
|
694
726
|
function getPluginCacheDir() {
|
|
695
|
-
return
|
|
727
|
+
return path5.join(process.cwd(), PLUGIN_CACHE_DIR);
|
|
696
728
|
}
|
|
697
729
|
function ensureCacheDir() {
|
|
698
730
|
const cacheDir = getPluginCacheDir();
|
|
699
|
-
if (!
|
|
700
|
-
|
|
731
|
+
if (!fs5.existsSync(cacheDir)) {
|
|
732
|
+
fs5.mkdirSync(cacheDir, { recursive: true });
|
|
701
733
|
}
|
|
702
734
|
}
|
|
703
735
|
function getTempFilePath(pluginKey, version) {
|
|
704
736
|
ensureCacheDir();
|
|
705
737
|
const safeKey = pluginKey.replace(/[/@]/g, "_");
|
|
706
738
|
const filename = `${safeKey}-${version}.tgz`;
|
|
707
|
-
return
|
|
739
|
+
return path5.join(getPluginCacheDir(), filename);
|
|
708
740
|
}
|
|
709
741
|
async function downloadPlugin(pluginKey, requestedVersion) {
|
|
710
742
|
console.log(`[action-plugin] Fetching plugin info for ${pluginKey}@${requestedVersion}...`);
|
|
@@ -720,7 +752,7 @@ async function downloadPlugin(pluginKey, requestedVersion) {
|
|
|
720
752
|
tgzBuffer = await downloadFromPublic(pluginInfo.downloadURL);
|
|
721
753
|
}
|
|
722
754
|
const tgzPath = getTempFilePath(pluginKey, pluginInfo.version);
|
|
723
|
-
|
|
755
|
+
fs5.writeFileSync(tgzPath, tgzBuffer);
|
|
724
756
|
console.log(`[action-plugin] Downloaded to ${tgzPath} (${(tgzBuffer.length / 1024).toFixed(2)} KB)`);
|
|
725
757
|
return {
|
|
726
758
|
tgzPath,
|
|
@@ -730,8 +762,8 @@ async function downloadPlugin(pluginKey, requestedVersion) {
|
|
|
730
762
|
}
|
|
731
763
|
function cleanupTempFile(tgzPath) {
|
|
732
764
|
try {
|
|
733
|
-
if (
|
|
734
|
-
|
|
765
|
+
if (fs5.existsSync(tgzPath)) {
|
|
766
|
+
fs5.unlinkSync(tgzPath);
|
|
735
767
|
}
|
|
736
768
|
} catch {
|
|
737
769
|
}
|
|
@@ -1056,39 +1088,39 @@ var actionPluginCommandGroup = {
|
|
|
1056
1088
|
};
|
|
1057
1089
|
|
|
1058
1090
|
// src/commands/capability/utils.ts
|
|
1059
|
-
import
|
|
1060
|
-
import
|
|
1091
|
+
import fs6 from "fs";
|
|
1092
|
+
import path6 from "path";
|
|
1061
1093
|
var CAPABILITIES_DIR = "server/capabilities";
|
|
1062
1094
|
function getProjectRoot2() {
|
|
1063
1095
|
return process.cwd();
|
|
1064
1096
|
}
|
|
1065
1097
|
function getCapabilitiesDir() {
|
|
1066
|
-
return
|
|
1098
|
+
return path6.join(getProjectRoot2(), CAPABILITIES_DIR);
|
|
1067
1099
|
}
|
|
1068
1100
|
function getCapabilityPath(id) {
|
|
1069
|
-
return
|
|
1101
|
+
return path6.join(getCapabilitiesDir(), `${id}.json`);
|
|
1070
1102
|
}
|
|
1071
1103
|
function getPluginManifestPath(pluginKey) {
|
|
1072
|
-
return
|
|
1104
|
+
return path6.join(getProjectRoot2(), "node_modules", pluginKey, "manifest.json");
|
|
1073
1105
|
}
|
|
1074
1106
|
function capabilitiesDirExists() {
|
|
1075
|
-
return
|
|
1107
|
+
return fs6.existsSync(getCapabilitiesDir());
|
|
1076
1108
|
}
|
|
1077
1109
|
function listCapabilityIds() {
|
|
1078
1110
|
const dir = getCapabilitiesDir();
|
|
1079
|
-
if (!
|
|
1111
|
+
if (!fs6.existsSync(dir)) {
|
|
1080
1112
|
return [];
|
|
1081
1113
|
}
|
|
1082
|
-
const files =
|
|
1114
|
+
const files = fs6.readdirSync(dir);
|
|
1083
1115
|
return files.filter((f) => f.endsWith(".json")).map((f) => f.replace(/\.json$/, ""));
|
|
1084
1116
|
}
|
|
1085
1117
|
function readCapability(id) {
|
|
1086
1118
|
const filePath = getCapabilityPath(id);
|
|
1087
|
-
if (!
|
|
1119
|
+
if (!fs6.existsSync(filePath)) {
|
|
1088
1120
|
throw new Error(`Capability not found: ${id}`);
|
|
1089
1121
|
}
|
|
1090
1122
|
try {
|
|
1091
|
-
const content =
|
|
1123
|
+
const content = fs6.readFileSync(filePath, "utf-8");
|
|
1092
1124
|
return JSON.parse(content);
|
|
1093
1125
|
} catch (error) {
|
|
1094
1126
|
if (error instanceof SyntaxError) {
|
|
@@ -1103,11 +1135,11 @@ function readAllCapabilities() {
|
|
|
1103
1135
|
}
|
|
1104
1136
|
function readPluginManifest(pluginKey) {
|
|
1105
1137
|
const manifestPath = getPluginManifestPath(pluginKey);
|
|
1106
|
-
if (!
|
|
1138
|
+
if (!fs6.existsSync(manifestPath)) {
|
|
1107
1139
|
throw new Error(`Plugin not installed: ${pluginKey} (manifest.json not found)`);
|
|
1108
1140
|
}
|
|
1109
1141
|
try {
|
|
1110
|
-
const content =
|
|
1142
|
+
const content = fs6.readFileSync(manifestPath, "utf-8");
|
|
1111
1143
|
return JSON.parse(content);
|
|
1112
1144
|
} catch (error) {
|
|
1113
1145
|
if (error instanceof SyntaxError) {
|
|
@@ -1138,14 +1170,6 @@ async function loadPlugin(pluginKey) {
|
|
|
1138
1170
|
);
|
|
1139
1171
|
}
|
|
1140
1172
|
}
|
|
1141
|
-
async function zodToJsonSchema(zodSchema) {
|
|
1142
|
-
try {
|
|
1143
|
-
const { zodToJsonSchema: convert } = await import("zod-to-json-schema");
|
|
1144
|
-
return convert(zodSchema);
|
|
1145
|
-
} catch {
|
|
1146
|
-
throw new Error("Failed to convert Zod schema to JSON Schema. Make sure zod-to-json-schema is installed.");
|
|
1147
|
-
}
|
|
1148
|
-
}
|
|
1149
1173
|
async function hydrateCapability(capability) {
|
|
1150
1174
|
try {
|
|
1151
1175
|
const manifest = readPluginManifest(capability.pluginKey);
|
|
@@ -1170,11 +1194,11 @@ async function hydrateCapability(capability) {
|
|
|
1170
1194
|
if (!pluginInstance) {
|
|
1171
1195
|
throw new Error(`Plugin instance not available for dynamic schema`);
|
|
1172
1196
|
}
|
|
1173
|
-
const
|
|
1174
|
-
if (!
|
|
1197
|
+
const jsonSchema = pluginInstance.getInputJsonSchema(manifestAction.key);
|
|
1198
|
+
if (!jsonSchema) {
|
|
1175
1199
|
throw new Error(`Failed to get input schema for action: ${manifestAction.key}`);
|
|
1176
1200
|
}
|
|
1177
|
-
inputSchema =
|
|
1201
|
+
inputSchema = jsonSchema;
|
|
1178
1202
|
} else {
|
|
1179
1203
|
inputSchema = manifestAction.inputSchema;
|
|
1180
1204
|
}
|
|
@@ -1183,11 +1207,11 @@ async function hydrateCapability(capability) {
|
|
|
1183
1207
|
if (!pluginInstance) {
|
|
1184
1208
|
throw new Error(`Plugin instance not available for dynamic schema`);
|
|
1185
1209
|
}
|
|
1186
|
-
const
|
|
1187
|
-
if (!
|
|
1210
|
+
const jsonSchema = pluginInstance.getOutputJsonSchema(manifestAction.key, capability.formValue || {});
|
|
1211
|
+
if (!jsonSchema) {
|
|
1188
1212
|
throw new Error(`Failed to get output schema for action: ${manifestAction.key}`);
|
|
1189
1213
|
}
|
|
1190
|
-
outputSchema =
|
|
1214
|
+
outputSchema = jsonSchema;
|
|
1191
1215
|
} else {
|
|
1192
1216
|
outputSchema = manifestAction.outputSchema;
|
|
1193
1217
|
}
|
|
@@ -1289,58 +1313,58 @@ var capabilityCommandGroup = {
|
|
|
1289
1313
|
};
|
|
1290
1314
|
|
|
1291
1315
|
// src/commands/migration/version-manager.ts
|
|
1292
|
-
import
|
|
1293
|
-
import
|
|
1316
|
+
import fs7 from "fs";
|
|
1317
|
+
import path7 from "path";
|
|
1294
1318
|
var PACKAGE_JSON = "package.json";
|
|
1295
1319
|
var VERSION_FIELD = "migrationVersion";
|
|
1296
1320
|
function getPackageJsonPath2() {
|
|
1297
|
-
return
|
|
1321
|
+
return path7.join(process.cwd(), PACKAGE_JSON);
|
|
1298
1322
|
}
|
|
1299
1323
|
function getCurrentVersion() {
|
|
1300
1324
|
const pkgPath = getPackageJsonPath2();
|
|
1301
|
-
if (!
|
|
1325
|
+
if (!fs7.existsSync(pkgPath)) {
|
|
1302
1326
|
throw new Error("package.json not found");
|
|
1303
1327
|
}
|
|
1304
|
-
const pkg2 = JSON.parse(
|
|
1328
|
+
const pkg2 = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
|
|
1305
1329
|
return pkg2[VERSION_FIELD] ?? 0;
|
|
1306
1330
|
}
|
|
1307
1331
|
function setCurrentVersion(version) {
|
|
1308
1332
|
const pkgPath = getPackageJsonPath2();
|
|
1309
|
-
const pkg2 = JSON.parse(
|
|
1333
|
+
const pkg2 = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
|
|
1310
1334
|
pkg2[VERSION_FIELD] = version;
|
|
1311
|
-
|
|
1335
|
+
fs7.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
|
|
1312
1336
|
}
|
|
1313
1337
|
|
|
1314
1338
|
// src/commands/migration/versions/v001_capability/json-migrator/detector.ts
|
|
1315
|
-
import
|
|
1316
|
-
import
|
|
1339
|
+
import fs9 from "fs";
|
|
1340
|
+
import path9 from "path";
|
|
1317
1341
|
|
|
1318
1342
|
// src/commands/migration/versions/v001_capability/utils.ts
|
|
1319
|
-
import
|
|
1320
|
-
import
|
|
1343
|
+
import fs8 from "fs";
|
|
1344
|
+
import path8 from "path";
|
|
1321
1345
|
var CAPABILITIES_DIR2 = "server/capabilities";
|
|
1322
1346
|
function getProjectRoot3() {
|
|
1323
1347
|
return process.cwd();
|
|
1324
1348
|
}
|
|
1325
1349
|
function getCapabilitiesDir2() {
|
|
1326
|
-
return
|
|
1350
|
+
return path8.join(getProjectRoot3(), CAPABILITIES_DIR2);
|
|
1327
1351
|
}
|
|
1328
1352
|
function getPluginManifestPath2(pluginKey) {
|
|
1329
|
-
return
|
|
1353
|
+
return path8.join(getProjectRoot3(), "node_modules", pluginKey, "manifest.json");
|
|
1330
1354
|
}
|
|
1331
1355
|
|
|
1332
1356
|
// src/commands/migration/versions/v001_capability/json-migrator/detector.ts
|
|
1333
1357
|
function detectJsonMigration() {
|
|
1334
1358
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
1335
|
-
const oldFilePath =
|
|
1336
|
-
if (!
|
|
1359
|
+
const oldFilePath = path9.join(capabilitiesDir, "capabilities.json");
|
|
1360
|
+
if (!fs9.existsSync(oldFilePath)) {
|
|
1337
1361
|
return {
|
|
1338
1362
|
needsMigration: false,
|
|
1339
1363
|
reason: "capabilities.json not found"
|
|
1340
1364
|
};
|
|
1341
1365
|
}
|
|
1342
1366
|
try {
|
|
1343
|
-
const content =
|
|
1367
|
+
const content = fs9.readFileSync(oldFilePath, "utf-8");
|
|
1344
1368
|
const parsed = JSON.parse(content);
|
|
1345
1369
|
const capabilities = Array.isArray(parsed) ? parsed : [];
|
|
1346
1370
|
return {
|
|
@@ -1349,10 +1373,12 @@ function detectJsonMigration() {
|
|
|
1349
1373
|
oldFilePath
|
|
1350
1374
|
};
|
|
1351
1375
|
} catch (error) {
|
|
1376
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1377
|
+
console.warn(`[migration] Warning: capabilities.json is not valid JSON, skipping migration`);
|
|
1378
|
+
console.warn(`[migration] Error: ${errorMessage}`);
|
|
1352
1379
|
return {
|
|
1353
|
-
needsMigration:
|
|
1354
|
-
|
|
1355
|
-
oldFilePath
|
|
1380
|
+
needsMigration: false,
|
|
1381
|
+
reason: `capabilities.json is not valid JSON: ${errorMessage}`
|
|
1356
1382
|
};
|
|
1357
1383
|
}
|
|
1358
1384
|
}
|
|
@@ -1378,8 +1404,8 @@ async function check(options) {
|
|
|
1378
1404
|
}
|
|
1379
1405
|
|
|
1380
1406
|
// src/commands/migration/versions/v001_capability/json-migrator/index.ts
|
|
1381
|
-
import
|
|
1382
|
-
import
|
|
1407
|
+
import fs10 from "fs";
|
|
1408
|
+
import path10 from "path";
|
|
1383
1409
|
|
|
1384
1410
|
// src/commands/migration/versions/v001_capability/mapping.ts
|
|
1385
1411
|
var DEFAULT_PLUGIN_VERSION = "1.0.0";
|
|
@@ -1494,7 +1520,7 @@ var DEFAULT_TEMPERATURE = 0.7;
|
|
|
1494
1520
|
function transformAITextGenerate(input) {
|
|
1495
1521
|
const actionInput = input;
|
|
1496
1522
|
return {
|
|
1497
|
-
modelID:
|
|
1523
|
+
modelID: DEFAULT_MODEL_ID,
|
|
1498
1524
|
prompt: actionInput.prompt ?? "",
|
|
1499
1525
|
modelParams: {
|
|
1500
1526
|
maxTokens: convertToNumber(actionInput.max_tokens, DEFAULT_MAX_TOKENS),
|
|
@@ -1512,7 +1538,7 @@ function transformAIImageUnderstanding(input) {
|
|
|
1512
1538
|
return {
|
|
1513
1539
|
prompt: actionInput.prompt ?? "",
|
|
1514
1540
|
images: actionInput.image_list ?? [],
|
|
1515
|
-
modelID:
|
|
1541
|
+
modelID: DEFAULT_MODEL_ID2,
|
|
1516
1542
|
modelParams: {
|
|
1517
1543
|
maxTokens: convertToNumber(actionInput.max_tokens, DEFAULT_MAX_TOKENS2),
|
|
1518
1544
|
temperature: convertToNumber(actionInput.temperature, DEFAULT_TEMPERATURE2)
|
|
@@ -1573,18 +1599,18 @@ function transformCapabilities(oldCapabilities) {
|
|
|
1573
1599
|
// src/commands/migration/versions/v001_capability/json-migrator/index.ts
|
|
1574
1600
|
function loadExistingCapabilities() {
|
|
1575
1601
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
1576
|
-
if (!
|
|
1602
|
+
if (!fs10.existsSync(capabilitiesDir)) {
|
|
1577
1603
|
return [];
|
|
1578
1604
|
}
|
|
1579
|
-
const files =
|
|
1605
|
+
const files = fs10.readdirSync(capabilitiesDir);
|
|
1580
1606
|
const capabilities = [];
|
|
1581
1607
|
for (const file of files) {
|
|
1582
1608
|
if (file === "capabilities.json" || !file.endsWith(".json")) {
|
|
1583
1609
|
continue;
|
|
1584
1610
|
}
|
|
1585
1611
|
try {
|
|
1586
|
-
const filePath =
|
|
1587
|
-
const content =
|
|
1612
|
+
const filePath = path10.join(capabilitiesDir, file);
|
|
1613
|
+
const content = fs10.readFileSync(filePath, "utf-8");
|
|
1588
1614
|
const capability = JSON.parse(content);
|
|
1589
1615
|
if (capability.id && capability.pluginKey) {
|
|
1590
1616
|
capabilities.push(capability);
|
|
@@ -1642,9 +1668,9 @@ async function migrateJsonFiles(options) {
|
|
|
1642
1668
|
}
|
|
1643
1669
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
1644
1670
|
for (const cap of newCapabilities) {
|
|
1645
|
-
const filePath =
|
|
1671
|
+
const filePath = path10.join(capabilitiesDir, `${cap.id}.json`);
|
|
1646
1672
|
const content = JSON.stringify(cap, null, 2);
|
|
1647
|
-
|
|
1673
|
+
fs10.writeFileSync(filePath, content, "utf-8");
|
|
1648
1674
|
console.log(` \u2713 Created: ${cap.id}.json`);
|
|
1649
1675
|
}
|
|
1650
1676
|
return {
|
|
@@ -1656,10 +1682,10 @@ async function migrateJsonFiles(options) {
|
|
|
1656
1682
|
}
|
|
1657
1683
|
|
|
1658
1684
|
// src/commands/migration/versions/v001_capability/plugin-installer/detector.ts
|
|
1659
|
-
import
|
|
1685
|
+
import fs11 from "fs";
|
|
1660
1686
|
function isPluginInstalled2(pluginKey) {
|
|
1661
1687
|
const manifestPath = getPluginManifestPath2(pluginKey);
|
|
1662
|
-
return
|
|
1688
|
+
return fs11.existsSync(manifestPath);
|
|
1663
1689
|
}
|
|
1664
1690
|
function detectPluginsToInstall(capabilities) {
|
|
1665
1691
|
const pluginKeys = /* @__PURE__ */ new Set();
|
|
@@ -1735,12 +1761,12 @@ async function installPlugins(capabilities, options) {
|
|
|
1735
1761
|
}
|
|
1736
1762
|
|
|
1737
1763
|
// src/commands/migration/versions/v001_capability/code-migrator/index.ts
|
|
1738
|
-
import
|
|
1764
|
+
import path12 from "path";
|
|
1739
1765
|
import { Project } from "ts-morph";
|
|
1740
1766
|
|
|
1741
1767
|
// src/commands/migration/versions/v001_capability/code-migrator/scanner.ts
|
|
1742
|
-
import
|
|
1743
|
-
import
|
|
1768
|
+
import fs12 from "fs";
|
|
1769
|
+
import path11 from "path";
|
|
1744
1770
|
var EXCLUDED_DIRS = [
|
|
1745
1771
|
"node_modules",
|
|
1746
1772
|
"dist",
|
|
@@ -1755,9 +1781,9 @@ var EXCLUDED_PATTERNS = [
|
|
|
1755
1781
|
/\.d\.ts$/
|
|
1756
1782
|
];
|
|
1757
1783
|
function scanDirectory(dir, files = []) {
|
|
1758
|
-
const entries =
|
|
1784
|
+
const entries = fs12.readdirSync(dir, { withFileTypes: true });
|
|
1759
1785
|
for (const entry of entries) {
|
|
1760
|
-
const fullPath =
|
|
1786
|
+
const fullPath = path11.join(dir, entry.name);
|
|
1761
1787
|
if (entry.isDirectory()) {
|
|
1762
1788
|
if (EXCLUDED_DIRS.includes(entry.name)) {
|
|
1763
1789
|
continue;
|
|
@@ -1773,14 +1799,14 @@ function scanDirectory(dir, files = []) {
|
|
|
1773
1799
|
return files;
|
|
1774
1800
|
}
|
|
1775
1801
|
function scanServerFiles() {
|
|
1776
|
-
const serverDir =
|
|
1777
|
-
if (!
|
|
1802
|
+
const serverDir = path11.join(getProjectRoot3(), "server");
|
|
1803
|
+
if (!fs12.existsSync(serverDir)) {
|
|
1778
1804
|
return [];
|
|
1779
1805
|
}
|
|
1780
1806
|
return scanDirectory(serverDir);
|
|
1781
1807
|
}
|
|
1782
1808
|
function hasCapabilityImport(filePath) {
|
|
1783
|
-
const content =
|
|
1809
|
+
const content = fs12.readFileSync(filePath, "utf-8");
|
|
1784
1810
|
return /import\s+.*from\s+['"][^'"]*capabilities[^'"]*['"]/.test(content);
|
|
1785
1811
|
}
|
|
1786
1812
|
function scanFilesToMigrate() {
|
|
@@ -2155,7 +2181,7 @@ function analyzeFile(project, filePath, actionNameMap) {
|
|
|
2155
2181
|
const callSites = analyzeCallSites(sourceFile, imports);
|
|
2156
2182
|
const classInfo = analyzeClass(sourceFile);
|
|
2157
2183
|
const { canMigrate, reason } = canAutoMigrate(classInfo);
|
|
2158
|
-
const relativePath =
|
|
2184
|
+
const relativePath = path12.relative(getProjectRoot3(), filePath);
|
|
2159
2185
|
return {
|
|
2160
2186
|
filePath: relativePath,
|
|
2161
2187
|
imports,
|
|
@@ -2166,7 +2192,7 @@ function analyzeFile(project, filePath, actionNameMap) {
|
|
|
2166
2192
|
};
|
|
2167
2193
|
}
|
|
2168
2194
|
function migrateFile(project, analysis, dryRun) {
|
|
2169
|
-
const absolutePath =
|
|
2195
|
+
const absolutePath = path12.join(getProjectRoot3(), analysis.filePath);
|
|
2170
2196
|
if (!analysis.canAutoMigrate) {
|
|
2171
2197
|
return {
|
|
2172
2198
|
filePath: analysis.filePath,
|
|
@@ -2269,17 +2295,17 @@ function getSuggestion(analysis) {
|
|
|
2269
2295
|
}
|
|
2270
2296
|
|
|
2271
2297
|
// src/commands/migration/versions/v001_capability/cleanup.ts
|
|
2272
|
-
import
|
|
2273
|
-
import
|
|
2298
|
+
import fs13 from "fs";
|
|
2299
|
+
import path13 from "path";
|
|
2274
2300
|
function cleanupOldFiles(capabilities, dryRun) {
|
|
2275
2301
|
const deletedFiles = [];
|
|
2276
2302
|
const errors = [];
|
|
2277
2303
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
2278
|
-
const oldJsonPath =
|
|
2279
|
-
if (
|
|
2304
|
+
const oldJsonPath = path13.join(capabilitiesDir, "capabilities.json");
|
|
2305
|
+
if (fs13.existsSync(oldJsonPath)) {
|
|
2280
2306
|
try {
|
|
2281
2307
|
if (!dryRun) {
|
|
2282
|
-
|
|
2308
|
+
fs13.unlinkSync(oldJsonPath);
|
|
2283
2309
|
}
|
|
2284
2310
|
deletedFiles.push("capabilities.json");
|
|
2285
2311
|
} catch (error) {
|
|
@@ -2287,11 +2313,11 @@ function cleanupOldFiles(capabilities, dryRun) {
|
|
|
2287
2313
|
}
|
|
2288
2314
|
}
|
|
2289
2315
|
for (const cap of capabilities) {
|
|
2290
|
-
const tsFilePath =
|
|
2291
|
-
if (
|
|
2316
|
+
const tsFilePath = path13.join(capabilitiesDir, `${cap.id}.ts`);
|
|
2317
|
+
if (fs13.existsSync(tsFilePath)) {
|
|
2292
2318
|
try {
|
|
2293
2319
|
if (!dryRun) {
|
|
2294
|
-
|
|
2320
|
+
fs13.unlinkSync(tsFilePath);
|
|
2295
2321
|
}
|
|
2296
2322
|
deletedFiles.push(`${cap.id}.ts`);
|
|
2297
2323
|
} catch (error) {
|
|
@@ -2307,8 +2333,8 @@ function cleanupOldFiles(capabilities, dryRun) {
|
|
|
2307
2333
|
}
|
|
2308
2334
|
|
|
2309
2335
|
// src/commands/migration/versions/v001_capability/report-generator.ts
|
|
2310
|
-
import
|
|
2311
|
-
import
|
|
2336
|
+
import fs14 from "fs";
|
|
2337
|
+
import path14 from "path";
|
|
2312
2338
|
var REPORT_FILE = "capability-migration-report.md";
|
|
2313
2339
|
function printSummary(result) {
|
|
2314
2340
|
const { jsonMigration, pluginInstallation, codeMigration, cleanup } = result;
|
|
@@ -2470,8 +2496,8 @@ async function generateReport(result) {
|
|
|
2470
2496
|
lines.push("- [ ] \u5904\u7406\u624B\u52A8\u8FC1\u79FB\u9879");
|
|
2471
2497
|
}
|
|
2472
2498
|
lines.push("");
|
|
2473
|
-
const reportPath =
|
|
2474
|
-
|
|
2499
|
+
const reportPath = path14.join(getProjectRoot3(), REPORT_FILE);
|
|
2500
|
+
fs14.writeFileSync(reportPath, lines.join("\n"), "utf-8");
|
|
2475
2501
|
console.log(`\u{1F4C4} Report generated: ${REPORT_FILE}`);
|
|
2476
2502
|
}
|
|
2477
2503
|
|
|
@@ -2871,6 +2897,10 @@ async function runMigrations(options) {
|
|
|
2871
2897
|
printSummary2(summary, !!options.dryRun);
|
|
2872
2898
|
return summary;
|
|
2873
2899
|
}
|
|
2900
|
+
function printMigrationResult(result) {
|
|
2901
|
+
console.log("");
|
|
2902
|
+
console.log(`MigrationResult=${JSON.stringify(result)}`);
|
|
2903
|
+
}
|
|
2874
2904
|
async function checkMigrations(options) {
|
|
2875
2905
|
const currentVersion = getCurrentVersion();
|
|
2876
2906
|
const latestVersion = getLatestVersion();
|
|
@@ -2878,6 +2908,12 @@ async function checkMigrations(options) {
|
|
|
2878
2908
|
log(`Current version: ${currentVersion || "0 (not set)"}`);
|
|
2879
2909
|
if (currentVersion >= latestVersion) {
|
|
2880
2910
|
log("All migrations are up to date.");
|
|
2911
|
+
printMigrationResult({
|
|
2912
|
+
needMigration: false,
|
|
2913
|
+
migrationVersion: currentVersion,
|
|
2914
|
+
targetVersion: latestVersion,
|
|
2915
|
+
pendingMigrations: []
|
|
2916
|
+
});
|
|
2881
2917
|
return {
|
|
2882
2918
|
needsMigration: false,
|
|
2883
2919
|
completed: [],
|
|
@@ -2896,13 +2932,16 @@ async function checkMigrations(options) {
|
|
|
2896
2932
|
};
|
|
2897
2933
|
let needsCount = 0;
|
|
2898
2934
|
let noActionCount = 0;
|
|
2935
|
+
const pendingMigrationNames = [];
|
|
2899
2936
|
for (const migration of pendingMigrations) {
|
|
2900
2937
|
const { version, name } = migration;
|
|
2901
2938
|
const versionTag = `v${version}`;
|
|
2939
|
+
const versionName = `v${String(version).padStart(3, "0")}_${name}`;
|
|
2902
2940
|
try {
|
|
2903
2941
|
const checkResult = await migration.check(options);
|
|
2904
2942
|
if (checkResult.needsMigration) {
|
|
2905
2943
|
needsCount++;
|
|
2944
|
+
pendingMigrationNames.push(versionName);
|
|
2906
2945
|
console.log(` ${versionTag} (${name}): needs migration`);
|
|
2907
2946
|
if (checkResult.items && checkResult.items.length > 0) {
|
|
2908
2947
|
for (const item of checkResult.items) {
|
|
@@ -2949,11 +2988,19 @@ async function checkMigrations(options) {
|
|
|
2949
2988
|
log("Run 'fullstack-cli migration' to apply.");
|
|
2950
2989
|
}
|
|
2951
2990
|
summary.needsMigration = needsCount > 0;
|
|
2991
|
+
let finalVersion = currentVersion;
|
|
2952
2992
|
if (needsCount === 0 && summary.failed.length === 0) {
|
|
2953
2993
|
setCurrentVersion(latestVersion);
|
|
2994
|
+
finalVersion = latestVersion;
|
|
2954
2995
|
console.log("");
|
|
2955
2996
|
log(`Updated migrationVersion to ${latestVersion}.`);
|
|
2956
2997
|
}
|
|
2998
|
+
printMigrationResult({
|
|
2999
|
+
needMigration: needsCount > 0,
|
|
3000
|
+
migrationVersion: finalVersion,
|
|
3001
|
+
targetVersion: latestVersion,
|
|
3002
|
+
pendingMigrations: pendingMigrationNames
|
|
3003
|
+
});
|
|
2957
3004
|
return summary;
|
|
2958
3005
|
}
|
|
2959
3006
|
|
|
@@ -2971,30 +3018,865 @@ var migrationCommand = {
|
|
|
2971
3018
|
migrationCmd.command("check").description("Check pending migrations without applying them").action(async () => {
|
|
2972
3019
|
const summary = await checkMigrations({});
|
|
2973
3020
|
if (summary.failed.length > 0) {
|
|
2974
|
-
process.exit(2);
|
|
2975
|
-
} else if (summary.needsMigration) {
|
|
2976
3021
|
process.exit(1);
|
|
2977
3022
|
}
|
|
2978
3023
|
});
|
|
2979
3024
|
}
|
|
2980
3025
|
};
|
|
2981
3026
|
|
|
3027
|
+
// src/commands/read-logs/index.ts
|
|
3028
|
+
import path15 from "path";
|
|
3029
|
+
|
|
3030
|
+
// src/commands/read-logs/std-utils.ts
|
|
3031
|
+
import fs15 from "fs";
|
|
3032
|
+
function formatStdPrefixTime(localTime) {
|
|
3033
|
+
const match = localTime.match(/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/);
|
|
3034
|
+
if (!match) return localTime;
|
|
3035
|
+
const year = Number(match[1]);
|
|
3036
|
+
const month = Number(match[2]);
|
|
3037
|
+
const day = Number(match[3]);
|
|
3038
|
+
const hour = Number(match[4]);
|
|
3039
|
+
const minute = Number(match[5]);
|
|
3040
|
+
const second = Number(match[6]);
|
|
3041
|
+
const date = new Date(year, month - 1, day, hour, minute, second, 0);
|
|
3042
|
+
if (Number.isNaN(date.getTime())) return localTime;
|
|
3043
|
+
const pad2 = (n) => String(n).padStart(2, "0");
|
|
3044
|
+
const tzOffsetMinutes = -date.getTimezoneOffset();
|
|
3045
|
+
const sign = tzOffsetMinutes >= 0 ? "+" : "-";
|
|
3046
|
+
const abs = Math.abs(tzOffsetMinutes);
|
|
3047
|
+
const offsetHH = pad2(Math.floor(abs / 60));
|
|
3048
|
+
const offsetMM = pad2(abs % 60);
|
|
3049
|
+
return `${localTime.replace(" ", "T")}${sign}${offsetHH}:${offsetMM}`;
|
|
3050
|
+
}
|
|
3051
|
+
function stripPrefixFromStdLine(line) {
|
|
3052
|
+
const match = line.match(/^(\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] \[(server|client)\] )(.*)$/);
|
|
3053
|
+
if (!match) {
|
|
3054
|
+
return line;
|
|
3055
|
+
}
|
|
3056
|
+
const time = formatStdPrefixTime(match[2] || "");
|
|
3057
|
+
const content = match[4] || "";
|
|
3058
|
+
return `[${time}] ${content}`;
|
|
3059
|
+
}
|
|
3060
|
+
function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarker) {
|
|
3061
|
+
const stat = fs15.statSync(filePath);
|
|
3062
|
+
if (stat.size === 0) {
|
|
3063
|
+
return { lines: [], markerFound: false, totalLinesCount: 0 };
|
|
3064
|
+
}
|
|
3065
|
+
const fd = fs15.openSync(filePath, "r");
|
|
3066
|
+
const chunkSize = 64 * 1024;
|
|
3067
|
+
let position = stat.size;
|
|
3068
|
+
let remainder = "";
|
|
3069
|
+
let markerFound = false;
|
|
3070
|
+
let finished = false;
|
|
3071
|
+
let totalLinesCount = 0;
|
|
3072
|
+
let skipped = 0;
|
|
3073
|
+
const collected = [];
|
|
3074
|
+
try {
|
|
3075
|
+
while (position > 0 && !finished) {
|
|
3076
|
+
const length = Math.min(chunkSize, position);
|
|
3077
|
+
position -= length;
|
|
3078
|
+
const buffer = Buffer.alloc(length);
|
|
3079
|
+
fs15.readSync(fd, buffer, 0, length, position);
|
|
3080
|
+
let chunk = buffer.toString("utf8");
|
|
3081
|
+
if (remainder) {
|
|
3082
|
+
chunk += remainder;
|
|
3083
|
+
remainder = "";
|
|
3084
|
+
}
|
|
3085
|
+
const parts = chunk.split("\n");
|
|
3086
|
+
remainder = parts.shift() ?? "";
|
|
3087
|
+
for (let i = parts.length - 1; i >= 0; i -= 1) {
|
|
3088
|
+
const rawLine = parts[i];
|
|
3089
|
+
const line = stripPrefixFromStdLine(rawLine);
|
|
3090
|
+
if (totalLinesCount === 0 && line === "") {
|
|
3091
|
+
continue;
|
|
3092
|
+
}
|
|
3093
|
+
totalLinesCount += 1;
|
|
3094
|
+
if (skipped < offset) {
|
|
3095
|
+
skipped += 1;
|
|
3096
|
+
} else if (collected.length < maxLines) {
|
|
3097
|
+
collected.push(line);
|
|
3098
|
+
}
|
|
3099
|
+
if (isMarker(line)) {
|
|
3100
|
+
markerFound = true;
|
|
3101
|
+
finished = true;
|
|
3102
|
+
break;
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
}
|
|
3106
|
+
if (!finished && remainder) {
|
|
3107
|
+
const line = stripPrefixFromStdLine(remainder);
|
|
3108
|
+
if (!(totalLinesCount === 0 && line === "")) {
|
|
3109
|
+
totalLinesCount += 1;
|
|
3110
|
+
if (skipped < offset) {
|
|
3111
|
+
skipped += 1;
|
|
3112
|
+
} else if (collected.length < maxLines) {
|
|
3113
|
+
collected.push(line);
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
if (isMarker(line)) {
|
|
3117
|
+
markerFound = true;
|
|
3118
|
+
}
|
|
3119
|
+
}
|
|
3120
|
+
} finally {
|
|
3121
|
+
fs15.closeSync(fd);
|
|
3122
|
+
}
|
|
3123
|
+
return { lines: collected.reverse(), markerFound, totalLinesCount };
|
|
3124
|
+
}
|
|
3125
|
+
|
|
3126
|
+
// src/commands/read-logs/server-std.ts
|
|
3127
|
+
function readServerStdSegment(filePath, maxLines, offset) {
|
|
3128
|
+
const marker = (line) => {
|
|
3129
|
+
if (!line) return false;
|
|
3130
|
+
if (/\bdev:server\b/.test(line)) return true;
|
|
3131
|
+
if (line.includes("Starting compilation in watch mode")) return true;
|
|
3132
|
+
if (line.includes("File change detected. Starting incremental compilation")) return true;
|
|
3133
|
+
if (line.includes("Starting Nest application")) return true;
|
|
3134
|
+
if (line.includes("Nest application successfully started")) return true;
|
|
3135
|
+
return false;
|
|
3136
|
+
};
|
|
3137
|
+
const segment = readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, marker);
|
|
3138
|
+
return { lines: segment.lines, totalLinesCount: segment.totalLinesCount };
|
|
3139
|
+
}
|
|
3140
|
+
|
|
3141
|
+
// src/commands/read-logs/tail.ts
|
|
3142
|
+
import fs16 from "fs";
|
|
3143
|
+
function fileExists(filePath) {
|
|
3144
|
+
try {
|
|
3145
|
+
fs16.accessSync(filePath, fs16.constants.F_OK | fs16.constants.R_OK);
|
|
3146
|
+
return true;
|
|
3147
|
+
} catch {
|
|
3148
|
+
return false;
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
function readFileTailLines(filePath, maxLines) {
|
|
3152
|
+
const stat = fs16.statSync(filePath);
|
|
3153
|
+
if (stat.size === 0) {
|
|
3154
|
+
return [];
|
|
3155
|
+
}
|
|
3156
|
+
const fd = fs16.openSync(filePath, "r");
|
|
3157
|
+
const chunkSize = 64 * 1024;
|
|
3158
|
+
const chunks = [];
|
|
3159
|
+
let position = stat.size;
|
|
3160
|
+
let collectedLines = 0;
|
|
3161
|
+
try {
|
|
3162
|
+
while (position > 0 && collectedLines <= maxLines) {
|
|
3163
|
+
const length = Math.min(chunkSize, position);
|
|
3164
|
+
position -= length;
|
|
3165
|
+
const buffer = Buffer.alloc(length);
|
|
3166
|
+
fs16.readSync(fd, buffer, 0, length, position);
|
|
3167
|
+
chunks.unshift(buffer.toString("utf8"));
|
|
3168
|
+
const chunkLines = buffer.toString("utf8").split("\n").length - 1;
|
|
3169
|
+
collectedLines += chunkLines;
|
|
3170
|
+
}
|
|
3171
|
+
} finally {
|
|
3172
|
+
fs16.closeSync(fd);
|
|
3173
|
+
}
|
|
3174
|
+
const content = chunks.join("");
|
|
3175
|
+
const allLines = content.split("\n");
|
|
3176
|
+
if (allLines.length === 0) {
|
|
3177
|
+
return [];
|
|
3178
|
+
}
|
|
3179
|
+
if (allLines[allLines.length - 1] === "") {
|
|
3180
|
+
allLines.pop();
|
|
3181
|
+
}
|
|
3182
|
+
if (allLines.length <= maxLines) {
|
|
3183
|
+
return allLines;
|
|
3184
|
+
}
|
|
3185
|
+
return allLines.slice(allLines.length - maxLines);
|
|
3186
|
+
}
|
|
3187
|
+
function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
|
|
3188
|
+
const stat = fs16.statSync(filePath);
|
|
3189
|
+
if (stat.size === 0) {
|
|
3190
|
+
return { lines: [], totalLinesCount: 0 };
|
|
3191
|
+
}
|
|
3192
|
+
const fd = fs16.openSync(filePath, "r");
|
|
3193
|
+
const chunkSize = 64 * 1024;
|
|
3194
|
+
let position = stat.size;
|
|
3195
|
+
let remainder = "";
|
|
3196
|
+
let totalLinesCount = 0;
|
|
3197
|
+
let skipped = 0;
|
|
3198
|
+
const collected = [];
|
|
3199
|
+
try {
|
|
3200
|
+
while (position > 0) {
|
|
3201
|
+
const length = Math.min(chunkSize, position);
|
|
3202
|
+
position -= length;
|
|
3203
|
+
const buffer = Buffer.alloc(length);
|
|
3204
|
+
fs16.readSync(fd, buffer, 0, length, position);
|
|
3205
|
+
let chunk = buffer.toString("utf8");
|
|
3206
|
+
if (remainder) {
|
|
3207
|
+
chunk += remainder;
|
|
3208
|
+
remainder = "";
|
|
3209
|
+
}
|
|
3210
|
+
const parts = chunk.split("\n");
|
|
3211
|
+
remainder = parts.shift() ?? "";
|
|
3212
|
+
for (let i = parts.length - 1; i >= 0; i -= 1) {
|
|
3213
|
+
const line = parts[i].trim();
|
|
3214
|
+
if (!line) continue;
|
|
3215
|
+
totalLinesCount += 1;
|
|
3216
|
+
if (skipped < offset) {
|
|
3217
|
+
skipped += 1;
|
|
3218
|
+
} else if (collected.length < maxLines) {
|
|
3219
|
+
collected.push(line);
|
|
3220
|
+
}
|
|
3221
|
+
}
|
|
3222
|
+
}
|
|
3223
|
+
if (remainder) {
|
|
3224
|
+
const line = remainder.trim();
|
|
3225
|
+
if (line) {
|
|
3226
|
+
totalLinesCount += 1;
|
|
3227
|
+
if (skipped < offset) {
|
|
3228
|
+
skipped += 1;
|
|
3229
|
+
} else if (collected.length < maxLines) {
|
|
3230
|
+
collected.push(line);
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3233
|
+
}
|
|
3234
|
+
} finally {
|
|
3235
|
+
fs16.closeSync(fd);
|
|
3236
|
+
}
|
|
3237
|
+
return { lines: collected.reverse(), totalLinesCount };
|
|
3238
|
+
}
|
|
3239
|
+
|
|
3240
|
+
// src/commands/read-logs/client-std.ts
|
|
3241
|
+
function readClientStdSegment(filePath, maxLines, offset) {
|
|
3242
|
+
const lines = readFileTailLines(filePath, Math.max((maxLines + offset) * 5, 2e3));
|
|
3243
|
+
return extractClientStdSegment(lines, maxLines, offset);
|
|
3244
|
+
}
|
|
3245
|
+
function extractClientStdSegment(lines, maxLines, offset) {
|
|
3246
|
+
const bodyLines = lines.map(stripPrefixFromStdLine);
|
|
3247
|
+
const hotStartMarkers = [
|
|
3248
|
+
/file change detected\..*incremental compilation/i,
|
|
3249
|
+
/starting incremental compilation/i,
|
|
3250
|
+
/starting compilation/i,
|
|
3251
|
+
/\bcompiling\b/i,
|
|
3252
|
+
/\brecompil/i
|
|
3253
|
+
];
|
|
3254
|
+
const hotEndMarkers = [
|
|
3255
|
+
/file change detected\..*incremental compilation/i,
|
|
3256
|
+
/\bwebpack compiled\b/i,
|
|
3257
|
+
/compiled successfully/i,
|
|
3258
|
+
/compiled with warnings/i,
|
|
3259
|
+
/compiled with errors/i,
|
|
3260
|
+
/failed to compile/i,
|
|
3261
|
+
/fast refresh/i,
|
|
3262
|
+
/\bhmr\b/i,
|
|
3263
|
+
/hot update/i,
|
|
3264
|
+
/\bhot reload\b/i,
|
|
3265
|
+
/\bhmr update\b/i
|
|
3266
|
+
];
|
|
3267
|
+
const compiledMarkers = [
|
|
3268
|
+
/\bwebpack compiled\b/i,
|
|
3269
|
+
/compiled successfully/i,
|
|
3270
|
+
/compiled with warnings/i,
|
|
3271
|
+
/compiled with errors/i,
|
|
3272
|
+
/failed to compile/i
|
|
3273
|
+
];
|
|
3274
|
+
let startIndex = -1;
|
|
3275
|
+
for (let i = bodyLines.length - 1; i >= 0; i -= 1) {
|
|
3276
|
+
const line = bodyLines[i];
|
|
3277
|
+
if (!line) continue;
|
|
3278
|
+
if (hotStartMarkers.some((re) => re.test(line))) {
|
|
3279
|
+
startIndex = i;
|
|
3280
|
+
break;
|
|
3281
|
+
}
|
|
3282
|
+
}
|
|
3283
|
+
if (startIndex === -1) {
|
|
3284
|
+
let pivotIndex = -1;
|
|
3285
|
+
for (let i = bodyLines.length - 1; i >= 0; i -= 1) {
|
|
3286
|
+
const line = bodyLines[i];
|
|
3287
|
+
if (!line) continue;
|
|
3288
|
+
if (hotEndMarkers.some((re) => re.test(line))) {
|
|
3289
|
+
pivotIndex = i;
|
|
3290
|
+
break;
|
|
3291
|
+
}
|
|
3292
|
+
}
|
|
3293
|
+
if (pivotIndex !== -1) {
|
|
3294
|
+
if (compiledMarkers.some((re) => re.test(bodyLines[pivotIndex] ?? ""))) {
|
|
3295
|
+
startIndex = pivotIndex;
|
|
3296
|
+
} else {
|
|
3297
|
+
const searchLimit = 80;
|
|
3298
|
+
const from = Math.max(0, pivotIndex - searchLimit);
|
|
3299
|
+
for (let i = pivotIndex; i >= from; i -= 1) {
|
|
3300
|
+
const line = bodyLines[i];
|
|
3301
|
+
if (!line) continue;
|
|
3302
|
+
if (compiledMarkers.some((re) => re.test(line))) {
|
|
3303
|
+
startIndex = i;
|
|
3304
|
+
break;
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
if (startIndex === -1) {
|
|
3308
|
+
startIndex = pivotIndex;
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
}
|
|
3312
|
+
}
|
|
3313
|
+
if (startIndex === -1) {
|
|
3314
|
+
for (let i = bodyLines.length - 1; i >= 0; i -= 1) {
|
|
3315
|
+
const line = bodyLines[i];
|
|
3316
|
+
if (!line) continue;
|
|
3317
|
+
if (/\bdev:client\b/.test(line)) {
|
|
3318
|
+
startIndex = i;
|
|
3319
|
+
break;
|
|
3320
|
+
}
|
|
3321
|
+
}
|
|
3322
|
+
}
|
|
3323
|
+
const segment = startIndex === -1 ? bodyLines : bodyLines.slice(startIndex);
|
|
3324
|
+
if (segment.length === 0) {
|
|
3325
|
+
return { lines: [], totalLinesCount: 0 };
|
|
3326
|
+
}
|
|
3327
|
+
const totalLinesCount = segment.length;
|
|
3328
|
+
const endExclusive = Math.max(0, segment.length - offset);
|
|
3329
|
+
const start = Math.max(0, endExclusive - maxLines);
|
|
3330
|
+
return {
|
|
3331
|
+
lines: segment.slice(start, endExclusive),
|
|
3332
|
+
totalLinesCount
|
|
3333
|
+
};
|
|
3334
|
+
}
|
|
3335
|
+
|
|
3336
|
+
// src/commands/read-logs/json-lines.ts
|
|
3337
|
+
import fs17 from "fs";
|
|
3338
|
+
function normalizePid(value) {
|
|
3339
|
+
if (typeof value === "number") {
|
|
3340
|
+
return String(value);
|
|
3341
|
+
}
|
|
3342
|
+
if (typeof value === "string" && value.length > 0) {
|
|
3343
|
+
return value;
|
|
3344
|
+
}
|
|
3345
|
+
return "unknown";
|
|
3346
|
+
}
|
|
3347
|
+
function normalizeLevelName(value) {
|
|
3348
|
+
if (typeof value === "string") {
|
|
3349
|
+
const trimmed = value.trim().toLowerCase();
|
|
3350
|
+
if (!trimmed) return null;
|
|
3351
|
+
if (trimmed === "warning") return "warn";
|
|
3352
|
+
if (trimmed === "err") return "error";
|
|
3353
|
+
const allowed = ["trace", "debug", "info", "warn", "error", "fatal"];
|
|
3354
|
+
return allowed.includes(trimmed) ? trimmed : null;
|
|
3355
|
+
}
|
|
3356
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
3357
|
+
const num = Math.floor(value);
|
|
3358
|
+
if (num <= 10) return "trace";
|
|
3359
|
+
if (num <= 20) return "debug";
|
|
3360
|
+
if (num <= 30) return "info";
|
|
3361
|
+
if (num <= 40) return "warn";
|
|
3362
|
+
if (num <= 50) return "error";
|
|
3363
|
+
return "fatal";
|
|
3364
|
+
}
|
|
3365
|
+
return null;
|
|
3366
|
+
}
|
|
3367
|
+
function extractLevelName(obj) {
|
|
3368
|
+
if (!obj || typeof obj !== "object") return null;
|
|
3369
|
+
const record = obj;
|
|
3370
|
+
const direct = normalizeLevelName(record["level"]);
|
|
3371
|
+
if (direct) return direct;
|
|
3372
|
+
const meta = record["meta"];
|
|
3373
|
+
if (meta && typeof meta === "object") {
|
|
3374
|
+
return normalizeLevelName(meta["level"]);
|
|
3375
|
+
}
|
|
3376
|
+
return null;
|
|
3377
|
+
}
|
|
3378
|
+
function buildWantedLevelSet(levels) {
|
|
3379
|
+
if (!levels || levels.length === 0) return null;
|
|
3380
|
+
const set = /* @__PURE__ */ new Set();
|
|
3381
|
+
for (const raw of levels) {
|
|
3382
|
+
const norm = normalizeLevelName(raw);
|
|
3383
|
+
if (norm) set.add(norm);
|
|
3384
|
+
}
|
|
3385
|
+
return set.size > 0 ? set : null;
|
|
3386
|
+
}
|
|
3387
|
+
function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
|
|
3388
|
+
const stat = fs17.statSync(filePath);
|
|
3389
|
+
if (stat.size === 0) {
|
|
3390
|
+
return { lines: [], totalLinesCount: 0 };
|
|
3391
|
+
}
|
|
3392
|
+
const fd = fs17.openSync(filePath, "r");
|
|
3393
|
+
const chunkSize = 64 * 1024;
|
|
3394
|
+
let position = stat.size;
|
|
3395
|
+
let remainder = "";
|
|
3396
|
+
let targetPid = null;
|
|
3397
|
+
let finished = false;
|
|
3398
|
+
let totalLinesCount = 0;
|
|
3399
|
+
let skipped = 0;
|
|
3400
|
+
const collected = [];
|
|
3401
|
+
const wantedLevelSet = buildWantedLevelSet(levels);
|
|
3402
|
+
try {
|
|
3403
|
+
while (position > 0 && !finished) {
|
|
3404
|
+
const length = Math.min(chunkSize, position);
|
|
3405
|
+
position -= length;
|
|
3406
|
+
const buffer = Buffer.alloc(length);
|
|
3407
|
+
fs17.readSync(fd, buffer, 0, length, position);
|
|
3408
|
+
let chunk = buffer.toString("utf8");
|
|
3409
|
+
if (remainder) {
|
|
3410
|
+
chunk += remainder;
|
|
3411
|
+
remainder = "";
|
|
3412
|
+
}
|
|
3413
|
+
const parts = chunk.split("\n");
|
|
3414
|
+
remainder = parts.shift() ?? "";
|
|
3415
|
+
for (let i = parts.length - 1; i >= 0; i -= 1) {
|
|
3416
|
+
const line = parts[i].trim();
|
|
3417
|
+
if (!line) continue;
|
|
3418
|
+
let parsed = null;
|
|
3419
|
+
try {
|
|
3420
|
+
parsed = JSON.parse(line);
|
|
3421
|
+
} catch {
|
|
3422
|
+
continue;
|
|
3423
|
+
}
|
|
3424
|
+
const pid = normalizePid(parsed?.pid);
|
|
3425
|
+
if (targetPid === null) {
|
|
3426
|
+
targetPid = pid;
|
|
3427
|
+
}
|
|
3428
|
+
if (pid !== targetPid) {
|
|
3429
|
+
finished = true;
|
|
3430
|
+
break;
|
|
3431
|
+
}
|
|
3432
|
+
const levelName = extractLevelName(parsed);
|
|
3433
|
+
if (!wantedLevelSet || levelName && wantedLevelSet.has(levelName)) {
|
|
3434
|
+
totalLinesCount += 1;
|
|
3435
|
+
if (skipped < offset) {
|
|
3436
|
+
skipped += 1;
|
|
3437
|
+
} else if (collected.length < maxLines) {
|
|
3438
|
+
collected.push(line);
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
if (!finished && remainder) {
|
|
3444
|
+
const line = remainder.trim();
|
|
3445
|
+
if (line) {
|
|
3446
|
+
try {
|
|
3447
|
+
const parsed = JSON.parse(line);
|
|
3448
|
+
const pid = normalizePid(parsed?.pid);
|
|
3449
|
+
if (targetPid === null) {
|
|
3450
|
+
targetPid = pid;
|
|
3451
|
+
}
|
|
3452
|
+
if (pid === targetPid) {
|
|
3453
|
+
const levelName = extractLevelName(parsed);
|
|
3454
|
+
if (!wantedLevelSet || levelName && wantedLevelSet.has(levelName)) {
|
|
3455
|
+
totalLinesCount += 1;
|
|
3456
|
+
if (skipped < offset) {
|
|
3457
|
+
skipped += 1;
|
|
3458
|
+
} else if (collected.length < maxLines) {
|
|
3459
|
+
collected.push(line);
|
|
3460
|
+
}
|
|
3461
|
+
}
|
|
3462
|
+
}
|
|
3463
|
+
} catch {
|
|
3464
|
+
return { lines: [], totalLinesCount: 0 };
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3467
|
+
}
|
|
3468
|
+
} finally {
|
|
3469
|
+
fs17.closeSync(fd);
|
|
3470
|
+
}
|
|
3471
|
+
return { lines: collected.reverse(), totalLinesCount };
|
|
3472
|
+
}
|
|
3473
|
+
function normalizeTraceId(value) {
|
|
3474
|
+
if (typeof value === "string") {
|
|
3475
|
+
const trimmed = value.trim();
|
|
3476
|
+
return trimmed ? trimmed : null;
|
|
3477
|
+
}
|
|
3478
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
3479
|
+
return String(value);
|
|
3480
|
+
}
|
|
3481
|
+
return null;
|
|
3482
|
+
}
|
|
3483
|
+
function extractTraceId(obj) {
|
|
3484
|
+
if (!obj || typeof obj !== "object") return null;
|
|
3485
|
+
const record = obj;
|
|
3486
|
+
const directKeys = ["trace_id", "traceId", "traceID", "traceid"];
|
|
3487
|
+
for (const key of directKeys) {
|
|
3488
|
+
const value = normalizeTraceId(record[key]);
|
|
3489
|
+
if (value) return value;
|
|
3490
|
+
}
|
|
3491
|
+
const meta = record["meta"];
|
|
3492
|
+
if (meta && typeof meta === "object") {
|
|
3493
|
+
const metaRecord = meta;
|
|
3494
|
+
for (const key of directKeys) {
|
|
3495
|
+
const value = normalizeTraceId(metaRecord[key]);
|
|
3496
|
+
if (value) return value;
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
3499
|
+
const attributes = record["attributes"];
|
|
3500
|
+
if (attributes && typeof attributes === "object") {
|
|
3501
|
+
const attrRecord = attributes;
|
|
3502
|
+
for (const key of ["traceID", "trace_id", "traceId", "traceid"]) {
|
|
3503
|
+
const value = normalizeTraceId(attrRecord[key]);
|
|
3504
|
+
if (value) return value;
|
|
3505
|
+
}
|
|
3506
|
+
}
|
|
3507
|
+
return null;
|
|
3508
|
+
}
|
|
3509
|
+
function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
|
|
3510
|
+
const wanted = traceId.trim();
|
|
3511
|
+
if (!wanted) return { lines: [], totalLinesCount: 0 };
|
|
3512
|
+
const stat = fs17.statSync(filePath);
|
|
3513
|
+
if (stat.size === 0) {
|
|
3514
|
+
return { lines: [], totalLinesCount: 0 };
|
|
3515
|
+
}
|
|
3516
|
+
const fd = fs17.openSync(filePath, "r");
|
|
3517
|
+
const chunkSize = 64 * 1024;
|
|
3518
|
+
let position = stat.size;
|
|
3519
|
+
let remainder = "";
|
|
3520
|
+
let totalLinesCount = 0;
|
|
3521
|
+
let skipped = 0;
|
|
3522
|
+
const collected = [];
|
|
3523
|
+
const wantedLevelSet = buildWantedLevelSet(levels);
|
|
3524
|
+
try {
|
|
3525
|
+
while (position > 0) {
|
|
3526
|
+
const length = Math.min(chunkSize, position);
|
|
3527
|
+
position -= length;
|
|
3528
|
+
const buffer = Buffer.alloc(length);
|
|
3529
|
+
fs17.readSync(fd, buffer, 0, length, position);
|
|
3530
|
+
let chunk = buffer.toString("utf8");
|
|
3531
|
+
if (remainder) {
|
|
3532
|
+
chunk += remainder;
|
|
3533
|
+
remainder = "";
|
|
3534
|
+
}
|
|
3535
|
+
const parts = chunk.split("\n");
|
|
3536
|
+
remainder = parts.shift() ?? "";
|
|
3537
|
+
for (let i = parts.length - 1; i >= 0; i -= 1) {
|
|
3538
|
+
const line = parts[i].trim();
|
|
3539
|
+
if (!line) continue;
|
|
3540
|
+
try {
|
|
3541
|
+
const parsed = JSON.parse(line);
|
|
3542
|
+
const lineTraceId = extractTraceId(parsed);
|
|
3543
|
+
if (lineTraceId === wanted) {
|
|
3544
|
+
const levelName = extractLevelName(parsed);
|
|
3545
|
+
if (!wantedLevelSet || levelName && wantedLevelSet.has(levelName)) {
|
|
3546
|
+
totalLinesCount += 1;
|
|
3547
|
+
if (skipped < offset) {
|
|
3548
|
+
skipped += 1;
|
|
3549
|
+
} else if (collected.length < maxLines) {
|
|
3550
|
+
collected.push(line);
|
|
3551
|
+
}
|
|
3552
|
+
}
|
|
3553
|
+
}
|
|
3554
|
+
} catch {
|
|
3555
|
+
continue;
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3558
|
+
}
|
|
3559
|
+
if (remainder) {
|
|
3560
|
+
const line = remainder.trim();
|
|
3561
|
+
if (line) {
|
|
3562
|
+
try {
|
|
3563
|
+
const parsed = JSON.parse(line);
|
|
3564
|
+
const lineTraceId = extractTraceId(parsed);
|
|
3565
|
+
if (lineTraceId === wanted) {
|
|
3566
|
+
const levelName = extractLevelName(parsed);
|
|
3567
|
+
if (!wantedLevelSet || levelName && wantedLevelSet.has(levelName)) {
|
|
3568
|
+
totalLinesCount += 1;
|
|
3569
|
+
if (skipped < offset) {
|
|
3570
|
+
skipped += 1;
|
|
3571
|
+
} else if (collected.length < maxLines) {
|
|
3572
|
+
collected.push(line);
|
|
3573
|
+
}
|
|
3574
|
+
}
|
|
3575
|
+
}
|
|
3576
|
+
} catch {
|
|
3577
|
+
return { lines: [], totalLinesCount: 0 };
|
|
3578
|
+
}
|
|
3579
|
+
}
|
|
3580
|
+
}
|
|
3581
|
+
} finally {
|
|
3582
|
+
fs17.closeSync(fd);
|
|
3583
|
+
}
|
|
3584
|
+
return { lines: collected.reverse(), totalLinesCount };
|
|
3585
|
+
}
|
|
3586
|
+
function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
|
|
3587
|
+
const wantedLevelSet = buildWantedLevelSet(levels);
|
|
3588
|
+
if (!wantedLevelSet) {
|
|
3589
|
+
return { lines: [], totalLinesCount: 0 };
|
|
3590
|
+
}
|
|
3591
|
+
const stat = fs17.statSync(filePath);
|
|
3592
|
+
if (stat.size === 0) {
|
|
3593
|
+
return { lines: [], totalLinesCount: 0 };
|
|
3594
|
+
}
|
|
3595
|
+
const fd = fs17.openSync(filePath, "r");
|
|
3596
|
+
const chunkSize = 64 * 1024;
|
|
3597
|
+
let position = stat.size;
|
|
3598
|
+
let remainder = "";
|
|
3599
|
+
let totalLinesCount = 0;
|
|
3600
|
+
let skipped = 0;
|
|
3601
|
+
const collected = [];
|
|
3602
|
+
try {
|
|
3603
|
+
while (position > 0) {
|
|
3604
|
+
const length = Math.min(chunkSize, position);
|
|
3605
|
+
position -= length;
|
|
3606
|
+
const buffer = Buffer.alloc(length);
|
|
3607
|
+
fs17.readSync(fd, buffer, 0, length, position);
|
|
3608
|
+
let chunk = buffer.toString("utf8");
|
|
3609
|
+
if (remainder) {
|
|
3610
|
+
chunk += remainder;
|
|
3611
|
+
remainder = "";
|
|
3612
|
+
}
|
|
3613
|
+
const parts = chunk.split("\n");
|
|
3614
|
+
remainder = parts.shift() ?? "";
|
|
3615
|
+
for (let i = parts.length - 1; i >= 0; i -= 1) {
|
|
3616
|
+
const line = parts[i].trim();
|
|
3617
|
+
if (!line) continue;
|
|
3618
|
+
try {
|
|
3619
|
+
const parsed = JSON.parse(line);
|
|
3620
|
+
const levelName = extractLevelName(parsed);
|
|
3621
|
+
if (levelName && wantedLevelSet.has(levelName)) {
|
|
3622
|
+
totalLinesCount += 1;
|
|
3623
|
+
if (skipped < offset) {
|
|
3624
|
+
skipped += 1;
|
|
3625
|
+
} else if (collected.length < maxLines) {
|
|
3626
|
+
collected.push(line);
|
|
3627
|
+
}
|
|
3628
|
+
}
|
|
3629
|
+
} catch {
|
|
3630
|
+
continue;
|
|
3631
|
+
}
|
|
3632
|
+
}
|
|
3633
|
+
}
|
|
3634
|
+
if (remainder) {
|
|
3635
|
+
const line = remainder.trim();
|
|
3636
|
+
if (line) {
|
|
3637
|
+
try {
|
|
3638
|
+
const parsed = JSON.parse(line);
|
|
3639
|
+
const levelName = extractLevelName(parsed);
|
|
3640
|
+
if (levelName && wantedLevelSet.has(levelName)) {
|
|
3641
|
+
totalLinesCount += 1;
|
|
3642
|
+
if (skipped < offset) {
|
|
3643
|
+
skipped += 1;
|
|
3644
|
+
} else if (collected.length < maxLines) {
|
|
3645
|
+
collected.push(line);
|
|
3646
|
+
}
|
|
3647
|
+
}
|
|
3648
|
+
} catch {
|
|
3649
|
+
return { lines: [], totalLinesCount: 0 };
|
|
3650
|
+
}
|
|
3651
|
+
}
|
|
3652
|
+
}
|
|
3653
|
+
} finally {
|
|
3654
|
+
fs17.closeSync(fd);
|
|
3655
|
+
}
|
|
3656
|
+
return { lines: collected.reverse(), totalLinesCount };
|
|
3657
|
+
}
|
|
3658
|
+
|
|
3659
|
+
// src/commands/read-logs/index.ts
|
|
3660
|
+
var LOG_TYPES = ["server", "trace", "server-std", "client-std", "browser"];
|
|
3661
|
+
function sanitizeStructuredLog(value) {
|
|
3662
|
+
if (!value || typeof value !== "object") return value;
|
|
3663
|
+
if (Array.isArray(value)) return value;
|
|
3664
|
+
const obj = value;
|
|
3665
|
+
const sanitized = { ...obj };
|
|
3666
|
+
delete sanitized.tenant_id;
|
|
3667
|
+
delete sanitized.app_id;
|
|
3668
|
+
delete sanitized.pid;
|
|
3669
|
+
return sanitized;
|
|
3670
|
+
}
|
|
3671
|
+
function hasErrorInStdLines(lines) {
|
|
3672
|
+
const combined = lines.join("\n");
|
|
3673
|
+
if (!combined) return false;
|
|
3674
|
+
const strong = [
|
|
3675
|
+
/compiled with errors/i,
|
|
3676
|
+
/failed to compile/i,
|
|
3677
|
+
/error: \w+/i,
|
|
3678
|
+
/uncaught/i,
|
|
3679
|
+
/unhandled/i,
|
|
3680
|
+
/eaddrinuse/i,
|
|
3681
|
+
/cannot find module/i,
|
|
3682
|
+
/module not found/i,
|
|
3683
|
+
/ts\d{3,5}:/i
|
|
3684
|
+
];
|
|
3685
|
+
if (strong.some((re) => re.test(combined))) return true;
|
|
3686
|
+
const weakLine = /\b(error|fatal|exception)\b/i;
|
|
3687
|
+
const ignorePatterns = [
|
|
3688
|
+
/\b0\s+errors?\b/i,
|
|
3689
|
+
/Server Error \d{3}/i
|
|
3690
|
+
];
|
|
3691
|
+
return lines.some((line) => {
|
|
3692
|
+
const text = line.trim();
|
|
3693
|
+
if (!text) return false;
|
|
3694
|
+
if (ignorePatterns.some((re) => re.test(text))) return false;
|
|
3695
|
+
return weakLine.test(text);
|
|
3696
|
+
});
|
|
3697
|
+
}
|
|
3698
|
+
function hasErrorInLogObject(value) {
|
|
3699
|
+
if (!value || typeof value !== "object") return false;
|
|
3700
|
+
const obj = value;
|
|
3701
|
+
const rawLevel = obj.level;
|
|
3702
|
+
const level = typeof rawLevel === "string" ? rawLevel.toLowerCase() : "";
|
|
3703
|
+
if (level === "error" || level === "fatal") return true;
|
|
3704
|
+
if (level === "err") return true;
|
|
3705
|
+
if (typeof rawLevel === "number" && Number.isFinite(rawLevel) && rawLevel >= 50) return true;
|
|
3706
|
+
if (level === "warn" && typeof obj.stack === "string" && obj.stack.length > 0) return true;
|
|
3707
|
+
if (typeof obj.stack === "string" && obj.stack.length > 0) return true;
|
|
3708
|
+
const statusCode = obj.statusCode;
|
|
3709
|
+
const status_code = obj.status_code;
|
|
3710
|
+
const meta = obj.meta;
|
|
3711
|
+
const metaObj = meta && typeof meta === "object" ? meta : null;
|
|
3712
|
+
const metaStatusCode = metaObj?.statusCode;
|
|
3713
|
+
const metaStatus_code = metaObj?.status_code;
|
|
3714
|
+
const candidates = [statusCode, status_code, metaStatusCode, metaStatus_code];
|
|
3715
|
+
for (const candidate of candidates) {
|
|
3716
|
+
if (typeof candidate === "number" && Number.isFinite(candidate) && candidate >= 400) {
|
|
3717
|
+
return true;
|
|
3718
|
+
}
|
|
3719
|
+
}
|
|
3720
|
+
const message = typeof obj.message === "string" ? obj.message : "";
|
|
3721
|
+
if (message && hasErrorInStdLines([message])) return true;
|
|
3722
|
+
return false;
|
|
3723
|
+
}
|
|
3724
|
+
async function readLatestLogLinesMeta(options) {
|
|
3725
|
+
const maxLines = options.maxLines ?? 30;
|
|
3726
|
+
const offset = options.offset ?? 0;
|
|
3727
|
+
const filePath = resolveLogFilePath(options.logDir, options.type);
|
|
3728
|
+
const levels = Array.isArray(options.levels) ? options.levels : void 0;
|
|
3729
|
+
if (!fileExists(filePath)) {
|
|
3730
|
+
throw new Error(`Log file not found: ${filePath}`);
|
|
3731
|
+
}
|
|
3732
|
+
if (options.type === "server-std") {
|
|
3733
|
+
return readServerStdSegment(filePath, maxLines, offset);
|
|
3734
|
+
}
|
|
3735
|
+
if (options.type === "client-std") {
|
|
3736
|
+
return readClientStdSegment(filePath, maxLines, offset);
|
|
3737
|
+
}
|
|
3738
|
+
const traceId = typeof options.traceId === "string" ? options.traceId.trim() : "";
|
|
3739
|
+
if (traceId) {
|
|
3740
|
+
return readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels);
|
|
3741
|
+
}
|
|
3742
|
+
if (options.type === "server" || options.type === "trace") {
|
|
3743
|
+
return readJsonLinesLastPid(filePath, maxLines, offset, levels);
|
|
3744
|
+
}
|
|
3745
|
+
if (options.type === "browser") {
|
|
3746
|
+
if (levels && levels.length > 0) {
|
|
3747
|
+
return readJsonLinesTailByLevel(filePath, maxLines, offset, levels);
|
|
3748
|
+
}
|
|
3749
|
+
return readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset);
|
|
3750
|
+
}
|
|
3751
|
+
return { lines: [], totalLinesCount: 0 };
|
|
3752
|
+
}
|
|
3753
|
+
async function readLogsJsonResult(options) {
|
|
3754
|
+
const { lines, totalLinesCount } = await readLatestLogLinesMeta(options);
|
|
3755
|
+
if (options.type === "server-std" || options.type === "client-std") {
|
|
3756
|
+
return {
|
|
3757
|
+
hasError: hasErrorInStdLines(lines),
|
|
3758
|
+
totalLinesCount,
|
|
3759
|
+
logs: lines
|
|
3760
|
+
};
|
|
3761
|
+
}
|
|
3762
|
+
const logs = [];
|
|
3763
|
+
let hasError = false;
|
|
3764
|
+
for (const line of lines) {
|
|
3765
|
+
if (!hasError && hasErrorInStdLines([line])) {
|
|
3766
|
+
hasError = true;
|
|
3767
|
+
}
|
|
3768
|
+
try {
|
|
3769
|
+
const parsed = JSON.parse(line);
|
|
3770
|
+
logs.push(sanitizeStructuredLog(parsed));
|
|
3771
|
+
if (!hasError && hasErrorInLogObject(parsed)) {
|
|
3772
|
+
hasError = true;
|
|
3773
|
+
}
|
|
3774
|
+
} catch {
|
|
3775
|
+
continue;
|
|
3776
|
+
}
|
|
3777
|
+
}
|
|
3778
|
+
return {
|
|
3779
|
+
hasError,
|
|
3780
|
+
totalLinesCount,
|
|
3781
|
+
logs
|
|
3782
|
+
};
|
|
3783
|
+
}
|
|
3784
|
+
function resolveLogFilePath(logDir, type) {
|
|
3785
|
+
const base = path15.isAbsolute(logDir) ? logDir : path15.join(process.cwd(), logDir);
|
|
3786
|
+
if (type === "server") {
|
|
3787
|
+
return path15.join(base, "server.log");
|
|
3788
|
+
}
|
|
3789
|
+
if (type === "trace") {
|
|
3790
|
+
return path15.join(base, "trace.log");
|
|
3791
|
+
}
|
|
3792
|
+
if (type === "server-std") {
|
|
3793
|
+
return path15.join(base, "server.std.log");
|
|
3794
|
+
}
|
|
3795
|
+
if (type === "client-std") {
|
|
3796
|
+
return path15.join(base, "client.std.log");
|
|
3797
|
+
}
|
|
3798
|
+
if (type === "browser") {
|
|
3799
|
+
return path15.join(base, "browser.log");
|
|
3800
|
+
}
|
|
3801
|
+
throw new Error(`Unsupported log type: ${type}`);
|
|
3802
|
+
}
|
|
3803
|
+
async function run4(options) {
|
|
3804
|
+
try {
|
|
3805
|
+
const result = await readLogsJsonResult(options);
|
|
3806
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
3807
|
+
} catch (error) {
|
|
3808
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3809
|
+
const result = { hasError: true, totalLinesCount: 0, logs: [message] };
|
|
3810
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
3811
|
+
process.exitCode = 1;
|
|
3812
|
+
}
|
|
3813
|
+
}
|
|
3814
|
+
function parseLogType(input) {
|
|
3815
|
+
const value = typeof input === "string" ? input.trim() : "";
|
|
3816
|
+
if (LOG_TYPES.includes(value)) {
|
|
3817
|
+
return value;
|
|
3818
|
+
}
|
|
3819
|
+
throw new Error(`Invalid --type: ${String(input)}. Expected one of: ${LOG_TYPES.join(", ")}`);
|
|
3820
|
+
}
|
|
3821
|
+
function parsePositiveInt(input, flagName) {
|
|
3822
|
+
const value = typeof input === "number" ? input : Number.parseInt(String(input), 10);
|
|
3823
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
3824
|
+
throw new Error(`Invalid ${flagName}: ${String(input)}. Expected a positive integer.`);
|
|
3825
|
+
}
|
|
3826
|
+
return value;
|
|
3827
|
+
}
|
|
3828
|
+
function parseNonNegativeInt(input, flagName) {
|
|
3829
|
+
const value = typeof input === "number" ? input : Number.parseInt(String(input), 10);
|
|
3830
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
3831
|
+
throw new Error(`Invalid ${flagName}: ${String(input)}. Expected a non-negative integer.`);
|
|
3832
|
+
}
|
|
3833
|
+
return value;
|
|
3834
|
+
}
|
|
3835
|
+
function parseCommaSeparatedList(input) {
|
|
3836
|
+
const value = typeof input === "string" ? input : "";
|
|
3837
|
+
const items = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
3838
|
+
return items.length > 0 ? items : void 0;
|
|
3839
|
+
}
|
|
3840
|
+
var readLogsCommand = {
|
|
3841
|
+
name: "read-logs",
|
|
3842
|
+
description: "Read latest logs from log files",
|
|
3843
|
+
register(program) {
|
|
3844
|
+
program.command(this.name).description(this.description).option("--dir <path>", "Logs directory", "logs").option("--type <type>", `Log type: ${LOG_TYPES.join("|")}`, "server-std").option("--max-lines <lines>", "Max lines to return", "30").option("--offset <lines>", "Skip latest N lines for pagination", "0").option("--trace-id <id>", "Filter structured logs by trace id").option("--level <levels>", "Filter structured logs by level (comma-separated)").action(async (rawOptions) => {
|
|
3845
|
+
try {
|
|
3846
|
+
const logDir = typeof rawOptions.dir === "string" ? rawOptions.dir : "logs";
|
|
3847
|
+
const type = parseLogType(rawOptions.type);
|
|
3848
|
+
const maxLines = parsePositiveInt(rawOptions.maxLines, "--max-lines");
|
|
3849
|
+
const offset = parseNonNegativeInt(rawOptions.offset, "--offset");
|
|
3850
|
+
const traceId = typeof rawOptions.traceId === "string" ? rawOptions.traceId : void 0;
|
|
3851
|
+
const levels = parseCommaSeparatedList(rawOptions.level);
|
|
3852
|
+
await run4({ logDir, type, maxLines, offset, traceId, levels });
|
|
3853
|
+
} catch (error) {
|
|
3854
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3855
|
+
const result = { hasError: true, totalLinesCount: 0, logs: [message] };
|
|
3856
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
3857
|
+
process.exitCode = 1;
|
|
3858
|
+
}
|
|
3859
|
+
});
|
|
3860
|
+
}
|
|
3861
|
+
};
|
|
3862
|
+
|
|
2982
3863
|
// src/commands/index.ts
|
|
2983
3864
|
var commands = [
|
|
2984
3865
|
genDbSchemaCommand,
|
|
2985
3866
|
syncCommand,
|
|
2986
3867
|
actionPluginCommandGroup,
|
|
2987
3868
|
capabilityCommandGroup,
|
|
2988
|
-
migrationCommand
|
|
3869
|
+
migrationCommand,
|
|
3870
|
+
readLogsCommand
|
|
2989
3871
|
];
|
|
2990
3872
|
|
|
2991
3873
|
// src/index.ts
|
|
2992
|
-
var envPath =
|
|
2993
|
-
if (
|
|
3874
|
+
var envPath = path16.join(process.cwd(), ".env");
|
|
3875
|
+
if (fs18.existsSync(envPath)) {
|
|
2994
3876
|
dotenvConfig({ path: envPath });
|
|
2995
3877
|
}
|
|
2996
|
-
var __dirname =
|
|
2997
|
-
var pkg = JSON.parse(
|
|
3878
|
+
var __dirname = path16.dirname(fileURLToPath3(import.meta.url));
|
|
3879
|
+
var pkg = JSON.parse(fs18.readFileSync(path16.join(__dirname, "../package.json"), "utf-8"));
|
|
2998
3880
|
var cli = new FullstackCLI(pkg.version);
|
|
2999
3881
|
cli.useAll(commands);
|
|
3000
3882
|
cli.run();
|