@lark-apaas/fullstack-cli 1.1.19 → 1.1.20-alpha.1

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.
@@ -38,6 +38,60 @@ export const fileAttachment = customType<{
38
38
  },
39
39
  });
40
40
 
41
+ /** Escape single quotes in SQL string literals */
42
+ function escapeLiteral(str: string): string {
43
+ return `'${str.replace(/'/g, "''")}'`;
44
+ }
45
+
46
+ export const userProfileArray = customType<{
47
+ data: string[];
48
+ driverData: string;
49
+ }>({
50
+ dataType() {
51
+ return 'user_profile[]';
52
+ },
53
+ toDriver(value: string[]) {
54
+ if (!value || value.length === 0) {
55
+ return sql`'{}'::user_profile[]`;
56
+ }
57
+ const elements = value.map(id => `ROW(${escapeLiteral(id)})::user_profile`).join(',');
58
+ return sql.raw(`ARRAY[${elements}]::user_profile[]`);
59
+ },
60
+ fromDriver(value: string): string[] {
61
+ if (!value || value === '{}') return [];
62
+ const inner = value.slice(1, -1);
63
+ const matches = inner.match(/\([^)]*\)/g) || [];
64
+ return matches.map(m => m.slice(1, -1).split(',')[0].trim());
65
+ },
66
+ });
67
+
68
+ export const fileAttachmentArray = customType<{
69
+ data: FileAttachment[];
70
+ driverData: string;
71
+ }>({
72
+ dataType() {
73
+ return 'file_attachment[]';
74
+ },
75
+ toDriver(value: FileAttachment[]) {
76
+ if (!value || value.length === 0) {
77
+ return sql`'{}'::file_attachment[]`;
78
+ }
79
+ const elements = value.map(f =>
80
+ `ROW(${escapeLiteral(f.bucket_id)},${escapeLiteral(f.file_path)})::file_attachment`
81
+ ).join(',');
82
+ return sql.raw(`ARRAY[${elements}]::file_attachment[]`);
83
+ },
84
+ fromDriver(value: string): FileAttachment[] {
85
+ if (!value || value === '{}') return [];
86
+ const inner = value.slice(1, -1);
87
+ const matches = inner.match(/\([^)]*\)/g) || [];
88
+ return matches.map(m => {
89
+ const [bucketId, filePath] = m.slice(1, -1).split(',');
90
+ return { bucket_id: bucketId.trim(), file_path: filePath.trim() };
91
+ });
92
+ },
93
+ });
94
+
41
95
  export const customTimestamptz = customType<{
42
96
  data: Date;
43
97
  driverData: string;
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/index.ts
2
- import fs21 from "fs";
3
- import path17 from "path";
2
+ import fs22 from "fs";
3
+ import path18 from "path";
4
4
  import { fileURLToPath as fileURLToPath4 } from "url";
5
5
  import { config as dotenvConfig } from "dotenv";
6
6
 
@@ -472,6 +472,10 @@ var KNOWN_TYPE_FACTORIES = {
472
472
  user_profile: "userProfile",
473
473
  file_attachment: "fileAttachment"
474
474
  };
475
+ var KNOWN_ARRAY_TYPE_FACTORIES = {
476
+ user_profile: "userProfileArray",
477
+ file_attachment: "fileAttachmentArray"
478
+ };
475
479
  var replaceUnknownTransform = {
476
480
  name: "replace-unknown",
477
481
  transform(ctx) {
@@ -490,12 +494,17 @@ var replaceUnknownTransform = {
490
494
  const lines = textBefore.split("\n");
491
495
  let factoryName = "text";
492
496
  let foundKnownType = false;
497
+ let isArrayType = false;
493
498
  for (let i = lines.length - 1; i >= Math.max(0, lines.length - 5); i--) {
494
499
  const line = lines[i];
495
500
  const todoMatch = line.match(/\/\/ TODO: failed to parse database type '(?:\w+\.)?([\w_]+)(\[\])?'/);
496
501
  if (todoMatch) {
497
502
  const typeName = todoMatch[1];
498
- if (KNOWN_TYPE_FACTORIES[typeName]) {
503
+ isArrayType = todoMatch[2] === "[]";
504
+ if (isArrayType && KNOWN_ARRAY_TYPE_FACTORIES[typeName]) {
505
+ factoryName = KNOWN_ARRAY_TYPE_FACTORIES[typeName];
506
+ foundKnownType = true;
507
+ } else if (KNOWN_TYPE_FACTORIES[typeName]) {
499
508
  factoryName = KNOWN_TYPE_FACTORIES[typeName];
500
509
  foundKnownType = true;
501
510
  }
@@ -503,6 +512,15 @@ var replaceUnknownTransform = {
503
512
  }
504
513
  }
505
514
  expression.replaceWithText(factoryName);
515
+ if (isArrayType && foundKnownType) {
516
+ const parent = node.getParent();
517
+ if (Node5.isPropertyAccessExpression(parent) && parent.getName() === "array") {
518
+ const grandParent = parent.getParent();
519
+ if (Node5.isCallExpression(grandParent)) {
520
+ grandParent.replaceWithText(node.getText());
521
+ }
522
+ }
523
+ }
506
524
  if (foundKnownType) {
507
525
  stats.replacedUnknown++;
508
526
  } else {
@@ -1005,6 +1023,139 @@ function resolveTemplateTypesPath() {
1005
1023
  return void 0;
1006
1024
  }
1007
1025
 
1026
+ // src/commands/db/gen-dbschema/utils/fetch-column-comments.ts
1027
+ import postgres from "postgres";
1028
+ var DEFAULT_TIMEOUT_MS = 1e4;
1029
+ async function fetchColumnComments(connectionString, options = {}) {
1030
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
1031
+ const url = new URL(connectionString);
1032
+ const schemaName = url.searchParams.get("schema") ?? "public";
1033
+ const sql = postgres(connectionString, {
1034
+ connect_timeout: Math.ceil(timeoutMs / 1e3),
1035
+ idle_timeout: Math.ceil(timeoutMs / 1e3)
1036
+ });
1037
+ try {
1038
+ const queryPromise = sql`
1039
+ SELECT
1040
+ c.table_name AS "tableName",
1041
+ c.column_name AS "columnName",
1042
+ pgd.description AS "comment"
1043
+ FROM information_schema.columns c
1044
+ JOIN pg_catalog.pg_statio_all_tables st
1045
+ ON c.table_name = st.relname AND c.table_schema = st.schemaname
1046
+ JOIN pg_catalog.pg_description pgd
1047
+ ON pgd.objoid = st.relid AND pgd.objsubid = c.ordinal_position
1048
+ WHERE c.table_schema = ${schemaName}
1049
+ AND pgd.description IS NOT NULL
1050
+ AND pgd.description != ''
1051
+ `;
1052
+ const timeoutPromise = new Promise((_, reject) => {
1053
+ setTimeout(() => reject(new Error(`Timeout after ${timeoutMs}ms`)), timeoutMs);
1054
+ });
1055
+ const result = await Promise.race([queryPromise, timeoutPromise]);
1056
+ const commentMap = /* @__PURE__ */ new Map();
1057
+ for (const row of result) {
1058
+ const key = `${row.tableName}.${row.columnName}`;
1059
+ commentMap.set(key, row.comment);
1060
+ }
1061
+ return commentMap;
1062
+ } finally {
1063
+ await sql.end().catch(() => {
1064
+ });
1065
+ }
1066
+ }
1067
+ function extractTypeAnnotation(comment) {
1068
+ const typeStart = comment.indexOf("@type");
1069
+ if (typeStart === -1) return null;
1070
+ const afterType = comment.slice(typeStart + 5).trimStart();
1071
+ if (!afterType.startsWith("{")) return null;
1072
+ let depth = 0;
1073
+ let endIndex = 0;
1074
+ for (let i = 0; i < afterType.length; i++) {
1075
+ if (afterType[i] === "{") depth++;
1076
+ if (afterType[i] === "}") depth--;
1077
+ if (depth === 0) {
1078
+ endIndex = i + 1;
1079
+ break;
1080
+ }
1081
+ }
1082
+ if (endIndex === 0) return null;
1083
+ return afterType.slice(0, endIndex);
1084
+ }
1085
+ function parseColumnComment(comment) {
1086
+ const typeValue = extractTypeAnnotation(comment);
1087
+ if (typeValue) {
1088
+ const descMatch = comment.match(/@description\s+([^@]+)/);
1089
+ return {
1090
+ type: typeValue,
1091
+ description: descMatch?.[1]?.trim()
1092
+ };
1093
+ }
1094
+ return { description: comment.trim() };
1095
+ }
1096
+
1097
+ // src/commands/db/gen-dbschema/transforms/text/jsonb-comments.ts
1098
+ function addJsonbTypeComments(source, columnComments) {
1099
+ if (!columnComments || columnComments.size === 0) {
1100
+ return { text: source, added: 0 };
1101
+ }
1102
+ const lines = source.split("\n");
1103
+ const result = [];
1104
+ let added = 0;
1105
+ let currentTableName = null;
1106
+ const tableDefRegex = /export const\s+\w+\s*=\s*pgTable\(\s*["'`]([^"'`]+)["'`]/;
1107
+ const jsonFieldWithNameRegex = /^\s*(\w+):\s*(?:json|jsonb)\(\s*["'`]([^"'`]+)["'`]\)/;
1108
+ const jsonFieldNoNameRegex = /^\s*(\w+):\s*(?:json|jsonb)\(\s*\)/;
1109
+ for (let i = 0; i < lines.length; i++) {
1110
+ const line = lines[i];
1111
+ const tableMatch = line.match(tableDefRegex);
1112
+ if (tableMatch) {
1113
+ currentTableName = tableMatch[1];
1114
+ }
1115
+ let columnName = null;
1116
+ const jsonMatchWithName = line.match(jsonFieldWithNameRegex);
1117
+ const jsonMatchNoName = line.match(jsonFieldNoNameRegex);
1118
+ if (jsonMatchWithName) {
1119
+ columnName = jsonMatchWithName[2];
1120
+ } else if (jsonMatchNoName) {
1121
+ columnName = jsonMatchNoName[1];
1122
+ }
1123
+ if (columnName && currentTableName) {
1124
+ const commentKey = `${currentTableName}.${columnName}`;
1125
+ const comment = columnComments.get(commentKey);
1126
+ if (comment) {
1127
+ const parsed = parseColumnComment(comment);
1128
+ const indentMatch = line.match(/^\s*/);
1129
+ const indent = indentMatch ? indentMatch[0] : "";
1130
+ const prevLine = result[result.length - 1]?.trim() ?? "";
1131
+ if (!prevLine.startsWith("/**") && !prevLine.startsWith("*") && !prevLine.startsWith("//")) {
1132
+ const commentLines = [];
1133
+ commentLines.push(`${indent}/**`);
1134
+ if (parsed.description) {
1135
+ const safeDesc = parsed.description.replace(/[\r\n]+/g, " ").trim();
1136
+ commentLines.push(`${indent} * ${safeDesc}`);
1137
+ }
1138
+ if (parsed.type) {
1139
+ if (parsed.description) {
1140
+ commentLines.push(`${indent} *`);
1141
+ }
1142
+ const safeType = parsed.type.replace(/[\r\n]+/g, " ").trim();
1143
+ commentLines.push(`${indent} * @type ${safeType}`);
1144
+ }
1145
+ commentLines.push(`${indent} */`);
1146
+ result.push(...commentLines);
1147
+ added++;
1148
+ }
1149
+ }
1150
+ }
1151
+ if (line.match(/^\}\);?\s*$/) || line.match(/^}\s*,\s*\{/)) {
1152
+ currentTableName = null;
1153
+ }
1154
+ result.push(line);
1155
+ }
1156
+ return { text: result.join("\n"), added };
1157
+ }
1158
+
1008
1159
  // src/commands/db/gen-dbschema/transforms/text/table-aliases.ts
1009
1160
  var TABLE_ALIAS_MARKER = "// table aliases";
1010
1161
  function generateTableAliases(source) {
@@ -1041,7 +1192,7 @@ function formatSource(source) {
1041
1192
  }
1042
1193
 
1043
1194
  // src/commands/db/gen-dbschema/postprocess.ts
1044
- function postprocessSchema(rawSource) {
1195
+ function postprocessSchema(rawSource, options = {}) {
1045
1196
  const patchResult = patchDefects(rawSource);
1046
1197
  let source = patchResult.text;
1047
1198
  const { sourceFile } = parseSource(source);
@@ -1051,12 +1202,15 @@ function postprocessSchema(rawSource) {
1051
1202
  source = ensureHeader(source);
1052
1203
  source = addSystemFieldComments(source);
1053
1204
  source = inlineCustomTypes(source);
1205
+ const jsonbCommentsResult = addJsonbTypeComments(source, options.columnComments);
1206
+ source = jsonbCommentsResult.text;
1054
1207
  source = generateTableAliases(source);
1055
1208
  source = formatSource(source);
1056
1209
  return {
1057
1210
  source,
1058
1211
  astStats,
1059
- patchedDefects: patchResult.fixed
1212
+ patchedDefects: patchResult.fixed,
1213
+ addedJsonbComments: jsonbCommentsResult.added
1060
1214
  };
1061
1215
  }
1062
1216
  function logStats(result, prefix = "[postprocess]") {
@@ -1103,17 +1257,20 @@ function logStats(result, prefix = "[postprocess]") {
1103
1257
  if (astStats.removedImports.length > 0) {
1104
1258
  console.info(`${prefix} Removed imports: ${astStats.removedImports.join(", ")}`);
1105
1259
  }
1260
+ if (result.addedJsonbComments > 0) {
1261
+ console.info(`${prefix} Added ${result.addedJsonbComments} JSDoc comments for jsonb fields`);
1262
+ }
1106
1263
  }
1107
1264
 
1108
1265
  // src/commands/db/gen-dbschema/index.ts
1109
- function postprocessDrizzleSchema(targetPath) {
1266
+ async function postprocessDrizzleSchema(targetPath, options = {}) {
1110
1267
  const resolvedPath = path.resolve(targetPath);
1111
1268
  if (!fs3.existsSync(resolvedPath)) {
1112
1269
  console.warn(`[postprocess-drizzle-schema] File not found: ${resolvedPath}`);
1113
1270
  return void 0;
1114
1271
  }
1115
1272
  const rawSource = fs3.readFileSync(resolvedPath, "utf8");
1116
- const result = postprocessSchema(rawSource);
1273
+ const result = postprocessSchema(rawSource, { columnComments: options.columnComments });
1117
1274
  fs3.writeFileSync(resolvedPath, result.source, "utf8");
1118
1275
  logStats(result, "[postprocess-drizzle-schema]");
1119
1276
  return {
@@ -1122,7 +1279,8 @@ function postprocessDrizzleSchema(targetPath) {
1122
1279
  unmatchedUnknown: result.astStats.unmatchedUnknown,
1123
1280
  patchedDefects: result.patchedDefects,
1124
1281
  replacedTimestamps: result.astStats.replacedTimestamp,
1125
- replacedDefaultNow: result.astStats.replacedDefaultNow
1282
+ replacedDefaultNow: result.astStats.replacedDefaultNow,
1283
+ addedJsonbComments: result.addedJsonbComments
1126
1284
  };
1127
1285
  }
1128
1286
 
@@ -1847,6 +2005,16 @@ async function run(options = {}) {
1847
2005
  }
1848
2006
  throw new Error("Unable to locate drizzle-kit package root");
1849
2007
  };
2008
+ let columnComments;
2009
+ try {
2010
+ columnComments = await fetchColumnComments(databaseUrl, { timeoutMs: 1e4 });
2011
+ console.log(`[gen-db-schema] \u2713 Fetched ${columnComments.size} column comments`);
2012
+ } catch (err) {
2013
+ console.warn(
2014
+ "[gen-db-schema] \u26A0 Failed to fetch column comments (skipping):",
2015
+ err instanceof Error ? err.message : String(err)
2016
+ );
2017
+ }
1850
2018
  try {
1851
2019
  const env = {
1852
2020
  ...process.env,
@@ -1870,7 +2038,9 @@ async function run(options = {}) {
1870
2038
  console.error("[gen-db-schema] schema.ts not generated");
1871
2039
  throw new Error("drizzle-kit introspect failed to generate schema.ts");
1872
2040
  }
1873
- const stats = postprocessDrizzleSchema(generatedSchema);
2041
+ const stats = await postprocessDrizzleSchema(generatedSchema, {
2042
+ columnComments
2043
+ });
1874
2044
  if (stats?.unmatchedUnknown?.length) {
1875
2045
  console.warn("[gen-db-schema] Unmatched custom types detected:", stats.unmatchedUnknown);
1876
2046
  }
@@ -3282,59 +3452,344 @@ var capabilityCommandGroup = {
3282
3452
  commands: [listCommand2]
3283
3453
  };
3284
3454
 
3285
- // src/commands/migration/version-manager.ts
3455
+ // src/commands/component/registry-preparer.ts
3286
3456
  import fs10 from "fs";
3287
3457
  import path8 from "path";
3458
+ import os from "os";
3459
+
3460
+ // src/commands/component/service.ts
3461
+ import { mapValues } from "es-toolkit";
3462
+
3463
+ // src/commands/component/utils.ts
3464
+ import createDebug from "debug";
3465
+ var debug = createDebug("component");
3466
+
3467
+ // src/commands/component/service.ts
3468
+ async function getComponents(keys) {
3469
+ const client = getHttpClient();
3470
+ debug("\u8C03\u7528 /components/batch_get %o", keys);
3471
+ const response = await client.post(
3472
+ `/api/v1/studio/innerapi/components/batch_get?keys=${keys.join(",")}`
3473
+ );
3474
+ if (response.status !== 200) {
3475
+ throw new Error(
3476
+ `\u83B7\u53D6\u7EC4\u4EF6\u4FE1\u606F\u5931\u8D25\uFF1A${response.status} ${response.statusText}`
3477
+ );
3478
+ }
3479
+ const result = await response.json();
3480
+ if (result.status_code !== "0") {
3481
+ debug("\u63A5\u53E3\u8FD4\u56DE\u9519\u8BEF\uFF1A%o", result);
3482
+ throw new Error(`\u83B7\u53D6\u7EC4\u4EF6\u4FE1\u606F\u5931\u8D25\uFF1A${result.error_msg}`);
3483
+ }
3484
+ return mapValues(result.data.components, ([component]) => component);
3485
+ }
3486
+ async function getRegistryItem(url) {
3487
+ const client = getHttpClient();
3488
+ debug("\u4E0B\u8F7D registry-item.json\uFF1A%s", url);
3489
+ const response = await client.get(url);
3490
+ if (!response.ok) {
3491
+ throw new Error(
3492
+ `\u4E0B\u8F7D registry-item.json \u5931\u8D25\uFF1A${response.status} ${response.statusText}`
3493
+ );
3494
+ }
3495
+ const item = await response.json();
3496
+ return item;
3497
+ }
3498
+ async function sendInstallEvent(key) {
3499
+ const client = getHttpClient();
3500
+ await client.post("/api/v1/studio/innerapi/resource_events", {
3501
+ events: [
3502
+ {
3503
+ resourceType: "component",
3504
+ resourceKey: key,
3505
+ eventType: "install",
3506
+ details: {}
3507
+ }
3508
+ ]
3509
+ });
3510
+ }
3511
+
3512
+ // src/commands/component/registry-preparer.ts
3513
+ var REGISTRY_TEMP_DIR = path8.join(os.tmpdir(), "miaoda-registry");
3514
+ function parseComponentKey(key) {
3515
+ const match = key.match(/^@([^/]+)\/(.+)$/);
3516
+ if (!match) {
3517
+ throw new Error(
3518
+ `Invalid component key format: ${key}. Expected format: @scope/name`
3519
+ );
3520
+ }
3521
+ return { scope: match[1], name: match[2] };
3522
+ }
3523
+ function getLocalRegistryPath(key) {
3524
+ const { scope, name } = parseComponentKey(key);
3525
+ return path8.join(REGISTRY_TEMP_DIR, scope, `${name}.json`);
3526
+ }
3527
+ function ensureDir(dirPath) {
3528
+ if (!fs10.existsSync(dirPath)) {
3529
+ fs10.mkdirSync(dirPath, { recursive: true });
3530
+ }
3531
+ }
3532
+ async function prepareRecursive(key, visited) {
3533
+ if (visited.has(key)) {
3534
+ debug("\u8DF3\u8FC7\u5DF2\u5904\u7406\u7684\u7EC4\u4EF6: %s", key);
3535
+ return;
3536
+ }
3537
+ visited.add(key);
3538
+ debug("\u5904\u7406\u7EC4\u4EF6: %s", key);
3539
+ debug("\u83B7\u53D6\u7EC4\u4EF6\u4E0B\u8F7D\u4FE1\u606F...");
3540
+ const infoMap = await getComponents([key]);
3541
+ const info = infoMap[key];
3542
+ debug("\u7EC4\u4EF6\u4FE1\u606F: %o", info);
3543
+ if (!info) {
3544
+ throw new Error(`Component not found: ${key}`);
3545
+ }
3546
+ if (info.status !== "active") {
3547
+ throw new Error(`Component is not active: ${key}`);
3548
+ }
3549
+ debug("\u4E0B\u8F7D registry item: %s", info.downloadURL);
3550
+ const registryItem = await getRegistryItem(info.downloadURL);
3551
+ debug("registry item \u5185\u5BB9: %o", registryItem);
3552
+ const deps = registryItem.registryDependencies || [];
3553
+ debug("\u4F9D\u8D56\u5217\u8868: %o", deps);
3554
+ for (const dep of deps) {
3555
+ await prepareRecursive(dep, visited);
3556
+ }
3557
+ const rewrittenItem = {
3558
+ ...registryItem,
3559
+ registryDependencies: deps.map((dep) => getLocalRegistryPath(dep))
3560
+ };
3561
+ const localPath = getLocalRegistryPath(key);
3562
+ ensureDir(path8.dirname(localPath));
3563
+ fs10.writeFileSync(localPath, JSON.stringify(rewrittenItem, null, 2), "utf-8");
3564
+ debug("\u4FDD\u5B58\u5230: %s", localPath);
3565
+ }
3566
+ async function prepareComponentRegistryItems(id) {
3567
+ const visited = /* @__PURE__ */ new Set();
3568
+ await prepareRecursive(id, visited);
3569
+ return getLocalRegistryPath(id);
3570
+ }
3571
+ function cleanupTempDir() {
3572
+ try {
3573
+ if (fs10.existsSync(REGISTRY_TEMP_DIR)) {
3574
+ fs10.rmSync(REGISTRY_TEMP_DIR, { recursive: true, force: true });
3575
+ }
3576
+ } catch {
3577
+ }
3578
+ }
3579
+ function getDownloadedRegistryItem(itemId) {
3580
+ const localPath = getLocalRegistryPath(itemId);
3581
+ if (!fs10.existsSync(localPath)) {
3582
+ return null;
3583
+ }
3584
+ const content = fs10.readFileSync(localPath, "utf-8");
3585
+ return JSON.parse(content);
3586
+ }
3587
+
3588
+ // src/commands/component/shadcn-executor.ts
3589
+ import * as pty from "@lydell/node-pty";
3590
+ function parseOutput(output) {
3591
+ const state = {
3592
+ currentSection: null,
3593
+ files: /* @__PURE__ */ new Set()
3594
+ };
3595
+ const lines = output.split("\n");
3596
+ for (const line of lines) {
3597
+ const trimmedLine = line.trim();
3598
+ if (/Created \d+ files?:/.test(trimmedLine)) {
3599
+ state.currentSection = "created";
3600
+ continue;
3601
+ }
3602
+ if (/Updated \d+ files?:/.test(trimmedLine)) {
3603
+ state.currentSection = "updated";
3604
+ continue;
3605
+ }
3606
+ if (/Skipped \d+ files?:/.test(trimmedLine)) {
3607
+ state.currentSection = "skipped";
3608
+ continue;
3609
+ }
3610
+ if (state.currentSection && trimmedLine.startsWith("- ")) {
3611
+ const filePath = trimmedLine.slice(2).trim();
3612
+ if (filePath && filePath.includes("/")) {
3613
+ state.files.add(filePath);
3614
+ }
3615
+ }
3616
+ if (state.currentSection && trimmedLine.length > 1 && !trimmedLine.startsWith("- ")) {
3617
+ state.currentSection = null;
3618
+ }
3619
+ }
3620
+ return Array.from(state.files);
3621
+ }
3622
+ function toFileInfo(filePath) {
3623
+ const name = filePath.split("/").pop() || filePath;
3624
+ return { name, path: filePath };
3625
+ }
3626
+ var PROMPT_PATTERNS = [
3627
+ // 文件覆盖确认 - 回答 n(不覆盖)
3628
+ { pattern: /overwrite/i, answer: "n\n" },
3629
+ // 主题/样式选择 - 回答 n(不安装额外主题)
3630
+ { pattern: /theme/i, answer: "n\n" },
3631
+ { pattern: /style/i, answer: "n\n" },
3632
+ // 继续确认 - 回答 y
3633
+ { pattern: /continue\?/i, answer: "y\n" },
3634
+ { pattern: /proceed\?/i, answer: "y\n" }
3635
+ ];
3636
+ async function executeShadcnAdd(registryItemPath) {
3637
+ return new Promise((resolve) => {
3638
+ let output = "";
3639
+ const args = ["--yes", "shadcn@3.8.2", "add", registryItemPath];
3640
+ const ptyProcess = pty.spawn("npx", args, {
3641
+ name: "xterm-color",
3642
+ cols: 120,
3643
+ rows: 30,
3644
+ cwd: process.cwd(),
3645
+ env: {
3646
+ ...process.env,
3647
+ // 禁用颜色输出以便解析
3648
+ NO_COLOR: "1",
3649
+ FORCE_COLOR: "0"
3650
+ }
3651
+ });
3652
+ ptyProcess.onData((data) => {
3653
+ output += data;
3654
+ for (const { pattern, answer } of PROMPT_PATTERNS) {
3655
+ if (pattern.test(data)) {
3656
+ ptyProcess.write(answer);
3657
+ return;
3658
+ }
3659
+ }
3660
+ });
3661
+ const timeoutId = setTimeout(() => {
3662
+ ptyProcess.kill();
3663
+ resolve({
3664
+ success: false,
3665
+ files: [],
3666
+ error: "\u6267\u884C\u8D85\u65F6"
3667
+ });
3668
+ }, 3 * 60 * 1e3);
3669
+ ptyProcess.onExit(({ exitCode }) => {
3670
+ clearTimeout(timeoutId);
3671
+ const success = exitCode === 0;
3672
+ const filePaths = parseOutput(output);
3673
+ const files = filePaths.map(toFileInfo);
3674
+ resolve({
3675
+ success,
3676
+ files,
3677
+ error: success ? void 0 : output || `Process exited with code ${exitCode}`
3678
+ });
3679
+ });
3680
+ });
3681
+ }
3682
+
3683
+ // src/commands/component/add.handler.ts
3684
+ function printResult(result) {
3685
+ console.log(JSON.stringify(result, null, 2));
3686
+ }
3687
+ async function addComponent(key) {
3688
+ debug("\u5F00\u59CB\u5B89\u88C5\u7EC4\u4EF6: %s", key);
3689
+ debug("\u51C6\u5907 registry items...");
3690
+ const registryItemPath = await prepareComponentRegistryItems(key);
3691
+ debug("registry item \u8DEF\u5F84: %s", registryItemPath);
3692
+ const registryItem = getDownloadedRegistryItem(key);
3693
+ debug("\u83B7\u53D6\u5230 registry item: %o", registryItem);
3694
+ debug("\u6267\u884C shadcn add...");
3695
+ const executeResult = await executeShadcnAdd(registryItemPath);
3696
+ debug("shadcn \u6267\u884C\u7ED3\u679C: %o", executeResult);
3697
+ if (!executeResult.success) {
3698
+ throw new Error(executeResult.error || "\u5B89\u88C5\u5931\u8D25\uFF0C\u672A\u77E5\u539F\u56E0");
3699
+ }
3700
+ return {
3701
+ success: true,
3702
+ name: key,
3703
+ description: registryItem?.description || "",
3704
+ files: executeResult.files,
3705
+ docs: registryItem?.docs || ""
3706
+ };
3707
+ }
3708
+ async function add(key) {
3709
+ try {
3710
+ const result = await addComponent(key);
3711
+ printResult(result);
3712
+ void sendInstallEvent(key);
3713
+ } catch (error) {
3714
+ const errorMessage = error instanceof Error ? error.message : "\u5B89\u88C5\u5931\u8D25\uFF0C\u539F\u56E0\u672A\u77E5";
3715
+ printResult({
3716
+ success: false,
3717
+ errors: [{ message: errorMessage }]
3718
+ });
3719
+ } finally {
3720
+ cleanupTempDir();
3721
+ }
3722
+ }
3723
+
3724
+ // src/commands/component/index.ts
3725
+ var addCommand = {
3726
+ name: "add",
3727
+ description: "\u5B89\u88C5\u5999\u642D\u7EC4\u4EF6\u5E02\u573A\u4E2D\u7684\u7EC4\u4EF6",
3728
+ register(program) {
3729
+ program.command(this.name).description(this.description).argument("<component>", "\u7EC4\u4EF6 ID (\u4F8B\u5982 @miaoda/button)").action(async (component) => {
3730
+ await add(component);
3731
+ });
3732
+ }
3733
+ };
3734
+ var componentCommandGroup = {
3735
+ name: "component",
3736
+ description: "\u7EC4\u4EF6\u76F8\u5173\u547D\u4EE4",
3737
+ commands: [addCommand]
3738
+ };
3739
+
3740
+ // src/commands/migration/version-manager.ts
3741
+ import fs11 from "fs";
3742
+ import path9 from "path";
3288
3743
  var PACKAGE_JSON = "package.json";
3289
3744
  var VERSION_FIELD = "migrationVersion";
3290
3745
  function getPackageJsonPath2() {
3291
- return path8.join(process.cwd(), PACKAGE_JSON);
3746
+ return path9.join(process.cwd(), PACKAGE_JSON);
3292
3747
  }
3293
3748
  function getCurrentVersion() {
3294
3749
  const pkgPath = getPackageJsonPath2();
3295
- if (!fs10.existsSync(pkgPath)) {
3750
+ if (!fs11.existsSync(pkgPath)) {
3296
3751
  throw new Error("package.json not found");
3297
3752
  }
3298
- const pkg2 = JSON.parse(fs10.readFileSync(pkgPath, "utf-8"));
3753
+ const pkg2 = JSON.parse(fs11.readFileSync(pkgPath, "utf-8"));
3299
3754
  return pkg2[VERSION_FIELD] ?? 0;
3300
3755
  }
3301
3756
  function setCurrentVersion(version) {
3302
3757
  const pkgPath = getPackageJsonPath2();
3303
- const pkg2 = JSON.parse(fs10.readFileSync(pkgPath, "utf-8"));
3758
+ const pkg2 = JSON.parse(fs11.readFileSync(pkgPath, "utf-8"));
3304
3759
  pkg2[VERSION_FIELD] = version;
3305
- fs10.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
3760
+ fs11.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
3306
3761
  }
3307
3762
 
3308
3763
  // src/commands/migration/versions/v001_capability/json-migrator/detector.ts
3309
- import fs12 from "fs";
3310
- import path10 from "path";
3764
+ import fs13 from "fs";
3765
+ import path11 from "path";
3311
3766
 
3312
3767
  // src/commands/migration/versions/v001_capability/utils.ts
3313
- import fs11 from "fs";
3314
- import path9 from "path";
3768
+ import fs12 from "fs";
3769
+ import path10 from "path";
3315
3770
  var CAPABILITIES_DIR2 = "server/capabilities";
3316
3771
  function getProjectRoot3() {
3317
3772
  return process.cwd();
3318
3773
  }
3319
3774
  function getCapabilitiesDir2() {
3320
- return path9.join(getProjectRoot3(), CAPABILITIES_DIR2);
3775
+ return path10.join(getProjectRoot3(), CAPABILITIES_DIR2);
3321
3776
  }
3322
3777
  function getPluginManifestPath2(pluginKey) {
3323
- return path9.join(getProjectRoot3(), "node_modules", pluginKey, "manifest.json");
3778
+ return path10.join(getProjectRoot3(), "node_modules", pluginKey, "manifest.json");
3324
3779
  }
3325
3780
 
3326
3781
  // src/commands/migration/versions/v001_capability/json-migrator/detector.ts
3327
3782
  function detectJsonMigration() {
3328
3783
  const capabilitiesDir = getCapabilitiesDir2();
3329
- const oldFilePath = path10.join(capabilitiesDir, "capabilities.json");
3330
- if (!fs12.existsSync(oldFilePath)) {
3784
+ const oldFilePath = path11.join(capabilitiesDir, "capabilities.json");
3785
+ if (!fs13.existsSync(oldFilePath)) {
3331
3786
  return {
3332
3787
  needsMigration: false,
3333
3788
  reason: "capabilities.json not found"
3334
3789
  };
3335
3790
  }
3336
3791
  try {
3337
- const content = fs12.readFileSync(oldFilePath, "utf-8");
3792
+ const content = fs13.readFileSync(oldFilePath, "utf-8");
3338
3793
  const parsed = JSON.parse(content);
3339
3794
  if (!Array.isArray(parsed)) {
3340
3795
  return {
@@ -3385,8 +3840,8 @@ async function check(options) {
3385
3840
  }
3386
3841
 
3387
3842
  // src/commands/migration/versions/v001_capability/json-migrator/index.ts
3388
- import fs13 from "fs";
3389
- import path11 from "path";
3843
+ import fs14 from "fs";
3844
+ import path12 from "path";
3390
3845
 
3391
3846
  // src/commands/migration/versions/v001_capability/mapping.ts
3392
3847
  var DEFAULT_PLUGIN_VERSION = "1.0.0";
@@ -3616,18 +4071,18 @@ function transformCapabilities(oldCapabilities) {
3616
4071
  // src/commands/migration/versions/v001_capability/json-migrator/index.ts
3617
4072
  function loadExistingCapabilities() {
3618
4073
  const capabilitiesDir = getCapabilitiesDir2();
3619
- if (!fs13.existsSync(capabilitiesDir)) {
4074
+ if (!fs14.existsSync(capabilitiesDir)) {
3620
4075
  return [];
3621
4076
  }
3622
- const files = fs13.readdirSync(capabilitiesDir);
4077
+ const files = fs14.readdirSync(capabilitiesDir);
3623
4078
  const capabilities = [];
3624
4079
  for (const file of files) {
3625
4080
  if (file === "capabilities.json" || !file.endsWith(".json")) {
3626
4081
  continue;
3627
4082
  }
3628
4083
  try {
3629
- const filePath = path11.join(capabilitiesDir, file);
3630
- const content = fs13.readFileSync(filePath, "utf-8");
4084
+ const filePath = path12.join(capabilitiesDir, file);
4085
+ const content = fs14.readFileSync(filePath, "utf-8");
3631
4086
  const capability = JSON.parse(content);
3632
4087
  if (capability.id && capability.pluginKey) {
3633
4088
  capabilities.push(capability);
@@ -3685,9 +4140,9 @@ async function migrateJsonFiles(options) {
3685
4140
  }
3686
4141
  const capabilitiesDir = getCapabilitiesDir2();
3687
4142
  for (const cap of newCapabilities) {
3688
- const filePath = path11.join(capabilitiesDir, `${cap.id}.json`);
4143
+ const filePath = path12.join(capabilitiesDir, `${cap.id}.json`);
3689
4144
  const content = JSON.stringify(cap, null, 2);
3690
- fs13.writeFileSync(filePath, content, "utf-8");
4145
+ fs14.writeFileSync(filePath, content, "utf-8");
3691
4146
  console.log(` \u2713 Created: ${cap.id}.json`);
3692
4147
  }
3693
4148
  return {
@@ -3699,11 +4154,11 @@ async function migrateJsonFiles(options) {
3699
4154
  }
3700
4155
 
3701
4156
  // src/commands/migration/versions/v001_capability/plugin-installer/detector.ts
3702
- import fs14 from "fs";
4157
+ import fs15 from "fs";
3703
4158
  function isPluginInstalled2(pluginKey) {
3704
4159
  const actionPlugins = readActionPlugins();
3705
4160
  const manifestPath = getPluginManifestPath2(pluginKey);
3706
- return fs14.existsSync(manifestPath) && !!actionPlugins[pluginKey];
4161
+ return fs15.existsSync(manifestPath) && !!actionPlugins[pluginKey];
3707
4162
  }
3708
4163
  function detectPluginsToInstall(capabilities) {
3709
4164
  const pluginKeys = /* @__PURE__ */ new Set();
@@ -3779,12 +4234,12 @@ async function installPlugins(capabilities, options) {
3779
4234
  }
3780
4235
 
3781
4236
  // src/commands/migration/versions/v001_capability/code-migrator/index.ts
3782
- import path13 from "path";
4237
+ import path14 from "path";
3783
4238
  import { Project as Project3 } from "ts-morph";
3784
4239
 
3785
4240
  // src/commands/migration/versions/v001_capability/code-migrator/scanner.ts
3786
- import fs15 from "fs";
3787
- import path12 from "path";
4241
+ import fs16 from "fs";
4242
+ import path13 from "path";
3788
4243
  var EXCLUDED_DIRS = [
3789
4244
  "node_modules",
3790
4245
  "dist",
@@ -3799,9 +4254,9 @@ var EXCLUDED_PATTERNS = [
3799
4254
  /\.d\.ts$/
3800
4255
  ];
3801
4256
  function scanDirectory(dir, files = []) {
3802
- const entries = fs15.readdirSync(dir, { withFileTypes: true });
4257
+ const entries = fs16.readdirSync(dir, { withFileTypes: true });
3803
4258
  for (const entry of entries) {
3804
- const fullPath = path12.join(dir, entry.name);
4259
+ const fullPath = path13.join(dir, entry.name);
3805
4260
  if (entry.isDirectory()) {
3806
4261
  if (EXCLUDED_DIRS.includes(entry.name)) {
3807
4262
  continue;
@@ -3817,14 +4272,14 @@ function scanDirectory(dir, files = []) {
3817
4272
  return files;
3818
4273
  }
3819
4274
  function scanServerFiles() {
3820
- const serverDir = path12.join(getProjectRoot3(), "server");
3821
- if (!fs15.existsSync(serverDir)) {
4275
+ const serverDir = path13.join(getProjectRoot3(), "server");
4276
+ if (!fs16.existsSync(serverDir)) {
3822
4277
  return [];
3823
4278
  }
3824
4279
  return scanDirectory(serverDir);
3825
4280
  }
3826
4281
  function hasCapabilityImport(filePath) {
3827
- const content = fs15.readFileSync(filePath, "utf-8");
4282
+ const content = fs16.readFileSync(filePath, "utf-8");
3828
4283
  return /import\s+.*from\s+['"][^'"]*capabilities[^'"]*['"]/.test(content);
3829
4284
  }
3830
4285
  function scanFilesToMigrate() {
@@ -4201,7 +4656,7 @@ function analyzeFile(project, filePath, actionNameMap) {
4201
4656
  const callSites = analyzeCallSites(sourceFile, imports);
4202
4657
  const classInfo = analyzeClass(sourceFile);
4203
4658
  const { canMigrate, reason } = canAutoMigrate(classInfo);
4204
- const relativePath = path13.relative(getProjectRoot3(), filePath);
4659
+ const relativePath = path14.relative(getProjectRoot3(), filePath);
4205
4660
  return {
4206
4661
  filePath: relativePath,
4207
4662
  imports,
@@ -4212,7 +4667,7 @@ function analyzeFile(project, filePath, actionNameMap) {
4212
4667
  };
4213
4668
  }
4214
4669
  function migrateFile(project, analysis, dryRun) {
4215
- const absolutePath = path13.join(getProjectRoot3(), analysis.filePath);
4670
+ const absolutePath = path14.join(getProjectRoot3(), analysis.filePath);
4216
4671
  if (!analysis.canAutoMigrate) {
4217
4672
  return {
4218
4673
  filePath: analysis.filePath,
@@ -4315,17 +4770,17 @@ function getSuggestion(analysis) {
4315
4770
  }
4316
4771
 
4317
4772
  // src/commands/migration/versions/v001_capability/cleanup.ts
4318
- import fs16 from "fs";
4319
- import path14 from "path";
4773
+ import fs17 from "fs";
4774
+ import path15 from "path";
4320
4775
  function cleanupOldFiles(capabilities, dryRun) {
4321
4776
  const deletedFiles = [];
4322
4777
  const errors = [];
4323
4778
  const capabilitiesDir = getCapabilitiesDir2();
4324
- const oldJsonPath = path14.join(capabilitiesDir, "capabilities.json");
4325
- if (fs16.existsSync(oldJsonPath)) {
4779
+ const oldJsonPath = path15.join(capabilitiesDir, "capabilities.json");
4780
+ if (fs17.existsSync(oldJsonPath)) {
4326
4781
  try {
4327
4782
  if (!dryRun) {
4328
- fs16.unlinkSync(oldJsonPath);
4783
+ fs17.unlinkSync(oldJsonPath);
4329
4784
  }
4330
4785
  deletedFiles.push("capabilities.json");
4331
4786
  } catch (error) {
@@ -4333,11 +4788,11 @@ function cleanupOldFiles(capabilities, dryRun) {
4333
4788
  }
4334
4789
  }
4335
4790
  for (const cap of capabilities) {
4336
- const tsFilePath = path14.join(capabilitiesDir, `${cap.id}.ts`);
4337
- if (fs16.existsSync(tsFilePath)) {
4791
+ const tsFilePath = path15.join(capabilitiesDir, `${cap.id}.ts`);
4792
+ if (fs17.existsSync(tsFilePath)) {
4338
4793
  try {
4339
4794
  if (!dryRun) {
4340
- fs16.unlinkSync(tsFilePath);
4795
+ fs17.unlinkSync(tsFilePath);
4341
4796
  }
4342
4797
  deletedFiles.push(`${cap.id}.ts`);
4343
4798
  } catch (error) {
@@ -4353,8 +4808,8 @@ function cleanupOldFiles(capabilities, dryRun) {
4353
4808
  }
4354
4809
 
4355
4810
  // src/commands/migration/versions/v001_capability/report-generator.ts
4356
- import fs17 from "fs";
4357
- import path15 from "path";
4811
+ import fs18 from "fs";
4812
+ import path16 from "path";
4358
4813
  var REPORT_FILE = "capability-migration-report.md";
4359
4814
  function printSummary(result) {
4360
4815
  const { jsonMigration, pluginInstallation, codeMigration, cleanup } = result;
@@ -4517,15 +4972,15 @@ async function generateReport(result) {
4517
4972
  }
4518
4973
  lines.push("");
4519
4974
  const logDir = process.env.LOG_DIR || "logs";
4520
- if (!fs17.existsSync(logDir)) {
4975
+ if (!fs18.existsSync(logDir)) {
4521
4976
  return;
4522
4977
  }
4523
- const reportDir = path15.join(logDir, "migration");
4524
- if (!fs17.existsSync(reportDir)) {
4525
- fs17.mkdirSync(reportDir, { recursive: true });
4978
+ const reportDir = path16.join(logDir, "migration");
4979
+ if (!fs18.existsSync(reportDir)) {
4980
+ fs18.mkdirSync(reportDir, { recursive: true });
4526
4981
  }
4527
- const reportPath = path15.join(reportDir, REPORT_FILE);
4528
- fs17.writeFileSync(reportPath, lines.join("\n"), "utf-8");
4982
+ const reportPath = path16.join(reportDir, REPORT_FILE);
4983
+ fs18.writeFileSync(reportPath, lines.join("\n"), "utf-8");
4529
4984
  console.log(`\u{1F4C4} Report generated: ${reportPath}`);
4530
4985
  }
4531
4986
 
@@ -5057,10 +5512,10 @@ var migrationCommand = {
5057
5512
  };
5058
5513
 
5059
5514
  // src/commands/read-logs/index.ts
5060
- import path16 from "path";
5515
+ import path17 from "path";
5061
5516
 
5062
5517
  // src/commands/read-logs/std-utils.ts
5063
- import fs18 from "fs";
5518
+ import fs19 from "fs";
5064
5519
  function formatStdPrefixTime(localTime) {
5065
5520
  const match = localTime.match(/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/);
5066
5521
  if (!match) return localTime;
@@ -5090,11 +5545,11 @@ function stripPrefixFromStdLine(line) {
5090
5545
  return `[${time}] ${content}`;
5091
5546
  }
5092
5547
  function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarker) {
5093
- const stat = fs18.statSync(filePath);
5548
+ const stat = fs19.statSync(filePath);
5094
5549
  if (stat.size === 0) {
5095
5550
  return { lines: [], markerFound: false, totalLinesCount: 0 };
5096
5551
  }
5097
- const fd = fs18.openSync(filePath, "r");
5552
+ const fd = fs19.openSync(filePath, "r");
5098
5553
  const chunkSize = 64 * 1024;
5099
5554
  let position = stat.size;
5100
5555
  let remainder = "";
@@ -5108,7 +5563,7 @@ function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarke
5108
5563
  const length = Math.min(chunkSize, position);
5109
5564
  position -= length;
5110
5565
  const buffer = Buffer.alloc(length);
5111
- fs18.readSync(fd, buffer, 0, length, position);
5566
+ fs19.readSync(fd, buffer, 0, length, position);
5112
5567
  let chunk = buffer.toString("utf8");
5113
5568
  if (remainder) {
5114
5569
  chunk += remainder;
@@ -5150,7 +5605,7 @@ function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarke
5150
5605
  }
5151
5606
  }
5152
5607
  } finally {
5153
- fs18.closeSync(fd);
5608
+ fs19.closeSync(fd);
5154
5609
  }
5155
5610
  return { lines: collected.reverse(), markerFound, totalLinesCount };
5156
5611
  }
@@ -5171,21 +5626,21 @@ function readServerStdSegment(filePath, maxLines, offset) {
5171
5626
  }
5172
5627
 
5173
5628
  // src/commands/read-logs/tail.ts
5174
- import fs19 from "fs";
5629
+ import fs20 from "fs";
5175
5630
  function fileExists(filePath) {
5176
5631
  try {
5177
- fs19.accessSync(filePath, fs19.constants.F_OK | fs19.constants.R_OK);
5632
+ fs20.accessSync(filePath, fs20.constants.F_OK | fs20.constants.R_OK);
5178
5633
  return true;
5179
5634
  } catch {
5180
5635
  return false;
5181
5636
  }
5182
5637
  }
5183
5638
  function readFileTailLines(filePath, maxLines) {
5184
- const stat = fs19.statSync(filePath);
5639
+ const stat = fs20.statSync(filePath);
5185
5640
  if (stat.size === 0) {
5186
5641
  return [];
5187
5642
  }
5188
- const fd = fs19.openSync(filePath, "r");
5643
+ const fd = fs20.openSync(filePath, "r");
5189
5644
  const chunkSize = 64 * 1024;
5190
5645
  const chunks = [];
5191
5646
  let position = stat.size;
@@ -5195,13 +5650,13 @@ function readFileTailLines(filePath, maxLines) {
5195
5650
  const length = Math.min(chunkSize, position);
5196
5651
  position -= length;
5197
5652
  const buffer = Buffer.alloc(length);
5198
- fs19.readSync(fd, buffer, 0, length, position);
5653
+ fs20.readSync(fd, buffer, 0, length, position);
5199
5654
  chunks.unshift(buffer.toString("utf8"));
5200
5655
  const chunkLines = buffer.toString("utf8").split("\n").length - 1;
5201
5656
  collectedLines += chunkLines;
5202
5657
  }
5203
5658
  } finally {
5204
- fs19.closeSync(fd);
5659
+ fs20.closeSync(fd);
5205
5660
  }
5206
5661
  const content = chunks.join("");
5207
5662
  const allLines = content.split("\n");
@@ -5217,11 +5672,11 @@ function readFileTailLines(filePath, maxLines) {
5217
5672
  return allLines.slice(allLines.length - maxLines);
5218
5673
  }
5219
5674
  function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
5220
- const stat = fs19.statSync(filePath);
5675
+ const stat = fs20.statSync(filePath);
5221
5676
  if (stat.size === 0) {
5222
5677
  return { lines: [], totalLinesCount: 0 };
5223
5678
  }
5224
- const fd = fs19.openSync(filePath, "r");
5679
+ const fd = fs20.openSync(filePath, "r");
5225
5680
  const chunkSize = 64 * 1024;
5226
5681
  let position = stat.size;
5227
5682
  let remainder = "";
@@ -5233,7 +5688,7 @@ function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
5233
5688
  const length = Math.min(chunkSize, position);
5234
5689
  position -= length;
5235
5690
  const buffer = Buffer.alloc(length);
5236
- fs19.readSync(fd, buffer, 0, length, position);
5691
+ fs20.readSync(fd, buffer, 0, length, position);
5237
5692
  let chunk = buffer.toString("utf8");
5238
5693
  if (remainder) {
5239
5694
  chunk += remainder;
@@ -5264,7 +5719,7 @@ function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
5264
5719
  }
5265
5720
  }
5266
5721
  } finally {
5267
- fs19.closeSync(fd);
5722
+ fs20.closeSync(fd);
5268
5723
  }
5269
5724
  return { lines: collected.reverse(), totalLinesCount };
5270
5725
  }
@@ -5277,13 +5732,19 @@ function readClientStdSegment(filePath, maxLines, offset) {
5277
5732
  function extractClientStdSegment(lines, maxLines, offset) {
5278
5733
  const bodyLines = lines.map(stripPrefixFromStdLine);
5279
5734
  const hotStartMarkers = [
5735
+ // Webpack markers
5280
5736
  /file change detected\..*incremental compilation/i,
5281
5737
  /starting incremental compilation/i,
5282
5738
  /starting compilation/i,
5283
5739
  /\bcompiling\b/i,
5284
- /\brecompil/i
5740
+ /\brecompil/i,
5741
+ // Vite markers - format: "3:11:46 PM [vite] (client) hmr update ..."
5742
+ /VITE v[\d.]+.*ready/i,
5743
+ /\[vite\].*page reload/i,
5744
+ /\[vite\].*hmr update/i
5285
5745
  ];
5286
5746
  const hotEndMarkers = [
5747
+ // Webpack markers
5287
5748
  /file change detected\..*incremental compilation/i,
5288
5749
  /\bwebpack compiled\b/i,
5289
5750
  /compiled successfully/i,
@@ -5294,14 +5755,25 @@ function extractClientStdSegment(lines, maxLines, offset) {
5294
5755
  /\bhmr\b/i,
5295
5756
  /hot update/i,
5296
5757
  /\bhot reload\b/i,
5297
- /\bhmr update\b/i
5758
+ /\bhmr update\b/i,
5759
+ // Vite markers
5760
+ /VITE v[\d.]+.*ready/i,
5761
+ /\[vite\].*page reload/i,
5762
+ /\[vite\].*hmr update/i,
5763
+ /\[vite\].*hmr invalidate/i,
5764
+ /\[vite\].*internal server error/i,
5765
+ /\[vite\].*pre-transform error/i
5298
5766
  ];
5299
5767
  const compiledMarkers = [
5768
+ // Webpack markers
5300
5769
  /\bwebpack compiled\b/i,
5301
5770
  /compiled successfully/i,
5302
5771
  /compiled with warnings/i,
5303
5772
  /compiled with errors/i,
5304
- /failed to compile/i
5773
+ /failed to compile/i,
5774
+ // Vite markers
5775
+ /VITE v[\d.]+.*ready/i,
5776
+ /\[vite\].*hmr update/i
5305
5777
  ];
5306
5778
  let startIndex = -1;
5307
5779
  for (let i = bodyLines.length - 1; i >= 0; i -= 1) {
@@ -5354,14 +5826,15 @@ function extractClientStdSegment(lines, maxLines, offset) {
5354
5826
  }
5355
5827
  const segment = startIndex === -1 ? bodyLines : bodyLines.slice(startIndex);
5356
5828
  if (segment.length === 0) {
5357
- return { lines: [], totalLinesCount: 0 };
5829
+ return { lines: [], totalLinesCount: 0, allLines: [] };
5358
5830
  }
5359
5831
  const totalLinesCount = segment.length;
5360
5832
  const endExclusive = Math.max(0, segment.length - offset);
5361
5833
  const start = Math.max(0, endExclusive - maxLines);
5362
5834
  return {
5363
5835
  lines: segment.slice(start, endExclusive),
5364
- totalLinesCount
5836
+ totalLinesCount,
5837
+ allLines: segment
5365
5838
  };
5366
5839
  }
5367
5840
 
@@ -5388,7 +5861,7 @@ function readDevStdSegment(filePath, maxLines, offset) {
5388
5861
  }
5389
5862
 
5390
5863
  // src/commands/read-logs/json-lines.ts
5391
- import fs20 from "fs";
5864
+ import fs21 from "fs";
5392
5865
  function normalizePid(value) {
5393
5866
  if (typeof value === "number") {
5394
5867
  return String(value);
@@ -5439,11 +5912,11 @@ function buildWantedLevelSet(levels) {
5439
5912
  return set.size > 0 ? set : null;
5440
5913
  }
5441
5914
  function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
5442
- const stat = fs20.statSync(filePath);
5915
+ const stat = fs21.statSync(filePath);
5443
5916
  if (stat.size === 0) {
5444
5917
  return { lines: [], totalLinesCount: 0 };
5445
5918
  }
5446
- const fd = fs20.openSync(filePath, "r");
5919
+ const fd = fs21.openSync(filePath, "r");
5447
5920
  const chunkSize = 64 * 1024;
5448
5921
  let position = stat.size;
5449
5922
  let remainder = "";
@@ -5458,7 +5931,7 @@ function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
5458
5931
  const length = Math.min(chunkSize, position);
5459
5932
  position -= length;
5460
5933
  const buffer = Buffer.alloc(length);
5461
- fs20.readSync(fd, buffer, 0, length, position);
5934
+ fs21.readSync(fd, buffer, 0, length, position);
5462
5935
  let chunk = buffer.toString("utf8");
5463
5936
  if (remainder) {
5464
5937
  chunk += remainder;
@@ -5520,7 +5993,7 @@ function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
5520
5993
  }
5521
5994
  }
5522
5995
  } finally {
5523
- fs20.closeSync(fd);
5996
+ fs21.closeSync(fd);
5524
5997
  }
5525
5998
  return { lines: collected.reverse(), totalLinesCount };
5526
5999
  }
@@ -5563,11 +6036,11 @@ function extractTraceId(obj) {
5563
6036
  function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
5564
6037
  const wanted = traceId.trim();
5565
6038
  if (!wanted) return { lines: [], totalLinesCount: 0 };
5566
- const stat = fs20.statSync(filePath);
6039
+ const stat = fs21.statSync(filePath);
5567
6040
  if (stat.size === 0) {
5568
6041
  return { lines: [], totalLinesCount: 0 };
5569
6042
  }
5570
- const fd = fs20.openSync(filePath, "r");
6043
+ const fd = fs21.openSync(filePath, "r");
5571
6044
  const chunkSize = 64 * 1024;
5572
6045
  let position = stat.size;
5573
6046
  let remainder = "";
@@ -5580,7 +6053,7 @@ function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
5580
6053
  const length = Math.min(chunkSize, position);
5581
6054
  position -= length;
5582
6055
  const buffer = Buffer.alloc(length);
5583
- fs20.readSync(fd, buffer, 0, length, position);
6056
+ fs21.readSync(fd, buffer, 0, length, position);
5584
6057
  let chunk = buffer.toString("utf8");
5585
6058
  if (remainder) {
5586
6059
  chunk += remainder;
@@ -5633,7 +6106,7 @@ function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
5633
6106
  }
5634
6107
  }
5635
6108
  } finally {
5636
- fs20.closeSync(fd);
6109
+ fs21.closeSync(fd);
5637
6110
  }
5638
6111
  return { lines: collected.reverse(), totalLinesCount };
5639
6112
  }
@@ -5642,11 +6115,11 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
5642
6115
  if (!wantedLevelSet) {
5643
6116
  return { lines: [], totalLinesCount: 0 };
5644
6117
  }
5645
- const stat = fs20.statSync(filePath);
6118
+ const stat = fs21.statSync(filePath);
5646
6119
  if (stat.size === 0) {
5647
6120
  return { lines: [], totalLinesCount: 0 };
5648
6121
  }
5649
- const fd = fs20.openSync(filePath, "r");
6122
+ const fd = fs21.openSync(filePath, "r");
5650
6123
  const chunkSize = 64 * 1024;
5651
6124
  let position = stat.size;
5652
6125
  let remainder = "";
@@ -5658,7 +6131,7 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
5658
6131
  const length = Math.min(chunkSize, position);
5659
6132
  position -= length;
5660
6133
  const buffer = Buffer.alloc(length);
5661
- fs20.readSync(fd, buffer, 0, length, position);
6134
+ fs21.readSync(fd, buffer, 0, length, position);
5662
6135
  let chunk = buffer.toString("utf8");
5663
6136
  if (remainder) {
5664
6137
  chunk += remainder;
@@ -5705,7 +6178,7 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
5705
6178
  }
5706
6179
  }
5707
6180
  } finally {
5708
- fs20.closeSync(fd);
6181
+ fs21.closeSync(fd);
5709
6182
  }
5710
6183
  return { lines: collected.reverse(), totalLinesCount };
5711
6184
  }
@@ -5898,12 +6371,21 @@ async function readLatestLogLinesMeta(options) {
5898
6371
  return { lines: [], totalLinesCount: 0 };
5899
6372
  }
5900
6373
  async function readLogsJsonResult(options) {
5901
- const { lines, totalLinesCount } = await readLatestLogLinesMeta(options);
6374
+ const { lines, totalLinesCount, allLines } = await readLatestLogLinesMeta(options);
5902
6375
  if (options.type === "server-std" || options.type === "client-std" || options.type === "dev" || options.type === "dev-std" || options.type === "install-dep-std") {
6376
+ const linesForErrorCheck = allLines ?? lines;
6377
+ const stackTracePattern = /\bat\s+\S+\s+\([^)]+:\d+:\d+\)/;
6378
+ const linesToFilter = allLines ?? lines;
6379
+ const filteredLines = linesToFilter.filter((line) => !stackTracePattern.test(line));
6380
+ const maxLines = options.maxLines ?? 30;
6381
+ const offset = options.offset ?? 0;
6382
+ const endExclusive = Math.max(0, filteredLines.length - offset);
6383
+ const start = Math.max(0, endExclusive - maxLines);
6384
+ const paginatedLogs = filteredLines.slice(start, endExclusive);
5903
6385
  return {
5904
- hasError: hasErrorInStdLines(lines),
5905
- totalLinesCount,
5906
- logs: lines
6386
+ hasError: hasErrorInStdLines(linesForErrorCheck),
6387
+ totalLinesCount: filteredLines.length,
6388
+ logs: paginatedLogs
5907
6389
  };
5908
6390
  }
5909
6391
  const logs = [];
@@ -5930,30 +6412,30 @@ async function readLogsJsonResult(options) {
5930
6412
  };
5931
6413
  }
5932
6414
  function resolveLogFilePath(logDir, type) {
5933
- const base = path16.isAbsolute(logDir) ? logDir : path16.join(process.cwd(), logDir);
6415
+ const base = path17.isAbsolute(logDir) ? logDir : path17.join(process.cwd(), logDir);
5934
6416
  if (type === "server") {
5935
- return path16.join(base, "server.log");
6417
+ return path17.join(base, "server.log");
5936
6418
  }
5937
6419
  if (type === "trace") {
5938
- return path16.join(base, "trace.log");
6420
+ return path17.join(base, "trace.log");
5939
6421
  }
5940
6422
  if (type === "server-std") {
5941
- return path16.join(base, "server.std.log");
6423
+ return path17.join(base, "server.std.log");
5942
6424
  }
5943
6425
  if (type === "client-std") {
5944
- return path16.join(base, "client.std.log");
6426
+ return path17.join(base, "client.std.log");
5945
6427
  }
5946
6428
  if (type === "dev") {
5947
- return path16.join(base, "dev.log");
6429
+ return path17.join(base, "dev.log");
5948
6430
  }
5949
6431
  if (type === "dev-std") {
5950
- return path16.join(base, "dev.std.log");
6432
+ return path17.join(base, "dev.std.log");
5951
6433
  }
5952
6434
  if (type === "install-dep-std") {
5953
- return path16.join(base, "install-dep.std.log");
6435
+ return path17.join(base, "install-dep.std.log");
5954
6436
  }
5955
6437
  if (type === "browser") {
5956
- return path16.join(base, "browser.log");
6438
+ return path17.join(base, "browser.log");
5957
6439
  }
5958
6440
  throw new Error(`Unsupported log type: ${type}`);
5959
6441
  }
@@ -6015,17 +6497,18 @@ var commands = [
6015
6497
  syncCommand,
6016
6498
  actionPluginCommandGroup,
6017
6499
  capabilityCommandGroup,
6500
+ componentCommandGroup,
6018
6501
  migrationCommand,
6019
6502
  readLogsCommand
6020
6503
  ];
6021
6504
 
6022
6505
  // src/index.ts
6023
- var envPath = path17.join(process.cwd(), ".env");
6024
- if (fs21.existsSync(envPath)) {
6506
+ var envPath = path18.join(process.cwd(), ".env");
6507
+ if (fs22.existsSync(envPath)) {
6025
6508
  dotenvConfig({ path: envPath });
6026
6509
  }
6027
- var __dirname = path17.dirname(fileURLToPath4(import.meta.url));
6028
- var pkg = JSON.parse(fs21.readFileSync(path17.join(__dirname, "../package.json"), "utf-8"));
6510
+ var __dirname = path18.dirname(fileURLToPath4(import.meta.url));
6511
+ var pkg = JSON.parse(fs22.readFileSync(path18.join(__dirname, "../package.json"), "utf-8"));
6029
6512
  var cli = new FullstackCLI(pkg.version);
6030
6513
  cli.useAll(commands);
6031
6514
  cli.run();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/fullstack-cli",
3
- "version": "1.1.19",
3
+ "version": "1.1.20-alpha.1",
4
4
  "description": "CLI tool for fullstack template management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -32,18 +32,23 @@
32
32
  },
33
33
  "dependencies": {
34
34
  "@lark-apaas/http-client": "^0.1.2",
35
+ "@lydell/node-pty": "1.1.0",
35
36
  "@vercel/nft": "^0.30.3",
36
37
  "commander": "^13.0.0",
38
+ "debug": "^4.4.3",
37
39
  "dotenv": "^16.0.0",
38
40
  "drizzle-kit": "0.31.5",
39
41
  "drizzle-orm": "0.44.6",
42
+ "es-toolkit": "^1.44.0",
40
43
  "inflection": "^3.0.2",
41
44
  "pinyin-pro": "^3.27.0",
42
45
  "postgres": "^3.4.3",
46
+ "shadcn": "3.8.2",
43
47
  "ts-morph": "^27.0.0",
44
48
  "zod-to-json-schema": "^3.24.1"
45
49
  },
46
50
  "devDependencies": {
51
+ "@types/debug": "^4",
47
52
  "@types/node": "^22.0.0",
48
53
  "tsup": "^8.3.5",
49
54
  "typescript": "^5.9.2",