@lark-apaas/fullstack-cli 1.1.18 → 1.1.19

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
@@ -1960,6 +1960,27 @@ var syncConfig = {
1960
1960
  type: "remove-line",
1961
1961
  to: ".gitignore",
1962
1962
  pattern: "package-lock.json"
1963
+ },
1964
+ // 5. 注册 postinstall 脚本,自动恢复 action plugins
1965
+ {
1966
+ type: "add-script",
1967
+ name: "postinstall",
1968
+ command: "fullstack-cli action-plugin init",
1969
+ overwrite: false
1970
+ },
1971
+ // 6. 替换 drizzle.config.ts(仅当文件存在时)
1972
+ {
1973
+ from: "templates/drizzle.config.ts",
1974
+ to: "drizzle.config.ts",
1975
+ type: "file",
1976
+ overwrite: true,
1977
+ onlyIfExists: true
1978
+ },
1979
+ // 7. 确保 .gitignore 包含 .agent/ 目录
1980
+ {
1981
+ type: "add-line",
1982
+ to: ".gitignore",
1983
+ line: ".agent/"
1963
1984
  }
1964
1985
  ],
1965
1986
  // 文件权限设置
@@ -2053,6 +2074,16 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
2053
2074
  removeLineFromFile(destPath2, rule.pattern);
2054
2075
  return;
2055
2076
  }
2077
+ if (rule.type === "add-script") {
2078
+ const packageJsonPath = path4.join(userProjectRoot, "package.json");
2079
+ addScript(packageJsonPath, rule.name, rule.command, rule.overwrite ?? false);
2080
+ return;
2081
+ }
2082
+ if (rule.type === "add-line") {
2083
+ const destPath2 = path4.join(userProjectRoot, rule.to);
2084
+ addLineToFile(destPath2, rule.line);
2085
+ return;
2086
+ }
2056
2087
  if (!("from" in rule)) {
2057
2088
  return;
2058
2089
  }
@@ -2067,14 +2098,18 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
2067
2098
  syncDirectory(srcPath, destPath, rule.overwrite ?? true);
2068
2099
  break;
2069
2100
  case "file":
2070
- syncFile(srcPath, destPath, rule.overwrite ?? true);
2101
+ syncFile(srcPath, destPath, rule.overwrite ?? true, rule.onlyIfExists ?? false);
2071
2102
  break;
2072
2103
  case "append":
2073
2104
  appendToFile(srcPath, destPath);
2074
2105
  break;
2075
2106
  }
2076
2107
  }
2077
- function syncFile(src, dest, overwrite = true) {
2108
+ function syncFile(src, dest, overwrite = true, onlyIfExists = false) {
2109
+ if (onlyIfExists && !fs6.existsSync(dest)) {
2110
+ console.log(`[fullstack-cli] \u25CB ${path4.basename(dest)} (skipped, target not exists)`);
2111
+ return;
2112
+ }
2078
2113
  const destDir = path4.dirname(dest);
2079
2114
  if (!fs6.existsSync(destDir)) {
2080
2115
  fs6.mkdirSync(destDir, { recursive: true });
@@ -2155,6 +2190,42 @@ function deleteDirectory(dirPath) {
2155
2190
  console.log(`[fullstack-cli] \u25CB ${path4.basename(dirPath)} (not found)`);
2156
2191
  }
2157
2192
  }
2193
+ function addScript(packageJsonPath, name, command, overwrite) {
2194
+ if (!fs6.existsSync(packageJsonPath)) {
2195
+ console.log(`[fullstack-cli] \u25CB package.json (not found)`);
2196
+ return;
2197
+ }
2198
+ const content = fs6.readFileSync(packageJsonPath, "utf-8");
2199
+ const pkg2 = JSON.parse(content);
2200
+ if (!pkg2.scripts) {
2201
+ pkg2.scripts = {};
2202
+ }
2203
+ if (pkg2.scripts[name]) {
2204
+ if (!overwrite) {
2205
+ console.log(`[fullstack-cli] \u25CB scripts.${name} (already exists)`);
2206
+ return;
2207
+ }
2208
+ }
2209
+ pkg2.scripts[name] = command;
2210
+ fs6.writeFileSync(packageJsonPath, JSON.stringify(pkg2, null, 2) + "\n");
2211
+ console.log(`[fullstack-cli] \u2713 scripts.${name}`);
2212
+ }
2213
+ function addLineToFile(filePath, line) {
2214
+ const fileName = path4.basename(filePath);
2215
+ if (!fs6.existsSync(filePath)) {
2216
+ console.log(`[fullstack-cli] \u25CB ${fileName} (not found, skipped)`);
2217
+ return;
2218
+ }
2219
+ const content = fs6.readFileSync(filePath, "utf-8");
2220
+ const lines = content.split("\n").map((l) => l.trim());
2221
+ if (lines.includes(line)) {
2222
+ console.log(`[fullstack-cli] \u25CB ${fileName} (line already exists: ${line})`);
2223
+ return;
2224
+ }
2225
+ const appendContent = (content.endsWith("\n") ? "" : "\n") + line + "\n";
2226
+ fs6.appendFileSync(filePath, appendContent);
2227
+ console.log(`[fullstack-cli] \u2713 ${fileName} (added: ${line})`);
2228
+ }
2158
2229
 
2159
2230
  // src/commands/sync/index.ts
2160
2231
  var syncCommand = {
@@ -2438,9 +2509,24 @@ function ensureCacheDir() {
2438
2509
  function getTempFilePath(pluginKey, version) {
2439
2510
  ensureCacheDir();
2440
2511
  const safeKey = pluginKey.replace(/[/@]/g, "_");
2441
- const filename = `${safeKey}-${version}.tgz`;
2512
+ const filename = `${safeKey}@${version}.tgz`;
2442
2513
  return path6.join(getPluginCacheDir(), filename);
2443
2514
  }
2515
+ var MAX_RETRIES = 2;
2516
+ async function withRetry(operation, description, maxRetries = MAX_RETRIES) {
2517
+ let lastError;
2518
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
2519
+ try {
2520
+ return await operation();
2521
+ } catch (error) {
2522
+ lastError = error instanceof Error ? error : new Error(String(error));
2523
+ if (attempt < maxRetries) {
2524
+ console.log(`[action-plugin] ${description} failed, retrying (${attempt + 1}/${maxRetries})...`);
2525
+ }
2526
+ }
2527
+ }
2528
+ throw lastError;
2529
+ }
2444
2530
  async function downloadPlugin(pluginKey, requestedVersion) {
2445
2531
  console.log(`[action-plugin] Fetching plugin info for ${pluginKey}@${requestedVersion}...`);
2446
2532
  const pluginInfo = await getPluginVersion(pluginKey, requestedVersion);
@@ -2449,10 +2535,16 @@ async function downloadPlugin(pluginKey, requestedVersion) {
2449
2535
  let tgzBuffer;
2450
2536
  if (pluginInfo.downloadApproach === "inner") {
2451
2537
  console.log(`[action-plugin] Downloading from inner API...`);
2452
- tgzBuffer = await downloadFromInner(pluginKey, pluginInfo.version);
2538
+ tgzBuffer = await withRetry(
2539
+ () => downloadFromInner(pluginKey, pluginInfo.version),
2540
+ "Download"
2541
+ );
2453
2542
  } else {
2454
2543
  console.log(`[action-plugin] Downloading from public URL...`);
2455
- tgzBuffer = await downloadFromPublic(pluginInfo.downloadURL);
2544
+ tgzBuffer = await withRetry(
2545
+ () => downloadFromPublic(pluginInfo.downloadURL),
2546
+ "Download"
2547
+ );
2456
2548
  }
2457
2549
  const tgzPath = getTempFilePath(pluginKey, pluginInfo.version);
2458
2550
  fs8.writeFileSync(tgzPath, tgzBuffer);
@@ -2464,25 +2556,99 @@ async function downloadPlugin(pluginKey, requestedVersion) {
2464
2556
  };
2465
2557
  }
2466
2558
  function cleanupTempFile(tgzPath) {
2467
- try {
2468
- if (fs8.existsSync(tgzPath)) {
2469
- fs8.unlinkSync(tgzPath);
2559
+ }
2560
+ function getCachePath(pluginKey, version) {
2561
+ ensureCacheDir();
2562
+ const safeKey = pluginKey.replace(/[/@]/g, "_");
2563
+ const filename = `${safeKey}@${version}.tgz`;
2564
+ return path6.join(getPluginCacheDir(), filename);
2565
+ }
2566
+ function hasCachedPlugin(pluginKey, version) {
2567
+ const cachePath = getCachePath(pluginKey, version);
2568
+ return fs8.existsSync(cachePath);
2569
+ }
2570
+ function listCachedPlugins() {
2571
+ const cacheDir = getPluginCacheDir();
2572
+ if (!fs8.existsSync(cacheDir)) {
2573
+ return [];
2574
+ }
2575
+ const files = fs8.readdirSync(cacheDir);
2576
+ const result = [];
2577
+ for (const file of files) {
2578
+ if (!file.endsWith(".tgz")) continue;
2579
+ const match = file.match(/^(.+)@(.+)\.tgz$/);
2580
+ if (!match) continue;
2581
+ const [, rawName, version] = match;
2582
+ const name = rawName.replace(/^_/, "@").replace(/_/, "/");
2583
+ const filePath = path6.join(cacheDir, file);
2584
+ const stat = fs8.statSync(filePath);
2585
+ result.push({
2586
+ name,
2587
+ version,
2588
+ filePath,
2589
+ size: stat.size,
2590
+ mtime: stat.mtime
2591
+ });
2592
+ }
2593
+ return result;
2594
+ }
2595
+ function cleanAllCache() {
2596
+ const cacheDir = getPluginCacheDir();
2597
+ if (!fs8.existsSync(cacheDir)) {
2598
+ return 0;
2599
+ }
2600
+ const files = fs8.readdirSync(cacheDir);
2601
+ let count = 0;
2602
+ for (const file of files) {
2603
+ if (file.endsWith(".tgz")) {
2604
+ fs8.unlinkSync(path6.join(cacheDir, file));
2605
+ count++;
2470
2606
  }
2471
- } catch {
2472
2607
  }
2608
+ return count;
2609
+ }
2610
+ function cleanPluginCache(pluginKey, version) {
2611
+ const cacheDir = getPluginCacheDir();
2612
+ if (!fs8.existsSync(cacheDir)) {
2613
+ return 0;
2614
+ }
2615
+ const safeKey = pluginKey.replace(/[/@]/g, "_");
2616
+ const files = fs8.readdirSync(cacheDir);
2617
+ let count = 0;
2618
+ for (const file of files) {
2619
+ if (version) {
2620
+ if (file === `${safeKey}@${version}.tgz`) {
2621
+ fs8.unlinkSync(path6.join(cacheDir, file));
2622
+ count++;
2623
+ }
2624
+ } else {
2625
+ if (file.startsWith(`${safeKey}@`) && file.endsWith(".tgz")) {
2626
+ fs8.unlinkSync(path6.join(cacheDir, file));
2627
+ count++;
2628
+ }
2629
+ }
2630
+ }
2631
+ return count;
2473
2632
  }
2474
2633
 
2475
2634
  // src/commands/action-plugin/init.handler.ts
2476
2635
  async function installOneForInit(name, version) {
2477
- let tgzPath;
2478
2636
  try {
2479
2637
  const installedVersion = getPackageVersion(name);
2480
2638
  if (installedVersion === version) {
2481
2639
  return { name, version, success: true, skipped: true };
2482
2640
  }
2483
- console.log(`[action-plugin] Installing ${name}@${version}...`);
2484
- const downloadResult = await downloadPlugin(name, version);
2485
- tgzPath = downloadResult.tgzPath;
2641
+ let tgzPath;
2642
+ let fromCache = false;
2643
+ if (hasCachedPlugin(name, version)) {
2644
+ console.log(`[action-plugin] \u21BB Restoring ${name}@${version} from cache...`);
2645
+ tgzPath = getCachePath(name, version);
2646
+ fromCache = true;
2647
+ } else {
2648
+ console.log(`[action-plugin] \u2193 Downloading ${name}@${version}...`);
2649
+ const downloadResult = await downloadPlugin(name, version);
2650
+ tgzPath = downloadResult.tgzPath;
2651
+ }
2486
2652
  const pluginDir = extractTgzToNodeModules(tgzPath, name);
2487
2653
  const pluginPkg = readPluginPackageJson(pluginDir);
2488
2654
  if (pluginPkg?.peerDependencies) {
@@ -2491,16 +2657,13 @@ async function installOneForInit(name, version) {
2491
2657
  installMissingDeps(missingDeps);
2492
2658
  }
2493
2659
  }
2494
- console.log(`[action-plugin] \u2713 Installed ${name}@${version}`);
2660
+ const source = fromCache ? "from cache" : "downloaded";
2661
+ console.log(`[action-plugin] \u2713 Installed ${name}@${version} (${source})`);
2495
2662
  return { name, version, success: true };
2496
2663
  } catch (error) {
2497
2664
  const message = error instanceof Error ? error.message : String(error);
2498
2665
  console.error(`[action-plugin] \u2717 Failed to install ${name}: ${message}`);
2499
2666
  return { name, version, success: false, error: message };
2500
- } finally {
2501
- if (tgzPath) {
2502
- cleanupTempFile(tgzPath);
2503
- }
2504
2667
  }
2505
2668
  }
2506
2669
  async function init() {
@@ -2546,7 +2709,6 @@ function syncActionPluginsRecord(name, version) {
2546
2709
  }
2547
2710
  }
2548
2711
  async function installOne(nameWithVersion) {
2549
- let tgzPath;
2550
2712
  const { name, version: requestedVersion } = parsePluginName(nameWithVersion);
2551
2713
  try {
2552
2714
  console.log(`[action-plugin] Installing ${name}@${requestedVersion}...`);
@@ -2558,17 +2720,28 @@ async function installOne(nameWithVersion) {
2558
2720
  return { name, version: actualVersion, success: true, skipped: true };
2559
2721
  }
2560
2722
  }
2561
- if (actualVersion && requestedVersion === "latest") {
2723
+ let targetVersion = requestedVersion;
2724
+ if (requestedVersion === "latest") {
2562
2725
  const latestInfo = await getPluginVersion(name, "latest");
2563
- if (actualVersion === latestInfo.version) {
2726
+ targetVersion = latestInfo.version;
2727
+ if (actualVersion === targetVersion) {
2564
2728
  console.log(`[action-plugin] Plugin ${name} is already up to date (version: ${actualVersion})`);
2565
2729
  syncActionPluginsRecord(name, actualVersion);
2566
2730
  return { name, version: actualVersion, success: true, skipped: true };
2567
2731
  }
2568
- console.log(`[action-plugin] Found newer version: ${latestInfo.version} (installed: ${actualVersion})`);
2732
+ console.log(`[action-plugin] Found newer version: ${targetVersion} (installed: ${actualVersion || "none"})`);
2733
+ }
2734
+ let tgzPath;
2735
+ let fromCache = false;
2736
+ if (hasCachedPlugin(name, targetVersion)) {
2737
+ console.log(`[action-plugin] \u21BB Using cached ${name}@${targetVersion}...`);
2738
+ tgzPath = getCachePath(name, targetVersion);
2739
+ fromCache = true;
2740
+ } else {
2741
+ console.log(`[action-plugin] \u2193 Downloading ${name}@${targetVersion}...`);
2742
+ const downloadResult = await downloadPlugin(name, requestedVersion);
2743
+ tgzPath = downloadResult.tgzPath;
2569
2744
  }
2570
- const downloadResult = await downloadPlugin(name, requestedVersion);
2571
- tgzPath = downloadResult.tgzPath;
2572
2745
  console.log(`[action-plugin] Extracting to node_modules...`);
2573
2746
  const pluginDir = extractTgzToNodeModules(tgzPath, name);
2574
2747
  const pluginPkg = readPluginPackageJson(pluginDir);
@@ -2578,20 +2751,17 @@ async function installOne(nameWithVersion) {
2578
2751
  installMissingDeps(missingDeps);
2579
2752
  }
2580
2753
  }
2581
- const installedVersion = getPackageVersion(name) || downloadResult.version;
2754
+ const installedVersion = getPackageVersion(name) || targetVersion;
2582
2755
  const plugins = readActionPlugins();
2583
2756
  plugins[name] = installedVersion;
2584
2757
  writeActionPlugins(plugins);
2585
- console.log(`[action-plugin] Successfully installed ${name}@${installedVersion}`);
2758
+ const source = fromCache ? "from cache" : "downloaded";
2759
+ console.log(`[action-plugin] Successfully installed ${name}@${installedVersion} (${source})`);
2586
2760
  return { name, version: installedVersion, success: true };
2587
2761
  } catch (error) {
2588
2762
  const message = error instanceof Error ? error.message : String(error);
2589
2763
  console.error(`[action-plugin] Failed to install ${name}: ${message}`);
2590
2764
  return { name, version: requestedVersion, success: false, error: message };
2591
- } finally {
2592
- if (tgzPath) {
2593
- cleanupTempFile(tgzPath);
2594
- }
2595
2765
  }
2596
2766
  }
2597
2767
  async function install(namesWithVersion) {
@@ -2745,6 +2915,58 @@ async function list() {
2745
2915
  }
2746
2916
  }
2747
2917
 
2918
+ // src/commands/action-plugin/cache.handler.ts
2919
+ async function cacheList() {
2920
+ const cached = listCachedPlugins();
2921
+ if (cached.length === 0) {
2922
+ console.log("[action-plugin] No cached plugins found");
2923
+ return;
2924
+ }
2925
+ console.log("[action-plugin] Cached plugins:\n");
2926
+ console.log(" Name Version Size Modified");
2927
+ 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\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\u2500");
2928
+ let totalSize = 0;
2929
+ for (const plugin of cached) {
2930
+ const name = plugin.name.padEnd(38);
2931
+ const version = plugin.version.padEnd(10);
2932
+ const size = formatSize(plugin.size).padEnd(9);
2933
+ const mtime = plugin.mtime.toISOString().replace("T", " ").slice(0, 19);
2934
+ console.log(` ${name} ${version} ${size} ${mtime}`);
2935
+ totalSize += plugin.size;
2936
+ }
2937
+ 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\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\u2500");
2938
+ console.log(` Total: ${cached.length} cached file(s), ${formatSize(totalSize)}`);
2939
+ }
2940
+ async function cacheClean(pluginName, version) {
2941
+ let count;
2942
+ if (pluginName) {
2943
+ console.log(`[action-plugin] Cleaning cache for ${pluginName}${version ? `@${version}` : ""}...`);
2944
+ count = cleanPluginCache(pluginName, version);
2945
+ if (count === 0) {
2946
+ console.log(`[action-plugin] No cached files found for ${pluginName}${version ? `@${version}` : ""}`);
2947
+ } else {
2948
+ console.log(`[action-plugin] Cleaned ${count} cached file(s)`);
2949
+ }
2950
+ } else {
2951
+ console.log("[action-plugin] Cleaning all cached plugins...");
2952
+ count = cleanAllCache();
2953
+ if (count === 0) {
2954
+ console.log("[action-plugin] No cached files found");
2955
+ } else {
2956
+ console.log(`[action-plugin] Cleaned ${count} cached file(s)`);
2957
+ }
2958
+ }
2959
+ }
2960
+ function formatSize(bytes) {
2961
+ if (bytes < 1024) {
2962
+ return `${bytes} B`;
2963
+ } else if (bytes < 1024 * 1024) {
2964
+ return `${(bytes / 1024).toFixed(1)} KB`;
2965
+ } else {
2966
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
2967
+ }
2968
+ }
2969
+
2748
2970
  // src/commands/action-plugin/index.ts
2749
2971
  var initCommand = {
2750
2972
  name: "init",
@@ -2795,10 +3017,28 @@ var listCommand = {
2795
3017
  });
2796
3018
  }
2797
3019
  };
3020
+ var cacheListCommand = {
3021
+ name: "cache-list",
3022
+ description: "List all cached plugin packages",
3023
+ register(program) {
3024
+ program.command(this.name).description(this.description).action(async () => {
3025
+ await cacheList();
3026
+ });
3027
+ }
3028
+ };
3029
+ var cacheCleanCommand = {
3030
+ name: "cache-clean",
3031
+ description: "Clean cached plugin packages",
3032
+ register(program) {
3033
+ program.command(this.name).description(this.description).argument("[name]", "Plugin name to clean (e.g., @office/feishu-create-group). If not provided, clean all cache.").option("-v, --version <version>", "Specific version to clean").action(async (name, options) => {
3034
+ await cacheClean(name, options.version);
3035
+ });
3036
+ }
3037
+ };
2798
3038
  var actionPluginCommandGroup = {
2799
3039
  name: "action-plugin",
2800
3040
  description: "Manage action plugins",
2801
- commands: [initCommand, installCommand, updateCommand, removeCommand, listCommand]
3041
+ commands: [initCommand, installCommand, updateCommand, removeCommand, listCommand, cacheListCommand, cacheCleanCommand]
2802
3042
  };
2803
3043
 
2804
3044
  // src/commands/capability/utils.ts
@@ -5125,6 +5365,28 @@ function extractClientStdSegment(lines, maxLines, offset) {
5125
5365
  };
5126
5366
  }
5127
5367
 
5368
+ // src/commands/read-logs/dev.ts
5369
+ function readDevSegment(filePath, maxLines, offset) {
5370
+ const marker = (line) => {
5371
+ if (!line) return false;
5372
+ if (line.includes("Dev session started")) return true;
5373
+ return false;
5374
+ };
5375
+ const segment = readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, marker);
5376
+ return { lines: segment.lines, totalLinesCount: segment.totalLinesCount };
5377
+ }
5378
+
5379
+ // src/commands/read-logs/dev-std.ts
5380
+ function readDevStdSegment(filePath, maxLines, offset) {
5381
+ const marker = (line) => {
5382
+ if (!line) return false;
5383
+ if (line.includes("Dev session started")) return true;
5384
+ return false;
5385
+ };
5386
+ const segment = readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, marker);
5387
+ return { lines: segment.lines, totalLinesCount: segment.totalLinesCount };
5388
+ }
5389
+
5128
5390
  // src/commands/read-logs/json-lines.ts
5129
5391
  import fs20 from "fs";
5130
5392
  function normalizePid(value) {
@@ -5449,7 +5711,7 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
5449
5711
  }
5450
5712
 
5451
5713
  // src/commands/read-logs/index.ts
5452
- var LOG_TYPES = ["server", "trace", "server-std", "client-std", "browser"];
5714
+ var LOG_TYPES = ["server", "trace", "server-std", "client-std", "dev", "dev-std", "install-dep-std", "browser"];
5453
5715
  function normalizeObjectKey(key) {
5454
5716
  return key.toLowerCase().replace(/_/g, "");
5455
5717
  }
@@ -5611,6 +5873,15 @@ async function readLatestLogLinesMeta(options) {
5611
5873
  if (options.type === "client-std") {
5612
5874
  return readClientStdSegment(filePath, maxLines, offset);
5613
5875
  }
5876
+ if (options.type === "dev") {
5877
+ return readDevSegment(filePath, maxLines, offset);
5878
+ }
5879
+ if (options.type === "dev-std") {
5880
+ return readDevStdSegment(filePath, maxLines, offset);
5881
+ }
5882
+ if (options.type === "install-dep-std") {
5883
+ return readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset);
5884
+ }
5614
5885
  const traceId = typeof options.traceId === "string" ? options.traceId.trim() : "";
5615
5886
  if (traceId) {
5616
5887
  return readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels);
@@ -5628,7 +5899,7 @@ async function readLatestLogLinesMeta(options) {
5628
5899
  }
5629
5900
  async function readLogsJsonResult(options) {
5630
5901
  const { lines, totalLinesCount } = await readLatestLogLinesMeta(options);
5631
- if (options.type === "server-std" || options.type === "client-std") {
5902
+ if (options.type === "server-std" || options.type === "client-std" || options.type === "dev" || options.type === "dev-std" || options.type === "install-dep-std") {
5632
5903
  return {
5633
5904
  hasError: hasErrorInStdLines(lines),
5634
5905
  totalLinesCount,
@@ -5672,6 +5943,15 @@ function resolveLogFilePath(logDir, type) {
5672
5943
  if (type === "client-std") {
5673
5944
  return path16.join(base, "client.std.log");
5674
5945
  }
5946
+ if (type === "dev") {
5947
+ return path16.join(base, "dev.log");
5948
+ }
5949
+ if (type === "dev-std") {
5950
+ return path16.join(base, "dev.std.log");
5951
+ }
5952
+ if (type === "install-dep-std") {
5953
+ return path16.join(base, "install-dep.std.log");
5954
+ }
5675
5955
  if (type === "browser") {
5676
5956
  return path16.join(base, "browser.log");
5677
5957
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/fullstack-cli",
3
- "version": "1.1.18",
3
+ "version": "1.1.19",
4
4
  "description": "CLI tool for fullstack template management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,55 @@
1
+ import { defineConfig, Config } from 'drizzle-kit';
2
+ require('dotenv').config();
3
+
4
+ const outputDir = process.env.__DRIZZLE_OUT_DIR__ || './server/database/.introspect';
5
+ const schemaPath = process.env.__DRIZZLE_SCHEMA_PATH__ || './server/database/schema.ts';
6
+
7
+ const parsedUrl = new URL(process.env.SUDA_DATABASE_URL || '');
8
+
9
+ const envSchemaFilter = process.env.DRIZZLE_SCHEMA_FILTER;
10
+ const urlSchemaFilter = parsedUrl.searchParams.get('schema');
11
+
12
+ const schemaFilter = (envSchemaFilter ?? urlSchemaFilter ?? '')
13
+ .split(',')
14
+ .map((s) => s.trim())
15
+ .filter(Boolean);
16
+
17
+ parsedUrl.searchParams.delete('schema'); // 移除schema参数,避免 drizzle-kit 解析错误
18
+
19
+ // 默认排除的系统对象(PostgreSQL 扩展和系统视图)
20
+ // 这些对象在 drizzle-kit introspect 时可能导致无效的 JS 代码生成
21
+ const SYSTEM_OBJECTS_EXCLUSIONS = [
22
+ '!spatial_ref_sys', // PostGIS 空间参考系统表
23
+ '!geography_columns', // PostGIS 地理列视图
24
+ '!geometry_columns', // PostGIS 几何列视图
25
+ '!raster_columns', // PostGIS 栅格列视图
26
+ '!raster_overviews', // PostGIS 栅格概览视图
27
+ '!pg_stat_statements', // pg_stat_statements 扩展
28
+ '!pg_stat_statements_info', // pg_stat_statements 扩展
29
+ '!part_config', // pg_partman 分区配置表
30
+ '!part_config_sub', // pg_partman 子分区配置表
31
+ '!table_privs', // 系统权限视图
32
+ ];
33
+
34
+ const envTablesFilter = process.env.DRIZZLE_TABLES_FILTER;
35
+ const userTablesFilter = (envTablesFilter ?? '*')
36
+ .split(',')
37
+ .map((s) => s.trim())
38
+ .filter(Boolean);
39
+
40
+ // 合并用户过滤器和系统对象排除
41
+ // 用户可以通过设置 DRIZZLE_TABLES_FILTER 来覆盖(如果需要包含某些系统对象)
42
+ const tablesFilter = [...userTablesFilter, ...SYSTEM_OBJECTS_EXCLUSIONS];
43
+
44
+ const config:Config = {
45
+ schema: schemaPath,
46
+ out: outputDir,
47
+ tablesFilter,
48
+ schemaFilter,
49
+ dialect: 'postgresql',
50
+ dbCredentials: {
51
+ url: parsedUrl.toString(),
52
+ },
53
+ }
54
+
55
+ export default defineConfig(config);
@@ -21,6 +21,10 @@ CLEANUP_DONE=false
21
21
 
22
22
  mkdir -p "${LOG_DIR}"
23
23
 
24
+ # Redirect all stdout/stderr to both terminal and log file
25
+ DEV_STD_LOG="${LOG_DIR}/dev.std.log"
26
+ exec > >(tee -a "$DEV_STD_LOG") 2>&1
27
+
24
28
  # Log event to dev.log with timestamp
25
29
  log_event() {
26
30
  local level=$1
@@ -234,7 +238,7 @@ log_event "INFO" "client" "Supervisor started with PID ${CLIENT_PID}"
234
238
  log_event "INFO" "main" "All processes started, monitoring..."
235
239
  echo ""
236
240
  echo "📋 Dev processes running. Press Ctrl+C to stop."
237
- echo "📄 Logs: ${DEV_LOG}"
241
+ echo "📄 Logs: ${DEV_STD_LOG}"
238
242
  echo ""
239
243
 
240
244
  # Wait for all background processes