@lark-apaas/fullstack-cli 1.1.6-alpha.1 → 1.1.6-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/index.ts
2
- import fs13 from "fs";
3
- import path11 from "path";
2
+ import fs15 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 getConfigPath() {
459
- return path3.join(getProjectRoot(), CONFIG_FILE_NAME);
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 readConfig() {
465
- const configPath = getConfigPath();
466
- if (!fs3.existsSync(configPath)) {
467
- return { plugins: {} };
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(configPath, "utf-8");
469
+ const content = fs3.readFileSync(pkgPath, "utf-8");
471
470
  return JSON.parse(content);
472
471
  } catch {
473
- console.warn(`[action-plugin] Warning: Failed to parse ${CONFIG_FILE_NAME}, using empty config`);
474
- return { plugins: {} };
472
+ throw new Error("Failed to parse package.json");
475
473
  }
476
474
  }
477
- function writeConfig(config) {
478
- const configPath = getConfigPath();
479
- fs3.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
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 config = readConfig();
483
- return pluginName in config.plugins;
489
+ const plugins = readActionPlugins();
490
+ return pluginName in plugins;
484
491
  }
485
- function getInstalledPluginInfo(pluginName) {
486
- const config = readConfig();
487
- return config.plugins[pluginName] || null;
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
- if (isPluginInstalled(name)) {
669
- const info = getInstalledPluginInfo(name);
670
- if (info) {
671
- const latestInfo = await getPluginVersion(name, "latest");
672
- const latestVersion = latestInfo.version;
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 };
815
+ }
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 };
679
822
  }
823
+ console.log(`[action-plugin] Found newer version: ${latestInfo.version} (installed: ${actualVersion})`);
680
824
  }
681
825
  const downloadResult = await downloadPlugin(name, requestedVersion);
682
826
  tgzPath = downloadResult.tgzPath;
683
- npmInstall(tgzPath);
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 config = readConfig();
686
- config.plugins[name] = {
687
- version: installedVersion,
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 currentInfo = getInstalledPluginInfo(name);
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 (currentInfo && currentInfo.version === downloadResult.version) {
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 config = readConfig();
755
- config.plugins[name] = {
756
- version: installedVersion,
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
- npmUninstall(name);
816
- const config = readConfig();
817
- delete config.plugins[name];
818
- writeConfig(config);
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 config = readConfig();
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(30, ...plugins.map(([name]) => name.length + 2));
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".padEnd(versionWidth) + "Installed At"
985
+ " " + "Plugin".padEnd(nameWidth) + "Version"
843
986
  );
844
- console.log(" " + "-".repeat(nameWidth + versionWidth + dateWidth));
845
- for (const [name, info] of plugins) {
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) + info.version.padEnd(versionWidth) + installedAt
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(pluginID) {
921
- return path5.join(getProjectRoot2(), "node_modules", pluginID, "manifest.json");
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(pluginID) {
954
- const manifestPath = getPluginManifestPath(pluginID);
1104
+ function readPluginManifest(pluginKey) {
1105
+ const manifestPath = getPluginManifestPath(pluginKey);
955
1106
  if (!fs5.existsSync(manifestPath)) {
956
- throw new Error(`Plugin not installed: ${pluginID} (manifest.json not found)`);
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: ${pluginID}/manifest.json`);
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(pluginID) {
1125
+ async function loadPlugin(pluginKey) {
975
1126
  try {
976
- const pluginPackage = (await import(pluginID)).default;
1127
+ const pluginPackage = (await import(pluginKey)).default;
977
1128
  if (!pluginPackage || typeof pluginPackage.create !== "function") {
978
- throw new Error(`Plugin ${pluginID} does not export a valid create function`);
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: ${pluginID}`);
1134
+ throw new Error(`Plugin not installed: ${pluginKey}`);
984
1135
  }
985
1136
  throw new Error(
986
- `Failed to load plugin ${pluginID}: ${error instanceof Error ? error.message : String(error)}`
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.pluginID);
1151
+ const manifest = readPluginManifest(capability.pluginKey);
1001
1152
  if (!manifest.actions || manifest.actions.length === 0) {
1002
- throw new Error(`Plugin ${capability.pluginID} has no actions defined`);
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.pluginID);
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
- pluginID: capability.pluginID,
1203
+ pluginKey: capability.pluginKey,
1053
1204
  pluginVersion: capability.pluginVersion,
1054
1205
  name: capability.name,
1055
1206
  description: capability.description,
@@ -1120,117 +1271,290 @@ async function listAll(summary) {
1120
1271
  }
1121
1272
  }
1122
1273
 
1123
- // src/commands/capability/migration.handler.ts
1124
- import fs12 from "fs";
1125
-
1126
- // src/commands/capability/migration/mapping.ts
1127
- var DEFAULT_MAPPING = {
1128
- // 飞书相关
1129
- "official_feishu/create_group": "@official_feishu/create_group",
1130
- "official_feishu/send_message": "@official_feishu/send_message",
1131
- "official_feishu/send_card": "@official_feishu/send_card",
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
- return { ...DEFAULT_MAPPING, ...customMapping };
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 getPluginID(sourceActionID, mapping) {
1145
- const pluginID = mapping[sourceActionID];
1146
- if (!pluginID) {
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
- return pluginID;
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/capability/migration/json-migrator/index.ts
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/capability/migration/json-migrator/detector.ts
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 = getCapabilitiesDir();
1163
- const oldFilePath = path6.join(capabilitiesDir, "capabilities.json");
1164
- if (!fs6.existsSync(capabilitiesDir)) {
1334
+ const capabilitiesDir = getCapabilitiesDir2();
1335
+ const oldFilePath = path8.join(capabilitiesDir, "capabilities.json");
1336
+ if (!fs8.existsSync(oldFilePath)) {
1165
1337
  return {
1166
1338
  needsMigration: false,
1167
- reason: "capabilities directory does not exist"
1168
- };
1169
- }
1170
- if (!fs6.existsSync(oldFilePath)) {
1171
- return {
1172
- needsMigration: false,
1173
- reason: "capabilities.json not found (may already be migrated)"
1339
+ reason: "capabilities.json not found"
1174
1340
  };
1175
1341
  }
1176
1342
  try {
1177
- const content = fs6.readFileSync(oldFilePath, "utf-8");
1343
+ const content = fs8.readFileSync(oldFilePath, "utf-8");
1178
1344
  const parsed = JSON.parse(content);
1179
- if (!Array.isArray(parsed)) {
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: parsed,
1348
+ oldCapabilities: capabilities,
1201
1349
  oldFilePath
1202
1350
  };
1203
1351
  } catch (error) {
1204
- if (error instanceof SyntaxError) {
1205
- return {
1206
- needsMigration: false,
1207
- reason: "capabilities.json contains invalid JSON"
1208
- };
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_feishu/send_message": {
1389
+ pluginKey: "@official-plugins/send-feishu-message",
1390
+ pluginVersion: "1.0.0"
1391
+ },
1392
+ "official_feishu/create_group": {
1393
+ pluginKey: "@official-plugins/feishu-group-create",
1394
+ pluginVersion: "1.0.0"
1395
+ },
1396
+ // AI 相关
1397
+ "official_ai/text_generate": {
1398
+ pluginKey: "@official-plugins/ai-text-generate",
1399
+ pluginVersion: "1.0.0"
1400
+ },
1401
+ "official_ai/image_understanding": {
1402
+ pluginKey: "@official-plugins/ai-image-understanding",
1403
+ pluginVersion: "1.0.0"
1404
+ },
1405
+ "official_ai/image_generate": {
1406
+ pluginKey: "@official-plugins/ai-text-to-image",
1407
+ pluginVersion: "1.0.0"
1408
+ }
1409
+ };
1410
+ function getPluginInfo(sourceActionID) {
1411
+ const item = PLUGIN_MAPPING[sourceActionID];
1412
+ if (!item) {
1413
+ throw new Error(
1414
+ `Unknown sourceActionID: "${sourceActionID}". This sourceActionID is not in the built-in mapping. Please contact the developer to add it.`
1415
+ );
1416
+ }
1417
+ return {
1418
+ pluginKey: item.pluginKey,
1419
+ pluginVersion: item.pluginVersion ?? DEFAULT_PLUGIN_VERSION
1420
+ };
1421
+ }
1422
+
1423
+ // src/commands/migration/versions/v001_capability/json-migrator/form-value-transformers/capabilities/send-feishu-message.ts
1424
+ function transformSendFeishuMessage(input) {
1425
+ const actionInput = input;
1426
+ const cardContent = actionInput.card_content;
1427
+ const header = cardContent?.header;
1428
+ const bodyElements = cardContent?.body?.elements;
1429
+ const title = {
1430
+ title: header?.title?.content ?? "",
1431
+ titleColor: header?.template
1432
+ };
1433
+ const markdownElement = bodyElements?.find((el) => el.tag === "markdown");
1434
+ const content = markdownElement?.content ?? "";
1435
+ const columnSet = bodyElements?.find((el) => el.tag === "column_set");
1436
+ const buttons = columnSet?.columns?.flatMap(
1437
+ (column) => column.elements?.filter((el) => el.tag === "button").map((btn) => ({
1438
+ style: btn.type,
1439
+ text: btn.text?.content,
1440
+ url: btn.behaviors?.[0]?.default_url
1441
+ })) ?? []
1442
+ ) ?? [];
1443
+ return {
1444
+ sender: "bot",
1445
+ receiverUserList: actionInput.receiver_user_list ?? [],
1446
+ receiverGroupList: actionInput.receiver_group_list ?? [],
1447
+ title,
1448
+ content,
1449
+ buttons
1450
+ };
1451
+ }
1452
+
1453
+ // src/commands/migration/versions/v001_capability/json-migrator/form-value-transformers/capabilities/feishu-group-create.ts
1454
+ function transformFeishuGroupCreate(input) {
1455
+ const actionInput = input;
1456
+ return {
1457
+ owner: actionInput.owner,
1458
+ members: actionInput.members ?? [],
1459
+ groupName: actionInput.group_name ?? "",
1460
+ groupDescription: actionInput.group_description ?? "",
1461
+ welcomeMessage: actionInput.welcome_message ?? ""
1462
+ };
1463
+ }
1464
+
1465
+ // src/commands/migration/versions/v001_capability/json-migrator/form-value-transformers/utils.ts
1466
+ function convertToNumber(value, defaultValue) {
1467
+ if (value === void 0 || value === "") {
1468
+ return defaultValue;
1469
+ }
1470
+ const num = Number(value);
1471
+ return isNaN(num) ? defaultValue : num;
1472
+ }
1473
+
1474
+ // src/commands/migration/versions/v001_capability/json-migrator/form-value-transformers/capabilities/ai-text-generate.ts
1475
+ var DEFAULT_MODEL_ID = "87";
1476
+ var DEFAULT_MAX_TOKENS = 8192;
1477
+ var DEFAULT_TEMPERATURE = 0.7;
1478
+ function transformAITextGenerate(input) {
1479
+ const actionInput = input;
1480
+ return {
1481
+ modelID: actionInput.model_id ?? DEFAULT_MODEL_ID,
1482
+ prompt: actionInput.prompt ?? "",
1483
+ modelParams: {
1484
+ maxTokens: convertToNumber(actionInput.max_tokens, DEFAULT_MAX_TOKENS),
1485
+ temperature: convertToNumber(actionInput.temperature, DEFAULT_TEMPERATURE)
1209
1486
  }
1210
- throw error;
1487
+ };
1488
+ }
1489
+
1490
+ // src/commands/migration/versions/v001_capability/json-migrator/form-value-transformers/capabilities/ai-image-understanding.ts
1491
+ var DEFAULT_MODEL_ID2 = "87";
1492
+ var DEFAULT_MAX_TOKENS2 = 8192;
1493
+ var DEFAULT_TEMPERATURE2 = 0.7;
1494
+ function transformAIImageUnderstanding(input) {
1495
+ const actionInput = input;
1496
+ return {
1497
+ prompt: actionInput.prompt ?? "",
1498
+ images: actionInput.image_list ?? [],
1499
+ modelID: actionInput.model_id ?? DEFAULT_MODEL_ID2,
1500
+ modelParams: {
1501
+ maxTokens: convertToNumber(actionInput.max_tokens, DEFAULT_MAX_TOKENS2),
1502
+ temperature: convertToNumber(actionInput.temperature, DEFAULT_TEMPERATURE2)
1503
+ }
1504
+ };
1505
+ }
1506
+
1507
+ // src/commands/migration/versions/v001_capability/json-migrator/form-value-transformers/capabilities/ai-text-to-image.ts
1508
+ var DEFAULT_RATIO = "1:1";
1509
+ var DEFAULT_WATERMARK = true;
1510
+ function transformAITextToImage(input) {
1511
+ const actionInput = input;
1512
+ return {
1513
+ prompt: actionInput.prompt ?? "",
1514
+ ratio: actionInput.image_ratio ?? DEFAULT_RATIO,
1515
+ watermark: actionInput.watermark ?? DEFAULT_WATERMARK
1516
+ };
1517
+ }
1518
+
1519
+ // src/commands/migration/versions/v001_capability/json-migrator/form-value-transformers/index.ts
1520
+ var TRANSFORMER_MAP = {
1521
+ "official_feishu/send_message": transformSendFeishuMessage,
1522
+ "official_feishu/create_group": transformFeishuGroupCreate,
1523
+ "official_ai/text_generate": transformAITextGenerate,
1524
+ "official_ai/image_understanding": transformAIImageUnderstanding,
1525
+ "official_ai/image_generate": transformAITextToImage
1526
+ };
1527
+ function transformFormValue(sourceActionID, actionInput) {
1528
+ const transformer = TRANSFORMER_MAP[sourceActionID];
1529
+ if (!transformer) {
1530
+ throw new Error(
1531
+ `No formValue transformer found for sourceActionID: "${sourceActionID}". Please implement the transformer and add it to TRANSFORMER_MAP.`
1532
+ );
1211
1533
  }
1534
+ return transformer(actionInput);
1212
1535
  }
1213
1536
 
1214
- // src/commands/capability/migration/json-migrator/transformer.ts
1215
- function transformCapability(old, mapping) {
1216
- const pluginID = getPluginID(old.sourceActionID, mapping);
1537
+ // src/commands/migration/versions/v001_capability/json-migrator/transformer.ts
1538
+ function transformCapability(old) {
1539
+ const { pluginKey, pluginVersion } = getPluginInfo(old.sourceActionID);
1540
+ const formValue = transformFormValue(old.sourceActionID, old.actionInput);
1217
1541
  return {
1218
1542
  id: old.id,
1219
- pluginID,
1220
- pluginVersion: "latest",
1543
+ pluginKey,
1544
+ pluginVersion,
1221
1545
  name: old.name,
1222
1546
  description: old.desc,
1223
1547
  paramsSchema: old.inputSchema,
1224
- formValue: old.actionInput,
1548
+ formValue,
1225
1549
  createdAt: old.createdAt,
1226
1550
  updatedAt: old.updatedAt
1227
1551
  };
1228
1552
  }
1229
- function transformCapabilities(oldCapabilities, mapping) {
1230
- return oldCapabilities.map((old) => transformCapability(old, mapping));
1553
+ function transformCapabilities(oldCapabilities) {
1554
+ return oldCapabilities.map((old) => transformCapability(old));
1231
1555
  }
1232
1556
 
1233
- // src/commands/capability/migration/json-migrator/index.ts
1557
+ // src/commands/migration/versions/v001_capability/json-migrator/index.ts
1234
1558
  async function migrateJsonFiles(options) {
1235
1559
  const detection = detectJsonMigration();
1236
1560
  if (!detection.needsMigration) {
@@ -1254,7 +1578,7 @@ async function migrateJsonFiles(options) {
1254
1578
  }
1255
1579
  let newCapabilities;
1256
1580
  try {
1257
- newCapabilities = transformCapabilities(oldCapabilities, options.mapping);
1581
+ newCapabilities = transformCapabilities(oldCapabilities);
1258
1582
  } catch (error) {
1259
1583
  return {
1260
1584
  success: false,
@@ -1267,7 +1591,7 @@ async function migrateJsonFiles(options) {
1267
1591
  if (options.dryRun) {
1268
1592
  console.log(` Would create ${newCapabilities.length} capability files:`);
1269
1593
  for (const cap of newCapabilities) {
1270
- console.log(` - ${cap.id}.json (pluginID: ${cap.pluginID})`);
1594
+ console.log(` - ${cap.id}.json (pluginKey: ${cap.pluginKey})`);
1271
1595
  }
1272
1596
  return {
1273
1597
  success: true,
@@ -1276,48 +1600,45 @@ async function migrateJsonFiles(options) {
1276
1600
  capabilities: newCapabilities
1277
1601
  };
1278
1602
  }
1279
- const capabilitiesDir = getCapabilitiesDir();
1603
+ const capabilitiesDir = getCapabilitiesDir2();
1280
1604
  for (const cap of newCapabilities) {
1281
- const filePath = path7.join(capabilitiesDir, `${cap.id}.json`);
1605
+ const filePath = path9.join(capabilitiesDir, `${cap.id}.json`);
1282
1606
  const content = JSON.stringify(cap, null, 2);
1283
- fs7.writeFileSync(filePath, content, "utf-8");
1607
+ fs9.writeFileSync(filePath, content, "utf-8");
1284
1608
  console.log(` \u2713 Created: ${cap.id}.json`);
1285
1609
  }
1286
- const backupPath = oldFilePath + ".backup";
1287
- fs7.renameSync(oldFilePath, backupPath);
1288
1610
  return {
1289
1611
  success: true,
1290
1612
  skipped: false,
1291
1613
  count: newCapabilities.length,
1292
- capabilities: newCapabilities,
1293
- backupPath
1614
+ capabilities: newCapabilities
1294
1615
  };
1295
1616
  }
1296
1617
 
1297
- // src/commands/capability/migration/plugin-installer/detector.ts
1298
- import fs8 from "fs";
1299
- function isPluginInstalled2(pluginID) {
1300
- const manifestPath = getPluginManifestPath(pluginID);
1301
- return fs8.existsSync(manifestPath);
1618
+ // src/commands/migration/versions/v001_capability/plugin-installer/detector.ts
1619
+ import fs10 from "fs";
1620
+ function isPluginInstalled2(pluginKey) {
1621
+ const manifestPath = getPluginManifestPath2(pluginKey);
1622
+ return fs10.existsSync(manifestPath);
1302
1623
  }
1303
1624
  function detectPluginsToInstall(capabilities) {
1304
- const pluginIDs = /* @__PURE__ */ new Set();
1625
+ const pluginKeys = /* @__PURE__ */ new Set();
1305
1626
  for (const cap of capabilities) {
1306
- pluginIDs.add(cap.pluginID);
1627
+ pluginKeys.add(cap.pluginKey);
1307
1628
  }
1308
1629
  const toInstall = [];
1309
1630
  const alreadyInstalled = [];
1310
- for (const pluginID of pluginIDs) {
1311
- if (isPluginInstalled2(pluginID)) {
1312
- alreadyInstalled.push(pluginID);
1631
+ for (const pluginKey of pluginKeys) {
1632
+ if (isPluginInstalled2(pluginKey)) {
1633
+ alreadyInstalled.push(pluginKey);
1313
1634
  } else {
1314
- toInstall.push(pluginID);
1635
+ toInstall.push(pluginKey);
1315
1636
  }
1316
1637
  }
1317
1638
  return { toInstall, alreadyInstalled };
1318
1639
  }
1319
1640
 
1320
- // src/commands/capability/migration/plugin-installer/index.ts
1641
+ // src/commands/migration/versions/v001_capability/plugin-installer/index.ts
1321
1642
  async function installPlugins(capabilities, options) {
1322
1643
  const detection = detectPluginsToInstall(capabilities);
1323
1644
  const result = {
@@ -1330,15 +1651,15 @@ async function installPlugins(capabilities, options) {
1330
1651
  }
1331
1652
  if (options.dryRun) {
1332
1653
  console.log(` Would install ${detection.toInstall.length} plugins:`);
1333
- for (const pluginID of detection.toInstall) {
1334
- console.log(` - ${pluginID}`);
1654
+ for (const pluginKey of detection.toInstall) {
1655
+ console.log(` - ${pluginKey}`);
1335
1656
  }
1336
1657
  result.installed = detection.toInstall;
1337
1658
  return result;
1338
1659
  }
1339
1660
  console.log(` \u2B07 Installing ${detection.toInstall.length} plugins...`);
1340
- for (const pluginID of detection.toInstall) {
1341
- console.log(` - ${pluginID}`);
1661
+ for (const pluginKey of detection.toInstall) {
1662
+ console.log(` - ${pluginKey}`);
1342
1663
  }
1343
1664
  try {
1344
1665
  const originalExit = process.exit;
@@ -1353,9 +1674,9 @@ async function installPlugins(capabilities, options) {
1353
1674
  await install(detection.toInstall);
1354
1675
  result.installed = detection.toInstall;
1355
1676
  } catch (error) {
1356
- for (const pluginID of detection.toInstall) {
1677
+ for (const pluginKey of detection.toInstall) {
1357
1678
  result.failed.push({
1358
- pluginID,
1679
+ pluginKey,
1359
1680
  error: error instanceof Error ? error.message : String(error)
1360
1681
  });
1361
1682
  }
@@ -1363,9 +1684,9 @@ async function installPlugins(capabilities, options) {
1363
1684
  process.exit = originalExit;
1364
1685
  }
1365
1686
  } catch (error) {
1366
- for (const pluginID of detection.toInstall) {
1687
+ for (const pluginKey of detection.toInstall) {
1367
1688
  result.failed.push({
1368
- pluginID,
1689
+ pluginKey,
1369
1690
  error: error instanceof Error ? error.message : String(error)
1370
1691
  });
1371
1692
  }
@@ -1373,14 +1694,14 @@ async function installPlugins(capabilities, options) {
1373
1694
  return result;
1374
1695
  }
1375
1696
 
1376
- // src/commands/capability/migration/code-migrator/index.ts
1377
- import fs10 from "fs";
1378
- import path9 from "path";
1697
+ // src/commands/migration/versions/v001_capability/code-migrator/index.ts
1698
+ import fs12 from "fs";
1699
+ import path11 from "path";
1379
1700
  import * as ts4 from "typescript";
1380
1701
 
1381
- // src/commands/capability/migration/code-migrator/scanner.ts
1382
- import fs9 from "fs";
1383
- import path8 from "path";
1702
+ // src/commands/migration/versions/v001_capability/code-migrator/scanner.ts
1703
+ import fs11 from "fs";
1704
+ import path10 from "path";
1384
1705
  var EXCLUDED_DIRS = [
1385
1706
  "node_modules",
1386
1707
  "dist",
@@ -1395,9 +1716,9 @@ var EXCLUDED_PATTERNS = [
1395
1716
  /\.d\.ts$/
1396
1717
  ];
1397
1718
  function scanDirectory(dir, files = []) {
1398
- const entries = fs9.readdirSync(dir, { withFileTypes: true });
1719
+ const entries = fs11.readdirSync(dir, { withFileTypes: true });
1399
1720
  for (const entry of entries) {
1400
- const fullPath = path8.join(dir, entry.name);
1721
+ const fullPath = path10.join(dir, entry.name);
1401
1722
  if (entry.isDirectory()) {
1402
1723
  if (EXCLUDED_DIRS.includes(entry.name)) {
1403
1724
  continue;
@@ -1413,14 +1734,14 @@ function scanDirectory(dir, files = []) {
1413
1734
  return files;
1414
1735
  }
1415
1736
  function scanServerFiles() {
1416
- const serverDir = path8.join(getProjectRoot2(), "server");
1417
- if (!fs9.existsSync(serverDir)) {
1737
+ const serverDir = path10.join(getProjectRoot3(), "server");
1738
+ if (!fs11.existsSync(serverDir)) {
1418
1739
  return [];
1419
1740
  }
1420
1741
  return scanDirectory(serverDir);
1421
1742
  }
1422
1743
  function hasCapabilityImport(filePath) {
1423
- const content = fs9.readFileSync(filePath, "utf-8");
1744
+ const content = fs11.readFileSync(filePath, "utf-8");
1424
1745
  return /import\s+.*from\s+['"][^'"]*capabilities[^'"]*['"]/.test(content);
1425
1746
  }
1426
1747
  function scanFilesToMigrate() {
@@ -1428,7 +1749,7 @@ function scanFilesToMigrate() {
1428
1749
  return allFiles.filter(hasCapabilityImport);
1429
1750
  }
1430
1751
 
1431
- // src/commands/capability/migration/code-migrator/analyzers/import-analyzer.ts
1752
+ // src/commands/migration/versions/v001_capability/code-migrator/analyzers/import-analyzer.ts
1432
1753
  import * as ts from "typescript";
1433
1754
  function extractCapabilityId(importPath) {
1434
1755
  const match = importPath.match(/capabilities\/([^/]+)$/);
@@ -1492,7 +1813,7 @@ function analyzeImports(sourceFile) {
1492
1813
  return imports;
1493
1814
  }
1494
1815
 
1495
- // src/commands/capability/migration/code-migrator/analyzers/call-site-analyzer.ts
1816
+ // src/commands/migration/versions/v001_capability/code-migrator/analyzers/call-site-analyzer.ts
1496
1817
  import * as ts2 from "typescript";
1497
1818
  function analyzeCallSites(sourceFile, imports) {
1498
1819
  const callSites = [];
@@ -1542,7 +1863,7 @@ function analyzeCallSites(sourceFile, imports) {
1542
1863
  return callSites;
1543
1864
  }
1544
1865
 
1545
- // src/commands/capability/migration/code-migrator/analyzers/class-analyzer.ts
1866
+ // src/commands/migration/versions/v001_capability/code-migrator/analyzers/class-analyzer.ts
1546
1867
  import * as ts3 from "typescript";
1547
1868
  function hasDecorator(node, decoratorName) {
1548
1869
  const decorators = ts3.getDecorators(node);
@@ -1618,10 +1939,14 @@ function canAutoMigrate(classInfo) {
1618
1939
  return { canMigrate: true };
1619
1940
  }
1620
1941
 
1621
- // src/commands/capability/migration/code-migrator/transformers/import-transformer.ts
1622
- var CAPABILITY_SERVICE_IMPORT = `import { CapabilityService } from '@lark-apaas/nestjs-core';`;
1942
+ // src/commands/migration/versions/v001_capability/code-migrator/transformers/import-transformer.ts
1943
+ var CAPABILITY_SERVICE_IMPORT = `import { CapabilityService, migrationAdaptor } from '@lark-apaas/fullstack-nestjs-core';`;
1944
+ var INJECT_IMPORT = `import { Inject } from '@nestjs/common';`;
1623
1945
  function hasCapabilityServiceImport(content) {
1624
- return /import\s+.*CapabilityService.*from\s+['"]@lark-apaas\/nestjs-core['"]/.test(content);
1946
+ return /import\s+.*CapabilityService.*from\s+['"]@lark-apaas\/fullstack-nestjs-core['"]/.test(content);
1947
+ }
1948
+ function hasInjectImport(content) {
1949
+ return /import\s+.*\bInject\b.*from\s+['"]@nestjs\/common['"]/.test(content);
1625
1950
  }
1626
1951
  function findImportInsertPosition(content) {
1627
1952
  const importRegex = /^import\s+.*?from\s+['"][^'"]+['"];?\s*$/gm;
@@ -1654,11 +1979,20 @@ function transformImports(content, imports) {
1654
1979
  }
1655
1980
  importAdded = true;
1656
1981
  }
1982
+ if (!hasInjectImport(result)) {
1983
+ const insertPos = findImportInsertPosition(result);
1984
+ if (insertPos > 0) {
1985
+ result = result.substring(0, insertPos) + "\n" + INJECT_IMPORT + result.substring(insertPos);
1986
+ } else {
1987
+ result = INJECT_IMPORT + "\n" + result;
1988
+ }
1989
+ importAdded = true;
1990
+ }
1657
1991
  return { content: result, importAdded };
1658
1992
  }
1659
1993
 
1660
- // src/commands/capability/migration/code-migrator/transformers/injection-transformer.ts
1661
- var INJECTION_PARAM = "private readonly capabilityService: CapabilityService";
1994
+ // src/commands/migration/versions/v001_capability/code-migrator/transformers/injection-transformer.ts
1995
+ var INJECTION_PARAM = "@Inject() private readonly capabilityService: CapabilityService";
1662
1996
  function hasCapabilityServiceInjection(content) {
1663
1997
  return /capabilityService\s*:\s*CapabilityService/.test(content);
1664
1998
  }
@@ -1687,7 +2021,7 @@ function addInjection(content, classInfo) {
1687
2021
  return { content: result, injectionAdded: result !== content };
1688
2022
  }
1689
2023
 
1690
- // src/commands/capability/migration/code-migrator/transformers/call-site-transformer.ts
2024
+ // src/commands/migration/versions/v001_capability/code-migrator/transformers/call-site-transformer.ts
1691
2025
  function generateNewCall(capabilityId, originalCall) {
1692
2026
  const parenIndex = originalCall.indexOf("(");
1693
2027
  if (parenIndex === -1) {
@@ -1695,14 +2029,14 @@ function generateNewCall(capabilityId, originalCall) {
1695
2029
  }
1696
2030
  const argsStart = parenIndex + 1;
1697
2031
  const argsEnd = originalCall.lastIndexOf(")");
1698
- if (argsEnd === -1 || argsEnd <= argsStart) {
1699
- return `this.capabilityService.load('${capabilityId}').call('run', {})`;
1700
- }
1701
- const args = originalCall.substring(argsStart, argsEnd).trim();
1702
- if (!args) {
1703
- return `this.capabilityService.load('${capabilityId}').call('run', {})`;
2032
+ let args = "{}";
2033
+ if (argsEnd !== -1 && argsEnd > argsStart) {
2034
+ const extractedArgs = originalCall.substring(argsStart, argsEnd).trim();
2035
+ if (extractedArgs) {
2036
+ args = extractedArgs;
2037
+ }
1704
2038
  }
1705
- return `this.capabilityService.load('${capabilityId}').call('run', ${args})`;
2039
+ return `migrationAdaptor(this.capabilityService.load('${capabilityId}').call('run', ${args}))`;
1706
2040
  }
1707
2041
  function transformCallSites(content, callSites) {
1708
2042
  const sortedCallSites = [...callSites].sort((a, b) => b.start - a.start);
@@ -1718,9 +2052,9 @@ function transformCallSites(content, callSites) {
1718
2052
  return { content: result, replacedCount };
1719
2053
  }
1720
2054
 
1721
- // src/commands/capability/migration/code-migrator/index.ts
2055
+ // src/commands/migration/versions/v001_capability/code-migrator/index.ts
1722
2056
  function analyzeFile(filePath) {
1723
- const content = fs10.readFileSync(filePath, "utf-8");
2057
+ const content = fs12.readFileSync(filePath, "utf-8");
1724
2058
  const sourceFile = ts4.createSourceFile(
1725
2059
  filePath,
1726
2060
  content,
@@ -1731,7 +2065,7 @@ function analyzeFile(filePath) {
1731
2065
  const callSites = analyzeCallSites(sourceFile, imports);
1732
2066
  const classInfo = analyzeClass(sourceFile);
1733
2067
  const { canMigrate, reason } = canAutoMigrate(classInfo);
1734
- const relativePath = path9.relative(getProjectRoot2(), filePath);
2068
+ const relativePath = path11.relative(getProjectRoot3(), filePath);
1735
2069
  return {
1736
2070
  filePath: relativePath,
1737
2071
  imports,
@@ -1742,7 +2076,7 @@ function analyzeFile(filePath) {
1742
2076
  };
1743
2077
  }
1744
2078
  function migrateFile(analysis, dryRun) {
1745
- const absolutePath = path9.join(getProjectRoot2(), analysis.filePath);
2079
+ const absolutePath = path11.join(getProjectRoot3(), analysis.filePath);
1746
2080
  if (!analysis.canAutoMigrate) {
1747
2081
  return {
1748
2082
  filePath: analysis.filePath,
@@ -1754,7 +2088,7 @@ function migrateFile(analysis, dryRun) {
1754
2088
  };
1755
2089
  }
1756
2090
  try {
1757
- let content = fs10.readFileSync(absolutePath, "utf-8");
2091
+ let content = fs12.readFileSync(absolutePath, "utf-8");
1758
2092
  const callResult = transformCallSites(content, analysis.callSites);
1759
2093
  content = callResult.content;
1760
2094
  const sourceFile = ts4.createSourceFile(
@@ -1782,7 +2116,7 @@ function migrateFile(analysis, dryRun) {
1782
2116
  }
1783
2117
  }
1784
2118
  if (!dryRun) {
1785
- fs10.writeFileSync(absolutePath, content, "utf-8");
2119
+ fs12.writeFileSync(absolutePath, content, "utf-8");
1786
2120
  }
1787
2121
  return {
1788
2122
  filePath: analysis.filePath,
@@ -1848,12 +2182,50 @@ function getSuggestion(analysis) {
1848
2182
  return "Manual review required";
1849
2183
  }
1850
2184
 
1851
- // src/commands/capability/migration/report-generator.ts
1852
- import fs11 from "fs";
1853
- import path10 from "path";
2185
+ // src/commands/migration/versions/v001_capability/cleanup.ts
2186
+ import fs13 from "fs";
2187
+ import path12 from "path";
2188
+ function cleanupOldFiles(capabilities, dryRun) {
2189
+ const deletedFiles = [];
2190
+ const errors = [];
2191
+ const capabilitiesDir = getCapabilitiesDir2();
2192
+ const oldJsonPath = path12.join(capabilitiesDir, "capabilities.json");
2193
+ if (fs13.existsSync(oldJsonPath)) {
2194
+ try {
2195
+ if (!dryRun) {
2196
+ fs13.unlinkSync(oldJsonPath);
2197
+ }
2198
+ deletedFiles.push("capabilities.json");
2199
+ } catch (error) {
2200
+ errors.push(`Failed to delete capabilities.json: ${error instanceof Error ? error.message : String(error)}`);
2201
+ }
2202
+ }
2203
+ for (const cap of capabilities) {
2204
+ const tsFilePath = path12.join(capabilitiesDir, `${cap.id}.ts`);
2205
+ if (fs13.existsSync(tsFilePath)) {
2206
+ try {
2207
+ if (!dryRun) {
2208
+ fs13.unlinkSync(tsFilePath);
2209
+ }
2210
+ deletedFiles.push(`${cap.id}.ts`);
2211
+ } catch (error) {
2212
+ errors.push(`Failed to delete ${cap.id}.ts: ${error instanceof Error ? error.message : String(error)}`);
2213
+ }
2214
+ }
2215
+ }
2216
+ return {
2217
+ success: errors.length === 0,
2218
+ deletedFiles,
2219
+ errors
2220
+ };
2221
+ }
2222
+
2223
+ // src/commands/migration/versions/v001_capability/report-generator.ts
2224
+ import fs14 from "fs";
2225
+ import path13 from "path";
1854
2226
  var REPORT_FILE = "capability-migration-report.md";
1855
2227
  function printSummary(result) {
1856
- const { jsonMigration, pluginInstallation, codeMigration } = result;
2228
+ const { jsonMigration, pluginInstallation, codeMigration, cleanup } = result;
1857
2229
  console.log("\u{1F4CA} Migration Summary");
1858
2230
  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
2231
  if (!jsonMigration.skipped) {
@@ -1878,6 +2250,9 @@ function printSummary(result) {
1878
2250
  console.log(` Files need manual work: ${codeMigration.manualRequired.length}`);
1879
2251
  }
1880
2252
  console.log(` Total call sites fixed: ${totalCallSites}`);
2253
+ if (cleanup.deletedFiles.length > 0) {
2254
+ console.log(` Old files cleaned up: ${cleanup.deletedFiles.length}`);
2255
+ }
1881
2256
  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
2257
  const hasErrors = pluginInstallation.failed.length > 0 || failedMigrations.length > 0;
1883
2258
  const hasManual = codeMigration.manualRequired.length > 0;
@@ -1898,7 +2273,7 @@ function printSummary(result) {
1898
2273
  console.log("");
1899
2274
  }
1900
2275
  async function generateReport(result) {
1901
- const { jsonMigration, pluginInstallation, codeMigration } = result;
2276
+ const { jsonMigration, pluginInstallation, codeMigration, cleanup } = result;
1902
2277
  const lines = [];
1903
2278
  lines.push("# Capability \u8FC1\u79FB\u62A5\u544A");
1904
2279
  lines.push("");
@@ -1911,34 +2286,28 @@ async function generateReport(result) {
1911
2286
  } else {
1912
2287
  lines.push("### 1.1 \u5DF2\u8FC1\u79FB\u7684 Capabilities");
1913
2288
  lines.push("");
1914
- lines.push("| ID | pluginID |");
1915
- lines.push("|----|----------|");
2289
+ lines.push("| ID | pluginKey |");
2290
+ lines.push("|----|-----------|");
1916
2291
  for (const cap of jsonMigration.capabilities) {
1917
- lines.push(`| ${cap.id} | ${cap.pluginID} |`);
2292
+ lines.push(`| ${cap.id} | ${cap.pluginKey} |`);
1918
2293
  }
1919
2294
  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
2295
  }
1927
2296
  lines.push("## 2. \u63D2\u4EF6\u5B89\u88C5");
1928
2297
  lines.push("");
1929
2298
  if (pluginInstallation.installed.length === 0 && pluginInstallation.alreadyInstalled.length === 0) {
1930
2299
  lines.push("\u65E0\u63D2\u4EF6\u9700\u8981\u5B89\u88C5\u3002");
1931
2300
  } else {
1932
- lines.push("| pluginID | \u72B6\u6001 |");
1933
- lines.push("|----------|------|");
1934
- for (const pluginID of pluginInstallation.alreadyInstalled) {
1935
- lines.push(`| ${pluginID} | \u5DF2\u5B89\u88C5 |`);
2301
+ lines.push("| pluginKey | \u72B6\u6001 |");
2302
+ lines.push("|-----------|------|");
2303
+ for (const pluginKey of pluginInstallation.alreadyInstalled) {
2304
+ lines.push(`| ${pluginKey} | \u5DF2\u5B89\u88C5 |`);
1936
2305
  }
1937
- for (const pluginID of pluginInstallation.installed) {
1938
- lines.push(`| ${pluginID} | \u65B0\u5B89\u88C5 |`);
2306
+ for (const pluginKey of pluginInstallation.installed) {
2307
+ lines.push(`| ${pluginKey} | \u65B0\u5B89\u88C5 |`);
1939
2308
  }
1940
2309
  for (const failed of pluginInstallation.failed) {
1941
- lines.push(`| ${failed.pluginID} | \u274C \u5931\u8D25: ${failed.error} |`);
2310
+ lines.push(`| ${failed.pluginKey} | \u274C \u5931\u8D25: ${failed.error} |`);
1942
2311
  }
1943
2312
  }
1944
2313
  lines.push("");
@@ -1983,7 +2352,30 @@ async function generateReport(result) {
1983
2352
  lines.push("");
1984
2353
  }
1985
2354
  }
1986
- lines.push("## 4. \u9A8C\u8BC1\u6E05\u5355");
2355
+ lines.push("## 4. \u6E05\u7406\u8001\u6587\u4EF6");
2356
+ lines.push("");
2357
+ if (cleanup.deletedFiles.length === 0 && cleanup.errors.length === 0) {
2358
+ lines.push("\u65E0\u6587\u4EF6\u9700\u8981\u6E05\u7406\u3002");
2359
+ } else {
2360
+ if (cleanup.deletedFiles.length > 0) {
2361
+ lines.push("### 4.1 \u5DF2\u5220\u9664\u7684\u6587\u4EF6");
2362
+ lines.push("");
2363
+ for (const file of cleanup.deletedFiles) {
2364
+ lines.push(`- \`${file}\``);
2365
+ }
2366
+ lines.push("");
2367
+ }
2368
+ if (cleanup.errors.length > 0) {
2369
+ lines.push("### 4.2 \u6E05\u7406\u5931\u8D25");
2370
+ lines.push("");
2371
+ for (const err of cleanup.errors) {
2372
+ lines.push(`- \u274C ${err}`);
2373
+ }
2374
+ lines.push("");
2375
+ }
2376
+ }
2377
+ lines.push("");
2378
+ lines.push("## 5. \u9A8C\u8BC1\u6E05\u5355");
1987
2379
  lines.push("");
1988
2380
  lines.push("- [ ] \u8FD0\u884C `git diff` \u68C0\u67E5\u4EE3\u7801\u53D8\u66F4");
1989
2381
  lines.push("- [ ] \u8FD0\u884C `npm run typecheck` \u68C0\u67E5\u7C7B\u578B");
@@ -1991,40 +2383,74 @@ async function generateReport(result) {
1991
2383
  if (codeMigration.manualRequired.length > 0) {
1992
2384
  lines.push("- [ ] \u5904\u7406\u624B\u52A8\u8FC1\u79FB\u9879");
1993
2385
  }
1994
- lines.push("- [ ] \u5220\u9664\u5907\u4EFD\u6587\u4EF6\uFF08\u786E\u8BA4\u65E0\u8BEF\u540E\uFF09");
1995
2386
  lines.push("");
1996
- const reportPath = path10.join(getProjectRoot2(), REPORT_FILE);
1997
- fs11.writeFileSync(reportPath, lines.join("\n"), "utf-8");
2387
+ const reportPath = path13.join(getProjectRoot3(), REPORT_FILE);
2388
+ fs14.writeFileSync(reportPath, lines.join("\n"), "utf-8");
1998
2389
  console.log(`\u{1F4C4} Report generated: ${REPORT_FILE}`);
1999
2390
  }
2000
2391
 
2001
- // src/commands/capability/migration/index.ts
2002
- async function runMigration(options = {}) {
2392
+ // src/commands/migration/versions/v001_capability/migration-runner.ts
2393
+ async function runCapabilityMigration(options = {}) {
2003
2394
  const fullOptions = {
2004
- dryRun: options.dryRun ?? false,
2005
- skipInstall: options.skipInstall ?? false,
2006
- skipCode: options.skipCode ?? false,
2007
- mapping: mergeMapping(options.mapping)
2395
+ dryRun: options.dryRun ?? false
2008
2396
  };
2009
2397
  console.log("\u{1F50D} Analyzing project...\n");
2010
2398
  if (fullOptions.dryRun) {
2011
2399
  console.log("\u{1F4CB} Running in dry-run mode (no files will be modified)\n");
2012
2400
  }
2401
+ let jsonResult = {
2402
+ success: false,
2403
+ skipped: true,
2404
+ reason: "Not executed",
2405
+ count: 0,
2406
+ capabilities: []
2407
+ };
2408
+ let pluginResult = {
2409
+ installed: [],
2410
+ alreadyInstalled: [],
2411
+ failed: []
2412
+ };
2413
+ let codeResult = {
2414
+ autoMigrated: [],
2415
+ manualRequired: []
2416
+ };
2417
+ let cleanupResult = {
2418
+ success: true,
2419
+ deletedFiles: [],
2420
+ errors: []
2421
+ };
2013
2422
  console.log("\u{1F4C1} Step 1: JSON File Migration");
2014
- const jsonResult = await migrateJsonFiles(fullOptions);
2015
- if (jsonResult.skipped) {
2016
- console.log(` \u25CB Skipped: ${jsonResult.reason}
2423
+ try {
2424
+ jsonResult = await migrateJsonFiles(fullOptions);
2425
+ if (jsonResult.skipped) {
2426
+ console.log(` \u25CB Skipped: ${jsonResult.reason}
2427
+ `);
2428
+ } else if (jsonResult.success) {
2429
+ console.log(` \u2713 Created ${jsonResult.count} capability files
2017
2430
  `);
2018
- } else if (jsonResult.success) {
2019
- console.log(` \u2713 Migrated ${jsonResult.count} capabilities`);
2020
- if (jsonResult.backupPath) {
2021
- console.log(` \u2713 Backed up: capabilities.json \u2192 capabilities.json.backup`);
2431
+ } else {
2432
+ console.log(` \u2717 Failed: ${jsonResult.reason}
2433
+ `);
2434
+ return buildResult(jsonResult, pluginResult, codeResult, cleanupResult);
2022
2435
  }
2023
- console.log("");
2436
+ } catch (error) {
2437
+ const errorMsg = error instanceof Error ? error.message : String(error);
2438
+ console.log(` \u2717 Error: ${errorMsg}
2439
+ `);
2440
+ jsonResult = {
2441
+ success: false,
2442
+ skipped: false,
2443
+ reason: errorMsg,
2444
+ count: 0,
2445
+ capabilities: []
2446
+ };
2447
+ return buildResult(jsonResult, pluginResult, codeResult, cleanupResult);
2024
2448
  }
2025
- let pluginResult = { installed: [], alreadyInstalled: [], failed: [] };
2026
- if (!fullOptions.skipInstall) {
2027
- console.log("\u{1F4E6} Step 2: Plugin Installation");
2449
+ if (jsonResult.skipped) {
2450
+ return buildResult(jsonResult, pluginResult, codeResult, cleanupResult);
2451
+ }
2452
+ console.log("\u{1F4E6} Step 2: Plugin Installation");
2453
+ try {
2028
2454
  pluginResult = await installPlugins(jsonResult.capabilities, fullOptions);
2029
2455
  if (pluginResult.alreadyInstalled.length > 0) {
2030
2456
  console.log(` \u2713 Already installed: ${pluginResult.alreadyInstalled.join(", ")}`);
@@ -2033,18 +2459,27 @@ async function runMigration(options = {}) {
2033
2459
  console.log(` \u2713 Installed: ${pluginResult.installed.join(", ")}`);
2034
2460
  }
2035
2461
  if (pluginResult.failed.length > 0) {
2036
- console.log(` \u2717 Failed: ${pluginResult.failed.map((f) => f.pluginID).join(", ")}`);
2462
+ console.log(` \u26A0 Failed (will continue): ${pluginResult.failed.map((f) => f.pluginKey).join(", ")}`);
2463
+ for (const f of pluginResult.failed) {
2464
+ console.log(` - ${f.pluginKey}: ${f.error}`);
2465
+ }
2037
2466
  }
2038
2467
  console.log("");
2039
- } else {
2040
- console.log("\u{1F4E6} Step 2: Plugin Installation");
2041
- console.log(" \u25CB Skipped (--skip-install)\n");
2468
+ } catch (error) {
2469
+ const errorMsg = error instanceof Error ? error.message : String(error);
2470
+ console.log(` \u26A0 Error (will continue): ${errorMsg}
2471
+ `);
2472
+ pluginResult = {
2473
+ installed: [],
2474
+ alreadyInstalled: [],
2475
+ failed: [{ pluginKey: "unknown", error: errorMsg }]
2476
+ };
2042
2477
  }
2043
- let codeResult = { autoMigrated: [], manualRequired: [] };
2044
- if (!fullOptions.skipCode) {
2045
- console.log("\u{1F527} Step 3: Code Migration");
2046
- console.log(" Scanning server/ directory...\n");
2478
+ console.log("\u{1F527} Step 3: Code Migration");
2479
+ console.log(" Scanning server/ directory...\n");
2480
+ try {
2047
2481
  codeResult = await migrateCode(fullOptions);
2482
+ let hasCodeError = false;
2048
2483
  for (const file of codeResult.autoMigrated) {
2049
2484
  if (file.success) {
2050
2485
  console.log(` \u2713 ${file.filePath}`);
@@ -2055,6 +2490,7 @@ async function runMigration(options = {}) {
2055
2490
  console.log(` - Replaced ${file.callSitesReplaced} call sites`);
2056
2491
  } else {
2057
2492
  console.log(` \u2717 ${file.filePath}: ${file.error}`);
2493
+ hasCodeError = true;
2058
2494
  }
2059
2495
  }
2060
2496
  if (codeResult.manualRequired.length > 0) {
@@ -2065,93 +2501,414 @@ async function runMigration(options = {}) {
2065
2501
  }
2066
2502
  }
2067
2503
  console.log("");
2068
- } else {
2069
- console.log("\u{1F527} Step 3: Code Migration");
2070
- console.log(" \u25CB Skipped (--skip-code)\n");
2504
+ if (hasCodeError) {
2505
+ console.log(" \u2717 Code migration failed, aborting...\n");
2506
+ return buildResult(jsonResult, pluginResult, codeResult, cleanupResult);
2507
+ }
2508
+ } catch (error) {
2509
+ const errorMsg = error instanceof Error ? error.message : String(error);
2510
+ console.log(` \u2717 Error: ${errorMsg}
2511
+ `);
2512
+ codeResult = {
2513
+ autoMigrated: [],
2514
+ manualRequired: []
2515
+ };
2516
+ return buildResult(jsonResult, pluginResult, codeResult, cleanupResult);
2071
2517
  }
2072
- const result = {
2073
- jsonMigration: jsonResult,
2074
- pluginInstallation: pluginResult,
2075
- codeMigration: codeResult
2076
- };
2518
+ console.log("\u{1F9F9} Step 4: Cleanup Old Files");
2519
+ try {
2520
+ cleanupResult = cleanupOldFiles(jsonResult.capabilities, fullOptions.dryRun);
2521
+ if (cleanupResult.deletedFiles.length > 0) {
2522
+ for (const file of cleanupResult.deletedFiles) {
2523
+ console.log(` \u2713 Deleted: ${file}`);
2524
+ }
2525
+ } else {
2526
+ console.log(" \u25CB No files to clean up");
2527
+ }
2528
+ if (cleanupResult.errors.length > 0) {
2529
+ for (const err of cleanupResult.errors) {
2530
+ console.log(` \u26A0 ${err}`);
2531
+ }
2532
+ }
2533
+ console.log("");
2534
+ } catch (error) {
2535
+ const errorMsg = error instanceof Error ? error.message : String(error);
2536
+ console.log(` \u26A0 Error: ${errorMsg}
2537
+ `);
2538
+ cleanupResult = {
2539
+ success: false,
2540
+ deletedFiles: [],
2541
+ errors: [errorMsg]
2542
+ };
2543
+ }
2544
+ const result = buildResult(jsonResult, pluginResult, codeResult, cleanupResult);
2077
2545
  printSummary(result);
2078
2546
  if (!fullOptions.dryRun) {
2079
2547
  await generateReport(result);
2080
2548
  }
2081
2549
  return result;
2082
2550
  }
2551
+ function buildResult(jsonMigration, pluginInstallation, codeMigration, cleanup) {
2552
+ return {
2553
+ jsonMigration,
2554
+ pluginInstallation,
2555
+ codeMigration,
2556
+ cleanup
2557
+ };
2558
+ }
2083
2559
 
2084
- // src/commands/capability/migration.handler.ts
2085
- async function migration(options = {}) {
2560
+ // src/commands/migration/versions/v001_capability/run.ts
2561
+ async function run3(options) {
2086
2562
  try {
2087
- let customMapping;
2088
- if (options.mapping) {
2089
- if (!fs12.existsSync(options.mapping)) {
2090
- console.error(`[capability] Mapping file not found: ${options.mapping}`);
2091
- process.exit(1);
2092
- }
2093
- try {
2094
- const content = fs12.readFileSync(options.mapping, "utf-8");
2095
- customMapping = JSON.parse(content);
2096
- } catch (error) {
2097
- console.error(`[capability] Failed to parse mapping file: ${error}`);
2098
- process.exit(1);
2563
+ const migrationOptions = {
2564
+ dryRun: options.dryRun ?? false
2565
+ };
2566
+ const result = await runCapabilityMigration(migrationOptions);
2567
+ const { jsonMigration, pluginInstallation, codeMigration, cleanup } = result;
2568
+ const jsonFailed = !jsonMigration.success && !jsonMigration.skipped;
2569
+ const codeFailed = codeMigration.autoMigrated.some((f) => !f.success);
2570
+ if (jsonFailed) {
2571
+ return {
2572
+ success: false,
2573
+ message: jsonMigration.reason || "JSON migration failed",
2574
+ error: new Error(jsonMigration.reason || "Unknown error")
2575
+ };
2576
+ }
2577
+ if (codeFailed) {
2578
+ return {
2579
+ success: false,
2580
+ message: "Code migration failed",
2581
+ error: new Error("Some files failed to migrate")
2582
+ };
2583
+ }
2584
+ const messages = [];
2585
+ if (jsonMigration.success) {
2586
+ messages.push(`${jsonMigration.count} capabilities migrated`);
2587
+ }
2588
+ if (pluginInstallation.installed.length > 0) {
2589
+ messages.push(`${pluginInstallation.installed.length} plugins installed`);
2590
+ }
2591
+ if (codeMigration.autoMigrated.length > 0) {
2592
+ const successCount = codeMigration.autoMigrated.filter((f) => f.success).length;
2593
+ if (successCount > 0) {
2594
+ messages.push(`${successCount} files auto-migrated`);
2099
2595
  }
2100
2596
  }
2101
- const migrationOptions = {
2102
- dryRun: options.dryRun,
2103
- skipInstall: options.skipInstall,
2104
- skipCode: options.skipCode,
2105
- mapping: customMapping
2597
+ if (cleanup.deletedFiles.length > 0) {
2598
+ messages.push(`${cleanup.deletedFiles.length} old files cleaned up`);
2599
+ }
2600
+ if (codeMigration.manualRequired.length > 0) {
2601
+ messages.push(`${codeMigration.manualRequired.length} files need manual migration`);
2602
+ }
2603
+ if (pluginInstallation.failed.length > 0) {
2604
+ messages.push(`${pluginInstallation.failed.length} plugins failed to install`);
2605
+ }
2606
+ return {
2607
+ success: true,
2608
+ message: messages.join(", ") || "No migration needed"
2106
2609
  };
2107
- await runMigration(migrationOptions);
2108
2610
  } catch (error) {
2109
- console.error(`[capability] Migration failed: ${error}`);
2110
- process.exit(1);
2611
+ const err = error instanceof Error ? error : new Error(String(error));
2612
+ return {
2613
+ success: false,
2614
+ message: err.message,
2615
+ error: err
2616
+ };
2111
2617
  }
2112
2618
  }
2113
2619
 
2114
- // src/commands/capability/index.ts
2115
- var listCommand2 = {
2116
- name: "list",
2117
- description: "List capability configurations",
2118
- aliases: ["ls"],
2119
- register(program) {
2120
- 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) => {
2121
- await list2(options);
2122
- });
2123
- }
2620
+ // src/commands/migration/versions/v001_capability/index.ts
2621
+ var v001CapabilityMigration = {
2622
+ version: 1,
2623
+ name: "capability",
2624
+ description: "Migrate capability configurations from old format (capabilities.json array) to new format (individual JSON files)",
2625
+ check,
2626
+ run: run3
2124
2627
  };
2628
+
2629
+ // src/commands/migration/versions/index.ts
2630
+ var versionedMigrations = [
2631
+ v001CapabilityMigration
2632
+ // 未来新增的迁移脚本在此添加,如:
2633
+ // v002ConfigMigration,
2634
+ // v003RoutesMigration,
2635
+ ];
2636
+ function getPendingMigrations(currentVersion) {
2637
+ return versionedMigrations.filter((m) => m.version > currentVersion).sort((a, b) => a.version - b.version);
2638
+ }
2639
+ function getLatestVersion() {
2640
+ if (versionedMigrations.length === 0) return 0;
2641
+ return Math.max(...versionedMigrations.map((m) => m.version));
2642
+ }
2643
+
2644
+ // src/commands/migration/runner.ts
2645
+ function log(message) {
2646
+ console.log(`[migration] ${message}`);
2647
+ }
2648
+ async function executeSingle(migration, options) {
2649
+ const { version, name } = migration;
2650
+ const versionTag = `v${version}`;
2651
+ try {
2652
+ log(`Checking [${versionTag}] ${name}...`);
2653
+ const checkResult = await migration.check(options);
2654
+ if (!checkResult.needsMigration) {
2655
+ log(`[${versionTag}] ${name}: no migration needed`);
2656
+ return {
2657
+ name,
2658
+ status: "skipped",
2659
+ message: checkResult.message || "No migration needed"
2660
+ };
2661
+ }
2662
+ log(`[${versionTag}] ${name}: needs migration (${checkResult.message})`);
2663
+ if (checkResult.items && checkResult.items.length > 0) {
2664
+ for (const item of checkResult.items) {
2665
+ console.log(` - ${item}`);
2666
+ }
2667
+ }
2668
+ if (options.dryRun) {
2669
+ return {
2670
+ name,
2671
+ status: "skipped",
2672
+ message: `Dry-run: ${checkResult.message}`
2673
+ };
2674
+ }
2675
+ log(`Running [${versionTag}] ${name} migration...`);
2676
+ const runResult = await migration.run(options);
2677
+ if (runResult.success) {
2678
+ log(`\u2713 [${versionTag}] ${name}: completed (${runResult.message})`);
2679
+ return {
2680
+ name,
2681
+ status: "completed",
2682
+ message: runResult.message
2683
+ };
2684
+ } else {
2685
+ log(`\u2717 [${versionTag}] ${name}: failed`);
2686
+ if (runResult.error) {
2687
+ console.log(` Error: ${runResult.error.message}`);
2688
+ }
2689
+ return {
2690
+ name,
2691
+ status: "failed",
2692
+ message: runResult.message,
2693
+ error: runResult.error
2694
+ };
2695
+ }
2696
+ } catch (error) {
2697
+ const err = error instanceof Error ? error : new Error(String(error));
2698
+ log(`\u2717 [${versionTag}] ${name}: failed`);
2699
+ console.log(` Error: ${err.message}`);
2700
+ return {
2701
+ name,
2702
+ status: "failed",
2703
+ message: err.message,
2704
+ error: err
2705
+ };
2706
+ }
2707
+ }
2708
+ function printSummary2(summary, dryRun) {
2709
+ console.log("");
2710
+ 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");
2711
+ console.log(dryRun ? "Dry-run Summary:" : "Migration Summary:");
2712
+ if (dryRun) {
2713
+ const needsMigration = summary.skipped.filter((s) => s.message.startsWith("Dry-run:")).length;
2714
+ const noAction = summary.skipped.filter((s) => !s.message.startsWith("Dry-run:")).length;
2715
+ console.log(` Needs migration: ${needsMigration}`);
2716
+ console.log(` No action needed: ${noAction}`);
2717
+ } else {
2718
+ console.log(` \u2713 Completed: ${summary.completed.length}`);
2719
+ console.log(` \u25CB Skipped: ${summary.skipped.length} (no migration needed)`);
2720
+ console.log(` \u2717 Failed: ${summary.failed.length}`);
2721
+ }
2722
+ 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");
2723
+ }
2724
+ async function runMigrations(options) {
2725
+ const currentVersion = getCurrentVersion();
2726
+ const latestVersion = getLatestVersion();
2727
+ console.log("");
2728
+ log(`Current version: ${currentVersion || "0 (not set)"}`);
2729
+ if (currentVersion >= latestVersion) {
2730
+ log("All migrations are up to date.");
2731
+ return {
2732
+ needsMigration: false,
2733
+ completed: [],
2734
+ skipped: [],
2735
+ failed: []
2736
+ };
2737
+ }
2738
+ const pendingMigrations = getPendingMigrations(currentVersion);
2739
+ if (options.dryRun) {
2740
+ log("Dry-run mode enabled, no changes will be made.");
2741
+ } else {
2742
+ log("Applying migrations...");
2743
+ }
2744
+ console.log("");
2745
+ const summary = {
2746
+ needsMigration: false,
2747
+ completed: [],
2748
+ skipped: [],
2749
+ failed: []
2750
+ };
2751
+ let hasAnyMigration = false;
2752
+ for (const migration of pendingMigrations) {
2753
+ const result = await executeSingle(migration, options);
2754
+ switch (result.status) {
2755
+ case "completed":
2756
+ summary.completed.push(result);
2757
+ hasAnyMigration = true;
2758
+ if (!options.dryRun) {
2759
+ setCurrentVersion(migration.version);
2760
+ }
2761
+ break;
2762
+ case "skipped":
2763
+ summary.skipped.push(result);
2764
+ if (result.message.startsWith("Dry-run:")) {
2765
+ hasAnyMigration = true;
2766
+ }
2767
+ break;
2768
+ case "failed":
2769
+ summary.failed.push(result);
2770
+ hasAnyMigration = true;
2771
+ log("Migration aborted due to failure.");
2772
+ printSummary2(summary, !!options.dryRun);
2773
+ return summary;
2774
+ }
2775
+ console.log("");
2776
+ }
2777
+ summary.needsMigration = hasAnyMigration;
2778
+ if (currentVersion === 0 && !hasAnyMigration && !options.dryRun) {
2779
+ log(`All migrations skipped. Project is up to date.`);
2780
+ log(`Updated migrationVersion to ${latestVersion}.`);
2781
+ setCurrentVersion(latestVersion);
2782
+ } else if (!options.dryRun && summary.completed.length > 0) {
2783
+ log(`Migration complete. Updated to version ${getCurrentVersion()}.`);
2784
+ }
2785
+ printSummary2(summary, !!options.dryRun);
2786
+ return summary;
2787
+ }
2788
+ async function checkMigrations(options) {
2789
+ const currentVersion = getCurrentVersion();
2790
+ const latestVersion = getLatestVersion();
2791
+ console.log("");
2792
+ log(`Current version: ${currentVersion || "0 (not set)"}`);
2793
+ if (currentVersion >= latestVersion) {
2794
+ log("All migrations are up to date.");
2795
+ return {
2796
+ needsMigration: false,
2797
+ completed: [],
2798
+ skipped: [],
2799
+ failed: []
2800
+ };
2801
+ }
2802
+ log("Checking pending migrations...");
2803
+ console.log("");
2804
+ const pendingMigrations = getPendingMigrations(currentVersion);
2805
+ const summary = {
2806
+ needsMigration: false,
2807
+ completed: [],
2808
+ skipped: [],
2809
+ failed: []
2810
+ };
2811
+ let needsCount = 0;
2812
+ let noActionCount = 0;
2813
+ for (const migration of pendingMigrations) {
2814
+ const { version, name } = migration;
2815
+ const versionTag = `v${version}`;
2816
+ try {
2817
+ const checkResult = await migration.check(options);
2818
+ if (checkResult.needsMigration) {
2819
+ needsCount++;
2820
+ console.log(` ${versionTag} (${name}): needs migration`);
2821
+ if (checkResult.items && checkResult.items.length > 0) {
2822
+ for (const item of checkResult.items) {
2823
+ console.log(` - ${item}`);
2824
+ }
2825
+ }
2826
+ summary.skipped.push({
2827
+ name,
2828
+ status: "skipped",
2829
+ message: `Needs migration: ${checkResult.message}`
2830
+ });
2831
+ } else {
2832
+ noActionCount++;
2833
+ console.log(` ${versionTag} (${name}): no migration needed`);
2834
+ summary.skipped.push({
2835
+ name,
2836
+ status: "skipped",
2837
+ message: checkResult.message || "No migration needed"
2838
+ });
2839
+ }
2840
+ } catch (error) {
2841
+ const err = error instanceof Error ? error : new Error(String(error));
2842
+ console.log(` ${versionTag} (${name}): check failed`);
2843
+ console.log(` Error: ${err.message}`);
2844
+ summary.failed.push({
2845
+ name,
2846
+ status: "failed",
2847
+ message: err.message,
2848
+ error: err
2849
+ });
2850
+ }
2851
+ }
2852
+ console.log("");
2853
+ 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");
2854
+ console.log("Check Summary:");
2855
+ console.log(` Needs migration: ${needsCount}`);
2856
+ console.log(` No action needed: ${noActionCount}`);
2857
+ if (summary.failed.length > 0) {
2858
+ console.log(` Check failed: ${summary.failed.length}`);
2859
+ }
2860
+ 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");
2861
+ if (needsCount > 0) {
2862
+ console.log("");
2863
+ log("Run 'fullstack-cli migration' to apply.");
2864
+ }
2865
+ summary.needsMigration = needsCount > 0;
2866
+ if (needsCount === 0 && summary.failed.length === 0) {
2867
+ setCurrentVersion(latestVersion);
2868
+ console.log("");
2869
+ log(`Updated migrationVersion to ${latestVersion}.`);
2870
+ }
2871
+ return summary;
2872
+ }
2873
+
2874
+ // src/commands/migration/index.ts
2125
2875
  var migrationCommand = {
2126
2876
  name: "migration",
2127
- description: "Execute capability migration",
2877
+ description: "Run versioned migration scripts",
2128
2878
  register(program) {
2129
- program.command(this.name).description(this.description).option("--dry-run", "Preview mode, do not modify files").option("--skip-install", "Skip plugin installation step").option("--skip-code", "Skip code migration step").option("--mapping <file>", "Custom sourceActionID to pluginID mapping file").action(async (options) => {
2130
- await migration(options);
2879
+ 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) => {
2880
+ const summary = await runMigrations(options);
2881
+ if (summary.failed.length > 0) {
2882
+ process.exit(1);
2883
+ }
2884
+ });
2885
+ migrationCmd.command("check").description("Check pending migrations without applying them").action(async () => {
2886
+ const summary = await checkMigrations({});
2887
+ if (summary.failed.length > 0) {
2888
+ process.exit(2);
2889
+ } else if (summary.needsMigration) {
2890
+ process.exit(1);
2891
+ }
2131
2892
  });
2132
2893
  }
2133
2894
  };
2134
- var capabilityCommandGroup = {
2135
- name: "capability",
2136
- description: "Manage capability configurations",
2137
- commands: [listCommand2, migrationCommand]
2138
- };
2139
2895
 
2140
2896
  // src/commands/index.ts
2141
2897
  var commands = [
2142
2898
  genDbSchemaCommand,
2143
2899
  syncCommand,
2144
2900
  actionPluginCommandGroup,
2145
- capabilityCommandGroup
2901
+ capabilityCommandGroup,
2902
+ migrationCommand
2146
2903
  ];
2147
2904
 
2148
2905
  // src/index.ts
2149
- var envPath = path11.join(process.cwd(), ".env");
2150
- if (fs13.existsSync(envPath)) {
2906
+ var envPath = path14.join(process.cwd(), ".env");
2907
+ if (fs15.existsSync(envPath)) {
2151
2908
  dotenvConfig({ path: envPath });
2152
2909
  }
2153
- var __dirname = path11.dirname(fileURLToPath3(import.meta.url));
2154
- var pkg = JSON.parse(fs13.readFileSync(path11.join(__dirname, "../package.json"), "utf-8"));
2910
+ var __dirname = path14.dirname(fileURLToPath3(import.meta.url));
2911
+ var pkg = JSON.parse(fs15.readFileSync(path14.join(__dirname, "../package.json"), "utf-8"));
2155
2912
  var cli = new FullstackCLI(pkg.version);
2156
2913
  cli.useAll(commands);
2157
2914
  cli.run();