@lark-apaas/fullstack-cli 1.1.16-beta.1 → 1.1.16-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -3
- package/dist/index.js +1629 -236
- package/package.json +6 -1
- package/templates/.spark_project +16 -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 -244
- 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
|
|
@@ -481,6 +481,7 @@ var replaceUnknownTransform = {
|
|
|
481
481
|
transform(ctx) {
|
|
482
482
|
const { sourceFile, stats } = ctx;
|
|
483
483
|
const fullText = sourceFile.getFullText();
|
|
484
|
+
const replacements = [];
|
|
484
485
|
sourceFile.forEachDescendant((node) => {
|
|
485
486
|
if (!Node5.isCallExpression(node)) {
|
|
486
487
|
return;
|
|
@@ -511,13 +512,23 @@ var replaceUnknownTransform = {
|
|
|
511
512
|
break;
|
|
512
513
|
}
|
|
513
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) {
|
|
514
524
|
expression.replaceWithText(factoryName);
|
|
515
525
|
if (isArrayType && foundKnownType) {
|
|
516
526
|
const parent = node.getParent();
|
|
517
527
|
if (Node5.isPropertyAccessExpression(parent) && parent.getName() === "array") {
|
|
518
528
|
const grandParent = parent.getParent();
|
|
519
529
|
if (Node5.isCallExpression(grandParent)) {
|
|
520
|
-
|
|
530
|
+
const nodeText = node.getText();
|
|
531
|
+
grandParent.replaceWithText(nodeText);
|
|
521
532
|
}
|
|
522
533
|
}
|
|
523
534
|
}
|
|
@@ -526,7 +537,7 @@ var replaceUnknownTransform = {
|
|
|
526
537
|
} else {
|
|
527
538
|
stats.fallbackToText++;
|
|
528
539
|
}
|
|
529
|
-
}
|
|
540
|
+
}
|
|
530
541
|
const todoCommentRegex = /\/\/ TODO: failed to parse database type '[^']+'\s*\n/g;
|
|
531
542
|
const currentText = sourceFile.getFullText();
|
|
532
543
|
const cleanedText = currentText.replace(todoCommentRegex, "");
|
|
@@ -1030,6 +1041,8 @@ async function fetchColumnComments(connectionString, options = {}) {
|
|
|
1030
1041
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
1031
1042
|
const url = new URL(connectionString);
|
|
1032
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)`);
|
|
1033
1046
|
const sql = postgres(connectionString, {
|
|
1034
1047
|
connect_timeout: Math.ceil(timeoutMs / 1e3),
|
|
1035
1048
|
idle_timeout: Math.ceil(timeoutMs / 1e3)
|
|
@@ -1058,6 +1071,7 @@ async function fetchColumnComments(connectionString, options = {}) {
|
|
|
1058
1071
|
const key = `${row.tableName}.${row.columnName}`;
|
|
1059
1072
|
commentMap.set(key, row.comment);
|
|
1060
1073
|
}
|
|
1074
|
+
console.log(`[fetchColumnComments] \u2190 Fetched ${commentMap.size} column comments (${Date.now() - start}ms)`);
|
|
1061
1075
|
return commentMap;
|
|
1062
1076
|
} finally {
|
|
1063
1077
|
await sql.end().catch(() => {
|
|
@@ -1156,6 +1170,79 @@ function addJsonbTypeComments(source, columnComments) {
|
|
|
1156
1170
|
return { text: result.join("\n"), added };
|
|
1157
1171
|
}
|
|
1158
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
|
+
|
|
1159
1246
|
// src/commands/db/gen-dbschema/transforms/text/table-aliases.ts
|
|
1160
1247
|
var TABLE_ALIAS_MARKER = "// table aliases";
|
|
1161
1248
|
function generateTableAliases(source) {
|
|
@@ -1204,13 +1291,16 @@ function postprocessSchema(rawSource, options = {}) {
|
|
|
1204
1291
|
source = inlineCustomTypes(source);
|
|
1205
1292
|
const jsonbCommentsResult = addJsonbTypeComments(source, options.columnComments);
|
|
1206
1293
|
source = jsonbCommentsResult.text;
|
|
1294
|
+
const syncedCommentsResult = addSyncedTableComments(source, options.syncedTableMap);
|
|
1295
|
+
source = syncedCommentsResult.text;
|
|
1207
1296
|
source = generateTableAliases(source);
|
|
1208
1297
|
source = formatSource(source);
|
|
1209
1298
|
return {
|
|
1210
1299
|
source,
|
|
1211
1300
|
astStats,
|
|
1212
1301
|
patchedDefects: patchResult.fixed,
|
|
1213
|
-
addedJsonbComments: jsonbCommentsResult.added
|
|
1302
|
+
addedJsonbComments: jsonbCommentsResult.added,
|
|
1303
|
+
addedSyncedComments: syncedCommentsResult.added
|
|
1214
1304
|
};
|
|
1215
1305
|
}
|
|
1216
1306
|
function logStats(result, prefix = "[postprocess]") {
|
|
@@ -1260,6 +1350,9 @@ function logStats(result, prefix = "[postprocess]") {
|
|
|
1260
1350
|
if (result.addedJsonbComments > 0) {
|
|
1261
1351
|
console.info(`${prefix} Added ${result.addedJsonbComments} JSDoc comments for jsonb fields`);
|
|
1262
1352
|
}
|
|
1353
|
+
if (result.addedSyncedComments > 0) {
|
|
1354
|
+
console.info(`${prefix} Added ${result.addedSyncedComments} comments for synced tables/fields`);
|
|
1355
|
+
}
|
|
1263
1356
|
}
|
|
1264
1357
|
|
|
1265
1358
|
// src/commands/db/gen-dbschema/index.ts
|
|
@@ -1270,7 +1363,10 @@ async function postprocessDrizzleSchema(targetPath, options = {}) {
|
|
|
1270
1363
|
return void 0;
|
|
1271
1364
|
}
|
|
1272
1365
|
const rawSource = fs3.readFileSync(resolvedPath, "utf8");
|
|
1273
|
-
const result = postprocessSchema(rawSource, {
|
|
1366
|
+
const result = postprocessSchema(rawSource, {
|
|
1367
|
+
columnComments: options.columnComments,
|
|
1368
|
+
syncedTableMap: options.syncedTableMap
|
|
1369
|
+
});
|
|
1274
1370
|
fs3.writeFileSync(resolvedPath, result.source, "utf8");
|
|
1275
1371
|
logStats(result, "[postprocess-drizzle-schema]");
|
|
1276
1372
|
return {
|
|
@@ -1280,10 +1376,113 @@ async function postprocessDrizzleSchema(targetPath, options = {}) {
|
|
|
1280
1376
|
patchedDefects: result.patchedDefects,
|
|
1281
1377
|
replacedTimestamps: result.astStats.replacedTimestamp,
|
|
1282
1378
|
replacedDefaultNow: result.astStats.replacedDefaultNow,
|
|
1283
|
-
addedJsonbComments: result.addedJsonbComments
|
|
1379
|
+
addedJsonbComments: result.addedJsonbComments,
|
|
1380
|
+
addedSyncedComments: result.addedSyncedComments
|
|
1284
1381
|
};
|
|
1285
1382
|
}
|
|
1286
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
|
+
|
|
1287
1486
|
// src/commands/db/gen-nest-resource/generator.ts
|
|
1288
1487
|
import { pluralize } from "inflection";
|
|
1289
1488
|
|
|
@@ -1962,7 +2161,9 @@ async function run(options = {}) {
|
|
|
1962
2161
|
}
|
|
1963
2162
|
const databaseUrl = process.env.SUDA_DATABASE_URL;
|
|
1964
2163
|
if (!databaseUrl) {
|
|
1965
|
-
console.error(
|
|
2164
|
+
console.error(
|
|
2165
|
+
"[gen-db-schema] Error: SUDA_DATABASE_URL environment variable is required"
|
|
2166
|
+
);
|
|
1966
2167
|
process.exit(1);
|
|
1967
2168
|
}
|
|
1968
2169
|
const outputPath = options.output || process.env.DB_SCHEMA_OUTPUT || "server/database/schema.ts";
|
|
@@ -1977,9 +2178,14 @@ async function run(options = {}) {
|
|
|
1977
2178
|
path2.resolve(__dirname2, "../../../dist/config/drizzle.config.js")
|
|
1978
2179
|
];
|
|
1979
2180
|
const configPath = configPathCandidates.find((p) => fs4.existsSync(p));
|
|
1980
|
-
console.log(
|
|
2181
|
+
console.log(
|
|
2182
|
+
"[gen-db-schema] Using drizzle config from:",
|
|
2183
|
+
configPath ?? "(not found)"
|
|
2184
|
+
);
|
|
1981
2185
|
if (!configPath) {
|
|
1982
|
-
console.error(
|
|
2186
|
+
console.error(
|
|
2187
|
+
"[gen-db-schema] Error: drizzle config not found in CLI package"
|
|
2188
|
+
);
|
|
1983
2189
|
process.exit(1);
|
|
1984
2190
|
}
|
|
1985
2191
|
const resolveDrizzleKitBin = () => {
|
|
@@ -1995,7 +2201,9 @@ async function run(options = {}) {
|
|
|
1995
2201
|
const binField = pkgJson.bin;
|
|
1996
2202
|
const binRelPath = typeof binField === "string" ? binField : binField?.["drizzle-kit"];
|
|
1997
2203
|
if (!binRelPath) {
|
|
1998
|
-
throw new Error(
|
|
2204
|
+
throw new Error(
|
|
2205
|
+
"Unable to resolve drizzle-kit binary from package.json"
|
|
2206
|
+
);
|
|
1999
2207
|
}
|
|
2000
2208
|
return path2.resolve(currentDir, binRelPath);
|
|
2001
2209
|
}
|
|
@@ -2006,15 +2214,66 @@ async function run(options = {}) {
|
|
|
2006
2214
|
throw new Error("Unable to locate drizzle-kit package root");
|
|
2007
2215
|
};
|
|
2008
2216
|
let columnComments;
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
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 {
|
|
2013
2253
|
console.warn(
|
|
2014
2254
|
"[gen-db-schema] \u26A0 Failed to fetch column comments (skipping):",
|
|
2015
|
-
|
|
2255
|
+
fetchTasks[0].reason instanceof Error ? fetchTasks[0].reason.message : String(fetchTasks[0].reason)
|
|
2016
2256
|
);
|
|
2017
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
|
+
}
|
|
2018
2277
|
try {
|
|
2019
2278
|
const env = {
|
|
2020
2279
|
...process.env,
|
|
@@ -2025,13 +2284,19 @@ async function run(options = {}) {
|
|
|
2025
2284
|
};
|
|
2026
2285
|
const drizzleKitBin = resolveDrizzleKitBin();
|
|
2027
2286
|
const spawnArgs = [drizzleKitBin, "introspect", "--config", configPath];
|
|
2028
|
-
const result = spawnSync(process.execPath, spawnArgs, {
|
|
2287
|
+
const result = spawnSync(process.execPath, spawnArgs, {
|
|
2288
|
+
stdio: "inherit",
|
|
2289
|
+
env,
|
|
2290
|
+
cwd: process.cwd()
|
|
2291
|
+
});
|
|
2029
2292
|
if (result.error) {
|
|
2030
2293
|
console.error("[gen-db-schema] Execution failed:", result.error);
|
|
2031
2294
|
throw result.error;
|
|
2032
2295
|
}
|
|
2033
2296
|
if ((result.status ?? 0) !== 0) {
|
|
2034
|
-
throw new Error(
|
|
2297
|
+
throw new Error(
|
|
2298
|
+
`drizzle-kit introspect failed with status ${result.status}`
|
|
2299
|
+
);
|
|
2035
2300
|
}
|
|
2036
2301
|
const generatedSchema = path2.join(OUT_DIR, "schema.ts");
|
|
2037
2302
|
if (!fs4.existsSync(generatedSchema)) {
|
|
@@ -2039,10 +2304,14 @@ async function run(options = {}) {
|
|
|
2039
2304
|
throw new Error("drizzle-kit introspect failed to generate schema.ts");
|
|
2040
2305
|
}
|
|
2041
2306
|
const stats = await postprocessDrizzleSchema(generatedSchema, {
|
|
2042
|
-
columnComments
|
|
2307
|
+
columnComments,
|
|
2308
|
+
syncedTableMap
|
|
2043
2309
|
});
|
|
2044
2310
|
if (stats?.unmatchedUnknown?.length) {
|
|
2045
|
-
console.warn(
|
|
2311
|
+
console.warn(
|
|
2312
|
+
"[gen-db-schema] Unmatched custom types detected:",
|
|
2313
|
+
stats.unmatchedUnknown
|
|
2314
|
+
);
|
|
2046
2315
|
}
|
|
2047
2316
|
console.log("[gen-db-schema] \u2713 Postprocessed schema");
|
|
2048
2317
|
fs4.mkdirSync(path2.dirname(SCHEMA_FILE), { recursive: true });
|
|
@@ -2057,14 +2326,22 @@ async function run(options = {}) {
|
|
|
2057
2326
|
schemaFilePath,
|
|
2058
2327
|
moduleOutputDir: path2.resolve(process.cwd(), "server/modules")
|
|
2059
2328
|
});
|
|
2060
|
-
console.log(
|
|
2329
|
+
console.log(
|
|
2330
|
+
"[gen-db-schema] \u2713 Generate NestJS Module Boilerplate Successfully"
|
|
2331
|
+
);
|
|
2061
2332
|
}
|
|
2062
2333
|
} catch (error) {
|
|
2063
|
-
console.warn(
|
|
2334
|
+
console.warn(
|
|
2335
|
+
"[gen-db-schema] Generate NestJS Module Boilerplate failed:",
|
|
2336
|
+
error instanceof Error ? error.message : String(error)
|
|
2337
|
+
);
|
|
2064
2338
|
}
|
|
2065
2339
|
console.log("[gen-db-schema] \u2713 Complete");
|
|
2066
2340
|
} catch (err) {
|
|
2067
|
-
console.error(
|
|
2341
|
+
console.error(
|
|
2342
|
+
"[gen-db-schema] Failed:",
|
|
2343
|
+
err instanceof Error ? err.message : String(err)
|
|
2344
|
+
);
|
|
2068
2345
|
exitCode = 1;
|
|
2069
2346
|
} finally {
|
|
2070
2347
|
if (fs4.existsSync(OUT_DIR)) {
|
|
@@ -2101,12 +2378,15 @@ var syncConfig = {
|
|
|
2101
2378
|
type: "directory",
|
|
2102
2379
|
overwrite: true
|
|
2103
2380
|
},
|
|
2104
|
-
// 2.
|
|
2381
|
+
// 2. 智能合并 nest-cli.json 配置(保留用户自定义的 assets、plugins 等)
|
|
2105
2382
|
{
|
|
2106
2383
|
from: "templates/nest-cli.json",
|
|
2107
2384
|
to: "nest-cli.json",
|
|
2108
|
-
type: "
|
|
2109
|
-
|
|
2385
|
+
type: "merge-json",
|
|
2386
|
+
arrayMerge: {
|
|
2387
|
+
"compilerOptions.assets": { key: "include" },
|
|
2388
|
+
"compilerOptions.plugins": { key: "name" }
|
|
2389
|
+
}
|
|
2110
2390
|
},
|
|
2111
2391
|
// // 2. 追加内容到 .gitignore
|
|
2112
2392
|
// {
|
|
@@ -2151,6 +2431,13 @@ var syncConfig = {
|
|
|
2151
2431
|
type: "add-line",
|
|
2152
2432
|
to: ".gitignore",
|
|
2153
2433
|
line: ".agent/"
|
|
2434
|
+
},
|
|
2435
|
+
// 8. 同步 .spark_project 配置文件(总是覆盖)
|
|
2436
|
+
{
|
|
2437
|
+
from: "templates/.spark_project",
|
|
2438
|
+
to: ".spark_project",
|
|
2439
|
+
type: "file",
|
|
2440
|
+
overwrite: true
|
|
2154
2441
|
}
|
|
2155
2442
|
],
|
|
2156
2443
|
// 文件权限设置
|
|
@@ -2192,6 +2479,52 @@ function removeLineFromFile(filePath, pattern) {
|
|
|
2192
2479
|
return true;
|
|
2193
2480
|
}
|
|
2194
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
|
+
|
|
2195
2528
|
// src/commands/sync/run.handler.ts
|
|
2196
2529
|
async function run2(options) {
|
|
2197
2530
|
const userProjectRoot = process.env.INIT_CWD || process.cwd();
|
|
@@ -2254,6 +2587,12 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
|
|
|
2254
2587
|
addLineToFile(destPath2, rule.line);
|
|
2255
2588
|
return;
|
|
2256
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
|
+
}
|
|
2257
2596
|
if (!("from" in rule)) {
|
|
2258
2597
|
return;
|
|
2259
2598
|
}
|
|
@@ -2396,6 +2735,33 @@ function addLineToFile(filePath, line) {
|
|
|
2396
2735
|
fs6.appendFileSync(filePath, appendContent);
|
|
2397
2736
|
console.log(`[fullstack-cli] \u2713 ${fileName} (added: ${line})`);
|
|
2398
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
|
+
}
|
|
2399
2765
|
|
|
2400
2766
|
// src/commands/sync/index.ts
|
|
2401
2767
|
var syncCommand = {
|
|
@@ -2408,10 +2774,398 @@ var syncCommand = {
|
|
|
2408
2774
|
}
|
|
2409
2775
|
};
|
|
2410
2776
|
|
|
2411
|
-
// src/
|
|
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;
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
async function reportInstallEvent(pluginKey, version) {
|
|
2801
|
+
await reportEvents([
|
|
2802
|
+
{
|
|
2803
|
+
resourceType: "plugin",
|
|
2804
|
+
resourceKey: pluginKey,
|
|
2805
|
+
eventType: "install",
|
|
2806
|
+
details: { version }
|
|
2807
|
+
}
|
|
2808
|
+
]);
|
|
2809
|
+
}
|
|
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
|
+
]);
|
|
2819
|
+
}
|
|
2820
|
+
|
|
2821
|
+
// src/utils/git.ts
|
|
2822
|
+
import { execSync, spawnSync as spawnSync2 } from "child_process";
|
|
2412
2823
|
import fs7 from "fs";
|
|
2413
2824
|
import path5 from "path";
|
|
2414
|
-
|
|
2825
|
+
function isGitRepository(cwd = process.cwd()) {
|
|
2826
|
+
try {
|
|
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;
|
|
2837
|
+
} catch {
|
|
2838
|
+
return false;
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
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";
|
|
2415
3169
|
function parsePluginName(input) {
|
|
2416
3170
|
const match = input.match(/^(@[^/]+\/[^@]+)(?:@(.+))?$/);
|
|
2417
3171
|
if (!match) {
|
|
@@ -2428,35 +3182,35 @@ function getProjectRoot() {
|
|
|
2428
3182
|
return process.cwd();
|
|
2429
3183
|
}
|
|
2430
3184
|
function getPackageJsonPath() {
|
|
2431
|
-
return
|
|
3185
|
+
return path8.join(getProjectRoot(), "package.json");
|
|
2432
3186
|
}
|
|
2433
3187
|
function getPluginPath(pluginName) {
|
|
2434
|
-
return
|
|
3188
|
+
return path8.join(getProjectRoot(), "node_modules", pluginName);
|
|
2435
3189
|
}
|
|
2436
|
-
function
|
|
3190
|
+
function readPackageJson2() {
|
|
2437
3191
|
const pkgPath = getPackageJsonPath();
|
|
2438
|
-
if (!
|
|
3192
|
+
if (!fs10.existsSync(pkgPath)) {
|
|
2439
3193
|
throw new Error("package.json not found in current directory");
|
|
2440
3194
|
}
|
|
2441
3195
|
try {
|
|
2442
|
-
const content =
|
|
3196
|
+
const content = fs10.readFileSync(pkgPath, "utf-8");
|
|
2443
3197
|
return JSON.parse(content);
|
|
2444
3198
|
} catch {
|
|
2445
3199
|
throw new Error("Failed to parse package.json");
|
|
2446
3200
|
}
|
|
2447
3201
|
}
|
|
2448
|
-
function
|
|
3202
|
+
function writePackageJson2(pkg2) {
|
|
2449
3203
|
const pkgPath = getPackageJsonPath();
|
|
2450
|
-
|
|
3204
|
+
fs10.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
|
|
2451
3205
|
}
|
|
2452
3206
|
function readActionPlugins() {
|
|
2453
|
-
const pkg2 =
|
|
3207
|
+
const pkg2 = readPackageJson2();
|
|
2454
3208
|
return pkg2.actionPlugins || {};
|
|
2455
3209
|
}
|
|
2456
3210
|
function writeActionPlugins(plugins) {
|
|
2457
|
-
const pkg2 =
|
|
3211
|
+
const pkg2 = readPackageJson2();
|
|
2458
3212
|
pkg2.actionPlugins = plugins;
|
|
2459
|
-
|
|
3213
|
+
writePackageJson2(pkg2);
|
|
2460
3214
|
}
|
|
2461
3215
|
function isPluginInstalled(pluginName) {
|
|
2462
3216
|
const plugins = readActionPlugins();
|
|
@@ -2468,7 +3222,7 @@ function getInstalledPluginVersion(pluginName) {
|
|
|
2468
3222
|
}
|
|
2469
3223
|
function npmInstall(tgzPath) {
|
|
2470
3224
|
console.log(`[action-plugin] Running npm install ${tgzPath}...`);
|
|
2471
|
-
const result =
|
|
3225
|
+
const result = spawnSync4("npm", ["install", tgzPath, "--no-save", "--no-package-lock", "--ignore-scripts"], {
|
|
2472
3226
|
cwd: getProjectRoot(),
|
|
2473
3227
|
stdio: "inherit"
|
|
2474
3228
|
});
|
|
@@ -2480,12 +3234,12 @@ function npmInstall(tgzPath) {
|
|
|
2480
3234
|
}
|
|
2481
3235
|
}
|
|
2482
3236
|
function getPackageVersion(pluginName) {
|
|
2483
|
-
const pkgJsonPath =
|
|
2484
|
-
if (!
|
|
3237
|
+
const pkgJsonPath = path8.join(getPluginPath(pluginName), "package.json");
|
|
3238
|
+
if (!fs10.existsSync(pkgJsonPath)) {
|
|
2485
3239
|
return null;
|
|
2486
3240
|
}
|
|
2487
3241
|
try {
|
|
2488
|
-
const content =
|
|
3242
|
+
const content = fs10.readFileSync(pkgJsonPath, "utf-8");
|
|
2489
3243
|
const pkg2 = JSON.parse(content);
|
|
2490
3244
|
return pkg2.version || null;
|
|
2491
3245
|
} catch {
|
|
@@ -2493,49 +3247,49 @@ function getPackageVersion(pluginName) {
|
|
|
2493
3247
|
}
|
|
2494
3248
|
}
|
|
2495
3249
|
function readPluginPackageJson(pluginPath) {
|
|
2496
|
-
const pkgJsonPath =
|
|
2497
|
-
if (!
|
|
3250
|
+
const pkgJsonPath = path8.join(pluginPath, "package.json");
|
|
3251
|
+
if (!fs10.existsSync(pkgJsonPath)) {
|
|
2498
3252
|
return null;
|
|
2499
3253
|
}
|
|
2500
3254
|
try {
|
|
2501
|
-
const content =
|
|
3255
|
+
const content = fs10.readFileSync(pkgJsonPath, "utf-8");
|
|
2502
3256
|
return JSON.parse(content);
|
|
2503
3257
|
} catch {
|
|
2504
3258
|
return null;
|
|
2505
3259
|
}
|
|
2506
3260
|
}
|
|
2507
3261
|
function extractTgzToNodeModules(tgzPath, pluginName) {
|
|
2508
|
-
const nodeModulesPath =
|
|
2509
|
-
const targetDir =
|
|
2510
|
-
const scopeDir =
|
|
2511
|
-
if (!
|
|
2512
|
-
|
|
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 });
|
|
2513
3267
|
}
|
|
2514
|
-
if (
|
|
2515
|
-
|
|
3268
|
+
if (fs10.existsSync(targetDir)) {
|
|
3269
|
+
fs10.rmSync(targetDir, { recursive: true });
|
|
2516
3270
|
}
|
|
2517
|
-
const tempDir =
|
|
2518
|
-
if (
|
|
2519
|
-
|
|
3271
|
+
const tempDir = path8.join(nodeModulesPath, ".cache", "fullstack-cli", "extract-temp");
|
|
3272
|
+
if (fs10.existsSync(tempDir)) {
|
|
3273
|
+
fs10.rmSync(tempDir, { recursive: true });
|
|
2520
3274
|
}
|
|
2521
|
-
|
|
3275
|
+
fs10.mkdirSync(tempDir, { recursive: true });
|
|
2522
3276
|
try {
|
|
2523
|
-
|
|
2524
|
-
const extractedDir =
|
|
2525
|
-
if (
|
|
2526
|
-
|
|
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);
|
|
2527
3281
|
} else {
|
|
2528
|
-
const files =
|
|
3282
|
+
const files = fs10.readdirSync(tempDir);
|
|
2529
3283
|
if (files.length === 1) {
|
|
2530
|
-
|
|
3284
|
+
fs10.renameSync(path8.join(tempDir, files[0]), targetDir);
|
|
2531
3285
|
} else {
|
|
2532
3286
|
throw new Error("Unexpected tgz structure");
|
|
2533
3287
|
}
|
|
2534
3288
|
}
|
|
2535
3289
|
return targetDir;
|
|
2536
3290
|
} finally {
|
|
2537
|
-
if (
|
|
2538
|
-
|
|
3291
|
+
if (fs10.existsSync(tempDir)) {
|
|
3292
|
+
fs10.rmSync(tempDir, { recursive: true });
|
|
2539
3293
|
}
|
|
2540
3294
|
}
|
|
2541
3295
|
}
|
|
@@ -2544,10 +3298,10 @@ function checkMissingPeerDeps(peerDeps) {
|
|
|
2544
3298
|
return [];
|
|
2545
3299
|
}
|
|
2546
3300
|
const missing = [];
|
|
2547
|
-
const nodeModulesPath =
|
|
3301
|
+
const nodeModulesPath = path8.join(getProjectRoot(), "node_modules");
|
|
2548
3302
|
for (const [depName, _version] of Object.entries(peerDeps)) {
|
|
2549
|
-
const depPath =
|
|
2550
|
-
if (!
|
|
3303
|
+
const depPath = path8.join(nodeModulesPath, depName);
|
|
3304
|
+
if (!fs10.existsSync(depPath)) {
|
|
2551
3305
|
missing.push(depName);
|
|
2552
3306
|
}
|
|
2553
3307
|
}
|
|
@@ -2558,7 +3312,7 @@ function installMissingDeps(deps) {
|
|
|
2558
3312
|
return;
|
|
2559
3313
|
}
|
|
2560
3314
|
console.log(`[action-plugin] Installing missing dependencies: ${deps.join(", ")}`);
|
|
2561
|
-
const result =
|
|
3315
|
+
const result = spawnSync4("npm", ["install", ...deps, "--no-save", "--no-package-lock"], {
|
|
2562
3316
|
cwd: getProjectRoot(),
|
|
2563
3317
|
stdio: "inherit"
|
|
2564
3318
|
});
|
|
@@ -2571,40 +3325,16 @@ function installMissingDeps(deps) {
|
|
|
2571
3325
|
}
|
|
2572
3326
|
function removePluginDirectory(pluginName) {
|
|
2573
3327
|
const pluginPath = getPluginPath(pluginName);
|
|
2574
|
-
if (
|
|
2575
|
-
|
|
3328
|
+
if (fs10.existsSync(pluginPath)) {
|
|
3329
|
+
fs10.rmSync(pluginPath, { recursive: true });
|
|
2576
3330
|
console.log(`[action-plugin] Removed ${pluginName}`);
|
|
2577
3331
|
}
|
|
2578
3332
|
}
|
|
2579
3333
|
|
|
2580
3334
|
// src/commands/action-plugin/api-client.ts
|
|
2581
3335
|
import { HttpClient as HttpClient2 } from "@lark-apaas/http-client";
|
|
2582
|
-
import
|
|
2583
|
-
import
|
|
2584
|
-
|
|
2585
|
-
// src/utils/http-client.ts
|
|
2586
|
-
import { HttpClient } from "@lark-apaas/http-client";
|
|
2587
|
-
var clientInstance = null;
|
|
2588
|
-
function getHttpClient() {
|
|
2589
|
-
if (!clientInstance) {
|
|
2590
|
-
clientInstance = new HttpClient({
|
|
2591
|
-
timeout: 3e4,
|
|
2592
|
-
platform: {
|
|
2593
|
-
enabled: true
|
|
2594
|
-
}
|
|
2595
|
-
});
|
|
2596
|
-
const canaryEnv = process.env.FORCE_FRAMEWORK_CLI_CANARY_ENV;
|
|
2597
|
-
if (canaryEnv) {
|
|
2598
|
-
clientInstance.interceptors.request.use((req) => {
|
|
2599
|
-
req.headers["x-tt-env"] = canaryEnv;
|
|
2600
|
-
return req;
|
|
2601
|
-
});
|
|
2602
|
-
}
|
|
2603
|
-
}
|
|
2604
|
-
return clientInstance;
|
|
2605
|
-
}
|
|
2606
|
-
|
|
2607
|
-
// src/commands/action-plugin/api-client.ts
|
|
3336
|
+
import fs11 from "fs";
|
|
3337
|
+
import path9 from "path";
|
|
2608
3338
|
var PLUGIN_CACHE_DIR = "node_modules/.cache/fullstack-cli/plugins";
|
|
2609
3339
|
async function getPluginVersions(keys, latestOnly = true) {
|
|
2610
3340
|
const client = getHttpClient();
|
|
@@ -2668,19 +3398,19 @@ async function downloadFromPublic(downloadURL) {
|
|
|
2668
3398
|
return Buffer.from(arrayBuffer);
|
|
2669
3399
|
}
|
|
2670
3400
|
function getPluginCacheDir() {
|
|
2671
|
-
return
|
|
3401
|
+
return path9.join(process.cwd(), PLUGIN_CACHE_DIR);
|
|
2672
3402
|
}
|
|
2673
3403
|
function ensureCacheDir() {
|
|
2674
3404
|
const cacheDir = getPluginCacheDir();
|
|
2675
|
-
if (!
|
|
2676
|
-
|
|
3405
|
+
if (!fs11.existsSync(cacheDir)) {
|
|
3406
|
+
fs11.mkdirSync(cacheDir, { recursive: true });
|
|
2677
3407
|
}
|
|
2678
3408
|
}
|
|
2679
3409
|
function getTempFilePath(pluginKey, version) {
|
|
2680
3410
|
ensureCacheDir();
|
|
2681
3411
|
const safeKey = pluginKey.replace(/[/@]/g, "_");
|
|
2682
3412
|
const filename = `${safeKey}@${version}.tgz`;
|
|
2683
|
-
return
|
|
3413
|
+
return path9.join(getPluginCacheDir(), filename);
|
|
2684
3414
|
}
|
|
2685
3415
|
var MAX_RETRIES = 2;
|
|
2686
3416
|
async function withRetry(operation, description, maxRetries = MAX_RETRIES) {
|
|
@@ -2717,7 +3447,7 @@ async function downloadPlugin(pluginKey, requestedVersion) {
|
|
|
2717
3447
|
);
|
|
2718
3448
|
}
|
|
2719
3449
|
const tgzPath = getTempFilePath(pluginKey, pluginInfo.version);
|
|
2720
|
-
|
|
3450
|
+
fs11.writeFileSync(tgzPath, tgzBuffer);
|
|
2721
3451
|
console.log(`[action-plugin] Downloaded to ${tgzPath} (${(tgzBuffer.length / 1024).toFixed(2)} KB)`);
|
|
2722
3452
|
return {
|
|
2723
3453
|
tgzPath,
|
|
@@ -2731,18 +3461,18 @@ function getCachePath(pluginKey, version) {
|
|
|
2731
3461
|
ensureCacheDir();
|
|
2732
3462
|
const safeKey = pluginKey.replace(/[/@]/g, "_");
|
|
2733
3463
|
const filename = `${safeKey}@${version}.tgz`;
|
|
2734
|
-
return
|
|
3464
|
+
return path9.join(getPluginCacheDir(), filename);
|
|
2735
3465
|
}
|
|
2736
3466
|
function hasCachedPlugin(pluginKey, version) {
|
|
2737
3467
|
const cachePath = getCachePath(pluginKey, version);
|
|
2738
|
-
return
|
|
3468
|
+
return fs11.existsSync(cachePath);
|
|
2739
3469
|
}
|
|
2740
3470
|
function listCachedPlugins() {
|
|
2741
3471
|
const cacheDir = getPluginCacheDir();
|
|
2742
|
-
if (!
|
|
3472
|
+
if (!fs11.existsSync(cacheDir)) {
|
|
2743
3473
|
return [];
|
|
2744
3474
|
}
|
|
2745
|
-
const files =
|
|
3475
|
+
const files = fs11.readdirSync(cacheDir);
|
|
2746
3476
|
const result = [];
|
|
2747
3477
|
for (const file of files) {
|
|
2748
3478
|
if (!file.endsWith(".tgz")) continue;
|
|
@@ -2750,8 +3480,8 @@ function listCachedPlugins() {
|
|
|
2750
3480
|
if (!match) continue;
|
|
2751
3481
|
const [, rawName, version] = match;
|
|
2752
3482
|
const name = rawName.replace(/^_/, "@").replace(/_/, "/");
|
|
2753
|
-
const filePath =
|
|
2754
|
-
const stat =
|
|
3483
|
+
const filePath = path9.join(cacheDir, file);
|
|
3484
|
+
const stat = fs11.statSync(filePath);
|
|
2755
3485
|
result.push({
|
|
2756
3486
|
name,
|
|
2757
3487
|
version,
|
|
@@ -2764,14 +3494,14 @@ function listCachedPlugins() {
|
|
|
2764
3494
|
}
|
|
2765
3495
|
function cleanAllCache() {
|
|
2766
3496
|
const cacheDir = getPluginCacheDir();
|
|
2767
|
-
if (!
|
|
3497
|
+
if (!fs11.existsSync(cacheDir)) {
|
|
2768
3498
|
return 0;
|
|
2769
3499
|
}
|
|
2770
|
-
const files =
|
|
3500
|
+
const files = fs11.readdirSync(cacheDir);
|
|
2771
3501
|
let count = 0;
|
|
2772
3502
|
for (const file of files) {
|
|
2773
3503
|
if (file.endsWith(".tgz")) {
|
|
2774
|
-
|
|
3504
|
+
fs11.unlinkSync(path9.join(cacheDir, file));
|
|
2775
3505
|
count++;
|
|
2776
3506
|
}
|
|
2777
3507
|
}
|
|
@@ -2779,21 +3509,21 @@ function cleanAllCache() {
|
|
|
2779
3509
|
}
|
|
2780
3510
|
function cleanPluginCache(pluginKey, version) {
|
|
2781
3511
|
const cacheDir = getPluginCacheDir();
|
|
2782
|
-
if (!
|
|
3512
|
+
if (!fs11.existsSync(cacheDir)) {
|
|
2783
3513
|
return 0;
|
|
2784
3514
|
}
|
|
2785
3515
|
const safeKey = pluginKey.replace(/[/@]/g, "_");
|
|
2786
|
-
const files =
|
|
3516
|
+
const files = fs11.readdirSync(cacheDir);
|
|
2787
3517
|
let count = 0;
|
|
2788
3518
|
for (const file of files) {
|
|
2789
3519
|
if (version) {
|
|
2790
3520
|
if (file === `${safeKey}@${version}.tgz`) {
|
|
2791
|
-
|
|
3521
|
+
fs11.unlinkSync(path9.join(cacheDir, file));
|
|
2792
3522
|
count++;
|
|
2793
3523
|
}
|
|
2794
3524
|
} else {
|
|
2795
3525
|
if (file.startsWith(`${safeKey}@`) && file.endsWith(".tgz")) {
|
|
2796
|
-
|
|
3526
|
+
fs11.unlinkSync(path9.join(cacheDir, file));
|
|
2797
3527
|
count++;
|
|
2798
3528
|
}
|
|
2799
3529
|
}
|
|
@@ -2887,6 +3617,8 @@ async function installOne(nameWithVersion) {
|
|
|
2887
3617
|
if (actualVersion === requestedVersion) {
|
|
2888
3618
|
console.log(`[action-plugin] Plugin ${name}@${requestedVersion} is already installed`);
|
|
2889
3619
|
syncActionPluginsRecord(name, actualVersion);
|
|
3620
|
+
reportCreateInstanceEvent(name, actualVersion).catch(() => {
|
|
3621
|
+
});
|
|
2890
3622
|
return { name, version: actualVersion, success: true, skipped: true };
|
|
2891
3623
|
}
|
|
2892
3624
|
}
|
|
@@ -2897,6 +3629,8 @@ async function installOne(nameWithVersion) {
|
|
|
2897
3629
|
if (actualVersion === targetVersion) {
|
|
2898
3630
|
console.log(`[action-plugin] Plugin ${name} is already up to date (version: ${actualVersion})`);
|
|
2899
3631
|
syncActionPluginsRecord(name, actualVersion);
|
|
3632
|
+
reportCreateInstanceEvent(name, actualVersion).catch(() => {
|
|
3633
|
+
});
|
|
2900
3634
|
return { name, version: actualVersion, success: true, skipped: true };
|
|
2901
3635
|
}
|
|
2902
3636
|
console.log(`[action-plugin] Found newer version: ${targetVersion} (installed: ${actualVersion || "none"})`);
|
|
@@ -2927,6 +3661,10 @@ async function installOne(nameWithVersion) {
|
|
|
2927
3661
|
writeActionPlugins(plugins);
|
|
2928
3662
|
const source = fromCache ? "from cache" : "downloaded";
|
|
2929
3663
|
console.log(`[action-plugin] Successfully installed ${name}@${installedVersion} (${source})`);
|
|
3664
|
+
reportInstallEvent(name, installedVersion).catch(() => {
|
|
3665
|
+
});
|
|
3666
|
+
reportCreateInstanceEvent(name, installedVersion).catch(() => {
|
|
3667
|
+
});
|
|
2930
3668
|
return { name, version: installedVersion, success: true };
|
|
2931
3669
|
} catch (error) {
|
|
2932
3670
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -3212,40 +3950,40 @@ var actionPluginCommandGroup = {
|
|
|
3212
3950
|
};
|
|
3213
3951
|
|
|
3214
3952
|
// src/commands/capability/utils.ts
|
|
3215
|
-
import
|
|
3953
|
+
import fs12 from "fs";
|
|
3216
3954
|
import { createRequire as createRequire2 } from "module";
|
|
3217
|
-
import
|
|
3955
|
+
import path10 from "path";
|
|
3218
3956
|
var CAPABILITIES_DIR = "server/capabilities";
|
|
3219
3957
|
function getProjectRoot2() {
|
|
3220
3958
|
return process.cwd();
|
|
3221
3959
|
}
|
|
3222
3960
|
function getCapabilitiesDir() {
|
|
3223
|
-
return
|
|
3961
|
+
return path10.join(getProjectRoot2(), CAPABILITIES_DIR);
|
|
3224
3962
|
}
|
|
3225
3963
|
function getCapabilityPath(id) {
|
|
3226
|
-
return
|
|
3964
|
+
return path10.join(getCapabilitiesDir(), `${id}.json`);
|
|
3227
3965
|
}
|
|
3228
3966
|
function getPluginManifestPath(pluginKey) {
|
|
3229
|
-
return
|
|
3967
|
+
return path10.join(getProjectRoot2(), "node_modules", pluginKey, "manifest.json");
|
|
3230
3968
|
}
|
|
3231
3969
|
function capabilitiesDirExists() {
|
|
3232
|
-
return
|
|
3970
|
+
return fs12.existsSync(getCapabilitiesDir());
|
|
3233
3971
|
}
|
|
3234
3972
|
function listCapabilityIds() {
|
|
3235
3973
|
const dir = getCapabilitiesDir();
|
|
3236
|
-
if (!
|
|
3974
|
+
if (!fs12.existsSync(dir)) {
|
|
3237
3975
|
return [];
|
|
3238
3976
|
}
|
|
3239
|
-
const files =
|
|
3977
|
+
const files = fs12.readdirSync(dir);
|
|
3240
3978
|
return files.filter((f) => f.endsWith(".json") && f !== "capabilities.json").map((f) => f.replace(/\.json$/, ""));
|
|
3241
3979
|
}
|
|
3242
3980
|
function readCapability(id) {
|
|
3243
3981
|
const filePath = getCapabilityPath(id);
|
|
3244
|
-
if (!
|
|
3982
|
+
if (!fs12.existsSync(filePath)) {
|
|
3245
3983
|
throw new Error(`Capability not found: ${id}`);
|
|
3246
3984
|
}
|
|
3247
3985
|
try {
|
|
3248
|
-
const content =
|
|
3986
|
+
const content = fs12.readFileSync(filePath, "utf-8");
|
|
3249
3987
|
return JSON.parse(content);
|
|
3250
3988
|
} catch (error) {
|
|
3251
3989
|
if (error instanceof SyntaxError) {
|
|
@@ -3272,11 +4010,11 @@ function readAllCapabilities() {
|
|
|
3272
4010
|
}
|
|
3273
4011
|
function readPluginManifest(pluginKey) {
|
|
3274
4012
|
const manifestPath = getPluginManifestPath(pluginKey);
|
|
3275
|
-
if (!
|
|
4013
|
+
if (!fs12.existsSync(manifestPath)) {
|
|
3276
4014
|
throw new Error(`Plugin not installed: ${pluginKey} (manifest.json not found)`);
|
|
3277
4015
|
}
|
|
3278
4016
|
try {
|
|
3279
|
-
const content =
|
|
4017
|
+
const content = fs12.readFileSync(manifestPath, "utf-8");
|
|
3280
4018
|
return JSON.parse(content);
|
|
3281
4019
|
} catch (error) {
|
|
3282
4020
|
if (error instanceof SyntaxError) {
|
|
@@ -3293,7 +4031,7 @@ function hasValidParamsSchema(paramsSchema) {
|
|
|
3293
4031
|
}
|
|
3294
4032
|
async function loadPlugin(pluginKey) {
|
|
3295
4033
|
try {
|
|
3296
|
-
const userRequire = createRequire2(
|
|
4034
|
+
const userRequire = createRequire2(path10.join(getProjectRoot2(), "package.json"));
|
|
3297
4035
|
const resolvedPath = userRequire.resolve(pluginKey);
|
|
3298
4036
|
const pluginModule = await import(resolvedPath);
|
|
3299
4037
|
const pluginPackage = pluginModule.default ?? pluginModule;
|
|
@@ -3452,59 +4190,358 @@ var capabilityCommandGroup = {
|
|
|
3452
4190
|
commands: [listCommand2]
|
|
3453
4191
|
};
|
|
3454
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;
|
|
4342
|
+
}
|
|
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;
|
|
4359
|
+
}
|
|
4360
|
+
}
|
|
4361
|
+
return Array.from(state.files);
|
|
4362
|
+
}
|
|
4363
|
+
function toFileInfo(filePath) {
|
|
4364
|
+
const name = filePath.split("/").pop() || filePath;
|
|
4365
|
+
return { name, path: filePath };
|
|
4366
|
+
}
|
|
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");
|
|
4450
|
+
}
|
|
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();
|
|
4473
|
+
}
|
|
4474
|
+
}
|
|
4475
|
+
|
|
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",
|
|
4480
|
+
register(program) {
|
|
4481
|
+
program.command(this.name).description(this.description).argument("<component>", "\u7EC4\u4EF6 ID (\u4F8B\u5982 @miaoda/button)").action(async (component) => {
|
|
4482
|
+
await add(component);
|
|
4483
|
+
});
|
|
4484
|
+
}
|
|
4485
|
+
};
|
|
4486
|
+
var componentCommandGroup = {
|
|
4487
|
+
name: "component",
|
|
4488
|
+
description: "\u7EC4\u4EF6\u76F8\u5173\u547D\u4EE4",
|
|
4489
|
+
commands: [addCommand]
|
|
4490
|
+
};
|
|
4491
|
+
|
|
3455
4492
|
// src/commands/migration/version-manager.ts
|
|
3456
|
-
import
|
|
3457
|
-
import
|
|
4493
|
+
import fs14 from "fs";
|
|
4494
|
+
import path12 from "path";
|
|
3458
4495
|
var PACKAGE_JSON = "package.json";
|
|
3459
4496
|
var VERSION_FIELD = "migrationVersion";
|
|
3460
4497
|
function getPackageJsonPath2() {
|
|
3461
|
-
return
|
|
4498
|
+
return path12.join(process.cwd(), PACKAGE_JSON);
|
|
3462
4499
|
}
|
|
3463
4500
|
function getCurrentVersion() {
|
|
3464
4501
|
const pkgPath = getPackageJsonPath2();
|
|
3465
|
-
if (!
|
|
4502
|
+
if (!fs14.existsSync(pkgPath)) {
|
|
3466
4503
|
throw new Error("package.json not found");
|
|
3467
4504
|
}
|
|
3468
|
-
const pkg2 = JSON.parse(
|
|
4505
|
+
const pkg2 = JSON.parse(fs14.readFileSync(pkgPath, "utf-8"));
|
|
3469
4506
|
return pkg2[VERSION_FIELD] ?? 0;
|
|
3470
4507
|
}
|
|
3471
4508
|
function setCurrentVersion(version) {
|
|
3472
4509
|
const pkgPath = getPackageJsonPath2();
|
|
3473
|
-
const pkg2 = JSON.parse(
|
|
4510
|
+
const pkg2 = JSON.parse(fs14.readFileSync(pkgPath, "utf-8"));
|
|
3474
4511
|
pkg2[VERSION_FIELD] = version;
|
|
3475
|
-
|
|
4512
|
+
fs14.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
|
|
3476
4513
|
}
|
|
3477
4514
|
|
|
3478
4515
|
// src/commands/migration/versions/v001_capability/json-migrator/detector.ts
|
|
3479
|
-
import
|
|
3480
|
-
import
|
|
4516
|
+
import fs16 from "fs";
|
|
4517
|
+
import path14 from "path";
|
|
3481
4518
|
|
|
3482
4519
|
// src/commands/migration/versions/v001_capability/utils.ts
|
|
3483
|
-
import
|
|
3484
|
-
import
|
|
4520
|
+
import fs15 from "fs";
|
|
4521
|
+
import path13 from "path";
|
|
3485
4522
|
var CAPABILITIES_DIR2 = "server/capabilities";
|
|
3486
4523
|
function getProjectRoot3() {
|
|
3487
4524
|
return process.cwd();
|
|
3488
4525
|
}
|
|
3489
4526
|
function getCapabilitiesDir2() {
|
|
3490
|
-
return
|
|
4527
|
+
return path13.join(getProjectRoot3(), CAPABILITIES_DIR2);
|
|
3491
4528
|
}
|
|
3492
4529
|
function getPluginManifestPath2(pluginKey) {
|
|
3493
|
-
return
|
|
4530
|
+
return path13.join(getProjectRoot3(), "node_modules", pluginKey, "manifest.json");
|
|
3494
4531
|
}
|
|
3495
4532
|
|
|
3496
4533
|
// src/commands/migration/versions/v001_capability/json-migrator/detector.ts
|
|
3497
4534
|
function detectJsonMigration() {
|
|
3498
4535
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
3499
|
-
const oldFilePath =
|
|
3500
|
-
if (!
|
|
4536
|
+
const oldFilePath = path14.join(capabilitiesDir, "capabilities.json");
|
|
4537
|
+
if (!fs16.existsSync(oldFilePath)) {
|
|
3501
4538
|
return {
|
|
3502
4539
|
needsMigration: false,
|
|
3503
4540
|
reason: "capabilities.json not found"
|
|
3504
4541
|
};
|
|
3505
4542
|
}
|
|
3506
4543
|
try {
|
|
3507
|
-
const content =
|
|
4544
|
+
const content = fs16.readFileSync(oldFilePath, "utf-8");
|
|
3508
4545
|
const parsed = JSON.parse(content);
|
|
3509
4546
|
if (!Array.isArray(parsed)) {
|
|
3510
4547
|
return {
|
|
@@ -3555,8 +4592,8 @@ async function check(options) {
|
|
|
3555
4592
|
}
|
|
3556
4593
|
|
|
3557
4594
|
// src/commands/migration/versions/v001_capability/json-migrator/index.ts
|
|
3558
|
-
import
|
|
3559
|
-
import
|
|
4595
|
+
import fs17 from "fs";
|
|
4596
|
+
import path15 from "path";
|
|
3560
4597
|
|
|
3561
4598
|
// src/commands/migration/versions/v001_capability/mapping.ts
|
|
3562
4599
|
var DEFAULT_PLUGIN_VERSION = "1.0.0";
|
|
@@ -3786,18 +4823,18 @@ function transformCapabilities(oldCapabilities) {
|
|
|
3786
4823
|
// src/commands/migration/versions/v001_capability/json-migrator/index.ts
|
|
3787
4824
|
function loadExistingCapabilities() {
|
|
3788
4825
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
3789
|
-
if (!
|
|
4826
|
+
if (!fs17.existsSync(capabilitiesDir)) {
|
|
3790
4827
|
return [];
|
|
3791
4828
|
}
|
|
3792
|
-
const files =
|
|
4829
|
+
const files = fs17.readdirSync(capabilitiesDir);
|
|
3793
4830
|
const capabilities = [];
|
|
3794
4831
|
for (const file of files) {
|
|
3795
4832
|
if (file === "capabilities.json" || !file.endsWith(".json")) {
|
|
3796
4833
|
continue;
|
|
3797
4834
|
}
|
|
3798
4835
|
try {
|
|
3799
|
-
const filePath =
|
|
3800
|
-
const content =
|
|
4836
|
+
const filePath = path15.join(capabilitiesDir, file);
|
|
4837
|
+
const content = fs17.readFileSync(filePath, "utf-8");
|
|
3801
4838
|
const capability = JSON.parse(content);
|
|
3802
4839
|
if (capability.id && capability.pluginKey) {
|
|
3803
4840
|
capabilities.push(capability);
|
|
@@ -3855,9 +4892,9 @@ async function migrateJsonFiles(options) {
|
|
|
3855
4892
|
}
|
|
3856
4893
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
3857
4894
|
for (const cap of newCapabilities) {
|
|
3858
|
-
const filePath =
|
|
4895
|
+
const filePath = path15.join(capabilitiesDir, `${cap.id}.json`);
|
|
3859
4896
|
const content = JSON.stringify(cap, null, 2);
|
|
3860
|
-
|
|
4897
|
+
fs17.writeFileSync(filePath, content, "utf-8");
|
|
3861
4898
|
console.log(` \u2713 Created: ${cap.id}.json`);
|
|
3862
4899
|
}
|
|
3863
4900
|
return {
|
|
@@ -3869,11 +4906,11 @@ async function migrateJsonFiles(options) {
|
|
|
3869
4906
|
}
|
|
3870
4907
|
|
|
3871
4908
|
// src/commands/migration/versions/v001_capability/plugin-installer/detector.ts
|
|
3872
|
-
import
|
|
4909
|
+
import fs18 from "fs";
|
|
3873
4910
|
function isPluginInstalled2(pluginKey) {
|
|
3874
4911
|
const actionPlugins = readActionPlugins();
|
|
3875
4912
|
const manifestPath = getPluginManifestPath2(pluginKey);
|
|
3876
|
-
return
|
|
4913
|
+
return fs18.existsSync(manifestPath) && !!actionPlugins[pluginKey];
|
|
3877
4914
|
}
|
|
3878
4915
|
function detectPluginsToInstall(capabilities) {
|
|
3879
4916
|
const pluginKeys = /* @__PURE__ */ new Set();
|
|
@@ -3949,12 +4986,12 @@ async function installPlugins(capabilities, options) {
|
|
|
3949
4986
|
}
|
|
3950
4987
|
|
|
3951
4988
|
// src/commands/migration/versions/v001_capability/code-migrator/index.ts
|
|
3952
|
-
import
|
|
4989
|
+
import path17 from "path";
|
|
3953
4990
|
import { Project as Project3 } from "ts-morph";
|
|
3954
4991
|
|
|
3955
4992
|
// src/commands/migration/versions/v001_capability/code-migrator/scanner.ts
|
|
3956
|
-
import
|
|
3957
|
-
import
|
|
4993
|
+
import fs19 from "fs";
|
|
4994
|
+
import path16 from "path";
|
|
3958
4995
|
var EXCLUDED_DIRS = [
|
|
3959
4996
|
"node_modules",
|
|
3960
4997
|
"dist",
|
|
@@ -3969,9 +5006,9 @@ var EXCLUDED_PATTERNS = [
|
|
|
3969
5006
|
/\.d\.ts$/
|
|
3970
5007
|
];
|
|
3971
5008
|
function scanDirectory(dir, files = []) {
|
|
3972
|
-
const entries =
|
|
5009
|
+
const entries = fs19.readdirSync(dir, { withFileTypes: true });
|
|
3973
5010
|
for (const entry of entries) {
|
|
3974
|
-
const fullPath =
|
|
5011
|
+
const fullPath = path16.join(dir, entry.name);
|
|
3975
5012
|
if (entry.isDirectory()) {
|
|
3976
5013
|
if (EXCLUDED_DIRS.includes(entry.name)) {
|
|
3977
5014
|
continue;
|
|
@@ -3987,14 +5024,14 @@ function scanDirectory(dir, files = []) {
|
|
|
3987
5024
|
return files;
|
|
3988
5025
|
}
|
|
3989
5026
|
function scanServerFiles() {
|
|
3990
|
-
const serverDir =
|
|
3991
|
-
if (!
|
|
5027
|
+
const serverDir = path16.join(getProjectRoot3(), "server");
|
|
5028
|
+
if (!fs19.existsSync(serverDir)) {
|
|
3992
5029
|
return [];
|
|
3993
5030
|
}
|
|
3994
5031
|
return scanDirectory(serverDir);
|
|
3995
5032
|
}
|
|
3996
5033
|
function hasCapabilityImport(filePath) {
|
|
3997
|
-
const content =
|
|
5034
|
+
const content = fs19.readFileSync(filePath, "utf-8");
|
|
3998
5035
|
return /import\s+.*from\s+['"][^'"]*capabilities[^'"]*['"]/.test(content);
|
|
3999
5036
|
}
|
|
4000
5037
|
function scanFilesToMigrate() {
|
|
@@ -4371,7 +5408,7 @@ function analyzeFile(project, filePath, actionNameMap) {
|
|
|
4371
5408
|
const callSites = analyzeCallSites(sourceFile, imports);
|
|
4372
5409
|
const classInfo = analyzeClass(sourceFile);
|
|
4373
5410
|
const { canMigrate, reason } = canAutoMigrate(classInfo);
|
|
4374
|
-
const relativePath =
|
|
5411
|
+
const relativePath = path17.relative(getProjectRoot3(), filePath);
|
|
4375
5412
|
return {
|
|
4376
5413
|
filePath: relativePath,
|
|
4377
5414
|
imports,
|
|
@@ -4382,7 +5419,7 @@ function analyzeFile(project, filePath, actionNameMap) {
|
|
|
4382
5419
|
};
|
|
4383
5420
|
}
|
|
4384
5421
|
function migrateFile(project, analysis, dryRun) {
|
|
4385
|
-
const absolutePath =
|
|
5422
|
+
const absolutePath = path17.join(getProjectRoot3(), analysis.filePath);
|
|
4386
5423
|
if (!analysis.canAutoMigrate) {
|
|
4387
5424
|
return {
|
|
4388
5425
|
filePath: analysis.filePath,
|
|
@@ -4485,17 +5522,17 @@ function getSuggestion(analysis) {
|
|
|
4485
5522
|
}
|
|
4486
5523
|
|
|
4487
5524
|
// src/commands/migration/versions/v001_capability/cleanup.ts
|
|
4488
|
-
import
|
|
4489
|
-
import
|
|
5525
|
+
import fs20 from "fs";
|
|
5526
|
+
import path18 from "path";
|
|
4490
5527
|
function cleanupOldFiles(capabilities, dryRun) {
|
|
4491
5528
|
const deletedFiles = [];
|
|
4492
5529
|
const errors = [];
|
|
4493
5530
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
4494
|
-
const oldJsonPath =
|
|
4495
|
-
if (
|
|
5531
|
+
const oldJsonPath = path18.join(capabilitiesDir, "capabilities.json");
|
|
5532
|
+
if (fs20.existsSync(oldJsonPath)) {
|
|
4496
5533
|
try {
|
|
4497
5534
|
if (!dryRun) {
|
|
4498
|
-
|
|
5535
|
+
fs20.unlinkSync(oldJsonPath);
|
|
4499
5536
|
}
|
|
4500
5537
|
deletedFiles.push("capabilities.json");
|
|
4501
5538
|
} catch (error) {
|
|
@@ -4503,11 +5540,11 @@ function cleanupOldFiles(capabilities, dryRun) {
|
|
|
4503
5540
|
}
|
|
4504
5541
|
}
|
|
4505
5542
|
for (const cap of capabilities) {
|
|
4506
|
-
const tsFilePath =
|
|
4507
|
-
if (
|
|
5543
|
+
const tsFilePath = path18.join(capabilitiesDir, `${cap.id}.ts`);
|
|
5544
|
+
if (fs20.existsSync(tsFilePath)) {
|
|
4508
5545
|
try {
|
|
4509
5546
|
if (!dryRun) {
|
|
4510
|
-
|
|
5547
|
+
fs20.unlinkSync(tsFilePath);
|
|
4511
5548
|
}
|
|
4512
5549
|
deletedFiles.push(`${cap.id}.ts`);
|
|
4513
5550
|
} catch (error) {
|
|
@@ -4523,8 +5560,8 @@ function cleanupOldFiles(capabilities, dryRun) {
|
|
|
4523
5560
|
}
|
|
4524
5561
|
|
|
4525
5562
|
// src/commands/migration/versions/v001_capability/report-generator.ts
|
|
4526
|
-
import
|
|
4527
|
-
import
|
|
5563
|
+
import fs21 from "fs";
|
|
5564
|
+
import path19 from "path";
|
|
4528
5565
|
var REPORT_FILE = "capability-migration-report.md";
|
|
4529
5566
|
function printSummary(result) {
|
|
4530
5567
|
const { jsonMigration, pluginInstallation, codeMigration, cleanup } = result;
|
|
@@ -4687,15 +5724,15 @@ async function generateReport(result) {
|
|
|
4687
5724
|
}
|
|
4688
5725
|
lines.push("");
|
|
4689
5726
|
const logDir = process.env.LOG_DIR || "logs";
|
|
4690
|
-
if (!
|
|
5727
|
+
if (!fs21.existsSync(logDir)) {
|
|
4691
5728
|
return;
|
|
4692
5729
|
}
|
|
4693
|
-
const reportDir =
|
|
4694
|
-
if (!
|
|
4695
|
-
|
|
5730
|
+
const reportDir = path19.join(logDir, "migration");
|
|
5731
|
+
if (!fs21.existsSync(reportDir)) {
|
|
5732
|
+
fs21.mkdirSync(reportDir, { recursive: true });
|
|
4696
5733
|
}
|
|
4697
|
-
const reportPath =
|
|
4698
|
-
|
|
5734
|
+
const reportPath = path19.join(reportDir, REPORT_FILE);
|
|
5735
|
+
fs21.writeFileSync(reportPath, lines.join("\n"), "utf-8");
|
|
4699
5736
|
console.log(`\u{1F4C4} Report generated: ${reportPath}`);
|
|
4700
5737
|
}
|
|
4701
5738
|
|
|
@@ -4872,7 +5909,7 @@ function buildResult(jsonMigration, pluginInstallation, codeMigration, cleanup)
|
|
|
4872
5909
|
}
|
|
4873
5910
|
|
|
4874
5911
|
// src/commands/migration/versions/v001_capability/run.ts
|
|
4875
|
-
async function
|
|
5912
|
+
async function run5(options) {
|
|
4876
5913
|
try {
|
|
4877
5914
|
const migrationOptions = {
|
|
4878
5915
|
dryRun: options.dryRun ?? false
|
|
@@ -4937,7 +5974,7 @@ var v001CapabilityMigration = {
|
|
|
4937
5974
|
name: "capability",
|
|
4938
5975
|
description: "Migrate capability configurations from old format (capabilities.json array) to new format (individual JSON files)",
|
|
4939
5976
|
check,
|
|
4940
|
-
run:
|
|
5977
|
+
run: run5
|
|
4941
5978
|
};
|
|
4942
5979
|
|
|
4943
5980
|
// src/commands/migration/versions/index.ts
|
|
@@ -5227,10 +6264,10 @@ var migrationCommand = {
|
|
|
5227
6264
|
};
|
|
5228
6265
|
|
|
5229
6266
|
// src/commands/read-logs/index.ts
|
|
5230
|
-
import
|
|
6267
|
+
import path20 from "path";
|
|
5231
6268
|
|
|
5232
6269
|
// src/commands/read-logs/std-utils.ts
|
|
5233
|
-
import
|
|
6270
|
+
import fs22 from "fs";
|
|
5234
6271
|
function formatStdPrefixTime(localTime) {
|
|
5235
6272
|
const match = localTime.match(/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/);
|
|
5236
6273
|
if (!match) return localTime;
|
|
@@ -5260,11 +6297,11 @@ function stripPrefixFromStdLine(line) {
|
|
|
5260
6297
|
return `[${time}] ${content}`;
|
|
5261
6298
|
}
|
|
5262
6299
|
function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarker) {
|
|
5263
|
-
const stat =
|
|
6300
|
+
const stat = fs22.statSync(filePath);
|
|
5264
6301
|
if (stat.size === 0) {
|
|
5265
6302
|
return { lines: [], markerFound: false, totalLinesCount: 0 };
|
|
5266
6303
|
}
|
|
5267
|
-
const fd =
|
|
6304
|
+
const fd = fs22.openSync(filePath, "r");
|
|
5268
6305
|
const chunkSize = 64 * 1024;
|
|
5269
6306
|
let position = stat.size;
|
|
5270
6307
|
let remainder = "";
|
|
@@ -5278,7 +6315,7 @@ function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarke
|
|
|
5278
6315
|
const length = Math.min(chunkSize, position);
|
|
5279
6316
|
position -= length;
|
|
5280
6317
|
const buffer = Buffer.alloc(length);
|
|
5281
|
-
|
|
6318
|
+
fs22.readSync(fd, buffer, 0, length, position);
|
|
5282
6319
|
let chunk = buffer.toString("utf8");
|
|
5283
6320
|
if (remainder) {
|
|
5284
6321
|
chunk += remainder;
|
|
@@ -5320,7 +6357,7 @@ function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarke
|
|
|
5320
6357
|
}
|
|
5321
6358
|
}
|
|
5322
6359
|
} finally {
|
|
5323
|
-
|
|
6360
|
+
fs22.closeSync(fd);
|
|
5324
6361
|
}
|
|
5325
6362
|
return { lines: collected.reverse(), markerFound, totalLinesCount };
|
|
5326
6363
|
}
|
|
@@ -5341,21 +6378,21 @@ function readServerStdSegment(filePath, maxLines, offset) {
|
|
|
5341
6378
|
}
|
|
5342
6379
|
|
|
5343
6380
|
// src/commands/read-logs/tail.ts
|
|
5344
|
-
import
|
|
6381
|
+
import fs23 from "fs";
|
|
5345
6382
|
function fileExists(filePath) {
|
|
5346
6383
|
try {
|
|
5347
|
-
|
|
6384
|
+
fs23.accessSync(filePath, fs23.constants.F_OK | fs23.constants.R_OK);
|
|
5348
6385
|
return true;
|
|
5349
6386
|
} catch {
|
|
5350
6387
|
return false;
|
|
5351
6388
|
}
|
|
5352
6389
|
}
|
|
5353
6390
|
function readFileTailLines(filePath, maxLines) {
|
|
5354
|
-
const stat =
|
|
6391
|
+
const stat = fs23.statSync(filePath);
|
|
5355
6392
|
if (stat.size === 0) {
|
|
5356
6393
|
return [];
|
|
5357
6394
|
}
|
|
5358
|
-
const fd =
|
|
6395
|
+
const fd = fs23.openSync(filePath, "r");
|
|
5359
6396
|
const chunkSize = 64 * 1024;
|
|
5360
6397
|
const chunks = [];
|
|
5361
6398
|
let position = stat.size;
|
|
@@ -5365,13 +6402,13 @@ function readFileTailLines(filePath, maxLines) {
|
|
|
5365
6402
|
const length = Math.min(chunkSize, position);
|
|
5366
6403
|
position -= length;
|
|
5367
6404
|
const buffer = Buffer.alloc(length);
|
|
5368
|
-
|
|
6405
|
+
fs23.readSync(fd, buffer, 0, length, position);
|
|
5369
6406
|
chunks.unshift(buffer.toString("utf8"));
|
|
5370
6407
|
const chunkLines = buffer.toString("utf8").split("\n").length - 1;
|
|
5371
6408
|
collectedLines += chunkLines;
|
|
5372
6409
|
}
|
|
5373
6410
|
} finally {
|
|
5374
|
-
|
|
6411
|
+
fs23.closeSync(fd);
|
|
5375
6412
|
}
|
|
5376
6413
|
const content = chunks.join("");
|
|
5377
6414
|
const allLines = content.split("\n");
|
|
@@ -5387,11 +6424,11 @@ function readFileTailLines(filePath, maxLines) {
|
|
|
5387
6424
|
return allLines.slice(allLines.length - maxLines);
|
|
5388
6425
|
}
|
|
5389
6426
|
function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
|
|
5390
|
-
const stat =
|
|
6427
|
+
const stat = fs23.statSync(filePath);
|
|
5391
6428
|
if (stat.size === 0) {
|
|
5392
6429
|
return { lines: [], totalLinesCount: 0 };
|
|
5393
6430
|
}
|
|
5394
|
-
const fd =
|
|
6431
|
+
const fd = fs23.openSync(filePath, "r");
|
|
5395
6432
|
const chunkSize = 64 * 1024;
|
|
5396
6433
|
let position = stat.size;
|
|
5397
6434
|
let remainder = "";
|
|
@@ -5403,7 +6440,7 @@ function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
|
|
|
5403
6440
|
const length = Math.min(chunkSize, position);
|
|
5404
6441
|
position -= length;
|
|
5405
6442
|
const buffer = Buffer.alloc(length);
|
|
5406
|
-
|
|
6443
|
+
fs23.readSync(fd, buffer, 0, length, position);
|
|
5407
6444
|
let chunk = buffer.toString("utf8");
|
|
5408
6445
|
if (remainder) {
|
|
5409
6446
|
chunk += remainder;
|
|
@@ -5434,7 +6471,7 @@ function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
|
|
|
5434
6471
|
}
|
|
5435
6472
|
}
|
|
5436
6473
|
} finally {
|
|
5437
|
-
|
|
6474
|
+
fs23.closeSync(fd);
|
|
5438
6475
|
}
|
|
5439
6476
|
return { lines: collected.reverse(), totalLinesCount };
|
|
5440
6477
|
}
|
|
@@ -5447,13 +6484,19 @@ function readClientStdSegment(filePath, maxLines, offset) {
|
|
|
5447
6484
|
function extractClientStdSegment(lines, maxLines, offset) {
|
|
5448
6485
|
const bodyLines = lines.map(stripPrefixFromStdLine);
|
|
5449
6486
|
const hotStartMarkers = [
|
|
6487
|
+
// Webpack markers
|
|
5450
6488
|
/file change detected\..*incremental compilation/i,
|
|
5451
6489
|
/starting incremental compilation/i,
|
|
5452
6490
|
/starting compilation/i,
|
|
5453
6491
|
/\bcompiling\b/i,
|
|
5454
|
-
/\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
|
|
5455
6497
|
];
|
|
5456
6498
|
const hotEndMarkers = [
|
|
6499
|
+
// Webpack markers
|
|
5457
6500
|
/file change detected\..*incremental compilation/i,
|
|
5458
6501
|
/\bwebpack compiled\b/i,
|
|
5459
6502
|
/compiled successfully/i,
|
|
@@ -5464,14 +6507,25 @@ function extractClientStdSegment(lines, maxLines, offset) {
|
|
|
5464
6507
|
/\bhmr\b/i,
|
|
5465
6508
|
/hot update/i,
|
|
5466
6509
|
/\bhot reload\b/i,
|
|
5467
|
-
/\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
|
|
5468
6518
|
];
|
|
5469
6519
|
const compiledMarkers = [
|
|
6520
|
+
// Webpack markers
|
|
5470
6521
|
/\bwebpack compiled\b/i,
|
|
5471
6522
|
/compiled successfully/i,
|
|
5472
6523
|
/compiled with warnings/i,
|
|
5473
6524
|
/compiled with errors/i,
|
|
5474
|
-
/failed to compile/i
|
|
6525
|
+
/failed to compile/i,
|
|
6526
|
+
// Vite markers
|
|
6527
|
+
/VITE v[\d.]+.*ready/i,
|
|
6528
|
+
/\[vite\].*hmr update/i
|
|
5475
6529
|
];
|
|
5476
6530
|
let startIndex = -1;
|
|
5477
6531
|
for (let i = bodyLines.length - 1; i >= 0; i -= 1) {
|
|
@@ -5524,14 +6578,15 @@ function extractClientStdSegment(lines, maxLines, offset) {
|
|
|
5524
6578
|
}
|
|
5525
6579
|
const segment = startIndex === -1 ? bodyLines : bodyLines.slice(startIndex);
|
|
5526
6580
|
if (segment.length === 0) {
|
|
5527
|
-
return { lines: [], totalLinesCount: 0 };
|
|
6581
|
+
return { lines: [], totalLinesCount: 0, allLines: [] };
|
|
5528
6582
|
}
|
|
5529
6583
|
const totalLinesCount = segment.length;
|
|
5530
6584
|
const endExclusive = Math.max(0, segment.length - offset);
|
|
5531
6585
|
const start = Math.max(0, endExclusive - maxLines);
|
|
5532
6586
|
return {
|
|
5533
6587
|
lines: segment.slice(start, endExclusive),
|
|
5534
|
-
totalLinesCount
|
|
6588
|
+
totalLinesCount,
|
|
6589
|
+
allLines: segment
|
|
5535
6590
|
};
|
|
5536
6591
|
}
|
|
5537
6592
|
|
|
@@ -5558,7 +6613,7 @@ function readDevStdSegment(filePath, maxLines, offset) {
|
|
|
5558
6613
|
}
|
|
5559
6614
|
|
|
5560
6615
|
// src/commands/read-logs/json-lines.ts
|
|
5561
|
-
import
|
|
6616
|
+
import fs24 from "fs";
|
|
5562
6617
|
function normalizePid(value) {
|
|
5563
6618
|
if (typeof value === "number") {
|
|
5564
6619
|
return String(value);
|
|
@@ -5609,11 +6664,11 @@ function buildWantedLevelSet(levels) {
|
|
|
5609
6664
|
return set.size > 0 ? set : null;
|
|
5610
6665
|
}
|
|
5611
6666
|
function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
|
|
5612
|
-
const stat =
|
|
6667
|
+
const stat = fs24.statSync(filePath);
|
|
5613
6668
|
if (stat.size === 0) {
|
|
5614
6669
|
return { lines: [], totalLinesCount: 0 };
|
|
5615
6670
|
}
|
|
5616
|
-
const fd =
|
|
6671
|
+
const fd = fs24.openSync(filePath, "r");
|
|
5617
6672
|
const chunkSize = 64 * 1024;
|
|
5618
6673
|
let position = stat.size;
|
|
5619
6674
|
let remainder = "";
|
|
@@ -5628,7 +6683,7 @@ function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
|
|
|
5628
6683
|
const length = Math.min(chunkSize, position);
|
|
5629
6684
|
position -= length;
|
|
5630
6685
|
const buffer = Buffer.alloc(length);
|
|
5631
|
-
|
|
6686
|
+
fs24.readSync(fd, buffer, 0, length, position);
|
|
5632
6687
|
let chunk = buffer.toString("utf8");
|
|
5633
6688
|
if (remainder) {
|
|
5634
6689
|
chunk += remainder;
|
|
@@ -5690,7 +6745,7 @@ function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
|
|
|
5690
6745
|
}
|
|
5691
6746
|
}
|
|
5692
6747
|
} finally {
|
|
5693
|
-
|
|
6748
|
+
fs24.closeSync(fd);
|
|
5694
6749
|
}
|
|
5695
6750
|
return { lines: collected.reverse(), totalLinesCount };
|
|
5696
6751
|
}
|
|
@@ -5733,11 +6788,11 @@ function extractTraceId(obj) {
|
|
|
5733
6788
|
function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
|
|
5734
6789
|
const wanted = traceId.trim();
|
|
5735
6790
|
if (!wanted) return { lines: [], totalLinesCount: 0 };
|
|
5736
|
-
const stat =
|
|
6791
|
+
const stat = fs24.statSync(filePath);
|
|
5737
6792
|
if (stat.size === 0) {
|
|
5738
6793
|
return { lines: [], totalLinesCount: 0 };
|
|
5739
6794
|
}
|
|
5740
|
-
const fd =
|
|
6795
|
+
const fd = fs24.openSync(filePath, "r");
|
|
5741
6796
|
const chunkSize = 64 * 1024;
|
|
5742
6797
|
let position = stat.size;
|
|
5743
6798
|
let remainder = "";
|
|
@@ -5750,7 +6805,7 @@ function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
|
|
|
5750
6805
|
const length = Math.min(chunkSize, position);
|
|
5751
6806
|
position -= length;
|
|
5752
6807
|
const buffer = Buffer.alloc(length);
|
|
5753
|
-
|
|
6808
|
+
fs24.readSync(fd, buffer, 0, length, position);
|
|
5754
6809
|
let chunk = buffer.toString("utf8");
|
|
5755
6810
|
if (remainder) {
|
|
5756
6811
|
chunk += remainder;
|
|
@@ -5803,7 +6858,7 @@ function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
|
|
|
5803
6858
|
}
|
|
5804
6859
|
}
|
|
5805
6860
|
} finally {
|
|
5806
|
-
|
|
6861
|
+
fs24.closeSync(fd);
|
|
5807
6862
|
}
|
|
5808
6863
|
return { lines: collected.reverse(), totalLinesCount };
|
|
5809
6864
|
}
|
|
@@ -5812,11 +6867,11 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
|
|
|
5812
6867
|
if (!wantedLevelSet) {
|
|
5813
6868
|
return { lines: [], totalLinesCount: 0 };
|
|
5814
6869
|
}
|
|
5815
|
-
const stat =
|
|
6870
|
+
const stat = fs24.statSync(filePath);
|
|
5816
6871
|
if (stat.size === 0) {
|
|
5817
6872
|
return { lines: [], totalLinesCount: 0 };
|
|
5818
6873
|
}
|
|
5819
|
-
const fd =
|
|
6874
|
+
const fd = fs24.openSync(filePath, "r");
|
|
5820
6875
|
const chunkSize = 64 * 1024;
|
|
5821
6876
|
let position = stat.size;
|
|
5822
6877
|
let remainder = "";
|
|
@@ -5828,7 +6883,7 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
|
|
|
5828
6883
|
const length = Math.min(chunkSize, position);
|
|
5829
6884
|
position -= length;
|
|
5830
6885
|
const buffer = Buffer.alloc(length);
|
|
5831
|
-
|
|
6886
|
+
fs24.readSync(fd, buffer, 0, length, position);
|
|
5832
6887
|
let chunk = buffer.toString("utf8");
|
|
5833
6888
|
if (remainder) {
|
|
5834
6889
|
chunk += remainder;
|
|
@@ -5875,7 +6930,7 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
|
|
|
5875
6930
|
}
|
|
5876
6931
|
}
|
|
5877
6932
|
} finally {
|
|
5878
|
-
|
|
6933
|
+
fs24.closeSync(fd);
|
|
5879
6934
|
}
|
|
5880
6935
|
return { lines: collected.reverse(), totalLinesCount };
|
|
5881
6936
|
}
|
|
@@ -6068,12 +7123,21 @@ async function readLatestLogLinesMeta(options) {
|
|
|
6068
7123
|
return { lines: [], totalLinesCount: 0 };
|
|
6069
7124
|
}
|
|
6070
7125
|
async function readLogsJsonResult(options) {
|
|
6071
|
-
const { lines, totalLinesCount } = await readLatestLogLinesMeta(options);
|
|
7126
|
+
const { lines, totalLinesCount, allLines } = await readLatestLogLinesMeta(options);
|
|
6072
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);
|
|
6073
7137
|
return {
|
|
6074
|
-
hasError: hasErrorInStdLines(
|
|
6075
|
-
totalLinesCount,
|
|
6076
|
-
logs:
|
|
7138
|
+
hasError: hasErrorInStdLines(linesForErrorCheck),
|
|
7139
|
+
totalLinesCount: filteredLines.length,
|
|
7140
|
+
logs: paginatedLogs
|
|
6077
7141
|
};
|
|
6078
7142
|
}
|
|
6079
7143
|
const logs = [];
|
|
@@ -6100,34 +7164,34 @@ async function readLogsJsonResult(options) {
|
|
|
6100
7164
|
};
|
|
6101
7165
|
}
|
|
6102
7166
|
function resolveLogFilePath(logDir, type) {
|
|
6103
|
-
const base =
|
|
7167
|
+
const base = path20.isAbsolute(logDir) ? logDir : path20.join(process.cwd(), logDir);
|
|
6104
7168
|
if (type === "server") {
|
|
6105
|
-
return
|
|
7169
|
+
return path20.join(base, "server.log");
|
|
6106
7170
|
}
|
|
6107
7171
|
if (type === "trace") {
|
|
6108
|
-
return
|
|
7172
|
+
return path20.join(base, "trace.log");
|
|
6109
7173
|
}
|
|
6110
7174
|
if (type === "server-std") {
|
|
6111
|
-
return
|
|
7175
|
+
return path20.join(base, "server.std.log");
|
|
6112
7176
|
}
|
|
6113
7177
|
if (type === "client-std") {
|
|
6114
|
-
return
|
|
7178
|
+
return path20.join(base, "client.std.log");
|
|
6115
7179
|
}
|
|
6116
7180
|
if (type === "dev") {
|
|
6117
|
-
return
|
|
7181
|
+
return path20.join(base, "dev.log");
|
|
6118
7182
|
}
|
|
6119
7183
|
if (type === "dev-std") {
|
|
6120
|
-
return
|
|
7184
|
+
return path20.join(base, "dev.std.log");
|
|
6121
7185
|
}
|
|
6122
7186
|
if (type === "install-dep-std") {
|
|
6123
|
-
return
|
|
7187
|
+
return path20.join(base, "install-dep.std.log");
|
|
6124
7188
|
}
|
|
6125
7189
|
if (type === "browser") {
|
|
6126
|
-
return
|
|
7190
|
+
return path20.join(base, "browser.log");
|
|
6127
7191
|
}
|
|
6128
7192
|
throw new Error(`Unsupported log type: ${type}`);
|
|
6129
7193
|
}
|
|
6130
|
-
async function
|
|
7194
|
+
async function run6(options) {
|
|
6131
7195
|
const result = await readLogsJsonResult(options);
|
|
6132
7196
|
process.stdout.write(JSON.stringify(result) + "\n");
|
|
6133
7197
|
}
|
|
@@ -6169,7 +7233,7 @@ var readLogsCommand = {
|
|
|
6169
7233
|
const offset = parseNonNegativeInt(rawOptions.offset, "--offset");
|
|
6170
7234
|
const traceId = typeof rawOptions.traceId === "string" ? rawOptions.traceId : void 0;
|
|
6171
7235
|
const levels = parseCommaSeparatedList(rawOptions.level);
|
|
6172
|
-
await
|
|
7236
|
+
await run6({ logDir, type, maxLines, offset, traceId, levels });
|
|
6173
7237
|
} catch (error) {
|
|
6174
7238
|
const message = error instanceof Error ? error.message : String(error);
|
|
6175
7239
|
process.stderr.write(message + "\n");
|
|
@@ -6179,23 +7243,352 @@ var readLogsCommand = {
|
|
|
6179
7243
|
}
|
|
6180
7244
|
};
|
|
6181
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 os2 from "os";
|
|
7355
|
+
import * as path21 from "path";
|
|
7356
|
+
import { execFileSync } from "child_process";
|
|
7357
|
+
function readCredentialsFromEnv() {
|
|
7358
|
+
const uploadPrefix = process.env.STATIC_UPLOAD_PREFIX;
|
|
7359
|
+
const uploadID = process.env.STATIC_UPLOAD_ID;
|
|
7360
|
+
const bucketId = process.env.STATIC_UPLOAD_BUCKET_ID;
|
|
7361
|
+
const accessKeyID = process.env.STATIC_UPLOAD_AK;
|
|
7362
|
+
const secretAccessKey = process.env.STATIC_UPLOAD_SK;
|
|
7363
|
+
const sessionToken = process.env.STATIC_UPLOAD_TOKEN;
|
|
7364
|
+
if (!uploadPrefix || !uploadID || !bucketId || !accessKeyID || !secretAccessKey || !sessionToken) {
|
|
7365
|
+
return null;
|
|
7366
|
+
}
|
|
7367
|
+
return { uploadPrefix, uploadID, bucketId, accessKeyID, secretAccessKey, sessionToken };
|
|
7368
|
+
}
|
|
7369
|
+
var LOG_PREFIX = "[upload-static]";
|
|
7370
|
+
async function uploadStatic(options) {
|
|
7371
|
+
try {
|
|
7372
|
+
const {
|
|
7373
|
+
appId,
|
|
7374
|
+
staticDir = UPLOAD_STATIC_DEFAULTS.staticDir,
|
|
7375
|
+
tosutilPath = UPLOAD_STATIC_DEFAULTS.tosutilPath,
|
|
7376
|
+
endpoint = UPLOAD_STATIC_DEFAULTS.endpoint,
|
|
7377
|
+
region = UPLOAD_STATIC_DEFAULTS.region
|
|
7378
|
+
} = options;
|
|
7379
|
+
const resolvedStaticDir = path21.resolve(staticDir);
|
|
7380
|
+
if (!fs25.existsSync(resolvedStaticDir)) {
|
|
7381
|
+
console.error(`${LOG_PREFIX} \u76EE\u5F55\u4E0D\u5B58\u5728: ${resolvedStaticDir}\uFF0C\u8DF3\u8FC7\u4E0A\u4F20`);
|
|
7382
|
+
return;
|
|
7383
|
+
}
|
|
7384
|
+
if (isDirEmpty(resolvedStaticDir)) {
|
|
7385
|
+
console.error(`${LOG_PREFIX} \u76EE\u5F55\u4E3A\u7A7A: ${resolvedStaticDir}\uFF0C\u8DF3\u8FC7\u4E0A\u4F20`);
|
|
7386
|
+
return;
|
|
7387
|
+
}
|
|
7388
|
+
const resolvedTosutil = resolveTosutilPath(tosutilPath);
|
|
7389
|
+
if (!resolvedTosutil) {
|
|
7390
|
+
throw new Error(
|
|
7391
|
+
`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`
|
|
7392
|
+
);
|
|
7393
|
+
}
|
|
7394
|
+
let uploadPrefix;
|
|
7395
|
+
let uploadID;
|
|
7396
|
+
let bucketId;
|
|
7397
|
+
let accessKeyID;
|
|
7398
|
+
let secretAccessKey;
|
|
7399
|
+
let sessionToken;
|
|
7400
|
+
const envCredentials = readCredentialsFromEnv();
|
|
7401
|
+
if (envCredentials) {
|
|
7402
|
+
console.error(`${LOG_PREFIX} \u4F7F\u7528\u73AF\u5883\u53D8\u91CF\u4E2D\u7684\u4E0A\u4F20\u51ED\u8BC1`);
|
|
7403
|
+
({ uploadPrefix, uploadID, bucketId, accessKeyID, secretAccessKey, sessionToken } = envCredentials);
|
|
7404
|
+
} else {
|
|
7405
|
+
console.error(`${LOG_PREFIX} \u73AF\u5883\u53D8\u91CF\u672A\u8BBE\u7F6E\uFF0C\u8C03\u7528 preUploadStatic...`);
|
|
7406
|
+
bucketId = await resolveBucketId(appId);
|
|
7407
|
+
const preUploadResp = await fetchPreUpload(appId, bucketId);
|
|
7408
|
+
const { uploadCredential } = preUploadResp.data;
|
|
7409
|
+
({ uploadPrefix, uploadID } = preUploadResp.data);
|
|
7410
|
+
({ AccessKeyID: accessKeyID, SecretAccessKey: secretAccessKey, SessionToken: sessionToken } = uploadCredential);
|
|
7411
|
+
}
|
|
7412
|
+
console.error(`${LOG_PREFIX} \u4E0A\u4F20\u76EE\u6807: ${uploadPrefix}`);
|
|
7413
|
+
const confPath = path21.join(os2.tmpdir(), `.tosutilconfig-static-${process.pid}`);
|
|
7414
|
+
fs25.writeFileSync(confPath, "");
|
|
7415
|
+
try {
|
|
7416
|
+
console.error(`${LOG_PREFIX} \u914D\u7F6E tosutil...`);
|
|
7417
|
+
configureTosutil(resolvedTosutil, confPath, {
|
|
7418
|
+
endpoint,
|
|
7419
|
+
region,
|
|
7420
|
+
accessKeyID,
|
|
7421
|
+
secretAccessKey,
|
|
7422
|
+
sessionToken
|
|
7423
|
+
});
|
|
7424
|
+
console.error(`${LOG_PREFIX} \u4E0A\u4F20 ${resolvedStaticDir} -> ${uploadPrefix}`);
|
|
7425
|
+
uploadToTos(resolvedTosutil, confPath, resolvedStaticDir, uploadPrefix);
|
|
7426
|
+
} finally {
|
|
7427
|
+
try {
|
|
7428
|
+
fs25.unlinkSync(confPath);
|
|
7429
|
+
} catch {
|
|
7430
|
+
}
|
|
7431
|
+
}
|
|
7432
|
+
console.error(`${LOG_PREFIX} tosutil \u4E0A\u4F20\u5B8C\u6210`);
|
|
7433
|
+
console.error(`${LOG_PREFIX} \u8C03\u7528 callbackStatic (uploadID: ${uploadID})...`);
|
|
7434
|
+
const callbackResp = await uploadStaticAttachmentCallback(appId, bucketId, { uploadID });
|
|
7435
|
+
if (callbackResp.status_code !== "0") {
|
|
7436
|
+
throw new Error(`callbackStatic \u8FD4\u56DE\u5F02\u5E38, status_code: ${callbackResp.status_code}`);
|
|
7437
|
+
}
|
|
7438
|
+
const attachments = callbackResp.data?.attachments || [];
|
|
7439
|
+
console.error(`${LOG_PREFIX} \u4E0A\u4F20\u5B8C\u6210\uFF0C\u5171 ${attachments.length} \u4E2A\u6587\u4EF6`);
|
|
7440
|
+
console.log(JSON.stringify(callbackResp));
|
|
7441
|
+
} catch (error) {
|
|
7442
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7443
|
+
console.error(`${LOG_PREFIX} Error: ${message}`);
|
|
7444
|
+
process.exit(1);
|
|
7445
|
+
}
|
|
7446
|
+
}
|
|
7447
|
+
function resolveTosutilPath(tosutilPath) {
|
|
7448
|
+
if (path21.isAbsolute(tosutilPath)) {
|
|
7449
|
+
return fs25.existsSync(tosutilPath) ? tosutilPath : null;
|
|
7450
|
+
}
|
|
7451
|
+
try {
|
|
7452
|
+
const resolved = execFileSync("which", [tosutilPath], { encoding: "utf-8" }).trim();
|
|
7453
|
+
return resolved || null;
|
|
7454
|
+
} catch {
|
|
7455
|
+
return null;
|
|
7456
|
+
}
|
|
7457
|
+
}
|
|
7458
|
+
async function fetchPreUpload(appId, bucketId) {
|
|
7459
|
+
const response = await preUploadStaticAttachment(appId, bucketId);
|
|
7460
|
+
if (response.status_code !== "0") {
|
|
7461
|
+
throw new Error(`preUploadStatic \u8FD4\u56DE\u5F02\u5E38, status_code: ${response.status_code}`);
|
|
7462
|
+
}
|
|
7463
|
+
const { uploadPrefix, uploadID, uploadCredential } = response.data || {};
|
|
7464
|
+
if (!uploadPrefix || !uploadID) {
|
|
7465
|
+
throw new Error("preUploadStatic \u8FD4\u56DE\u6570\u636E\u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5C11 uploadPrefix \u6216 uploadID");
|
|
7466
|
+
}
|
|
7467
|
+
if (!uploadCredential?.AccessKeyID || !uploadCredential?.SecretAccessKey || !uploadCredential?.SessionToken) {
|
|
7468
|
+
throw new Error("preUploadStatic \u8FD4\u56DE\u7684\u51ED\u8BC1\u5B57\u6BB5\u4E0D\u5B8C\u6574");
|
|
7469
|
+
}
|
|
7470
|
+
return response;
|
|
7471
|
+
}
|
|
7472
|
+
function configureTosutil(tosutilPath, confPath, config) {
|
|
7473
|
+
const { endpoint, region, accessKeyID, secretAccessKey, sessionToken } = config;
|
|
7474
|
+
execFileSync(
|
|
7475
|
+
tosutilPath,
|
|
7476
|
+
["config", "-conf", confPath, "-e", endpoint, "-i", accessKeyID, "-k", secretAccessKey, "-t", sessionToken, "-re", region],
|
|
7477
|
+
{ stdio: "pipe" }
|
|
7478
|
+
);
|
|
7479
|
+
}
|
|
7480
|
+
function uploadToTos(tosutilPath, confPath, sourceDir, destUrl) {
|
|
7481
|
+
execFileSync(
|
|
7482
|
+
tosutilPath,
|
|
7483
|
+
["cp", "-conf", confPath, sourceDir, destUrl, "-r", "-flat", "-j", "5", "-p", "3", "-ps", "10485760", "-f"],
|
|
7484
|
+
{ stdio: "inherit" }
|
|
7485
|
+
);
|
|
7486
|
+
}
|
|
7487
|
+
async function resolveBucketId(appId) {
|
|
7488
|
+
console.error(`${LOG_PREFIX} \u83B7\u53D6\u9ED8\u8BA4\u5B58\u50A8\u6876...`);
|
|
7489
|
+
const bucketId = await getDefaultBucketId(appId);
|
|
7490
|
+
console.error(`${LOG_PREFIX} \u9ED8\u8BA4\u5B58\u50A8\u6876: ${bucketId}`);
|
|
7491
|
+
return bucketId;
|
|
7492
|
+
}
|
|
7493
|
+
function isDirEmpty(dirPath) {
|
|
7494
|
+
const entries = fs25.readdirSync(dirPath);
|
|
7495
|
+
return entries.length === 0;
|
|
7496
|
+
}
|
|
7497
|
+
|
|
7498
|
+
// src/commands/build/pre-upload-static.handler.ts
|
|
7499
|
+
var LOG_PREFIX2 = "[pre-upload-static]";
|
|
7500
|
+
function shellEscape(value) {
|
|
7501
|
+
return value.replace(/["\\`$]/g, "\\$&");
|
|
7502
|
+
}
|
|
7503
|
+
async function preUploadStatic(options) {
|
|
7504
|
+
try {
|
|
7505
|
+
const { appId } = options;
|
|
7506
|
+
console.error(`${LOG_PREFIX2} \u83B7\u53D6\u9ED8\u8BA4\u5B58\u50A8\u6876...`);
|
|
7507
|
+
const bucketId = await getDefaultBucketId(appId);
|
|
7508
|
+
console.error(`${LOG_PREFIX2} \u9ED8\u8BA4\u5B58\u50A8\u6876: ${bucketId}`);
|
|
7509
|
+
console.error(`${LOG_PREFIX2} \u8C03\u7528 preUploadStatic...`);
|
|
7510
|
+
const response = await preUploadStaticAttachment(appId, bucketId);
|
|
7511
|
+
if (response.status_code !== "0") {
|
|
7512
|
+
console.error(`${LOG_PREFIX2} preUploadStatic \u8FD4\u56DE\u5F02\u5E38, status_code: ${response.status_code}`);
|
|
7513
|
+
return;
|
|
7514
|
+
}
|
|
7515
|
+
const { downloadURLPrefix, uploadPrefix, uploadID, uploadCredential } = response.data || {};
|
|
7516
|
+
if (!downloadURLPrefix || !uploadPrefix || !uploadID) {
|
|
7517
|
+
console.error(`${LOG_PREFIX2} preUploadStatic \u8FD4\u56DE\u6570\u636E\u4E0D\u5B8C\u6574`);
|
|
7518
|
+
return;
|
|
7519
|
+
}
|
|
7520
|
+
if (!uploadCredential?.AccessKeyID || !uploadCredential?.SecretAccessKey || !uploadCredential?.SessionToken) {
|
|
7521
|
+
console.error(`${LOG_PREFIX2} preUploadStatic \u8FD4\u56DE\u7684\u51ED\u8BC1\u5B57\u6BB5\u4E0D\u5B8C\u6574`);
|
|
7522
|
+
return;
|
|
7523
|
+
}
|
|
7524
|
+
console.log(`export STATIC_ASSETS_BASE_URL="${shellEscape(downloadURLPrefix)}"`);
|
|
7525
|
+
console.log(`export STATIC_UPLOAD_PREFIX="${shellEscape(uploadPrefix)}"`);
|
|
7526
|
+
console.log(`export STATIC_UPLOAD_ID="${shellEscape(uploadID)}"`);
|
|
7527
|
+
console.log(`export STATIC_UPLOAD_AK="${shellEscape(uploadCredential.AccessKeyID)}"`);
|
|
7528
|
+
console.log(`export STATIC_UPLOAD_SK="${shellEscape(uploadCredential.SecretAccessKey)}"`);
|
|
7529
|
+
console.log(`export STATIC_UPLOAD_TOKEN="${shellEscape(uploadCredential.SessionToken)}"`);
|
|
7530
|
+
console.log(`export STATIC_UPLOAD_BUCKET_ID="${shellEscape(bucketId)}"`);
|
|
7531
|
+
console.error(`${LOG_PREFIX2} \u73AF\u5883\u53D8\u91CF\u5DF2\u8F93\u51FA`);
|
|
7532
|
+
} catch (error) {
|
|
7533
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7534
|
+
console.error(`${LOG_PREFIX2} Warning: ${message}`);
|
|
7535
|
+
}
|
|
7536
|
+
}
|
|
7537
|
+
|
|
7538
|
+
// src/commands/build/index.ts
|
|
7539
|
+
var getTokenCommand = {
|
|
7540
|
+
name: "get-token",
|
|
7541
|
+
description: "Get artifact upload credential (STI token)",
|
|
7542
|
+
register(program) {
|
|
7543
|
+
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) => {
|
|
7544
|
+
await getToken(options);
|
|
7545
|
+
});
|
|
7546
|
+
}
|
|
7547
|
+
};
|
|
7548
|
+
var uploadStaticCommand = {
|
|
7549
|
+
name: "upload-static",
|
|
7550
|
+
description: "Upload shared/static files to TOS",
|
|
7551
|
+
register(program) {
|
|
7552
|
+
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) => {
|
|
7553
|
+
await uploadStatic(options);
|
|
7554
|
+
});
|
|
7555
|
+
}
|
|
7556
|
+
};
|
|
7557
|
+
var preUploadStaticCommand = {
|
|
7558
|
+
name: "pre-upload-static",
|
|
7559
|
+
description: "Get TOS upload info and output as env vars for build.sh eval",
|
|
7560
|
+
register(program) {
|
|
7561
|
+
program.command(this.name).description(this.description).requiredOption("--app-id <id>", "Application ID").action(async (options) => {
|
|
7562
|
+
await preUploadStatic(options);
|
|
7563
|
+
});
|
|
7564
|
+
}
|
|
7565
|
+
};
|
|
7566
|
+
var buildCommandGroup = {
|
|
7567
|
+
name: "build",
|
|
7568
|
+
description: "Build related commands",
|
|
7569
|
+
commands: [getTokenCommand, uploadStaticCommand, preUploadStaticCommand]
|
|
7570
|
+
};
|
|
7571
|
+
|
|
6182
7572
|
// src/commands/index.ts
|
|
6183
7573
|
var commands = [
|
|
6184
7574
|
genDbSchemaCommand,
|
|
6185
7575
|
syncCommand,
|
|
7576
|
+
upgradeCommand,
|
|
6186
7577
|
actionPluginCommandGroup,
|
|
6187
7578
|
capabilityCommandGroup,
|
|
7579
|
+
componentCommandGroup,
|
|
6188
7580
|
migrationCommand,
|
|
6189
|
-
readLogsCommand
|
|
7581
|
+
readLogsCommand,
|
|
7582
|
+
buildCommandGroup
|
|
6190
7583
|
];
|
|
6191
7584
|
|
|
6192
7585
|
// src/index.ts
|
|
6193
|
-
var envPath =
|
|
6194
|
-
if (
|
|
7586
|
+
var envPath = path22.join(process.cwd(), ".env");
|
|
7587
|
+
if (fs26.existsSync(envPath)) {
|
|
6195
7588
|
dotenvConfig({ path: envPath });
|
|
6196
7589
|
}
|
|
6197
|
-
var __dirname =
|
|
6198
|
-
var pkg = JSON.parse(
|
|
7590
|
+
var __dirname = path22.dirname(fileURLToPath5(import.meta.url));
|
|
7591
|
+
var pkg = JSON.parse(fs26.readFileSync(path22.join(__dirname, "../package.json"), "utf-8"));
|
|
6199
7592
|
var cli = new FullstackCLI(pkg.version);
|
|
6200
7593
|
cli.useAll(commands);
|
|
6201
7594
|
cli.run();
|