@lark-apaas/fullstack-cli 1.1.16-beta.0 → 1.1.16-beta.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/README.md +41 -3
- package/dist/gen-dbschema-template/types.ts +54 -0
- package/dist/index.js +2160 -323
- package/package.json +6 -1
- package/templates/.spark_project +16 -0
- package/templates/drizzle.config.ts +55 -0
- package/templates/nest-cli.json +1 -1
- package/templates/scripts/build.sh +17 -11
- package/templates/scripts/dev.js +275 -0
- package/templates/scripts/dev.sh +1 -240
- package/templates/scripts/prune-smart.js +4 -6
- package/templates/scripts/run.sh +4 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import { fileURLToPath as
|
|
2
|
+
import fs26 from "fs";
|
|
3
|
+
import path22 from "path";
|
|
4
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
5
5
|
import { config as dotenvConfig } from "dotenv";
|
|
6
6
|
|
|
7
7
|
// src/cli.ts
|
|
@@ -472,11 +472,16 @@ 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) {
|
|
478
482
|
const { sourceFile, stats } = ctx;
|
|
479
483
|
const fullText = sourceFile.getFullText();
|
|
484
|
+
const replacements = [];
|
|
480
485
|
sourceFile.forEachDescendant((node) => {
|
|
481
486
|
if (!Node5.isCallExpression(node)) {
|
|
482
487
|
return;
|
|
@@ -490,25 +495,49 @@ var replaceUnknownTransform = {
|
|
|
490
495
|
const lines = textBefore.split("\n");
|
|
491
496
|
let factoryName = "text";
|
|
492
497
|
let foundKnownType = false;
|
|
498
|
+
let isArrayType = false;
|
|
493
499
|
for (let i = lines.length - 1; i >= Math.max(0, lines.length - 5); i--) {
|
|
494
500
|
const line = lines[i];
|
|
495
501
|
const todoMatch = line.match(/\/\/ TODO: failed to parse database type '(?:\w+\.)?([\w_]+)(\[\])?'/);
|
|
496
502
|
if (todoMatch) {
|
|
497
503
|
const typeName = todoMatch[1];
|
|
498
|
-
|
|
504
|
+
isArrayType = todoMatch[2] === "[]";
|
|
505
|
+
if (isArrayType && KNOWN_ARRAY_TYPE_FACTORIES[typeName]) {
|
|
506
|
+
factoryName = KNOWN_ARRAY_TYPE_FACTORIES[typeName];
|
|
507
|
+
foundKnownType = true;
|
|
508
|
+
} else if (KNOWN_TYPE_FACTORIES[typeName]) {
|
|
499
509
|
factoryName = KNOWN_TYPE_FACTORIES[typeName];
|
|
500
510
|
foundKnownType = true;
|
|
501
511
|
}
|
|
502
512
|
break;
|
|
503
513
|
}
|
|
504
514
|
}
|
|
515
|
+
replacements.push({
|
|
516
|
+
expression,
|
|
517
|
+
factoryName,
|
|
518
|
+
foundKnownType,
|
|
519
|
+
isArrayType,
|
|
520
|
+
node
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
for (const { expression, factoryName, foundKnownType, isArrayType, node } of replacements) {
|
|
505
524
|
expression.replaceWithText(factoryName);
|
|
525
|
+
if (isArrayType && foundKnownType) {
|
|
526
|
+
const parent = node.getParent();
|
|
527
|
+
if (Node5.isPropertyAccessExpression(parent) && parent.getName() === "array") {
|
|
528
|
+
const grandParent = parent.getParent();
|
|
529
|
+
if (Node5.isCallExpression(grandParent)) {
|
|
530
|
+
const nodeText = node.getText();
|
|
531
|
+
grandParent.replaceWithText(nodeText);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
506
535
|
if (foundKnownType) {
|
|
507
536
|
stats.replacedUnknown++;
|
|
508
537
|
} else {
|
|
509
538
|
stats.fallbackToText++;
|
|
510
539
|
}
|
|
511
|
-
}
|
|
540
|
+
}
|
|
512
541
|
const todoCommentRegex = /\/\/ TODO: failed to parse database type '[^']+'\s*\n/g;
|
|
513
542
|
const currentText = sourceFile.getFullText();
|
|
514
543
|
const cleanedText = currentText.replace(todoCommentRegex, "");
|
|
@@ -1005,6 +1034,215 @@ function resolveTemplateTypesPath() {
|
|
|
1005
1034
|
return void 0;
|
|
1006
1035
|
}
|
|
1007
1036
|
|
|
1037
|
+
// src/commands/db/gen-dbschema/utils/fetch-column-comments.ts
|
|
1038
|
+
import postgres from "postgres";
|
|
1039
|
+
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
1040
|
+
async function fetchColumnComments(connectionString, options = {}) {
|
|
1041
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
1042
|
+
const url = new URL(connectionString);
|
|
1043
|
+
const schemaName = url.searchParams.get("schema") ?? "public";
|
|
1044
|
+
const start = Date.now();
|
|
1045
|
+
console.log(`[fetchColumnComments] \u2192 Querying pg_description for schema=${schemaName} (timeout=${timeoutMs}ms)`);
|
|
1046
|
+
const sql = postgres(connectionString, {
|
|
1047
|
+
connect_timeout: Math.ceil(timeoutMs / 1e3),
|
|
1048
|
+
idle_timeout: Math.ceil(timeoutMs / 1e3)
|
|
1049
|
+
});
|
|
1050
|
+
try {
|
|
1051
|
+
const queryPromise = sql`
|
|
1052
|
+
SELECT
|
|
1053
|
+
c.table_name AS "tableName",
|
|
1054
|
+
c.column_name AS "columnName",
|
|
1055
|
+
pgd.description AS "comment"
|
|
1056
|
+
FROM information_schema.columns c
|
|
1057
|
+
JOIN pg_catalog.pg_statio_all_tables st
|
|
1058
|
+
ON c.table_name = st.relname AND c.table_schema = st.schemaname
|
|
1059
|
+
JOIN pg_catalog.pg_description pgd
|
|
1060
|
+
ON pgd.objoid = st.relid AND pgd.objsubid = c.ordinal_position
|
|
1061
|
+
WHERE c.table_schema = ${schemaName}
|
|
1062
|
+
AND pgd.description IS NOT NULL
|
|
1063
|
+
AND pgd.description != ''
|
|
1064
|
+
`;
|
|
1065
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
1066
|
+
setTimeout(() => reject(new Error(`Timeout after ${timeoutMs}ms`)), timeoutMs);
|
|
1067
|
+
});
|
|
1068
|
+
const result = await Promise.race([queryPromise, timeoutPromise]);
|
|
1069
|
+
const commentMap = /* @__PURE__ */ new Map();
|
|
1070
|
+
for (const row of result) {
|
|
1071
|
+
const key = `${row.tableName}.${row.columnName}`;
|
|
1072
|
+
commentMap.set(key, row.comment);
|
|
1073
|
+
}
|
|
1074
|
+
console.log(`[fetchColumnComments] \u2190 Fetched ${commentMap.size} column comments (${Date.now() - start}ms)`);
|
|
1075
|
+
return commentMap;
|
|
1076
|
+
} finally {
|
|
1077
|
+
await sql.end().catch(() => {
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
function extractTypeAnnotation(comment) {
|
|
1082
|
+
const typeStart = comment.indexOf("@type");
|
|
1083
|
+
if (typeStart === -1) return null;
|
|
1084
|
+
const afterType = comment.slice(typeStart + 5).trimStart();
|
|
1085
|
+
if (!afterType.startsWith("{")) return null;
|
|
1086
|
+
let depth = 0;
|
|
1087
|
+
let endIndex = 0;
|
|
1088
|
+
for (let i = 0; i < afterType.length; i++) {
|
|
1089
|
+
if (afterType[i] === "{") depth++;
|
|
1090
|
+
if (afterType[i] === "}") depth--;
|
|
1091
|
+
if (depth === 0) {
|
|
1092
|
+
endIndex = i + 1;
|
|
1093
|
+
break;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
if (endIndex === 0) return null;
|
|
1097
|
+
return afterType.slice(0, endIndex);
|
|
1098
|
+
}
|
|
1099
|
+
function parseColumnComment(comment) {
|
|
1100
|
+
const typeValue = extractTypeAnnotation(comment);
|
|
1101
|
+
if (typeValue) {
|
|
1102
|
+
const descMatch = comment.match(/@description\s+([^@]+)/);
|
|
1103
|
+
return {
|
|
1104
|
+
type: typeValue,
|
|
1105
|
+
description: descMatch?.[1]?.trim()
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
return { description: comment.trim() };
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
// src/commands/db/gen-dbschema/transforms/text/jsonb-comments.ts
|
|
1112
|
+
function addJsonbTypeComments(source, columnComments) {
|
|
1113
|
+
if (!columnComments || columnComments.size === 0) {
|
|
1114
|
+
return { text: source, added: 0 };
|
|
1115
|
+
}
|
|
1116
|
+
const lines = source.split("\n");
|
|
1117
|
+
const result = [];
|
|
1118
|
+
let added = 0;
|
|
1119
|
+
let currentTableName = null;
|
|
1120
|
+
const tableDefRegex = /export const\s+\w+\s*=\s*pgTable\(\s*["'`]([^"'`]+)["'`]/;
|
|
1121
|
+
const jsonFieldWithNameRegex = /^\s*(\w+):\s*(?:json|jsonb)\(\s*["'`]([^"'`]+)["'`]\)/;
|
|
1122
|
+
const jsonFieldNoNameRegex = /^\s*(\w+):\s*(?:json|jsonb)\(\s*\)/;
|
|
1123
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1124
|
+
const line = lines[i];
|
|
1125
|
+
const tableMatch = line.match(tableDefRegex);
|
|
1126
|
+
if (tableMatch) {
|
|
1127
|
+
currentTableName = tableMatch[1];
|
|
1128
|
+
}
|
|
1129
|
+
let columnName = null;
|
|
1130
|
+
const jsonMatchWithName = line.match(jsonFieldWithNameRegex);
|
|
1131
|
+
const jsonMatchNoName = line.match(jsonFieldNoNameRegex);
|
|
1132
|
+
if (jsonMatchWithName) {
|
|
1133
|
+
columnName = jsonMatchWithName[2];
|
|
1134
|
+
} else if (jsonMatchNoName) {
|
|
1135
|
+
columnName = jsonMatchNoName[1];
|
|
1136
|
+
}
|
|
1137
|
+
if (columnName && currentTableName) {
|
|
1138
|
+
const commentKey = `${currentTableName}.${columnName}`;
|
|
1139
|
+
const comment = columnComments.get(commentKey);
|
|
1140
|
+
if (comment) {
|
|
1141
|
+
const parsed = parseColumnComment(comment);
|
|
1142
|
+
const indentMatch = line.match(/^\s*/);
|
|
1143
|
+
const indent = indentMatch ? indentMatch[0] : "";
|
|
1144
|
+
const prevLine = result[result.length - 1]?.trim() ?? "";
|
|
1145
|
+
if (!prevLine.startsWith("/**") && !prevLine.startsWith("*") && !prevLine.startsWith("//")) {
|
|
1146
|
+
const commentLines = [];
|
|
1147
|
+
commentLines.push(`${indent}/**`);
|
|
1148
|
+
if (parsed.description) {
|
|
1149
|
+
const safeDesc = parsed.description.replace(/[\r\n]+/g, " ").trim();
|
|
1150
|
+
commentLines.push(`${indent} * ${safeDesc}`);
|
|
1151
|
+
}
|
|
1152
|
+
if (parsed.type) {
|
|
1153
|
+
if (parsed.description) {
|
|
1154
|
+
commentLines.push(`${indent} *`);
|
|
1155
|
+
}
|
|
1156
|
+
const safeType = parsed.type.replace(/[\r\n]+/g, " ").trim();
|
|
1157
|
+
commentLines.push(`${indent} * @type ${safeType}`);
|
|
1158
|
+
}
|
|
1159
|
+
commentLines.push(`${indent} */`);
|
|
1160
|
+
result.push(...commentLines);
|
|
1161
|
+
added++;
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
if (line.match(/^\}\);?\s*$/) || line.match(/^}\s*,\s*\{/)) {
|
|
1166
|
+
currentTableName = null;
|
|
1167
|
+
}
|
|
1168
|
+
result.push(line);
|
|
1169
|
+
}
|
|
1170
|
+
return { text: result.join("\n"), added };
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
// src/commands/db/gen-dbschema/transforms/text/synced-table-comments.ts
|
|
1174
|
+
var TABLE_COMMENT = "Synced table: data is auto-synced from external source. Do not rename or delete this table.";
|
|
1175
|
+
var FIELD_COMMENT = "Synced field: auto-synced, do not modify or delete";
|
|
1176
|
+
var TABLE_DEF_REGEX = /^(export const\s+\w+\s*=\s*(?:pgTable|pgView|pgMaterializedView)\(\s*["'`])([^"'`]+)(["'`])/;
|
|
1177
|
+
var FIELD_WITH_NAME_REGEX = /^\s*[\w"']+\s*:\s*\w+\(\s*["'`]([^"'`]+)["'`]/;
|
|
1178
|
+
var FIELD_PROP_NAME_REGEX = /^\s*([\w]+)\s*:/;
|
|
1179
|
+
function addSyncedTableComments(source, syncedTableMap) {
|
|
1180
|
+
if (!syncedTableMap || syncedTableMap.size === 0) {
|
|
1181
|
+
return { text: source, added: 0 };
|
|
1182
|
+
}
|
|
1183
|
+
const lines = source.split("\n");
|
|
1184
|
+
const result = [];
|
|
1185
|
+
let added = 0;
|
|
1186
|
+
let currentSyncedFields = null;
|
|
1187
|
+
let insideTableBody = false;
|
|
1188
|
+
let braceDepth = 0;
|
|
1189
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1190
|
+
const line = lines[i];
|
|
1191
|
+
const tableMatch = line.match(TABLE_DEF_REGEX);
|
|
1192
|
+
if (tableMatch) {
|
|
1193
|
+
const tableName = tableMatch[2];
|
|
1194
|
+
const syncedFields = syncedTableMap.get(tableName);
|
|
1195
|
+
if (syncedFields) {
|
|
1196
|
+
currentSyncedFields = syncedFields;
|
|
1197
|
+
insideTableBody = true;
|
|
1198
|
+
braceDepth = 0;
|
|
1199
|
+
const prevLine = result[result.length - 1]?.trim() ?? "";
|
|
1200
|
+
if (!prevLine.includes("Synced table")) {
|
|
1201
|
+
const indentMatch = line.match(/^\s*/);
|
|
1202
|
+
const indent = indentMatch ? indentMatch[0] : "";
|
|
1203
|
+
result.push(`${indent}// ${TABLE_COMMENT}`);
|
|
1204
|
+
added++;
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
if (insideTableBody) {
|
|
1209
|
+
for (const ch of line) {
|
|
1210
|
+
if (ch === "{") braceDepth++;
|
|
1211
|
+
if (ch === "}") braceDepth--;
|
|
1212
|
+
}
|
|
1213
|
+
if (braceDepth <= 0) {
|
|
1214
|
+
insideTableBody = false;
|
|
1215
|
+
currentSyncedFields = null;
|
|
1216
|
+
}
|
|
1217
|
+
if (currentSyncedFields && braceDepth >= 1 && !tableMatch) {
|
|
1218
|
+
const columnName = extractColumnName2(line);
|
|
1219
|
+
if (columnName && currentSyncedFields.has(columnName)) {
|
|
1220
|
+
const prevLine = result[result.length - 1]?.trim() ?? "";
|
|
1221
|
+
if (!prevLine.includes("Synced field")) {
|
|
1222
|
+
const indentMatch = line.match(/^\s*/);
|
|
1223
|
+
const indent = indentMatch ? indentMatch[0] : "";
|
|
1224
|
+
result.push(`${indent}// ${FIELD_COMMENT}`);
|
|
1225
|
+
added++;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
result.push(line);
|
|
1231
|
+
}
|
|
1232
|
+
return { text: result.join("\n"), added };
|
|
1233
|
+
}
|
|
1234
|
+
function extractColumnName2(line) {
|
|
1235
|
+
const withNameMatch = line.match(FIELD_WITH_NAME_REGEX);
|
|
1236
|
+
if (withNameMatch) {
|
|
1237
|
+
return withNameMatch[1];
|
|
1238
|
+
}
|
|
1239
|
+
const propMatch = line.match(FIELD_PROP_NAME_REGEX);
|
|
1240
|
+
if (propMatch) {
|
|
1241
|
+
return propMatch[1];
|
|
1242
|
+
}
|
|
1243
|
+
return null;
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1008
1246
|
// src/commands/db/gen-dbschema/transforms/text/table-aliases.ts
|
|
1009
1247
|
var TABLE_ALIAS_MARKER = "// table aliases";
|
|
1010
1248
|
function generateTableAliases(source) {
|
|
@@ -1041,7 +1279,7 @@ function formatSource(source) {
|
|
|
1041
1279
|
}
|
|
1042
1280
|
|
|
1043
1281
|
// src/commands/db/gen-dbschema/postprocess.ts
|
|
1044
|
-
function postprocessSchema(rawSource) {
|
|
1282
|
+
function postprocessSchema(rawSource, options = {}) {
|
|
1045
1283
|
const patchResult = patchDefects(rawSource);
|
|
1046
1284
|
let source = patchResult.text;
|
|
1047
1285
|
const { sourceFile } = parseSource(source);
|
|
@@ -1051,12 +1289,18 @@ function postprocessSchema(rawSource) {
|
|
|
1051
1289
|
source = ensureHeader(source);
|
|
1052
1290
|
source = addSystemFieldComments(source);
|
|
1053
1291
|
source = inlineCustomTypes(source);
|
|
1292
|
+
const jsonbCommentsResult = addJsonbTypeComments(source, options.columnComments);
|
|
1293
|
+
source = jsonbCommentsResult.text;
|
|
1294
|
+
const syncedCommentsResult = addSyncedTableComments(source, options.syncedTableMap);
|
|
1295
|
+
source = syncedCommentsResult.text;
|
|
1054
1296
|
source = generateTableAliases(source);
|
|
1055
1297
|
source = formatSource(source);
|
|
1056
1298
|
return {
|
|
1057
1299
|
source,
|
|
1058
1300
|
astStats,
|
|
1059
|
-
patchedDefects: patchResult.fixed
|
|
1301
|
+
patchedDefects: patchResult.fixed,
|
|
1302
|
+
addedJsonbComments: jsonbCommentsResult.added,
|
|
1303
|
+
addedSyncedComments: syncedCommentsResult.added
|
|
1060
1304
|
};
|
|
1061
1305
|
}
|
|
1062
1306
|
function logStats(result, prefix = "[postprocess]") {
|
|
@@ -1103,17 +1347,26 @@ function logStats(result, prefix = "[postprocess]") {
|
|
|
1103
1347
|
if (astStats.removedImports.length > 0) {
|
|
1104
1348
|
console.info(`${prefix} Removed imports: ${astStats.removedImports.join(", ")}`);
|
|
1105
1349
|
}
|
|
1350
|
+
if (result.addedJsonbComments > 0) {
|
|
1351
|
+
console.info(`${prefix} Added ${result.addedJsonbComments} JSDoc comments for jsonb fields`);
|
|
1352
|
+
}
|
|
1353
|
+
if (result.addedSyncedComments > 0) {
|
|
1354
|
+
console.info(`${prefix} Added ${result.addedSyncedComments} comments for synced tables/fields`);
|
|
1355
|
+
}
|
|
1106
1356
|
}
|
|
1107
1357
|
|
|
1108
1358
|
// src/commands/db/gen-dbschema/index.ts
|
|
1109
|
-
function postprocessDrizzleSchema(targetPath) {
|
|
1359
|
+
async function postprocessDrizzleSchema(targetPath, options = {}) {
|
|
1110
1360
|
const resolvedPath = path.resolve(targetPath);
|
|
1111
1361
|
if (!fs3.existsSync(resolvedPath)) {
|
|
1112
1362
|
console.warn(`[postprocess-drizzle-schema] File not found: ${resolvedPath}`);
|
|
1113
1363
|
return void 0;
|
|
1114
1364
|
}
|
|
1115
1365
|
const rawSource = fs3.readFileSync(resolvedPath, "utf8");
|
|
1116
|
-
const result = postprocessSchema(rawSource
|
|
1366
|
+
const result = postprocessSchema(rawSource, {
|
|
1367
|
+
columnComments: options.columnComments,
|
|
1368
|
+
syncedTableMap: options.syncedTableMap
|
|
1369
|
+
});
|
|
1117
1370
|
fs3.writeFileSync(resolvedPath, result.source, "utf8");
|
|
1118
1371
|
logStats(result, "[postprocess-drizzle-schema]");
|
|
1119
1372
|
return {
|
|
@@ -1122,10 +1375,114 @@ function postprocessDrizzleSchema(targetPath) {
|
|
|
1122
1375
|
unmatchedUnknown: result.astStats.unmatchedUnknown,
|
|
1123
1376
|
patchedDefects: result.patchedDefects,
|
|
1124
1377
|
replacedTimestamps: result.astStats.replacedTimestamp,
|
|
1125
|
-
replacedDefaultNow: result.astStats.replacedDefaultNow
|
|
1378
|
+
replacedDefaultNow: result.astStats.replacedDefaultNow,
|
|
1379
|
+
addedJsonbComments: result.addedJsonbComments,
|
|
1380
|
+
addedSyncedComments: result.addedSyncedComments
|
|
1126
1381
|
};
|
|
1127
1382
|
}
|
|
1128
1383
|
|
|
1384
|
+
// src/utils/http-client.ts
|
|
1385
|
+
import { HttpClient } from "@lark-apaas/http-client";
|
|
1386
|
+
var clientInstance = null;
|
|
1387
|
+
function getHttpClient() {
|
|
1388
|
+
if (!clientInstance) {
|
|
1389
|
+
clientInstance = new HttpClient({
|
|
1390
|
+
timeout: 3e4,
|
|
1391
|
+
platform: {
|
|
1392
|
+
enabled: true
|
|
1393
|
+
}
|
|
1394
|
+
});
|
|
1395
|
+
const canaryEnv = process.env.FORCE_FRAMEWORK_CLI_CANARY_ENV;
|
|
1396
|
+
if (canaryEnv) {
|
|
1397
|
+
clientInstance.interceptors.request.use((req) => {
|
|
1398
|
+
req.headers["x-tt-env"] = canaryEnv;
|
|
1399
|
+
return req;
|
|
1400
|
+
});
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
return clientInstance;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
// src/commands/db/gen-dbschema/utils/fetch-synced-tables.ts
|
|
1407
|
+
var DEFAULT_TIMEOUT_MS2 = 1e4;
|
|
1408
|
+
async function fetchSyncedTables(appId, workspace) {
|
|
1409
|
+
try {
|
|
1410
|
+
const client = getHttpClient();
|
|
1411
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
1412
|
+
setTimeout(
|
|
1413
|
+
() => reject(new Error(`Timeout after ${DEFAULT_TIMEOUT_MS2}ms`)),
|
|
1414
|
+
DEFAULT_TIMEOUT_MS2
|
|
1415
|
+
);
|
|
1416
|
+
});
|
|
1417
|
+
const dbBranch = process.env.FORCE_DB_BRANCH || "main";
|
|
1418
|
+
const start = Date.now();
|
|
1419
|
+
console.log(
|
|
1420
|
+
`[fetchSyncedTables] \u2192 GET listTableView (dbBranch=${dbBranch}) appId=${appId ? "set" : "unset"} workspace=${workspace ? "set" : "unset"}`
|
|
1421
|
+
);
|
|
1422
|
+
const response = await Promise.race([
|
|
1423
|
+
client.get(
|
|
1424
|
+
`/api/v1/dataloom/inner/app/${appId}/workspaces/${workspace}/listTableView`,
|
|
1425
|
+
{ params: { dbBranch }, headers: { "x-supaas-bizsource": "miaoda" } }
|
|
1426
|
+
),
|
|
1427
|
+
timeoutPromise
|
|
1428
|
+
]);
|
|
1429
|
+
console.log(
|
|
1430
|
+
`[fetchSyncedTables] \u2190 listTableView response: ${response.status} ${response.statusText} (${Date.now() - start}ms) with logId=${response.headers.get("x-tt-logid")}`
|
|
1431
|
+
);
|
|
1432
|
+
if (!response.ok) {
|
|
1433
|
+
throw new Error(
|
|
1434
|
+
`listTableView API failed: ${response.status} ${response.statusText}`
|
|
1435
|
+
);
|
|
1436
|
+
}
|
|
1437
|
+
let json;
|
|
1438
|
+
try {
|
|
1439
|
+
json = await response.json();
|
|
1440
|
+
} catch (error) {
|
|
1441
|
+
console.warn(
|
|
1442
|
+
"[fetchSyncedTables] \u26A0 Failed to parse listTableView response JSON, returning empty map:",
|
|
1443
|
+
error instanceof Error ? error.message : String(error)
|
|
1444
|
+
);
|
|
1445
|
+
return /* @__PURE__ */ new Map();
|
|
1446
|
+
}
|
|
1447
|
+
const tableView = json?.data?.data;
|
|
1448
|
+
if (!tableView) {
|
|
1449
|
+
console.warn(
|
|
1450
|
+
"[fetchSyncedTables] \u26A0 listTableView response missing data.data, returning empty map"
|
|
1451
|
+
);
|
|
1452
|
+
return /* @__PURE__ */ new Map();
|
|
1453
|
+
}
|
|
1454
|
+
const syncedMap = extractSyncedTableMap(tableView);
|
|
1455
|
+
const totalCount = (tableView.table?.data?.length ?? 0) + (tableView.view?.data?.length ?? 0) + (tableView.materializedView?.data?.length ?? 0);
|
|
1456
|
+
console.log(
|
|
1457
|
+
`[fetchSyncedTables] \u2713 Extracted synced tables: ${syncedMap.size}/${totalCount} (elapsed ${Date.now() - start}ms)`
|
|
1458
|
+
);
|
|
1459
|
+
return syncedMap;
|
|
1460
|
+
} catch (error) {
|
|
1461
|
+
console.error(
|
|
1462
|
+
"[fetchSyncedTables] \u274C Error fetching synced tables:",
|
|
1463
|
+
error
|
|
1464
|
+
);
|
|
1465
|
+
return /* @__PURE__ */ new Map();
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
function extractSyncedTableMap(tableView) {
|
|
1469
|
+
const syncedMap = /* @__PURE__ */ new Map();
|
|
1470
|
+
const allTables = [
|
|
1471
|
+
...tableView.table?.data ?? [],
|
|
1472
|
+
...tableView.view?.data ?? [],
|
|
1473
|
+
...tableView.materializedView?.data ?? []
|
|
1474
|
+
];
|
|
1475
|
+
for (const table of allTables) {
|
|
1476
|
+
if (table.bitableSyncTask && table.bitableSyncTask.fieldApiNameList?.length > 0) {
|
|
1477
|
+
syncedMap.set(
|
|
1478
|
+
table.tableName,
|
|
1479
|
+
new Set(table.bitableSyncTask.fieldApiNameList)
|
|
1480
|
+
);
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
return syncedMap;
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1129
1486
|
// src/commands/db/gen-nest-resource/generator.ts
|
|
1130
1487
|
import { pluralize } from "inflection";
|
|
1131
1488
|
|
|
@@ -1804,7 +2161,9 @@ async function run(options = {}) {
|
|
|
1804
2161
|
}
|
|
1805
2162
|
const databaseUrl = process.env.SUDA_DATABASE_URL;
|
|
1806
2163
|
if (!databaseUrl) {
|
|
1807
|
-
console.error(
|
|
2164
|
+
console.error(
|
|
2165
|
+
"[gen-db-schema] Error: SUDA_DATABASE_URL environment variable is required"
|
|
2166
|
+
);
|
|
1808
2167
|
process.exit(1);
|
|
1809
2168
|
}
|
|
1810
2169
|
const outputPath = options.output || process.env.DB_SCHEMA_OUTPUT || "server/database/schema.ts";
|
|
@@ -1819,9 +2178,14 @@ async function run(options = {}) {
|
|
|
1819
2178
|
path2.resolve(__dirname2, "../../../dist/config/drizzle.config.js")
|
|
1820
2179
|
];
|
|
1821
2180
|
const configPath = configPathCandidates.find((p) => fs4.existsSync(p));
|
|
1822
|
-
console.log(
|
|
2181
|
+
console.log(
|
|
2182
|
+
"[gen-db-schema] Using drizzle config from:",
|
|
2183
|
+
configPath ?? "(not found)"
|
|
2184
|
+
);
|
|
1823
2185
|
if (!configPath) {
|
|
1824
|
-
console.error(
|
|
2186
|
+
console.error(
|
|
2187
|
+
"[gen-db-schema] Error: drizzle config not found in CLI package"
|
|
2188
|
+
);
|
|
1825
2189
|
process.exit(1);
|
|
1826
2190
|
}
|
|
1827
2191
|
const resolveDrizzleKitBin = () => {
|
|
@@ -1837,7 +2201,9 @@ async function run(options = {}) {
|
|
|
1837
2201
|
const binField = pkgJson.bin;
|
|
1838
2202
|
const binRelPath = typeof binField === "string" ? binField : binField?.["drizzle-kit"];
|
|
1839
2203
|
if (!binRelPath) {
|
|
1840
|
-
throw new Error(
|
|
2204
|
+
throw new Error(
|
|
2205
|
+
"Unable to resolve drizzle-kit binary from package.json"
|
|
2206
|
+
);
|
|
1841
2207
|
}
|
|
1842
2208
|
return path2.resolve(currentDir, binRelPath);
|
|
1843
2209
|
}
|
|
@@ -1847,6 +2213,67 @@ async function run(options = {}) {
|
|
|
1847
2213
|
}
|
|
1848
2214
|
throw new Error("Unable to locate drizzle-kit package root");
|
|
1849
2215
|
};
|
|
2216
|
+
let columnComments;
|
|
2217
|
+
let syncedTableMap;
|
|
2218
|
+
const appId = process.env.app_id;
|
|
2219
|
+
const workspace = process.env.suda_workspace_id;
|
|
2220
|
+
console.log(
|
|
2221
|
+
`[gen-db-schema] Pre-fetch info: columnComments=enabled, syncedTables=${appId && workspace ? "enabled" : "skipped"} (app_id=${appId ? "set" : "unset"}, suda_workspace_id=${workspace ? "set" : "unset"})`
|
|
2222
|
+
);
|
|
2223
|
+
const columnCommentsTask = (async () => {
|
|
2224
|
+
const start = Date.now();
|
|
2225
|
+
console.log("[gen-db-schema] \u2192 Fetching column comments...");
|
|
2226
|
+
const res = await fetchColumnComments(databaseUrl, { timeoutMs: 1e4 });
|
|
2227
|
+
console.log(
|
|
2228
|
+
`[gen-db-schema] \u2190 Fetched column comments: ${res.size} items (${Date.now() - start}ms)`
|
|
2229
|
+
);
|
|
2230
|
+
return res;
|
|
2231
|
+
})();
|
|
2232
|
+
const syncedTablesTask = appId && workspace ? (async () => {
|
|
2233
|
+
const start = Date.now();
|
|
2234
|
+
console.log(
|
|
2235
|
+
"[gen-db-schema] \u2192 Fetching synced tables from listTableView..."
|
|
2236
|
+
);
|
|
2237
|
+
const res = await fetchSyncedTables(appId, workspace);
|
|
2238
|
+
console.log(
|
|
2239
|
+
`[gen-db-schema] \u2190 Fetched synced tables: ${res.size} tables (${Date.now() - start}ms)`
|
|
2240
|
+
);
|
|
2241
|
+
return res;
|
|
2242
|
+
})() : void 0;
|
|
2243
|
+
const fetchTasks = await Promise.allSettled([
|
|
2244
|
+
columnCommentsTask,
|
|
2245
|
+
...syncedTablesTask ? [syncedTablesTask] : []
|
|
2246
|
+
]);
|
|
2247
|
+
if (fetchTasks[0].status === "fulfilled") {
|
|
2248
|
+
columnComments = fetchTasks[0].value;
|
|
2249
|
+
console.log(
|
|
2250
|
+
`[gen-db-schema] \u2713 Column comments ready: ${columnComments.size}`
|
|
2251
|
+
);
|
|
2252
|
+
} else {
|
|
2253
|
+
console.warn(
|
|
2254
|
+
"[gen-db-schema] \u26A0 Failed to fetch column comments (skipping):",
|
|
2255
|
+
fetchTasks[0].reason instanceof Error ? fetchTasks[0].reason.message : String(fetchTasks[0].reason)
|
|
2256
|
+
);
|
|
2257
|
+
}
|
|
2258
|
+
if (appId && workspace) {
|
|
2259
|
+
if (fetchTasks[1]?.status === "fulfilled") {
|
|
2260
|
+
syncedTableMap = fetchTasks[1].value;
|
|
2261
|
+
console.log(
|
|
2262
|
+
`[gen-db-schema] \u2713 Synced tables ready: ${syncedTableMap.size}`
|
|
2263
|
+
);
|
|
2264
|
+
} else if (fetchTasks[1]?.status === "rejected") {
|
|
2265
|
+
console.warn(
|
|
2266
|
+
"[gen-db-schema] \u26A0 Failed to fetch synced tables (skipping):",
|
|
2267
|
+
fetchTasks[1].reason instanceof Error ? fetchTasks[1].reason.message : String(fetchTasks[1].reason)
|
|
2268
|
+
);
|
|
2269
|
+
syncedTableMap = /* @__PURE__ */ new Map();
|
|
2270
|
+
}
|
|
2271
|
+
} else {
|
|
2272
|
+
console.info(
|
|
2273
|
+
"[gen-db-schema] \u2139 Skipping synced table detection (app_id or suda_workspace_id not set)"
|
|
2274
|
+
);
|
|
2275
|
+
syncedTableMap = /* @__PURE__ */ new Map();
|
|
2276
|
+
}
|
|
1850
2277
|
try {
|
|
1851
2278
|
const env = {
|
|
1852
2279
|
...process.env,
|
|
@@ -1857,22 +2284,34 @@ async function run(options = {}) {
|
|
|
1857
2284
|
};
|
|
1858
2285
|
const drizzleKitBin = resolveDrizzleKitBin();
|
|
1859
2286
|
const spawnArgs = [drizzleKitBin, "introspect", "--config", configPath];
|
|
1860
|
-
const result = spawnSync(process.execPath, spawnArgs, {
|
|
2287
|
+
const result = spawnSync(process.execPath, spawnArgs, {
|
|
2288
|
+
stdio: "inherit",
|
|
2289
|
+
env,
|
|
2290
|
+
cwd: process.cwd()
|
|
2291
|
+
});
|
|
1861
2292
|
if (result.error) {
|
|
1862
2293
|
console.error("[gen-db-schema] Execution failed:", result.error);
|
|
1863
2294
|
throw result.error;
|
|
1864
2295
|
}
|
|
1865
2296
|
if ((result.status ?? 0) !== 0) {
|
|
1866
|
-
throw new Error(
|
|
2297
|
+
throw new Error(
|
|
2298
|
+
`drizzle-kit introspect failed with status ${result.status}`
|
|
2299
|
+
);
|
|
1867
2300
|
}
|
|
1868
2301
|
const generatedSchema = path2.join(OUT_DIR, "schema.ts");
|
|
1869
2302
|
if (!fs4.existsSync(generatedSchema)) {
|
|
1870
2303
|
console.error("[gen-db-schema] schema.ts not generated");
|
|
1871
2304
|
throw new Error("drizzle-kit introspect failed to generate schema.ts");
|
|
1872
2305
|
}
|
|
1873
|
-
const stats = postprocessDrizzleSchema(generatedSchema
|
|
2306
|
+
const stats = await postprocessDrizzleSchema(generatedSchema, {
|
|
2307
|
+
columnComments,
|
|
2308
|
+
syncedTableMap
|
|
2309
|
+
});
|
|
1874
2310
|
if (stats?.unmatchedUnknown?.length) {
|
|
1875
|
-
console.warn(
|
|
2311
|
+
console.warn(
|
|
2312
|
+
"[gen-db-schema] Unmatched custom types detected:",
|
|
2313
|
+
stats.unmatchedUnknown
|
|
2314
|
+
);
|
|
1876
2315
|
}
|
|
1877
2316
|
console.log("[gen-db-schema] \u2713 Postprocessed schema");
|
|
1878
2317
|
fs4.mkdirSync(path2.dirname(SCHEMA_FILE), { recursive: true });
|
|
@@ -1887,14 +2326,22 @@ async function run(options = {}) {
|
|
|
1887
2326
|
schemaFilePath,
|
|
1888
2327
|
moduleOutputDir: path2.resolve(process.cwd(), "server/modules")
|
|
1889
2328
|
});
|
|
1890
|
-
console.log(
|
|
2329
|
+
console.log(
|
|
2330
|
+
"[gen-db-schema] \u2713 Generate NestJS Module Boilerplate Successfully"
|
|
2331
|
+
);
|
|
1891
2332
|
}
|
|
1892
2333
|
} catch (error) {
|
|
1893
|
-
console.warn(
|
|
2334
|
+
console.warn(
|
|
2335
|
+
"[gen-db-schema] Generate NestJS Module Boilerplate failed:",
|
|
2336
|
+
error instanceof Error ? error.message : String(error)
|
|
2337
|
+
);
|
|
1894
2338
|
}
|
|
1895
2339
|
console.log("[gen-db-schema] \u2713 Complete");
|
|
1896
2340
|
} catch (err) {
|
|
1897
|
-
console.error(
|
|
2341
|
+
console.error(
|
|
2342
|
+
"[gen-db-schema] Failed:",
|
|
2343
|
+
err instanceof Error ? err.message : String(err)
|
|
2344
|
+
);
|
|
1898
2345
|
exitCode = 1;
|
|
1899
2346
|
} finally {
|
|
1900
2347
|
if (fs4.existsSync(OUT_DIR)) {
|
|
@@ -1931,12 +2378,15 @@ var syncConfig = {
|
|
|
1931
2378
|
type: "directory",
|
|
1932
2379
|
overwrite: true
|
|
1933
2380
|
},
|
|
1934
|
-
// 2.
|
|
2381
|
+
// 2. 智能合并 nest-cli.json 配置(保留用户自定义的 assets、plugins 等)
|
|
1935
2382
|
{
|
|
1936
2383
|
from: "templates/nest-cli.json",
|
|
1937
2384
|
to: "nest-cli.json",
|
|
1938
|
-
type: "
|
|
1939
|
-
|
|
2385
|
+
type: "merge-json",
|
|
2386
|
+
arrayMerge: {
|
|
2387
|
+
"compilerOptions.assets": { key: "include" },
|
|
2388
|
+
"compilerOptions.plugins": { key: "name" }
|
|
2389
|
+
}
|
|
1940
2390
|
},
|
|
1941
2391
|
// // 2. 追加内容到 .gitignore
|
|
1942
2392
|
// {
|
|
@@ -1960,6 +2410,34 @@ var syncConfig = {
|
|
|
1960
2410
|
type: "remove-line",
|
|
1961
2411
|
to: ".gitignore",
|
|
1962
2412
|
pattern: "package-lock.json"
|
|
2413
|
+
},
|
|
2414
|
+
// 5. 注册 postinstall 脚本,自动恢复 action plugins
|
|
2415
|
+
{
|
|
2416
|
+
type: "add-script",
|
|
2417
|
+
name: "postinstall",
|
|
2418
|
+
command: "fullstack-cli action-plugin init",
|
|
2419
|
+
overwrite: false
|
|
2420
|
+
},
|
|
2421
|
+
// 6. 替换 drizzle.config.ts(仅当文件存在时)
|
|
2422
|
+
{
|
|
2423
|
+
from: "templates/drizzle.config.ts",
|
|
2424
|
+
to: "drizzle.config.ts",
|
|
2425
|
+
type: "file",
|
|
2426
|
+
overwrite: true,
|
|
2427
|
+
onlyIfExists: true
|
|
2428
|
+
},
|
|
2429
|
+
// 7. 确保 .gitignore 包含 .agent/ 目录
|
|
2430
|
+
{
|
|
2431
|
+
type: "add-line",
|
|
2432
|
+
to: ".gitignore",
|
|
2433
|
+
line: ".agent/"
|
|
2434
|
+
},
|
|
2435
|
+
// 8. 同步 .spark_project 配置文件(总是覆盖)
|
|
2436
|
+
{
|
|
2437
|
+
from: "templates/.spark_project",
|
|
2438
|
+
to: ".spark_project",
|
|
2439
|
+
type: "file",
|
|
2440
|
+
overwrite: true
|
|
1963
2441
|
}
|
|
1964
2442
|
],
|
|
1965
2443
|
// 文件权限设置
|
|
@@ -2001,6 +2479,52 @@ function removeLineFromFile(filePath, pattern) {
|
|
|
2001
2479
|
return true;
|
|
2002
2480
|
}
|
|
2003
2481
|
|
|
2482
|
+
// src/utils/merge-json.ts
|
|
2483
|
+
function isPlainObject(value) {
|
|
2484
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2485
|
+
}
|
|
2486
|
+
function mergeArrayByKey(userArr, templateArr, key) {
|
|
2487
|
+
const result = [...userArr];
|
|
2488
|
+
for (const templateItem of templateArr) {
|
|
2489
|
+
if (!isPlainObject(templateItem)) continue;
|
|
2490
|
+
const templateKey = templateItem[key];
|
|
2491
|
+
const existingIndex = result.findIndex(
|
|
2492
|
+
(item) => isPlainObject(item) && item[key] === templateKey
|
|
2493
|
+
);
|
|
2494
|
+
if (existingIndex >= 0) {
|
|
2495
|
+
result[existingIndex] = templateItem;
|
|
2496
|
+
} else {
|
|
2497
|
+
result.push(templateItem);
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
return result;
|
|
2501
|
+
}
|
|
2502
|
+
function deepMergeJson(user, template, arrayMerge = {}, currentPath = "") {
|
|
2503
|
+
const result = { ...user };
|
|
2504
|
+
for (const key of Object.keys(template)) {
|
|
2505
|
+
const fullPath = currentPath ? `${currentPath}.${key}` : key;
|
|
2506
|
+
const templateValue = template[key];
|
|
2507
|
+
const userValue = user[key];
|
|
2508
|
+
if (Array.isArray(templateValue)) {
|
|
2509
|
+
const mergeConfig = arrayMerge[fullPath];
|
|
2510
|
+
if (mergeConfig && Array.isArray(userValue)) {
|
|
2511
|
+
result[key] = mergeArrayByKey(userValue, templateValue, mergeConfig.key);
|
|
2512
|
+
} else {
|
|
2513
|
+
result[key] = templateValue;
|
|
2514
|
+
}
|
|
2515
|
+
} else if (isPlainObject(templateValue)) {
|
|
2516
|
+
if (isPlainObject(userValue)) {
|
|
2517
|
+
result[key] = deepMergeJson(userValue, templateValue, arrayMerge, fullPath);
|
|
2518
|
+
} else {
|
|
2519
|
+
result[key] = templateValue;
|
|
2520
|
+
}
|
|
2521
|
+
} else {
|
|
2522
|
+
result[key] = templateValue;
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
return result;
|
|
2526
|
+
}
|
|
2527
|
+
|
|
2004
2528
|
// src/commands/sync/run.handler.ts
|
|
2005
2529
|
async function run2(options) {
|
|
2006
2530
|
const userProjectRoot = process.env.INIT_CWD || process.cwd();
|
|
@@ -2053,6 +2577,22 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
|
|
|
2053
2577
|
removeLineFromFile(destPath2, rule.pattern);
|
|
2054
2578
|
return;
|
|
2055
2579
|
}
|
|
2580
|
+
if (rule.type === "add-script") {
|
|
2581
|
+
const packageJsonPath = path4.join(userProjectRoot, "package.json");
|
|
2582
|
+
addScript(packageJsonPath, rule.name, rule.command, rule.overwrite ?? false);
|
|
2583
|
+
return;
|
|
2584
|
+
}
|
|
2585
|
+
if (rule.type === "add-line") {
|
|
2586
|
+
const destPath2 = path4.join(userProjectRoot, rule.to);
|
|
2587
|
+
addLineToFile(destPath2, rule.line);
|
|
2588
|
+
return;
|
|
2589
|
+
}
|
|
2590
|
+
if (rule.type === "merge-json") {
|
|
2591
|
+
const srcPath2 = path4.join(pluginRoot, rule.from);
|
|
2592
|
+
const destPath2 = path4.join(userProjectRoot, rule.to);
|
|
2593
|
+
mergeJsonFile(srcPath2, destPath2, rule.arrayMerge);
|
|
2594
|
+
return;
|
|
2595
|
+
}
|
|
2056
2596
|
if (!("from" in rule)) {
|
|
2057
2597
|
return;
|
|
2058
2598
|
}
|
|
@@ -2067,14 +2607,18 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
|
|
|
2067
2607
|
syncDirectory(srcPath, destPath, rule.overwrite ?? true);
|
|
2068
2608
|
break;
|
|
2069
2609
|
case "file":
|
|
2070
|
-
syncFile(srcPath, destPath, rule.overwrite ?? true);
|
|
2610
|
+
syncFile(srcPath, destPath, rule.overwrite ?? true, rule.onlyIfExists ?? false);
|
|
2071
2611
|
break;
|
|
2072
2612
|
case "append":
|
|
2073
2613
|
appendToFile(srcPath, destPath);
|
|
2074
2614
|
break;
|
|
2075
2615
|
}
|
|
2076
2616
|
}
|
|
2077
|
-
function syncFile(src, dest, overwrite = true) {
|
|
2617
|
+
function syncFile(src, dest, overwrite = true, onlyIfExists = false) {
|
|
2618
|
+
if (onlyIfExists && !fs6.existsSync(dest)) {
|
|
2619
|
+
console.log(`[fullstack-cli] \u25CB ${path4.basename(dest)} (skipped, target not exists)`);
|
|
2620
|
+
return;
|
|
2621
|
+
}
|
|
2078
2622
|
const destDir = path4.dirname(dest);
|
|
2079
2623
|
if (!fs6.existsSync(destDir)) {
|
|
2080
2624
|
fs6.mkdirSync(destDir, { recursive: true });
|
|
@@ -2155,6 +2699,69 @@ function deleteDirectory(dirPath) {
|
|
|
2155
2699
|
console.log(`[fullstack-cli] \u25CB ${path4.basename(dirPath)} (not found)`);
|
|
2156
2700
|
}
|
|
2157
2701
|
}
|
|
2702
|
+
function addScript(packageJsonPath, name, command, overwrite) {
|
|
2703
|
+
if (!fs6.existsSync(packageJsonPath)) {
|
|
2704
|
+
console.log(`[fullstack-cli] \u25CB package.json (not found)`);
|
|
2705
|
+
return;
|
|
2706
|
+
}
|
|
2707
|
+
const content = fs6.readFileSync(packageJsonPath, "utf-8");
|
|
2708
|
+
const pkg2 = JSON.parse(content);
|
|
2709
|
+
if (!pkg2.scripts) {
|
|
2710
|
+
pkg2.scripts = {};
|
|
2711
|
+
}
|
|
2712
|
+
if (pkg2.scripts[name]) {
|
|
2713
|
+
if (!overwrite) {
|
|
2714
|
+
console.log(`[fullstack-cli] \u25CB scripts.${name} (already exists)`);
|
|
2715
|
+
return;
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2718
|
+
pkg2.scripts[name] = command;
|
|
2719
|
+
fs6.writeFileSync(packageJsonPath, JSON.stringify(pkg2, null, 2) + "\n");
|
|
2720
|
+
console.log(`[fullstack-cli] \u2713 scripts.${name}`);
|
|
2721
|
+
}
|
|
2722
|
+
function addLineToFile(filePath, line) {
|
|
2723
|
+
const fileName = path4.basename(filePath);
|
|
2724
|
+
if (!fs6.existsSync(filePath)) {
|
|
2725
|
+
console.log(`[fullstack-cli] \u25CB ${fileName} (not found, skipped)`);
|
|
2726
|
+
return;
|
|
2727
|
+
}
|
|
2728
|
+
const content = fs6.readFileSync(filePath, "utf-8");
|
|
2729
|
+
const lines = content.split("\n").map((l) => l.trim());
|
|
2730
|
+
if (lines.includes(line)) {
|
|
2731
|
+
console.log(`[fullstack-cli] \u25CB ${fileName} (line already exists: ${line})`);
|
|
2732
|
+
return;
|
|
2733
|
+
}
|
|
2734
|
+
const appendContent = (content.endsWith("\n") ? "" : "\n") + line + "\n";
|
|
2735
|
+
fs6.appendFileSync(filePath, appendContent);
|
|
2736
|
+
console.log(`[fullstack-cli] \u2713 ${fileName} (added: ${line})`);
|
|
2737
|
+
}
|
|
2738
|
+
function mergeJsonFile(src, dest, arrayMerge) {
|
|
2739
|
+
const fileName = path4.basename(dest);
|
|
2740
|
+
if (!fs6.existsSync(src)) {
|
|
2741
|
+
console.warn(`[fullstack-cli] Source not found: ${src}`);
|
|
2742
|
+
return;
|
|
2743
|
+
}
|
|
2744
|
+
const templateContent = JSON.parse(fs6.readFileSync(src, "utf-8"));
|
|
2745
|
+
if (!fs6.existsSync(dest)) {
|
|
2746
|
+
const destDir = path4.dirname(dest);
|
|
2747
|
+
if (!fs6.existsSync(destDir)) {
|
|
2748
|
+
fs6.mkdirSync(destDir, { recursive: true });
|
|
2749
|
+
}
|
|
2750
|
+
fs6.writeFileSync(dest, JSON.stringify(templateContent, null, 2) + "\n");
|
|
2751
|
+
console.log(`[fullstack-cli] \u2713 ${fileName} (created)`);
|
|
2752
|
+
return;
|
|
2753
|
+
}
|
|
2754
|
+
const userContent = JSON.parse(fs6.readFileSync(dest, "utf-8"));
|
|
2755
|
+
const merged = deepMergeJson(userContent, templateContent, arrayMerge ?? {});
|
|
2756
|
+
const userStr = JSON.stringify(userContent, null, 2);
|
|
2757
|
+
const mergedStr = JSON.stringify(merged, null, 2);
|
|
2758
|
+
if (userStr === mergedStr) {
|
|
2759
|
+
console.log(`[fullstack-cli] \u25CB ${fileName} (already up to date)`);
|
|
2760
|
+
return;
|
|
2761
|
+
}
|
|
2762
|
+
fs6.writeFileSync(dest, mergedStr + "\n");
|
|
2763
|
+
console.log(`[fullstack-cli] \u2713 ${fileName} (merged)`);
|
|
2764
|
+
}
|
|
2158
2765
|
|
|
2159
2766
|
// src/commands/sync/index.ts
|
|
2160
2767
|
var syncCommand = {
|
|
@@ -2167,55 +2774,443 @@ var syncCommand = {
|
|
|
2167
2774
|
}
|
|
2168
2775
|
};
|
|
2169
2776
|
|
|
2170
|
-
// src/
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2777
|
+
// src/utils/telemetry.ts
|
|
2778
|
+
async function reportEvents(events) {
|
|
2779
|
+
if (events.length === 0) {
|
|
2780
|
+
return true;
|
|
2781
|
+
}
|
|
2782
|
+
try {
|
|
2783
|
+
const client = getHttpClient();
|
|
2784
|
+
const response = await client.post("/api/v1/studio/innerapi/resource_events", { events });
|
|
2785
|
+
if (!response.ok) {
|
|
2786
|
+
console.warn(`[telemetry] Failed to report events: ${response.status} ${response.statusText}`);
|
|
2787
|
+
return false;
|
|
2788
|
+
}
|
|
2789
|
+
const result = await response.json();
|
|
2790
|
+
if (result.status_code !== "0") {
|
|
2791
|
+
console.warn(`[telemetry] API error: ${result.message}`);
|
|
2792
|
+
return false;
|
|
2793
|
+
}
|
|
2794
|
+
return true;
|
|
2795
|
+
} catch (error) {
|
|
2796
|
+
console.warn(`[telemetry] Failed to report events: ${error instanceof Error ? error.message : error}`);
|
|
2797
|
+
return false;
|
|
2180
2798
|
}
|
|
2181
|
-
return {
|
|
2182
|
-
name: match[1],
|
|
2183
|
-
version: match[2] || "latest"
|
|
2184
|
-
};
|
|
2185
|
-
}
|
|
2186
|
-
function getProjectRoot() {
|
|
2187
|
-
return process.cwd();
|
|
2188
2799
|
}
|
|
2189
|
-
function
|
|
2190
|
-
|
|
2800
|
+
async function reportInstallEvent(pluginKey, version) {
|
|
2801
|
+
await reportEvents([
|
|
2802
|
+
{
|
|
2803
|
+
resourceType: "plugin",
|
|
2804
|
+
resourceKey: pluginKey,
|
|
2805
|
+
eventType: "install",
|
|
2806
|
+
details: { version }
|
|
2807
|
+
}
|
|
2808
|
+
]);
|
|
2191
2809
|
}
|
|
2192
|
-
function
|
|
2193
|
-
|
|
2810
|
+
async function reportCreateInstanceEvent(pluginKey, version) {
|
|
2811
|
+
await reportEvents([
|
|
2812
|
+
{
|
|
2813
|
+
resourceType: "plugin",
|
|
2814
|
+
resourceKey: pluginKey,
|
|
2815
|
+
eventType: "create_instance",
|
|
2816
|
+
details: { version }
|
|
2817
|
+
}
|
|
2818
|
+
]);
|
|
2194
2819
|
}
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2820
|
+
|
|
2821
|
+
// src/utils/git.ts
|
|
2822
|
+
import { execSync, spawnSync as spawnSync2 } from "child_process";
|
|
2823
|
+
import fs7 from "fs";
|
|
2824
|
+
import path5 from "path";
|
|
2825
|
+
function isGitRepository(cwd = process.cwd()) {
|
|
2200
2826
|
try {
|
|
2201
|
-
const
|
|
2202
|
-
|
|
2827
|
+
const gitDir = path5.join(cwd, ".git");
|
|
2828
|
+
if (fs7.existsSync(gitDir)) {
|
|
2829
|
+
return true;
|
|
2830
|
+
}
|
|
2831
|
+
const result = spawnSync2("git", ["rev-parse", "--git-dir"], {
|
|
2832
|
+
cwd,
|
|
2833
|
+
stdio: "pipe",
|
|
2834
|
+
encoding: "utf-8"
|
|
2835
|
+
});
|
|
2836
|
+
return result.status === 0;
|
|
2203
2837
|
} catch {
|
|
2204
|
-
|
|
2838
|
+
return false;
|
|
2205
2839
|
}
|
|
2206
2840
|
}
|
|
2207
|
-
function
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2841
|
+
function getChangedFiles(cwd = process.cwd()) {
|
|
2842
|
+
try {
|
|
2843
|
+
const output = execSync("git status --porcelain", {
|
|
2844
|
+
cwd,
|
|
2845
|
+
stdio: "pipe",
|
|
2846
|
+
encoding: "utf-8"
|
|
2847
|
+
});
|
|
2848
|
+
return output.split("\n").filter((line) => line.trim()).map((line) => line.substring(3));
|
|
2849
|
+
} catch (error) {
|
|
2850
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2851
|
+
throw new Error(`Failed to get changed files: ${message}`);
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
function gitAddUpgradeFiles(cwd = process.cwd(), filesToStage) {
|
|
2855
|
+
const filteredFiles = [];
|
|
2856
|
+
for (const filePath of filesToStage) {
|
|
2857
|
+
if (fs7.existsSync(path5.join(cwd, filePath))) {
|
|
2858
|
+
filteredFiles.push(filePath);
|
|
2859
|
+
continue;
|
|
2860
|
+
}
|
|
2861
|
+
const tracked = spawnSync2("git", ["ls-files", "--error-unmatch", "--", filePath], {
|
|
2862
|
+
cwd,
|
|
2863
|
+
stdio: "pipe",
|
|
2864
|
+
encoding: "utf-8"
|
|
2865
|
+
});
|
|
2866
|
+
if (tracked.status === 0) {
|
|
2867
|
+
filteredFiles.push(filePath);
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2870
|
+
if (filteredFiles.length === 0) {
|
|
2871
|
+
return;
|
|
2872
|
+
}
|
|
2873
|
+
const result = spawnSync2("git", ["add", "--", ...filteredFiles], {
|
|
2874
|
+
cwd,
|
|
2875
|
+
stdio: "pipe",
|
|
2876
|
+
encoding: "utf-8"
|
|
2877
|
+
});
|
|
2878
|
+
if (result.error || result.status !== 0) {
|
|
2879
|
+
const errorMsg = result.stderr || result.error?.message || "Unknown error";
|
|
2880
|
+
throw new Error(`git add failed: ${errorMsg}`);
|
|
2881
|
+
}
|
|
2882
|
+
}
|
|
2883
|
+
function hasStagedChanges(cwd = process.cwd()) {
|
|
2884
|
+
const result = spawnSync2("git", ["diff", "--cached", "--quiet"], {
|
|
2885
|
+
cwd,
|
|
2886
|
+
stdio: "pipe",
|
|
2887
|
+
encoding: "utf-8"
|
|
2888
|
+
});
|
|
2889
|
+
if (result.status === 0) {
|
|
2890
|
+
return false;
|
|
2891
|
+
}
|
|
2892
|
+
if (result.status === 1) {
|
|
2893
|
+
return true;
|
|
2894
|
+
}
|
|
2895
|
+
const errorMsg = result.stderr || result.error?.message || "Unknown error";
|
|
2896
|
+
throw new Error(`Failed to check staged changes: ${errorMsg}`);
|
|
2897
|
+
}
|
|
2898
|
+
function gitCommit(message, cwd = process.cwd()) {
|
|
2899
|
+
const result = spawnSync2("git", ["commit", "-m", message], {
|
|
2900
|
+
cwd,
|
|
2901
|
+
stdio: "pipe",
|
|
2902
|
+
encoding: "utf-8"
|
|
2903
|
+
});
|
|
2904
|
+
if (result.error || result.status !== 0) {
|
|
2905
|
+
const errorMsg = result.stderr || result.error?.message || "Unknown error";
|
|
2906
|
+
throw new Error(`git commit failed: ${errorMsg}`);
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
function autoCommitUpgradeChanges(version, cwd, filesToStage, commitMessage) {
|
|
2910
|
+
if (!isGitRepository(cwd)) {
|
|
2911
|
+
console.log("[fullstack-cli] \u26A0 Not a git repository, skipping auto-commit");
|
|
2912
|
+
return false;
|
|
2913
|
+
}
|
|
2914
|
+
const changedFiles = getChangedFiles(cwd);
|
|
2915
|
+
if (changedFiles.length === 0) {
|
|
2916
|
+
console.log("[fullstack-cli] No changes to commit");
|
|
2917
|
+
return false;
|
|
2918
|
+
}
|
|
2919
|
+
try {
|
|
2920
|
+
gitAddUpgradeFiles(cwd, filesToStage);
|
|
2921
|
+
if (!hasStagedChanges(cwd)) {
|
|
2922
|
+
console.log("[fullstack-cli] No upgrade changes to commit");
|
|
2923
|
+
return false;
|
|
2924
|
+
}
|
|
2925
|
+
const message = commitMessage || `chore(upgrade): auto-upgrade by fullstack-cli
|
|
2926
|
+
|
|
2927
|
+
- Sync template files
|
|
2928
|
+
- Cleanup package.json config
|
|
2929
|
+
- Upgrade @lark-apaas dependencies (if any)
|
|
2930
|
+
|
|
2931
|
+
Auto-committed by fullstack-cli`;
|
|
2932
|
+
gitCommit(message, cwd);
|
|
2933
|
+
console.log(`[fullstack-cli] \u2713 Auto-committed ${changedFiles.length} changed file(s)`);
|
|
2934
|
+
return true;
|
|
2935
|
+
} catch (error) {
|
|
2936
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2937
|
+
throw new Error(`Failed to auto-commit changes: ${message}`);
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2940
|
+
|
|
2941
|
+
// src/utils/package-json.ts
|
|
2942
|
+
import fs8 from "fs";
|
|
2943
|
+
import path6 from "path";
|
|
2944
|
+
function readPackageJson(cwd = process.cwd()) {
|
|
2945
|
+
const pkgPath = path6.join(cwd, "package.json");
|
|
2946
|
+
if (!fs8.existsSync(pkgPath)) {
|
|
2947
|
+
throw new Error(`package.json not found at ${pkgPath}`);
|
|
2948
|
+
}
|
|
2949
|
+
const content = fs8.readFileSync(pkgPath, "utf-8");
|
|
2950
|
+
return JSON.parse(content);
|
|
2951
|
+
}
|
|
2952
|
+
function writePackageJson(pkg2, cwd = process.cwd()) {
|
|
2953
|
+
const pkgPath = path6.join(cwd, "package.json");
|
|
2954
|
+
const content = JSON.stringify(pkg2, null, 2) + "\n";
|
|
2955
|
+
fs8.writeFileSync(pkgPath, content, "utf-8");
|
|
2956
|
+
}
|
|
2957
|
+
function removeUpgradeScript(pkg2) {
|
|
2958
|
+
if (!pkg2.scripts?.upgrade) {
|
|
2959
|
+
return false;
|
|
2960
|
+
}
|
|
2961
|
+
delete pkg2.scripts.upgrade;
|
|
2962
|
+
return true;
|
|
2963
|
+
}
|
|
2964
|
+
function cleanDevScript(pkg2) {
|
|
2965
|
+
if (!pkg2.scripts?.dev) {
|
|
2966
|
+
return false;
|
|
2967
|
+
}
|
|
2968
|
+
const originalDev = pkg2.scripts.dev;
|
|
2969
|
+
const cleanedDev = originalDev.replace(/npm\s+run\s+upgrade\s*&&\s*/g, "").replace(/npm\s+run\s+upgrade\s*$/g, "").trim();
|
|
2970
|
+
if (cleanedDev !== originalDev) {
|
|
2971
|
+
pkg2.scripts.dev = cleanedDev;
|
|
2972
|
+
return true;
|
|
2973
|
+
}
|
|
2974
|
+
return false;
|
|
2975
|
+
}
|
|
2976
|
+
function cleanupPackageJson(cwd = process.cwd()) {
|
|
2977
|
+
try {
|
|
2978
|
+
const pkg2 = readPackageJson(cwd);
|
|
2979
|
+
let changed = false;
|
|
2980
|
+
if (removeUpgradeScript(pkg2)) {
|
|
2981
|
+
console.log("[fullstack-cli] \u2713 Removed scripts.upgrade");
|
|
2982
|
+
changed = true;
|
|
2983
|
+
}
|
|
2984
|
+
if (cleanDevScript(pkg2)) {
|
|
2985
|
+
console.log("[fullstack-cli] \u2713 Cleaned scripts.dev (removed npm run upgrade)");
|
|
2986
|
+
changed = true;
|
|
2987
|
+
}
|
|
2988
|
+
if (changed) {
|
|
2989
|
+
writePackageJson(pkg2, cwd);
|
|
2990
|
+
}
|
|
2991
|
+
return changed;
|
|
2992
|
+
} catch (error) {
|
|
2993
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2994
|
+
console.log(`[fullstack-cli] \u26A0 Could not cleanup package.json: ${message}`);
|
|
2995
|
+
return false;
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
|
|
2999
|
+
// src/commands/upgrade/shared/utils.ts
|
|
3000
|
+
import path7 from "path";
|
|
3001
|
+
import fs9 from "fs";
|
|
3002
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
3003
|
+
function getCliVersion() {
|
|
3004
|
+
try {
|
|
3005
|
+
const __filename = fileURLToPath4(import.meta.url);
|
|
3006
|
+
const __dirname2 = path7.dirname(__filename);
|
|
3007
|
+
const pkgPath = path7.resolve(__dirname2, "../../../package.json");
|
|
3008
|
+
const pkgContent = fs9.readFileSync(pkgPath, "utf-8");
|
|
3009
|
+
const pkg2 = JSON.parse(pkgContent);
|
|
3010
|
+
return pkg2.version || "unknown";
|
|
3011
|
+
} catch {
|
|
3012
|
+
return "unknown";
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
3015
|
+
|
|
3016
|
+
// src/commands/upgrade/get-upgrade-files.ts
|
|
3017
|
+
function getUpgradeFilesToStage(disableGenOpenapi = true) {
|
|
3018
|
+
const syncConfig2 = genSyncConfig({ disableGenOpenapi });
|
|
3019
|
+
const filesToStage = /* @__PURE__ */ new Set();
|
|
3020
|
+
syncConfig2.sync.forEach((rule) => {
|
|
3021
|
+
if (rule.type === "file" || rule.type === "directory" || rule.type === "merge-json") {
|
|
3022
|
+
filesToStage.add(rule.to);
|
|
3023
|
+
} else if (rule.type === "remove-line" || rule.type === "add-line") {
|
|
3024
|
+
filesToStage.add(rule.to);
|
|
3025
|
+
} else if (rule.type === "add-script") {
|
|
3026
|
+
filesToStage.add("package.json");
|
|
3027
|
+
} else if (rule.type === "delete-file" || rule.type === "delete-directory") {
|
|
3028
|
+
filesToStage.add(rule.to);
|
|
3029
|
+
}
|
|
3030
|
+
});
|
|
3031
|
+
filesToStage.add("package.json");
|
|
3032
|
+
filesToStage.add("package-lock.json");
|
|
3033
|
+
return Array.from(filesToStage);
|
|
3034
|
+
}
|
|
3035
|
+
|
|
3036
|
+
// src/commands/upgrade/run.handler.ts
|
|
3037
|
+
async function run3(options = {}) {
|
|
3038
|
+
const userProjectRoot = process.env.INIT_CWD || process.cwd();
|
|
3039
|
+
console.log("[fullstack-cli] Starting upgrade...");
|
|
3040
|
+
try {
|
|
3041
|
+
console.log("[fullstack-cli] Step 1/3: Syncing template files...");
|
|
3042
|
+
await run2({ disableGenOpenapi: options.disableGenOpenapi ?? true });
|
|
3043
|
+
console.log("[fullstack-cli] Step 2/3: Cleaning up package.json...");
|
|
3044
|
+
const cleaned = cleanupPackageJson(userProjectRoot);
|
|
3045
|
+
if (!cleaned) {
|
|
3046
|
+
console.log("[fullstack-cli] \u25CB No cleanup needed");
|
|
3047
|
+
}
|
|
3048
|
+
const shouldCommit = options.commit ?? true;
|
|
3049
|
+
if (shouldCommit) {
|
|
3050
|
+
console.log("[fullstack-cli] Step 3/3: Committing changes...");
|
|
3051
|
+
const version = getCliVersion();
|
|
3052
|
+
const filesToStage = getUpgradeFilesToStage(options.disableGenOpenapi ?? true);
|
|
3053
|
+
autoCommitUpgradeChanges(version, userProjectRoot, filesToStage);
|
|
3054
|
+
} else {
|
|
3055
|
+
console.log("[fullstack-cli] Step 3/3: Skipping commit (--no-commit flag)");
|
|
3056
|
+
}
|
|
3057
|
+
console.log("[fullstack-cli] Upgrade completed successfully \u2705");
|
|
3058
|
+
} catch (error) {
|
|
3059
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3060
|
+
console.error("[fullstack-cli] Failed to upgrade:", message);
|
|
3061
|
+
process.exit(1);
|
|
3062
|
+
}
|
|
3063
|
+
}
|
|
3064
|
+
|
|
3065
|
+
// src/commands/upgrade/deps/run.handler.ts
|
|
3066
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
3067
|
+
function findLarkAapaasPackages(cwd, filterPackages) {
|
|
3068
|
+
const pkg2 = readPackageJson(cwd);
|
|
3069
|
+
const allPackages = /* @__PURE__ */ new Set();
|
|
3070
|
+
if (pkg2.dependencies) {
|
|
3071
|
+
Object.keys(pkg2.dependencies).forEach((name) => {
|
|
3072
|
+
if (name.startsWith("@lark-apaas/")) {
|
|
3073
|
+
allPackages.add(name);
|
|
3074
|
+
}
|
|
3075
|
+
});
|
|
3076
|
+
}
|
|
3077
|
+
if (pkg2.devDependencies) {
|
|
3078
|
+
Object.keys(pkg2.devDependencies).forEach((name) => {
|
|
3079
|
+
if (name.startsWith("@lark-apaas/")) {
|
|
3080
|
+
allPackages.add(name);
|
|
3081
|
+
}
|
|
3082
|
+
});
|
|
3083
|
+
}
|
|
3084
|
+
const packages = Array.from(allPackages);
|
|
3085
|
+
if (filterPackages) {
|
|
3086
|
+
const filter = new Set(filterPackages.split(",").map((p) => p.trim()));
|
|
3087
|
+
return packages.filter((p) => filter.has(p));
|
|
3088
|
+
}
|
|
3089
|
+
return packages;
|
|
3090
|
+
}
|
|
3091
|
+
function upgradePackages(packages, version, cwd) {
|
|
3092
|
+
if (version) {
|
|
3093
|
+
console.log(`[fullstack-cli] Upgrading to version ${version}...`);
|
|
3094
|
+
packages.forEach((pkg2) => {
|
|
3095
|
+
const target = `${pkg2}@${version}`;
|
|
3096
|
+
console.log(`[fullstack-cli] Installing ${target}...`);
|
|
3097
|
+
const result = spawnSync3("npm", ["install", target], {
|
|
3098
|
+
cwd,
|
|
3099
|
+
stdio: "inherit"
|
|
3100
|
+
});
|
|
3101
|
+
if (result.error || result.status !== 0) {
|
|
3102
|
+
throw new Error(`Failed to install ${target}`);
|
|
3103
|
+
}
|
|
3104
|
+
});
|
|
3105
|
+
} else {
|
|
3106
|
+
console.log("[fullstack-cli] Upgrading to latest compatible versions...");
|
|
3107
|
+
packages.forEach((pkg2) => {
|
|
3108
|
+
console.log(`[fullstack-cli] Updating ${pkg2}...`);
|
|
3109
|
+
const result = spawnSync3("npm", ["update", pkg2], {
|
|
3110
|
+
cwd,
|
|
3111
|
+
stdio: "inherit"
|
|
3112
|
+
});
|
|
3113
|
+
if (result.error || result.status !== 0) {
|
|
3114
|
+
throw new Error(`Failed to update ${pkg2}`);
|
|
3115
|
+
}
|
|
3116
|
+
});
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3119
|
+
async function run4(options = {}) {
|
|
3120
|
+
const cwd = process.env.INIT_CWD || process.cwd();
|
|
3121
|
+
console.log("[fullstack-cli] Starting dependencies upgrade...");
|
|
3122
|
+
try {
|
|
3123
|
+
console.log("[fullstack-cli] Step 1/2: Scanning @lark-apaas dependencies...");
|
|
3124
|
+
const packages = findLarkAapaasPackages(cwd, options.packages);
|
|
3125
|
+
if (packages.length === 0) {
|
|
3126
|
+
console.log("[fullstack-cli] No @lark-apaas packages found");
|
|
3127
|
+
return;
|
|
3128
|
+
}
|
|
3129
|
+
console.log(`[fullstack-cli] Found ${packages.length} @lark-apaas package(s):`);
|
|
3130
|
+
packages.forEach((p) => console.log(` - ${p}`));
|
|
3131
|
+
console.log("[fullstack-cli] Step 2/2: Upgrading packages...");
|
|
3132
|
+
upgradePackages(packages, options.version, cwd);
|
|
3133
|
+
console.log(`[fullstack-cli] \u2713 Successfully upgraded ${packages.length} package(s)`);
|
|
3134
|
+
console.log("[fullstack-cli] Dependencies upgrade completed successfully \u2705");
|
|
3135
|
+
} catch (error) {
|
|
3136
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3137
|
+
console.error("[fullstack-cli] Failed to upgrade dependencies:", message);
|
|
3138
|
+
process.exit(1);
|
|
3139
|
+
}
|
|
3140
|
+
}
|
|
3141
|
+
|
|
3142
|
+
// src/commands/upgrade/deps/index.ts
|
|
3143
|
+
var depsCommand = {
|
|
3144
|
+
name: "deps",
|
|
3145
|
+
description: "Upgrade @lark-apaas dependencies",
|
|
3146
|
+
register(parentCommand) {
|
|
3147
|
+
parentCommand.command(this.name).description(this.description).option("--version <version>", "Upgrade to specific version").option("--packages <packages>", "Only upgrade specific packages (comma-separated)").action(async (options) => {
|
|
3148
|
+
await run4(options);
|
|
3149
|
+
});
|
|
3150
|
+
}
|
|
3151
|
+
};
|
|
3152
|
+
|
|
3153
|
+
// src/commands/upgrade/index.ts
|
|
3154
|
+
var upgradeCommand = {
|
|
3155
|
+
name: "upgrade",
|
|
3156
|
+
description: "Upgrade template files and auto-commit changes",
|
|
3157
|
+
register(program) {
|
|
3158
|
+
const upgradeCmd = program.command(this.name).description(this.description).option("--disable-gen-openapi", "Disable generating openapi.ts").option("--no-commit", "Skip auto-commit changes").action(async (options) => {
|
|
3159
|
+
await run3(options);
|
|
3160
|
+
});
|
|
3161
|
+
depsCommand.register(upgradeCmd);
|
|
3162
|
+
}
|
|
3163
|
+
};
|
|
3164
|
+
|
|
3165
|
+
// src/commands/action-plugin/utils.ts
|
|
3166
|
+
import fs10 from "fs";
|
|
3167
|
+
import path8 from "path";
|
|
3168
|
+
import { spawnSync as spawnSync4, execSync as execSync2 } from "child_process";
|
|
3169
|
+
function parsePluginName(input) {
|
|
3170
|
+
const match = input.match(/^(@[^/]+\/[^@]+)(?:@(.+))?$/);
|
|
3171
|
+
if (!match) {
|
|
3172
|
+
throw new Error(
|
|
3173
|
+
`Invalid plugin name format: ${input}. Expected format: @scope/name or @scope/name@version`
|
|
3174
|
+
);
|
|
3175
|
+
}
|
|
3176
|
+
return {
|
|
3177
|
+
name: match[1],
|
|
3178
|
+
version: match[2] || "latest"
|
|
3179
|
+
};
|
|
3180
|
+
}
|
|
3181
|
+
function getProjectRoot() {
|
|
3182
|
+
return process.cwd();
|
|
3183
|
+
}
|
|
3184
|
+
function getPackageJsonPath() {
|
|
3185
|
+
return path8.join(getProjectRoot(), "package.json");
|
|
3186
|
+
}
|
|
3187
|
+
function getPluginPath(pluginName) {
|
|
3188
|
+
return path8.join(getProjectRoot(), "node_modules", pluginName);
|
|
3189
|
+
}
|
|
3190
|
+
function readPackageJson2() {
|
|
3191
|
+
const pkgPath = getPackageJsonPath();
|
|
3192
|
+
if (!fs10.existsSync(pkgPath)) {
|
|
3193
|
+
throw new Error("package.json not found in current directory");
|
|
3194
|
+
}
|
|
3195
|
+
try {
|
|
3196
|
+
const content = fs10.readFileSync(pkgPath, "utf-8");
|
|
3197
|
+
return JSON.parse(content);
|
|
3198
|
+
} catch {
|
|
3199
|
+
throw new Error("Failed to parse package.json");
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
function writePackageJson2(pkg2) {
|
|
3203
|
+
const pkgPath = getPackageJsonPath();
|
|
3204
|
+
fs10.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
|
|
3205
|
+
}
|
|
3206
|
+
function readActionPlugins() {
|
|
3207
|
+
const pkg2 = readPackageJson2();
|
|
3208
|
+
return pkg2.actionPlugins || {};
|
|
2214
3209
|
}
|
|
2215
3210
|
function writeActionPlugins(plugins) {
|
|
2216
|
-
const pkg2 =
|
|
3211
|
+
const pkg2 = readPackageJson2();
|
|
2217
3212
|
pkg2.actionPlugins = plugins;
|
|
2218
|
-
|
|
3213
|
+
writePackageJson2(pkg2);
|
|
2219
3214
|
}
|
|
2220
3215
|
function isPluginInstalled(pluginName) {
|
|
2221
3216
|
const plugins = readActionPlugins();
|
|
@@ -2227,7 +3222,7 @@ function getInstalledPluginVersion(pluginName) {
|
|
|
2227
3222
|
}
|
|
2228
3223
|
function npmInstall(tgzPath) {
|
|
2229
3224
|
console.log(`[action-plugin] Running npm install ${tgzPath}...`);
|
|
2230
|
-
const result =
|
|
3225
|
+
const result = spawnSync4("npm", ["install", tgzPath, "--no-save", "--no-package-lock", "--ignore-scripts"], {
|
|
2231
3226
|
cwd: getProjectRoot(),
|
|
2232
3227
|
stdio: "inherit"
|
|
2233
3228
|
});
|
|
@@ -2239,12 +3234,12 @@ function npmInstall(tgzPath) {
|
|
|
2239
3234
|
}
|
|
2240
3235
|
}
|
|
2241
3236
|
function getPackageVersion(pluginName) {
|
|
2242
|
-
const pkgJsonPath =
|
|
2243
|
-
if (!
|
|
3237
|
+
const pkgJsonPath = path8.join(getPluginPath(pluginName), "package.json");
|
|
3238
|
+
if (!fs10.existsSync(pkgJsonPath)) {
|
|
2244
3239
|
return null;
|
|
2245
3240
|
}
|
|
2246
3241
|
try {
|
|
2247
|
-
const content =
|
|
3242
|
+
const content = fs10.readFileSync(pkgJsonPath, "utf-8");
|
|
2248
3243
|
const pkg2 = JSON.parse(content);
|
|
2249
3244
|
return pkg2.version || null;
|
|
2250
3245
|
} catch {
|
|
@@ -2252,49 +3247,49 @@ function getPackageVersion(pluginName) {
|
|
|
2252
3247
|
}
|
|
2253
3248
|
}
|
|
2254
3249
|
function readPluginPackageJson(pluginPath) {
|
|
2255
|
-
const pkgJsonPath =
|
|
2256
|
-
if (!
|
|
3250
|
+
const pkgJsonPath = path8.join(pluginPath, "package.json");
|
|
3251
|
+
if (!fs10.existsSync(pkgJsonPath)) {
|
|
2257
3252
|
return null;
|
|
2258
3253
|
}
|
|
2259
3254
|
try {
|
|
2260
|
-
const content =
|
|
3255
|
+
const content = fs10.readFileSync(pkgJsonPath, "utf-8");
|
|
2261
3256
|
return JSON.parse(content);
|
|
2262
3257
|
} catch {
|
|
2263
3258
|
return null;
|
|
2264
3259
|
}
|
|
2265
3260
|
}
|
|
2266
3261
|
function extractTgzToNodeModules(tgzPath, pluginName) {
|
|
2267
|
-
const nodeModulesPath =
|
|
2268
|
-
const targetDir =
|
|
2269
|
-
const scopeDir =
|
|
2270
|
-
if (!
|
|
2271
|
-
|
|
3262
|
+
const nodeModulesPath = path8.join(getProjectRoot(), "node_modules");
|
|
3263
|
+
const targetDir = path8.join(nodeModulesPath, pluginName);
|
|
3264
|
+
const scopeDir = path8.dirname(targetDir);
|
|
3265
|
+
if (!fs10.existsSync(scopeDir)) {
|
|
3266
|
+
fs10.mkdirSync(scopeDir, { recursive: true });
|
|
2272
3267
|
}
|
|
2273
|
-
if (
|
|
2274
|
-
|
|
3268
|
+
if (fs10.existsSync(targetDir)) {
|
|
3269
|
+
fs10.rmSync(targetDir, { recursive: true });
|
|
2275
3270
|
}
|
|
2276
|
-
const tempDir =
|
|
2277
|
-
if (
|
|
2278
|
-
|
|
3271
|
+
const tempDir = path8.join(nodeModulesPath, ".cache", "fullstack-cli", "extract-temp");
|
|
3272
|
+
if (fs10.existsSync(tempDir)) {
|
|
3273
|
+
fs10.rmSync(tempDir, { recursive: true });
|
|
2279
3274
|
}
|
|
2280
|
-
|
|
3275
|
+
fs10.mkdirSync(tempDir, { recursive: true });
|
|
2281
3276
|
try {
|
|
2282
|
-
|
|
2283
|
-
const extractedDir =
|
|
2284
|
-
if (
|
|
2285
|
-
|
|
3277
|
+
execSync2(`tar -xzf "${tgzPath}" -C "${tempDir}"`, { stdio: "pipe" });
|
|
3278
|
+
const extractedDir = path8.join(tempDir, "package");
|
|
3279
|
+
if (fs10.existsSync(extractedDir)) {
|
|
3280
|
+
fs10.renameSync(extractedDir, targetDir);
|
|
2286
3281
|
} else {
|
|
2287
|
-
const files =
|
|
3282
|
+
const files = fs10.readdirSync(tempDir);
|
|
2288
3283
|
if (files.length === 1) {
|
|
2289
|
-
|
|
3284
|
+
fs10.renameSync(path8.join(tempDir, files[0]), targetDir);
|
|
2290
3285
|
} else {
|
|
2291
3286
|
throw new Error("Unexpected tgz structure");
|
|
2292
3287
|
}
|
|
2293
3288
|
}
|
|
2294
3289
|
return targetDir;
|
|
2295
3290
|
} finally {
|
|
2296
|
-
if (
|
|
2297
|
-
|
|
3291
|
+
if (fs10.existsSync(tempDir)) {
|
|
3292
|
+
fs10.rmSync(tempDir, { recursive: true });
|
|
2298
3293
|
}
|
|
2299
3294
|
}
|
|
2300
3295
|
}
|
|
@@ -2303,10 +3298,10 @@ function checkMissingPeerDeps(peerDeps) {
|
|
|
2303
3298
|
return [];
|
|
2304
3299
|
}
|
|
2305
3300
|
const missing = [];
|
|
2306
|
-
const nodeModulesPath =
|
|
3301
|
+
const nodeModulesPath = path8.join(getProjectRoot(), "node_modules");
|
|
2307
3302
|
for (const [depName, _version] of Object.entries(peerDeps)) {
|
|
2308
|
-
const depPath =
|
|
2309
|
-
if (!
|
|
3303
|
+
const depPath = path8.join(nodeModulesPath, depName);
|
|
3304
|
+
if (!fs10.existsSync(depPath)) {
|
|
2310
3305
|
missing.push(depName);
|
|
2311
3306
|
}
|
|
2312
3307
|
}
|
|
@@ -2317,7 +3312,7 @@ function installMissingDeps(deps) {
|
|
|
2317
3312
|
return;
|
|
2318
3313
|
}
|
|
2319
3314
|
console.log(`[action-plugin] Installing missing dependencies: ${deps.join(", ")}`);
|
|
2320
|
-
const result =
|
|
3315
|
+
const result = spawnSync4("npm", ["install", ...deps, "--no-save", "--no-package-lock"], {
|
|
2321
3316
|
cwd: getProjectRoot(),
|
|
2322
3317
|
stdio: "inherit"
|
|
2323
3318
|
});
|
|
@@ -2330,40 +3325,16 @@ function installMissingDeps(deps) {
|
|
|
2330
3325
|
}
|
|
2331
3326
|
function removePluginDirectory(pluginName) {
|
|
2332
3327
|
const pluginPath = getPluginPath(pluginName);
|
|
2333
|
-
if (
|
|
2334
|
-
|
|
3328
|
+
if (fs10.existsSync(pluginPath)) {
|
|
3329
|
+
fs10.rmSync(pluginPath, { recursive: true });
|
|
2335
3330
|
console.log(`[action-plugin] Removed ${pluginName}`);
|
|
2336
3331
|
}
|
|
2337
3332
|
}
|
|
2338
3333
|
|
|
2339
3334
|
// src/commands/action-plugin/api-client.ts
|
|
2340
3335
|
import { HttpClient as HttpClient2 } from "@lark-apaas/http-client";
|
|
2341
|
-
import
|
|
2342
|
-
import
|
|
2343
|
-
|
|
2344
|
-
// src/utils/http-client.ts
|
|
2345
|
-
import { HttpClient } from "@lark-apaas/http-client";
|
|
2346
|
-
var clientInstance = null;
|
|
2347
|
-
function getHttpClient() {
|
|
2348
|
-
if (!clientInstance) {
|
|
2349
|
-
clientInstance = new HttpClient({
|
|
2350
|
-
timeout: 3e4,
|
|
2351
|
-
platform: {
|
|
2352
|
-
enabled: true
|
|
2353
|
-
}
|
|
2354
|
-
});
|
|
2355
|
-
const canaryEnv = process.env.FORCE_FRAMEWORK_CLI_CANARY_ENV;
|
|
2356
|
-
if (canaryEnv) {
|
|
2357
|
-
clientInstance.interceptors.request.use((req) => {
|
|
2358
|
-
req.headers["x-tt-env"] = canaryEnv;
|
|
2359
|
-
return req;
|
|
2360
|
-
});
|
|
2361
|
-
}
|
|
2362
|
-
}
|
|
2363
|
-
return clientInstance;
|
|
2364
|
-
}
|
|
2365
|
-
|
|
2366
|
-
// src/commands/action-plugin/api-client.ts
|
|
3336
|
+
import fs11 from "fs";
|
|
3337
|
+
import path9 from "path";
|
|
2367
3338
|
var PLUGIN_CACHE_DIR = "node_modules/.cache/fullstack-cli/plugins";
|
|
2368
3339
|
async function getPluginVersions(keys, latestOnly = true) {
|
|
2369
3340
|
const client = getHttpClient();
|
|
@@ -2427,19 +3398,34 @@ async function downloadFromPublic(downloadURL) {
|
|
|
2427
3398
|
return Buffer.from(arrayBuffer);
|
|
2428
3399
|
}
|
|
2429
3400
|
function getPluginCacheDir() {
|
|
2430
|
-
return
|
|
3401
|
+
return path9.join(process.cwd(), PLUGIN_CACHE_DIR);
|
|
2431
3402
|
}
|
|
2432
3403
|
function ensureCacheDir() {
|
|
2433
3404
|
const cacheDir = getPluginCacheDir();
|
|
2434
|
-
if (!
|
|
2435
|
-
|
|
3405
|
+
if (!fs11.existsSync(cacheDir)) {
|
|
3406
|
+
fs11.mkdirSync(cacheDir, { recursive: true });
|
|
2436
3407
|
}
|
|
2437
3408
|
}
|
|
2438
3409
|
function getTempFilePath(pluginKey, version) {
|
|
2439
3410
|
ensureCacheDir();
|
|
2440
3411
|
const safeKey = pluginKey.replace(/[/@]/g, "_");
|
|
2441
|
-
const filename = `${safeKey}
|
|
2442
|
-
return
|
|
3412
|
+
const filename = `${safeKey}@${version}.tgz`;
|
|
3413
|
+
return path9.join(getPluginCacheDir(), filename);
|
|
3414
|
+
}
|
|
3415
|
+
var MAX_RETRIES = 2;
|
|
3416
|
+
async function withRetry(operation, description, maxRetries = MAX_RETRIES) {
|
|
3417
|
+
let lastError;
|
|
3418
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
3419
|
+
try {
|
|
3420
|
+
return await operation();
|
|
3421
|
+
} catch (error) {
|
|
3422
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
3423
|
+
if (attempt < maxRetries) {
|
|
3424
|
+
console.log(`[action-plugin] ${description} failed, retrying (${attempt + 1}/${maxRetries})...`);
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3427
|
+
}
|
|
3428
|
+
throw lastError;
|
|
2443
3429
|
}
|
|
2444
3430
|
async function downloadPlugin(pluginKey, requestedVersion) {
|
|
2445
3431
|
console.log(`[action-plugin] Fetching plugin info for ${pluginKey}@${requestedVersion}...`);
|
|
@@ -2449,13 +3435,19 @@ async function downloadPlugin(pluginKey, requestedVersion) {
|
|
|
2449
3435
|
let tgzBuffer;
|
|
2450
3436
|
if (pluginInfo.downloadApproach === "inner") {
|
|
2451
3437
|
console.log(`[action-plugin] Downloading from inner API...`);
|
|
2452
|
-
tgzBuffer = await
|
|
3438
|
+
tgzBuffer = await withRetry(
|
|
3439
|
+
() => downloadFromInner(pluginKey, pluginInfo.version),
|
|
3440
|
+
"Download"
|
|
3441
|
+
);
|
|
2453
3442
|
} else {
|
|
2454
3443
|
console.log(`[action-plugin] Downloading from public URL...`);
|
|
2455
|
-
tgzBuffer = await
|
|
3444
|
+
tgzBuffer = await withRetry(
|
|
3445
|
+
() => downloadFromPublic(pluginInfo.downloadURL),
|
|
3446
|
+
"Download"
|
|
3447
|
+
);
|
|
2456
3448
|
}
|
|
2457
3449
|
const tgzPath = getTempFilePath(pluginKey, pluginInfo.version);
|
|
2458
|
-
|
|
3450
|
+
fs11.writeFileSync(tgzPath, tgzBuffer);
|
|
2459
3451
|
console.log(`[action-plugin] Downloaded to ${tgzPath} (${(tgzBuffer.length / 1024).toFixed(2)} KB)`);
|
|
2460
3452
|
return {
|
|
2461
3453
|
tgzPath,
|
|
@@ -2464,25 +3456,99 @@ async function downloadPlugin(pluginKey, requestedVersion) {
|
|
|
2464
3456
|
};
|
|
2465
3457
|
}
|
|
2466
3458
|
function cleanupTempFile(tgzPath) {
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
3459
|
+
}
|
|
3460
|
+
function getCachePath(pluginKey, version) {
|
|
3461
|
+
ensureCacheDir();
|
|
3462
|
+
const safeKey = pluginKey.replace(/[/@]/g, "_");
|
|
3463
|
+
const filename = `${safeKey}@${version}.tgz`;
|
|
3464
|
+
return path9.join(getPluginCacheDir(), filename);
|
|
3465
|
+
}
|
|
3466
|
+
function hasCachedPlugin(pluginKey, version) {
|
|
3467
|
+
const cachePath = getCachePath(pluginKey, version);
|
|
3468
|
+
return fs11.existsSync(cachePath);
|
|
3469
|
+
}
|
|
3470
|
+
function listCachedPlugins() {
|
|
3471
|
+
const cacheDir = getPluginCacheDir();
|
|
3472
|
+
if (!fs11.existsSync(cacheDir)) {
|
|
3473
|
+
return [];
|
|
3474
|
+
}
|
|
3475
|
+
const files = fs11.readdirSync(cacheDir);
|
|
3476
|
+
const result = [];
|
|
3477
|
+
for (const file of files) {
|
|
3478
|
+
if (!file.endsWith(".tgz")) continue;
|
|
3479
|
+
const match = file.match(/^(.+)@(.+)\.tgz$/);
|
|
3480
|
+
if (!match) continue;
|
|
3481
|
+
const [, rawName, version] = match;
|
|
3482
|
+
const name = rawName.replace(/^_/, "@").replace(/_/, "/");
|
|
3483
|
+
const filePath = path9.join(cacheDir, file);
|
|
3484
|
+
const stat = fs11.statSync(filePath);
|
|
3485
|
+
result.push({
|
|
3486
|
+
name,
|
|
3487
|
+
version,
|
|
3488
|
+
filePath,
|
|
3489
|
+
size: stat.size,
|
|
3490
|
+
mtime: stat.mtime
|
|
3491
|
+
});
|
|
3492
|
+
}
|
|
3493
|
+
return result;
|
|
3494
|
+
}
|
|
3495
|
+
function cleanAllCache() {
|
|
3496
|
+
const cacheDir = getPluginCacheDir();
|
|
3497
|
+
if (!fs11.existsSync(cacheDir)) {
|
|
3498
|
+
return 0;
|
|
3499
|
+
}
|
|
3500
|
+
const files = fs11.readdirSync(cacheDir);
|
|
3501
|
+
let count = 0;
|
|
3502
|
+
for (const file of files) {
|
|
3503
|
+
if (file.endsWith(".tgz")) {
|
|
3504
|
+
fs11.unlinkSync(path9.join(cacheDir, file));
|
|
3505
|
+
count++;
|
|
2470
3506
|
}
|
|
2471
|
-
} catch {
|
|
2472
3507
|
}
|
|
3508
|
+
return count;
|
|
3509
|
+
}
|
|
3510
|
+
function cleanPluginCache(pluginKey, version) {
|
|
3511
|
+
const cacheDir = getPluginCacheDir();
|
|
3512
|
+
if (!fs11.existsSync(cacheDir)) {
|
|
3513
|
+
return 0;
|
|
3514
|
+
}
|
|
3515
|
+
const safeKey = pluginKey.replace(/[/@]/g, "_");
|
|
3516
|
+
const files = fs11.readdirSync(cacheDir);
|
|
3517
|
+
let count = 0;
|
|
3518
|
+
for (const file of files) {
|
|
3519
|
+
if (version) {
|
|
3520
|
+
if (file === `${safeKey}@${version}.tgz`) {
|
|
3521
|
+
fs11.unlinkSync(path9.join(cacheDir, file));
|
|
3522
|
+
count++;
|
|
3523
|
+
}
|
|
3524
|
+
} else {
|
|
3525
|
+
if (file.startsWith(`${safeKey}@`) && file.endsWith(".tgz")) {
|
|
3526
|
+
fs11.unlinkSync(path9.join(cacheDir, file));
|
|
3527
|
+
count++;
|
|
3528
|
+
}
|
|
3529
|
+
}
|
|
3530
|
+
}
|
|
3531
|
+
return count;
|
|
2473
3532
|
}
|
|
2474
3533
|
|
|
2475
3534
|
// src/commands/action-plugin/init.handler.ts
|
|
2476
3535
|
async function installOneForInit(name, version) {
|
|
2477
|
-
let tgzPath;
|
|
2478
3536
|
try {
|
|
2479
3537
|
const installedVersion = getPackageVersion(name);
|
|
2480
3538
|
if (installedVersion === version) {
|
|
2481
3539
|
return { name, version, success: true, skipped: true };
|
|
2482
3540
|
}
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
3541
|
+
let tgzPath;
|
|
3542
|
+
let fromCache = false;
|
|
3543
|
+
if (hasCachedPlugin(name, version)) {
|
|
3544
|
+
console.log(`[action-plugin] \u21BB Restoring ${name}@${version} from cache...`);
|
|
3545
|
+
tgzPath = getCachePath(name, version);
|
|
3546
|
+
fromCache = true;
|
|
3547
|
+
} else {
|
|
3548
|
+
console.log(`[action-plugin] \u2193 Downloading ${name}@${version}...`);
|
|
3549
|
+
const downloadResult = await downloadPlugin(name, version);
|
|
3550
|
+
tgzPath = downloadResult.tgzPath;
|
|
3551
|
+
}
|
|
2486
3552
|
const pluginDir = extractTgzToNodeModules(tgzPath, name);
|
|
2487
3553
|
const pluginPkg = readPluginPackageJson(pluginDir);
|
|
2488
3554
|
if (pluginPkg?.peerDependencies) {
|
|
@@ -2491,16 +3557,13 @@ async function installOneForInit(name, version) {
|
|
|
2491
3557
|
installMissingDeps(missingDeps);
|
|
2492
3558
|
}
|
|
2493
3559
|
}
|
|
2494
|
-
|
|
3560
|
+
const source = fromCache ? "from cache" : "downloaded";
|
|
3561
|
+
console.log(`[action-plugin] \u2713 Installed ${name}@${version} (${source})`);
|
|
2495
3562
|
return { name, version, success: true };
|
|
2496
3563
|
} catch (error) {
|
|
2497
3564
|
const message = error instanceof Error ? error.message : String(error);
|
|
2498
3565
|
console.error(`[action-plugin] \u2717 Failed to install ${name}: ${message}`);
|
|
2499
3566
|
return { name, version, success: false, error: message };
|
|
2500
|
-
} finally {
|
|
2501
|
-
if (tgzPath) {
|
|
2502
|
-
cleanupTempFile(tgzPath);
|
|
2503
|
-
}
|
|
2504
3567
|
}
|
|
2505
3568
|
}
|
|
2506
3569
|
async function init() {
|
|
@@ -2546,7 +3609,6 @@ function syncActionPluginsRecord(name, version) {
|
|
|
2546
3609
|
}
|
|
2547
3610
|
}
|
|
2548
3611
|
async function installOne(nameWithVersion) {
|
|
2549
|
-
let tgzPath;
|
|
2550
3612
|
const { name, version: requestedVersion } = parsePluginName(nameWithVersion);
|
|
2551
3613
|
try {
|
|
2552
3614
|
console.log(`[action-plugin] Installing ${name}@${requestedVersion}...`);
|
|
@@ -2555,20 +3617,35 @@ async function installOne(nameWithVersion) {
|
|
|
2555
3617
|
if (actualVersion === requestedVersion) {
|
|
2556
3618
|
console.log(`[action-plugin] Plugin ${name}@${requestedVersion} is already installed`);
|
|
2557
3619
|
syncActionPluginsRecord(name, actualVersion);
|
|
3620
|
+
reportCreateInstanceEvent(name, actualVersion).catch(() => {
|
|
3621
|
+
});
|
|
2558
3622
|
return { name, version: actualVersion, success: true, skipped: true };
|
|
2559
3623
|
}
|
|
2560
3624
|
}
|
|
2561
|
-
|
|
3625
|
+
let targetVersion = requestedVersion;
|
|
3626
|
+
if (requestedVersion === "latest") {
|
|
2562
3627
|
const latestInfo = await getPluginVersion(name, "latest");
|
|
2563
|
-
|
|
3628
|
+
targetVersion = latestInfo.version;
|
|
3629
|
+
if (actualVersion === targetVersion) {
|
|
2564
3630
|
console.log(`[action-plugin] Plugin ${name} is already up to date (version: ${actualVersion})`);
|
|
2565
3631
|
syncActionPluginsRecord(name, actualVersion);
|
|
3632
|
+
reportCreateInstanceEvent(name, actualVersion).catch(() => {
|
|
3633
|
+
});
|
|
2566
3634
|
return { name, version: actualVersion, success: true, skipped: true };
|
|
2567
3635
|
}
|
|
2568
|
-
console.log(`[action-plugin] Found newer version: ${
|
|
3636
|
+
console.log(`[action-plugin] Found newer version: ${targetVersion} (installed: ${actualVersion || "none"})`);
|
|
3637
|
+
}
|
|
3638
|
+
let tgzPath;
|
|
3639
|
+
let fromCache = false;
|
|
3640
|
+
if (hasCachedPlugin(name, targetVersion)) {
|
|
3641
|
+
console.log(`[action-plugin] \u21BB Using cached ${name}@${targetVersion}...`);
|
|
3642
|
+
tgzPath = getCachePath(name, targetVersion);
|
|
3643
|
+
fromCache = true;
|
|
3644
|
+
} else {
|
|
3645
|
+
console.log(`[action-plugin] \u2193 Downloading ${name}@${targetVersion}...`);
|
|
3646
|
+
const downloadResult = await downloadPlugin(name, requestedVersion);
|
|
3647
|
+
tgzPath = downloadResult.tgzPath;
|
|
2569
3648
|
}
|
|
2570
|
-
const downloadResult = await downloadPlugin(name, requestedVersion);
|
|
2571
|
-
tgzPath = downloadResult.tgzPath;
|
|
2572
3649
|
console.log(`[action-plugin] Extracting to node_modules...`);
|
|
2573
3650
|
const pluginDir = extractTgzToNodeModules(tgzPath, name);
|
|
2574
3651
|
const pluginPkg = readPluginPackageJson(pluginDir);
|
|
@@ -2578,20 +3655,21 @@ async function installOne(nameWithVersion) {
|
|
|
2578
3655
|
installMissingDeps(missingDeps);
|
|
2579
3656
|
}
|
|
2580
3657
|
}
|
|
2581
|
-
const installedVersion = getPackageVersion(name) ||
|
|
3658
|
+
const installedVersion = getPackageVersion(name) || targetVersion;
|
|
2582
3659
|
const plugins = readActionPlugins();
|
|
2583
3660
|
plugins[name] = installedVersion;
|
|
2584
3661
|
writeActionPlugins(plugins);
|
|
2585
|
-
|
|
3662
|
+
const source = fromCache ? "from cache" : "downloaded";
|
|
3663
|
+
console.log(`[action-plugin] Successfully installed ${name}@${installedVersion} (${source})`);
|
|
3664
|
+
reportInstallEvent(name, installedVersion).catch(() => {
|
|
3665
|
+
});
|
|
3666
|
+
reportCreateInstanceEvent(name, installedVersion).catch(() => {
|
|
3667
|
+
});
|
|
2586
3668
|
return { name, version: installedVersion, success: true };
|
|
2587
3669
|
} catch (error) {
|
|
2588
3670
|
const message = error instanceof Error ? error.message : String(error);
|
|
2589
3671
|
console.error(`[action-plugin] Failed to install ${name}: ${message}`);
|
|
2590
3672
|
return { name, version: requestedVersion, success: false, error: message };
|
|
2591
|
-
} finally {
|
|
2592
|
-
if (tgzPath) {
|
|
2593
|
-
cleanupTempFile(tgzPath);
|
|
2594
|
-
}
|
|
2595
3673
|
}
|
|
2596
3674
|
}
|
|
2597
3675
|
async function install(namesWithVersion) {
|
|
@@ -2745,6 +3823,58 @@ async function list() {
|
|
|
2745
3823
|
}
|
|
2746
3824
|
}
|
|
2747
3825
|
|
|
3826
|
+
// src/commands/action-plugin/cache.handler.ts
|
|
3827
|
+
async function cacheList() {
|
|
3828
|
+
const cached = listCachedPlugins();
|
|
3829
|
+
if (cached.length === 0) {
|
|
3830
|
+
console.log("[action-plugin] No cached plugins found");
|
|
3831
|
+
return;
|
|
3832
|
+
}
|
|
3833
|
+
console.log("[action-plugin] Cached plugins:\n");
|
|
3834
|
+
console.log(" Name Version Size Modified");
|
|
3835
|
+
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");
|
|
3836
|
+
let totalSize = 0;
|
|
3837
|
+
for (const plugin of cached) {
|
|
3838
|
+
const name = plugin.name.padEnd(38);
|
|
3839
|
+
const version = plugin.version.padEnd(10);
|
|
3840
|
+
const size = formatSize(plugin.size).padEnd(9);
|
|
3841
|
+
const mtime = plugin.mtime.toISOString().replace("T", " ").slice(0, 19);
|
|
3842
|
+
console.log(` ${name} ${version} ${size} ${mtime}`);
|
|
3843
|
+
totalSize += plugin.size;
|
|
3844
|
+
}
|
|
3845
|
+
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");
|
|
3846
|
+
console.log(` Total: ${cached.length} cached file(s), ${formatSize(totalSize)}`);
|
|
3847
|
+
}
|
|
3848
|
+
async function cacheClean(pluginName, version) {
|
|
3849
|
+
let count;
|
|
3850
|
+
if (pluginName) {
|
|
3851
|
+
console.log(`[action-plugin] Cleaning cache for ${pluginName}${version ? `@${version}` : ""}...`);
|
|
3852
|
+
count = cleanPluginCache(pluginName, version);
|
|
3853
|
+
if (count === 0) {
|
|
3854
|
+
console.log(`[action-plugin] No cached files found for ${pluginName}${version ? `@${version}` : ""}`);
|
|
3855
|
+
} else {
|
|
3856
|
+
console.log(`[action-plugin] Cleaned ${count} cached file(s)`);
|
|
3857
|
+
}
|
|
3858
|
+
} else {
|
|
3859
|
+
console.log("[action-plugin] Cleaning all cached plugins...");
|
|
3860
|
+
count = cleanAllCache();
|
|
3861
|
+
if (count === 0) {
|
|
3862
|
+
console.log("[action-plugin] No cached files found");
|
|
3863
|
+
} else {
|
|
3864
|
+
console.log(`[action-plugin] Cleaned ${count} cached file(s)`);
|
|
3865
|
+
}
|
|
3866
|
+
}
|
|
3867
|
+
}
|
|
3868
|
+
function formatSize(bytes) {
|
|
3869
|
+
if (bytes < 1024) {
|
|
3870
|
+
return `${bytes} B`;
|
|
3871
|
+
} else if (bytes < 1024 * 1024) {
|
|
3872
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
3873
|
+
} else {
|
|
3874
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
3875
|
+
}
|
|
3876
|
+
}
|
|
3877
|
+
|
|
2748
3878
|
// src/commands/action-plugin/index.ts
|
|
2749
3879
|
var initCommand = {
|
|
2750
3880
|
name: "init",
|
|
@@ -2795,46 +3925,65 @@ var listCommand = {
|
|
|
2795
3925
|
});
|
|
2796
3926
|
}
|
|
2797
3927
|
};
|
|
3928
|
+
var cacheListCommand = {
|
|
3929
|
+
name: "cache-list",
|
|
3930
|
+
description: "List all cached plugin packages",
|
|
3931
|
+
register(program) {
|
|
3932
|
+
program.command(this.name).description(this.description).action(async () => {
|
|
3933
|
+
await cacheList();
|
|
3934
|
+
});
|
|
3935
|
+
}
|
|
3936
|
+
};
|
|
3937
|
+
var cacheCleanCommand = {
|
|
3938
|
+
name: "cache-clean",
|
|
3939
|
+
description: "Clean cached plugin packages",
|
|
3940
|
+
register(program) {
|
|
3941
|
+
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) => {
|
|
3942
|
+
await cacheClean(name, options.version);
|
|
3943
|
+
});
|
|
3944
|
+
}
|
|
3945
|
+
};
|
|
2798
3946
|
var actionPluginCommandGroup = {
|
|
2799
3947
|
name: "action-plugin",
|
|
2800
3948
|
description: "Manage action plugins",
|
|
2801
|
-
commands: [initCommand, installCommand, updateCommand, removeCommand, listCommand]
|
|
3949
|
+
commands: [initCommand, installCommand, updateCommand, removeCommand, listCommand, cacheListCommand, cacheCleanCommand]
|
|
2802
3950
|
};
|
|
2803
3951
|
|
|
2804
3952
|
// src/commands/capability/utils.ts
|
|
2805
|
-
import
|
|
2806
|
-
import
|
|
3953
|
+
import fs12 from "fs";
|
|
3954
|
+
import { createRequire as createRequire2 } from "module";
|
|
3955
|
+
import path10 from "path";
|
|
2807
3956
|
var CAPABILITIES_DIR = "server/capabilities";
|
|
2808
3957
|
function getProjectRoot2() {
|
|
2809
3958
|
return process.cwd();
|
|
2810
3959
|
}
|
|
2811
3960
|
function getCapabilitiesDir() {
|
|
2812
|
-
return
|
|
3961
|
+
return path10.join(getProjectRoot2(), CAPABILITIES_DIR);
|
|
2813
3962
|
}
|
|
2814
3963
|
function getCapabilityPath(id) {
|
|
2815
|
-
return
|
|
3964
|
+
return path10.join(getCapabilitiesDir(), `${id}.json`);
|
|
2816
3965
|
}
|
|
2817
3966
|
function getPluginManifestPath(pluginKey) {
|
|
2818
|
-
return
|
|
3967
|
+
return path10.join(getProjectRoot2(), "node_modules", pluginKey, "manifest.json");
|
|
2819
3968
|
}
|
|
2820
3969
|
function capabilitiesDirExists() {
|
|
2821
|
-
return
|
|
3970
|
+
return fs12.existsSync(getCapabilitiesDir());
|
|
2822
3971
|
}
|
|
2823
3972
|
function listCapabilityIds() {
|
|
2824
3973
|
const dir = getCapabilitiesDir();
|
|
2825
|
-
if (!
|
|
3974
|
+
if (!fs12.existsSync(dir)) {
|
|
2826
3975
|
return [];
|
|
2827
3976
|
}
|
|
2828
|
-
const files =
|
|
3977
|
+
const files = fs12.readdirSync(dir);
|
|
2829
3978
|
return files.filter((f) => f.endsWith(".json") && f !== "capabilities.json").map((f) => f.replace(/\.json$/, ""));
|
|
2830
3979
|
}
|
|
2831
3980
|
function readCapability(id) {
|
|
2832
3981
|
const filePath = getCapabilityPath(id);
|
|
2833
|
-
if (!
|
|
3982
|
+
if (!fs12.existsSync(filePath)) {
|
|
2834
3983
|
throw new Error(`Capability not found: ${id}`);
|
|
2835
3984
|
}
|
|
2836
3985
|
try {
|
|
2837
|
-
const content =
|
|
3986
|
+
const content = fs12.readFileSync(filePath, "utf-8");
|
|
2838
3987
|
return JSON.parse(content);
|
|
2839
3988
|
} catch (error) {
|
|
2840
3989
|
if (error instanceof SyntaxError) {
|
|
@@ -2861,11 +4010,11 @@ function readAllCapabilities() {
|
|
|
2861
4010
|
}
|
|
2862
4011
|
function readPluginManifest(pluginKey) {
|
|
2863
4012
|
const manifestPath = getPluginManifestPath(pluginKey);
|
|
2864
|
-
if (!
|
|
4013
|
+
if (!fs12.existsSync(manifestPath)) {
|
|
2865
4014
|
throw new Error(`Plugin not installed: ${pluginKey} (manifest.json not found)`);
|
|
2866
4015
|
}
|
|
2867
4016
|
try {
|
|
2868
|
-
const content =
|
|
4017
|
+
const content = fs12.readFileSync(manifestPath, "utf-8");
|
|
2869
4018
|
return JSON.parse(content);
|
|
2870
4019
|
} catch (error) {
|
|
2871
4020
|
if (error instanceof SyntaxError) {
|
|
@@ -2882,7 +4031,10 @@ function hasValidParamsSchema(paramsSchema) {
|
|
|
2882
4031
|
}
|
|
2883
4032
|
async function loadPlugin(pluginKey) {
|
|
2884
4033
|
try {
|
|
2885
|
-
const
|
|
4034
|
+
const userRequire = createRequire2(path10.join(getProjectRoot2(), "package.json"));
|
|
4035
|
+
const resolvedPath = userRequire.resolve(pluginKey);
|
|
4036
|
+
const pluginModule = await import(resolvedPath);
|
|
4037
|
+
const pluginPackage = pluginModule.default ?? pluginModule;
|
|
2886
4038
|
if (!pluginPackage || typeof pluginPackage.create !== "function") {
|
|
2887
4039
|
throw new Error(`Plugin ${pluginKey} does not export a valid create function`);
|
|
2888
4040
|
}
|
|
@@ -2976,121 +4128,420 @@ function logError(message) {
|
|
|
2976
4128
|
console.error(`[capability] Error: ${message}`);
|
|
2977
4129
|
}
|
|
2978
4130
|
|
|
2979
|
-
// src/commands/capability/list.handler.ts
|
|
2980
|
-
async function list2(options) {
|
|
2981
|
-
try {
|
|
2982
|
-
if (!capabilitiesDirExists()) {
|
|
2983
|
-
logError("server/capabilities directory not found");
|
|
2984
|
-
process.exit(1);
|
|
4131
|
+
// src/commands/capability/list.handler.ts
|
|
4132
|
+
async function list2(options) {
|
|
4133
|
+
try {
|
|
4134
|
+
if (!capabilitiesDirExists()) {
|
|
4135
|
+
logError("server/capabilities directory not found");
|
|
4136
|
+
process.exit(1);
|
|
4137
|
+
}
|
|
4138
|
+
if (options.id) {
|
|
4139
|
+
await listSingle(options.id, options.summary);
|
|
4140
|
+
} else {
|
|
4141
|
+
await listAll(options.summary);
|
|
4142
|
+
}
|
|
4143
|
+
} catch (error) {
|
|
4144
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4145
|
+
logError(message);
|
|
4146
|
+
process.exit(1);
|
|
4147
|
+
}
|
|
4148
|
+
}
|
|
4149
|
+
async function listSingle(id, summary) {
|
|
4150
|
+
const capability = readCapability(id);
|
|
4151
|
+
if (summary) {
|
|
4152
|
+
printJson(capability);
|
|
4153
|
+
} else {
|
|
4154
|
+
const hydrated = await hydrateCapability(capability);
|
|
4155
|
+
printJson(hydrated);
|
|
4156
|
+
}
|
|
4157
|
+
}
|
|
4158
|
+
async function listAll(summary) {
|
|
4159
|
+
const capabilities = readAllCapabilities();
|
|
4160
|
+
if (capabilities.length === 0) {
|
|
4161
|
+
printJson([]);
|
|
4162
|
+
return;
|
|
4163
|
+
}
|
|
4164
|
+
if (summary) {
|
|
4165
|
+
printJson(capabilities);
|
|
4166
|
+
} else {
|
|
4167
|
+
const hydrated = [];
|
|
4168
|
+
for (const capability of capabilities) {
|
|
4169
|
+
const result = await hydrateCapability(capability);
|
|
4170
|
+
hydrated.push(result);
|
|
4171
|
+
}
|
|
4172
|
+
printJson(hydrated);
|
|
4173
|
+
}
|
|
4174
|
+
}
|
|
4175
|
+
|
|
4176
|
+
// src/commands/capability/index.ts
|
|
4177
|
+
var listCommand2 = {
|
|
4178
|
+
name: "list",
|
|
4179
|
+
description: "List capability configurations",
|
|
4180
|
+
aliases: ["ls"],
|
|
4181
|
+
register(program) {
|
|
4182
|
+
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) => {
|
|
4183
|
+
await list2(options);
|
|
4184
|
+
});
|
|
4185
|
+
}
|
|
4186
|
+
};
|
|
4187
|
+
var capabilityCommandGroup = {
|
|
4188
|
+
name: "capability",
|
|
4189
|
+
description: "Manage capability configurations",
|
|
4190
|
+
commands: [listCommand2]
|
|
4191
|
+
};
|
|
4192
|
+
|
|
4193
|
+
// src/commands/component/add.handler.ts
|
|
4194
|
+
import { execFile } from "child_process";
|
|
4195
|
+
|
|
4196
|
+
// src/commands/component/registry-preparer.ts
|
|
4197
|
+
import fs13 from "fs";
|
|
4198
|
+
import path11 from "path";
|
|
4199
|
+
import os from "os";
|
|
4200
|
+
|
|
4201
|
+
// src/commands/component/service.ts
|
|
4202
|
+
import { mapValues } from "es-toolkit";
|
|
4203
|
+
|
|
4204
|
+
// src/commands/component/utils.ts
|
|
4205
|
+
import createDebug from "debug";
|
|
4206
|
+
var debug = createDebug("component");
|
|
4207
|
+
|
|
4208
|
+
// src/commands/component/service.ts
|
|
4209
|
+
async function getComponents(keys) {
|
|
4210
|
+
const client = getHttpClient();
|
|
4211
|
+
debug("\u8C03\u7528 /components/batch_get %o", keys);
|
|
4212
|
+
const response = await client.post(
|
|
4213
|
+
`/api/v1/studio/innerapi/components/batch_get?keys=${keys.join(",")}`
|
|
4214
|
+
);
|
|
4215
|
+
if (response.status !== 200) {
|
|
4216
|
+
throw new Error(
|
|
4217
|
+
`\u83B7\u53D6\u7EC4\u4EF6\u4FE1\u606F\u5931\u8D25\uFF1A${response.status} ${response.statusText}`
|
|
4218
|
+
);
|
|
4219
|
+
}
|
|
4220
|
+
const result = await response.json();
|
|
4221
|
+
if (result.status_code !== "0") {
|
|
4222
|
+
debug("\u63A5\u53E3\u8FD4\u56DE\u9519\u8BEF\uFF1A%o", result);
|
|
4223
|
+
throw new Error(`\u83B7\u53D6\u7EC4\u4EF6\u4FE1\u606F\u5931\u8D25\uFF1A${result.error_msg}`);
|
|
4224
|
+
}
|
|
4225
|
+
return mapValues(result.data.components, ([component]) => component);
|
|
4226
|
+
}
|
|
4227
|
+
async function getRegistryItem(url) {
|
|
4228
|
+
const client = getHttpClient();
|
|
4229
|
+
debug("\u4E0B\u8F7D registry-item.json\uFF1A%s", url);
|
|
4230
|
+
const response = await client.get(url);
|
|
4231
|
+
if (!response.ok) {
|
|
4232
|
+
throw new Error(
|
|
4233
|
+
`\u4E0B\u8F7D registry-item.json \u5931\u8D25\uFF1A${response.status} ${response.statusText}`
|
|
4234
|
+
);
|
|
4235
|
+
}
|
|
4236
|
+
const item = await response.json();
|
|
4237
|
+
return item;
|
|
4238
|
+
}
|
|
4239
|
+
async function sendInstallEvent(key) {
|
|
4240
|
+
const client = getHttpClient();
|
|
4241
|
+
await client.post("/api/v1/studio/innerapi/resource_events", {
|
|
4242
|
+
events: [
|
|
4243
|
+
{
|
|
4244
|
+
resourceType: "component",
|
|
4245
|
+
resourceKey: key,
|
|
4246
|
+
eventType: "install",
|
|
4247
|
+
details: {}
|
|
4248
|
+
}
|
|
4249
|
+
]
|
|
4250
|
+
});
|
|
4251
|
+
}
|
|
4252
|
+
|
|
4253
|
+
// src/commands/component/registry-preparer.ts
|
|
4254
|
+
var REGISTRY_TEMP_DIR = path11.join(os.tmpdir(), "miaoda-registry");
|
|
4255
|
+
function parseComponentKey(key) {
|
|
4256
|
+
const match = key.match(/^@([^/]+)\/(.+)$/);
|
|
4257
|
+
if (!match) {
|
|
4258
|
+
throw new Error(
|
|
4259
|
+
`Invalid component key format: ${key}. Expected format: @scope/name`
|
|
4260
|
+
);
|
|
4261
|
+
}
|
|
4262
|
+
return { scope: match[1], name: match[2] };
|
|
4263
|
+
}
|
|
4264
|
+
function getLocalRegistryPath(key) {
|
|
4265
|
+
const { scope, name } = parseComponentKey(key);
|
|
4266
|
+
return path11.join(REGISTRY_TEMP_DIR, scope, `${name}.json`);
|
|
4267
|
+
}
|
|
4268
|
+
function ensureDir(dirPath) {
|
|
4269
|
+
if (!fs13.existsSync(dirPath)) {
|
|
4270
|
+
fs13.mkdirSync(dirPath, { recursive: true });
|
|
4271
|
+
}
|
|
4272
|
+
}
|
|
4273
|
+
async function prepareRecursive(key, visited) {
|
|
4274
|
+
if (visited.has(key)) {
|
|
4275
|
+
debug("\u8DF3\u8FC7\u5DF2\u5904\u7406\u7684\u7EC4\u4EF6: %s", key);
|
|
4276
|
+
return;
|
|
4277
|
+
}
|
|
4278
|
+
visited.add(key);
|
|
4279
|
+
debug("\u5904\u7406\u7EC4\u4EF6: %s", key);
|
|
4280
|
+
debug("\u83B7\u53D6\u7EC4\u4EF6\u4E0B\u8F7D\u4FE1\u606F...");
|
|
4281
|
+
const infoMap = await getComponents([key]);
|
|
4282
|
+
const info = infoMap[key];
|
|
4283
|
+
debug("\u7EC4\u4EF6\u4FE1\u606F: %o", info);
|
|
4284
|
+
if (!info) {
|
|
4285
|
+
throw new Error(`Component not found: ${key}`);
|
|
4286
|
+
}
|
|
4287
|
+
if (info.status !== "active") {
|
|
4288
|
+
throw new Error(`Component is not active: ${key}`);
|
|
4289
|
+
}
|
|
4290
|
+
debug("\u4E0B\u8F7D registry item: %s", info.downloadURL);
|
|
4291
|
+
const registryItem = await getRegistryItem(info.downloadURL);
|
|
4292
|
+
debug("registry item \u5185\u5BB9: %o", registryItem);
|
|
4293
|
+
const deps = registryItem.registryDependencies || [];
|
|
4294
|
+
debug("\u4F9D\u8D56\u5217\u8868: %o", deps);
|
|
4295
|
+
for (const dep of deps) {
|
|
4296
|
+
await prepareRecursive(dep, visited);
|
|
4297
|
+
}
|
|
4298
|
+
const rewrittenItem = {
|
|
4299
|
+
...registryItem,
|
|
4300
|
+
registryDependencies: deps.map((dep) => getLocalRegistryPath(dep))
|
|
4301
|
+
};
|
|
4302
|
+
const localPath = getLocalRegistryPath(key);
|
|
4303
|
+
ensureDir(path11.dirname(localPath));
|
|
4304
|
+
fs13.writeFileSync(localPath, JSON.stringify(rewrittenItem, null, 2), "utf-8");
|
|
4305
|
+
debug("\u4FDD\u5B58\u5230: %s", localPath);
|
|
4306
|
+
}
|
|
4307
|
+
async function prepareComponentRegistryItems(id) {
|
|
4308
|
+
const visited = /* @__PURE__ */ new Set();
|
|
4309
|
+
await prepareRecursive(id, visited);
|
|
4310
|
+
return getLocalRegistryPath(id);
|
|
4311
|
+
}
|
|
4312
|
+
function cleanupTempDir() {
|
|
4313
|
+
try {
|
|
4314
|
+
if (fs13.existsSync(REGISTRY_TEMP_DIR)) {
|
|
4315
|
+
fs13.rmSync(REGISTRY_TEMP_DIR, { recursive: true, force: true });
|
|
4316
|
+
}
|
|
4317
|
+
} catch {
|
|
4318
|
+
}
|
|
4319
|
+
}
|
|
4320
|
+
function getDownloadedRegistryItem(itemId) {
|
|
4321
|
+
const localPath = getLocalRegistryPath(itemId);
|
|
4322
|
+
if (!fs13.existsSync(localPath)) {
|
|
4323
|
+
return null;
|
|
4324
|
+
}
|
|
4325
|
+
const content = fs13.readFileSync(localPath, "utf-8");
|
|
4326
|
+
return JSON.parse(content);
|
|
4327
|
+
}
|
|
4328
|
+
|
|
4329
|
+
// src/commands/component/shadcn-executor.ts
|
|
4330
|
+
import * as pty from "@lydell/node-pty";
|
|
4331
|
+
function parseOutput(output) {
|
|
4332
|
+
const state = {
|
|
4333
|
+
currentSection: null,
|
|
4334
|
+
files: /* @__PURE__ */ new Set()
|
|
4335
|
+
};
|
|
4336
|
+
const lines = output.split("\n");
|
|
4337
|
+
for (const line of lines) {
|
|
4338
|
+
const trimmedLine = line.trim();
|
|
4339
|
+
if (/Created \d+ files?:/.test(trimmedLine)) {
|
|
4340
|
+
state.currentSection = "created";
|
|
4341
|
+
continue;
|
|
2985
4342
|
}
|
|
2986
|
-
if (
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
4343
|
+
if (/Updated \d+ files?:/.test(trimmedLine)) {
|
|
4344
|
+
state.currentSection = "updated";
|
|
4345
|
+
continue;
|
|
4346
|
+
}
|
|
4347
|
+
if (/Skipped \d+ files?:/.test(trimmedLine)) {
|
|
4348
|
+
state.currentSection = "skipped";
|
|
4349
|
+
continue;
|
|
4350
|
+
}
|
|
4351
|
+
if (state.currentSection && trimmedLine.startsWith("- ")) {
|
|
4352
|
+
const filePath = trimmedLine.slice(2).trim();
|
|
4353
|
+
if (filePath && filePath.includes("/")) {
|
|
4354
|
+
state.files.add(filePath);
|
|
4355
|
+
}
|
|
4356
|
+
}
|
|
4357
|
+
if (state.currentSection && trimmedLine.length > 1 && !trimmedLine.startsWith("- ")) {
|
|
4358
|
+
state.currentSection = null;
|
|
2990
4359
|
}
|
|
2991
|
-
} catch (error) {
|
|
2992
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2993
|
-
logError(message);
|
|
2994
|
-
process.exit(1);
|
|
2995
4360
|
}
|
|
4361
|
+
return Array.from(state.files);
|
|
2996
4362
|
}
|
|
2997
|
-
|
|
2998
|
-
const
|
|
2999
|
-
|
|
3000
|
-
printJson(capability);
|
|
3001
|
-
} else {
|
|
3002
|
-
const hydrated = await hydrateCapability(capability);
|
|
3003
|
-
printJson(hydrated);
|
|
3004
|
-
}
|
|
4363
|
+
function toFileInfo(filePath) {
|
|
4364
|
+
const name = filePath.split("/").pop() || filePath;
|
|
4365
|
+
return { name, path: filePath };
|
|
3005
4366
|
}
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
4367
|
+
var PROMPT_PATTERNS = [
|
|
4368
|
+
// 文件覆盖确认 - 回答 n(不覆盖)
|
|
4369
|
+
{ pattern: /overwrite/i, answer: "n\n" },
|
|
4370
|
+
// 主题/样式选择 - 回答 n(不安装额外主题)
|
|
4371
|
+
{ pattern: /theme/i, answer: "n\n" },
|
|
4372
|
+
{ pattern: /style/i, answer: "n\n" },
|
|
4373
|
+
// 继续确认 - 回答 y
|
|
4374
|
+
{ pattern: /continue\?/i, answer: "y\n" },
|
|
4375
|
+
{ pattern: /proceed\?/i, answer: "y\n" }
|
|
4376
|
+
];
|
|
4377
|
+
async function executeShadcnAdd(registryItemPath) {
|
|
4378
|
+
return new Promise((resolve2) => {
|
|
4379
|
+
let output = "";
|
|
4380
|
+
const args = ["--yes", "shadcn@3.8.2", "add", registryItemPath];
|
|
4381
|
+
const ptyProcess = pty.spawn("npx", args, {
|
|
4382
|
+
name: "xterm-color",
|
|
4383
|
+
cols: 120,
|
|
4384
|
+
rows: 30,
|
|
4385
|
+
cwd: process.cwd(),
|
|
4386
|
+
env: {
|
|
4387
|
+
...process.env,
|
|
4388
|
+
// 禁用颜色输出以便解析
|
|
4389
|
+
NO_COLOR: "1",
|
|
4390
|
+
FORCE_COLOR: "0"
|
|
4391
|
+
}
|
|
4392
|
+
});
|
|
4393
|
+
ptyProcess.onData((data) => {
|
|
4394
|
+
output += data;
|
|
4395
|
+
for (const { pattern, answer } of PROMPT_PATTERNS) {
|
|
4396
|
+
if (pattern.test(data)) {
|
|
4397
|
+
ptyProcess.write(answer);
|
|
4398
|
+
return;
|
|
4399
|
+
}
|
|
4400
|
+
}
|
|
4401
|
+
});
|
|
4402
|
+
const timeoutId = setTimeout(() => {
|
|
4403
|
+
ptyProcess.kill();
|
|
4404
|
+
resolve2({
|
|
4405
|
+
success: false,
|
|
4406
|
+
files: [],
|
|
4407
|
+
error: "\u6267\u884C\u8D85\u65F6"
|
|
4408
|
+
});
|
|
4409
|
+
}, 3 * 60 * 1e3);
|
|
4410
|
+
ptyProcess.onExit(({ exitCode }) => {
|
|
4411
|
+
clearTimeout(timeoutId);
|
|
4412
|
+
const success = exitCode === 0;
|
|
4413
|
+
const filePaths = parseOutput(output);
|
|
4414
|
+
const files = filePaths.map(toFileInfo);
|
|
4415
|
+
resolve2({
|
|
4416
|
+
success,
|
|
4417
|
+
files,
|
|
4418
|
+
error: success ? void 0 : output || `Process exited with code ${exitCode}`
|
|
4419
|
+
});
|
|
4420
|
+
});
|
|
4421
|
+
});
|
|
4422
|
+
}
|
|
4423
|
+
|
|
4424
|
+
// src/commands/component/add.handler.ts
|
|
4425
|
+
function runActionPluginInit() {
|
|
4426
|
+
return new Promise((resolve2) => {
|
|
4427
|
+
execFile("fullstack-cli", ["action-plugin", "init"], { cwd: process.cwd(), stdio: "ignore" }, (error) => {
|
|
4428
|
+
if (error) {
|
|
4429
|
+
debug("action-plugin init \u5931\u8D25: %s", error.message);
|
|
4430
|
+
}
|
|
4431
|
+
resolve2();
|
|
4432
|
+
});
|
|
4433
|
+
});
|
|
4434
|
+
}
|
|
4435
|
+
function printResult(result) {
|
|
4436
|
+
console.log(JSON.stringify(result, null, 2));
|
|
4437
|
+
}
|
|
4438
|
+
async function addComponent(key) {
|
|
4439
|
+
debug("\u5F00\u59CB\u5B89\u88C5\u7EC4\u4EF6: %s", key);
|
|
4440
|
+
debug("\u51C6\u5907 registry items...");
|
|
4441
|
+
const registryItemPath = await prepareComponentRegistryItems(key);
|
|
4442
|
+
debug("registry item \u8DEF\u5F84: %s", registryItemPath);
|
|
4443
|
+
const registryItem = getDownloadedRegistryItem(key);
|
|
4444
|
+
debug("\u83B7\u53D6\u5230 registry item: %o", registryItem);
|
|
4445
|
+
debug("\u6267\u884C shadcn add...");
|
|
4446
|
+
const executeResult = await executeShadcnAdd(registryItemPath);
|
|
4447
|
+
debug("shadcn \u6267\u884C\u7ED3\u679C: %o", executeResult);
|
|
4448
|
+
if (!executeResult.success) {
|
|
4449
|
+
throw new Error(executeResult.error || "\u5B89\u88C5\u5931\u8D25\uFF0C\u672A\u77E5\u539F\u56E0");
|
|
3011
4450
|
}
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
4451
|
+
return {
|
|
4452
|
+
success: true,
|
|
4453
|
+
name: key,
|
|
4454
|
+
description: registryItem?.description || "",
|
|
4455
|
+
files: executeResult.files,
|
|
4456
|
+
docs: registryItem?.docs || ""
|
|
4457
|
+
};
|
|
4458
|
+
}
|
|
4459
|
+
async function add(key) {
|
|
4460
|
+
try {
|
|
4461
|
+
const result = await addComponent(key);
|
|
4462
|
+
printResult(result);
|
|
4463
|
+
void sendInstallEvent(key);
|
|
4464
|
+
} catch (error) {
|
|
4465
|
+
const errorMessage = error instanceof Error ? error.message : "\u5B89\u88C5\u5931\u8D25\uFF0C\u539F\u56E0\u672A\u77E5";
|
|
4466
|
+
printResult({
|
|
4467
|
+
success: false,
|
|
4468
|
+
errors: [{ message: errorMessage }]
|
|
4469
|
+
});
|
|
4470
|
+
} finally {
|
|
4471
|
+
await runActionPluginInit();
|
|
4472
|
+
cleanupTempDir();
|
|
3021
4473
|
}
|
|
3022
4474
|
}
|
|
3023
4475
|
|
|
3024
|
-
// src/commands/
|
|
3025
|
-
var
|
|
3026
|
-
name: "
|
|
3027
|
-
description: "
|
|
3028
|
-
aliases: ["ls"],
|
|
4476
|
+
// src/commands/component/index.ts
|
|
4477
|
+
var addCommand = {
|
|
4478
|
+
name: "add",
|
|
4479
|
+
description: "\u5B89\u88C5\u5999\u642D\u7EC4\u4EF6\u5E02\u573A\u4E2D\u7684\u7EC4\u4EF6",
|
|
3029
4480
|
register(program) {
|
|
3030
|
-
program.command(this.name).
|
|
3031
|
-
await
|
|
4481
|
+
program.command(this.name).description(this.description).argument("<component>", "\u7EC4\u4EF6 ID (\u4F8B\u5982 @miaoda/button)").action(async (component) => {
|
|
4482
|
+
await add(component);
|
|
3032
4483
|
});
|
|
3033
4484
|
}
|
|
3034
4485
|
};
|
|
3035
|
-
var
|
|
3036
|
-
name: "
|
|
3037
|
-
description: "
|
|
3038
|
-
commands: [
|
|
4486
|
+
var componentCommandGroup = {
|
|
4487
|
+
name: "component",
|
|
4488
|
+
description: "\u7EC4\u4EF6\u76F8\u5173\u547D\u4EE4",
|
|
4489
|
+
commands: [addCommand]
|
|
3039
4490
|
};
|
|
3040
4491
|
|
|
3041
4492
|
// src/commands/migration/version-manager.ts
|
|
3042
|
-
import
|
|
3043
|
-
import
|
|
4493
|
+
import fs14 from "fs";
|
|
4494
|
+
import path12 from "path";
|
|
3044
4495
|
var PACKAGE_JSON = "package.json";
|
|
3045
4496
|
var VERSION_FIELD = "migrationVersion";
|
|
3046
4497
|
function getPackageJsonPath2() {
|
|
3047
|
-
return
|
|
4498
|
+
return path12.join(process.cwd(), PACKAGE_JSON);
|
|
3048
4499
|
}
|
|
3049
4500
|
function getCurrentVersion() {
|
|
3050
4501
|
const pkgPath = getPackageJsonPath2();
|
|
3051
|
-
if (!
|
|
4502
|
+
if (!fs14.existsSync(pkgPath)) {
|
|
3052
4503
|
throw new Error("package.json not found");
|
|
3053
4504
|
}
|
|
3054
|
-
const pkg2 = JSON.parse(
|
|
4505
|
+
const pkg2 = JSON.parse(fs14.readFileSync(pkgPath, "utf-8"));
|
|
3055
4506
|
return pkg2[VERSION_FIELD] ?? 0;
|
|
3056
4507
|
}
|
|
3057
4508
|
function setCurrentVersion(version) {
|
|
3058
4509
|
const pkgPath = getPackageJsonPath2();
|
|
3059
|
-
const pkg2 = JSON.parse(
|
|
4510
|
+
const pkg2 = JSON.parse(fs14.readFileSync(pkgPath, "utf-8"));
|
|
3060
4511
|
pkg2[VERSION_FIELD] = version;
|
|
3061
|
-
|
|
4512
|
+
fs14.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
|
|
3062
4513
|
}
|
|
3063
4514
|
|
|
3064
4515
|
// src/commands/migration/versions/v001_capability/json-migrator/detector.ts
|
|
3065
|
-
import
|
|
3066
|
-
import
|
|
4516
|
+
import fs16 from "fs";
|
|
4517
|
+
import path14 from "path";
|
|
3067
4518
|
|
|
3068
4519
|
// src/commands/migration/versions/v001_capability/utils.ts
|
|
3069
|
-
import
|
|
3070
|
-
import
|
|
4520
|
+
import fs15 from "fs";
|
|
4521
|
+
import path13 from "path";
|
|
3071
4522
|
var CAPABILITIES_DIR2 = "server/capabilities";
|
|
3072
4523
|
function getProjectRoot3() {
|
|
3073
4524
|
return process.cwd();
|
|
3074
4525
|
}
|
|
3075
4526
|
function getCapabilitiesDir2() {
|
|
3076
|
-
return
|
|
4527
|
+
return path13.join(getProjectRoot3(), CAPABILITIES_DIR2);
|
|
3077
4528
|
}
|
|
3078
4529
|
function getPluginManifestPath2(pluginKey) {
|
|
3079
|
-
return
|
|
4530
|
+
return path13.join(getProjectRoot3(), "node_modules", pluginKey, "manifest.json");
|
|
3080
4531
|
}
|
|
3081
4532
|
|
|
3082
4533
|
// src/commands/migration/versions/v001_capability/json-migrator/detector.ts
|
|
3083
4534
|
function detectJsonMigration() {
|
|
3084
4535
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
3085
|
-
const oldFilePath =
|
|
3086
|
-
if (!
|
|
4536
|
+
const oldFilePath = path14.join(capabilitiesDir, "capabilities.json");
|
|
4537
|
+
if (!fs16.existsSync(oldFilePath)) {
|
|
3087
4538
|
return {
|
|
3088
4539
|
needsMigration: false,
|
|
3089
4540
|
reason: "capabilities.json not found"
|
|
3090
4541
|
};
|
|
3091
4542
|
}
|
|
3092
4543
|
try {
|
|
3093
|
-
const content =
|
|
4544
|
+
const content = fs16.readFileSync(oldFilePath, "utf-8");
|
|
3094
4545
|
const parsed = JSON.parse(content);
|
|
3095
4546
|
if (!Array.isArray(parsed)) {
|
|
3096
4547
|
return {
|
|
@@ -3141,8 +4592,8 @@ async function check(options) {
|
|
|
3141
4592
|
}
|
|
3142
4593
|
|
|
3143
4594
|
// src/commands/migration/versions/v001_capability/json-migrator/index.ts
|
|
3144
|
-
import
|
|
3145
|
-
import
|
|
4595
|
+
import fs17 from "fs";
|
|
4596
|
+
import path15 from "path";
|
|
3146
4597
|
|
|
3147
4598
|
// src/commands/migration/versions/v001_capability/mapping.ts
|
|
3148
4599
|
var DEFAULT_PLUGIN_VERSION = "1.0.0";
|
|
@@ -3372,18 +4823,18 @@ function transformCapabilities(oldCapabilities) {
|
|
|
3372
4823
|
// src/commands/migration/versions/v001_capability/json-migrator/index.ts
|
|
3373
4824
|
function loadExistingCapabilities() {
|
|
3374
4825
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
3375
|
-
if (!
|
|
4826
|
+
if (!fs17.existsSync(capabilitiesDir)) {
|
|
3376
4827
|
return [];
|
|
3377
4828
|
}
|
|
3378
|
-
const files =
|
|
4829
|
+
const files = fs17.readdirSync(capabilitiesDir);
|
|
3379
4830
|
const capabilities = [];
|
|
3380
4831
|
for (const file of files) {
|
|
3381
4832
|
if (file === "capabilities.json" || !file.endsWith(".json")) {
|
|
3382
4833
|
continue;
|
|
3383
4834
|
}
|
|
3384
4835
|
try {
|
|
3385
|
-
const filePath =
|
|
3386
|
-
const content =
|
|
4836
|
+
const filePath = path15.join(capabilitiesDir, file);
|
|
4837
|
+
const content = fs17.readFileSync(filePath, "utf-8");
|
|
3387
4838
|
const capability = JSON.parse(content);
|
|
3388
4839
|
if (capability.id && capability.pluginKey) {
|
|
3389
4840
|
capabilities.push(capability);
|
|
@@ -3441,9 +4892,9 @@ async function migrateJsonFiles(options) {
|
|
|
3441
4892
|
}
|
|
3442
4893
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
3443
4894
|
for (const cap of newCapabilities) {
|
|
3444
|
-
const filePath =
|
|
4895
|
+
const filePath = path15.join(capabilitiesDir, `${cap.id}.json`);
|
|
3445
4896
|
const content = JSON.stringify(cap, null, 2);
|
|
3446
|
-
|
|
4897
|
+
fs17.writeFileSync(filePath, content, "utf-8");
|
|
3447
4898
|
console.log(` \u2713 Created: ${cap.id}.json`);
|
|
3448
4899
|
}
|
|
3449
4900
|
return {
|
|
@@ -3455,11 +4906,11 @@ async function migrateJsonFiles(options) {
|
|
|
3455
4906
|
}
|
|
3456
4907
|
|
|
3457
4908
|
// src/commands/migration/versions/v001_capability/plugin-installer/detector.ts
|
|
3458
|
-
import
|
|
4909
|
+
import fs18 from "fs";
|
|
3459
4910
|
function isPluginInstalled2(pluginKey) {
|
|
3460
4911
|
const actionPlugins = readActionPlugins();
|
|
3461
4912
|
const manifestPath = getPluginManifestPath2(pluginKey);
|
|
3462
|
-
return
|
|
4913
|
+
return fs18.existsSync(manifestPath) && !!actionPlugins[pluginKey];
|
|
3463
4914
|
}
|
|
3464
4915
|
function detectPluginsToInstall(capabilities) {
|
|
3465
4916
|
const pluginKeys = /* @__PURE__ */ new Set();
|
|
@@ -3535,12 +4986,12 @@ async function installPlugins(capabilities, options) {
|
|
|
3535
4986
|
}
|
|
3536
4987
|
|
|
3537
4988
|
// src/commands/migration/versions/v001_capability/code-migrator/index.ts
|
|
3538
|
-
import
|
|
4989
|
+
import path17 from "path";
|
|
3539
4990
|
import { Project as Project3 } from "ts-morph";
|
|
3540
4991
|
|
|
3541
4992
|
// src/commands/migration/versions/v001_capability/code-migrator/scanner.ts
|
|
3542
|
-
import
|
|
3543
|
-
import
|
|
4993
|
+
import fs19 from "fs";
|
|
4994
|
+
import path16 from "path";
|
|
3544
4995
|
var EXCLUDED_DIRS = [
|
|
3545
4996
|
"node_modules",
|
|
3546
4997
|
"dist",
|
|
@@ -3555,9 +5006,9 @@ var EXCLUDED_PATTERNS = [
|
|
|
3555
5006
|
/\.d\.ts$/
|
|
3556
5007
|
];
|
|
3557
5008
|
function scanDirectory(dir, files = []) {
|
|
3558
|
-
const entries =
|
|
5009
|
+
const entries = fs19.readdirSync(dir, { withFileTypes: true });
|
|
3559
5010
|
for (const entry of entries) {
|
|
3560
|
-
const fullPath =
|
|
5011
|
+
const fullPath = path16.join(dir, entry.name);
|
|
3561
5012
|
if (entry.isDirectory()) {
|
|
3562
5013
|
if (EXCLUDED_DIRS.includes(entry.name)) {
|
|
3563
5014
|
continue;
|
|
@@ -3573,14 +5024,14 @@ function scanDirectory(dir, files = []) {
|
|
|
3573
5024
|
return files;
|
|
3574
5025
|
}
|
|
3575
5026
|
function scanServerFiles() {
|
|
3576
|
-
const serverDir =
|
|
3577
|
-
if (!
|
|
5027
|
+
const serverDir = path16.join(getProjectRoot3(), "server");
|
|
5028
|
+
if (!fs19.existsSync(serverDir)) {
|
|
3578
5029
|
return [];
|
|
3579
5030
|
}
|
|
3580
5031
|
return scanDirectory(serverDir);
|
|
3581
5032
|
}
|
|
3582
5033
|
function hasCapabilityImport(filePath) {
|
|
3583
|
-
const content =
|
|
5034
|
+
const content = fs19.readFileSync(filePath, "utf-8");
|
|
3584
5035
|
return /import\s+.*from\s+['"][^'"]*capabilities[^'"]*['"]/.test(content);
|
|
3585
5036
|
}
|
|
3586
5037
|
function scanFilesToMigrate() {
|
|
@@ -3957,7 +5408,7 @@ function analyzeFile(project, filePath, actionNameMap) {
|
|
|
3957
5408
|
const callSites = analyzeCallSites(sourceFile, imports);
|
|
3958
5409
|
const classInfo = analyzeClass(sourceFile);
|
|
3959
5410
|
const { canMigrate, reason } = canAutoMigrate(classInfo);
|
|
3960
|
-
const relativePath =
|
|
5411
|
+
const relativePath = path17.relative(getProjectRoot3(), filePath);
|
|
3961
5412
|
return {
|
|
3962
5413
|
filePath: relativePath,
|
|
3963
5414
|
imports,
|
|
@@ -3968,7 +5419,7 @@ function analyzeFile(project, filePath, actionNameMap) {
|
|
|
3968
5419
|
};
|
|
3969
5420
|
}
|
|
3970
5421
|
function migrateFile(project, analysis, dryRun) {
|
|
3971
|
-
const absolutePath =
|
|
5422
|
+
const absolutePath = path17.join(getProjectRoot3(), analysis.filePath);
|
|
3972
5423
|
if (!analysis.canAutoMigrate) {
|
|
3973
5424
|
return {
|
|
3974
5425
|
filePath: analysis.filePath,
|
|
@@ -4071,17 +5522,17 @@ function getSuggestion(analysis) {
|
|
|
4071
5522
|
}
|
|
4072
5523
|
|
|
4073
5524
|
// src/commands/migration/versions/v001_capability/cleanup.ts
|
|
4074
|
-
import
|
|
4075
|
-
import
|
|
5525
|
+
import fs20 from "fs";
|
|
5526
|
+
import path18 from "path";
|
|
4076
5527
|
function cleanupOldFiles(capabilities, dryRun) {
|
|
4077
5528
|
const deletedFiles = [];
|
|
4078
5529
|
const errors = [];
|
|
4079
5530
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
4080
|
-
const oldJsonPath =
|
|
4081
|
-
if (
|
|
5531
|
+
const oldJsonPath = path18.join(capabilitiesDir, "capabilities.json");
|
|
5532
|
+
if (fs20.existsSync(oldJsonPath)) {
|
|
4082
5533
|
try {
|
|
4083
5534
|
if (!dryRun) {
|
|
4084
|
-
|
|
5535
|
+
fs20.unlinkSync(oldJsonPath);
|
|
4085
5536
|
}
|
|
4086
5537
|
deletedFiles.push("capabilities.json");
|
|
4087
5538
|
} catch (error) {
|
|
@@ -4089,11 +5540,11 @@ function cleanupOldFiles(capabilities, dryRun) {
|
|
|
4089
5540
|
}
|
|
4090
5541
|
}
|
|
4091
5542
|
for (const cap of capabilities) {
|
|
4092
|
-
const tsFilePath =
|
|
4093
|
-
if (
|
|
5543
|
+
const tsFilePath = path18.join(capabilitiesDir, `${cap.id}.ts`);
|
|
5544
|
+
if (fs20.existsSync(tsFilePath)) {
|
|
4094
5545
|
try {
|
|
4095
5546
|
if (!dryRun) {
|
|
4096
|
-
|
|
5547
|
+
fs20.unlinkSync(tsFilePath);
|
|
4097
5548
|
}
|
|
4098
5549
|
deletedFiles.push(`${cap.id}.ts`);
|
|
4099
5550
|
} catch (error) {
|
|
@@ -4109,8 +5560,8 @@ function cleanupOldFiles(capabilities, dryRun) {
|
|
|
4109
5560
|
}
|
|
4110
5561
|
|
|
4111
5562
|
// src/commands/migration/versions/v001_capability/report-generator.ts
|
|
4112
|
-
import
|
|
4113
|
-
import
|
|
5563
|
+
import fs21 from "fs";
|
|
5564
|
+
import path19 from "path";
|
|
4114
5565
|
var REPORT_FILE = "capability-migration-report.md";
|
|
4115
5566
|
function printSummary(result) {
|
|
4116
5567
|
const { jsonMigration, pluginInstallation, codeMigration, cleanup } = result;
|
|
@@ -4273,15 +5724,15 @@ async function generateReport(result) {
|
|
|
4273
5724
|
}
|
|
4274
5725
|
lines.push("");
|
|
4275
5726
|
const logDir = process.env.LOG_DIR || "logs";
|
|
4276
|
-
if (!
|
|
5727
|
+
if (!fs21.existsSync(logDir)) {
|
|
4277
5728
|
return;
|
|
4278
5729
|
}
|
|
4279
|
-
const reportDir =
|
|
4280
|
-
if (!
|
|
4281
|
-
|
|
5730
|
+
const reportDir = path19.join(logDir, "migration");
|
|
5731
|
+
if (!fs21.existsSync(reportDir)) {
|
|
5732
|
+
fs21.mkdirSync(reportDir, { recursive: true });
|
|
4282
5733
|
}
|
|
4283
|
-
const reportPath =
|
|
4284
|
-
|
|
5734
|
+
const reportPath = path19.join(reportDir, REPORT_FILE);
|
|
5735
|
+
fs21.writeFileSync(reportPath, lines.join("\n"), "utf-8");
|
|
4285
5736
|
console.log(`\u{1F4C4} Report generated: ${reportPath}`);
|
|
4286
5737
|
}
|
|
4287
5738
|
|
|
@@ -4458,7 +5909,7 @@ function buildResult(jsonMigration, pluginInstallation, codeMigration, cleanup)
|
|
|
4458
5909
|
}
|
|
4459
5910
|
|
|
4460
5911
|
// src/commands/migration/versions/v001_capability/run.ts
|
|
4461
|
-
async function
|
|
5912
|
+
async function run5(options) {
|
|
4462
5913
|
try {
|
|
4463
5914
|
const migrationOptions = {
|
|
4464
5915
|
dryRun: options.dryRun ?? false
|
|
@@ -4523,7 +5974,7 @@ var v001CapabilityMigration = {
|
|
|
4523
5974
|
name: "capability",
|
|
4524
5975
|
description: "Migrate capability configurations from old format (capabilities.json array) to new format (individual JSON files)",
|
|
4525
5976
|
check,
|
|
4526
|
-
run:
|
|
5977
|
+
run: run5
|
|
4527
5978
|
};
|
|
4528
5979
|
|
|
4529
5980
|
// src/commands/migration/versions/index.ts
|
|
@@ -4813,10 +6264,10 @@ var migrationCommand = {
|
|
|
4813
6264
|
};
|
|
4814
6265
|
|
|
4815
6266
|
// src/commands/read-logs/index.ts
|
|
4816
|
-
import
|
|
6267
|
+
import path20 from "path";
|
|
4817
6268
|
|
|
4818
6269
|
// src/commands/read-logs/std-utils.ts
|
|
4819
|
-
import
|
|
6270
|
+
import fs22 from "fs";
|
|
4820
6271
|
function formatStdPrefixTime(localTime) {
|
|
4821
6272
|
const match = localTime.match(/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/);
|
|
4822
6273
|
if (!match) return localTime;
|
|
@@ -4846,11 +6297,11 @@ function stripPrefixFromStdLine(line) {
|
|
|
4846
6297
|
return `[${time}] ${content}`;
|
|
4847
6298
|
}
|
|
4848
6299
|
function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarker) {
|
|
4849
|
-
const stat =
|
|
6300
|
+
const stat = fs22.statSync(filePath);
|
|
4850
6301
|
if (stat.size === 0) {
|
|
4851
6302
|
return { lines: [], markerFound: false, totalLinesCount: 0 };
|
|
4852
6303
|
}
|
|
4853
|
-
const fd =
|
|
6304
|
+
const fd = fs22.openSync(filePath, "r");
|
|
4854
6305
|
const chunkSize = 64 * 1024;
|
|
4855
6306
|
let position = stat.size;
|
|
4856
6307
|
let remainder = "";
|
|
@@ -4864,7 +6315,7 @@ function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarke
|
|
|
4864
6315
|
const length = Math.min(chunkSize, position);
|
|
4865
6316
|
position -= length;
|
|
4866
6317
|
const buffer = Buffer.alloc(length);
|
|
4867
|
-
|
|
6318
|
+
fs22.readSync(fd, buffer, 0, length, position);
|
|
4868
6319
|
let chunk = buffer.toString("utf8");
|
|
4869
6320
|
if (remainder) {
|
|
4870
6321
|
chunk += remainder;
|
|
@@ -4906,7 +6357,7 @@ function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarke
|
|
|
4906
6357
|
}
|
|
4907
6358
|
}
|
|
4908
6359
|
} finally {
|
|
4909
|
-
|
|
6360
|
+
fs22.closeSync(fd);
|
|
4910
6361
|
}
|
|
4911
6362
|
return { lines: collected.reverse(), markerFound, totalLinesCount };
|
|
4912
6363
|
}
|
|
@@ -4927,21 +6378,21 @@ function readServerStdSegment(filePath, maxLines, offset) {
|
|
|
4927
6378
|
}
|
|
4928
6379
|
|
|
4929
6380
|
// src/commands/read-logs/tail.ts
|
|
4930
|
-
import
|
|
6381
|
+
import fs23 from "fs";
|
|
4931
6382
|
function fileExists(filePath) {
|
|
4932
6383
|
try {
|
|
4933
|
-
|
|
6384
|
+
fs23.accessSync(filePath, fs23.constants.F_OK | fs23.constants.R_OK);
|
|
4934
6385
|
return true;
|
|
4935
6386
|
} catch {
|
|
4936
6387
|
return false;
|
|
4937
6388
|
}
|
|
4938
6389
|
}
|
|
4939
6390
|
function readFileTailLines(filePath, maxLines) {
|
|
4940
|
-
const stat =
|
|
6391
|
+
const stat = fs23.statSync(filePath);
|
|
4941
6392
|
if (stat.size === 0) {
|
|
4942
6393
|
return [];
|
|
4943
6394
|
}
|
|
4944
|
-
const fd =
|
|
6395
|
+
const fd = fs23.openSync(filePath, "r");
|
|
4945
6396
|
const chunkSize = 64 * 1024;
|
|
4946
6397
|
const chunks = [];
|
|
4947
6398
|
let position = stat.size;
|
|
@@ -4951,13 +6402,13 @@ function readFileTailLines(filePath, maxLines) {
|
|
|
4951
6402
|
const length = Math.min(chunkSize, position);
|
|
4952
6403
|
position -= length;
|
|
4953
6404
|
const buffer = Buffer.alloc(length);
|
|
4954
|
-
|
|
6405
|
+
fs23.readSync(fd, buffer, 0, length, position);
|
|
4955
6406
|
chunks.unshift(buffer.toString("utf8"));
|
|
4956
6407
|
const chunkLines = buffer.toString("utf8").split("\n").length - 1;
|
|
4957
6408
|
collectedLines += chunkLines;
|
|
4958
6409
|
}
|
|
4959
6410
|
} finally {
|
|
4960
|
-
|
|
6411
|
+
fs23.closeSync(fd);
|
|
4961
6412
|
}
|
|
4962
6413
|
const content = chunks.join("");
|
|
4963
6414
|
const allLines = content.split("\n");
|
|
@@ -4973,11 +6424,11 @@ function readFileTailLines(filePath, maxLines) {
|
|
|
4973
6424
|
return allLines.slice(allLines.length - maxLines);
|
|
4974
6425
|
}
|
|
4975
6426
|
function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
|
|
4976
|
-
const stat =
|
|
6427
|
+
const stat = fs23.statSync(filePath);
|
|
4977
6428
|
if (stat.size === 0) {
|
|
4978
6429
|
return { lines: [], totalLinesCount: 0 };
|
|
4979
6430
|
}
|
|
4980
|
-
const fd =
|
|
6431
|
+
const fd = fs23.openSync(filePath, "r");
|
|
4981
6432
|
const chunkSize = 64 * 1024;
|
|
4982
6433
|
let position = stat.size;
|
|
4983
6434
|
let remainder = "";
|
|
@@ -4989,7 +6440,7 @@ function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
|
|
|
4989
6440
|
const length = Math.min(chunkSize, position);
|
|
4990
6441
|
position -= length;
|
|
4991
6442
|
const buffer = Buffer.alloc(length);
|
|
4992
|
-
|
|
6443
|
+
fs23.readSync(fd, buffer, 0, length, position);
|
|
4993
6444
|
let chunk = buffer.toString("utf8");
|
|
4994
6445
|
if (remainder) {
|
|
4995
6446
|
chunk += remainder;
|
|
@@ -5020,7 +6471,7 @@ function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
|
|
|
5020
6471
|
}
|
|
5021
6472
|
}
|
|
5022
6473
|
} finally {
|
|
5023
|
-
|
|
6474
|
+
fs23.closeSync(fd);
|
|
5024
6475
|
}
|
|
5025
6476
|
return { lines: collected.reverse(), totalLinesCount };
|
|
5026
6477
|
}
|
|
@@ -5033,13 +6484,19 @@ function readClientStdSegment(filePath, maxLines, offset) {
|
|
|
5033
6484
|
function extractClientStdSegment(lines, maxLines, offset) {
|
|
5034
6485
|
const bodyLines = lines.map(stripPrefixFromStdLine);
|
|
5035
6486
|
const hotStartMarkers = [
|
|
6487
|
+
// Webpack markers
|
|
5036
6488
|
/file change detected\..*incremental compilation/i,
|
|
5037
6489
|
/starting incremental compilation/i,
|
|
5038
6490
|
/starting compilation/i,
|
|
5039
6491
|
/\bcompiling\b/i,
|
|
5040
|
-
/\brecompil/i
|
|
6492
|
+
/\brecompil/i,
|
|
6493
|
+
// Vite markers - format: "3:11:46 PM [vite] (client) hmr update ..."
|
|
6494
|
+
/VITE v[\d.]+.*ready/i,
|
|
6495
|
+
/\[vite\].*page reload/i,
|
|
6496
|
+
/\[vite\].*hmr update/i
|
|
5041
6497
|
];
|
|
5042
6498
|
const hotEndMarkers = [
|
|
6499
|
+
// Webpack markers
|
|
5043
6500
|
/file change detected\..*incremental compilation/i,
|
|
5044
6501
|
/\bwebpack compiled\b/i,
|
|
5045
6502
|
/compiled successfully/i,
|
|
@@ -5050,14 +6507,25 @@ function extractClientStdSegment(lines, maxLines, offset) {
|
|
|
5050
6507
|
/\bhmr\b/i,
|
|
5051
6508
|
/hot update/i,
|
|
5052
6509
|
/\bhot reload\b/i,
|
|
5053
|
-
/\bhmr update\b/i
|
|
6510
|
+
/\bhmr update\b/i,
|
|
6511
|
+
// Vite markers
|
|
6512
|
+
/VITE v[\d.]+.*ready/i,
|
|
6513
|
+
/\[vite\].*page reload/i,
|
|
6514
|
+
/\[vite\].*hmr update/i,
|
|
6515
|
+
/\[vite\].*hmr invalidate/i,
|
|
6516
|
+
/\[vite\].*internal server error/i,
|
|
6517
|
+
/\[vite\].*pre-transform error/i
|
|
5054
6518
|
];
|
|
5055
6519
|
const compiledMarkers = [
|
|
6520
|
+
// Webpack markers
|
|
5056
6521
|
/\bwebpack compiled\b/i,
|
|
5057
6522
|
/compiled successfully/i,
|
|
5058
6523
|
/compiled with warnings/i,
|
|
5059
6524
|
/compiled with errors/i,
|
|
5060
|
-
/failed to compile/i
|
|
6525
|
+
/failed to compile/i,
|
|
6526
|
+
// Vite markers
|
|
6527
|
+
/VITE v[\d.]+.*ready/i,
|
|
6528
|
+
/\[vite\].*hmr update/i
|
|
5061
6529
|
];
|
|
5062
6530
|
let startIndex = -1;
|
|
5063
6531
|
for (let i = bodyLines.length - 1; i >= 0; i -= 1) {
|
|
@@ -5110,19 +6578,42 @@ function extractClientStdSegment(lines, maxLines, offset) {
|
|
|
5110
6578
|
}
|
|
5111
6579
|
const segment = startIndex === -1 ? bodyLines : bodyLines.slice(startIndex);
|
|
5112
6580
|
if (segment.length === 0) {
|
|
5113
|
-
return { lines: [], totalLinesCount: 0 };
|
|
6581
|
+
return { lines: [], totalLinesCount: 0, allLines: [] };
|
|
5114
6582
|
}
|
|
5115
6583
|
const totalLinesCount = segment.length;
|
|
5116
6584
|
const endExclusive = Math.max(0, segment.length - offset);
|
|
5117
6585
|
const start = Math.max(0, endExclusive - maxLines);
|
|
5118
6586
|
return {
|
|
5119
6587
|
lines: segment.slice(start, endExclusive),
|
|
5120
|
-
totalLinesCount
|
|
6588
|
+
totalLinesCount,
|
|
6589
|
+
allLines: segment
|
|
6590
|
+
};
|
|
6591
|
+
}
|
|
6592
|
+
|
|
6593
|
+
// src/commands/read-logs/dev.ts
|
|
6594
|
+
function readDevSegment(filePath, maxLines, offset) {
|
|
6595
|
+
const marker = (line) => {
|
|
6596
|
+
if (!line) return false;
|
|
6597
|
+
if (line.includes("Dev session started")) return true;
|
|
6598
|
+
return false;
|
|
6599
|
+
};
|
|
6600
|
+
const segment = readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, marker);
|
|
6601
|
+
return { lines: segment.lines, totalLinesCount: segment.totalLinesCount };
|
|
6602
|
+
}
|
|
6603
|
+
|
|
6604
|
+
// src/commands/read-logs/dev-std.ts
|
|
6605
|
+
function readDevStdSegment(filePath, maxLines, offset) {
|
|
6606
|
+
const marker = (line) => {
|
|
6607
|
+
if (!line) return false;
|
|
6608
|
+
if (line.includes("Dev session started")) return true;
|
|
6609
|
+
return false;
|
|
5121
6610
|
};
|
|
6611
|
+
const segment = readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, marker);
|
|
6612
|
+
return { lines: segment.lines, totalLinesCount: segment.totalLinesCount };
|
|
5122
6613
|
}
|
|
5123
6614
|
|
|
5124
6615
|
// src/commands/read-logs/json-lines.ts
|
|
5125
|
-
import
|
|
6616
|
+
import fs24 from "fs";
|
|
5126
6617
|
function normalizePid(value) {
|
|
5127
6618
|
if (typeof value === "number") {
|
|
5128
6619
|
return String(value);
|
|
@@ -5173,11 +6664,11 @@ function buildWantedLevelSet(levels) {
|
|
|
5173
6664
|
return set.size > 0 ? set : null;
|
|
5174
6665
|
}
|
|
5175
6666
|
function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
|
|
5176
|
-
const stat =
|
|
6667
|
+
const stat = fs24.statSync(filePath);
|
|
5177
6668
|
if (stat.size === 0) {
|
|
5178
6669
|
return { lines: [], totalLinesCount: 0 };
|
|
5179
6670
|
}
|
|
5180
|
-
const fd =
|
|
6671
|
+
const fd = fs24.openSync(filePath, "r");
|
|
5181
6672
|
const chunkSize = 64 * 1024;
|
|
5182
6673
|
let position = stat.size;
|
|
5183
6674
|
let remainder = "";
|
|
@@ -5192,7 +6683,7 @@ function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
|
|
|
5192
6683
|
const length = Math.min(chunkSize, position);
|
|
5193
6684
|
position -= length;
|
|
5194
6685
|
const buffer = Buffer.alloc(length);
|
|
5195
|
-
|
|
6686
|
+
fs24.readSync(fd, buffer, 0, length, position);
|
|
5196
6687
|
let chunk = buffer.toString("utf8");
|
|
5197
6688
|
if (remainder) {
|
|
5198
6689
|
chunk += remainder;
|
|
@@ -5254,7 +6745,7 @@ function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
|
|
|
5254
6745
|
}
|
|
5255
6746
|
}
|
|
5256
6747
|
} finally {
|
|
5257
|
-
|
|
6748
|
+
fs24.closeSync(fd);
|
|
5258
6749
|
}
|
|
5259
6750
|
return { lines: collected.reverse(), totalLinesCount };
|
|
5260
6751
|
}
|
|
@@ -5297,11 +6788,11 @@ function extractTraceId(obj) {
|
|
|
5297
6788
|
function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
|
|
5298
6789
|
const wanted = traceId.trim();
|
|
5299
6790
|
if (!wanted) return { lines: [], totalLinesCount: 0 };
|
|
5300
|
-
const stat =
|
|
6791
|
+
const stat = fs24.statSync(filePath);
|
|
5301
6792
|
if (stat.size === 0) {
|
|
5302
6793
|
return { lines: [], totalLinesCount: 0 };
|
|
5303
6794
|
}
|
|
5304
|
-
const fd =
|
|
6795
|
+
const fd = fs24.openSync(filePath, "r");
|
|
5305
6796
|
const chunkSize = 64 * 1024;
|
|
5306
6797
|
let position = stat.size;
|
|
5307
6798
|
let remainder = "";
|
|
@@ -5314,7 +6805,7 @@ function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
|
|
|
5314
6805
|
const length = Math.min(chunkSize, position);
|
|
5315
6806
|
position -= length;
|
|
5316
6807
|
const buffer = Buffer.alloc(length);
|
|
5317
|
-
|
|
6808
|
+
fs24.readSync(fd, buffer, 0, length, position);
|
|
5318
6809
|
let chunk = buffer.toString("utf8");
|
|
5319
6810
|
if (remainder) {
|
|
5320
6811
|
chunk += remainder;
|
|
@@ -5367,7 +6858,7 @@ function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
|
|
|
5367
6858
|
}
|
|
5368
6859
|
}
|
|
5369
6860
|
} finally {
|
|
5370
|
-
|
|
6861
|
+
fs24.closeSync(fd);
|
|
5371
6862
|
}
|
|
5372
6863
|
return { lines: collected.reverse(), totalLinesCount };
|
|
5373
6864
|
}
|
|
@@ -5376,11 +6867,11 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
|
|
|
5376
6867
|
if (!wantedLevelSet) {
|
|
5377
6868
|
return { lines: [], totalLinesCount: 0 };
|
|
5378
6869
|
}
|
|
5379
|
-
const stat =
|
|
6870
|
+
const stat = fs24.statSync(filePath);
|
|
5380
6871
|
if (stat.size === 0) {
|
|
5381
6872
|
return { lines: [], totalLinesCount: 0 };
|
|
5382
6873
|
}
|
|
5383
|
-
const fd =
|
|
6874
|
+
const fd = fs24.openSync(filePath, "r");
|
|
5384
6875
|
const chunkSize = 64 * 1024;
|
|
5385
6876
|
let position = stat.size;
|
|
5386
6877
|
let remainder = "";
|
|
@@ -5392,7 +6883,7 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
|
|
|
5392
6883
|
const length = Math.min(chunkSize, position);
|
|
5393
6884
|
position -= length;
|
|
5394
6885
|
const buffer = Buffer.alloc(length);
|
|
5395
|
-
|
|
6886
|
+
fs24.readSync(fd, buffer, 0, length, position);
|
|
5396
6887
|
let chunk = buffer.toString("utf8");
|
|
5397
6888
|
if (remainder) {
|
|
5398
6889
|
chunk += remainder;
|
|
@@ -5439,13 +6930,13 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
|
|
|
5439
6930
|
}
|
|
5440
6931
|
}
|
|
5441
6932
|
} finally {
|
|
5442
|
-
|
|
6933
|
+
fs24.closeSync(fd);
|
|
5443
6934
|
}
|
|
5444
6935
|
return { lines: collected.reverse(), totalLinesCount };
|
|
5445
6936
|
}
|
|
5446
6937
|
|
|
5447
6938
|
// src/commands/read-logs/index.ts
|
|
5448
|
-
var LOG_TYPES = ["server", "trace", "server-std", "client-std", "browser"];
|
|
6939
|
+
var LOG_TYPES = ["server", "trace", "server-std", "client-std", "dev", "dev-std", "install-dep-std", "browser"];
|
|
5449
6940
|
function normalizeObjectKey(key) {
|
|
5450
6941
|
return key.toLowerCase().replace(/_/g, "");
|
|
5451
6942
|
}
|
|
@@ -5607,6 +7098,15 @@ async function readLatestLogLinesMeta(options) {
|
|
|
5607
7098
|
if (options.type === "client-std") {
|
|
5608
7099
|
return readClientStdSegment(filePath, maxLines, offset);
|
|
5609
7100
|
}
|
|
7101
|
+
if (options.type === "dev") {
|
|
7102
|
+
return readDevSegment(filePath, maxLines, offset);
|
|
7103
|
+
}
|
|
7104
|
+
if (options.type === "dev-std") {
|
|
7105
|
+
return readDevStdSegment(filePath, maxLines, offset);
|
|
7106
|
+
}
|
|
7107
|
+
if (options.type === "install-dep-std") {
|
|
7108
|
+
return readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset);
|
|
7109
|
+
}
|
|
5610
7110
|
const traceId = typeof options.traceId === "string" ? options.traceId.trim() : "";
|
|
5611
7111
|
if (traceId) {
|
|
5612
7112
|
return readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels);
|
|
@@ -5623,12 +7123,21 @@ async function readLatestLogLinesMeta(options) {
|
|
|
5623
7123
|
return { lines: [], totalLinesCount: 0 };
|
|
5624
7124
|
}
|
|
5625
7125
|
async function readLogsJsonResult(options) {
|
|
5626
|
-
const { lines, totalLinesCount } = await readLatestLogLinesMeta(options);
|
|
5627
|
-
if (options.type === "server-std" || options.type === "client-std") {
|
|
7126
|
+
const { lines, totalLinesCount, allLines } = await readLatestLogLinesMeta(options);
|
|
7127
|
+
if (options.type === "server-std" || options.type === "client-std" || options.type === "dev" || options.type === "dev-std" || options.type === "install-dep-std") {
|
|
7128
|
+
const linesForErrorCheck = allLines ?? lines;
|
|
7129
|
+
const stackTracePattern = /\bat\s+\S+\s+\([^)]+:\d+:\d+\)/;
|
|
7130
|
+
const linesToFilter = allLines ?? lines;
|
|
7131
|
+
const filteredLines = linesToFilter.filter((line) => !stackTracePattern.test(line));
|
|
7132
|
+
const maxLines = options.maxLines ?? 30;
|
|
7133
|
+
const offset = options.offset ?? 0;
|
|
7134
|
+
const endExclusive = Math.max(0, filteredLines.length - offset);
|
|
7135
|
+
const start = Math.max(0, endExclusive - maxLines);
|
|
7136
|
+
const paginatedLogs = filteredLines.slice(start, endExclusive);
|
|
5628
7137
|
return {
|
|
5629
|
-
hasError: hasErrorInStdLines(
|
|
5630
|
-
totalLinesCount,
|
|
5631
|
-
logs:
|
|
7138
|
+
hasError: hasErrorInStdLines(linesForErrorCheck),
|
|
7139
|
+
totalLinesCount: filteredLines.length,
|
|
7140
|
+
logs: paginatedLogs
|
|
5632
7141
|
};
|
|
5633
7142
|
}
|
|
5634
7143
|
const logs = [];
|
|
@@ -5655,25 +7164,34 @@ async function readLogsJsonResult(options) {
|
|
|
5655
7164
|
};
|
|
5656
7165
|
}
|
|
5657
7166
|
function resolveLogFilePath(logDir, type) {
|
|
5658
|
-
const base =
|
|
7167
|
+
const base = path20.isAbsolute(logDir) ? logDir : path20.join(process.cwd(), logDir);
|
|
5659
7168
|
if (type === "server") {
|
|
5660
|
-
return
|
|
7169
|
+
return path20.join(base, "server.log");
|
|
5661
7170
|
}
|
|
5662
7171
|
if (type === "trace") {
|
|
5663
|
-
return
|
|
7172
|
+
return path20.join(base, "trace.log");
|
|
5664
7173
|
}
|
|
5665
7174
|
if (type === "server-std") {
|
|
5666
|
-
return
|
|
7175
|
+
return path20.join(base, "server.std.log");
|
|
5667
7176
|
}
|
|
5668
7177
|
if (type === "client-std") {
|
|
5669
|
-
return
|
|
7178
|
+
return path20.join(base, "client.std.log");
|
|
7179
|
+
}
|
|
7180
|
+
if (type === "dev") {
|
|
7181
|
+
return path20.join(base, "dev.log");
|
|
7182
|
+
}
|
|
7183
|
+
if (type === "dev-std") {
|
|
7184
|
+
return path20.join(base, "dev.std.log");
|
|
7185
|
+
}
|
|
7186
|
+
if (type === "install-dep-std") {
|
|
7187
|
+
return path20.join(base, "install-dep.std.log");
|
|
5670
7188
|
}
|
|
5671
7189
|
if (type === "browser") {
|
|
5672
|
-
return
|
|
7190
|
+
return path20.join(base, "browser.log");
|
|
5673
7191
|
}
|
|
5674
7192
|
throw new Error(`Unsupported log type: ${type}`);
|
|
5675
7193
|
}
|
|
5676
|
-
async function
|
|
7194
|
+
async function run6(options) {
|
|
5677
7195
|
const result = await readLogsJsonResult(options);
|
|
5678
7196
|
process.stdout.write(JSON.stringify(result) + "\n");
|
|
5679
7197
|
}
|
|
@@ -5715,7 +7233,7 @@ var readLogsCommand = {
|
|
|
5715
7233
|
const offset = parseNonNegativeInt(rawOptions.offset, "--offset");
|
|
5716
7234
|
const traceId = typeof rawOptions.traceId === "string" ? rawOptions.traceId : void 0;
|
|
5717
7235
|
const levels = parseCommaSeparatedList(rawOptions.level);
|
|
5718
|
-
await
|
|
7236
|
+
await run6({ logDir, type, maxLines, offset, traceId, levels });
|
|
5719
7237
|
} catch (error) {
|
|
5720
7238
|
const message = error instanceof Error ? error.message : String(error);
|
|
5721
7239
|
process.stderr.write(message + "\n");
|
|
@@ -5725,23 +7243,342 @@ var readLogsCommand = {
|
|
|
5725
7243
|
}
|
|
5726
7244
|
};
|
|
5727
7245
|
|
|
7246
|
+
// src/commands/build/types.ts
|
|
7247
|
+
var SCENE_CONFIGS = {
|
|
7248
|
+
pipeline: {
|
|
7249
|
+
name: "pipeline",
|
|
7250
|
+
requiredOptions: ["commitId"],
|
|
7251
|
+
buildRequestBody: (opts) => ({ commitID: opts.commitId })
|
|
7252
|
+
},
|
|
7253
|
+
static: {
|
|
7254
|
+
name: "static",
|
|
7255
|
+
requiredOptions: [],
|
|
7256
|
+
buildRequestBody: () => ({ commitID: "" })
|
|
7257
|
+
}
|
|
7258
|
+
};
|
|
7259
|
+
var UPLOAD_STATIC_DEFAULTS = {
|
|
7260
|
+
staticDir: "shared/static",
|
|
7261
|
+
tosutilPath: "tosutil",
|
|
7262
|
+
endpoint: "tos-cn-beijing.volces.com",
|
|
7263
|
+
region: "cn-beijing"
|
|
7264
|
+
};
|
|
7265
|
+
|
|
7266
|
+
// src/commands/build/api-client.ts
|
|
7267
|
+
async function genArtifactUploadCredential(appId, body) {
|
|
7268
|
+
const client = getHttpClient();
|
|
7269
|
+
const url = `/v1/app/${appId}/pipeline/gen_artifact_upload_credential`;
|
|
7270
|
+
const response = await client.post(url, body);
|
|
7271
|
+
if (!response.ok || response.status !== 200) {
|
|
7272
|
+
throw new Error(
|
|
7273
|
+
`gen_artifact_upload_credential \u8BF7\u6C42\u5931\u8D25: ${response.status} ${response.statusText}`
|
|
7274
|
+
);
|
|
7275
|
+
}
|
|
7276
|
+
return response.json();
|
|
7277
|
+
}
|
|
7278
|
+
async function getDefaultBucketId(appId) {
|
|
7279
|
+
const client = getHttpClient();
|
|
7280
|
+
const url = `/v1/app/${appId}/storage/inner/staticBucket`;
|
|
7281
|
+
const response = await client.post(url, {});
|
|
7282
|
+
if (!response.ok || response.status !== 200) {
|
|
7283
|
+
throw new Error(
|
|
7284
|
+
`getOrCreateStaticBucket \u8BF7\u6C42\u5931\u8D25: ${response.status} ${response.statusText}`
|
|
7285
|
+
);
|
|
7286
|
+
}
|
|
7287
|
+
const data = await response.json();
|
|
7288
|
+
if (data.status_code !== "0") {
|
|
7289
|
+
throw new Error(`getOrCreateStaticBucket \u8FD4\u56DE\u5F02\u5E38, status_code: ${data.status_code}`);
|
|
7290
|
+
}
|
|
7291
|
+
const bucketId = data?.data?.bucketID;
|
|
7292
|
+
if (!bucketId) {
|
|
7293
|
+
throw new Error(`\u672A\u627E\u5230\u5E94\u7528 ${appId} \u7684\u9759\u6001\u8D44\u6E90\u5B58\u50A8\u6876`);
|
|
7294
|
+
}
|
|
7295
|
+
return bucketId;
|
|
7296
|
+
}
|
|
7297
|
+
async function preUploadStaticAttachment(appId, bucketId) {
|
|
7298
|
+
const client = getHttpClient();
|
|
7299
|
+
const url = `/v1/app/${appId}/storage/bucket/${bucketId}/preUploadStatic`;
|
|
7300
|
+
const response = await client.post(url, {});
|
|
7301
|
+
if (!response.ok || response.status !== 200) {
|
|
7302
|
+
throw new Error(
|
|
7303
|
+
`preUploadStatic \u8BF7\u6C42\u5931\u8D25: ${response.status} ${response.statusText}`
|
|
7304
|
+
);
|
|
7305
|
+
}
|
|
7306
|
+
return response.json();
|
|
7307
|
+
}
|
|
7308
|
+
async function uploadStaticAttachmentCallback(appId, bucketId, body) {
|
|
7309
|
+
const client = getHttpClient();
|
|
7310
|
+
const url = `/v1/app/${appId}/storage/bucket/${bucketId}/object/callbackStatic`;
|
|
7311
|
+
const response = await client.post(url, body);
|
|
7312
|
+
if (!response.ok || response.status !== 200) {
|
|
7313
|
+
throw new Error(
|
|
7314
|
+
`callbackStatic \u8BF7\u6C42\u5931\u8D25: ${response.status} ${response.statusText}`
|
|
7315
|
+
);
|
|
7316
|
+
}
|
|
7317
|
+
return response.json();
|
|
7318
|
+
}
|
|
7319
|
+
|
|
7320
|
+
// src/commands/build/get-token.handler.ts
|
|
7321
|
+
async function getToken(options) {
|
|
7322
|
+
try {
|
|
7323
|
+
const sceneConfig = SCENE_CONFIGS[options.scene];
|
|
7324
|
+
if (!sceneConfig) {
|
|
7325
|
+
const available = Object.keys(SCENE_CONFIGS).join(", ");
|
|
7326
|
+
console.error(
|
|
7327
|
+
`[build] Error: invalid scene "${options.scene}". Available scenes: ${available}`
|
|
7328
|
+
);
|
|
7329
|
+
process.exit(1);
|
|
7330
|
+
}
|
|
7331
|
+
for (const key of sceneConfig.requiredOptions) {
|
|
7332
|
+
if (!options[key]) {
|
|
7333
|
+
console.error(
|
|
7334
|
+
`[build] Error: --${camelToKebab(key)} is required for scene "${options.scene}"`
|
|
7335
|
+
);
|
|
7336
|
+
process.exit(1);
|
|
7337
|
+
}
|
|
7338
|
+
}
|
|
7339
|
+
const body = sceneConfig.buildRequestBody(options);
|
|
7340
|
+
const response = await genArtifactUploadCredential(options.appId, body);
|
|
7341
|
+
console.log(JSON.stringify(response));
|
|
7342
|
+
} catch (error) {
|
|
7343
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7344
|
+
console.error(`[build] Error: ${message}`);
|
|
7345
|
+
process.exit(1);
|
|
7346
|
+
}
|
|
7347
|
+
}
|
|
7348
|
+
function camelToKebab(str) {
|
|
7349
|
+
return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
7350
|
+
}
|
|
7351
|
+
|
|
7352
|
+
// src/commands/build/upload-static.handler.ts
|
|
7353
|
+
import * as fs25 from "fs";
|
|
7354
|
+
import * as path21 from "path";
|
|
7355
|
+
import { execFileSync } from "child_process";
|
|
7356
|
+
function readCredentialsFromEnv() {
|
|
7357
|
+
const uploadPrefix = process.env.STATIC_UPLOAD_PREFIX;
|
|
7358
|
+
const uploadID = process.env.STATIC_UPLOAD_ID;
|
|
7359
|
+
const bucketId = process.env.STATIC_UPLOAD_BUCKET_ID;
|
|
7360
|
+
const accessKeyID = process.env.STATIC_UPLOAD_AK;
|
|
7361
|
+
const secretAccessKey = process.env.STATIC_UPLOAD_SK;
|
|
7362
|
+
const sessionToken = process.env.STATIC_UPLOAD_TOKEN;
|
|
7363
|
+
if (!uploadPrefix || !uploadID || !bucketId || !accessKeyID || !secretAccessKey || !sessionToken) {
|
|
7364
|
+
return null;
|
|
7365
|
+
}
|
|
7366
|
+
return { uploadPrefix, uploadID, bucketId, accessKeyID, secretAccessKey, sessionToken };
|
|
7367
|
+
}
|
|
7368
|
+
var LOG_PREFIX = "[upload-static]";
|
|
7369
|
+
async function uploadStatic(options) {
|
|
7370
|
+
try {
|
|
7371
|
+
const {
|
|
7372
|
+
appId,
|
|
7373
|
+
staticDir = UPLOAD_STATIC_DEFAULTS.staticDir,
|
|
7374
|
+
tosutilPath = UPLOAD_STATIC_DEFAULTS.tosutilPath,
|
|
7375
|
+
endpoint = UPLOAD_STATIC_DEFAULTS.endpoint,
|
|
7376
|
+
region = UPLOAD_STATIC_DEFAULTS.region
|
|
7377
|
+
} = options;
|
|
7378
|
+
const resolvedStaticDir = path21.resolve(staticDir);
|
|
7379
|
+
if (!fs25.existsSync(resolvedStaticDir)) {
|
|
7380
|
+
console.error(`${LOG_PREFIX} \u76EE\u5F55\u4E0D\u5B58\u5728: ${resolvedStaticDir}\uFF0C\u8DF3\u8FC7\u4E0A\u4F20`);
|
|
7381
|
+
return;
|
|
7382
|
+
}
|
|
7383
|
+
if (isDirEmpty(resolvedStaticDir)) {
|
|
7384
|
+
console.error(`${LOG_PREFIX} \u76EE\u5F55\u4E3A\u7A7A: ${resolvedStaticDir}\uFF0C\u8DF3\u8FC7\u4E0A\u4F20`);
|
|
7385
|
+
return;
|
|
7386
|
+
}
|
|
7387
|
+
const resolvedTosutil = resolveTosutilPath(tosutilPath);
|
|
7388
|
+
if (!resolvedTosutil) {
|
|
7389
|
+
throw new Error(
|
|
7390
|
+
`tosutil \u4E0D\u5B58\u5728: ${tosutilPath}\u3002\u8BF7\u786E\u4FDD tosutil \u5DF2\u5B89\u88C5\u5728\u7CFB\u7EDF PATH \u4E2D\u6216\u901A\u8FC7 --tosutil-path \u6307\u5B9A\u8DEF\u5F84\u3002`
|
|
7391
|
+
);
|
|
7392
|
+
}
|
|
7393
|
+
let uploadPrefix;
|
|
7394
|
+
let uploadID;
|
|
7395
|
+
let bucketId;
|
|
7396
|
+
let accessKeyID;
|
|
7397
|
+
let secretAccessKey;
|
|
7398
|
+
let sessionToken;
|
|
7399
|
+
const envCredentials = readCredentialsFromEnv();
|
|
7400
|
+
if (envCredentials) {
|
|
7401
|
+
console.error(`${LOG_PREFIX} \u4F7F\u7528\u73AF\u5883\u53D8\u91CF\u4E2D\u7684\u4E0A\u4F20\u51ED\u8BC1`);
|
|
7402
|
+
({ uploadPrefix, uploadID, bucketId, accessKeyID, secretAccessKey, sessionToken } = envCredentials);
|
|
7403
|
+
} else {
|
|
7404
|
+
console.error(`${LOG_PREFIX} \u73AF\u5883\u53D8\u91CF\u672A\u8BBE\u7F6E\uFF0C\u8C03\u7528 preUploadStatic...`);
|
|
7405
|
+
bucketId = await resolveBucketId(appId);
|
|
7406
|
+
const preUploadResp = await fetchPreUpload(appId, bucketId);
|
|
7407
|
+
const { uploadCredential } = preUploadResp.data;
|
|
7408
|
+
({ uploadPrefix, uploadID } = preUploadResp.data);
|
|
7409
|
+
({ AccessKeyID: accessKeyID, SecretAccessKey: secretAccessKey, SessionToken: sessionToken } = uploadCredential);
|
|
7410
|
+
}
|
|
7411
|
+
console.error(`${LOG_PREFIX} \u4E0A\u4F20\u76EE\u6807: ${uploadPrefix}`);
|
|
7412
|
+
console.error(`${LOG_PREFIX} \u914D\u7F6E tosutil...`);
|
|
7413
|
+
configureTosutil(resolvedTosutil, {
|
|
7414
|
+
endpoint,
|
|
7415
|
+
region,
|
|
7416
|
+
accessKeyID,
|
|
7417
|
+
secretAccessKey,
|
|
7418
|
+
sessionToken
|
|
7419
|
+
});
|
|
7420
|
+
console.error(`${LOG_PREFIX} \u4E0A\u4F20 ${resolvedStaticDir} -> ${uploadPrefix}`);
|
|
7421
|
+
uploadToTos(resolvedTosutil, resolvedStaticDir, uploadPrefix);
|
|
7422
|
+
console.error(`${LOG_PREFIX} tosutil \u4E0A\u4F20\u5B8C\u6210`);
|
|
7423
|
+
console.error(`${LOG_PREFIX} \u8C03\u7528 callbackStatic (uploadID: ${uploadID})...`);
|
|
7424
|
+
const callbackResp = await uploadStaticAttachmentCallback(appId, bucketId, { uploadID });
|
|
7425
|
+
if (callbackResp.status_code !== "0") {
|
|
7426
|
+
throw new Error(`callbackStatic \u8FD4\u56DE\u5F02\u5E38, status_code: ${callbackResp.status_code}`);
|
|
7427
|
+
}
|
|
7428
|
+
const attachments = callbackResp.data?.attachments || [];
|
|
7429
|
+
console.error(`${LOG_PREFIX} \u4E0A\u4F20\u5B8C\u6210\uFF0C\u5171 ${attachments.length} \u4E2A\u6587\u4EF6`);
|
|
7430
|
+
console.log(JSON.stringify(callbackResp));
|
|
7431
|
+
} catch (error) {
|
|
7432
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7433
|
+
console.error(`${LOG_PREFIX} Error: ${message}`);
|
|
7434
|
+
process.exit(1);
|
|
7435
|
+
}
|
|
7436
|
+
}
|
|
7437
|
+
function resolveTosutilPath(tosutilPath) {
|
|
7438
|
+
if (path21.isAbsolute(tosutilPath)) {
|
|
7439
|
+
return fs25.existsSync(tosutilPath) ? tosutilPath : null;
|
|
7440
|
+
}
|
|
7441
|
+
try {
|
|
7442
|
+
const resolved = execFileSync("which", [tosutilPath], { encoding: "utf-8" }).trim();
|
|
7443
|
+
return resolved || null;
|
|
7444
|
+
} catch {
|
|
7445
|
+
return null;
|
|
7446
|
+
}
|
|
7447
|
+
}
|
|
7448
|
+
async function fetchPreUpload(appId, bucketId) {
|
|
7449
|
+
const response = await preUploadStaticAttachment(appId, bucketId);
|
|
7450
|
+
if (response.status_code !== "0") {
|
|
7451
|
+
throw new Error(`preUploadStatic \u8FD4\u56DE\u5F02\u5E38, status_code: ${response.status_code}`);
|
|
7452
|
+
}
|
|
7453
|
+
const { uploadPrefix, uploadID, uploadCredential } = response.data || {};
|
|
7454
|
+
if (!uploadPrefix || !uploadID) {
|
|
7455
|
+
throw new Error("preUploadStatic \u8FD4\u56DE\u6570\u636E\u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5C11 uploadPrefix \u6216 uploadID");
|
|
7456
|
+
}
|
|
7457
|
+
if (!uploadCredential?.AccessKeyID || !uploadCredential?.SecretAccessKey || !uploadCredential?.SessionToken) {
|
|
7458
|
+
throw new Error("preUploadStatic \u8FD4\u56DE\u7684\u51ED\u8BC1\u5B57\u6BB5\u4E0D\u5B8C\u6574");
|
|
7459
|
+
}
|
|
7460
|
+
return response;
|
|
7461
|
+
}
|
|
7462
|
+
function configureTosutil(tosutilPath, config) {
|
|
7463
|
+
const { endpoint, region, accessKeyID, secretAccessKey, sessionToken } = config;
|
|
7464
|
+
execFileSync(
|
|
7465
|
+
tosutilPath,
|
|
7466
|
+
["config", "-e", endpoint, "-i", accessKeyID, "-k", secretAccessKey, "-t", sessionToken, "-re", region],
|
|
7467
|
+
{ stdio: "pipe" }
|
|
7468
|
+
);
|
|
7469
|
+
}
|
|
7470
|
+
function uploadToTos(tosutilPath, sourceDir, destUrl) {
|
|
7471
|
+
execFileSync(
|
|
7472
|
+
tosutilPath,
|
|
7473
|
+
["cp", sourceDir, destUrl, "-r", "-flat", "-j", "5", "-p", "3", "-ps", "10485760", "-f"],
|
|
7474
|
+
{ stdio: "inherit" }
|
|
7475
|
+
);
|
|
7476
|
+
}
|
|
7477
|
+
async function resolveBucketId(appId) {
|
|
7478
|
+
console.error(`${LOG_PREFIX} \u83B7\u53D6\u9ED8\u8BA4\u5B58\u50A8\u6876...`);
|
|
7479
|
+
const bucketId = await getDefaultBucketId(appId);
|
|
7480
|
+
console.error(`${LOG_PREFIX} \u9ED8\u8BA4\u5B58\u50A8\u6876: ${bucketId}`);
|
|
7481
|
+
return bucketId;
|
|
7482
|
+
}
|
|
7483
|
+
function isDirEmpty(dirPath) {
|
|
7484
|
+
const entries = fs25.readdirSync(dirPath);
|
|
7485
|
+
return entries.length === 0;
|
|
7486
|
+
}
|
|
7487
|
+
|
|
7488
|
+
// src/commands/build/pre-upload-static.handler.ts
|
|
7489
|
+
var LOG_PREFIX2 = "[pre-upload-static]";
|
|
7490
|
+
function shellEscape(value) {
|
|
7491
|
+
return value.replace(/["\\`$]/g, "\\$&");
|
|
7492
|
+
}
|
|
7493
|
+
async function preUploadStatic(options) {
|
|
7494
|
+
try {
|
|
7495
|
+
const { appId } = options;
|
|
7496
|
+
console.error(`${LOG_PREFIX2} \u83B7\u53D6\u9ED8\u8BA4\u5B58\u50A8\u6876...`);
|
|
7497
|
+
const bucketId = await getDefaultBucketId(appId);
|
|
7498
|
+
console.error(`${LOG_PREFIX2} \u9ED8\u8BA4\u5B58\u50A8\u6876: ${bucketId}`);
|
|
7499
|
+
console.error(`${LOG_PREFIX2} \u8C03\u7528 preUploadStatic...`);
|
|
7500
|
+
const response = await preUploadStaticAttachment(appId, bucketId);
|
|
7501
|
+
if (response.status_code !== "0") {
|
|
7502
|
+
console.error(`${LOG_PREFIX2} preUploadStatic \u8FD4\u56DE\u5F02\u5E38, status_code: ${response.status_code}`);
|
|
7503
|
+
return;
|
|
7504
|
+
}
|
|
7505
|
+
const { downloadURLPrefix, uploadPrefix, uploadID, uploadCredential } = response.data || {};
|
|
7506
|
+
if (!downloadURLPrefix || !uploadPrefix || !uploadID) {
|
|
7507
|
+
console.error(`${LOG_PREFIX2} preUploadStatic \u8FD4\u56DE\u6570\u636E\u4E0D\u5B8C\u6574`);
|
|
7508
|
+
return;
|
|
7509
|
+
}
|
|
7510
|
+
if (!uploadCredential?.AccessKeyID || !uploadCredential?.SecretAccessKey || !uploadCredential?.SessionToken) {
|
|
7511
|
+
console.error(`${LOG_PREFIX2} preUploadStatic \u8FD4\u56DE\u7684\u51ED\u8BC1\u5B57\u6BB5\u4E0D\u5B8C\u6574`);
|
|
7512
|
+
return;
|
|
7513
|
+
}
|
|
7514
|
+
console.log(`export STATIC_ASSETS_BASE_URL="${shellEscape(downloadURLPrefix)}"`);
|
|
7515
|
+
console.log(`export STATIC_UPLOAD_PREFIX="${shellEscape(uploadPrefix)}"`);
|
|
7516
|
+
console.log(`export STATIC_UPLOAD_ID="${shellEscape(uploadID)}"`);
|
|
7517
|
+
console.log(`export STATIC_UPLOAD_AK="${shellEscape(uploadCredential.AccessKeyID)}"`);
|
|
7518
|
+
console.log(`export STATIC_UPLOAD_SK="${shellEscape(uploadCredential.SecretAccessKey)}"`);
|
|
7519
|
+
console.log(`export STATIC_UPLOAD_TOKEN="${shellEscape(uploadCredential.SessionToken)}"`);
|
|
7520
|
+
console.log(`export STATIC_UPLOAD_BUCKET_ID="${shellEscape(bucketId)}"`);
|
|
7521
|
+
console.error(`${LOG_PREFIX2} \u73AF\u5883\u53D8\u91CF\u5DF2\u8F93\u51FA`);
|
|
7522
|
+
} catch (error) {
|
|
7523
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7524
|
+
console.error(`${LOG_PREFIX2} Warning: ${message}`);
|
|
7525
|
+
}
|
|
7526
|
+
}
|
|
7527
|
+
|
|
7528
|
+
// src/commands/build/index.ts
|
|
7529
|
+
var getTokenCommand = {
|
|
7530
|
+
name: "get-token",
|
|
7531
|
+
description: "Get artifact upload credential (STI token)",
|
|
7532
|
+
register(program) {
|
|
7533
|
+
program.command(this.name).description(this.description).requiredOption("--app-id <id>", "Application ID").requiredOption("--scene <scene>", "Build scene (pipeline, static)").option("--commit-id <id>", "Git commit ID (required for pipeline scene)").action(async (options) => {
|
|
7534
|
+
await getToken(options);
|
|
7535
|
+
});
|
|
7536
|
+
}
|
|
7537
|
+
};
|
|
7538
|
+
var uploadStaticCommand = {
|
|
7539
|
+
name: "upload-static",
|
|
7540
|
+
description: "Upload shared/static files to TOS",
|
|
7541
|
+
register(program) {
|
|
7542
|
+
program.command(this.name).description(this.description).requiredOption("--app-id <id>", "Application ID").option("--static-dir <dir>", "Static files directory", UPLOAD_STATIC_DEFAULTS.staticDir).option("--tosutil-path <path>", "Path to tosutil binary", UPLOAD_STATIC_DEFAULTS.tosutilPath).option("--endpoint <endpoint>", "TOS endpoint", UPLOAD_STATIC_DEFAULTS.endpoint).option("--region <region>", "TOS region", UPLOAD_STATIC_DEFAULTS.region).action(async (options) => {
|
|
7543
|
+
await uploadStatic(options);
|
|
7544
|
+
});
|
|
7545
|
+
}
|
|
7546
|
+
};
|
|
7547
|
+
var preUploadStaticCommand = {
|
|
7548
|
+
name: "pre-upload-static",
|
|
7549
|
+
description: "Get TOS upload info and output as env vars for build.sh eval",
|
|
7550
|
+
register(program) {
|
|
7551
|
+
program.command(this.name).description(this.description).requiredOption("--app-id <id>", "Application ID").action(async (options) => {
|
|
7552
|
+
await preUploadStatic(options);
|
|
7553
|
+
});
|
|
7554
|
+
}
|
|
7555
|
+
};
|
|
7556
|
+
var buildCommandGroup = {
|
|
7557
|
+
name: "build",
|
|
7558
|
+
description: "Build related commands",
|
|
7559
|
+
commands: [getTokenCommand, uploadStaticCommand, preUploadStaticCommand]
|
|
7560
|
+
};
|
|
7561
|
+
|
|
5728
7562
|
// src/commands/index.ts
|
|
5729
7563
|
var commands = [
|
|
5730
7564
|
genDbSchemaCommand,
|
|
5731
7565
|
syncCommand,
|
|
7566
|
+
upgradeCommand,
|
|
5732
7567
|
actionPluginCommandGroup,
|
|
5733
7568
|
capabilityCommandGroup,
|
|
7569
|
+
componentCommandGroup,
|
|
5734
7570
|
migrationCommand,
|
|
5735
|
-
readLogsCommand
|
|
7571
|
+
readLogsCommand,
|
|
7572
|
+
buildCommandGroup
|
|
5736
7573
|
];
|
|
5737
7574
|
|
|
5738
7575
|
// src/index.ts
|
|
5739
|
-
var envPath =
|
|
5740
|
-
if (
|
|
7576
|
+
var envPath = path22.join(process.cwd(), ".env");
|
|
7577
|
+
if (fs26.existsSync(envPath)) {
|
|
5741
7578
|
dotenvConfig({ path: envPath });
|
|
5742
7579
|
}
|
|
5743
|
-
var __dirname =
|
|
5744
|
-
var pkg = JSON.parse(
|
|
7580
|
+
var __dirname = path22.dirname(fileURLToPath5(import.meta.url));
|
|
7581
|
+
var pkg = JSON.parse(fs26.readFileSync(path22.join(__dirname, "../package.json"), "utf-8"));
|
|
5745
7582
|
var cli = new FullstackCLI(pkg.version);
|
|
5746
7583
|
cli.useAll(commands);
|
|
5747
7584
|
cli.run();
|