@lark-apaas/fullstack-cli 1.1.6-alpha.1 → 1.1.6-alpha.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -3
- package/dist/index.js +1417 -574
- package/package.json +2 -1
- package/templates/scripts/build.sh +16 -9
- package/templates/scripts/dev.sh +10 -1
- package/templates/scripts/prune-smart.js +41 -7
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import fs14 from "fs";
|
|
3
|
+
import path14 from "path";
|
|
4
4
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
5
5
|
import { config as dotenvConfig } from "dotenv";
|
|
6
6
|
|
|
@@ -438,8 +438,7 @@ var syncCommand = {
|
|
|
438
438
|
// src/commands/action-plugin/utils.ts
|
|
439
439
|
import fs3 from "fs";
|
|
440
440
|
import path3 from "path";
|
|
441
|
-
import { spawnSync as spawnSync2 } from "child_process";
|
|
442
|
-
var CONFIG_FILE_NAME = ".capabilityrc.json";
|
|
441
|
+
import { spawnSync as spawnSync2, execSync } from "child_process";
|
|
443
442
|
function parsePluginName(input) {
|
|
444
443
|
const match = input.match(/^(@[^/]+\/[^@]+)(?:@(.+))?$/);
|
|
445
444
|
if (!match) {
|
|
@@ -455,36 +454,44 @@ function parsePluginName(input) {
|
|
|
455
454
|
function getProjectRoot() {
|
|
456
455
|
return process.cwd();
|
|
457
456
|
}
|
|
458
|
-
function
|
|
459
|
-
return path3.join(getProjectRoot(),
|
|
457
|
+
function getPackageJsonPath() {
|
|
458
|
+
return path3.join(getProjectRoot(), "package.json");
|
|
460
459
|
}
|
|
461
460
|
function getPluginPath(pluginName) {
|
|
462
461
|
return path3.join(getProjectRoot(), "node_modules", pluginName);
|
|
463
462
|
}
|
|
464
|
-
function
|
|
465
|
-
const
|
|
466
|
-
if (!fs3.existsSync(
|
|
467
|
-
|
|
463
|
+
function readPackageJson() {
|
|
464
|
+
const pkgPath = getPackageJsonPath();
|
|
465
|
+
if (!fs3.existsSync(pkgPath)) {
|
|
466
|
+
throw new Error("package.json not found in current directory");
|
|
468
467
|
}
|
|
469
468
|
try {
|
|
470
|
-
const content = fs3.readFileSync(
|
|
469
|
+
const content = fs3.readFileSync(pkgPath, "utf-8");
|
|
471
470
|
return JSON.parse(content);
|
|
472
471
|
} catch {
|
|
473
|
-
|
|
474
|
-
return { plugins: {} };
|
|
472
|
+
throw new Error("Failed to parse package.json");
|
|
475
473
|
}
|
|
476
474
|
}
|
|
477
|
-
function
|
|
478
|
-
const
|
|
479
|
-
fs3.writeFileSync(
|
|
475
|
+
function writePackageJson(pkg2) {
|
|
476
|
+
const pkgPath = getPackageJsonPath();
|
|
477
|
+
fs3.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
|
|
478
|
+
}
|
|
479
|
+
function readActionPlugins() {
|
|
480
|
+
const pkg2 = readPackageJson();
|
|
481
|
+
return pkg2.actionPlugins || {};
|
|
482
|
+
}
|
|
483
|
+
function writeActionPlugins(plugins) {
|
|
484
|
+
const pkg2 = readPackageJson();
|
|
485
|
+
pkg2.actionPlugins = plugins;
|
|
486
|
+
writePackageJson(pkg2);
|
|
480
487
|
}
|
|
481
488
|
function isPluginInstalled(pluginName) {
|
|
482
|
-
const
|
|
483
|
-
return pluginName in
|
|
489
|
+
const plugins = readActionPlugins();
|
|
490
|
+
return pluginName in plugins;
|
|
484
491
|
}
|
|
485
|
-
function
|
|
486
|
-
const
|
|
487
|
-
return
|
|
492
|
+
function getInstalledPluginVersion(pluginName) {
|
|
493
|
+
const plugins = readActionPlugins();
|
|
494
|
+
return plugins[pluginName] || null;
|
|
488
495
|
}
|
|
489
496
|
function npmInstall(tgzPath) {
|
|
490
497
|
console.log(`[action-plugin] Running npm install ${tgzPath}...`);
|
|
@@ -499,19 +506,6 @@ function npmInstall(tgzPath) {
|
|
|
499
506
|
throw new Error(`npm install failed with exit code ${result.status}`);
|
|
500
507
|
}
|
|
501
508
|
}
|
|
502
|
-
function npmUninstall(pluginName) {
|
|
503
|
-
console.log(`[action-plugin] Running npm uninstall ${pluginName}...`);
|
|
504
|
-
const result = spawnSync2("npm", ["uninstall", pluginName], {
|
|
505
|
-
cwd: getProjectRoot(),
|
|
506
|
-
stdio: "inherit"
|
|
507
|
-
});
|
|
508
|
-
if (result.error) {
|
|
509
|
-
throw new Error(`npm uninstall failed: ${result.error.message}`);
|
|
510
|
-
}
|
|
511
|
-
if (result.status !== 0) {
|
|
512
|
-
throw new Error(`npm uninstall failed with exit code ${result.status}`);
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
509
|
function getPackageVersion(pluginName) {
|
|
516
510
|
const pkgJsonPath = path3.join(getPluginPath(pluginName), "package.json");
|
|
517
511
|
if (!fs3.existsSync(pkgJsonPath)) {
|
|
@@ -525,6 +519,90 @@ function getPackageVersion(pluginName) {
|
|
|
525
519
|
return null;
|
|
526
520
|
}
|
|
527
521
|
}
|
|
522
|
+
function readPluginPackageJson(pluginPath) {
|
|
523
|
+
const pkgJsonPath = path3.join(pluginPath, "package.json");
|
|
524
|
+
if (!fs3.existsSync(pkgJsonPath)) {
|
|
525
|
+
return null;
|
|
526
|
+
}
|
|
527
|
+
try {
|
|
528
|
+
const content = fs3.readFileSync(pkgJsonPath, "utf-8");
|
|
529
|
+
return JSON.parse(content);
|
|
530
|
+
} catch {
|
|
531
|
+
return null;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
function extractTgzToNodeModules(tgzPath, pluginName) {
|
|
535
|
+
const nodeModulesPath = path3.join(getProjectRoot(), "node_modules");
|
|
536
|
+
const targetDir = path3.join(nodeModulesPath, pluginName);
|
|
537
|
+
const scopeDir = path3.dirname(targetDir);
|
|
538
|
+
if (!fs3.existsSync(scopeDir)) {
|
|
539
|
+
fs3.mkdirSync(scopeDir, { recursive: true });
|
|
540
|
+
}
|
|
541
|
+
if (fs3.existsSync(targetDir)) {
|
|
542
|
+
fs3.rmSync(targetDir, { recursive: true });
|
|
543
|
+
}
|
|
544
|
+
const tempDir = path3.join(nodeModulesPath, ".cache", "fullstack-cli", "extract-temp");
|
|
545
|
+
if (fs3.existsSync(tempDir)) {
|
|
546
|
+
fs3.rmSync(tempDir, { recursive: true });
|
|
547
|
+
}
|
|
548
|
+
fs3.mkdirSync(tempDir, { recursive: true });
|
|
549
|
+
try {
|
|
550
|
+
execSync(`tar -xzf "${tgzPath}" -C "${tempDir}"`, { stdio: "pipe" });
|
|
551
|
+
const extractedDir = path3.join(tempDir, "package");
|
|
552
|
+
if (fs3.existsSync(extractedDir)) {
|
|
553
|
+
fs3.renameSync(extractedDir, targetDir);
|
|
554
|
+
} else {
|
|
555
|
+
const files = fs3.readdirSync(tempDir);
|
|
556
|
+
if (files.length === 1) {
|
|
557
|
+
fs3.renameSync(path3.join(tempDir, files[0]), targetDir);
|
|
558
|
+
} else {
|
|
559
|
+
throw new Error("Unexpected tgz structure");
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
return targetDir;
|
|
563
|
+
} finally {
|
|
564
|
+
if (fs3.existsSync(tempDir)) {
|
|
565
|
+
fs3.rmSync(tempDir, { recursive: true });
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
function checkMissingPeerDeps(peerDeps) {
|
|
570
|
+
if (!peerDeps || Object.keys(peerDeps).length === 0) {
|
|
571
|
+
return [];
|
|
572
|
+
}
|
|
573
|
+
const missing = [];
|
|
574
|
+
const nodeModulesPath = path3.join(getProjectRoot(), "node_modules");
|
|
575
|
+
for (const [depName, _version] of Object.entries(peerDeps)) {
|
|
576
|
+
const depPath = path3.join(nodeModulesPath, depName);
|
|
577
|
+
if (!fs3.existsSync(depPath)) {
|
|
578
|
+
missing.push(depName);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return missing;
|
|
582
|
+
}
|
|
583
|
+
function installMissingDeps(deps) {
|
|
584
|
+
if (deps.length === 0) {
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
console.log(`[action-plugin] Installing missing dependencies: ${deps.join(", ")}`);
|
|
588
|
+
const result = spawnSync2("npm", ["install", ...deps, "--no-save", "--no-package-lock"], {
|
|
589
|
+
cwd: getProjectRoot(),
|
|
590
|
+
stdio: "inherit"
|
|
591
|
+
});
|
|
592
|
+
if (result.error) {
|
|
593
|
+
throw new Error(`npm install failed: ${result.error.message}`);
|
|
594
|
+
}
|
|
595
|
+
if (result.status !== 0) {
|
|
596
|
+
throw new Error(`npm install failed with exit code ${result.status}`);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
function removePluginDirectory(pluginName) {
|
|
600
|
+
const pluginPath = getPluginPath(pluginName);
|
|
601
|
+
if (fs3.existsSync(pluginPath)) {
|
|
602
|
+
fs3.rmSync(pluginPath, { recursive: true });
|
|
603
|
+
console.log(`[action-plugin] Removed ${pluginName}`);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
528
606
|
|
|
529
607
|
// src/commands/action-plugin/api-client.ts
|
|
530
608
|
import { HttpClient as HttpClient2 } from "@lark-apaas/http-client";
|
|
@@ -659,35 +737,106 @@ function cleanupTempFile(tgzPath) {
|
|
|
659
737
|
}
|
|
660
738
|
}
|
|
661
739
|
|
|
740
|
+
// src/commands/action-plugin/init.handler.ts
|
|
741
|
+
async function installOneForInit(name, version) {
|
|
742
|
+
let tgzPath;
|
|
743
|
+
try {
|
|
744
|
+
const installedVersion = getPackageVersion(name);
|
|
745
|
+
if (installedVersion === version) {
|
|
746
|
+
return { name, version, success: true, skipped: true };
|
|
747
|
+
}
|
|
748
|
+
console.log(`[action-plugin] Installing ${name}@${version}...`);
|
|
749
|
+
const downloadResult = await downloadPlugin(name, version);
|
|
750
|
+
tgzPath = downloadResult.tgzPath;
|
|
751
|
+
const pluginDir = extractTgzToNodeModules(tgzPath, name);
|
|
752
|
+
const pluginPkg = readPluginPackageJson(pluginDir);
|
|
753
|
+
if (pluginPkg?.peerDependencies) {
|
|
754
|
+
const missingDeps = checkMissingPeerDeps(pluginPkg.peerDependencies);
|
|
755
|
+
if (missingDeps.length > 0) {
|
|
756
|
+
installMissingDeps(missingDeps);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
console.log(`[action-plugin] \u2713 Installed ${name}@${version}`);
|
|
760
|
+
return { name, version, success: true };
|
|
761
|
+
} catch (error) {
|
|
762
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
763
|
+
console.error(`[action-plugin] \u2717 Failed to install ${name}: ${message}`);
|
|
764
|
+
return { name, version, success: false, error: message };
|
|
765
|
+
} finally {
|
|
766
|
+
if (tgzPath) {
|
|
767
|
+
cleanupTempFile(tgzPath);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
async function init() {
|
|
772
|
+
console.log("[action-plugin] Reading plugins from package.json...");
|
|
773
|
+
const plugins = readActionPlugins();
|
|
774
|
+
const entries = Object.entries(plugins);
|
|
775
|
+
if (entries.length === 0) {
|
|
776
|
+
console.log("[action-plugin] No plugins found in package.json");
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
console.log(`[action-plugin] Found ${entries.length} plugin(s) to install
|
|
780
|
+
`);
|
|
781
|
+
const results = [];
|
|
782
|
+
for (const [name, version] of entries) {
|
|
783
|
+
const result = await installOneForInit(name, version);
|
|
784
|
+
results.push(result);
|
|
785
|
+
}
|
|
786
|
+
console.log("");
|
|
787
|
+
const successful = results.filter((r) => r.success && !r.skipped);
|
|
788
|
+
const skipped = results.filter((r) => r.skipped);
|
|
789
|
+
const failed = results.filter((r) => !r.success);
|
|
790
|
+
if (successful.length > 0 || skipped.length > 0) {
|
|
791
|
+
console.log(
|
|
792
|
+
`[action-plugin] \u2713 All plugins installed successfully (${successful.length + skipped.length}/${entries.length})`
|
|
793
|
+
);
|
|
794
|
+
if (skipped.length > 0) {
|
|
795
|
+
console.log(` Skipped (already installed): ${skipped.map((r) => r.name).join(", ")}`);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
if (failed.length > 0) {
|
|
799
|
+
console.log(`[action-plugin] \u2717 Failed to install ${failed.length} plugin(s): ${failed.map((r) => r.name).join(", ")}`);
|
|
800
|
+
process.exit(1);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
662
804
|
// src/commands/action-plugin/install.handler.ts
|
|
663
805
|
async function installOne(nameWithVersion) {
|
|
664
806
|
let tgzPath;
|
|
665
807
|
const { name, version: requestedVersion } = parsePluginName(nameWithVersion);
|
|
666
808
|
try {
|
|
667
809
|
console.log(`[action-plugin] Installing ${name}@${requestedVersion}...`);
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
if (
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
if (info.version === latestVersion) {
|
|
674
|
-
console.log(`[action-plugin] Plugin ${name} is already up to date (version: ${info.version})`);
|
|
675
|
-
return { name, version: info.version, success: true, skipped: true };
|
|
676
|
-
}
|
|
677
|
-
console.log(`[action-plugin] Found newer version: ${latestVersion} (installed: ${info.version})`);
|
|
678
|
-
console.log(`[action-plugin] Updating to ${latestVersion}...`);
|
|
810
|
+
const actualVersion = getPackageVersion(name);
|
|
811
|
+
if (actualVersion && requestedVersion !== "latest") {
|
|
812
|
+
if (actualVersion === requestedVersion) {
|
|
813
|
+
console.log(`[action-plugin] Plugin ${name}@${requestedVersion} is already installed`);
|
|
814
|
+
return { name, version: actualVersion, success: true, skipped: true };
|
|
679
815
|
}
|
|
680
816
|
}
|
|
817
|
+
if (actualVersion && requestedVersion === "latest") {
|
|
818
|
+
const latestInfo = await getPluginVersion(name, "latest");
|
|
819
|
+
if (actualVersion === latestInfo.version) {
|
|
820
|
+
console.log(`[action-plugin] Plugin ${name} is already up to date (version: ${actualVersion})`);
|
|
821
|
+
return { name, version: actualVersion, success: true, skipped: true };
|
|
822
|
+
}
|
|
823
|
+
console.log(`[action-plugin] Found newer version: ${latestInfo.version} (installed: ${actualVersion})`);
|
|
824
|
+
}
|
|
681
825
|
const downloadResult = await downloadPlugin(name, requestedVersion);
|
|
682
826
|
tgzPath = downloadResult.tgzPath;
|
|
683
|
-
|
|
827
|
+
console.log(`[action-plugin] Extracting to node_modules...`);
|
|
828
|
+
const pluginDir = extractTgzToNodeModules(tgzPath, name);
|
|
829
|
+
const pluginPkg = readPluginPackageJson(pluginDir);
|
|
830
|
+
if (pluginPkg?.peerDependencies) {
|
|
831
|
+
const missingDeps = checkMissingPeerDeps(pluginPkg.peerDependencies);
|
|
832
|
+
if (missingDeps.length > 0) {
|
|
833
|
+
installMissingDeps(missingDeps);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
684
836
|
const installedVersion = getPackageVersion(name) || downloadResult.version;
|
|
685
|
-
const
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
689
|
-
};
|
|
690
|
-
writeConfig(config);
|
|
837
|
+
const plugins = readActionPlugins();
|
|
838
|
+
plugins[name] = installedVersion;
|
|
839
|
+
writeActionPlugins(plugins);
|
|
691
840
|
console.log(`[action-plugin] Successfully installed ${name}@${installedVersion}`);
|
|
692
841
|
return { name, version: installedVersion, success: true };
|
|
693
842
|
} catch (error) {
|
|
@@ -740,23 +889,19 @@ async function updateOne(nameWithVersion) {
|
|
|
740
889
|
console.error(`[action-plugin] Plugin ${name} is not installed`);
|
|
741
890
|
return { name, success: false, notInstalled: true };
|
|
742
891
|
}
|
|
743
|
-
const
|
|
744
|
-
const oldVersion = currentInfo?.version || "unknown";
|
|
892
|
+
const oldVersion = getInstalledPluginVersion(name) || "unknown";
|
|
745
893
|
console.log(`[action-plugin] Current version: ${oldVersion}`);
|
|
746
894
|
const downloadResult = await downloadPlugin(name, "latest");
|
|
747
895
|
tgzPath = downloadResult.tgzPath;
|
|
748
|
-
if (
|
|
896
|
+
if (oldVersion === downloadResult.version) {
|
|
749
897
|
console.log(`[action-plugin] Plugin ${name} is already up to date (version: ${downloadResult.version})`);
|
|
750
898
|
return { name, oldVersion, newVersion: downloadResult.version, success: true, skipped: true };
|
|
751
899
|
}
|
|
752
900
|
npmInstall(tgzPath);
|
|
753
901
|
const installedVersion = getPackageVersion(name) || downloadResult.version;
|
|
754
|
-
const
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
758
|
-
};
|
|
759
|
-
writeConfig(config);
|
|
902
|
+
const plugins = readActionPlugins();
|
|
903
|
+
plugins[name] = installedVersion;
|
|
904
|
+
writeActionPlugins(plugins);
|
|
760
905
|
console.log(`[action-plugin] Successfully updated ${name} to ${installedVersion}`);
|
|
761
906
|
return { name, oldVersion, newVersion: installedVersion, success: true };
|
|
762
907
|
} catch (error) {
|
|
@@ -812,10 +957,10 @@ async function remove(nameWithVersion) {
|
|
|
812
957
|
console.error(`[action-plugin] Plugin ${name} is not installed`);
|
|
813
958
|
process.exit(1);
|
|
814
959
|
}
|
|
815
|
-
|
|
816
|
-
const
|
|
817
|
-
delete
|
|
818
|
-
|
|
960
|
+
removePluginDirectory(name);
|
|
961
|
+
const plugins = readActionPlugins();
|
|
962
|
+
delete plugins[name];
|
|
963
|
+
writeActionPlugins(plugins);
|
|
819
964
|
console.log(`[action-plugin] Successfully removed ${name}`);
|
|
820
965
|
} catch (error) {
|
|
821
966
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -827,25 +972,22 @@ async function remove(nameWithVersion) {
|
|
|
827
972
|
// src/commands/action-plugin/list.handler.ts
|
|
828
973
|
async function list() {
|
|
829
974
|
try {
|
|
830
|
-
const
|
|
831
|
-
const plugins = Object.entries(config.plugins);
|
|
975
|
+
const plugins = Object.entries(readActionPlugins());
|
|
832
976
|
if (plugins.length === 0) {
|
|
833
977
|
console.log("[action-plugin] No plugins installed");
|
|
834
978
|
console.log('[action-plugin] Use "action-plugin install <name>" to install a plugin');
|
|
835
979
|
return;
|
|
836
980
|
}
|
|
837
981
|
console.log("[action-plugin] Installed plugins:\n");
|
|
838
|
-
const nameWidth = Math.max(
|
|
982
|
+
const nameWidth = Math.max(40, ...plugins.map(([name]) => name.length + 2));
|
|
839
983
|
const versionWidth = 15;
|
|
840
|
-
const dateWidth = 25;
|
|
841
984
|
console.log(
|
|
842
|
-
" " + "Plugin".padEnd(nameWidth) + "Version"
|
|
985
|
+
" " + "Plugin".padEnd(nameWidth) + "Version"
|
|
843
986
|
);
|
|
844
|
-
console.log(" " + "-".repeat(nameWidth + versionWidth
|
|
845
|
-
for (const [name,
|
|
846
|
-
const installedAt = new Date(info.installedAt).toLocaleString();
|
|
987
|
+
console.log(" " + "-".repeat(nameWidth + versionWidth));
|
|
988
|
+
for (const [name, version] of plugins) {
|
|
847
989
|
console.log(
|
|
848
|
-
" " + name.padEnd(nameWidth) +
|
|
990
|
+
" " + name.padEnd(nameWidth) + version
|
|
849
991
|
);
|
|
850
992
|
}
|
|
851
993
|
console.log(`
|
|
@@ -858,6 +1000,15 @@ async function list() {
|
|
|
858
1000
|
}
|
|
859
1001
|
|
|
860
1002
|
// src/commands/action-plugin/index.ts
|
|
1003
|
+
var initCommand = {
|
|
1004
|
+
name: "init",
|
|
1005
|
+
description: "Install all plugins from package.json",
|
|
1006
|
+
register(program) {
|
|
1007
|
+
program.command(this.name).description(this.description).action(async () => {
|
|
1008
|
+
await init();
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
};
|
|
861
1012
|
var installCommand = {
|
|
862
1013
|
name: "install",
|
|
863
1014
|
description: "Install action plugin(s) (default: latest version)",
|
|
@@ -901,7 +1052,7 @@ var listCommand = {
|
|
|
901
1052
|
var actionPluginCommandGroup = {
|
|
902
1053
|
name: "action-plugin",
|
|
903
1054
|
description: "Manage action plugins",
|
|
904
|
-
commands: [installCommand, updateCommand, removeCommand, listCommand]
|
|
1055
|
+
commands: [initCommand, installCommand, updateCommand, removeCommand, listCommand]
|
|
905
1056
|
};
|
|
906
1057
|
|
|
907
1058
|
// src/commands/capability/utils.ts
|
|
@@ -917,8 +1068,8 @@ function getCapabilitiesDir() {
|
|
|
917
1068
|
function getCapabilityPath(id) {
|
|
918
1069
|
return path5.join(getCapabilitiesDir(), `${id}.json`);
|
|
919
1070
|
}
|
|
920
|
-
function getPluginManifestPath(
|
|
921
|
-
return path5.join(getProjectRoot2(), "node_modules",
|
|
1071
|
+
function getPluginManifestPath(pluginKey) {
|
|
1072
|
+
return path5.join(getProjectRoot2(), "node_modules", pluginKey, "manifest.json");
|
|
922
1073
|
}
|
|
923
1074
|
function capabilitiesDirExists() {
|
|
924
1075
|
return fs5.existsSync(getCapabilitiesDir());
|
|
@@ -950,17 +1101,17 @@ function readAllCapabilities() {
|
|
|
950
1101
|
const ids = listCapabilityIds();
|
|
951
1102
|
return ids.map((id) => readCapability(id));
|
|
952
1103
|
}
|
|
953
|
-
function readPluginManifest(
|
|
954
|
-
const manifestPath = getPluginManifestPath(
|
|
1104
|
+
function readPluginManifest(pluginKey) {
|
|
1105
|
+
const manifestPath = getPluginManifestPath(pluginKey);
|
|
955
1106
|
if (!fs5.existsSync(manifestPath)) {
|
|
956
|
-
throw new Error(`Plugin not installed: ${
|
|
1107
|
+
throw new Error(`Plugin not installed: ${pluginKey} (manifest.json not found)`);
|
|
957
1108
|
}
|
|
958
1109
|
try {
|
|
959
1110
|
const content = fs5.readFileSync(manifestPath, "utf-8");
|
|
960
1111
|
return JSON.parse(content);
|
|
961
1112
|
} catch (error) {
|
|
962
1113
|
if (error instanceof SyntaxError) {
|
|
963
|
-
throw new Error(`Invalid JSON in plugin manifest: ${
|
|
1114
|
+
throw new Error(`Invalid JSON in plugin manifest: ${pluginKey}/manifest.json`);
|
|
964
1115
|
}
|
|
965
1116
|
throw error;
|
|
966
1117
|
}
|
|
@@ -971,19 +1122,19 @@ function isDynamicSchema(schema) {
|
|
|
971
1122
|
function hasValidParamsSchema(paramsSchema) {
|
|
972
1123
|
return paramsSchema !== void 0 && Object.keys(paramsSchema).length > 0;
|
|
973
1124
|
}
|
|
974
|
-
async function loadPlugin(
|
|
1125
|
+
async function loadPlugin(pluginKey) {
|
|
975
1126
|
try {
|
|
976
|
-
const pluginPackage = (await import(
|
|
1127
|
+
const pluginPackage = (await import(pluginKey)).default;
|
|
977
1128
|
if (!pluginPackage || typeof pluginPackage.create !== "function") {
|
|
978
|
-
throw new Error(`Plugin ${
|
|
1129
|
+
throw new Error(`Plugin ${pluginKey} does not export a valid create function`);
|
|
979
1130
|
}
|
|
980
1131
|
return pluginPackage;
|
|
981
1132
|
} catch (error) {
|
|
982
1133
|
if (error.code === "MODULE_NOT_FOUND") {
|
|
983
|
-
throw new Error(`Plugin not installed: ${
|
|
1134
|
+
throw new Error(`Plugin not installed: ${pluginKey}`);
|
|
984
1135
|
}
|
|
985
1136
|
throw new Error(
|
|
986
|
-
`Failed to load plugin ${
|
|
1137
|
+
`Failed to load plugin ${pluginKey}: ${error instanceof Error ? error.message : String(error)}`
|
|
987
1138
|
);
|
|
988
1139
|
}
|
|
989
1140
|
}
|
|
@@ -997,16 +1148,16 @@ async function zodToJsonSchema(zodSchema) {
|
|
|
997
1148
|
}
|
|
998
1149
|
async function hydrateCapability(capability) {
|
|
999
1150
|
try {
|
|
1000
|
-
const manifest = readPluginManifest(capability.
|
|
1151
|
+
const manifest = readPluginManifest(capability.pluginKey);
|
|
1001
1152
|
if (!manifest.actions || manifest.actions.length === 0) {
|
|
1002
|
-
throw new Error(`Plugin ${capability.
|
|
1153
|
+
throw new Error(`Plugin ${capability.pluginKey} has no actions defined`);
|
|
1003
1154
|
}
|
|
1004
1155
|
const hasDynamicSchema = manifest.actions.some(
|
|
1005
1156
|
(action) => isDynamicSchema(action.inputSchema) || isDynamicSchema(action.outputSchema)
|
|
1006
1157
|
);
|
|
1007
1158
|
let pluginInstance = null;
|
|
1008
1159
|
if (hasDynamicSchema) {
|
|
1009
|
-
const plugin = await loadPlugin(capability.
|
|
1160
|
+
const plugin = await loadPlugin(capability.pluginKey);
|
|
1010
1161
|
pluginInstance = plugin.create(capability.formValue || {});
|
|
1011
1162
|
}
|
|
1012
1163
|
const actions = [];
|
|
@@ -1049,7 +1200,7 @@ async function hydrateCapability(capability) {
|
|
|
1049
1200
|
}
|
|
1050
1201
|
return {
|
|
1051
1202
|
id: capability.id,
|
|
1052
|
-
|
|
1203
|
+
pluginKey: capability.pluginKey,
|
|
1053
1204
|
pluginVersion: capability.pluginVersion,
|
|
1054
1205
|
name: capability.name,
|
|
1055
1206
|
description: capability.description,
|
|
@@ -1120,126 +1271,339 @@ async function listAll(summary) {
|
|
|
1120
1271
|
}
|
|
1121
1272
|
}
|
|
1122
1273
|
|
|
1123
|
-
// src/commands/capability/
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
// AI 相关
|
|
1133
|
-
"ai/generate_text": "@official_ai/text_generation",
|
|
1134
|
-
"ai/chat_completion": "@official_ai/chat_completion"
|
|
1135
|
-
// 其他
|
|
1136
|
-
// 根据实际需要添加更多映射
|
|
1137
|
-
};
|
|
1138
|
-
function mergeMapping(customMapping) {
|
|
1139
|
-
if (!customMapping) {
|
|
1140
|
-
return { ...DEFAULT_MAPPING };
|
|
1274
|
+
// src/commands/capability/index.ts
|
|
1275
|
+
var listCommand2 = {
|
|
1276
|
+
name: "list",
|
|
1277
|
+
description: "List capability configurations",
|
|
1278
|
+
aliases: ["ls"],
|
|
1279
|
+
register(program) {
|
|
1280
|
+
program.command(this.name).alias("ls").description(this.description).option("--summary", "Return raw capability config (without hydration)").option("--id <id>", "Specify capability ID").action(async (options) => {
|
|
1281
|
+
await list2(options);
|
|
1282
|
+
});
|
|
1141
1283
|
}
|
|
1142
|
-
|
|
1284
|
+
};
|
|
1285
|
+
var capabilityCommandGroup = {
|
|
1286
|
+
name: "capability",
|
|
1287
|
+
description: "Manage capability configurations",
|
|
1288
|
+
commands: [listCommand2]
|
|
1289
|
+
};
|
|
1290
|
+
|
|
1291
|
+
// src/commands/migration/version-manager.ts
|
|
1292
|
+
import fs6 from "fs";
|
|
1293
|
+
import path6 from "path";
|
|
1294
|
+
var PACKAGE_JSON = "package.json";
|
|
1295
|
+
var VERSION_FIELD = "migrationVersion";
|
|
1296
|
+
function getPackageJsonPath2() {
|
|
1297
|
+
return path6.join(process.cwd(), PACKAGE_JSON);
|
|
1143
1298
|
}
|
|
1144
|
-
function
|
|
1145
|
-
const
|
|
1146
|
-
if (!
|
|
1147
|
-
throw new Error(
|
|
1148
|
-
`Unknown sourceActionID: "${sourceActionID}". Please provide mapping via --mapping option or add to default mapping.`
|
|
1149
|
-
);
|
|
1299
|
+
function getCurrentVersion() {
|
|
1300
|
+
const pkgPath = getPackageJsonPath2();
|
|
1301
|
+
if (!fs6.existsSync(pkgPath)) {
|
|
1302
|
+
throw new Error("package.json not found");
|
|
1150
1303
|
}
|
|
1151
|
-
|
|
1304
|
+
const pkg2 = JSON.parse(fs6.readFileSync(pkgPath, "utf-8"));
|
|
1305
|
+
return pkg2[VERSION_FIELD] ?? 0;
|
|
1306
|
+
}
|
|
1307
|
+
function setCurrentVersion(version) {
|
|
1308
|
+
const pkgPath = getPackageJsonPath2();
|
|
1309
|
+
const pkg2 = JSON.parse(fs6.readFileSync(pkgPath, "utf-8"));
|
|
1310
|
+
pkg2[VERSION_FIELD] = version;
|
|
1311
|
+
fs6.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
|
|
1152
1312
|
}
|
|
1153
1313
|
|
|
1154
|
-
// src/commands/
|
|
1314
|
+
// src/commands/migration/versions/v001_capability/json-migrator/detector.ts
|
|
1315
|
+
import fs8 from "fs";
|
|
1316
|
+
import path8 from "path";
|
|
1317
|
+
|
|
1318
|
+
// src/commands/migration/versions/v001_capability/utils.ts
|
|
1155
1319
|
import fs7 from "fs";
|
|
1156
1320
|
import path7 from "path";
|
|
1321
|
+
var CAPABILITIES_DIR2 = "server/capabilities";
|
|
1322
|
+
function getProjectRoot3() {
|
|
1323
|
+
return process.cwd();
|
|
1324
|
+
}
|
|
1325
|
+
function getCapabilitiesDir2() {
|
|
1326
|
+
return path7.join(getProjectRoot3(), CAPABILITIES_DIR2);
|
|
1327
|
+
}
|
|
1328
|
+
function getPluginManifestPath2(pluginKey) {
|
|
1329
|
+
return path7.join(getProjectRoot3(), "node_modules", pluginKey, "manifest.json");
|
|
1330
|
+
}
|
|
1157
1331
|
|
|
1158
|
-
// src/commands/
|
|
1159
|
-
import fs6 from "fs";
|
|
1160
|
-
import path6 from "path";
|
|
1332
|
+
// src/commands/migration/versions/v001_capability/json-migrator/detector.ts
|
|
1161
1333
|
function detectJsonMigration() {
|
|
1162
|
-
const capabilitiesDir =
|
|
1163
|
-
const oldFilePath =
|
|
1164
|
-
if (!
|
|
1165
|
-
return {
|
|
1166
|
-
needsMigration: false,
|
|
1167
|
-
reason: "capabilities directory does not exist"
|
|
1168
|
-
};
|
|
1169
|
-
}
|
|
1170
|
-
if (!fs6.existsSync(oldFilePath)) {
|
|
1334
|
+
const capabilitiesDir = getCapabilitiesDir2();
|
|
1335
|
+
const oldFilePath = path8.join(capabilitiesDir, "capabilities.json");
|
|
1336
|
+
if (!fs8.existsSync(oldFilePath)) {
|
|
1171
1337
|
return {
|
|
1172
1338
|
needsMigration: false,
|
|
1173
|
-
reason: "capabilities.json not found
|
|
1339
|
+
reason: "capabilities.json not found"
|
|
1174
1340
|
};
|
|
1175
1341
|
}
|
|
1176
1342
|
try {
|
|
1177
|
-
const content =
|
|
1343
|
+
const content = fs8.readFileSync(oldFilePath, "utf-8");
|
|
1178
1344
|
const parsed = JSON.parse(content);
|
|
1179
|
-
|
|
1180
|
-
return {
|
|
1181
|
-
needsMigration: false,
|
|
1182
|
-
reason: "capabilities.json is not array format"
|
|
1183
|
-
};
|
|
1184
|
-
}
|
|
1185
|
-
if (parsed.length === 0) {
|
|
1186
|
-
return {
|
|
1187
|
-
needsMigration: false,
|
|
1188
|
-
reason: "capabilities.json is empty array"
|
|
1189
|
-
};
|
|
1190
|
-
}
|
|
1191
|
-
const firstItem = parsed[0];
|
|
1192
|
-
if (!firstItem.sourceActionID) {
|
|
1193
|
-
return {
|
|
1194
|
-
needsMigration: false,
|
|
1195
|
-
reason: "capabilities.json does not contain old format (missing sourceActionID)"
|
|
1196
|
-
};
|
|
1197
|
-
}
|
|
1345
|
+
const capabilities = Array.isArray(parsed) ? parsed : [];
|
|
1198
1346
|
return {
|
|
1199
1347
|
needsMigration: true,
|
|
1200
|
-
oldCapabilities:
|
|
1348
|
+
oldCapabilities: capabilities,
|
|
1201
1349
|
oldFilePath
|
|
1202
1350
|
};
|
|
1203
1351
|
} catch (error) {
|
|
1204
|
-
|
|
1352
|
+
return {
|
|
1353
|
+
needsMigration: true,
|
|
1354
|
+
oldCapabilities: [],
|
|
1355
|
+
oldFilePath
|
|
1356
|
+
};
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
// src/commands/migration/versions/v001_capability/check.ts
|
|
1361
|
+
async function check(options) {
|
|
1362
|
+
const detection = detectJsonMigration();
|
|
1363
|
+
if (!detection.needsMigration) {
|
|
1364
|
+
return {
|
|
1365
|
+
needsMigration: false,
|
|
1366
|
+
message: detection.reason || "No migration needed"
|
|
1367
|
+
};
|
|
1368
|
+
}
|
|
1369
|
+
const capabilityCount = detection.oldCapabilities?.length || 0;
|
|
1370
|
+
return {
|
|
1371
|
+
needsMigration: true,
|
|
1372
|
+
message: `found ${capabilityCount} capabilities to migrate`,
|
|
1373
|
+
items: [
|
|
1374
|
+
`server/capabilities/capabilities.json \u2192 server/capabilities/*.json`,
|
|
1375
|
+
`${capabilityCount} capabilities will be converted to new format`
|
|
1376
|
+
]
|
|
1377
|
+
};
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
// src/commands/migration/versions/v001_capability/json-migrator/index.ts
|
|
1381
|
+
import fs9 from "fs";
|
|
1382
|
+
import path9 from "path";
|
|
1383
|
+
|
|
1384
|
+
// src/commands/migration/versions/v001_capability/mapping.ts
|
|
1385
|
+
var DEFAULT_PLUGIN_VERSION = "1.0.0";
|
|
1386
|
+
var PLUGIN_MAPPING = {
|
|
1387
|
+
// 飞书相关
|
|
1388
|
+
"@official-plugins/send-feishu-message": {
|
|
1389
|
+
sourceActionID: "official_feishu/send_message",
|
|
1390
|
+
pluginVersion: "1.0.0",
|
|
1391
|
+
actionName: "send_feishu_message"
|
|
1392
|
+
},
|
|
1393
|
+
"@official-plugins/feishu-group-create": {
|
|
1394
|
+
sourceActionID: "official_feishu/create_group",
|
|
1395
|
+
pluginVersion: "1.0.0",
|
|
1396
|
+
actionName: "createGroup"
|
|
1397
|
+
},
|
|
1398
|
+
// AI 相关
|
|
1399
|
+
"@official-plugins/ai-text-generate": {
|
|
1400
|
+
sourceActionID: "official_ai/text_generate",
|
|
1401
|
+
pluginVersion: "1.0.0",
|
|
1402
|
+
actionName: "text_generate"
|
|
1403
|
+
},
|
|
1404
|
+
"@official-plugins/ai-image-understanding": {
|
|
1405
|
+
sourceActionID: "official_ai/image_understanding",
|
|
1406
|
+
pluginVersion: "1.0.0",
|
|
1407
|
+
actionName: "imageUnderstanding"
|
|
1408
|
+
},
|
|
1409
|
+
"@official-plugins/ai-text-to-image": {
|
|
1410
|
+
sourceActionID: "official_ai/image_generate",
|
|
1411
|
+
pluginVersion: "1.0.0",
|
|
1412
|
+
actionName: "textToImage"
|
|
1413
|
+
}
|
|
1414
|
+
};
|
|
1415
|
+
function getPluginInfoBySourceActionID(sourceActionID) {
|
|
1416
|
+
for (const [pluginKey, item] of Object.entries(PLUGIN_MAPPING)) {
|
|
1417
|
+
if (item.sourceActionID === sourceActionID) {
|
|
1205
1418
|
return {
|
|
1206
|
-
|
|
1207
|
-
|
|
1419
|
+
pluginKey,
|
|
1420
|
+
pluginVersion: item.pluginVersion ?? DEFAULT_PLUGIN_VERSION,
|
|
1421
|
+
actionName: item.actionName
|
|
1208
1422
|
};
|
|
1209
1423
|
}
|
|
1210
|
-
throw error;
|
|
1211
1424
|
}
|
|
1425
|
+
throw new Error(
|
|
1426
|
+
`Unknown sourceActionID: "${sourceActionID}". This sourceActionID is not in the built-in mapping. Please contact the developer to add it.`
|
|
1427
|
+
);
|
|
1428
|
+
}
|
|
1429
|
+
function getActionNameByPluginKey(pluginKey) {
|
|
1430
|
+
const item = PLUGIN_MAPPING[pluginKey];
|
|
1431
|
+
if (!item) {
|
|
1432
|
+
throw new Error(
|
|
1433
|
+
`Unknown pluginKey: "${pluginKey}". This pluginKey is not in the built-in mapping. Please contact the developer to add it.`
|
|
1434
|
+
);
|
|
1435
|
+
}
|
|
1436
|
+
return item.actionName;
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
// src/commands/migration/versions/v001_capability/json-migrator/form-value-transformers/capabilities/send-feishu-message.ts
|
|
1440
|
+
function transformSendFeishuMessage(input) {
|
|
1441
|
+
const actionInput = input;
|
|
1442
|
+
const cardContent = actionInput.card_content;
|
|
1443
|
+
const header = cardContent?.header;
|
|
1444
|
+
const bodyElements = cardContent?.body?.elements;
|
|
1445
|
+
const title = {
|
|
1446
|
+
title: header?.title?.content ?? "",
|
|
1447
|
+
titleColor: header?.template
|
|
1448
|
+
};
|
|
1449
|
+
const markdownElement = bodyElements?.find((el) => el.tag === "markdown");
|
|
1450
|
+
const content = markdownElement?.content ?? "";
|
|
1451
|
+
const columnSet = bodyElements?.find((el) => el.tag === "column_set");
|
|
1452
|
+
const buttons = columnSet?.columns?.flatMap(
|
|
1453
|
+
(column) => column.elements?.filter((el) => el.tag === "button").map((btn) => ({
|
|
1454
|
+
style: btn.type,
|
|
1455
|
+
text: btn.text?.content,
|
|
1456
|
+
url: btn.behaviors?.[0]?.default_url
|
|
1457
|
+
})) ?? []
|
|
1458
|
+
) ?? [];
|
|
1459
|
+
return {
|
|
1460
|
+
sender: "bot",
|
|
1461
|
+
receiverUserList: actionInput.receiver_user_list ?? [],
|
|
1462
|
+
receiverGroupList: actionInput.receiver_group_list ?? [],
|
|
1463
|
+
title,
|
|
1464
|
+
content,
|
|
1465
|
+
buttons
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
// src/commands/migration/versions/v001_capability/json-migrator/form-value-transformers/capabilities/feishu-group-create.ts
|
|
1470
|
+
function transformFeishuGroupCreate(input) {
|
|
1471
|
+
const actionInput = input;
|
|
1472
|
+
return {
|
|
1473
|
+
owner: actionInput.owner,
|
|
1474
|
+
members: actionInput.members ?? [],
|
|
1475
|
+
groupName: actionInput.group_name ?? "",
|
|
1476
|
+
groupDescription: actionInput.group_description ?? "",
|
|
1477
|
+
welcomeMessage: actionInput.welcome_message ?? ""
|
|
1478
|
+
};
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
// src/commands/migration/versions/v001_capability/json-migrator/form-value-transformers/utils.ts
|
|
1482
|
+
function convertToNumber(value, defaultValue) {
|
|
1483
|
+
if (value === void 0 || value === "") {
|
|
1484
|
+
return defaultValue;
|
|
1485
|
+
}
|
|
1486
|
+
const num = Number(value);
|
|
1487
|
+
return isNaN(num) ? defaultValue : num;
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
// src/commands/migration/versions/v001_capability/json-migrator/form-value-transformers/capabilities/ai-text-generate.ts
|
|
1491
|
+
var DEFAULT_MODEL_ID = "87";
|
|
1492
|
+
var DEFAULT_MAX_TOKENS = 8192;
|
|
1493
|
+
var DEFAULT_TEMPERATURE = 0.7;
|
|
1494
|
+
function transformAITextGenerate(input) {
|
|
1495
|
+
const actionInput = input;
|
|
1496
|
+
return {
|
|
1497
|
+
modelID: actionInput.model_id ?? DEFAULT_MODEL_ID,
|
|
1498
|
+
prompt: actionInput.prompt ?? "",
|
|
1499
|
+
modelParams: {
|
|
1500
|
+
maxTokens: convertToNumber(actionInput.max_tokens, DEFAULT_MAX_TOKENS),
|
|
1501
|
+
temperature: convertToNumber(actionInput.temperature, DEFAULT_TEMPERATURE)
|
|
1502
|
+
}
|
|
1503
|
+
};
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
// src/commands/migration/versions/v001_capability/json-migrator/form-value-transformers/capabilities/ai-image-understanding.ts
|
|
1507
|
+
var DEFAULT_MODEL_ID2 = "87";
|
|
1508
|
+
var DEFAULT_MAX_TOKENS2 = 8192;
|
|
1509
|
+
var DEFAULT_TEMPERATURE2 = 0.7;
|
|
1510
|
+
function transformAIImageUnderstanding(input) {
|
|
1511
|
+
const actionInput = input;
|
|
1512
|
+
return {
|
|
1513
|
+
prompt: actionInput.prompt ?? "",
|
|
1514
|
+
images: actionInput.image_list ?? [],
|
|
1515
|
+
modelID: actionInput.model_id ?? DEFAULT_MODEL_ID2,
|
|
1516
|
+
modelParams: {
|
|
1517
|
+
maxTokens: convertToNumber(actionInput.max_tokens, DEFAULT_MAX_TOKENS2),
|
|
1518
|
+
temperature: convertToNumber(actionInput.temperature, DEFAULT_TEMPERATURE2)
|
|
1519
|
+
}
|
|
1520
|
+
};
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
// src/commands/migration/versions/v001_capability/json-migrator/form-value-transformers/capabilities/ai-text-to-image.ts
|
|
1524
|
+
var DEFAULT_RATIO = "1:1";
|
|
1525
|
+
var DEFAULT_WATERMARK = true;
|
|
1526
|
+
function transformAITextToImage(input) {
|
|
1527
|
+
const actionInput = input;
|
|
1528
|
+
return {
|
|
1529
|
+
prompt: actionInput.prompt ?? "",
|
|
1530
|
+
ratio: actionInput.image_ratio ?? DEFAULT_RATIO,
|
|
1531
|
+
watermark: actionInput.watermark ?? DEFAULT_WATERMARK
|
|
1532
|
+
};
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
// src/commands/migration/versions/v001_capability/json-migrator/form-value-transformers/index.ts
|
|
1536
|
+
var TRANSFORMER_MAP = {
|
|
1537
|
+
"official_feishu/send_message": transformSendFeishuMessage,
|
|
1538
|
+
"official_feishu/create_group": transformFeishuGroupCreate,
|
|
1539
|
+
"official_ai/text_generate": transformAITextGenerate,
|
|
1540
|
+
"official_ai/image_understanding": transformAIImageUnderstanding,
|
|
1541
|
+
"official_ai/image_generate": transformAITextToImage
|
|
1542
|
+
};
|
|
1543
|
+
function transformFormValue(sourceActionID, actionInput) {
|
|
1544
|
+
const transformer = TRANSFORMER_MAP[sourceActionID];
|
|
1545
|
+
if (!transformer) {
|
|
1546
|
+
throw new Error(
|
|
1547
|
+
`No formValue transformer found for sourceActionID: "${sourceActionID}". Please implement the transformer and add it to TRANSFORMER_MAP.`
|
|
1548
|
+
);
|
|
1549
|
+
}
|
|
1550
|
+
return transformer(actionInput);
|
|
1212
1551
|
}
|
|
1213
1552
|
|
|
1214
|
-
// src/commands/
|
|
1215
|
-
function transformCapability(old
|
|
1216
|
-
const
|
|
1553
|
+
// src/commands/migration/versions/v001_capability/json-migrator/transformer.ts
|
|
1554
|
+
function transformCapability(old) {
|
|
1555
|
+
const { pluginKey, pluginVersion } = getPluginInfoBySourceActionID(old.sourceActionID);
|
|
1556
|
+
const formValue = transformFormValue(old.sourceActionID, old.actionInput);
|
|
1217
1557
|
return {
|
|
1218
1558
|
id: old.id,
|
|
1219
|
-
|
|
1220
|
-
pluginVersion
|
|
1559
|
+
pluginKey,
|
|
1560
|
+
pluginVersion,
|
|
1221
1561
|
name: old.name,
|
|
1222
1562
|
description: old.desc,
|
|
1223
1563
|
paramsSchema: old.inputSchema,
|
|
1224
|
-
formValue
|
|
1564
|
+
formValue,
|
|
1225
1565
|
createdAt: old.createdAt,
|
|
1226
1566
|
updatedAt: old.updatedAt
|
|
1227
1567
|
};
|
|
1228
1568
|
}
|
|
1229
|
-
function transformCapabilities(oldCapabilities
|
|
1230
|
-
return oldCapabilities.map((old) => transformCapability(old
|
|
1569
|
+
function transformCapabilities(oldCapabilities) {
|
|
1570
|
+
return oldCapabilities.map((old) => transformCapability(old));
|
|
1231
1571
|
}
|
|
1232
1572
|
|
|
1233
|
-
// src/commands/
|
|
1573
|
+
// src/commands/migration/versions/v001_capability/json-migrator/index.ts
|
|
1574
|
+
function loadExistingCapabilities() {
|
|
1575
|
+
const capabilitiesDir = getCapabilitiesDir2();
|
|
1576
|
+
if (!fs9.existsSync(capabilitiesDir)) {
|
|
1577
|
+
return [];
|
|
1578
|
+
}
|
|
1579
|
+
const files = fs9.readdirSync(capabilitiesDir);
|
|
1580
|
+
const capabilities = [];
|
|
1581
|
+
for (const file of files) {
|
|
1582
|
+
if (file === "capabilities.json" || !file.endsWith(".json")) {
|
|
1583
|
+
continue;
|
|
1584
|
+
}
|
|
1585
|
+
try {
|
|
1586
|
+
const filePath = path9.join(capabilitiesDir, file);
|
|
1587
|
+
const content = fs9.readFileSync(filePath, "utf-8");
|
|
1588
|
+
const capability = JSON.parse(content);
|
|
1589
|
+
if (capability.id && capability.pluginKey) {
|
|
1590
|
+
capabilities.push(capability);
|
|
1591
|
+
}
|
|
1592
|
+
} catch {
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
return capabilities;
|
|
1596
|
+
}
|
|
1234
1597
|
async function migrateJsonFiles(options) {
|
|
1235
1598
|
const detection = detectJsonMigration();
|
|
1236
1599
|
if (!detection.needsMigration) {
|
|
1600
|
+
const existingCapabilities = loadExistingCapabilities();
|
|
1237
1601
|
return {
|
|
1238
1602
|
success: true,
|
|
1239
1603
|
skipped: true,
|
|
1240
1604
|
reason: detection.reason,
|
|
1241
|
-
count:
|
|
1242
|
-
capabilities:
|
|
1605
|
+
count: existingCapabilities.length,
|
|
1606
|
+
capabilities: existingCapabilities
|
|
1243
1607
|
};
|
|
1244
1608
|
}
|
|
1245
1609
|
const { oldCapabilities, oldFilePath } = detection;
|
|
@@ -1254,7 +1618,7 @@ async function migrateJsonFiles(options) {
|
|
|
1254
1618
|
}
|
|
1255
1619
|
let newCapabilities;
|
|
1256
1620
|
try {
|
|
1257
|
-
newCapabilities = transformCapabilities(oldCapabilities
|
|
1621
|
+
newCapabilities = transformCapabilities(oldCapabilities);
|
|
1258
1622
|
} catch (error) {
|
|
1259
1623
|
return {
|
|
1260
1624
|
success: false,
|
|
@@ -1267,7 +1631,7 @@ async function migrateJsonFiles(options) {
|
|
|
1267
1631
|
if (options.dryRun) {
|
|
1268
1632
|
console.log(` Would create ${newCapabilities.length} capability files:`);
|
|
1269
1633
|
for (const cap of newCapabilities) {
|
|
1270
|
-
console.log(` - ${cap.id}.json (
|
|
1634
|
+
console.log(` - ${cap.id}.json (pluginKey: ${cap.pluginKey})`);
|
|
1271
1635
|
}
|
|
1272
1636
|
return {
|
|
1273
1637
|
success: true,
|
|
@@ -1276,48 +1640,45 @@ async function migrateJsonFiles(options) {
|
|
|
1276
1640
|
capabilities: newCapabilities
|
|
1277
1641
|
};
|
|
1278
1642
|
}
|
|
1279
|
-
const capabilitiesDir =
|
|
1643
|
+
const capabilitiesDir = getCapabilitiesDir2();
|
|
1280
1644
|
for (const cap of newCapabilities) {
|
|
1281
|
-
const filePath =
|
|
1645
|
+
const filePath = path9.join(capabilitiesDir, `${cap.id}.json`);
|
|
1282
1646
|
const content = JSON.stringify(cap, null, 2);
|
|
1283
|
-
|
|
1647
|
+
fs9.writeFileSync(filePath, content, "utf-8");
|
|
1284
1648
|
console.log(` \u2713 Created: ${cap.id}.json`);
|
|
1285
1649
|
}
|
|
1286
|
-
const backupPath = oldFilePath + ".backup";
|
|
1287
|
-
fs7.renameSync(oldFilePath, backupPath);
|
|
1288
1650
|
return {
|
|
1289
1651
|
success: true,
|
|
1290
1652
|
skipped: false,
|
|
1291
1653
|
count: newCapabilities.length,
|
|
1292
|
-
capabilities: newCapabilities
|
|
1293
|
-
backupPath
|
|
1654
|
+
capabilities: newCapabilities
|
|
1294
1655
|
};
|
|
1295
1656
|
}
|
|
1296
1657
|
|
|
1297
|
-
// src/commands/
|
|
1298
|
-
import
|
|
1299
|
-
function isPluginInstalled2(
|
|
1300
|
-
const manifestPath =
|
|
1301
|
-
return
|
|
1658
|
+
// src/commands/migration/versions/v001_capability/plugin-installer/detector.ts
|
|
1659
|
+
import fs10 from "fs";
|
|
1660
|
+
function isPluginInstalled2(pluginKey) {
|
|
1661
|
+
const manifestPath = getPluginManifestPath2(pluginKey);
|
|
1662
|
+
return fs10.existsSync(manifestPath);
|
|
1302
1663
|
}
|
|
1303
1664
|
function detectPluginsToInstall(capabilities) {
|
|
1304
|
-
const
|
|
1665
|
+
const pluginKeys = /* @__PURE__ */ new Set();
|
|
1305
1666
|
for (const cap of capabilities) {
|
|
1306
|
-
|
|
1667
|
+
pluginKeys.add(cap.pluginKey);
|
|
1307
1668
|
}
|
|
1308
1669
|
const toInstall = [];
|
|
1309
1670
|
const alreadyInstalled = [];
|
|
1310
|
-
for (const
|
|
1311
|
-
if (isPluginInstalled2(
|
|
1312
|
-
alreadyInstalled.push(
|
|
1671
|
+
for (const pluginKey of pluginKeys) {
|
|
1672
|
+
if (isPluginInstalled2(pluginKey)) {
|
|
1673
|
+
alreadyInstalled.push(pluginKey);
|
|
1313
1674
|
} else {
|
|
1314
|
-
toInstall.push(
|
|
1675
|
+
toInstall.push(pluginKey);
|
|
1315
1676
|
}
|
|
1316
1677
|
}
|
|
1317
1678
|
return { toInstall, alreadyInstalled };
|
|
1318
1679
|
}
|
|
1319
1680
|
|
|
1320
|
-
// src/commands/
|
|
1681
|
+
// src/commands/migration/versions/v001_capability/plugin-installer/index.ts
|
|
1321
1682
|
async function installPlugins(capabilities, options) {
|
|
1322
1683
|
const detection = detectPluginsToInstall(capabilities);
|
|
1323
1684
|
const result = {
|
|
@@ -1330,15 +1691,15 @@ async function installPlugins(capabilities, options) {
|
|
|
1330
1691
|
}
|
|
1331
1692
|
if (options.dryRun) {
|
|
1332
1693
|
console.log(` Would install ${detection.toInstall.length} plugins:`);
|
|
1333
|
-
for (const
|
|
1334
|
-
console.log(` - ${
|
|
1694
|
+
for (const pluginKey of detection.toInstall) {
|
|
1695
|
+
console.log(` - ${pluginKey}`);
|
|
1335
1696
|
}
|
|
1336
1697
|
result.installed = detection.toInstall;
|
|
1337
1698
|
return result;
|
|
1338
1699
|
}
|
|
1339
1700
|
console.log(` \u2B07 Installing ${detection.toInstall.length} plugins...`);
|
|
1340
|
-
for (const
|
|
1341
|
-
console.log(` - ${
|
|
1701
|
+
for (const pluginKey of detection.toInstall) {
|
|
1702
|
+
console.log(` - ${pluginKey}`);
|
|
1342
1703
|
}
|
|
1343
1704
|
try {
|
|
1344
1705
|
const originalExit = process.exit;
|
|
@@ -1353,9 +1714,9 @@ async function installPlugins(capabilities, options) {
|
|
|
1353
1714
|
await install(detection.toInstall);
|
|
1354
1715
|
result.installed = detection.toInstall;
|
|
1355
1716
|
} catch (error) {
|
|
1356
|
-
for (const
|
|
1717
|
+
for (const pluginKey of detection.toInstall) {
|
|
1357
1718
|
result.failed.push({
|
|
1358
|
-
|
|
1719
|
+
pluginKey,
|
|
1359
1720
|
error: error instanceof Error ? error.message : String(error)
|
|
1360
1721
|
});
|
|
1361
1722
|
}
|
|
@@ -1363,9 +1724,9 @@ async function installPlugins(capabilities, options) {
|
|
|
1363
1724
|
process.exit = originalExit;
|
|
1364
1725
|
}
|
|
1365
1726
|
} catch (error) {
|
|
1366
|
-
for (const
|
|
1727
|
+
for (const pluginKey of detection.toInstall) {
|
|
1367
1728
|
result.failed.push({
|
|
1368
|
-
|
|
1729
|
+
pluginKey,
|
|
1369
1730
|
error: error instanceof Error ? error.message : String(error)
|
|
1370
1731
|
});
|
|
1371
1732
|
}
|
|
@@ -1373,14 +1734,13 @@ async function installPlugins(capabilities, options) {
|
|
|
1373
1734
|
return result;
|
|
1374
1735
|
}
|
|
1375
1736
|
|
|
1376
|
-
// src/commands/
|
|
1377
|
-
import
|
|
1378
|
-
import
|
|
1379
|
-
import * as ts4 from "typescript";
|
|
1737
|
+
// src/commands/migration/versions/v001_capability/code-migrator/index.ts
|
|
1738
|
+
import path11 from "path";
|
|
1739
|
+
import { Project } from "ts-morph";
|
|
1380
1740
|
|
|
1381
|
-
// src/commands/
|
|
1382
|
-
import
|
|
1383
|
-
import
|
|
1741
|
+
// src/commands/migration/versions/v001_capability/code-migrator/scanner.ts
|
|
1742
|
+
import fs11 from "fs";
|
|
1743
|
+
import path10 from "path";
|
|
1384
1744
|
var EXCLUDED_DIRS = [
|
|
1385
1745
|
"node_modules",
|
|
1386
1746
|
"dist",
|
|
@@ -1395,9 +1755,9 @@ var EXCLUDED_PATTERNS = [
|
|
|
1395
1755
|
/\.d\.ts$/
|
|
1396
1756
|
];
|
|
1397
1757
|
function scanDirectory(dir, files = []) {
|
|
1398
|
-
const entries =
|
|
1758
|
+
const entries = fs11.readdirSync(dir, { withFileTypes: true });
|
|
1399
1759
|
for (const entry of entries) {
|
|
1400
|
-
const fullPath =
|
|
1760
|
+
const fullPath = path10.join(dir, entry.name);
|
|
1401
1761
|
if (entry.isDirectory()) {
|
|
1402
1762
|
if (EXCLUDED_DIRS.includes(entry.name)) {
|
|
1403
1763
|
continue;
|
|
@@ -1413,14 +1773,14 @@ function scanDirectory(dir, files = []) {
|
|
|
1413
1773
|
return files;
|
|
1414
1774
|
}
|
|
1415
1775
|
function scanServerFiles() {
|
|
1416
|
-
const serverDir =
|
|
1417
|
-
if (!
|
|
1776
|
+
const serverDir = path10.join(getProjectRoot3(), "server");
|
|
1777
|
+
if (!fs11.existsSync(serverDir)) {
|
|
1418
1778
|
return [];
|
|
1419
1779
|
}
|
|
1420
1780
|
return scanDirectory(serverDir);
|
|
1421
1781
|
}
|
|
1422
1782
|
function hasCapabilityImport(filePath) {
|
|
1423
|
-
const content =
|
|
1783
|
+
const content = fs11.readFileSync(filePath, "utf-8");
|
|
1424
1784
|
return /import\s+.*from\s+['"][^'"]*capabilities[^'"]*['"]/.test(content);
|
|
1425
1785
|
}
|
|
1426
1786
|
function scanFilesToMigrate() {
|
|
@@ -1428,178 +1788,154 @@ function scanFilesToMigrate() {
|
|
|
1428
1788
|
return allFiles.filter(hasCapabilityImport);
|
|
1429
1789
|
}
|
|
1430
1790
|
|
|
1431
|
-
// src/commands/
|
|
1432
|
-
import * as ts from "typescript";
|
|
1791
|
+
// src/commands/migration/versions/v001_capability/code-migrator/analyzers/import-analyzer.ts
|
|
1433
1792
|
function extractCapabilityId(importPath) {
|
|
1434
1793
|
const match = importPath.match(/capabilities\/([^/]+)$/);
|
|
1435
|
-
|
|
1436
|
-
return match[1];
|
|
1437
|
-
}
|
|
1438
|
-
return null;
|
|
1794
|
+
return match ? match[1] : null;
|
|
1439
1795
|
}
|
|
1440
1796
|
function isCapabilityImport(importPath) {
|
|
1441
1797
|
return importPath.includes("capabilities/") || importPath.includes("capabilities\\");
|
|
1442
1798
|
}
|
|
1443
1799
|
function analyzeImports(sourceFile) {
|
|
1444
1800
|
const imports = [];
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1801
|
+
const importDeclarations = sourceFile.getImportDeclarations();
|
|
1802
|
+
for (const importDecl of importDeclarations) {
|
|
1803
|
+
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
1804
|
+
if (!isCapabilityImport(moduleSpecifier)) {
|
|
1805
|
+
continue;
|
|
1806
|
+
}
|
|
1807
|
+
const capabilityId = extractCapabilityId(moduleSpecifier);
|
|
1808
|
+
if (!capabilityId) {
|
|
1809
|
+
continue;
|
|
1810
|
+
}
|
|
1811
|
+
const namedImports = importDecl.getNamedImports();
|
|
1812
|
+
for (const namedImport of namedImports) {
|
|
1813
|
+
const importName = namedImport.getAliasNode()?.getText() || namedImport.getName();
|
|
1814
|
+
imports.push({
|
|
1815
|
+
importName,
|
|
1816
|
+
capabilityId,
|
|
1817
|
+
start: importDecl.getStart(),
|
|
1818
|
+
end: importDecl.getEnd(),
|
|
1819
|
+
text: importDecl.getText()
|
|
1820
|
+
});
|
|
1821
|
+
}
|
|
1822
|
+
const namespaceImport = importDecl.getNamespaceImport();
|
|
1823
|
+
if (namespaceImport) {
|
|
1824
|
+
imports.push({
|
|
1825
|
+
importName: namespaceImport.getText(),
|
|
1826
|
+
capabilityId,
|
|
1827
|
+
start: importDecl.getStart(),
|
|
1828
|
+
end: importDecl.getEnd(),
|
|
1829
|
+
text: importDecl.getText()
|
|
1830
|
+
});
|
|
1831
|
+
}
|
|
1832
|
+
const defaultImport = importDecl.getDefaultImport();
|
|
1833
|
+
if (defaultImport) {
|
|
1834
|
+
imports.push({
|
|
1835
|
+
importName: defaultImport.getText(),
|
|
1836
|
+
capabilityId,
|
|
1837
|
+
start: importDecl.getStart(),
|
|
1838
|
+
end: importDecl.getEnd(),
|
|
1839
|
+
text: importDecl.getText()
|
|
1840
|
+
});
|
|
1488
1841
|
}
|
|
1489
|
-
ts.forEachChild(node, visit);
|
|
1490
1842
|
}
|
|
1491
|
-
visit(sourceFile);
|
|
1492
1843
|
return imports;
|
|
1493
1844
|
}
|
|
1494
1845
|
|
|
1495
|
-
// src/commands/
|
|
1496
|
-
import
|
|
1846
|
+
// src/commands/migration/versions/v001_capability/code-migrator/analyzers/call-site-analyzer.ts
|
|
1847
|
+
import { SyntaxKind } from "ts-morph";
|
|
1497
1848
|
function analyzeCallSites(sourceFile, imports) {
|
|
1498
1849
|
const callSites = [];
|
|
1499
1850
|
const importMap = /* @__PURE__ */ new Map();
|
|
1500
1851
|
for (const imp of imports) {
|
|
1501
1852
|
importMap.set(imp.importName, imp.capabilityId);
|
|
1502
1853
|
}
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
const
|
|
1523
|
-
if (
|
|
1524
|
-
const
|
|
1854
|
+
const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
|
|
1855
|
+
for (const callExpr of callExpressions) {
|
|
1856
|
+
const expression = callExpr.getExpression();
|
|
1857
|
+
if (expression.getKind() === SyntaxKind.Identifier) {
|
|
1858
|
+
const functionName = expression.getText();
|
|
1859
|
+
const capabilityId = importMap.get(functionName);
|
|
1860
|
+
if (capabilityId) {
|
|
1861
|
+
callSites.push({
|
|
1862
|
+
functionName,
|
|
1863
|
+
capabilityId,
|
|
1864
|
+
start: callExpr.getStart(),
|
|
1865
|
+
end: callExpr.getEnd(),
|
|
1866
|
+
line: callExpr.getStartLineNumber(),
|
|
1867
|
+
text: callExpr.getText()
|
|
1868
|
+
});
|
|
1869
|
+
}
|
|
1870
|
+
} else if (expression.getKind() === SyntaxKind.PropertyAccessExpression) {
|
|
1871
|
+
const propAccess = expression.asKind(SyntaxKind.PropertyAccessExpression);
|
|
1872
|
+
if (propAccess) {
|
|
1873
|
+
const objectExpr = propAccess.getExpression();
|
|
1874
|
+
if (objectExpr.getKind() === SyntaxKind.Identifier) {
|
|
1875
|
+
const objectName = objectExpr.getText();
|
|
1876
|
+
const capabilityId = importMap.get(objectName);
|
|
1525
1877
|
if (capabilityId) {
|
|
1526
|
-
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
1527
1878
|
callSites.push({
|
|
1528
|
-
functionName: `${objectName
|
|
1879
|
+
functionName: `${objectName}.${propAccess.getName()}`,
|
|
1529
1880
|
capabilityId,
|
|
1530
|
-
start:
|
|
1531
|
-
end:
|
|
1532
|
-
line:
|
|
1533
|
-
text:
|
|
1881
|
+
start: callExpr.getStart(),
|
|
1882
|
+
end: callExpr.getEnd(),
|
|
1883
|
+
line: callExpr.getStartLineNumber(),
|
|
1884
|
+
text: callExpr.getText()
|
|
1534
1885
|
});
|
|
1535
1886
|
}
|
|
1536
1887
|
}
|
|
1537
1888
|
}
|
|
1538
1889
|
}
|
|
1539
|
-
ts2.forEachChild(node, visit);
|
|
1540
1890
|
}
|
|
1541
|
-
visit(sourceFile);
|
|
1542
1891
|
return callSites;
|
|
1543
1892
|
}
|
|
1544
1893
|
|
|
1545
|
-
// src/commands/
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
const decorators = ts3.getDecorators(node);
|
|
1549
|
-
if (!decorators) {
|
|
1550
|
-
return false;
|
|
1551
|
-
}
|
|
1894
|
+
// src/commands/migration/versions/v001_capability/code-migrator/analyzers/class-analyzer.ts
|
|
1895
|
+
function hasDecorator(classDecl, decoratorName) {
|
|
1896
|
+
const decorators = classDecl.getDecorators();
|
|
1552
1897
|
return decorators.some((decorator) => {
|
|
1553
|
-
const
|
|
1554
|
-
|
|
1555
|
-
return expression.text === decoratorName;
|
|
1556
|
-
}
|
|
1557
|
-
if (ts3.isCallExpression(expression) && ts3.isIdentifier(expression.expression)) {
|
|
1558
|
-
return expression.expression.text === decoratorName;
|
|
1559
|
-
}
|
|
1560
|
-
return false;
|
|
1898
|
+
const name = decorator.getName();
|
|
1899
|
+
return name === decoratorName;
|
|
1561
1900
|
});
|
|
1562
1901
|
}
|
|
1563
1902
|
function analyzeClass(sourceFile) {
|
|
1903
|
+
const classes = sourceFile.getClasses();
|
|
1564
1904
|
let classInfo;
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
break;
|
|
1594
|
-
}
|
|
1595
|
-
}
|
|
1596
|
-
if (isInjectable || isController || !classInfo) {
|
|
1597
|
-
classInfo = info;
|
|
1905
|
+
for (const classDecl of classes) {
|
|
1906
|
+
const name = classDecl.getName();
|
|
1907
|
+
if (!name) continue;
|
|
1908
|
+
const isInjectable = hasDecorator(classDecl, "Injectable");
|
|
1909
|
+
const isController = hasDecorator(classDecl, "Controller");
|
|
1910
|
+
if (classInfo && classInfo.isInjectable && !isInjectable && !isController) {
|
|
1911
|
+
continue;
|
|
1912
|
+
}
|
|
1913
|
+
const info = {
|
|
1914
|
+
name,
|
|
1915
|
+
isInjectable,
|
|
1916
|
+
isController,
|
|
1917
|
+
constructorParamCount: 0
|
|
1918
|
+
};
|
|
1919
|
+
const classBody = classDecl.getChildSyntaxListOrThrow();
|
|
1920
|
+
info.classBodyStart = classBody.getStart();
|
|
1921
|
+
const constructors = classDecl.getConstructors();
|
|
1922
|
+
if (constructors.length > 0) {
|
|
1923
|
+
const ctor = constructors[0];
|
|
1924
|
+
info.constructorParamCount = ctor.getParameters().length;
|
|
1925
|
+
info.constructorStart = ctor.getStart();
|
|
1926
|
+
info.constructorEnd = ctor.getEnd();
|
|
1927
|
+
const params = ctor.getParameters();
|
|
1928
|
+
if (params.length > 0) {
|
|
1929
|
+
const lastParam = params[params.length - 1];
|
|
1930
|
+
info.constructorParamsEnd = lastParam.getEnd();
|
|
1931
|
+
} else {
|
|
1932
|
+
info.constructorParamsEnd = ctor.getStart();
|
|
1598
1933
|
}
|
|
1599
1934
|
}
|
|
1600
|
-
|
|
1935
|
+
if (isInjectable || isController || !classInfo) {
|
|
1936
|
+
classInfo = info;
|
|
1937
|
+
}
|
|
1601
1938
|
}
|
|
1602
|
-
visit(sourceFile);
|
|
1603
1939
|
return classInfo;
|
|
1604
1940
|
}
|
|
1605
1941
|
function canAutoMigrate(classInfo) {
|
|
@@ -1618,120 +1954,208 @@ function canAutoMigrate(classInfo) {
|
|
|
1618
1954
|
return { canMigrate: true };
|
|
1619
1955
|
}
|
|
1620
1956
|
|
|
1621
|
-
// src/commands/
|
|
1622
|
-
var
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
result = before + trimmedAfter;
|
|
1957
|
+
// src/commands/migration/versions/v001_capability/code-migrator/transformers/import-transformer.ts
|
|
1958
|
+
var CORE_MODULE = "@lark-apaas/fullstack-nestjs-core";
|
|
1959
|
+
var NESTJS_COMMON_MODULE = "@nestjs/common";
|
|
1960
|
+
function hasCapabilityServiceImport(sourceFile) {
|
|
1961
|
+
const imports = sourceFile.getImportDeclarations();
|
|
1962
|
+
for (const importDecl of imports) {
|
|
1963
|
+
if (importDecl.getModuleSpecifierValue() === CORE_MODULE) {
|
|
1964
|
+
const namedImports = importDecl.getNamedImports();
|
|
1965
|
+
if (namedImports.some((ni) => ni.getName() === "CapabilityService")) {
|
|
1966
|
+
return true;
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
return false;
|
|
1971
|
+
}
|
|
1972
|
+
function hasInjectImport(sourceFile) {
|
|
1973
|
+
const imports = sourceFile.getImportDeclarations();
|
|
1974
|
+
for (const importDecl of imports) {
|
|
1975
|
+
if (importDecl.getModuleSpecifierValue() === NESTJS_COMMON_MODULE) {
|
|
1976
|
+
const namedImports = importDecl.getNamedImports();
|
|
1977
|
+
if (namedImports.some((ni) => ni.getName() === "Inject")) {
|
|
1978
|
+
return true;
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1646
1981
|
}
|
|
1982
|
+
return false;
|
|
1983
|
+
}
|
|
1984
|
+
function transformImports(sourceFile, capabilityImports) {
|
|
1647
1985
|
let importAdded = false;
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1986
|
+
let importsRemoved = 0;
|
|
1987
|
+
const importPaths = new Set(capabilityImports.map((imp) => {
|
|
1988
|
+
const match = imp.text.match(/from\s+['"]([^'"]+)['"]/);
|
|
1989
|
+
return match ? match[1] : "";
|
|
1990
|
+
}));
|
|
1991
|
+
const importDeclarations = sourceFile.getImportDeclarations();
|
|
1992
|
+
for (const importDecl of importDeclarations) {
|
|
1993
|
+
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
1994
|
+
if (importPaths.has(moduleSpecifier)) {
|
|
1995
|
+
importDecl.remove();
|
|
1996
|
+
importsRemoved++;
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
if (!hasCapabilityServiceImport(sourceFile)) {
|
|
2000
|
+
const existingCoreImport = sourceFile.getImportDeclaration(CORE_MODULE);
|
|
2001
|
+
if (existingCoreImport) {
|
|
2002
|
+
const namedImports = existingCoreImport.getNamedImports();
|
|
2003
|
+
const existingNames = namedImports.map((ni) => ni.getName());
|
|
2004
|
+
if (!existingNames.includes("CapabilityService")) {
|
|
2005
|
+
existingCoreImport.addNamedImport("CapabilityService");
|
|
2006
|
+
}
|
|
2007
|
+
if (!existingNames.includes("migrationAdaptor")) {
|
|
2008
|
+
existingCoreImport.addNamedImport("migrationAdaptor");
|
|
2009
|
+
}
|
|
2010
|
+
} else {
|
|
2011
|
+
sourceFile.addImportDeclaration({
|
|
2012
|
+
moduleSpecifier: CORE_MODULE,
|
|
2013
|
+
namedImports: ["CapabilityService", "migrationAdaptor"]
|
|
2014
|
+
});
|
|
2015
|
+
}
|
|
2016
|
+
importAdded = true;
|
|
2017
|
+
}
|
|
2018
|
+
if (!hasInjectImport(sourceFile)) {
|
|
2019
|
+
const existingNestImport = sourceFile.getImportDeclaration(NESTJS_COMMON_MODULE);
|
|
2020
|
+
if (existingNestImport) {
|
|
2021
|
+
const namedImports = existingNestImport.getNamedImports();
|
|
2022
|
+
const existingNames = namedImports.map((ni) => ni.getName());
|
|
2023
|
+
if (!existingNames.includes("Inject")) {
|
|
2024
|
+
existingNestImport.addNamedImport("Inject");
|
|
2025
|
+
}
|
|
1652
2026
|
} else {
|
|
1653
|
-
|
|
2027
|
+
sourceFile.addImportDeclaration({
|
|
2028
|
+
moduleSpecifier: NESTJS_COMMON_MODULE,
|
|
2029
|
+
namedImports: ["Inject"]
|
|
2030
|
+
});
|
|
1654
2031
|
}
|
|
1655
2032
|
importAdded = true;
|
|
1656
2033
|
}
|
|
1657
|
-
return {
|
|
2034
|
+
return { importAdded, importsRemoved };
|
|
1658
2035
|
}
|
|
1659
2036
|
|
|
1660
|
-
// src/commands/
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
2037
|
+
// src/commands/migration/versions/v001_capability/code-migrator/transformers/injection-transformer.ts
|
|
2038
|
+
import { Scope } from "ts-morph";
|
|
2039
|
+
var PARAM_NAME = "capabilityService";
|
|
2040
|
+
var PARAM_TYPE = "CapabilityService";
|
|
2041
|
+
function hasCapabilityServiceInjection(sourceFile) {
|
|
2042
|
+
const classes = sourceFile.getClasses();
|
|
2043
|
+
for (const classDecl of classes) {
|
|
2044
|
+
const constructors = classDecl.getConstructors();
|
|
2045
|
+
for (const ctor of constructors) {
|
|
2046
|
+
const params = ctor.getParameters();
|
|
2047
|
+
for (const param of params) {
|
|
2048
|
+
if (param.getName() === PARAM_NAME) {
|
|
2049
|
+
return true;
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
return false;
|
|
2055
|
+
}
|
|
2056
|
+
function findInjectableClass(sourceFile) {
|
|
2057
|
+
const classes = sourceFile.getClasses();
|
|
2058
|
+
for (const classDecl of classes) {
|
|
2059
|
+
const decorators = classDecl.getDecorators();
|
|
2060
|
+
const isInjectable = decorators.some((d) => d.getName() === "Injectable");
|
|
2061
|
+
const isController = decorators.some((d) => d.getName() === "Controller");
|
|
2062
|
+
if (isInjectable || isController) {
|
|
2063
|
+
return classDecl;
|
|
1685
2064
|
}
|
|
1686
2065
|
}
|
|
1687
|
-
return
|
|
2066
|
+
return void 0;
|
|
2067
|
+
}
|
|
2068
|
+
function addInjection(sourceFile) {
|
|
2069
|
+
if (hasCapabilityServiceInjection(sourceFile)) {
|
|
2070
|
+
return { injectionAdded: false };
|
|
2071
|
+
}
|
|
2072
|
+
const classDecl = findInjectableClass(sourceFile);
|
|
2073
|
+
if (!classDecl) {
|
|
2074
|
+
return { injectionAdded: false };
|
|
2075
|
+
}
|
|
2076
|
+
const constructors = classDecl.getConstructors();
|
|
2077
|
+
if (constructors.length > 0) {
|
|
2078
|
+
const ctor = constructors[0];
|
|
2079
|
+
ctor.addParameter({
|
|
2080
|
+
name: PARAM_NAME,
|
|
2081
|
+
type: PARAM_TYPE,
|
|
2082
|
+
scope: Scope.Private,
|
|
2083
|
+
isReadonly: true,
|
|
2084
|
+
decorators: [{ name: "Inject", arguments: [] }]
|
|
2085
|
+
});
|
|
2086
|
+
} else {
|
|
2087
|
+
classDecl.addConstructor({
|
|
2088
|
+
parameters: [{
|
|
2089
|
+
name: PARAM_NAME,
|
|
2090
|
+
type: PARAM_TYPE,
|
|
2091
|
+
scope: Scope.Private,
|
|
2092
|
+
isReadonly: true,
|
|
2093
|
+
decorators: [{ name: "Inject", arguments: [] }]
|
|
2094
|
+
}]
|
|
2095
|
+
});
|
|
2096
|
+
}
|
|
2097
|
+
return { injectionAdded: true };
|
|
1688
2098
|
}
|
|
1689
2099
|
|
|
1690
|
-
// src/commands/
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
}
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
}
|
|
1705
|
-
return `this.capabilityService.load('${capabilityId}').call('run', ${args})`;
|
|
1706
|
-
}
|
|
1707
|
-
function transformCallSites(content, callSites) {
|
|
1708
|
-
const sortedCallSites = [...callSites].sort((a, b) => b.start - a.start);
|
|
1709
|
-
let result = content;
|
|
2100
|
+
// src/commands/migration/versions/v001_capability/code-migrator/transformers/call-site-transformer.ts
|
|
2101
|
+
import { SyntaxKind as SyntaxKind2 } from "ts-morph";
|
|
2102
|
+
var DEFAULT_ACTION_NAME = "run";
|
|
2103
|
+
function generateNewCallText(capabilityId, actionName, args) {
|
|
2104
|
+
const argsText = args.trim() || "{}";
|
|
2105
|
+
return `migrationAdaptor(this.capabilityService.load('${capabilityId}').call('${actionName}', ${argsText}))`;
|
|
2106
|
+
}
|
|
2107
|
+
function transformCallSites(sourceFile, imports) {
|
|
2108
|
+
const importMap = /* @__PURE__ */ new Map();
|
|
2109
|
+
for (const imp of imports) {
|
|
2110
|
+
importMap.set(imp.importName, {
|
|
2111
|
+
capabilityId: imp.capabilityId,
|
|
2112
|
+
actionName: imp.actionName ?? DEFAULT_ACTION_NAME
|
|
2113
|
+
});
|
|
2114
|
+
}
|
|
1710
2115
|
let replacedCount = 0;
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
const
|
|
1715
|
-
|
|
1716
|
-
|
|
2116
|
+
const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind2.CallExpression);
|
|
2117
|
+
const sortedCalls = [...callExpressions].sort((a, b) => b.getStart() - a.getStart());
|
|
2118
|
+
for (const callExpr of sortedCalls) {
|
|
2119
|
+
const expression = callExpr.getExpression();
|
|
2120
|
+
let importInfo;
|
|
2121
|
+
if (expression.getKind() === SyntaxKind2.Identifier) {
|
|
2122
|
+
const functionName = expression.getText();
|
|
2123
|
+
importInfo = importMap.get(functionName);
|
|
2124
|
+
} else if (expression.getKind() === SyntaxKind2.PropertyAccessExpression) {
|
|
2125
|
+
const propAccess = expression.asKind(SyntaxKind2.PropertyAccessExpression);
|
|
2126
|
+
if (propAccess) {
|
|
2127
|
+
const objectExpr = propAccess.getExpression();
|
|
2128
|
+
if (objectExpr.getKind() === SyntaxKind2.Identifier) {
|
|
2129
|
+
const objectName = objectExpr.getText();
|
|
2130
|
+
importInfo = importMap.get(objectName);
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
if (importInfo) {
|
|
2135
|
+
const args = callExpr.getArguments();
|
|
2136
|
+
const argsText = args.map((arg) => arg.getText()).join(", ");
|
|
2137
|
+
const newCallText = generateNewCallText(importInfo.capabilityId, importInfo.actionName, argsText);
|
|
2138
|
+
callExpr.replaceWithText(newCallText);
|
|
2139
|
+
replacedCount++;
|
|
2140
|
+
}
|
|
1717
2141
|
}
|
|
1718
|
-
return {
|
|
2142
|
+
return { replacedCount };
|
|
1719
2143
|
}
|
|
1720
2144
|
|
|
1721
|
-
// src/commands/
|
|
1722
|
-
function analyzeFile(filePath) {
|
|
1723
|
-
const
|
|
1724
|
-
const sourceFile = ts4.createSourceFile(
|
|
1725
|
-
filePath,
|
|
1726
|
-
content,
|
|
1727
|
-
ts4.ScriptTarget.Latest,
|
|
1728
|
-
true
|
|
1729
|
-
);
|
|
2145
|
+
// src/commands/migration/versions/v001_capability/code-migrator/index.ts
|
|
2146
|
+
function analyzeFile(project, filePath, actionNameMap) {
|
|
2147
|
+
const sourceFile = project.addSourceFileAtPath(filePath);
|
|
1730
2148
|
const imports = analyzeImports(sourceFile);
|
|
2149
|
+
for (const imp of imports) {
|
|
2150
|
+
const actionName = actionNameMap.get(imp.capabilityId);
|
|
2151
|
+
if (actionName) {
|
|
2152
|
+
imp.actionName = actionName;
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
1731
2155
|
const callSites = analyzeCallSites(sourceFile, imports);
|
|
1732
2156
|
const classInfo = analyzeClass(sourceFile);
|
|
1733
2157
|
const { canMigrate, reason } = canAutoMigrate(classInfo);
|
|
1734
|
-
const relativePath =
|
|
2158
|
+
const relativePath = path11.relative(getProjectRoot3(), filePath);
|
|
1735
2159
|
return {
|
|
1736
2160
|
filePath: relativePath,
|
|
1737
2161
|
imports,
|
|
@@ -1741,8 +2165,8 @@ function analyzeFile(filePath) {
|
|
|
1741
2165
|
manualMigrationReason: reason
|
|
1742
2166
|
};
|
|
1743
2167
|
}
|
|
1744
|
-
function migrateFile(analysis, dryRun) {
|
|
1745
|
-
const absolutePath =
|
|
2168
|
+
function migrateFile(project, analysis, dryRun) {
|
|
2169
|
+
const absolutePath = path11.join(getProjectRoot3(), analysis.filePath);
|
|
1746
2170
|
if (!analysis.canAutoMigrate) {
|
|
1747
2171
|
return {
|
|
1748
2172
|
filePath: analysis.filePath,
|
|
@@ -1754,42 +2178,19 @@ function migrateFile(analysis, dryRun) {
|
|
|
1754
2178
|
};
|
|
1755
2179
|
}
|
|
1756
2180
|
try {
|
|
1757
|
-
|
|
1758
|
-
const callResult = transformCallSites(
|
|
1759
|
-
|
|
1760
|
-
const
|
|
1761
|
-
analysis.filePath,
|
|
1762
|
-
content,
|
|
1763
|
-
ts4.ScriptTarget.Latest,
|
|
1764
|
-
true
|
|
1765
|
-
);
|
|
1766
|
-
const newImports = analyzeImports(sourceFile);
|
|
1767
|
-
const importResult = transformImports(content, newImports);
|
|
1768
|
-
content = importResult.content;
|
|
1769
|
-
let injectionAdded = false;
|
|
1770
|
-
if (analysis.classInfo) {
|
|
1771
|
-
const newSourceFile = ts4.createSourceFile(
|
|
1772
|
-
analysis.filePath,
|
|
1773
|
-
content,
|
|
1774
|
-
ts4.ScriptTarget.Latest,
|
|
1775
|
-
true
|
|
1776
|
-
);
|
|
1777
|
-
const newClassInfo = analyzeClass(newSourceFile);
|
|
1778
|
-
if (newClassInfo) {
|
|
1779
|
-
const injectionResult = addInjection(content, newClassInfo);
|
|
1780
|
-
content = injectionResult.content;
|
|
1781
|
-
injectionAdded = injectionResult.injectionAdded;
|
|
1782
|
-
}
|
|
1783
|
-
}
|
|
2181
|
+
const sourceFile = project.getSourceFileOrThrow(absolutePath);
|
|
2182
|
+
const callResult = transformCallSites(sourceFile, analysis.imports);
|
|
2183
|
+
const importResult = transformImports(sourceFile, analysis.imports);
|
|
2184
|
+
const injectionResult = addInjection(sourceFile);
|
|
1784
2185
|
if (!dryRun) {
|
|
1785
|
-
|
|
2186
|
+
sourceFile.saveSync();
|
|
1786
2187
|
}
|
|
1787
2188
|
return {
|
|
1788
2189
|
filePath: analysis.filePath,
|
|
1789
2190
|
success: true,
|
|
1790
|
-
importsRemoved:
|
|
2191
|
+
importsRemoved: importResult.importsRemoved,
|
|
1791
2192
|
callSitesReplaced: callResult.replacedCount,
|
|
1792
|
-
injectionAdded
|
|
2193
|
+
injectionAdded: injectionResult.injectionAdded
|
|
1793
2194
|
};
|
|
1794
2195
|
} catch (error) {
|
|
1795
2196
|
return {
|
|
@@ -1802,20 +2203,39 @@ function migrateFile(analysis, dryRun) {
|
|
|
1802
2203
|
};
|
|
1803
2204
|
}
|
|
1804
2205
|
}
|
|
1805
|
-
|
|
2206
|
+
function buildActionNameMap(capabilities) {
|
|
2207
|
+
const map = /* @__PURE__ */ new Map();
|
|
2208
|
+
for (const cap of capabilities) {
|
|
2209
|
+
try {
|
|
2210
|
+
const actionName = getActionNameByPluginKey(cap.pluginKey);
|
|
2211
|
+
map.set(cap.id, actionName);
|
|
2212
|
+
} catch {
|
|
2213
|
+
map.set(cap.id, "run");
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
return map;
|
|
2217
|
+
}
|
|
2218
|
+
async function migrateCode(options, capabilities) {
|
|
1806
2219
|
const result = {
|
|
1807
2220
|
autoMigrated: [],
|
|
1808
2221
|
manualRequired: []
|
|
1809
2222
|
};
|
|
2223
|
+
const actionNameMap = buildActionNameMap(capabilities);
|
|
1810
2224
|
const filesToMigrate = scanFilesToMigrate();
|
|
1811
2225
|
if (filesToMigrate.length === 0) {
|
|
1812
2226
|
console.log(" No files need code migration.\n");
|
|
1813
2227
|
return result;
|
|
1814
2228
|
}
|
|
2229
|
+
const project = new Project({
|
|
2230
|
+
skipAddingFilesFromTsConfig: true,
|
|
2231
|
+
compilerOptions: {
|
|
2232
|
+
allowJs: true
|
|
2233
|
+
}
|
|
2234
|
+
});
|
|
1815
2235
|
const analyses = [];
|
|
1816
2236
|
for (const filePath of filesToMigrate) {
|
|
1817
2237
|
try {
|
|
1818
|
-
const analysis = analyzeFile(filePath);
|
|
2238
|
+
const analysis = analyzeFile(project, filePath, actionNameMap);
|
|
1819
2239
|
analyses.push(analysis);
|
|
1820
2240
|
} catch (error) {
|
|
1821
2241
|
console.error(` \u2717 Failed to analyze ${filePath}: ${error}`);
|
|
@@ -1833,7 +2253,7 @@ async function migrateCode(options) {
|
|
|
1833
2253
|
});
|
|
1834
2254
|
}
|
|
1835
2255
|
for (const analysis of autoMigratable) {
|
|
1836
|
-
const migrationResult = migrateFile(analysis, options.dryRun);
|
|
2256
|
+
const migrationResult = migrateFile(project, analysis, options.dryRun);
|
|
1837
2257
|
result.autoMigrated.push(migrationResult);
|
|
1838
2258
|
}
|
|
1839
2259
|
return result;
|
|
@@ -1848,12 +2268,50 @@ function getSuggestion(analysis) {
|
|
|
1848
2268
|
return "Manual review required";
|
|
1849
2269
|
}
|
|
1850
2270
|
|
|
1851
|
-
// src/commands/
|
|
1852
|
-
import
|
|
1853
|
-
import
|
|
2271
|
+
// src/commands/migration/versions/v001_capability/cleanup.ts
|
|
2272
|
+
import fs12 from "fs";
|
|
2273
|
+
import path12 from "path";
|
|
2274
|
+
function cleanupOldFiles(capabilities, dryRun) {
|
|
2275
|
+
const deletedFiles = [];
|
|
2276
|
+
const errors = [];
|
|
2277
|
+
const capabilitiesDir = getCapabilitiesDir2();
|
|
2278
|
+
const oldJsonPath = path12.join(capabilitiesDir, "capabilities.json");
|
|
2279
|
+
if (fs12.existsSync(oldJsonPath)) {
|
|
2280
|
+
try {
|
|
2281
|
+
if (!dryRun) {
|
|
2282
|
+
fs12.unlinkSync(oldJsonPath);
|
|
2283
|
+
}
|
|
2284
|
+
deletedFiles.push("capabilities.json");
|
|
2285
|
+
} catch (error) {
|
|
2286
|
+
errors.push(`Failed to delete capabilities.json: ${error instanceof Error ? error.message : String(error)}`);
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
for (const cap of capabilities) {
|
|
2290
|
+
const tsFilePath = path12.join(capabilitiesDir, `${cap.id}.ts`);
|
|
2291
|
+
if (fs12.existsSync(tsFilePath)) {
|
|
2292
|
+
try {
|
|
2293
|
+
if (!dryRun) {
|
|
2294
|
+
fs12.unlinkSync(tsFilePath);
|
|
2295
|
+
}
|
|
2296
|
+
deletedFiles.push(`${cap.id}.ts`);
|
|
2297
|
+
} catch (error) {
|
|
2298
|
+
errors.push(`Failed to delete ${cap.id}.ts: ${error instanceof Error ? error.message : String(error)}`);
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
return {
|
|
2303
|
+
success: errors.length === 0,
|
|
2304
|
+
deletedFiles,
|
|
2305
|
+
errors
|
|
2306
|
+
};
|
|
2307
|
+
}
|
|
2308
|
+
|
|
2309
|
+
// src/commands/migration/versions/v001_capability/report-generator.ts
|
|
2310
|
+
import fs13 from "fs";
|
|
2311
|
+
import path13 from "path";
|
|
1854
2312
|
var REPORT_FILE = "capability-migration-report.md";
|
|
1855
2313
|
function printSummary(result) {
|
|
1856
|
-
const { jsonMigration, pluginInstallation, codeMigration } = result;
|
|
2314
|
+
const { jsonMigration, pluginInstallation, codeMigration, cleanup } = result;
|
|
1857
2315
|
console.log("\u{1F4CA} Migration Summary");
|
|
1858
2316
|
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1859
2317
|
if (!jsonMigration.skipped) {
|
|
@@ -1878,6 +2336,9 @@ function printSummary(result) {
|
|
|
1878
2336
|
console.log(` Files need manual work: ${codeMigration.manualRequired.length}`);
|
|
1879
2337
|
}
|
|
1880
2338
|
console.log(` Total call sites fixed: ${totalCallSites}`);
|
|
2339
|
+
if (cleanup.deletedFiles.length > 0) {
|
|
2340
|
+
console.log(` Old files cleaned up: ${cleanup.deletedFiles.length}`);
|
|
2341
|
+
}
|
|
1881
2342
|
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
|
|
1882
2343
|
const hasErrors = pluginInstallation.failed.length > 0 || failedMigrations.length > 0;
|
|
1883
2344
|
const hasManual = codeMigration.manualRequired.length > 0;
|
|
@@ -1898,7 +2359,7 @@ function printSummary(result) {
|
|
|
1898
2359
|
console.log("");
|
|
1899
2360
|
}
|
|
1900
2361
|
async function generateReport(result) {
|
|
1901
|
-
const { jsonMigration, pluginInstallation, codeMigration } = result;
|
|
2362
|
+
const { jsonMigration, pluginInstallation, codeMigration, cleanup } = result;
|
|
1902
2363
|
const lines = [];
|
|
1903
2364
|
lines.push("# Capability \u8FC1\u79FB\u62A5\u544A");
|
|
1904
2365
|
lines.push("");
|
|
@@ -1911,34 +2372,28 @@ async function generateReport(result) {
|
|
|
1911
2372
|
} else {
|
|
1912
2373
|
lines.push("### 1.1 \u5DF2\u8FC1\u79FB\u7684 Capabilities");
|
|
1913
2374
|
lines.push("");
|
|
1914
|
-
lines.push("| ID |
|
|
1915
|
-
lines.push("
|
|
2375
|
+
lines.push("| ID | pluginKey |");
|
|
2376
|
+
lines.push("|----|-----------|");
|
|
1916
2377
|
for (const cap of jsonMigration.capabilities) {
|
|
1917
|
-
lines.push(`| ${cap.id} | ${cap.
|
|
2378
|
+
lines.push(`| ${cap.id} | ${cap.pluginKey} |`);
|
|
1918
2379
|
}
|
|
1919
2380
|
lines.push("");
|
|
1920
|
-
if (jsonMigration.backupPath) {
|
|
1921
|
-
lines.push("### 1.2 \u5907\u4EFD\u6587\u4EF6");
|
|
1922
|
-
lines.push("");
|
|
1923
|
-
lines.push(`- \`${jsonMigration.backupPath}\``);
|
|
1924
|
-
lines.push("");
|
|
1925
|
-
}
|
|
1926
2381
|
}
|
|
1927
2382
|
lines.push("## 2. \u63D2\u4EF6\u5B89\u88C5");
|
|
1928
2383
|
lines.push("");
|
|
1929
2384
|
if (pluginInstallation.installed.length === 0 && pluginInstallation.alreadyInstalled.length === 0) {
|
|
1930
2385
|
lines.push("\u65E0\u63D2\u4EF6\u9700\u8981\u5B89\u88C5\u3002");
|
|
1931
2386
|
} else {
|
|
1932
|
-
lines.push("|
|
|
1933
|
-
lines.push("
|
|
1934
|
-
for (const
|
|
1935
|
-
lines.push(`| ${
|
|
2387
|
+
lines.push("| pluginKey | \u72B6\u6001 |");
|
|
2388
|
+
lines.push("|-----------|------|");
|
|
2389
|
+
for (const pluginKey of pluginInstallation.alreadyInstalled) {
|
|
2390
|
+
lines.push(`| ${pluginKey} | \u5DF2\u5B89\u88C5 |`);
|
|
1936
2391
|
}
|
|
1937
|
-
for (const
|
|
1938
|
-
lines.push(`| ${
|
|
2392
|
+
for (const pluginKey of pluginInstallation.installed) {
|
|
2393
|
+
lines.push(`| ${pluginKey} | \u65B0\u5B89\u88C5 |`);
|
|
1939
2394
|
}
|
|
1940
2395
|
for (const failed of pluginInstallation.failed) {
|
|
1941
|
-
lines.push(`| ${failed.
|
|
2396
|
+
lines.push(`| ${failed.pluginKey} | \u274C \u5931\u8D25: ${failed.error} |`);
|
|
1942
2397
|
}
|
|
1943
2398
|
}
|
|
1944
2399
|
lines.push("");
|
|
@@ -1983,7 +2438,30 @@ async function generateReport(result) {
|
|
|
1983
2438
|
lines.push("");
|
|
1984
2439
|
}
|
|
1985
2440
|
}
|
|
1986
|
-
lines.push("## 4. \
|
|
2441
|
+
lines.push("## 4. \u6E05\u7406\u8001\u6587\u4EF6");
|
|
2442
|
+
lines.push("");
|
|
2443
|
+
if (cleanup.deletedFiles.length === 0 && cleanup.errors.length === 0) {
|
|
2444
|
+
lines.push("\u65E0\u6587\u4EF6\u9700\u8981\u6E05\u7406\u3002");
|
|
2445
|
+
} else {
|
|
2446
|
+
if (cleanup.deletedFiles.length > 0) {
|
|
2447
|
+
lines.push("### 4.1 \u5DF2\u5220\u9664\u7684\u6587\u4EF6");
|
|
2448
|
+
lines.push("");
|
|
2449
|
+
for (const file of cleanup.deletedFiles) {
|
|
2450
|
+
lines.push(`- \`${file}\``);
|
|
2451
|
+
}
|
|
2452
|
+
lines.push("");
|
|
2453
|
+
}
|
|
2454
|
+
if (cleanup.errors.length > 0) {
|
|
2455
|
+
lines.push("### 4.2 \u6E05\u7406\u5931\u8D25");
|
|
2456
|
+
lines.push("");
|
|
2457
|
+
for (const err of cleanup.errors) {
|
|
2458
|
+
lines.push(`- \u274C ${err}`);
|
|
2459
|
+
}
|
|
2460
|
+
lines.push("");
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
lines.push("");
|
|
2464
|
+
lines.push("## 5. \u9A8C\u8BC1\u6E05\u5355");
|
|
1987
2465
|
lines.push("");
|
|
1988
2466
|
lines.push("- [ ] \u8FD0\u884C `git diff` \u68C0\u67E5\u4EE3\u7801\u53D8\u66F4");
|
|
1989
2467
|
lines.push("- [ ] \u8FD0\u884C `npm run typecheck` \u68C0\u67E5\u7C7B\u578B");
|
|
@@ -1991,40 +2469,74 @@ async function generateReport(result) {
|
|
|
1991
2469
|
if (codeMigration.manualRequired.length > 0) {
|
|
1992
2470
|
lines.push("- [ ] \u5904\u7406\u624B\u52A8\u8FC1\u79FB\u9879");
|
|
1993
2471
|
}
|
|
1994
|
-
lines.push("- [ ] \u5220\u9664\u5907\u4EFD\u6587\u4EF6\uFF08\u786E\u8BA4\u65E0\u8BEF\u540E\uFF09");
|
|
1995
2472
|
lines.push("");
|
|
1996
|
-
const reportPath =
|
|
1997
|
-
|
|
2473
|
+
const reportPath = path13.join(getProjectRoot3(), REPORT_FILE);
|
|
2474
|
+
fs13.writeFileSync(reportPath, lines.join("\n"), "utf-8");
|
|
1998
2475
|
console.log(`\u{1F4C4} Report generated: ${REPORT_FILE}`);
|
|
1999
2476
|
}
|
|
2000
2477
|
|
|
2001
|
-
// src/commands/
|
|
2002
|
-
async function
|
|
2478
|
+
// src/commands/migration/versions/v001_capability/migration-runner.ts
|
|
2479
|
+
async function runCapabilityMigration(options = {}) {
|
|
2003
2480
|
const fullOptions = {
|
|
2004
|
-
dryRun: options.dryRun ?? false
|
|
2005
|
-
skipInstall: options.skipInstall ?? false,
|
|
2006
|
-
skipCode: options.skipCode ?? false,
|
|
2007
|
-
mapping: mergeMapping(options.mapping)
|
|
2481
|
+
dryRun: options.dryRun ?? false
|
|
2008
2482
|
};
|
|
2009
2483
|
console.log("\u{1F50D} Analyzing project...\n");
|
|
2010
2484
|
if (fullOptions.dryRun) {
|
|
2011
2485
|
console.log("\u{1F4CB} Running in dry-run mode (no files will be modified)\n");
|
|
2012
2486
|
}
|
|
2487
|
+
let jsonResult = {
|
|
2488
|
+
success: false,
|
|
2489
|
+
skipped: true,
|
|
2490
|
+
reason: "Not executed",
|
|
2491
|
+
count: 0,
|
|
2492
|
+
capabilities: []
|
|
2493
|
+
};
|
|
2494
|
+
let pluginResult = {
|
|
2495
|
+
installed: [],
|
|
2496
|
+
alreadyInstalled: [],
|
|
2497
|
+
failed: []
|
|
2498
|
+
};
|
|
2499
|
+
let codeResult = {
|
|
2500
|
+
autoMigrated: [],
|
|
2501
|
+
manualRequired: []
|
|
2502
|
+
};
|
|
2503
|
+
let cleanupResult = {
|
|
2504
|
+
success: true,
|
|
2505
|
+
deletedFiles: [],
|
|
2506
|
+
errors: []
|
|
2507
|
+
};
|
|
2013
2508
|
console.log("\u{1F4C1} Step 1: JSON File Migration");
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2509
|
+
try {
|
|
2510
|
+
jsonResult = await migrateJsonFiles(fullOptions);
|
|
2511
|
+
if (jsonResult.skipped) {
|
|
2512
|
+
console.log(` \u25CB Skipped: ${jsonResult.reason}
|
|
2017
2513
|
`);
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2514
|
+
} else if (jsonResult.success) {
|
|
2515
|
+
console.log(` \u2713 Created ${jsonResult.count} capability files
|
|
2516
|
+
`);
|
|
2517
|
+
} else {
|
|
2518
|
+
console.log(` \u2717 Failed: ${jsonResult.reason}
|
|
2519
|
+
`);
|
|
2520
|
+
return buildResult(jsonResult, pluginResult, codeResult, cleanupResult);
|
|
2022
2521
|
}
|
|
2023
|
-
|
|
2522
|
+
} catch (error) {
|
|
2523
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
2524
|
+
console.log(` \u2717 Error: ${errorMsg}
|
|
2525
|
+
`);
|
|
2526
|
+
jsonResult = {
|
|
2527
|
+
success: false,
|
|
2528
|
+
skipped: false,
|
|
2529
|
+
reason: errorMsg,
|
|
2530
|
+
count: 0,
|
|
2531
|
+
capabilities: []
|
|
2532
|
+
};
|
|
2533
|
+
return buildResult(jsonResult, pluginResult, codeResult, cleanupResult);
|
|
2534
|
+
}
|
|
2535
|
+
if (jsonResult.skipped) {
|
|
2536
|
+
return buildResult(jsonResult, pluginResult, codeResult, cleanupResult);
|
|
2024
2537
|
}
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
console.log("\u{1F4E6} Step 2: Plugin Installation");
|
|
2538
|
+
console.log("\u{1F4E6} Step 2: Plugin Installation");
|
|
2539
|
+
try {
|
|
2028
2540
|
pluginResult = await installPlugins(jsonResult.capabilities, fullOptions);
|
|
2029
2541
|
if (pluginResult.alreadyInstalled.length > 0) {
|
|
2030
2542
|
console.log(` \u2713 Already installed: ${pluginResult.alreadyInstalled.join(", ")}`);
|
|
@@ -2033,18 +2545,27 @@ async function runMigration(options = {}) {
|
|
|
2033
2545
|
console.log(` \u2713 Installed: ${pluginResult.installed.join(", ")}`);
|
|
2034
2546
|
}
|
|
2035
2547
|
if (pluginResult.failed.length > 0) {
|
|
2036
|
-
console.log(` \
|
|
2548
|
+
console.log(` \u26A0 Failed (will continue): ${pluginResult.failed.map((f) => f.pluginKey).join(", ")}`);
|
|
2549
|
+
for (const f of pluginResult.failed) {
|
|
2550
|
+
console.log(` - ${f.pluginKey}: ${f.error}`);
|
|
2551
|
+
}
|
|
2037
2552
|
}
|
|
2038
2553
|
console.log("");
|
|
2039
|
-
}
|
|
2040
|
-
|
|
2041
|
-
console.log(
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2554
|
+
} catch (error) {
|
|
2555
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
2556
|
+
console.log(` \u26A0 Error (will continue): ${errorMsg}
|
|
2557
|
+
`);
|
|
2558
|
+
pluginResult = {
|
|
2559
|
+
installed: [],
|
|
2560
|
+
alreadyInstalled: [],
|
|
2561
|
+
failed: [{ pluginKey: "unknown", error: errorMsg }]
|
|
2562
|
+
};
|
|
2563
|
+
}
|
|
2564
|
+
console.log("\u{1F527} Step 3: Code Migration");
|
|
2565
|
+
console.log(" Scanning server/ directory...\n");
|
|
2566
|
+
try {
|
|
2567
|
+
codeResult = await migrateCode(fullOptions, jsonResult.capabilities);
|
|
2568
|
+
let hasCodeError = false;
|
|
2048
2569
|
for (const file of codeResult.autoMigrated) {
|
|
2049
2570
|
if (file.success) {
|
|
2050
2571
|
console.log(` \u2713 ${file.filePath}`);
|
|
@@ -2055,6 +2576,7 @@ async function runMigration(options = {}) {
|
|
|
2055
2576
|
console.log(` - Replaced ${file.callSitesReplaced} call sites`);
|
|
2056
2577
|
} else {
|
|
2057
2578
|
console.log(` \u2717 ${file.filePath}: ${file.error}`);
|
|
2579
|
+
hasCodeError = true;
|
|
2058
2580
|
}
|
|
2059
2581
|
}
|
|
2060
2582
|
if (codeResult.manualRequired.length > 0) {
|
|
@@ -2065,93 +2587,414 @@ async function runMigration(options = {}) {
|
|
|
2065
2587
|
}
|
|
2066
2588
|
}
|
|
2067
2589
|
console.log("");
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2590
|
+
if (hasCodeError) {
|
|
2591
|
+
console.log(" \u2717 Code migration failed, aborting...\n");
|
|
2592
|
+
return buildResult(jsonResult, pluginResult, codeResult, cleanupResult);
|
|
2593
|
+
}
|
|
2594
|
+
} catch (error) {
|
|
2595
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
2596
|
+
console.log(` \u2717 Error: ${errorMsg}
|
|
2597
|
+
`);
|
|
2598
|
+
codeResult = {
|
|
2599
|
+
autoMigrated: [],
|
|
2600
|
+
manualRequired: []
|
|
2601
|
+
};
|
|
2602
|
+
return buildResult(jsonResult, pluginResult, codeResult, cleanupResult);
|
|
2071
2603
|
}
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2604
|
+
console.log("\u{1F9F9} Step 4: Cleanup Old Files");
|
|
2605
|
+
try {
|
|
2606
|
+
cleanupResult = cleanupOldFiles(jsonResult.capabilities, fullOptions.dryRun);
|
|
2607
|
+
if (cleanupResult.deletedFiles.length > 0) {
|
|
2608
|
+
for (const file of cleanupResult.deletedFiles) {
|
|
2609
|
+
console.log(` \u2713 Deleted: ${file}`);
|
|
2610
|
+
}
|
|
2611
|
+
} else {
|
|
2612
|
+
console.log(" \u25CB No files to clean up");
|
|
2613
|
+
}
|
|
2614
|
+
if (cleanupResult.errors.length > 0) {
|
|
2615
|
+
for (const err of cleanupResult.errors) {
|
|
2616
|
+
console.log(` \u26A0 ${err}`);
|
|
2617
|
+
}
|
|
2618
|
+
}
|
|
2619
|
+
console.log("");
|
|
2620
|
+
} catch (error) {
|
|
2621
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
2622
|
+
console.log(` \u26A0 Error: ${errorMsg}
|
|
2623
|
+
`);
|
|
2624
|
+
cleanupResult = {
|
|
2625
|
+
success: false,
|
|
2626
|
+
deletedFiles: [],
|
|
2627
|
+
errors: [errorMsg]
|
|
2628
|
+
};
|
|
2629
|
+
}
|
|
2630
|
+
const result = buildResult(jsonResult, pluginResult, codeResult, cleanupResult);
|
|
2077
2631
|
printSummary(result);
|
|
2078
2632
|
if (!fullOptions.dryRun) {
|
|
2079
2633
|
await generateReport(result);
|
|
2080
2634
|
}
|
|
2081
2635
|
return result;
|
|
2082
2636
|
}
|
|
2637
|
+
function buildResult(jsonMigration, pluginInstallation, codeMigration, cleanup) {
|
|
2638
|
+
return {
|
|
2639
|
+
jsonMigration,
|
|
2640
|
+
pluginInstallation,
|
|
2641
|
+
codeMigration,
|
|
2642
|
+
cleanup
|
|
2643
|
+
};
|
|
2644
|
+
}
|
|
2083
2645
|
|
|
2084
|
-
// src/commands/
|
|
2085
|
-
async function
|
|
2646
|
+
// src/commands/migration/versions/v001_capability/run.ts
|
|
2647
|
+
async function run3(options) {
|
|
2086
2648
|
try {
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2649
|
+
const migrationOptions = {
|
|
2650
|
+
dryRun: options.dryRun ?? false
|
|
2651
|
+
};
|
|
2652
|
+
const result = await runCapabilityMigration(migrationOptions);
|
|
2653
|
+
const { jsonMigration, pluginInstallation, codeMigration, cleanup } = result;
|
|
2654
|
+
const jsonFailed = !jsonMigration.success && !jsonMigration.skipped;
|
|
2655
|
+
const codeFailed = codeMigration.autoMigrated.some((f) => !f.success);
|
|
2656
|
+
if (jsonFailed) {
|
|
2657
|
+
return {
|
|
2658
|
+
success: false,
|
|
2659
|
+
message: jsonMigration.reason || "JSON migration failed",
|
|
2660
|
+
error: new Error(jsonMigration.reason || "Unknown error")
|
|
2661
|
+
};
|
|
2662
|
+
}
|
|
2663
|
+
if (codeFailed) {
|
|
2664
|
+
return {
|
|
2665
|
+
success: false,
|
|
2666
|
+
message: "Code migration failed",
|
|
2667
|
+
error: new Error("Some files failed to migrate")
|
|
2668
|
+
};
|
|
2669
|
+
}
|
|
2670
|
+
const messages = [];
|
|
2671
|
+
if (jsonMigration.success) {
|
|
2672
|
+
messages.push(`${jsonMigration.count} capabilities migrated`);
|
|
2673
|
+
}
|
|
2674
|
+
if (pluginInstallation.installed.length > 0) {
|
|
2675
|
+
messages.push(`${pluginInstallation.installed.length} plugins installed`);
|
|
2676
|
+
}
|
|
2677
|
+
if (codeMigration.autoMigrated.length > 0) {
|
|
2678
|
+
const successCount = codeMigration.autoMigrated.filter((f) => f.success).length;
|
|
2679
|
+
if (successCount > 0) {
|
|
2680
|
+
messages.push(`${successCount} files auto-migrated`);
|
|
2099
2681
|
}
|
|
2100
2682
|
}
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2683
|
+
if (cleanup.deletedFiles.length > 0) {
|
|
2684
|
+
messages.push(`${cleanup.deletedFiles.length} old files cleaned up`);
|
|
2685
|
+
}
|
|
2686
|
+
if (codeMigration.manualRequired.length > 0) {
|
|
2687
|
+
messages.push(`${codeMigration.manualRequired.length} files need manual migration`);
|
|
2688
|
+
}
|
|
2689
|
+
if (pluginInstallation.failed.length > 0) {
|
|
2690
|
+
messages.push(`${pluginInstallation.failed.length} plugins failed to install`);
|
|
2691
|
+
}
|
|
2692
|
+
return {
|
|
2693
|
+
success: true,
|
|
2694
|
+
message: messages.join(", ") || "No migration needed"
|
|
2106
2695
|
};
|
|
2107
|
-
await runMigration(migrationOptions);
|
|
2108
2696
|
} catch (error) {
|
|
2109
|
-
|
|
2110
|
-
|
|
2697
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
2698
|
+
return {
|
|
2699
|
+
success: false,
|
|
2700
|
+
message: err.message,
|
|
2701
|
+
error: err
|
|
2702
|
+
};
|
|
2111
2703
|
}
|
|
2112
2704
|
}
|
|
2113
2705
|
|
|
2114
|
-
// src/commands/
|
|
2115
|
-
var
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
await list2(options);
|
|
2122
|
-
});
|
|
2123
|
-
}
|
|
2706
|
+
// src/commands/migration/versions/v001_capability/index.ts
|
|
2707
|
+
var v001CapabilityMigration = {
|
|
2708
|
+
version: 1,
|
|
2709
|
+
name: "capability",
|
|
2710
|
+
description: "Migrate capability configurations from old format (capabilities.json array) to new format (individual JSON files)",
|
|
2711
|
+
check,
|
|
2712
|
+
run: run3
|
|
2124
2713
|
};
|
|
2714
|
+
|
|
2715
|
+
// src/commands/migration/versions/index.ts
|
|
2716
|
+
var versionedMigrations = [
|
|
2717
|
+
v001CapabilityMigration
|
|
2718
|
+
// 未来新增的迁移脚本在此添加,如:
|
|
2719
|
+
// v002ConfigMigration,
|
|
2720
|
+
// v003RoutesMigration,
|
|
2721
|
+
];
|
|
2722
|
+
function getPendingMigrations(currentVersion) {
|
|
2723
|
+
return versionedMigrations.filter((m) => m.version > currentVersion).sort((a, b) => a.version - b.version);
|
|
2724
|
+
}
|
|
2725
|
+
function getLatestVersion() {
|
|
2726
|
+
if (versionedMigrations.length === 0) return 0;
|
|
2727
|
+
return Math.max(...versionedMigrations.map((m) => m.version));
|
|
2728
|
+
}
|
|
2729
|
+
|
|
2730
|
+
// src/commands/migration/runner.ts
|
|
2731
|
+
function log(message) {
|
|
2732
|
+
console.log(`[migration] ${message}`);
|
|
2733
|
+
}
|
|
2734
|
+
async function executeSingle(migration, options) {
|
|
2735
|
+
const { version, name } = migration;
|
|
2736
|
+
const versionTag = `v${version}`;
|
|
2737
|
+
try {
|
|
2738
|
+
log(`Checking [${versionTag}] ${name}...`);
|
|
2739
|
+
const checkResult = await migration.check(options);
|
|
2740
|
+
if (!checkResult.needsMigration) {
|
|
2741
|
+
log(`[${versionTag}] ${name}: no migration needed`);
|
|
2742
|
+
return {
|
|
2743
|
+
name,
|
|
2744
|
+
status: "skipped",
|
|
2745
|
+
message: checkResult.message || "No migration needed"
|
|
2746
|
+
};
|
|
2747
|
+
}
|
|
2748
|
+
log(`[${versionTag}] ${name}: needs migration (${checkResult.message})`);
|
|
2749
|
+
if (checkResult.items && checkResult.items.length > 0) {
|
|
2750
|
+
for (const item of checkResult.items) {
|
|
2751
|
+
console.log(` - ${item}`);
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
2754
|
+
if (options.dryRun) {
|
|
2755
|
+
return {
|
|
2756
|
+
name,
|
|
2757
|
+
status: "skipped",
|
|
2758
|
+
message: `Dry-run: ${checkResult.message}`
|
|
2759
|
+
};
|
|
2760
|
+
}
|
|
2761
|
+
log(`Running [${versionTag}] ${name} migration...`);
|
|
2762
|
+
const runResult = await migration.run(options);
|
|
2763
|
+
if (runResult.success) {
|
|
2764
|
+
log(`\u2713 [${versionTag}] ${name}: completed (${runResult.message})`);
|
|
2765
|
+
return {
|
|
2766
|
+
name,
|
|
2767
|
+
status: "completed",
|
|
2768
|
+
message: runResult.message
|
|
2769
|
+
};
|
|
2770
|
+
} else {
|
|
2771
|
+
log(`\u2717 [${versionTag}] ${name}: failed`);
|
|
2772
|
+
if (runResult.error) {
|
|
2773
|
+
console.log(` Error: ${runResult.error.message}`);
|
|
2774
|
+
}
|
|
2775
|
+
return {
|
|
2776
|
+
name,
|
|
2777
|
+
status: "failed",
|
|
2778
|
+
message: runResult.message,
|
|
2779
|
+
error: runResult.error
|
|
2780
|
+
};
|
|
2781
|
+
}
|
|
2782
|
+
} catch (error) {
|
|
2783
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
2784
|
+
log(`\u2717 [${versionTag}] ${name}: failed`);
|
|
2785
|
+
console.log(` Error: ${err.message}`);
|
|
2786
|
+
return {
|
|
2787
|
+
name,
|
|
2788
|
+
status: "failed",
|
|
2789
|
+
message: err.message,
|
|
2790
|
+
error: err
|
|
2791
|
+
};
|
|
2792
|
+
}
|
|
2793
|
+
}
|
|
2794
|
+
function printSummary2(summary, dryRun) {
|
|
2795
|
+
console.log("");
|
|
2796
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
2797
|
+
console.log(dryRun ? "Dry-run Summary:" : "Migration Summary:");
|
|
2798
|
+
if (dryRun) {
|
|
2799
|
+
const needsMigration = summary.skipped.filter((s) => s.message.startsWith("Dry-run:")).length;
|
|
2800
|
+
const noAction = summary.skipped.filter((s) => !s.message.startsWith("Dry-run:")).length;
|
|
2801
|
+
console.log(` Needs migration: ${needsMigration}`);
|
|
2802
|
+
console.log(` No action needed: ${noAction}`);
|
|
2803
|
+
} else {
|
|
2804
|
+
console.log(` \u2713 Completed: ${summary.completed.length}`);
|
|
2805
|
+
console.log(` \u25CB Skipped: ${summary.skipped.length} (no migration needed)`);
|
|
2806
|
+
console.log(` \u2717 Failed: ${summary.failed.length}`);
|
|
2807
|
+
}
|
|
2808
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
2809
|
+
}
|
|
2810
|
+
async function runMigrations(options) {
|
|
2811
|
+
const currentVersion = getCurrentVersion();
|
|
2812
|
+
const latestVersion = getLatestVersion();
|
|
2813
|
+
console.log("");
|
|
2814
|
+
log(`Current version: ${currentVersion || "0 (not set)"}`);
|
|
2815
|
+
if (currentVersion >= latestVersion) {
|
|
2816
|
+
log("All migrations are up to date.");
|
|
2817
|
+
return {
|
|
2818
|
+
needsMigration: false,
|
|
2819
|
+
completed: [],
|
|
2820
|
+
skipped: [],
|
|
2821
|
+
failed: []
|
|
2822
|
+
};
|
|
2823
|
+
}
|
|
2824
|
+
const pendingMigrations = getPendingMigrations(currentVersion);
|
|
2825
|
+
if (options.dryRun) {
|
|
2826
|
+
log("Dry-run mode enabled, no changes will be made.");
|
|
2827
|
+
} else {
|
|
2828
|
+
log("Applying migrations...");
|
|
2829
|
+
}
|
|
2830
|
+
console.log("");
|
|
2831
|
+
const summary = {
|
|
2832
|
+
needsMigration: false,
|
|
2833
|
+
completed: [],
|
|
2834
|
+
skipped: [],
|
|
2835
|
+
failed: []
|
|
2836
|
+
};
|
|
2837
|
+
let hasAnyMigration = false;
|
|
2838
|
+
for (const migration of pendingMigrations) {
|
|
2839
|
+
const result = await executeSingle(migration, options);
|
|
2840
|
+
switch (result.status) {
|
|
2841
|
+
case "completed":
|
|
2842
|
+
summary.completed.push(result);
|
|
2843
|
+
hasAnyMigration = true;
|
|
2844
|
+
if (!options.dryRun) {
|
|
2845
|
+
setCurrentVersion(migration.version);
|
|
2846
|
+
}
|
|
2847
|
+
break;
|
|
2848
|
+
case "skipped":
|
|
2849
|
+
summary.skipped.push(result);
|
|
2850
|
+
if (result.message.startsWith("Dry-run:")) {
|
|
2851
|
+
hasAnyMigration = true;
|
|
2852
|
+
}
|
|
2853
|
+
break;
|
|
2854
|
+
case "failed":
|
|
2855
|
+
summary.failed.push(result);
|
|
2856
|
+
hasAnyMigration = true;
|
|
2857
|
+
log("Migration aborted due to failure.");
|
|
2858
|
+
printSummary2(summary, !!options.dryRun);
|
|
2859
|
+
return summary;
|
|
2860
|
+
}
|
|
2861
|
+
console.log("");
|
|
2862
|
+
}
|
|
2863
|
+
summary.needsMigration = hasAnyMigration;
|
|
2864
|
+
if (currentVersion === 0 && !hasAnyMigration && !options.dryRun) {
|
|
2865
|
+
log(`All migrations skipped. Project is up to date.`);
|
|
2866
|
+
log(`Updated migrationVersion to ${latestVersion}.`);
|
|
2867
|
+
setCurrentVersion(latestVersion);
|
|
2868
|
+
} else if (!options.dryRun && summary.completed.length > 0) {
|
|
2869
|
+
log(`Migration complete. Updated to version ${getCurrentVersion()}.`);
|
|
2870
|
+
}
|
|
2871
|
+
printSummary2(summary, !!options.dryRun);
|
|
2872
|
+
return summary;
|
|
2873
|
+
}
|
|
2874
|
+
async function checkMigrations(options) {
|
|
2875
|
+
const currentVersion = getCurrentVersion();
|
|
2876
|
+
const latestVersion = getLatestVersion();
|
|
2877
|
+
console.log("");
|
|
2878
|
+
log(`Current version: ${currentVersion || "0 (not set)"}`);
|
|
2879
|
+
if (currentVersion >= latestVersion) {
|
|
2880
|
+
log("All migrations are up to date.");
|
|
2881
|
+
return {
|
|
2882
|
+
needsMigration: false,
|
|
2883
|
+
completed: [],
|
|
2884
|
+
skipped: [],
|
|
2885
|
+
failed: []
|
|
2886
|
+
};
|
|
2887
|
+
}
|
|
2888
|
+
log("Checking pending migrations...");
|
|
2889
|
+
console.log("");
|
|
2890
|
+
const pendingMigrations = getPendingMigrations(currentVersion);
|
|
2891
|
+
const summary = {
|
|
2892
|
+
needsMigration: false,
|
|
2893
|
+
completed: [],
|
|
2894
|
+
skipped: [],
|
|
2895
|
+
failed: []
|
|
2896
|
+
};
|
|
2897
|
+
let needsCount = 0;
|
|
2898
|
+
let noActionCount = 0;
|
|
2899
|
+
for (const migration of pendingMigrations) {
|
|
2900
|
+
const { version, name } = migration;
|
|
2901
|
+
const versionTag = `v${version}`;
|
|
2902
|
+
try {
|
|
2903
|
+
const checkResult = await migration.check(options);
|
|
2904
|
+
if (checkResult.needsMigration) {
|
|
2905
|
+
needsCount++;
|
|
2906
|
+
console.log(` ${versionTag} (${name}): needs migration`);
|
|
2907
|
+
if (checkResult.items && checkResult.items.length > 0) {
|
|
2908
|
+
for (const item of checkResult.items) {
|
|
2909
|
+
console.log(` - ${item}`);
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
summary.skipped.push({
|
|
2913
|
+
name,
|
|
2914
|
+
status: "skipped",
|
|
2915
|
+
message: `Needs migration: ${checkResult.message}`
|
|
2916
|
+
});
|
|
2917
|
+
} else {
|
|
2918
|
+
noActionCount++;
|
|
2919
|
+
console.log(` ${versionTag} (${name}): no migration needed`);
|
|
2920
|
+
summary.skipped.push({
|
|
2921
|
+
name,
|
|
2922
|
+
status: "skipped",
|
|
2923
|
+
message: checkResult.message || "No migration needed"
|
|
2924
|
+
});
|
|
2925
|
+
}
|
|
2926
|
+
} catch (error) {
|
|
2927
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
2928
|
+
console.log(` ${versionTag} (${name}): check failed`);
|
|
2929
|
+
console.log(` Error: ${err.message}`);
|
|
2930
|
+
summary.failed.push({
|
|
2931
|
+
name,
|
|
2932
|
+
status: "failed",
|
|
2933
|
+
message: err.message,
|
|
2934
|
+
error: err
|
|
2935
|
+
});
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2938
|
+
console.log("");
|
|
2939
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
2940
|
+
console.log("Check Summary:");
|
|
2941
|
+
console.log(` Needs migration: ${needsCount}`);
|
|
2942
|
+
console.log(` No action needed: ${noActionCount}`);
|
|
2943
|
+
if (summary.failed.length > 0) {
|
|
2944
|
+
console.log(` Check failed: ${summary.failed.length}`);
|
|
2945
|
+
}
|
|
2946
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
2947
|
+
if (needsCount > 0) {
|
|
2948
|
+
console.log("");
|
|
2949
|
+
log("Run 'fullstack-cli migration' to apply.");
|
|
2950
|
+
}
|
|
2951
|
+
summary.needsMigration = needsCount > 0;
|
|
2952
|
+
if (needsCount === 0 && summary.failed.length === 0) {
|
|
2953
|
+
setCurrentVersion(latestVersion);
|
|
2954
|
+
console.log("");
|
|
2955
|
+
log(`Updated migrationVersion to ${latestVersion}.`);
|
|
2956
|
+
}
|
|
2957
|
+
return summary;
|
|
2958
|
+
}
|
|
2959
|
+
|
|
2960
|
+
// src/commands/migration/index.ts
|
|
2125
2961
|
var migrationCommand = {
|
|
2126
2962
|
name: "migration",
|
|
2127
|
-
description: "
|
|
2963
|
+
description: "Run versioned migration scripts",
|
|
2128
2964
|
register(program) {
|
|
2129
|
-
program.command(this.name).description(
|
|
2130
|
-
await
|
|
2965
|
+
const migrationCmd = program.command(this.name).description("Run all pending migrations based on current migrationVersion").option("--dry-run", "Preview mode, only run check() without executing run()").action(async (options) => {
|
|
2966
|
+
const summary = await runMigrations(options);
|
|
2967
|
+
if (summary.failed.length > 0) {
|
|
2968
|
+
process.exit(1);
|
|
2969
|
+
}
|
|
2970
|
+
});
|
|
2971
|
+
migrationCmd.command("check").description("Check pending migrations without applying them").action(async () => {
|
|
2972
|
+
const summary = await checkMigrations({});
|
|
2973
|
+
if (summary.failed.length > 0) {
|
|
2974
|
+
process.exit(2);
|
|
2975
|
+
} else if (summary.needsMigration) {
|
|
2976
|
+
process.exit(1);
|
|
2977
|
+
}
|
|
2131
2978
|
});
|
|
2132
2979
|
}
|
|
2133
2980
|
};
|
|
2134
|
-
var capabilityCommandGroup = {
|
|
2135
|
-
name: "capability",
|
|
2136
|
-
description: "Manage capability configurations",
|
|
2137
|
-
commands: [listCommand2, migrationCommand]
|
|
2138
|
-
};
|
|
2139
2981
|
|
|
2140
2982
|
// src/commands/index.ts
|
|
2141
2983
|
var commands = [
|
|
2142
2984
|
genDbSchemaCommand,
|
|
2143
2985
|
syncCommand,
|
|
2144
2986
|
actionPluginCommandGroup,
|
|
2145
|
-
capabilityCommandGroup
|
|
2987
|
+
capabilityCommandGroup,
|
|
2988
|
+
migrationCommand
|
|
2146
2989
|
];
|
|
2147
2990
|
|
|
2148
2991
|
// src/index.ts
|
|
2149
|
-
var envPath =
|
|
2150
|
-
if (
|
|
2992
|
+
var envPath = path14.join(process.cwd(), ".env");
|
|
2993
|
+
if (fs14.existsSync(envPath)) {
|
|
2151
2994
|
dotenvConfig({ path: envPath });
|
|
2152
2995
|
}
|
|
2153
|
-
var __dirname =
|
|
2154
|
-
var pkg = JSON.parse(
|
|
2996
|
+
var __dirname = path14.dirname(fileURLToPath3(import.meta.url));
|
|
2997
|
+
var pkg = JSON.parse(fs14.readFileSync(path14.join(__dirname, "../package.json"), "utf-8"));
|
|
2155
2998
|
var cli = new FullstackCLI(pkg.version);
|
|
2156
2999
|
cli.useAll(commands);
|
|
2157
3000
|
cli.run();
|