ai-project-manage-cli 6.0.83 → 6.0.85

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.
Files changed (2) hide show
  1. package/dist/index.js +666 -219
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -490,6 +490,16 @@ var requestConfig = {
490
490
  completeTaskDeployment: defineEndpoint({
491
491
  method: "PUT",
492
492
  path: "/cli/task-deployments/complete"
493
+ }),
494
+ getDeployArtifactStorage: defineEndpoint(
495
+ {
496
+ method: "GET",
497
+ path: "/cli/deploy-artifact-storage"
498
+ }
499
+ ),
500
+ attachTaskDeploymentArtifact: defineEndpoint({
501
+ method: "PUT",
502
+ path: "/cli/task-deployments/artifact"
493
503
  })
494
504
  }
495
505
  };
@@ -835,6 +845,24 @@ async function isGitRepo(cwd) {
835
845
  return false;
836
846
  }
837
847
  }
848
+ async function ensureGitRepo(cwd) {
849
+ await execGit(cwd, ["rev-parse", "--git-dir"], true);
850
+ }
851
+ async function getCurrentBranch(cwd) {
852
+ return (await execGit(cwd, ["rev-parse", "--abbrev-ref", "HEAD"], true)).trim();
853
+ }
854
+ async function isWorkingTreeDirty(cwd) {
855
+ const out = await execGit(cwd, ["status", "--porcelain"], true);
856
+ return out.trim().length > 0;
857
+ }
858
+ async function remoteBranchExists(cwd, branch) {
859
+ const out = await execGit(
860
+ cwd,
861
+ ["ls-remote", "--heads", "origin", branch],
862
+ true
863
+ );
864
+ return out.trim().length > 0;
865
+ }
838
866
  async function hasUpstream(cwd) {
839
867
  try {
840
868
  await execGit(cwd, ["rev-parse", "--abbrev-ref", "@{upstream}"], true);
@@ -1023,14 +1051,14 @@ async function execGit2(cwd, args, quiet) {
1023
1051
  );
1024
1052
  }
1025
1053
  }
1026
- async function ensureGitRepo(cwd) {
1054
+ async function ensureGitRepo2(cwd) {
1027
1055
  await execGit2(cwd, ["rev-parse", "--git-dir"], true);
1028
1056
  }
1029
- async function getCurrentBranch(cwd) {
1057
+ async function getCurrentBranch2(cwd) {
1030
1058
  const name = (await execGit2(cwd, ["rev-parse", "--abbrev-ref", "HEAD"], true)).trim();
1031
1059
  return name;
1032
1060
  }
1033
- async function isWorkingTreeDirty(cwd) {
1061
+ async function isWorkingTreeDirty2(cwd) {
1034
1062
  const out = await execGit2(cwd, ["status", "--porcelain"], true);
1035
1063
  return out.trim().length > 0;
1036
1064
  }
@@ -1055,11 +1083,11 @@ async function localBranchExists(cwd, branch) {
1055
1083
  }
1056
1084
  }
1057
1085
  async function commitWorkingTreeIfDirty(cwd, message) {
1058
- await ensureGitRepo(cwd);
1059
- if (!await isWorkingTreeDirty(cwd)) {
1086
+ await ensureGitRepo2(cwd);
1087
+ if (!await isWorkingTreeDirty2(cwd)) {
1060
1088
  return false;
1061
1089
  }
1062
- const commitMessage = message?.trim() || `chore(apm): \u540C\u6B65\u5DE5\u4F5C\u533A (${await getCurrentBranch(cwd)})`;
1090
+ const commitMessage = message?.trim() || `chore(apm): \u540C\u6B65\u5DE5\u4F5C\u533A (${await getCurrentBranch2(cwd)})`;
1063
1091
  await execGit2(cwd, ["add", "-A"]);
1064
1092
  await execGit2(cwd, ["commit", "-m", commitMessage]);
1065
1093
  console.log(`[apm] \u5DF2\u63D0\u4EA4\u5DE5\u4F5C\u533A\u53D8\u66F4: ${commitMessage}`);
@@ -1068,14 +1096,14 @@ async function commitWorkingTreeIfDirty(cwd, message) {
1068
1096
  async function ensureFeatureBranch(branch, baselineBranch, options) {
1069
1097
  const cwd = options.cwd ?? process.cwd();
1070
1098
  const commitMessage = options.message?.trim() || `chore(apm): \u540C\u6B65\u5DE5\u4F5C\u533A (${branch})`;
1071
- await ensureGitRepo(cwd);
1099
+ await ensureGitRepo2(cwd);
1072
1100
  if (!await remoteHeadBranchExists(cwd, baselineBranch)) {
1073
1101
  throw new Error(
1074
1102
  `[apm] \u8FDC\u7A0B\u4E0D\u5B58\u5728\u57FA\u7EBF\u5206\u652F ${baselineBranch}\uFF0C\u8BF7\u786E\u8BA4\u4ED3\u5E93\u9ED8\u8BA4\u5206\u652F\u5DF2\u63A8\u9001\u5230 origin`
1075
1103
  );
1076
1104
  }
1077
- const current = await getCurrentBranch(cwd);
1078
- const dirty = await isWorkingTreeDirty(cwd);
1105
+ const current = await getCurrentBranch2(cwd);
1106
+ const dirty = await isWorkingTreeDirty2(cwd);
1079
1107
  if (dirty) {
1080
1108
  if (current === branch) {
1081
1109
  await commitWorkingTreeIfDirty(cwd, commitMessage);
@@ -1089,7 +1117,7 @@ async function ensureFeatureBranch(branch, baselineBranch, options) {
1089
1117
  ]);
1090
1118
  }
1091
1119
  }
1092
- const onTargetBranch = await getCurrentBranch(cwd) === branch;
1120
+ const onTargetBranch = await getCurrentBranch2(cwd) === branch;
1093
1121
  if (onTargetBranch) {
1094
1122
  await execGit2(cwd, ["fetch", "origin", baselineBranch]);
1095
1123
  await execGit2(cwd, ["merge", `origin/${baselineBranch}`, "--no-edit"]);
@@ -1099,7 +1127,7 @@ async function ensureFeatureBranch(branch, baselineBranch, options) {
1099
1127
  await execGit2(cwd, ["fetch", "origin", branch]);
1100
1128
  await execGit2(cwd, ["checkout", "-B", branch, `origin/${branch}`]);
1101
1129
  } else if (await localBranchExists(cwd, branch)) {
1102
- if (await getCurrentBranch(cwd) !== branch) {
1130
+ if (await getCurrentBranch2(cwd) !== branch) {
1103
1131
  await execGit2(cwd, ["checkout", branch]);
1104
1132
  }
1105
1133
  console.log(`[apm] \u5206\u652F ${branch} \u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u521B\u5EFA`);
@@ -1115,7 +1143,7 @@ async function ensureFeatureBranch(branch, baselineBranch, options) {
1115
1143
  await execGit2(cwd, ["push", "-u", "origin", branch]);
1116
1144
  } catch (err) {
1117
1145
  if (await localBranchExists(cwd, branch)) {
1118
- if (await getCurrentBranch(cwd) !== branch) {
1146
+ if (await getCurrentBranch2(cwd) !== branch) {
1119
1147
  await execGit2(cwd, ["checkout", branch]);
1120
1148
  }
1121
1149
  console.log(`[apm] \u5206\u652F ${branch} \u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u521B\u5EFA`);
@@ -1178,10 +1206,10 @@ async function execGit3(cwd, args, quiet) {
1178
1206
  );
1179
1207
  }
1180
1208
  }
1181
- async function ensureGitRepo2(cwd) {
1209
+ async function ensureGitRepo3(cwd) {
1182
1210
  await execGit3(cwd, ["rev-parse", "--git-dir"], true);
1183
1211
  }
1184
- async function getCurrentBranch2(cwd) {
1212
+ async function getCurrentBranch3(cwd) {
1185
1213
  return (await execGit3(cwd, ["rev-parse", "--abbrev-ref", "HEAD"], true)).trim();
1186
1214
  }
1187
1215
  async function resolveBaselineBranch(cwd, api) {
@@ -1197,7 +1225,7 @@ async function resolveBaselineBranch(cwd, api) {
1197
1225
  throw new Error("[apm] \u5E73\u53F0\u8FD4\u56DE\u7684\u57FA\u7EBF\u5206\u652F\u540D\u4E3A\u7A7A");
1198
1226
  }
1199
1227
  await execGit3(cwd, ["fetch", "origin", baselineBranch], true);
1200
- if (!await remoteBranchExists(cwd, baselineBranch)) {
1228
+ if (!await remoteBranchExists2(cwd, baselineBranch)) {
1201
1229
  throw new Error(
1202
1230
  `[apm] \u8FDC\u7A0B\u4E0D\u5B58\u5728\u57FA\u7EBF\u5206\u652F origin/${baselineBranch}\uFF0C\u8BF7\u786E\u8BA4\u4ED3\u5E93\u9ED8\u8BA4\u5206\u652F\u5DF2\u63A8\u9001\u5230 origin`
1203
1231
  );
@@ -1264,7 +1292,7 @@ async function localBranchExists2(cwd, branch) {
1264
1292
  return false;
1265
1293
  }
1266
1294
  }
1267
- async function remoteBranchExists(cwd, branch) {
1295
+ async function remoteBranchExists2(cwd, branch) {
1268
1296
  const out = await execGit3(
1269
1297
  cwd,
1270
1298
  ["ls-remote", "--heads", "origin", branch],
@@ -1298,7 +1326,7 @@ async function runCleanBranches(options = {}) {
1298
1326
  const cwd = options.cwd ?? process.cwd();
1299
1327
  const dryRun = options.dryRun ?? false;
1300
1328
  const includeRemote = options.includeRemote ?? false;
1301
- await ensureGitRepo2(cwd);
1329
+ await ensureGitRepo3(cwd);
1302
1330
  await execGit3(cwd, ["fetch", "--prune", "origin"], true);
1303
1331
  const cfg = await ensureLoggedConfig();
1304
1332
  const api = createApmApiClient(cfg);
@@ -1335,7 +1363,7 @@ async function runCleanBranches(options = {}) {
1335
1363
  console.log("[apm] \u6CA1\u6709\u9700\u8981\u6E05\u7406\u7684 feat/session-* \u5206\u652F");
1336
1364
  return;
1337
1365
  }
1338
- let currentBranch = await getCurrentBranch2(cwd);
1366
+ let currentBranch = await getCurrentBranch3(cwd);
1339
1367
  for (const item of toDelete) {
1340
1368
  const { branch, sessionId, reason } = item;
1341
1369
  const label = `${branch} (${sessionId}: ${reason})`;
@@ -1357,7 +1385,7 @@ async function runCleanBranches(options = {}) {
1357
1385
  await execGit3(cwd, ["branch", "-D", branch], true);
1358
1386
  console.log(`[apm] \u5DF2\u5220\u9664\u672C\u5730\u5206\u652F ${branch}`);
1359
1387
  }
1360
- if (await remoteBranchExists(cwd, branch)) {
1388
+ if (await remoteBranchExists2(cwd, branch)) {
1361
1389
  await execGit3(cwd, ["push", "origin", "--delete", branch], true);
1362
1390
  console.log(`[apm] \u5DF2\u5220\u9664\u8FDC\u7A0B\u5206\u652F origin/${branch}`);
1363
1391
  }
@@ -2280,6 +2308,254 @@ function validateAgentWsMessage(value, kind) {
2280
2308
 
2281
2309
  // src/commands/connect/deploy-run.ts
2282
2310
  import { spawn } from "node:child_process";
2311
+
2312
+ // src/commands/deploy/internal/deploy-artifact-minio.ts
2313
+ import { readFile as readFile2 } from "node:fs/promises";
2314
+
2315
+ // src/commands/deploy/internal/minio.ts
2316
+ import { statSync as statSync5 } from "node:fs";
2317
+ import { readdir, readFile } from "node:fs/promises";
2318
+ import path from "node:path";
2319
+ import * as Minio from "minio";
2320
+ var DEFAULT_MAX_FILE_SIZE_MB = 50;
2321
+ async function isDirectoryPath(dir) {
2322
+ try {
2323
+ const st = statSync5(dir);
2324
+ return st.isDirectory();
2325
+ } catch {
2326
+ return false;
2327
+ }
2328
+ }
2329
+ function sanitizeRelativePath(rel) {
2330
+ const norm = rel.replace(/\\/g, "/").replace(/^\/+/, "");
2331
+ const segments = norm.split("/").filter(Boolean);
2332
+ for (const s of segments) {
2333
+ if (s === "." || s === "..") {
2334
+ throw new Error(`\u975E\u6CD5\u76F8\u5BF9\u8DEF\u5F84\u7247\u6BB5\uFF1A${s}`);
2335
+ }
2336
+ }
2337
+ return segments.join("/");
2338
+ }
2339
+ async function collectFiles(root) {
2340
+ const out = [];
2341
+ async function walk(dir, prefix) {
2342
+ const entries = await readdir(dir, { withFileTypes: true });
2343
+ for (const e of entries) {
2344
+ const name = e.name;
2345
+ if (name === "." || name === "..") {
2346
+ continue;
2347
+ }
2348
+ const abs = path.join(dir, name);
2349
+ const rel = prefix ? `${prefix}/${name}` : name;
2350
+ if (e.isDirectory()) {
2351
+ await walk(abs, rel);
2352
+ } else if (e.isFile()) {
2353
+ const st = statSync5(abs);
2354
+ out.push({
2355
+ absPath: abs,
2356
+ relativePath: rel.replace(/\\/g, "/"),
2357
+ size: st.size
2358
+ });
2359
+ }
2360
+ }
2361
+ }
2362
+ await walk(root, "");
2363
+ out.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
2364
+ return out;
2365
+ }
2366
+ async function readArtifactFile(absPath) {
2367
+ return readFile(absPath);
2368
+ }
2369
+ function toMB(bytes) {
2370
+ return Math.round(bytes / (1024 * 1024) * 1e3) / 1e3;
2371
+ }
2372
+ var MIME = {
2373
+ ".html": "text/html; charset=utf-8",
2374
+ ".css": "text/css; charset=utf-8",
2375
+ ".js": "application/javascript; charset=utf-8",
2376
+ ".json": "application/json; charset=utf-8",
2377
+ ".svg": "image/svg+xml",
2378
+ ".png": "image/png",
2379
+ ".jpg": "image/jpeg",
2380
+ ".jpeg": "image/jpeg",
2381
+ ".gif": "image/gif",
2382
+ ".webp": "image/webp",
2383
+ ".woff": "font/woff",
2384
+ ".woff2": "font/woff2",
2385
+ ".ttf": "font/ttf",
2386
+ ".ico": "image/x-icon",
2387
+ ".txt": "text/plain; charset=utf-8",
2388
+ ".map": "application/json"
2389
+ };
2390
+ function detectMimeType(filePath) {
2391
+ const ext = path.extname(filePath).toLowerCase();
2392
+ return MIME[ext] ?? "";
2393
+ }
2394
+ var MinioClient = class {
2395
+ inner;
2396
+ constructor(opts) {
2397
+ const endPoint = opts.endPoint.replace(/^https?:\/\//i, "").split("/")[0] ?? opts.endPoint;
2398
+ this.inner = new Minio.Client({
2399
+ endPoint,
2400
+ port: opts.port,
2401
+ useSSL: opts.useSSL,
2402
+ accessKey: opts.accessKey,
2403
+ secretKey: opts.secretKey
2404
+ });
2405
+ }
2406
+ async ensureBucket(bucket) {
2407
+ const exists = await this.inner.bucketExists(bucket);
2408
+ if (!exists) {
2409
+ await this.inner.makeBucket(bucket);
2410
+ }
2411
+ }
2412
+ async deleteObjectsByPrefix(bucket, prefix) {
2413
+ const objectsStream = this.inner.listObjectsV2(bucket, prefix, true);
2414
+ const keys = [];
2415
+ await new Promise((resolve5, reject) => {
2416
+ objectsStream.on("data", (obj) => {
2417
+ if (obj.name) {
2418
+ keys.push(obj.name);
2419
+ }
2420
+ });
2421
+ objectsStream.on("error", reject);
2422
+ objectsStream.on("end", resolve5);
2423
+ });
2424
+ const chunkSize = 500;
2425
+ for (let i = 0; i < keys.length; i += chunkSize) {
2426
+ const chunk = keys.slice(i, i + chunkSize);
2427
+ await this.inner.removeObjects(
2428
+ bucket,
2429
+ chunk.map((name) => name)
2430
+ );
2431
+ }
2432
+ }
2433
+ async putObject(bucket, objectKey, body, meta) {
2434
+ await this.inner.putObject(bucket, objectKey, body, body.length, meta);
2435
+ }
2436
+ /** 匿名可读当前桶全部对象(便于静态站点直链) */
2437
+ async setBucketPublicRead(bucket) {
2438
+ const policy = {
2439
+ Version: "2012-10-17",
2440
+ Statement: [
2441
+ {
2442
+ Effect: "Allow",
2443
+ Principal: { AWS: ["*"] },
2444
+ Action: ["s3:GetObject"],
2445
+ Resource: [`arn:aws:s3:::${bucket}/*`]
2446
+ }
2447
+ ]
2448
+ };
2449
+ await this.inner.setBucketPolicy(bucket, JSON.stringify(policy));
2450
+ }
2451
+ };
2452
+
2453
+ // src/commands/deploy/internal/deploy-artifact-minio.ts
2454
+ var APM_DEPLOYMENT_RUN_ID_ENV = "APM_DEPLOYMENT_RUN_ID";
2455
+ function formatDeployArtifactTimestamp(date = /* @__PURE__ */ new Date()) {
2456
+ const pad = (n) => String(n).padStart(2, "0");
2457
+ return `${date.getFullYear()}${pad(date.getMonth() + 1)}${pad(date.getDate())}${pad(date.getHours())}${pad(date.getMinutes())}${pad(date.getSeconds())}`;
2458
+ }
2459
+ function sanitizeDeployProjectName(name) {
2460
+ const trimmed = name.trim().replace(/\\/g, "/");
2461
+ const base = trimmed.split("/").filter(Boolean).pop() ?? trimmed;
2462
+ try {
2463
+ return sanitizeRelativePath(base.replace(/[/\\:*?"<>|]/g, "_"));
2464
+ } catch {
2465
+ return "project";
2466
+ }
2467
+ }
2468
+ function buildDeployArtifactFileName(kind, projectName, timestamp = formatDeployArtifactTimestamp()) {
2469
+ const safeName = sanitizeDeployProjectName(projectName);
2470
+ const suffix = kind === "frontend" ? "dist.zip" : "jar.zip";
2471
+ return `${timestamp}-${safeName}.${suffix}`;
2472
+ }
2473
+ function buildDeployArtifactObjectKey(projectName, fileName) {
2474
+ const safeProject = sanitizeDeployProjectName(projectName);
2475
+ return `deploy/${safeProject}/${fileName}`;
2476
+ }
2477
+ function resolveDeploymentRunIdFromEnv() {
2478
+ const raw = process.env[APM_DEPLOYMENT_RUN_ID_ENV]?.trim();
2479
+ return raw || null;
2480
+ }
2481
+ async function fetchDeployArtifactStorage(api) {
2482
+ return api.cli.getDeployArtifactStorage(void 0);
2483
+ }
2484
+ async function uploadDeployArtifactZip(options) {
2485
+ const deploymentRunId = options.deploymentRunId ?? resolveDeploymentRunIdFromEnv();
2486
+ if (!deploymentRunId) {
2487
+ return null;
2488
+ }
2489
+ const cfg = await tryReadApmConfig();
2490
+ if (!cfg || !resolveApiKey(cfg)) {
2491
+ console.warn("[apm] \u672A\u767B\u5F55\uFF0C\u8DF3\u8FC7 MinIO \u90E8\u7F72\u4EA7\u7269\u5F52\u6863");
2492
+ return null;
2493
+ }
2494
+ const api = options.api ?? createApmApiClient(cfg);
2495
+ let storage;
2496
+ try {
2497
+ storage = await fetchDeployArtifactStorage(api);
2498
+ } catch (error) {
2499
+ const detail = error instanceof Error ? error.message : String(error);
2500
+ console.warn(`[apm] \u83B7\u53D6 MinIO \u914D\u7F6E\u5931\u8D25\uFF0C\u8DF3\u8FC7\u90E8\u7F72\u4EA7\u7269\u5F52\u6863: ${detail}`);
2501
+ return null;
2502
+ }
2503
+ const fileName = buildDeployArtifactFileName(
2504
+ options.kind,
2505
+ options.projectName,
2506
+ options.timestamp
2507
+ );
2508
+ const objectKey = buildDeployArtifactObjectKey(options.projectName, fileName);
2509
+ let body;
2510
+ try {
2511
+ body = await readFile2(options.zipPath);
2512
+ } catch (error) {
2513
+ const detail = error instanceof Error ? error.message : String(error);
2514
+ console.warn(`[apm] \u8BFB\u53D6\u90E8\u7F72\u4EA7\u7269 zip \u5931\u8D25\uFF0C\u8DF3\u8FC7 MinIO \u5F52\u6863: ${detail}`);
2515
+ return null;
2516
+ }
2517
+ try {
2518
+ const client = new MinioClient({
2519
+ endPoint: storage.endpoint,
2520
+ port: storage.port,
2521
+ useSSL: storage.useSsl,
2522
+ accessKey: storage.accessKey,
2523
+ secretKey: storage.secretKey
2524
+ });
2525
+ await client.ensureBucket(storage.bucket);
2526
+ await client.putObject(storage.bucket, objectKey, body, {
2527
+ "Content-Type": "application/zip"
2528
+ });
2529
+ console.error(
2530
+ `[apm] \u5DF2\u5F52\u6863\u90E8\u7F72\u4EA7\u7269: ${storage.bucket}/${objectKey} (${(body.length / 1024 / 1024).toFixed(2)} MB)`
2531
+ );
2532
+ } catch (error) {
2533
+ const detail = error instanceof Error ? error.message : String(error);
2534
+ console.warn(`[apm] MinIO \u90E8\u7F72\u4EA7\u7269\u5F52\u6863\u5931\u8D25: ${detail}`);
2535
+ return null;
2536
+ }
2537
+ try {
2538
+ await api.cli.attachTaskDeploymentArtifact({
2539
+ id: deploymentRunId,
2540
+ artifactObjectKey: objectKey,
2541
+ artifactFileName: fileName
2542
+ });
2543
+ console.error(
2544
+ `[apm] \u5DF2\u7ED1\u5B9A\u90E8\u7F72\u4EA7\u7269\u5230\u8BB0\u5F55 id=${deploymentRunId} file=${fileName}`
2545
+ );
2546
+ } catch (error) {
2547
+ const detail = error instanceof Error ? error.message : String(error);
2548
+ console.warn(`[apm] \u7ED1\u5B9A\u90E8\u7F72\u4EA7\u7269\u5230\u8BB0\u5F55\u5931\u8D25: ${detail}`);
2549
+ return null;
2550
+ }
2551
+ return {
2552
+ objectKey,
2553
+ fileName,
2554
+ bucket: storage.bucket
2555
+ };
2556
+ }
2557
+
2558
+ // src/commands/connect/deploy-run.ts
2283
2559
  var DEPLOY_LOG_SYNC_INTERVAL_MS = 3e4;
2284
2560
  function resolveDeployCommand(environment) {
2285
2561
  return `apm deploy ${environment}`;
@@ -2385,6 +2661,8 @@ async function handleInboundDeploy(cfg, msg, signal) {
2385
2661
  console.log(`[apm] deploy command: ${command}`);
2386
2662
  const logSyncer = createDeployLogSyncer(api, deploymentRunId);
2387
2663
  let latestLog = "";
2664
+ const previousDeploymentRunId = process.env[APM_DEPLOYMENT_RUN_ID_ENV];
2665
+ process.env[APM_DEPLOYMENT_RUN_ID_ENV] = deploymentRunId;
2388
2666
  try {
2389
2667
  const { log: log2 } = await runShellCommand(command, workdir, signal, (log3) => {
2390
2668
  latestLog = log3;
@@ -2412,6 +2690,11 @@ async function handleInboundDeploy(cfg, msg, signal) {
2412
2690
  });
2413
2691
  console.error(`[apm] deploy failed id=${deploymentRunId}: ${detail}`);
2414
2692
  } finally {
2693
+ if (previousDeploymentRunId === void 0) {
2694
+ delete process.env[APM_DEPLOYMENT_RUN_ID_ENV];
2695
+ } else {
2696
+ process.env[APM_DEPLOYMENT_RUN_ID_ENV] = previousDeploymentRunId;
2697
+ }
2415
2698
  logSyncer.dispose();
2416
2699
  }
2417
2700
  }
@@ -3939,6 +4222,99 @@ async function runConnect(options) {
3939
4222
  });
3940
4223
  }
3941
4224
 
4225
+ // src/commands/create-pr-errors.ts
4226
+ import { ApiError as ApiError2 } from "listpage-http";
4227
+ function extractMessage(error) {
4228
+ if (error instanceof ApiError2) {
4229
+ return String(error.message ?? "").trim();
4230
+ }
4231
+ if (error instanceof Error) {
4232
+ return error.message.trim();
4233
+ }
4234
+ return String(error).trim();
4235
+ }
4236
+ function extractHttpStatus(error) {
4237
+ if (error instanceof ApiError2) {
4238
+ return error.httpStatus;
4239
+ }
4240
+ return void 0;
4241
+ }
4242
+ function buildHints(message, httpStatus) {
4243
+ const hints = [];
4244
+ if (/未配置.*Gitee.*API Key/i.test(message)) {
4245
+ hints.push(
4246
+ "\u8BF7\u5728 APM Web \u2192 \u5BA2\u6237\u673A\u7BA1\u7406 \u2192 \u7F16\u8F91\u5F53\u524D\u5BA2\u6237\u673A \u2192 \u586B\u5199\u300CGitee API Key\u300D\uFF08Gitee \u79C1\u4EBA\u4EE4\u724C\uFF0C\u9700\u6709\u4ED3\u5E93 PR \u6743\u9650\uFF09"
4247
+ );
4248
+ } else if (/未配置.*GitHub.*API Key/i.test(message)) {
4249
+ hints.push(
4250
+ "\u8BF7\u5728 APM Web \u2192 \u5BA2\u6237\u673A\u7BA1\u7406 \u2192 \u7F16\u8F91\u5F53\u524D\u5BA2\u6237\u673A \u2192 \u586B\u5199\u300CGithub API Key\u300D\uFF08GitHub Personal Access Token\uFF0C\u9700\u6709 repo \u6743\u9650\uFF09"
4251
+ );
4252
+ } else if (httpStatus === 401 || /access token does not exist/i.test(message) || /bad credentials/i.test(message) || /Git 平台返回 401|认证失败 \(401\)/i.test(message)) {
4253
+ hints.push(
4254
+ "Git \u5E73\u53F0 Token \u65E0\u6548\u6216\u5DF2\u8FC7\u671F\uFF1A\u8BF7\u5728 Gitee/GitHub \u91CD\u65B0\u751F\u6210\u79C1\u4EBA\u4EE4\u724C\uFF0C\u5E76\u66F4\u65B0\u5230 APM\u300C\u5BA2\u6237\u673A\u7BA1\u7406\u300D"
4255
+ );
4256
+ }
4257
+ if (/未绑定仓库|未关联仓库|选择关联仓库/i.test(message)) {
4258
+ hints.push("\u5728 APM Web \u2192 \u5DE5\u4F5C\u76EE\u5F55 \u2192 \u4E3A\u5F53\u524D\u8DEF\u5F84\u5173\u8054\u5BF9\u5E94 Git \u4ED3\u5E93");
4259
+ }
4260
+ if (/路径.*不匹配|尚未登记任何工作目录/i.test(message)) {
4261
+ hints.push(
4262
+ "\u5728 APM Web \u2192 \u5DE5\u4F5C\u76EE\u5F55 \u2192 \u767B\u8BB0\u5F53\u524D\u9879\u76EE\u8DEF\u5F84\uFF08\u9700\u4E0E apm create-pr \u6267\u884C\u65F6\u7684 cwd \u4E00\u81F4\uFF09"
4263
+ );
4264
+ }
4265
+ if (/no commits between|分支不存在|branch.*not found|找不到.*分支/i.test(
4266
+ message
4267
+ )) {
4268
+ hints.push(
4269
+ "\u7279\u6027\u5206\u652F\u53EF\u80FD\u672A\u63A8\u9001\uFF1A\u5148\u6267\u884C apm branch <sessionId> \u521B\u5EFA\u5206\u652F\u5E76 push\uFF0C\u518D\u91CD\u8BD5 create-pr"
4270
+ );
4271
+ }
4272
+ if (/pull request already exists|已存在.*pull/i.test(message)) {
4273
+ hints.push("\u8BE5\u5206\u652F\u53EF\u80FD\u5DF2\u6709\u5F00\u542F\u7684 PR\uFF0C\u8BF7\u5230 Git \u5E73\u53F0\u6216 APM \u4F1A\u8BDD\u8BE6\u60C5\u67E5\u770B");
4274
+ }
4275
+ if (/403|无权限|forbidden/i.test(message)) {
4276
+ hints.push("\u786E\u8BA4 Token \u5BF9\u76EE\u6807\u4ED3\u5E93\u6709\u521B\u5EFA Pull Request \u7684\u6743\u9650");
4277
+ }
4278
+ if (hints.length === 0) {
4279
+ hints.push(
4280
+ "\u82E5\u65E0\u6CD5\u81EA\u884C\u6392\u67E5\uFF0C\u8BF7\u5728\u4F1A\u8BDD\u4E2D @\u9879\u76EE\u7ECF\u7406\uFF0C\u5E76\u63D0\u4F9B\u4E0A\u8FF0\u4F1A\u8BDD ID \u4E0E\u5DE5\u4F5C\u76EE\u5F55"
4281
+ );
4282
+ }
4283
+ return hints;
4284
+ }
4285
+ function formatCreatePrErrorLines(error, context) {
4286
+ const message = extractMessage(error);
4287
+ const httpStatus = extractHttpStatus(error);
4288
+ const headBranch = `feat/session-${context.sessionId}`;
4289
+ const lines = [
4290
+ "[apm] \u521B\u5EFA PR \u5931\u8D25",
4291
+ "",
4292
+ `\u4F1A\u8BDD ID: ${context.sessionId}`,
4293
+ `\u5DE5\u4F5C\u76EE\u5F55: ${context.workdir}`,
4294
+ `\u7279\u6027\u5206\u652F: ${headBranch}`
4295
+ ];
4296
+ if (context.clientMachineId) {
4297
+ lines.push(`\u5BA2\u6237\u673A ID: ${context.clientMachineId}`);
4298
+ }
4299
+ lines.push("", "\u539F\u56E0:", message || "(\u672A\u77E5\u9519\u8BEF)");
4300
+ const hints = buildHints(message, httpStatus);
4301
+ if (hints.length > 0) {
4302
+ lines.push("", "\u5EFA\u8BAE:");
4303
+ hints.forEach((hint, index) => {
4304
+ lines.push(`${index + 1}. ${hint}`);
4305
+ });
4306
+ }
4307
+ if (httpStatus !== void 0) {
4308
+ lines.push("", `HTTP \u72B6\u6001: ${httpStatus}`);
4309
+ }
4310
+ return lines;
4311
+ }
4312
+ function printCreatePrError(error, context) {
4313
+ for (const line of formatCreatePrErrorLines(error, context)) {
4314
+ console.error(line);
4315
+ }
4316
+ }
4317
+
3942
4318
  // src/commands/create-pr.ts
3943
4319
  async function runCreatePr(options) {
3944
4320
  const sessionId = options.sessionId.trim();
@@ -3954,18 +4330,27 @@ async function runCreatePr(options) {
3954
4330
  const cfg = await ensureLoggedConfig();
3955
4331
  const api = createApmApiClient(cfg);
3956
4332
  const workdir = resolveWorkdirPath(options.cwd ?? process.cwd());
3957
- const pr = await api.cli.createPullRequest({
3958
- sessionId,
3959
- workdir,
3960
- title,
3961
- content: options.content ?? ""
3962
- });
3963
- console.log(`[apm] PR \u5DF2\u5C31\u7EEA #${pr.number} (${pr.state}): ${pr.url}`);
4333
+ try {
4334
+ const pr = await api.cli.createPullRequest({
4335
+ sessionId,
4336
+ workdir,
4337
+ title,
4338
+ content: options.content ?? ""
4339
+ });
4340
+ console.log(`[apm] PR \u5DF2\u5C31\u7EEA #${pr.number} (${pr.state}): ${pr.url}`);
4341
+ } catch (error) {
4342
+ printCreatePrError(error, {
4343
+ sessionId,
4344
+ workdir,
4345
+ clientMachineId: resolveClientMachineId(cfg)
4346
+ });
4347
+ process.exit(1);
4348
+ }
3964
4349
  }
3965
4350
 
3966
4351
  // src/commands/deploy/deploy-execute.ts
3967
4352
  import { spawnSync as spawnSync5 } from "node:child_process";
3968
- import path4 from "node:path";
4353
+ import path5 from "node:path";
3969
4354
 
3970
4355
  // src/commands/deploy/internal/apm-config.ts
3971
4356
  import { existsSync as existsSync15, readFileSync as readFileSync12 } from "node:fs";
@@ -4307,7 +4692,7 @@ var DeployExecutionError = class extends Error {
4307
4692
 
4308
4693
  // src/commands/deploy/internal/wisdom-auto-deploy.ts
4309
4694
  import { existsSync as existsSync17, readFileSync as readFileSync14 } from "node:fs";
4310
- import path3 from "node:path";
4695
+ import path4 from "node:path";
4311
4696
  import { spawnSync as spawnSync4 } from "node:child_process";
4312
4697
 
4313
4698
  // src/commands/deploy/internal/wisdom-backend-deploy.ts
@@ -4316,31 +4701,31 @@ import {
4316
4701
  mkdirSync as mkdirSync8,
4317
4702
  readdirSync as readdirSync5,
4318
4703
  readFileSync as readFileSync13,
4319
- statSync as statSync5,
4704
+ statSync as statSync6,
4320
4705
  writeFileSync as writeFileSync14
4321
4706
  } from "node:fs";
4322
4707
  import { spawnSync as spawnSync3 } from "node:child_process";
4323
- import path2 from "node:path";
4324
- import { Client } from "ssh2";
4708
+ import path3 from "node:path";
4709
+ import { Client as Client2 } from "ssh2";
4325
4710
  import JSZip2 from "jszip";
4326
4711
  import SftpClient2 from "ssh2-sftp-client";
4327
4712
 
4328
4713
  // src/commands/deploy/internal/wisdom-sftp.ts
4329
- import { readdir, readFile, unlink, writeFile } from "node:fs/promises";
4330
- import path from "node:path";
4714
+ import { readdir as readdir2, readFile as readFile3, unlink, writeFile } from "node:fs/promises";
4715
+ import path2 from "node:path";
4331
4716
  import JSZip from "jszip";
4332
4717
  import SftpClient from "ssh2-sftp-client";
4333
4718
  async function addDirToZip(dir, zipFolder) {
4334
- const entries = await readdir(dir, { withFileTypes: true });
4719
+ const entries = await readdir2(dir, { withFileTypes: true });
4335
4720
  for (const entry of entries) {
4336
- const fullPath = path.join(dir, entry.name);
4721
+ const fullPath = path2.join(dir, entry.name);
4337
4722
  if (entry.isDirectory()) {
4338
4723
  const folder = zipFolder.folder(entry.name);
4339
4724
  if (folder) {
4340
4725
  await addDirToZip(fullPath, folder);
4341
4726
  }
4342
4727
  } else {
4343
- const content = await readFile(fullPath);
4728
+ const content = await readFile3(fullPath);
4344
4729
  zipFolder.file(entry.name, content);
4345
4730
  }
4346
4731
  }
@@ -4448,8 +4833,8 @@ async function uploadAndMaybeExtract(settings, localZip, extract) {
4448
4833
  }
4449
4834
  }
4450
4835
  async function runWisdomSftpDeploy(params) {
4451
- const zipPath = path.join(params.localDir, "..", ".deploy-sftp-dist.zip");
4452
- const resolvedZipPath = path.resolve(zipPath);
4836
+ const zipPath = path2.join(params.localDir, "..", ".deploy-sftp-dist.zip");
4837
+ const resolvedZipPath = path2.resolve(zipPath);
4453
4838
  let zipSizeBytes = 0;
4454
4839
  try {
4455
4840
  zipSizeBytes = await zipDirectory(params.localDir, resolvedZipPath);
@@ -4458,6 +4843,11 @@ async function runWisdomSftpDeploy(params) {
4458
4843
  resolvedZipPath,
4459
4844
  params.extract
4460
4845
  );
4846
+ await uploadDeployArtifactZip({
4847
+ zipPath: resolvedZipPath,
4848
+ projectName: params.projectName,
4849
+ kind: "frontend"
4850
+ });
4461
4851
  } finally {
4462
4852
  try {
4463
4853
  await unlink(resolvedZipPath);
@@ -4492,7 +4882,7 @@ function fail(message) {
4492
4882
  }
4493
4883
  function expandPath(pathStr) {
4494
4884
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
4495
- return path2.resolve(pathStr.replace(/^~(?=\/|\\|$)/, home));
4885
+ return path3.resolve(pathStr.replace(/^~(?=\/|\\|$)/, home));
4496
4886
  }
4497
4887
  function quoteForShell(value) {
4498
4888
  if (process.platform === "win32") {
@@ -4507,19 +4897,19 @@ function formatMavenLocalRepoArg(repoPath) {
4507
4897
  return `-Dmaven.repo.local=${repoPath}`;
4508
4898
  }
4509
4899
  function deployCacheDir() {
4510
- return path2.join(workspaceApmDir(), "deploy", ".deploy_cache");
4900
+ return path3.join(workspaceApmDir(), "deploy", ".deploy_cache");
4511
4901
  }
4512
4902
  function manifestFilePath() {
4513
- return path2.join(deployCacheDir(), "manifest.json");
4903
+ return path3.join(deployCacheDir(), "manifest.json");
4514
4904
  }
4515
4905
  function getTargetDir(projectRoot) {
4516
- return path2.join(projectRoot, MAVEN_MODULE, "target");
4906
+ return path3.join(projectRoot, MAVEN_MODULE, "target");
4517
4907
  }
4518
4908
  function relativeKey(projectRoot, filePath) {
4519
- return path2.relative(projectRoot, filePath).split(path2.sep).join("/");
4909
+ return path3.relative(projectRoot, filePath).split(path3.sep).join("/");
4520
4910
  }
4521
4911
  function fileSignature(filePath) {
4522
- const stat2 = statSync5(filePath);
4912
+ const stat2 = statSync6(filePath);
4523
4913
  return { size: stat2.size, mtime: stat2.mtimeMs / 1e3 };
4524
4914
  }
4525
4915
  function loadManifest4() {
@@ -4541,12 +4931,12 @@ function shouldUploadLibFile(localPath, remoteAttr, manifest, projectRoot) {
4541
4931
  if (!remoteAttr) {
4542
4932
  return [false, "\u8FDC\u7A0B\u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7"];
4543
4933
  }
4544
- const localSize = statSync5(localPath).size;
4934
+ const localSize = statSync6(localPath).size;
4545
4935
  const remoteSize = remoteAttr.size;
4546
4936
  if (localSize !== remoteSize) {
4547
4937
  return [true, `\u5927\u5C0F\u53D8\u5316 ${remoteSize} -> ${localSize}`];
4548
4938
  }
4549
- if (isProjectLibJar(path2.basename(localPath)) && manifest) {
4939
+ if (isProjectLibJar(path3.basename(localPath)) && manifest) {
4550
4940
  const key = relativeKey(projectRoot, localPath);
4551
4941
  const current = fileSignature(localPath);
4552
4942
  const previous = manifest[key];
@@ -4566,7 +4956,7 @@ function listLibFilesToUpload(localLibDir, remoteStats, projectRoot, manifest =
4566
4956
  const entries = [];
4567
4957
  const jarFiles = readdirSync5(localLibDir).filter((name) => name.endsWith(".jar")).sort();
4568
4958
  for (const jarName of jarFiles) {
4569
- const jarPath = path2.join(localLibDir, jarName);
4959
+ const jarPath = path3.join(localLibDir, jarName);
4570
4960
  const remoteAttr = remoteStats.get(jarName);
4571
4961
  const [shouldUpload, reason] = shouldUploadLibFile(
4572
4962
  jarPath,
@@ -4589,8 +4979,8 @@ function updateManifestEntries(manifest, entries, projectRoot) {
4589
4979
  async function createUpdatePackage(entries, packageName) {
4590
4980
  const dir = deployCacheDir();
4591
4981
  mkdirSync8(dir, { recursive: true });
4592
- const zipPath = path2.join(dir, packageName);
4593
- log(`\u521B\u5EFA\u66F4\u65B0\u5305: ${path2.basename(zipPath)}\uFF08${entries.length} \u4E2A\u6587\u4EF6\uFF09`);
4982
+ const zipPath = path3.join(dir, packageName);
4983
+ log(`\u521B\u5EFA\u66F4\u65B0\u5305: ${path3.basename(zipPath)}\uFF08${entries.length} \u4E2A\u6587\u4EF6\uFF09`);
4594
4984
  const zip = new JSZip2();
4595
4985
  for (const entry of entries) {
4596
4986
  const content = readFileSync13(entry.path);
@@ -4654,8 +5044,8 @@ function locateLibDir(projectRoot) {
4654
5044
  if (!existsSync16(targetDir)) {
4655
5045
  fail(`\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728: ${targetDir}`);
4656
5046
  }
4657
- const libDir = path2.join(targetDir, "lib");
4658
- if (!existsSync16(libDir) || !statSync5(libDir).isDirectory()) {
5047
+ const libDir = path3.join(targetDir, "lib");
5048
+ if (!existsSync16(libDir) || !statSync6(libDir).isDirectory()) {
4659
5049
  fail(`lib \u76EE\u5F55\u4E0D\u5B58\u5728: ${libDir}`);
4660
5050
  }
4661
5051
  const libJars = readdirSync5(libDir).filter((name) => name.endsWith(".jar"));
@@ -4670,16 +5060,16 @@ function locateMainJar(projectRoot) {
4670
5060
  if (!existsSync16(targetDir)) {
4671
5061
  fail(`\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728: ${targetDir}`);
4672
5062
  }
4673
- const jarFiles = readdirSync5(targetDir).filter((name) => name.endsWith(".jar") && !name.endsWith(".jar.original")).map((name) => path2.join(targetDir, name)).sort((a, b) => statSync5(b).mtimeMs - statSync5(a).mtimeMs);
5063
+ const jarFiles = readdirSync5(targetDir).filter((name) => name.endsWith(".jar") && !name.endsWith(".jar.original")).map((name) => path3.join(targetDir, name)).sort((a, b) => statSync6(b).mtimeMs - statSync6(a).mtimeMs);
4674
5064
  if (jarFiles.length === 0) {
4675
5065
  fail(`target \u76EE\u5F55\u4E0B\u6CA1\u6709\u4E3B JAR: ${targetDir}`);
4676
5066
  }
4677
5067
  const mainJar = jarFiles[0];
4678
- log(`\u5B9A\u4F4D\u4E3B JAR \u4EA7\u7269: ${path2.basename(mainJar)}`);
5068
+ log(`\u5B9A\u4F4D\u4E3B JAR \u4EA7\u7269: ${path3.basename(mainJar)}`);
4679
5069
  return mainJar;
4680
5070
  }
4681
5071
  async function connectSsh(config) {
4682
- const client = new Client();
5072
+ const client = new Client2();
4683
5073
  log(`\u8FDE\u63A5\u670D\u52A1\u5668 ${config.username}@${config.host}:${config.port}`);
4684
5074
  await new Promise((resolve5, reject) => {
4685
5075
  client.on("ready", () => resolve5()).on("error", (err) => reject(err)).connect({
@@ -4724,7 +5114,7 @@ async function getRemoteFileStats(sftp, remoteDir) {
4724
5114
  }
4725
5115
  async function uploadUpdatePackage(sftp, zipPath, config) {
4726
5116
  const remoteDir = config.remoteVueDistDir.replace(/\/$/, "");
4727
- const remotePath = `${remoteDir}/${path2.basename(zipPath)}`;
5117
+ const remotePath = `${remoteDir}/${path3.basename(zipPath)}`;
4728
5118
  log(`\u4E0A\u4F20\u66F4\u65B0\u5305 -> ${remotePath}`);
4729
5119
  try {
4730
5120
  await sftp.fastPut(zipPath, remotePath);
@@ -4737,7 +5127,7 @@ async function uploadUpdatePackage(sftp, zipPath, config) {
4737
5127
  async function uploadFullJar(sftp, localJarPath, config) {
4738
5128
  const remoteDir = config.remoteAppDir.replace(/\/$/, "");
4739
5129
  const remotePath = `${remoteDir}/${config.startupJar}`;
4740
- log(`\u4E0A\u4F20\u5168\u91CF JAR (${path2.basename(localJarPath)}) -> ${remotePath}`);
5130
+ log(`\u4E0A\u4F20\u5168\u91CF JAR (${path3.basename(localJarPath)}) -> ${remotePath}`);
4741
5131
  try {
4742
5132
  await sftp.mkdir(remoteDir, true);
4743
5133
  await sftp.fastPut(localJarPath, remotePath);
@@ -4996,10 +5386,10 @@ async function restartRemoteService(client, config) {
4996
5386
  await runRemoteServiceScript(client, script, "restart");
4997
5387
  }
4998
5388
  async function runWisdomBackendDeploy(options) {
4999
- const projectRoot = path2.resolve(options.projectRoot ?? process.cwd());
5389
+ const projectRoot = path3.resolve(options.projectRoot ?? process.cwd());
5000
5390
  const config = options.config;
5001
5391
  log(`=== \u81EA\u52A8\u90E8\u7F72: ${config.projectName} ===`);
5002
- log(`\u914D\u7F6E\u6587\u4EF6: ${path2.join(workspaceApmDir(), "apm.config.json")}`);
5392
+ log(`\u914D\u7F6E\u6587\u4EF6: ${path3.join(workspaceApmDir(), "apm.config.json")}`);
5003
5393
  log(`\u9879\u76EE\u6839\u76EE\u5F55: ${projectRoot}`);
5004
5394
  log(
5005
5395
  `\u90E8\u7F72\u6A21\u5F0F: ${config.mode}${config.mode === "full" ? "\uFF08\u5168\u91CF JAR\uFF0C\u8DF3\u8FC7 lib \u68C0\u67E5\uFF09" : "\uFF08\u589E\u91CF lib \u66F4\u65B0\uFF09"}`
@@ -5013,6 +5403,21 @@ async function runWisdomBackendDeploy(options) {
5013
5403
  if (config.mode === "full") {
5014
5404
  const mainJar = locateMainJar(projectRoot);
5015
5405
  await uploadFullJar(conn.sftp, mainJar, config);
5406
+ const archiveZipPath = await createUpdatePackage(
5407
+ [
5408
+ {
5409
+ path: mainJar,
5410
+ arcname: path3.basename(mainJar),
5411
+ reason: "\u5168\u91CF JAR \u5F52\u6863"
5412
+ }
5413
+ ],
5414
+ `.deploy-archive-${config.projectName}.jar.zip`
5415
+ );
5416
+ await uploadDeployArtifactZip({
5417
+ zipPath: archiveZipPath,
5418
+ projectName: config.projectName,
5419
+ kind: "backend"
5420
+ });
5016
5421
  log("\u91CD\u542F\u670D\u52A1...");
5017
5422
  await restartRemoteService(conn.client, config);
5018
5423
  await healthCheckService(conn.client, config);
@@ -5043,6 +5448,11 @@ async function runWisdomBackendDeploy(options) {
5043
5448
  zipPath,
5044
5449
  config
5045
5450
  );
5451
+ await uploadDeployArtifactZip({
5452
+ zipPath,
5453
+ projectName: config.projectName,
5454
+ kind: "backend"
5455
+ });
5046
5456
  log("\u8FDC\u7A0B\u89E3\u538B lib \u76EE\u5F55\uFF08\u4EC5\u8986\u76D6\u5DF2\u6709 JAR\uFF09...");
5047
5457
  updated = await extractUpdatePackageOnRemote(
5048
5458
  conn.client,
@@ -5086,10 +5496,10 @@ function isWisdomLegacyDeploy(cfg) {
5086
5496
  return !hasNewFrontend && !hasNewBackend;
5087
5497
  }
5088
5498
  function detectWisdomProjectType(cwd) {
5089
- return existsSync17(path3.join(cwd, "package.json")) ? "frontend" : "backend";
5499
+ return existsSync17(path4.join(cwd, "package.json")) ? "frontend" : "backend";
5090
5500
  }
5091
5501
  function readPackageScripts(cwd) {
5092
- const pkgPath = path3.join(cwd, "package.json");
5502
+ const pkgPath = path4.join(cwd, "package.json");
5093
5503
  if (!existsSync17(pkgPath)) {
5094
5504
  return {};
5095
5505
  }
@@ -5134,7 +5544,7 @@ function runShellCommand2(command, cwd, captureOutput) {
5134
5544
  return output;
5135
5545
  }
5136
5546
  async function runWisdomAutoDeploy(options) {
5137
- const cwd = path3.resolve(options.cwd ?? process.cwd());
5547
+ const cwd = path4.resolve(options.cwd ?? process.cwd());
5138
5548
  const captureOutput = options.captureOutput ?? false;
5139
5549
  const projectType = detectWisdomProjectType(cwd);
5140
5550
  console.error(
@@ -5145,14 +5555,14 @@ async function runWisdomAutoDeploy(options) {
5145
5555
  if (!deployCmd) {
5146
5556
  const lines = formatWisdomFrontendDeployNotConfiguredLines(
5147
5557
  options.env,
5148
- path3.join(cwd, "package.json")
5558
+ path4.join(cwd, "package.json")
5149
5559
  );
5150
5560
  throw new DeployExecutionError(lines.join("\n"), 1, lines.join("\n"));
5151
5561
  }
5152
5562
  runShellCommand2(deployCmd, cwd, captureOutput);
5153
5563
  return { projectType };
5154
5564
  }
5155
- const configPath = options.configPath ?? path3.join(workspaceApmDir(cwd), "apm.config.json");
5565
+ const configPath = options.configPath ?? path4.join(workspaceApmDir(cwd), "apm.config.json");
5156
5566
  const cfg = loadApmConfig({ configPath });
5157
5567
  const settings = resolveWisdomBackendDeployFromApmConfig(cfg);
5158
5568
  console.error(
@@ -5165,6 +5575,150 @@ async function runWisdomAutoDeploy(options) {
5165
5575
  return { projectType };
5166
5576
  }
5167
5577
 
5578
+ // src/commands/deploy/internal/deploy-baseline-sync.ts
5579
+ var LOG_PREFIX = "[apm] \u90E8\u7F72\u524D\u57FA\u7EBF\u540C\u6B65:";
5580
+ function formatGitError(error) {
5581
+ return error instanceof Error ? error.message : String(error);
5582
+ }
5583
+ async function resolveBaselineBranch2(cwd, api) {
5584
+ const workdirPath = resolveWorkdirPath(cwd);
5585
+ const baseline = await api.cli.workspaceBaseline({ workdirPath });
5586
+ if (!baseline.repositoryId) {
5587
+ const detail = baseline.diagnostic?.message ?? `\u672A\u5728\u5E73\u53F0\u627E\u5230\u4E0E\u5F53\u524D\u76EE\u5F55\u5339\u914D\u7684\u5DE5\u4F5C\u76EE\u5F55\u767B\u8BB0\uFF1A${workdirPath}\uFF08\u89C4\u8303\u5316\uFF1A${baseline.workdirPath}\uFF09
5588
+ \u8BF7\u5148\u5728\u5E73\u53F0\u767B\u8BB0\u8BE5\u8DEF\u5F84\u5BF9\u5E94\u7684\u5DE5\u4F5C\u76EE\u5F55\u4E0E\u4ED3\u5E93\u3002`;
5589
+ throw new Error(detail);
5590
+ }
5591
+ const baselineBranch = (baseline.defaultBranch ?? "").trim();
5592
+ if (!baselineBranch) {
5593
+ throw new Error("\u5E73\u53F0\u8FD4\u56DE\u7684\u57FA\u7EBF\u5206\u652F\u540D\u4E3A\u7A7A");
5594
+ }
5595
+ return baselineBranch;
5596
+ }
5597
+ async function isMergeInProgress(cwd) {
5598
+ try {
5599
+ await execGit(cwd, ["rev-parse", "-q", "--verify", "MERGE_HEAD"], true);
5600
+ return true;
5601
+ } catch {
5602
+ return false;
5603
+ }
5604
+ }
5605
+ async function abortMergeIfNeeded(cwd) {
5606
+ if (await isMergeInProgress(cwd)) {
5607
+ await execGit(cwd, ["merge", "--abort"], true);
5608
+ }
5609
+ }
5610
+ async function restoreWorkingTree(cwd) {
5611
+ await execGit(cwd, ["reset", "--hard", "HEAD"], true);
5612
+ }
5613
+ async function mergeBaselineBranch(cwd, baselineBranch, currentBranch, note) {
5614
+ const mergeTarget = `origin/${baselineBranch}`;
5615
+ note(`${LOG_PREFIX} \u5F00\u59CB\u5408\u5E76 ${mergeTarget} \u5230\u5F53\u524D\u5206\u652F ${currentBranch}`);
5616
+ try {
5617
+ if (currentBranch === baselineBranch) {
5618
+ await execGit(cwd, ["merge", "--ff-only", mergeTarget]);
5619
+ } else {
5620
+ await execGit(cwd, ["merge", mergeTarget, "--no-edit"]);
5621
+ }
5622
+ note(`${LOG_PREFIX} \u5DF2\u5408\u5E76 ${mergeTarget} \u6700\u65B0\u4EE3\u7801`);
5623
+ return { merged: true, conflictRollback: false };
5624
+ } catch (error) {
5625
+ await abortMergeIfNeeded(cwd);
5626
+ note(`${LOG_PREFIX} \u5408\u5E76 ${mergeTarget} \u5931\u8D25: ${formatGitError(error)}`);
5627
+ note(`${LOG_PREFIX} \u5DF2\u6267\u884C git merge --abort \u56DE\u9000\u5230\u5408\u5E76\u524D\u72B6\u6001`);
5628
+ note(`${LOG_PREFIX} \u5C06\u4F7F\u7528\u56DE\u9000\u540E\u7684\u4EE3\u7801\u7EE7\u7EED\u90E8\u7F72`);
5629
+ return { merged: false, conflictRollback: true };
5630
+ }
5631
+ }
5632
+ async function restoreStashIfNeeded(cwd, stashed, note) {
5633
+ if (!stashed) {
5634
+ return false;
5635
+ }
5636
+ note(`${LOG_PREFIX} \u6B63\u5728\u6062\u590D stash \u4E2D\u7684\u672A\u63D0\u4EA4\u6539\u52A8`);
5637
+ try {
5638
+ await execGit(cwd, ["stash", "pop"], true);
5639
+ note(`${LOG_PREFIX} \u5DF2\u6062\u590D stash \u4E2D\u7684\u672A\u63D0\u4EA4\u6539\u52A8`);
5640
+ return false;
5641
+ } catch (error) {
5642
+ await abortMergeIfNeeded(cwd);
5643
+ await restoreWorkingTree(cwd);
5644
+ note(`${LOG_PREFIX} \u6062\u590D stash \u5931\u8D25: ${formatGitError(error)}`);
5645
+ note(`${LOG_PREFIX} \u5DF2\u56DE\u9000\u5DE5\u4F5C\u533A\uFF08git reset --hard HEAD\uFF09\uFF0Cstash \u4ECD\u4FDD\u7559`);
5646
+ note(`${LOG_PREFIX} \u5C06\u4F7F\u7528\u56DE\u9000\u540E\u7684\u4EE3\u7801\u7EE7\u7EED\u90E8\u7F72`);
5647
+ return true;
5648
+ }
5649
+ }
5650
+ async function syncDeployBaselineBeforeDeploy(cwd) {
5651
+ const lines = [];
5652
+ const note = (message) => {
5653
+ lines.push(message);
5654
+ console.error(message);
5655
+ };
5656
+ const emptyResult = (partial = {}) => ({
5657
+ log: lines.join("\n"),
5658
+ skipped: true,
5659
+ merged: false,
5660
+ conflictRollback: false,
5661
+ stashConflict: false,
5662
+ ...partial
5663
+ });
5664
+ if (!await isGitRepo(cwd)) {
5665
+ note(`${LOG_PREFIX} \u5F53\u524D\u76EE\u5F55\u4E0D\u662F git \u4ED3\u5E93\uFF0C\u8DF3\u8FC7`);
5666
+ return emptyResult();
5667
+ }
5668
+ const cfg = await tryReadApmConfig();
5669
+ if (!cfg || !resolveApiKey(cfg)) {
5670
+ note(`${LOG_PREFIX} \u672A\u767B\u5F55\uFF0C\u8DF3\u8FC7`);
5671
+ return emptyResult();
5672
+ }
5673
+ await ensureGitRepo(cwd);
5674
+ const api = createApmApiClient(cfg);
5675
+ const baselineBranch = await resolveBaselineBranch2(cwd, api);
5676
+ note(`${LOG_PREFIX} \u57FA\u7EBF\u5206\u652F ${baselineBranch}`);
5677
+ note(`${LOG_PREFIX} \u6B63\u5728 fetch origin ${baselineBranch}`);
5678
+ await execGit(cwd, ["fetch", "origin", baselineBranch], true);
5679
+ if (!await remoteBranchExists(cwd, baselineBranch)) {
5680
+ throw new Error(
5681
+ `\u8FDC\u7A0B\u4E0D\u5B58\u5728\u57FA\u7EBF\u5206\u652F origin/${baselineBranch}\uFF0C\u8BF7\u786E\u8BA4\u4ED3\u5E93\u9ED8\u8BA4\u5206\u652F\u5DF2\u63A8\u9001\u5230 origin`
5682
+ );
5683
+ }
5684
+ note(`${LOG_PREFIX} fetch \u5B8C\u6210`);
5685
+ const currentBranch = await getCurrentBranch(cwd);
5686
+ let stashed = false;
5687
+ if (await isWorkingTreeDirty(cwd)) {
5688
+ note(`${LOG_PREFIX} \u68C0\u6D4B\u5230\u672A\u63D0\u4EA4\u6539\u52A8\uFF0C\u5148 stash`);
5689
+ await execGit(cwd, [
5690
+ "stash",
5691
+ "push",
5692
+ "-u",
5693
+ "-m",
5694
+ "apm: deploy baseline sync"
5695
+ ]);
5696
+ stashed = true;
5697
+ note(`${LOG_PREFIX} \u5DF2 stash \u672A\u63D0\u4EA4\u6539\u52A8`);
5698
+ }
5699
+ const { merged, conflictRollback } = await mergeBaselineBranch(
5700
+ cwd,
5701
+ baselineBranch,
5702
+ currentBranch,
5703
+ note
5704
+ );
5705
+ const stashConflict = await restoreStashIfNeeded(cwd, stashed, note);
5706
+ if (conflictRollback || stashConflict) {
5707
+ note(`${LOG_PREFIX} \u57FA\u7EBF\u540C\u6B65\u672A\u5B8C\u6210\uFF08\u5B58\u5728\u51B2\u7A81\u5E76\u5DF2\u56DE\u9000\uFF09\uFF0C\u7EE7\u7EED\u6267\u884C\u90E8\u7F72`);
5708
+ } else if (merged) {
5709
+ note(
5710
+ `${LOG_PREFIX} \u5B8C\u6210\uFF08\u5F53\u524D\u5206\u652F ${currentBranch} \u5DF2\u5305\u542B ${baselineBranch} \u6700\u65B0\u4EE3\u7801\uFF09`
5711
+ );
5712
+ }
5713
+ return {
5714
+ log: lines.join("\n"),
5715
+ skipped: false,
5716
+ merged,
5717
+ conflictRollback,
5718
+ stashConflict
5719
+ };
5720
+ }
5721
+
5168
5722
  // src/commands/deploy/deploy-execute.ts
5169
5723
  function runShellCommand3(command, cwd, captureOutput) {
5170
5724
  const result = spawnSync5(command, {
@@ -5191,8 +5745,22 @@ function runShellCommand3(command, cwd, captureOutput) {
5191
5745
  return output;
5192
5746
  }
5193
5747
  async function executeDeploy(options) {
5194
- const cwd = path4.resolve(options.cwd ?? process.cwd());
5748
+ const cwd = path5.resolve(options.cwd ?? process.cwd());
5195
5749
  const captureOutput = options.captureOutput ?? false;
5750
+ const outputParts = [];
5751
+ try {
5752
+ const syncResult = await syncDeployBaselineBeforeDeploy(cwd);
5753
+ if (syncResult.log.trim()) {
5754
+ outputParts.push(syncResult.log);
5755
+ }
5756
+ } catch (error) {
5757
+ const detail = error instanceof Error ? error.message : String(error);
5758
+ throw new DeployExecutionError(
5759
+ `\u90E8\u7F72\u524D\u540C\u6B65\u57FA\u7EBF\u5206\u652F\u5931\u8D25: ${detail}`,
5760
+ 1,
5761
+ detail
5762
+ );
5763
+ }
5196
5764
  const cfg = loadApmConfig({ configPath: options.configPath });
5197
5765
  if (isWisdomLegacyDeploy(cfg)) {
5198
5766
  try {
@@ -5202,7 +5770,7 @@ async function executeDeploy(options) {
5202
5770
  configPath: options.configPath,
5203
5771
  captureOutput
5204
5772
  });
5205
- return "";
5773
+ return outputParts.join("\n");
5206
5774
  } catch (error) {
5207
5775
  if (error instanceof DeployExecutionError) {
5208
5776
  throw error;
@@ -5217,7 +5785,11 @@ async function executeDeploy(options) {
5217
5785
  const lines = formatDeployNotConfiguredLines(options.env, deployCommands);
5218
5786
  throw new DeployExecutionError(lines.join("\n"), 1, lines.join("\n"));
5219
5787
  }
5220
- return runShellCommand3(command, cwd, captureOutput);
5788
+ const commandOutput = runShellCommand3(command, cwd, captureOutput);
5789
+ if (commandOutput.trim()) {
5790
+ outputParts.push(commandOutput);
5791
+ }
5792
+ return outputParts.join("\n");
5221
5793
  }
5222
5794
  function printDeployExecutionError(error) {
5223
5795
  if (error.output.trim()) {
@@ -5282,6 +5854,8 @@ async function runDeployWithBackendTracking(options) {
5282
5854
  status: "DEPLOYING"
5283
5855
  });
5284
5856
  const logSyncer = createDeployLogSyncer(api, deploymentRunId);
5857
+ const previousDeploymentRunId = process.env[APM_DEPLOYMENT_RUN_ID_ENV];
5858
+ process.env[APM_DEPLOYMENT_RUN_ID_ENV] = deploymentRunId;
5285
5859
  const chunks = [];
5286
5860
  const appendLog = (line) => {
5287
5861
  if (!line) return;
@@ -5328,6 +5902,11 @@ async function runDeployWithBackendTracking(options) {
5328
5902
  printDeployExecutionError(deployError);
5329
5903
  process.exit(deployError.exitCode);
5330
5904
  } finally {
5905
+ if (previousDeploymentRunId === void 0) {
5906
+ delete process.env[APM_DEPLOYMENT_RUN_ID_ENV];
5907
+ } else {
5908
+ process.env[APM_DEPLOYMENT_RUN_ID_ENV] = previousDeploymentRunId;
5909
+ }
5331
5910
  console.log = originalLog;
5332
5911
  console.error = originalError;
5333
5912
  logSyncer.dispose();
@@ -5371,23 +5950,23 @@ function registerDeployMainCommand(program) {
5371
5950
  }
5372
5951
 
5373
5952
  // src/commands/deploy/backend.ts
5374
- import path9 from "node:path";
5953
+ import path10 from "node:path";
5375
5954
 
5376
5955
  // src/commands/deploy/internal/backend-deploy/backend-deploy-workflow.ts
5377
- import path8 from "node:path";
5956
+ import path9 from "node:path";
5378
5957
 
5379
5958
  // src/commands/deploy/internal/backend-deploy/dockerode-client/client.ts
5380
5959
  import Docker from "dockerode";
5381
5960
 
5382
5961
  // src/commands/deploy/internal/backend-deploy/dockerode-client/connection-options.ts
5383
5962
  import { existsSync as existsSync18, readFileSync as readFileSync15 } from "node:fs";
5384
- import path5 from "node:path";
5963
+ import path6 from "node:path";
5385
5964
  function asOptionalTlsBuffer(value) {
5386
5965
  if (typeof value !== "string") {
5387
5966
  console.log("tls filepath not exist");
5388
5967
  return void 0;
5389
5968
  }
5390
- console.log("tls filepath", path5.join(process.cwd(), value));
5969
+ console.log("tls filepath", path6.join(process.cwd(), value));
5391
5970
  const normalized = value.trim();
5392
5971
  if (normalized === "") {
5393
5972
  return void 0;
@@ -5603,8 +6182,8 @@ var DockerodeClient = class {
5603
6182
  var createDockerodeClient = (config) => new DockerodeClient(config);
5604
6183
 
5605
6184
  // src/commands/deploy/internal/backend-deploy/dockerode-client/env.ts
5606
- import { existsSync as existsSync19, readFileSync as readFileSync16, statSync as statSync6 } from "node:fs";
5607
- import path6 from "node:path";
6185
+ import { existsSync as existsSync19, readFileSync as readFileSync16, statSync as statSync7 } from "node:fs";
6186
+ import path7 from "node:path";
5608
6187
  function stripSurroundingQuotes(value) {
5609
6188
  const t = value.trim();
5610
6189
  if (t.length >= 2) {
@@ -5619,8 +6198,8 @@ function loadEnvFromFile(envFilePath) {
5619
6198
  if (!envFilePath) {
5620
6199
  return {};
5621
6200
  }
5622
- const targetPath = path6.resolve(envFilePath);
5623
- if (!existsSync19(targetPath) || !statSync6(targetPath).isFile()) {
6201
+ const targetPath = path7.resolve(envFilePath);
6202
+ if (!existsSync19(targetPath) || !statSync7(targetPath).isFile()) {
5624
6203
  return {};
5625
6204
  }
5626
6205
  const raw = readFileSync16(targetPath, "utf-8");
@@ -5795,9 +6374,9 @@ function dockerPushImage(params, cwd) {
5795
6374
 
5796
6375
  // src/commands/deploy/internal/backend-deploy/resolve-dockerfile.ts
5797
6376
  import { existsSync as existsSync20 } from "node:fs";
5798
- import path7 from "node:path";
6377
+ import path8 from "node:path";
5799
6378
  function resolveDockerBuildPaths(cwd) {
5800
- const dockerfilePath = path7.join(cwd, "Dockerfile");
6379
+ const dockerfilePath = path8.join(cwd, "Dockerfile");
5801
6380
  Logger.info(`\u67E5\u627EDockerfile\u6587\u4EF6\uFF0C\u8DEF\u5F84: ${dockerfilePath}`);
5802
6381
  if (!existsSync20(dockerfilePath)) {
5803
6382
  throw new Error(`Dockerfile \u4E0D\u5B58\u5728\uFF1A${dockerfilePath}`);
@@ -5854,7 +6433,7 @@ var BackendDeployWorkflow = class {
5854
6433
  serveraddress
5855
6434
  });
5856
6435
  Logger.success("\u8FDC\u7A0B\u62C9\u53D6\u955C\u50CF\u5B8C\u6210");
5857
- const envFilePath = path8.resolve(cwd, this.params.envFilePath || ".env");
6436
+ const envFilePath = path9.resolve(cwd, this.params.envFilePath || ".env");
5858
6437
  const envObj = loadEnvFromFile(envFilePath);
5859
6438
  if (this.params.dockerNetwork?.trim()) {
5860
6439
  Logger.info(`\u8FDC\u7A0B\u5BB9\u5668\u5C06\u52A0\u5165 Docker \u7F51\u7EDC\uFF1A${this.params.dockerNetwork.trim()}`);
@@ -5894,7 +6473,7 @@ function registerDeployBackendCommands(program) {
5894
6473
  assertDeployImageTag(tag);
5895
6474
  const cfg = loadApmConfig({ configPath: opts.config });
5896
6475
  const fromApm = resolveBackendDeployFromApmConfig(cfg);
5897
- const dirAbs = path9.resolve(process.cwd(), opts.dir || "servers/api");
6476
+ const dirAbs = path10.resolve(process.cwd(), opts.dir || "servers/api");
5898
6477
  const params = {
5899
6478
  image: fromApm.name,
5900
6479
  tag,
@@ -5926,146 +6505,6 @@ function registerDeployBackendCommands(program) {
5926
6505
  // src/commands/deploy/frontend.ts
5927
6506
  import { copyFile, readdir as readdir3, stat } from "node:fs/promises";
5928
6507
  import path11 from "node:path";
5929
-
5930
- // src/commands/deploy/internal/minio.ts
5931
- import { statSync as statSync7 } from "node:fs";
5932
- import { readdir as readdir2, readFile as readFile2 } from "node:fs/promises";
5933
- import path10 from "node:path";
5934
- import * as Minio from "minio";
5935
- var DEFAULT_MAX_FILE_SIZE_MB = 50;
5936
- async function isDirectoryPath(dir) {
5937
- try {
5938
- const st = statSync7(dir);
5939
- return st.isDirectory();
5940
- } catch {
5941
- return false;
5942
- }
5943
- }
5944
- function sanitizeRelativePath(rel) {
5945
- const norm = rel.replace(/\\/g, "/").replace(/^\/+/, "");
5946
- const segments = norm.split("/").filter(Boolean);
5947
- for (const s of segments) {
5948
- if (s === "." || s === "..") {
5949
- throw new Error(`\u975E\u6CD5\u76F8\u5BF9\u8DEF\u5F84\u7247\u6BB5\uFF1A${s}`);
5950
- }
5951
- }
5952
- return segments.join("/");
5953
- }
5954
- async function collectFiles(root) {
5955
- const out = [];
5956
- async function walk(dir, prefix) {
5957
- const entries = await readdir2(dir, { withFileTypes: true });
5958
- for (const e of entries) {
5959
- const name = e.name;
5960
- if (name === "." || name === "..") {
5961
- continue;
5962
- }
5963
- const abs = path10.join(dir, name);
5964
- const rel = prefix ? `${prefix}/${name}` : name;
5965
- if (e.isDirectory()) {
5966
- await walk(abs, rel);
5967
- } else if (e.isFile()) {
5968
- const st = statSync7(abs);
5969
- out.push({
5970
- absPath: abs,
5971
- relativePath: rel.replace(/\\/g, "/"),
5972
- size: st.size
5973
- });
5974
- }
5975
- }
5976
- }
5977
- await walk(root, "");
5978
- out.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
5979
- return out;
5980
- }
5981
- async function readArtifactFile(absPath) {
5982
- return readFile2(absPath);
5983
- }
5984
- function toMB(bytes) {
5985
- return Math.round(bytes / (1024 * 1024) * 1e3) / 1e3;
5986
- }
5987
- var MIME = {
5988
- ".html": "text/html; charset=utf-8",
5989
- ".css": "text/css; charset=utf-8",
5990
- ".js": "application/javascript; charset=utf-8",
5991
- ".json": "application/json; charset=utf-8",
5992
- ".svg": "image/svg+xml",
5993
- ".png": "image/png",
5994
- ".jpg": "image/jpeg",
5995
- ".jpeg": "image/jpeg",
5996
- ".gif": "image/gif",
5997
- ".webp": "image/webp",
5998
- ".woff": "font/woff",
5999
- ".woff2": "font/woff2",
6000
- ".ttf": "font/ttf",
6001
- ".ico": "image/x-icon",
6002
- ".txt": "text/plain; charset=utf-8",
6003
- ".map": "application/json"
6004
- };
6005
- function detectMimeType(filePath) {
6006
- const ext = path10.extname(filePath).toLowerCase();
6007
- return MIME[ext] ?? "";
6008
- }
6009
- var MinioClient = class {
6010
- inner;
6011
- constructor(opts) {
6012
- const endPoint = opts.endPoint.replace(/^https?:\/\//i, "").split("/")[0] ?? opts.endPoint;
6013
- this.inner = new Minio.Client({
6014
- endPoint,
6015
- port: opts.port,
6016
- useSSL: opts.useSSL,
6017
- accessKey: opts.accessKey,
6018
- secretKey: opts.secretKey
6019
- });
6020
- }
6021
- async ensureBucket(bucket) {
6022
- const exists = await this.inner.bucketExists(bucket);
6023
- if (!exists) {
6024
- await this.inner.makeBucket(bucket);
6025
- }
6026
- }
6027
- async deleteObjectsByPrefix(bucket, prefix) {
6028
- const objectsStream = this.inner.listObjectsV2(bucket, prefix, true);
6029
- const keys = [];
6030
- await new Promise((resolve5, reject) => {
6031
- objectsStream.on("data", (obj) => {
6032
- if (obj.name) {
6033
- keys.push(obj.name);
6034
- }
6035
- });
6036
- objectsStream.on("error", reject);
6037
- objectsStream.on("end", resolve5);
6038
- });
6039
- const chunkSize = 500;
6040
- for (let i = 0; i < keys.length; i += chunkSize) {
6041
- const chunk = keys.slice(i, i + chunkSize);
6042
- await this.inner.removeObjects(
6043
- bucket,
6044
- chunk.map((name) => name)
6045
- );
6046
- }
6047
- }
6048
- async putObject(bucket, objectKey, body, meta) {
6049
- await this.inner.putObject(bucket, objectKey, body, body.length, meta);
6050
- }
6051
- /** 匿名可读当前桶全部对象(便于静态站点直链) */
6052
- async setBucketPublicRead(bucket) {
6053
- const policy = {
6054
- Version: "2012-10-17",
6055
- Statement: [
6056
- {
6057
- Effect: "Allow",
6058
- Principal: { AWS: ["*"] },
6059
- Action: ["s3:GetObject"],
6060
- Resource: [`arn:aws:s3:::${bucket}/*`]
6061
- }
6062
- ]
6063
- };
6064
- await this.inner.setBucketPolicy(bucket, JSON.stringify(policy));
6065
- }
6066
- };
6067
-
6068
- // src/commands/deploy/frontend.ts
6069
6508
  function resolveArtifactNamePrefix(cfg) {
6070
6509
  const nameRaw = (cfg.name ?? "").trim();
6071
6510
  if (!nameRaw) {
@@ -6239,6 +6678,13 @@ function registerDeploySftpCommands(program) {
6239
6678
  async (opts) => {
6240
6679
  const cfg = loadApmConfig({ configPath: opts.config });
6241
6680
  const settings = resolveWisdomDeployFromApmConfig(cfg);
6681
+ const projectName = (cfg.name ?? "").trim();
6682
+ if (!projectName) {
6683
+ console.error(
6684
+ "\u8BF7\u5728 .apm/apm.config.json \u9876\u5C42\u914D\u7F6E name\uFF08\u4F5C\u4E3A\u90E8\u7F72\u4EA7\u7269\u9879\u76EE\u540D\uFF09"
6685
+ );
6686
+ process.exit(1);
6687
+ }
6242
6688
  const root = path12.resolve(process.cwd(), opts.dir || "apps/web/dist");
6243
6689
  if (!await isDirectoryPath(root)) {
6244
6690
  console.error(`\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${root}`);
@@ -6248,7 +6694,8 @@ function registerDeploySftpCommands(program) {
6248
6694
  const result = await runWisdomSftpDeploy({
6249
6695
  localDir: root,
6250
6696
  settings,
6251
- extract: Boolean(opts.extract)
6697
+ extract: Boolean(opts.extract),
6698
+ projectName
6252
6699
  });
6253
6700
  console.log(JSON.stringify(result, null, 2));
6254
6701
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-project-manage-cli",
3
- "version": "6.0.83",
3
+ "version": "6.0.85",
4
4
  "description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
5
5
  "type": "module",
6
6
  "private": false,