ai-project-manage-cli 6.0.102 → 6.0.104

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 +2096 -1738
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2389,7 +2389,7 @@ async function runUpdateMessageStatus(options) {
2389
2389
 
2390
2390
  // src/commands/connect.ts
2391
2391
  import WebSocket from "ws";
2392
- import { setTimeout as delay2 } from "node:timers/promises";
2392
+ import { setTimeout as delay3 } from "node:timers/promises";
2393
2393
 
2394
2394
  // src/ws/protocol.ts
2395
2395
  function nonEmptyString(v) {
@@ -2511,8 +2511,67 @@ function validateAgentWsMessage(value, kind) {
2511
2511
  return validateMessagePush(o);
2512
2512
  }
2513
2513
 
2514
+ // src/commands/deploy/deploy-log-utils.ts
2515
+ import { setTimeout as delay } from "node:timers/promises";
2516
+ var DEPLOY_SPAWN_MAX_BUFFER_BYTES = 50 * 1024 * 1024;
2517
+ var DEPLOY_LOG_SYNC_MAX_CHUNK_BYTES = 4 * 1024 * 1024;
2518
+ var DEPLOY_COMPLETE_LOG_MAX_BYTES = 9 * 1024 * 1024;
2519
+ var DEPLOY_LOG_SYNC_MAX_ATTEMPTS = 3;
2520
+ async function retryDeployLogSync(fn, attempts = DEPLOY_LOG_SYNC_MAX_ATTEMPTS) {
2521
+ let lastError;
2522
+ for (let attempt = 1; attempt <= attempts; attempt += 1) {
2523
+ try {
2524
+ return await fn();
2525
+ } catch (error) {
2526
+ lastError = error;
2527
+ if (attempt < attempts) {
2528
+ await delay(1e3 * attempt);
2529
+ }
2530
+ }
2531
+ }
2532
+ throw lastError;
2533
+ }
2534
+ function splitUtf8StringByMaxBytes(value, maxBytes) {
2535
+ if (maxBytes <= 0) {
2536
+ throw new Error("maxBytes must be positive");
2537
+ }
2538
+ const bytes = Buffer.from(value, "utf8");
2539
+ if (bytes.length <= maxBytes) {
2540
+ return [value];
2541
+ }
2542
+ const chunks = [];
2543
+ let offset = 0;
2544
+ while (offset < bytes.length) {
2545
+ let end = Math.min(offset + maxBytes, bytes.length);
2546
+ while (end > offset && (bytes[end] & 192) === 128) {
2547
+ end -= 1;
2548
+ }
2549
+ if (end <= offset) {
2550
+ end = Math.min(offset + maxBytes, bytes.length);
2551
+ }
2552
+ chunks.push(bytes.subarray(offset, end).toString("utf8"));
2553
+ offset = end;
2554
+ }
2555
+ return chunks;
2556
+ }
2557
+ function truncateDeployLogForComplete(log2) {
2558
+ const bytes = Buffer.from(log2, "utf8");
2559
+ if (bytes.length <= DEPLOY_COMPLETE_LOG_MAX_BYTES) {
2560
+ return { log: log2, truncated: false };
2561
+ }
2562
+ const tailBudget = Math.max(DEPLOY_COMPLETE_LOG_MAX_BYTES - 512, 1024);
2563
+ const prefix = `[apm] \u65E5\u5FD7\u8FC7\u957F\uFF08${Math.ceil(bytes.length / 1024)}KB\uFF09\uFF0C\u4EC5\u4FDD\u7559\u672B\u5C3E ${Math.floor(tailBudget / 1024)}KB
2564
+ `;
2565
+ let tail = bytes.subarray(bytes.length - tailBudget).toString("utf8");
2566
+ const brokenPrefix = tail.indexOf("\uFFFD");
2567
+ if (brokenPrefix >= 0) {
2568
+ const newline = tail.indexOf("\n", brokenPrefix);
2569
+ tail = newline >= 0 ? tail.slice(newline + 1) : tail.slice(brokenPrefix + 1);
2570
+ }
2571
+ return { log: prefix + tail, truncated: true };
2572
+ }
2573
+
2514
2574
  // src/commands/deploy/deploy-execute.ts
2515
- import { spawnSync as spawnSync6 } from "node:child_process";
2516
2575
  import path5 from "node:path";
2517
2576
 
2518
2577
  // src/commands/deploy/internal/apm-config.ts
@@ -2819,33 +2878,17 @@ function posixBasename(p) {
2819
2878
  }
2820
2879
 
2821
2880
  // src/commands/deploy/internal/deploy-hints.ts
2822
- function formatDeployNotConfiguredLines(env, deployCommands) {
2823
- const configuredEnvs = Object.keys(deployCommands).filter(
2824
- (key) => deployCommands[key]?.trim()
2825
- );
2826
- if (configuredEnvs.length > 0) {
2827
- return [
2828
- `[apm] \u65E0\u6CD5\u6267\u884C\u90E8\u7F72\uFF1Aapm.config.json \u4E2D\u672A\u914D\u7F6E deploy.${env}`,
2829
- `[apm] \u5F53\u524D\u5DF2\u914D\u7F6E\u7684\u73AF\u5883: ${configuredEnvs.join(", ")}`
2830
- ];
2831
- }
2881
+ function formatMissingWisdomDeployLines() {
2832
2882
  return [
2833
- "[apm] \u65E0\u6CD5\u6267\u884C\u90E8\u7F72\uFF1A\u5F53\u524D\u9879\u76EE\u672A\u914D\u7F6E\u81EA\u52A8\u5316\u90E8\u7F72\uFF08apm.config.json \u4E2D\u65E0 deploy \u6BB5\uFF09",
2834
- "[apm] \u90E8\u5206\u9879\u76EE\u4E0D\u652F\u6301\u6216\u672A\u542F\u7528\u81EA\u52A8\u5316\u90E8\u7F72\uFF0C\u5C5E\u6B63\u5E38\u60C5\u51B5\uFF0C\u53EF\u8DF3\u8FC7\u90E8\u7F72"
2883
+ "[apm] \u65E0\u6CD5\u6267\u884C apm deploy\uFF1A\u8BF7\u5728 apm.config.json \u4E2D\u914D\u7F6E wisdomDeploy\uFF08host\u3001remotePath\uFF09"
2835
2884
  ];
2836
2885
  }
2837
2886
  function formatWisdomFrontendBuildNotConfiguredLines(env, packageJsonPath) {
2838
2887
  return [
2839
- `[apm] \u65E0\u6CD5\u6267\u884C\u4EC5\u6253\u5305\uFF1A${packageJsonPath} \u4E2D\u672A\u627E\u5230 build:${env} \u811A\u672C`,
2888
+ `[apm] \u65E0\u6CD5\u6267\u884C\u524D\u7AEF\u90E8\u7F72\uFF1A${packageJsonPath} \u4E2D\u672A\u627E\u5230 build:${env} \u811A\u672C`,
2840
2889
  "[apm] \u8BF7\u914D\u7F6E npm run build:test / build:online \u540E\u518D\u8BD5"
2841
2890
  ];
2842
2891
  }
2843
- function formatWisdomFrontendDeployNotConfiguredLines(env, packageJsonPath) {
2844
- return [
2845
- `[apm] \u65E0\u6CD5\u6267\u884C\u90E8\u7F72\uFF1A${packageJsonPath} \u4E2D\u672A\u627E\u5230 deploy:${env} \u811A\u672C`,
2846
- "[apm] \u82E5\u672C\u9879\u76EE\u4E0D\u652F\u6301\u81EA\u52A8\u5316\u90E8\u7F72\uFF0C\u5C5E\u6B63\u5E38\u60C5\u51B5\uFF0C\u53EF\u8DF3\u8FC7\u90E8\u7F72"
2847
- ];
2848
- }
2849
2892
 
2850
2893
  // src/commands/deploy/deploy-errors.ts
2851
2894
  var DeployExecutionError = class extends Error {
@@ -2859,31 +2902,10 @@ var DeployExecutionError = class extends Error {
2859
2902
  }
2860
2903
  };
2861
2904
 
2862
- // src/commands/deploy/internal/wisdom-auto-deploy.ts
2863
- import { existsSync as existsSync15, readFileSync as readFileSync13, statSync as statSync7 } from "node:fs";
2864
- import path4 from "node:path";
2865
- import { spawnSync as spawnSync5 } from "node:child_process";
2866
-
2867
- // src/commands/deploy/internal/wisdom-backend-deploy.ts
2868
- import {
2869
- existsSync as existsSync12,
2870
- mkdirSync as mkdirSync5,
2871
- readdirSync as readdirSync5,
2872
- readFileSync as readFileSync11,
2873
- statSync as statSync6,
2874
- writeFileSync as writeFileSync10
2875
- } from "node:fs";
2876
- import { spawnSync as spawnSync2 } from "node:child_process";
2877
- import path3 from "node:path";
2878
- import { Client as Client2 } from "ssh2";
2879
- import JSZip2 from "jszip";
2880
- import SftpClient2 from "ssh2-sftp-client";
2881
-
2882
- // src/commands/deploy/internal/wisdom-sftp.ts
2883
- import { readdir as readdir2, readFile as readFile3, unlink, writeFile } from "node:fs/promises";
2884
- import path2 from "node:path";
2885
- import JSZip from "jszip";
2886
- import SftpClient from "ssh2-sftp-client";
2905
+ // src/commands/deploy/deploy-debug-log.ts
2906
+ import { spawnSync as spawnSync4 } from "node:child_process";
2907
+ import { existsSync as existsSync14 } from "node:fs";
2908
+ import { dirname as dirname5, join as join16 } from "node:path";
2887
2909
 
2888
2910
  // src/commands/deploy/internal/deploy-artifact-minio.ts
2889
2911
  import { readFile as readFile2 } from "node:fs/promises";
@@ -3135,1711 +3157,1974 @@ async function uploadDeployArtifactZip(options) {
3135
3157
  };
3136
3158
  }
3137
3159
 
3138
- // src/commands/deploy/internal/wisdom-sftp.ts
3139
- async function addDirToZip(dir, zipFolder) {
3140
- const entries = await readdir2(dir, { withFileTypes: true });
3141
- for (const entry of entries) {
3142
- const fullPath = path2.join(dir, entry.name);
3143
- if (entry.isDirectory()) {
3144
- const folder = zipFolder.folder(entry.name);
3145
- if (folder) {
3146
- await addDirToZip(fullPath, folder);
3147
- }
3148
- } else {
3149
- const content = await readFile3(fullPath);
3150
- zipFolder.file(entry.name, content);
3151
- }
3160
+ // src/commands/deploy/internal/deploy-shell-env.ts
3161
+ import { spawnSync as spawnSync3 } from "node:child_process";
3162
+ import { dirname as dirname4 } from "node:path";
3163
+
3164
+ // src/commands/daemon.ts
3165
+ import { spawnSync as spawnSync2 } from "child_process";
3166
+ import { setTimeout as delay2 } from "node:timers/promises";
3167
+ import { existsSync as existsSync13, mkdirSync as mkdirSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "fs";
3168
+ import { join as join15 } from "path";
3169
+
3170
+ // src/commands/connect-lock.ts
3171
+ import {
3172
+ existsSync as existsSync12,
3173
+ mkdirSync as mkdirSync5,
3174
+ readFileSync as readFileSync11,
3175
+ unlinkSync,
3176
+ writeFileSync as writeFileSync10
3177
+ } from "fs";
3178
+ import { join as join14 } from "path";
3179
+ var CONNECT_LOCK_PATH = join14(APM_CONFIG_DIR, "connect.lock");
3180
+ function isProcessAlive(pid) {
3181
+ if (!Number.isInteger(pid) || pid <= 0) return false;
3182
+ try {
3183
+ process.kill(pid, 0);
3184
+ return true;
3185
+ } catch {
3186
+ return false;
3152
3187
  }
3153
3188
  }
3154
- async function zipDirectory(distDir, zipPath) {
3155
- console.error(`\u538B\u7F29\u76EE\u5F55: ${distDir}`);
3156
- const zip = new JSZip();
3157
- await addDirToZip(distDir, zip);
3158
- const content = await zip.generateAsync({
3159
- type: "nodebuffer",
3160
- compression: "DEFLATE",
3161
- compressionOptions: { level: 6 }
3162
- });
3163
- await writeFile(zipPath, content);
3164
- const sizeMb = (content.length / 1024 / 1024).toFixed(2);
3165
- console.error(`\u5DF2\u751F\u6210: ${zipPath} (${sizeMb} MB)`);
3166
- return content.length;
3167
- }
3168
- var SFTP_UPLOAD_MAX_ATTEMPTS = 3;
3169
- var SFTP_FAST_PUT_OPTIONS = {
3170
- chunkSize: 64 * 1024,
3171
- concurrency: 4
3172
- };
3173
- function buildSftpConnectOptions(settings) {
3174
- return {
3175
- host: settings.host,
3176
- port: settings.port,
3177
- username: settings.username,
3178
- password: settings.password,
3179
- readyTimeout: 3e4,
3180
- tryKeyboard: true,
3181
- keepaliveInterval: 1e4,
3182
- keepaliveCountMax: 3
3183
- };
3184
- }
3185
- async function sleep(ms) {
3186
- await new Promise((resolve5) => setTimeout(resolve5, ms));
3187
- }
3188
- async function uploadZipWithRetry(settings, localZip, remoteZipPath) {
3189
- let lastError;
3190
- for (let attempt = 1; attempt <= SFTP_UPLOAD_MAX_ATTEMPTS; attempt++) {
3191
- const sftp = new SftpClient();
3192
- try {
3193
- if (attempt > 1) {
3194
- console.error(
3195
- `SFTP \u4E0A\u4F20\u91CD\u8BD5 (${attempt}/${SFTP_UPLOAD_MAX_ATTEMPTS})...`
3196
- );
3197
- }
3198
- await sftp.connect(buildSftpConnectOptions(settings));
3199
- await ensureRemoteDir(sftp, settings.remotePath);
3200
- await sftp.fastPut(localZip, remoteZipPath, SFTP_FAST_PUT_OPTIONS);
3201
- return sftp;
3202
- } catch (err) {
3203
- lastError = err;
3204
- try {
3205
- await sftp.end();
3206
- } catch {
3207
- }
3208
- if (attempt < SFTP_UPLOAD_MAX_ATTEMPTS) {
3209
- const message = err instanceof Error ? err.message : String(err);
3210
- const delaySec = attempt * 2;
3211
- console.error(`SFTP \u4E0A\u4F20\u5931\u8D25 (${message})\uFF0C${delaySec}s \u540E\u91CD\u8BD5...`);
3212
- await sleep(delaySec * 1e3);
3213
- }
3214
- }
3189
+ function sleepSync(ms) {
3190
+ const deadline = Date.now() + ms;
3191
+ while (Date.now() < deadline) {
3215
3192
  }
3216
- throw lastError;
3217
3193
  }
3218
- async function ensureRemoteDir(sftp, dir) {
3219
- const parts = dir.replace(/\\/g, "/").split("/").filter(Boolean);
3220
- let current = dir.startsWith("/") ? "" : ".";
3221
- for (const part of parts) {
3222
- current = current ? `${current}/${part}` : `/${part}`;
3223
- try {
3224
- await sftp.mkdir(current, true);
3225
- } catch {
3194
+ function waitForPm2LockHandoff(timeoutMs = 5e3) {
3195
+ const deadline = Date.now() + timeoutMs;
3196
+ while (Date.now() < deadline) {
3197
+ pruneStaleConnectLock();
3198
+ const lock = readConnectLock();
3199
+ if (!lock || lock.mode !== "pm2" || lock.pid === process.pid || !isProcessAlive(lock.pid)) {
3200
+ return;
3226
3201
  }
3202
+ sleepSync(100);
3227
3203
  }
3228
3204
  }
3229
- function execCommand(client, command) {
3230
- return new Promise((resolve5, reject) => {
3231
- client.exec(command, (err, stream) => {
3232
- if (err) return reject(err);
3233
- let stdout = "";
3234
- let stderr = "";
3235
- stream.on("close", (code) => {
3236
- if (code !== 0) {
3237
- reject(new Error(`\u8FDC\u7A0B\u547D\u4EE4\u5931\u8D25 (${code}): ${stderr || stdout}`));
3238
- return;
3239
- }
3240
- resolve5(stdout);
3241
- }).on("data", (data) => {
3242
- stdout += data.toString();
3243
- });
3244
- stream.stderr.on("data", (data) => {
3245
- stderr += data.toString();
3246
- });
3247
- });
3248
- });
3249
- }
3250
- function shellSingleQuote(value) {
3251
- return `'${value.replace(/'/g, `'"'"'`)}'`;
3252
- }
3253
- function buildClearRemoteDirExceptZipCommand(target) {
3254
- const normalized = target.replace(/\\/g, "/").replace(/\/$/, "");
3255
- const quotedTarget = shellSingleQuote(normalized);
3256
- const script = [
3257
- `T=${quotedTarget}`,
3258
- "S=$(mktemp -d)",
3259
- 'while IFS= read -r -d "" z; do r="${z#${T}/}"',
3260
- 'mkdir -p "${S}/$(dirname "$r")"',
3261
- 'mv "$z" "${S}/${r}"',
3262
- 'done < <(find "$T" -mindepth 1 -type f -iname "*.zip" -print0)',
3263
- 'rm -rf "${T}"/*',
3264
- 'while IFS= read -r -d "" r; do r="${r#./}"',
3265
- 'mkdir -p "${T}/$(dirname "$r")"',
3266
- 'mv "${S}/${r}" "${T}/${r}"',
3267
- 'done < <(cd "$S" 2>/dev/null && find . -type f -print0)',
3268
- 'rm -rf "$S"'
3269
- ].join("; ");
3270
- return `bash -c ${shellSingleQuote(script)}`;
3271
- }
3272
- async function uploadAndMaybeExtract(settings, localZip, extract) {
3273
- const remoteZipPath = `${settings.remotePath.replace(/\/$/, "")}/dist.zip`;
3274
- console.error(
3275
- `\u8FDE\u63A5 ${settings.username}@${settings.host}:${settings.port} ...`
3276
- );
3277
- const sftp = await uploadZipWithRetry(settings, localZip, remoteZipPath);
3205
+ function readConnectLock() {
3206
+ if (!existsSync12(CONNECT_LOCK_PATH)) return null;
3278
3207
  try {
3279
- console.error(` \u2713 ${localZip} -> ${remoteZipPath}`);
3280
- if (extract) {
3281
- const target = settings.remotePath.replace(/\/$/, "");
3282
- const client = sftp.client;
3283
- const clearCmd = buildClearRemoteDirExceptZipCommand(target);
3284
- console.error(`\u8FDC\u7A0B\u6E05\u7406\u89E3\u538B\u76EE\u5F55: ${clearCmd}`);
3285
- await execCommand(client, clearCmd);
3286
- const unzipCmd = `unzip -o "${remoteZipPath}" -d "${target}"`;
3287
- console.error(`\u8FDC\u7A0B\u89E3\u538B: ${unzipCmd}`);
3288
- await execCommand(client, unzipCmd);
3289
- console.error("\u8FDC\u7A0B\u89E3\u538B\u5B8C\u6210!");
3208
+ const raw = readFileSync11(CONNECT_LOCK_PATH, "utf8");
3209
+ const parsed = JSON.parse(raw);
3210
+ if (typeof parsed.pid !== "number" || parsed.mode !== "foreground" && parsed.mode !== "pm2" || typeof parsed.startedAt !== "string") {
3211
+ return null;
3290
3212
  }
3291
- console.error(extract ? "\u90E8\u7F72\u5B8C\u6210!" : "\u4E0A\u4F20\u5B8C\u6210!");
3292
- } finally {
3293
- await sftp.end();
3213
+ return parsed;
3214
+ } catch {
3215
+ return null;
3294
3216
  }
3295
3217
  }
3296
- async function runWisdomSftpDeploy(params) {
3297
- const zipPath = path2.join(params.localDir, "..", ".deploy-sftp-dist.zip");
3298
- const resolvedZipPath = path2.resolve(zipPath);
3299
- const packOnly = Boolean(params.packOnly);
3300
- let zipSizeBytes = 0;
3301
- let artifactUploaded = false;
3218
+ function pruneStaleConnectLock() {
3219
+ const lock = readConnectLock();
3220
+ if (!lock) return;
3221
+ if (isProcessAlive(lock.pid)) return;
3302
3222
  try {
3303
- zipSizeBytes = await zipDirectory(params.localDir, resolvedZipPath);
3304
- if (!packOnly) {
3305
- await uploadAndMaybeExtract(
3306
- params.settings,
3307
- resolvedZipPath,
3308
- params.extract
3309
- );
3310
- } else {
3311
- console.error("[apm] \u4EC5\u6253\u5305\u6A21\u5F0F\uFF1A\u8DF3\u8FC7 SFTP \u8FDC\u7A0B\u4E0A\u4F20");
3312
- }
3313
- const artifact = await uploadDeployArtifactZip({
3314
- zipPath: resolvedZipPath,
3315
- projectName: params.projectName,
3316
- kind: "frontend",
3317
- uploadWithoutRunId: packOnly && !resolveDeploymentRunIdFromEnv()
3318
- });
3319
- artifactUploaded = artifact !== null;
3320
- if (packOnly && artifactUploaded) {
3321
- console.error("\u6253\u5305\u5E76\u4E0A\u4F20 MinIO \u5B8C\u6210\uFF08\u672A\u4E0A\u4F20\u8FDC\u7A0B\u670D\u52A1\u5668\uFF09");
3223
+ unlinkSync(CONNECT_LOCK_PATH);
3224
+ } catch {
3225
+ }
3226
+ }
3227
+ function acquireConnectLock(mode) {
3228
+ pruneStaleConnectLock();
3229
+ const existing = readConnectLock();
3230
+ if (existing && isProcessAlive(existing.pid)) {
3231
+ if (existing.pid === process.pid) {
3232
+ return;
3322
3233
  }
3323
- } finally {
3324
- try {
3325
- await unlink(resolvedZipPath);
3326
- console.error(`\u5DF2\u6E05\u7406\u672C\u5730\u4E34\u65F6\u6587\u4EF6: ${resolvedZipPath}`);
3327
- } catch {
3234
+ if (mode === "pm2" && existing.mode === "pm2") {
3235
+ forceReleaseConnectLock();
3236
+ } else {
3237
+ console.error(
3238
+ `[apm] \u5DF2\u6709 apm connect \u5728\u8FD0\u884C (pid=${existing.pid}, mode=${existing.mode})`
3239
+ );
3240
+ process.exit(1);
3328
3241
  }
3329
3242
  }
3330
- return {
3331
- ok: true,
3332
- localDir: params.localDir,
3333
- host: params.settings.host,
3334
- remotePath: params.settings.remotePath,
3335
- zipSizeBytes,
3336
- extracted: packOnly ? false : params.extract,
3337
- packOnly,
3338
- artifactUploaded
3243
+ mkdirSync5(APM_CONFIG_DIR, { recursive: true });
3244
+ const lock = {
3245
+ pid: process.pid,
3246
+ mode,
3247
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
3339
3248
  };
3249
+ writeFileSync10(
3250
+ CONNECT_LOCK_PATH,
3251
+ JSON.stringify(lock, null, 2) + "\n",
3252
+ "utf8"
3253
+ );
3254
+ }
3255
+ function releaseConnectLock() {
3256
+ const lock = readConnectLock();
3257
+ if (lock?.pid !== process.pid) return;
3258
+ try {
3259
+ unlinkSync(CONNECT_LOCK_PATH);
3260
+ } catch {
3261
+ }
3262
+ }
3263
+ function forceReleaseConnectLock() {
3264
+ try {
3265
+ if (existsSync12(CONNECT_LOCK_PATH)) {
3266
+ unlinkSync(CONNECT_LOCK_PATH);
3267
+ }
3268
+ } catch {
3269
+ }
3340
3270
  }
3341
3271
 
3342
- // src/commands/deploy/internal/wisdom-backend-deploy.ts
3343
- var SPRINGBOOT_JAVA_OPTS = "-XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -Xms512M -Xmx1G";
3344
- var MAVEN_MODULE = "jeecg-module-system/jeecg-system-start";
3345
- var MAVEN_PROFILE = "dev";
3346
- function log(message) {
3347
- const now = /* @__PURE__ */ new Date();
3348
- const hh = String(now.getHours()).padStart(2, "0");
3349
- const mm = String(now.getMinutes()).padStart(2, "0");
3350
- const ss = String(now.getSeconds()).padStart(2, "0");
3351
- console.error(`[${hh}:${mm}:${ss}] ${message}`);
3272
+ // src/commands/daemon.ts
3273
+ var PM2_APP_NAME = "apm-connect";
3274
+ var PM2_ECOSYSTEM_PATH = join15(
3275
+ APM_CONFIG_DIR,
3276
+ "connect.ecosystem.config.cjs"
3277
+ );
3278
+ var PM2_CONNECT_ENTRY_PATH = join15(APM_CONFIG_DIR, "connect.entry.cjs");
3279
+ var PM2_CONNECT_LAUNCH_PATH = join15(
3280
+ APM_CONFIG_DIR,
3281
+ "connect.launch.json"
3282
+ );
3283
+ var LEGACY_PM2_ECOSYSTEM_PATH = join15(
3284
+ APM_CONFIG_DIR,
3285
+ "connect.ecosystem.cjs"
3286
+ );
3287
+ var LEGACY_PM2_APP_NAMES = ["connect.ecosystem"];
3288
+ var CONNECT_ENTRY_SCRIPT = `'use strict';
3289
+ const { spawnSync } = require('node:child_process');
3290
+ const { readFileSync } = require('node:fs');
3291
+ const { join } = require('node:path');
3292
+
3293
+ const launchConfigPath = join(__dirname, 'connect.launch.json');
3294
+ let config;
3295
+ try {
3296
+ config = JSON.parse(readFileSync(launchConfigPath, 'utf8'));
3297
+ } catch (err) {
3298
+ console.error('[apm] \u65E0\u6CD5\u8BFB\u53D6 connect.launch.json:', err instanceof Error ? err.message : err);
3299
+ process.exit(1);
3352
3300
  }
3353
- function fail(message) {
3354
- log(`ERROR: ${message}`);
3301
+
3302
+ const nodeArgs = [config.apmScript, ...(config.connectArgs || [])];
3303
+ const childEnv = {
3304
+ ...process.env,
3305
+ ...(config.env || {}),
3306
+ APM_CONNECT_UNDER_PM2: '1',
3307
+ };
3308
+
3309
+ const result = spawnSync(config.nodePath, nodeArgs, {
3310
+ cwd: config.cwd || undefined,
3311
+ env: childEnv,
3312
+ stdio: 'inherit',
3313
+ windowsHide: true,
3314
+ });
3315
+
3316
+ if (result.error) {
3317
+ console.error('[apm] connect \u542F\u52A8\u5931\u8D25:', result.error.message);
3355
3318
  process.exit(1);
3356
3319
  }
3357
- function expandPath(pathStr) {
3358
- const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
3359
- return path3.resolve(pathStr.replace(/^~(?=\/|\\|$)/, home));
3320
+ process.exit(result.status ?? 0);
3321
+ `;
3322
+ function resolveConnectArgs(server) {
3323
+ const args = ["connect"];
3324
+ const trimmed = server?.trim();
3325
+ if (trimmed) {
3326
+ args.push("--server", trimmed.replace(/\/+$/, ""));
3327
+ }
3328
+ return args;
3360
3329
  }
3361
- function quoteForShell(value) {
3362
- if (process.platform === "win32") {
3363
- return `"${value.replace(/"/g, '""')}"`;
3330
+ var PM2_LAUNCH_ENV_KEYS = [
3331
+ "PATH",
3332
+ "HOME",
3333
+ "USER",
3334
+ "SHELL",
3335
+ "LANG",
3336
+ "LC_ALL",
3337
+ "LC_CTYPE",
3338
+ "NVM_DIR",
3339
+ "NVM_BIN",
3340
+ "NVM_INC",
3341
+ "FNM_DIR",
3342
+ "VOLTA_HOME",
3343
+ "PNPM_HOME",
3344
+ "npm_config_prefix",
3345
+ "NODE_HOME"
3346
+ ];
3347
+ function collectPm2LaunchEnv() {
3348
+ const env = {};
3349
+ for (const key of PM2_LAUNCH_ENV_KEYS) {
3350
+ const value = process.env[key]?.trim();
3351
+ if (value) {
3352
+ env[key] = value;
3353
+ }
3364
3354
  }
3365
- return shellSingleQuote(value);
3355
+ return env;
3366
3356
  }
3367
- function formatMavenLocalRepoArg(repoPath) {
3357
+ function buildConnectPm2Ecosystem(options) {
3358
+ const env = {};
3359
+ const baseUrl = options.baseUrl?.trim().replace(/\/+$/, "");
3360
+ if (baseUrl) {
3361
+ env.AI_PM_SERVER = baseUrl;
3362
+ }
3363
+ const app = {
3364
+ name: PM2_APP_NAME,
3365
+ script: options.entryScript,
3366
+ cwd: APM_CONFIG_DIR,
3367
+ autorestart: true,
3368
+ min_uptime: "10s",
3369
+ max_restarts: 10,
3370
+ restart_delay: 3e3,
3371
+ exp_backoff_restart_delay: 1e3,
3372
+ max_memory_restart: "2G",
3373
+ kill_timeout: 5e3,
3374
+ shutdown_with_message: true,
3375
+ instances: 1,
3376
+ exec_mode: "fork",
3377
+ env
3378
+ };
3368
3379
  if (process.platform === "win32") {
3369
- return `-Dmaven.repo.local=${quoteForShell(repoPath)}`;
3380
+ app.windowsHide = true;
3370
3381
  }
3371
- return `-Dmaven.repo.local=${repoPath}`;
3382
+ return {
3383
+ apps: [app]
3384
+ };
3372
3385
  }
3373
- function deployCacheDir() {
3374
- return path3.join(workspaceApmDir(), "deploy", ".deploy_cache");
3386
+ function formatConnectPm2EcosystemFile(ecosystem) {
3387
+ return `module.exports = ${JSON.stringify(ecosystem, null, 2)};
3388
+ `;
3375
3389
  }
3376
- function manifestFilePath() {
3377
- return path3.join(deployCacheDir(), "manifest.json");
3390
+ var useNpmShell2 = process.platform === "win32";
3391
+ function runNpm2(args, options = {}) {
3392
+ return spawnSync2(useNpmShell2 ? "npm.cmd" : "npm", args, {
3393
+ ...options,
3394
+ shell: useNpmShell2
3395
+ });
3378
3396
  }
3379
- function getTargetDir(projectRoot) {
3380
- return path3.join(projectRoot, MAVEN_MODULE, "target");
3397
+ function resolveNpmGlobalBin() {
3398
+ const binResult = runNpm2(["bin", "-g"], {
3399
+ encoding: "utf8",
3400
+ stdio: ["ignore", "pipe", "pipe"]
3401
+ });
3402
+ if (binResult.status !== 0) return null;
3403
+ return binResult.stdout?.toString().trim() || null;
3381
3404
  }
3382
- function relativeKey(projectRoot, filePath) {
3383
- return path3.relative(projectRoot, filePath).split(path3.sep).join("/");
3405
+ function resolvePm2FromNpmRoot() {
3406
+ const rootResult = runNpm2(["root", "-g"], {
3407
+ encoding: "utf8",
3408
+ stdio: ["ignore", "pipe", "pipe"]
3409
+ });
3410
+ if (rootResult.status !== 0) return null;
3411
+ const root = rootResult.stdout?.toString().trim();
3412
+ if (!root) return null;
3413
+ for (const rel of ["pm2/bin/pm2", "pm2/bin/pm2.js"]) {
3414
+ const candidate = join15(root, ...rel.split("/"));
3415
+ if (existsSync13(candidate)) {
3416
+ return candidate;
3417
+ }
3418
+ }
3419
+ return null;
3384
3420
  }
3385
- function fileSignature(filePath) {
3386
- const stat2 = statSync6(filePath);
3387
- return { size: stat2.size, mtime: stat2.mtimeMs / 1e3 };
3421
+ function isPm2NodeScript(path13) {
3422
+ return path13.endsWith(".js") || /[\\/]pm2[\\/]bin[\\/]pm2$/.test(path13);
3388
3423
  }
3389
- function loadManifest3() {
3390
- const manifestPath2 = manifestFilePath();
3391
- if (!existsSync12(manifestPath2)) {
3392
- return {};
3424
+ function buildPm2SpawnTarget(binPath) {
3425
+ if (isPm2NodeScript(binPath)) {
3426
+ return {
3427
+ command: process.execPath,
3428
+ prefixArgs: [binPath],
3429
+ displayPath: binPath
3430
+ };
3393
3431
  }
3394
- return JSON.parse(readFileSync11(manifestPath2, "utf8"));
3432
+ return {
3433
+ command: binPath,
3434
+ prefixArgs: [],
3435
+ displayPath: binPath
3436
+ };
3395
3437
  }
3396
- function saveManifest3(manifest) {
3397
- const dir = deployCacheDir();
3398
- mkdirSync5(dir, { recursive: true });
3399
- writeFileSync10(manifestFilePath(), JSON.stringify(manifest, null, 2), "utf8");
3438
+ function spawnPm2Target(target, args, options = {}) {
3439
+ const useShell = useNpmShell2 && target.prefixArgs.length === 0;
3440
+ return spawnSync2(target.command, [...target.prefixArgs, ...args], {
3441
+ encoding: "utf8",
3442
+ env: process.env,
3443
+ shell: useShell,
3444
+ windowsHide: useNpmShell2,
3445
+ ...options
3446
+ });
3400
3447
  }
3401
- function isProjectLibJar(jarName) {
3402
- return jarName.startsWith("jeecg-");
3448
+ function spawnPm2At(binPath, args, options = {}) {
3449
+ return spawnPm2Target(buildPm2SpawnTarget(binPath), args, options);
3403
3450
  }
3404
- function shouldUploadLibFile(localPath, remoteAttr, manifest, projectRoot) {
3405
- if (!remoteAttr) {
3406
- return [false, "\u8FDC\u7A0B\u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7"];
3407
- }
3408
- const localSize = statSync6(localPath).size;
3409
- const remoteSize = remoteAttr.size;
3410
- if (localSize !== remoteSize) {
3411
- return [true, `\u5927\u5C0F\u53D8\u5316 ${remoteSize} -> ${localSize}`];
3451
+ function verifyPm2Bin(pm2Bin) {
3452
+ if (!pm2Bin.trim() || !existsSync13(pm2Bin)) {
3453
+ return false;
3412
3454
  }
3413
- if (isProjectLibJar(path3.basename(localPath)) && manifest) {
3414
- const key = relativeKey(projectRoot, localPath);
3415
- const current = fileSignature(localPath);
3416
- const previous = manifest[key];
3417
- if (!previous) {
3418
- return [true, "\u9879\u76EE\u6A21\u5757\u672A\u8BB0\u5F55"];
3419
- }
3420
- if (previous.size !== current.size) {
3421
- return [true, "\u9879\u76EE\u6A21\u5757\u5927\u5C0F\u53D8\u5316"];
3455
+ const result = spawnPm2At(pm2Bin, ["--version"], {
3456
+ stdio: ["ignore", "pipe", "pipe"]
3457
+ });
3458
+ return !result.error && result.status === 0;
3459
+ }
3460
+ function collectPm2Candidates() {
3461
+ const candidates = [];
3462
+ const globalBin = resolveNpmGlobalBin();
3463
+ if (globalBin) {
3464
+ if (useNpmShell2) {
3465
+ candidates.push(join15(globalBin, "pm2.cmd"));
3466
+ candidates.push(join15(globalBin, "pm2"));
3467
+ } else {
3468
+ candidates.push(join15(globalBin, "pm2"));
3422
3469
  }
3423
- if (previous.mtime < current.mtime) {
3424
- return [true, "\u9879\u76EE\u6A21\u5757\u91CD\u65B0\u6784\u5EFA"];
3470
+ }
3471
+ const fromRoot = resolvePm2FromNpmRoot();
3472
+ if (fromRoot) {
3473
+ candidates.push(fromRoot);
3474
+ }
3475
+ const whichResult = spawnSync2(useNpmShell2 ? "where" : "which", ["pm2"], {
3476
+ encoding: "utf8",
3477
+ shell: useNpmShell2,
3478
+ env: process.env,
3479
+ stdio: ["ignore", "pipe", "pipe"]
3480
+ });
3481
+ if (whichResult.status === 0) {
3482
+ const fromPath = whichResult.stdout?.toString().trim().split(/\r?\n/).map((line) => line.trim()).filter(Boolean) ?? [];
3483
+ if (useNpmShell2) {
3484
+ fromPath.sort((a, b) => {
3485
+ const aCmd = a.toLowerCase().endsWith(".cmd") ? 0 : 1;
3486
+ const bCmd = b.toLowerCase().endsWith(".cmd") ? 0 : 1;
3487
+ return aCmd - bCmd;
3488
+ });
3425
3489
  }
3490
+ candidates.push(...fromPath);
3426
3491
  }
3427
- return [false, "\u5927\u5C0F\u4E00\u81F4\uFF0C\u8DF3\u8FC7"];
3492
+ return candidates;
3428
3493
  }
3429
- function listLibFilesToUpload(localLibDir, remoteStats, projectRoot, manifest = null) {
3430
- const entries = [];
3431
- const jarFiles = readdirSync5(localLibDir).filter((name) => name.endsWith(".jar")).sort();
3432
- for (const jarName of jarFiles) {
3433
- const jarPath = path3.join(localLibDir, jarName);
3434
- const remoteAttr = remoteStats.get(jarName);
3435
- const [shouldUpload, reason] = shouldUploadLibFile(
3436
- jarPath,
3437
- remoteAttr,
3438
- manifest,
3439
- projectRoot
3440
- );
3441
- if (shouldUpload) {
3442
- entries.push({ path: jarPath, arcname: jarName, reason });
3494
+ function findGlobalPm2() {
3495
+ const seen = /* @__PURE__ */ new Set();
3496
+ for (const candidate of collectPm2Candidates()) {
3497
+ const key = useNpmShell2 ? candidate.toLowerCase() : candidate;
3498
+ if (seen.has(key)) continue;
3499
+ seen.add(key);
3500
+ if (verifyPm2Bin(candidate)) {
3501
+ return candidate;
3443
3502
  }
3444
3503
  }
3445
- return entries;
3504
+ return null;
3446
3505
  }
3447
- function updateManifestEntries(manifest, entries, projectRoot) {
3448
- for (const entry of entries) {
3449
- manifest[relativeKey(projectRoot, entry.path)] = fileSignature(entry.path);
3506
+ function installGlobalPm2() {
3507
+ console.log("[apm] \u672A\u68C0\u6D4B\u5230\u5168\u5C40 pm2\uFF0C\u6B63\u5728\u5B89\u88C5 npm install -g pm2 \u2026");
3508
+ const result = runNpm2(["install", "-g", "pm2"], {
3509
+ encoding: "utf8",
3510
+ stdio: "inherit"
3511
+ });
3512
+ if (result.status !== 0) {
3513
+ console.error("[apm] \u5B89\u88C5 pm2 \u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u6267\u884C: npm install -g pm2");
3514
+ process.exit(result.status ?? 1);
3450
3515
  }
3451
- return manifest;
3452
3516
  }
3453
- async function createUpdatePackage(entries, packageName) {
3454
- const dir = deployCacheDir();
3455
- mkdirSync5(dir, { recursive: true });
3456
- const zipPath = path3.join(dir, packageName);
3457
- log(`\u521B\u5EFA\u66F4\u65B0\u5305: ${path3.basename(zipPath)}\uFF08${entries.length} \u4E2A\u6587\u4EF6\uFF09`);
3458
- const zip = new JSZip2();
3459
- for (const entry of entries) {
3460
- const content = readFileSync11(entry.path);
3461
- zip.file(entry.arcname, content);
3462
- log(` \u6253\u5305: ${entry.arcname} (${entry.reason})`);
3517
+ function readPm2Version(pm2Bin) {
3518
+ const result = spawnPm2At(pm2Bin, ["--version"], {
3519
+ stdio: ["ignore", "pipe", "pipe"]
3520
+ });
3521
+ if (result.error || result.status !== 0) {
3522
+ return null;
3463
3523
  }
3464
- const buffer = await zip.generateAsync({
3465
- type: "nodebuffer",
3466
- compression: "DEFLATE",
3467
- compressionOptions: { level: 6 }
3524
+ return result.stdout?.toString().trim() || null;
3525
+ }
3526
+ function logPm2Ready(pm2Bin, installed2) {
3527
+ const version = readPm2Version(pm2Bin);
3528
+ const versionSuffix = version ? ` ${version}` : "";
3529
+ if (installed2) {
3530
+ console.log(`[apm] \u5168\u5C40 pm2 \u5DF2\u5B89\u88C5${versionSuffix}: ${pm2Bin}`);
3531
+ } else {
3532
+ console.log(`[apm] \u5DF2\u68C0\u6D4B\u5230\u5168\u5C40 pm2${versionSuffix}: ${pm2Bin}`);
3533
+ }
3534
+ }
3535
+ function isPm2DaemonOutOfDate(output) {
3536
+ return /In-memory PM2 is out-of-date/i.test(output);
3537
+ }
3538
+ function ensurePm2DaemonUpdated(pm2Bin) {
3539
+ const probe = spawnPm2At(pm2Bin, ["jlist"], {
3540
+ stdio: ["ignore", "pipe", "pipe"]
3468
3541
  });
3469
- writeFileSync10(zipPath, buffer);
3470
- return zipPath;
3542
+ const combined = `${probe.stdout ?? ""}
3543
+ ${probe.stderr ?? ""}`;
3544
+ if (!isPm2DaemonOutOfDate(combined)) {
3545
+ return;
3546
+ }
3547
+ console.log("[apm] \u68C0\u6D4B\u5230 PM2 \u5185\u5B58\u7248\u672C\u4E0E\u672C\u5730\u4E0D\u4E00\u81F4\uFF0C\u6B63\u5728\u6267\u884C pm2 update \u2026");
3548
+ const update = spawnPm2At(pm2Bin, ["update"], {
3549
+ stdio: "inherit"
3550
+ });
3551
+ if (update.error || update.status !== 0) {
3552
+ console.error("[apm] pm2 update \u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u6267\u884C: pm2 update");
3553
+ }
3471
3554
  }
3472
- function getMvnExecutable() {
3473
- const isWin = process.platform === "win32";
3474
- const candidates = isWin ? ["mvn.cmd", "mvn.bat", "mvn"] : ["mvn"];
3475
- for (const name of candidates) {
3476
- const result = spawnSync2(isWin ? `where ${name}` : `which ${name}`, {
3477
- encoding: "utf8",
3478
- shell: true
3479
- });
3480
- if (result.status === 0 && result.stdout.trim()) {
3481
- return result.stdout.trim().split(/\r?\n/)[0].trim();
3555
+ function ensureGlobalPm2(options) {
3556
+ const existing = findGlobalPm2();
3557
+ if (existing) {
3558
+ ensurePm2DaemonUpdated(existing);
3559
+ if (!options?.quiet) {
3560
+ logPm2Ready(existing, false);
3482
3561
  }
3562
+ return existing;
3483
3563
  }
3484
- fail("\u672A\u627E\u5230 mvn \u547D\u4EE4\uFF0C\u8BF7\u786E\u8BA4 Maven \u5DF2\u5B89\u88C5\u5E76\u52A0\u5165 PATH");
3485
- }
3486
- function runMavenBuild(projectRoot, mavenLocalRepo, repoSource) {
3487
- const mavenRepo = expandPath(mavenLocalRepo);
3488
- const mvn = getMvnExecutable();
3489
- const command = [
3490
- quoteForShell(mvn),
3491
- "clean",
3492
- "package",
3493
- `-P${MAVEN_PROFILE}`,
3494
- formatMavenLocalRepoArg(mavenRepo),
3495
- "-DskipTests"
3496
- ].join(" ");
3497
- log("\u5F00\u59CB Maven \u6784\u5EFA...");
3498
- if (repoSource) {
3499
- log(
3500
- `Maven \u672C\u5730\u4ED3\u5E93: ${mavenRepo} (\u6765\u6E90: ${repoSource.source}/${repoSource.sourceDetail})`
3564
+ installGlobalPm2();
3565
+ const installed2 = findGlobalPm2();
3566
+ if (!installed2) {
3567
+ const globalBin = resolveNpmGlobalBin();
3568
+ const moduleEntry = resolvePm2FromNpmRoot();
3569
+ console.error(
3570
+ "[apm] \u5B89\u88C5 pm2 \u540E\u4ECD\u65E0\u6CD5\u627E\u5230\u53EF\u6267\u884C\u6587\u4EF6\uFF0C\u8BF7\u624B\u52A8\u6267\u884C: npm install -g pm2"
3501
3571
  );
3502
- } else {
3503
- log(`Maven \u672C\u5730\u4ED3\u5E93: ${mavenRepo}`);
3572
+ if (globalBin) {
3573
+ console.error(`[apm] npm \u5168\u5C40 bin: ${globalBin}`);
3574
+ }
3575
+ if (moduleEntry) {
3576
+ console.error(`[apm] \u5DF2\u68C0\u6D4B\u5230 pm2 \u6A21\u5757: ${moduleEntry}`);
3577
+ console.error(
3578
+ "[apm] \u82E5 pm2 --version \u53EF\u7528\uFF0C\u8BF7\u68C0\u67E5 Node/npm \u5168\u5C40\u5B89\u88C5\u6743\u9650\u6216 PATH"
3579
+ );
3580
+ }
3581
+ process.exit(1);
3504
3582
  }
3505
- log(`\u6784\u5EFA\u76EE\u5F55: ${projectRoot}`);
3506
- const result = spawnSync2(command, {
3507
- cwd: projectRoot,
3508
- stdio: "inherit",
3509
- shell: true,
3510
- env: process.env
3511
- });
3512
- if (result.status !== 0) {
3513
- fail(`Maven \u6784\u5EFA\u5931\u8D25\uFF0C\u9000\u51FA\u7801: ${result.status ?? 1}`);
3583
+ ensurePm2DaemonUpdated(installed2);
3584
+ if (!options?.quiet) {
3585
+ logPm2Ready(installed2, true);
3514
3586
  }
3587
+ return installed2;
3515
3588
  }
3516
- function locateLibDir(projectRoot) {
3517
- const targetDir = getTargetDir(projectRoot);
3518
- if (!existsSync12(targetDir)) {
3519
- fail(`\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728: ${targetDir}`);
3589
+ function resolveApmEntryPath(entryArg = process.argv[1]) {
3590
+ const fromArgv = entryArg?.trim();
3591
+ if (fromArgv && existsSync13(fromArgv)) {
3592
+ return fromArgv;
3520
3593
  }
3521
- const libDir = path3.join(targetDir, "lib");
3522
- if (!existsSync12(libDir) || !statSync6(libDir).isDirectory()) {
3523
- fail(`lib \u76EE\u5F55\u4E0D\u5B58\u5728: ${libDir}`);
3594
+ const npmResult = spawnSync2(useNpmShell2 ? "npm.cmd" : "npm", ["root", "-g"], {
3595
+ encoding: "utf8",
3596
+ shell: useNpmShell2,
3597
+ stdio: ["ignore", "pipe", "pipe"]
3598
+ });
3599
+ if (npmResult.status === 0) {
3600
+ const globalRoot = npmResult.stdout?.toString().trim();
3601
+ if (globalRoot) {
3602
+ const candidate = join15(globalRoot, CLI_PACKAGE_NAME, "dist", "index.js");
3603
+ if (existsSync13(candidate)) {
3604
+ return candidate;
3605
+ }
3606
+ }
3524
3607
  }
3525
- const libJars = readdirSync5(libDir).filter((name) => name.endsWith(".jar"));
3526
- if (libJars.length === 0) {
3527
- fail(`lib \u76EE\u5F55\u4E0B\u6CA1\u6709\u4F9D\u8D56 JAR: ${libDir}`);
3608
+ if (fromArgv) {
3609
+ return fromArgv;
3528
3610
  }
3529
- log(`\u5B9A\u4F4D lib \u4EA7\u7269: ${libJars.length} \u4E2A`);
3530
- return libDir;
3611
+ console.error("[apm] \u65E0\u6CD5\u89E3\u6790 apm \u5165\u53E3\u8DEF\u5F84");
3612
+ process.exit(1);
3531
3613
  }
3532
- function locateMainJar(projectRoot) {
3533
- const targetDir = getTargetDir(projectRoot);
3534
- if (!existsSync12(targetDir)) {
3535
- fail(`\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728: ${targetDir}`);
3614
+ function isRunningUnderPm2() {
3615
+ if (process.env.APM_CONNECT_UNDER_PM2 === "1") {
3616
+ return true;
3536
3617
  }
3537
- 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);
3538
- if (jarFiles.length === 0) {
3539
- fail(`target \u76EE\u5F55\u4E0B\u6CA1\u6709\u4E3B JAR: ${targetDir}`);
3618
+ if (process.env.pm_id === void 0) {
3619
+ return false;
3540
3620
  }
3541
- const mainJar = jarFiles[0];
3542
- log(`\u5B9A\u4F4D\u4E3B JAR \u4EA7\u7269: ${path3.basename(mainJar)}`);
3543
- return mainJar;
3621
+ const name = process.env.name;
3622
+ return name === PM2_APP_NAME || name !== void 0 && LEGACY_PM2_APP_NAMES.includes(name);
3544
3623
  }
3545
- async function connectSsh(config) {
3546
- const client = new Client2();
3547
- log(`\u8FDE\u63A5\u670D\u52A1\u5668 ${config.username}@${config.host}:${config.port}`);
3548
- await new Promise((resolve5, reject) => {
3549
- client.on("ready", () => resolve5()).on("error", (err) => reject(err)).connect({
3550
- host: config.host,
3551
- port: config.port,
3552
- username: config.username,
3553
- password: config.password,
3554
- readyTimeout: 3e4,
3555
- tryKeyboard: true
3556
- });
3557
- });
3558
- const sftp = new SftpClient2();
3559
- await sftp.connect({
3560
- host: config.host,
3561
- port: config.port,
3562
- username: config.username,
3563
- password: config.password,
3564
- readyTimeout: 3e4,
3565
- tryKeyboard: true
3566
- });
3567
- return { client, sftp };
3624
+ function isConnectPm2Process(app) {
3625
+ if (app.name === PM2_APP_NAME) {
3626
+ return true;
3627
+ }
3628
+ return app.name !== void 0 && LEGACY_PM2_APP_NAMES.includes(app.name);
3568
3629
  }
3569
- async function closeSsh(conn) {
3630
+ function listPm2Processes() {
3570
3631
  try {
3571
- await conn.sftp.end();
3632
+ const raw = runPm2Json(["jlist"]);
3633
+ return JSON.parse(raw);
3572
3634
  } catch {
3635
+ return [];
3573
3636
  }
3574
- conn.client.end();
3575
3637
  }
3576
- async function getRemoteFileStats(sftp, remoteDir) {
3577
- const stats = /* @__PURE__ */ new Map();
3578
- try {
3579
- const listing = await sftp.list(remoteDir);
3580
- for (const item of listing) {
3581
- if (item.name.endsWith(".jar")) {
3582
- stats.set(item.name, { filename: item.name, size: item.size });
3583
- }
3584
- }
3585
- } catch {
3638
+ function findPm2ConnectProcess() {
3639
+ return listPm2Processes().find(isConnectPm2Process) ?? null;
3640
+ }
3641
+ function resolvePm2ConnectName(app) {
3642
+ return app?.name ?? PM2_APP_NAME;
3643
+ }
3644
+ function deletePm2AppIfExists(name) {
3645
+ const pm2Bin = findGlobalPm2();
3646
+ if (!pm2Bin) {
3647
+ return;
3586
3648
  }
3587
- return stats;
3649
+ ensurePm2DaemonUpdated(pm2Bin);
3650
+ spawnPm2At(pm2Bin, ["delete", name], {
3651
+ stdio: "ignore"
3652
+ });
3588
3653
  }
3589
- async function uploadUpdatePackage(sftp, zipPath, config) {
3590
- const remoteDir = config.remoteVueDistDir.replace(/\/$/, "");
3591
- const remotePath = `${remoteDir}/${path3.basename(zipPath)}`;
3592
- log(`\u4E0A\u4F20\u66F4\u65B0\u5305 -> ${remotePath}`);
3593
- try {
3594
- await sftp.fastPut(zipPath, remotePath);
3595
- log("\u66F4\u65B0\u5305\u4E0A\u4F20\u6210\u529F");
3596
- } catch (err) {
3597
- fail(`\u66F4\u65B0\u5305\u4E0A\u4F20\u5931\u8D25: ${err instanceof Error ? err.message : err}`);
3654
+ function removePm2ConnectProcesses() {
3655
+ const seen = /* @__PURE__ */ new Set();
3656
+ for (const app of listPm2Processes()) {
3657
+ if (!isConnectPm2Process(app) || !app.name || seen.has(app.name)) {
3658
+ continue;
3659
+ }
3660
+ seen.add(app.name);
3661
+ deletePm2AppIfExists(app.name);
3598
3662
  }
3599
- return remotePath;
3663
+ for (const legacyName of LEGACY_PM2_APP_NAMES) {
3664
+ deletePm2AppIfExists(legacyName);
3665
+ }
3666
+ deletePm2AppIfExists(PM2_APP_NAME);
3600
3667
  }
3601
- async function uploadFullJar(sftp, localJarPath, config) {
3602
- const remoteDir = config.remoteAppDir.replace(/\/$/, "");
3603
- const remotePath = `${remoteDir}/${config.startupJar}`;
3604
- log(`\u4E0A\u4F20\u5168\u91CF JAR (${path3.basename(localJarPath)}) -> ${remotePath}`);
3605
- try {
3606
- await sftp.mkdir(remoteDir, true);
3607
- await sftp.fastPut(localJarPath, remotePath);
3608
- log("\u5168\u91CF JAR \u4E0A\u4F20\u6210\u529F");
3609
- } catch (err) {
3610
- fail(`\u5168\u91CF JAR \u4E0A\u4F20\u5931\u8D25: ${err instanceof Error ? err.message : err}`);
3668
+ function runPm2(args, options) {
3669
+ const pm2Bin = ensureGlobalPm2({ quiet: true });
3670
+ const result = spawnPm2At(pm2Bin, args, {
3671
+ stdio: options?.inherit ? "inherit" : ["ignore", "pipe", "pipe"]
3672
+ });
3673
+ if (result.error) {
3674
+ console.error("[apm] \u6267\u884C pm2 \u5931\u8D25:", result.error.message);
3675
+ process.exit(1);
3676
+ }
3677
+ if (result.status !== 0) {
3678
+ const stderr = result.stderr?.toString().trim();
3679
+ const stdout = result.stdout?.toString().trim();
3680
+ const detail = stderr || stdout || `exit code ${result.status}`;
3681
+ console.error(`[apm] pm2 ${args.join(" ")} \u5931\u8D25: ${detail}`);
3682
+ process.exit(result.status ?? 1);
3611
3683
  }
3612
3684
  }
3613
- async function runRemoteCommand(client, command, options) {
3614
- const check = options?.check ?? true;
3615
- const stream = options?.stream ?? false;
3616
- const label = options?.label ?? "\u8FDC\u7A0B\u547D\u4EE4";
3617
- return new Promise((resolve5, reject) => {
3618
- client.exec(command, (err, execStream) => {
3619
- if (err) {
3620
- reject(err);
3621
- return;
3622
- }
3623
- let out = "";
3624
- let errText = "";
3625
- execStream.on("data", (data) => {
3626
- const text = data.toString();
3627
- out += text;
3628
- if (stream) {
3629
- process.stdout.write(text);
3630
- }
3631
- });
3632
- execStream.stderr.on("data", (data) => {
3633
- errText += data.toString();
3634
- });
3635
- execStream.on("close", (code) => {
3636
- if (stream && out && !out.endsWith("\n")) {
3637
- process.stdout.write("\n");
3638
- }
3639
- if (check && code !== 0) {
3640
- const combined = `${out}
3641
- ${errText}`.trim();
3642
- fail(
3643
- `${label}\u5931\u8D25 (exit ${code})` + (combined ? `
3644
- \u8F93\u51FA: ${combined}` : "")
3645
- );
3646
- }
3647
- resolve5({ exitCode: code, out: out.trim(), err: errText.trim() });
3648
- });
3649
- });
3685
+ function runPm2Json(args) {
3686
+ const pm2Bin = findGlobalPm2();
3687
+ if (!pm2Bin) {
3688
+ return "[]";
3689
+ }
3690
+ ensurePm2DaemonUpdated(pm2Bin);
3691
+ const result = spawnPm2At(pm2Bin, args, {
3692
+ stdio: ["ignore", "pipe", "pipe"]
3650
3693
  });
3694
+ if (result.status !== 0) return "[]";
3695
+ return result.stdout?.toString() ?? "[]";
3651
3696
  }
3652
- function buildExtractUpdatePackageScript(remoteZipPath, remoteLibDir) {
3653
- const quotedZip = shellSingleQuote(remoteZipPath);
3654
- const quotedLib = shellSingleQuote(remoteLibDir);
3655
- return `
3656
- set -e
3657
- TMP=$(mktemp -d)
3658
- trap 'rm -rf "$TMP"' EXIT
3659
- unzip -oq ${quotedZip} -d "$TMP"
3660
- updated=0
3661
- while IFS= read -r -d '' src; do
3662
- name=$(basename "$src")
3663
- dest=${quotedLib}/"$name"
3664
- if [ -f "$dest" ]; then
3665
- cp -f "$src" "$dest"
3666
- echo "\u8986\u76D6: $name"
3667
- updated=$((updated + 1))
3668
- else
3669
- echo "\u8DF3\u8FC7(\u8FDC\u7A0B\u4E0D\u5B58\u5728): $name"
3670
- fi
3671
- done < <(find "$TMP" -name '*.jar' -type f -print0)
3672
- echo "UPDATED_COUNT=$updated"
3673
- `.trim();
3697
+ function isPm2ConnectOnline() {
3698
+ const app = findPm2ConnectProcess();
3699
+ if (!app) return false;
3700
+ const status = app.pm2_env?.status;
3701
+ return status === "online" || status === "launching";
3674
3702
  }
3675
- async function extractUpdatePackageOnRemote(client, config, remoteZipPath) {
3676
- const script = buildExtractUpdatePackageScript(
3677
- remoteZipPath,
3678
- config.remoteLibDir
3679
- );
3680
- const { out } = await runRemoteCommand(client, script, {
3681
- label: "\u8FDC\u7A0B\u89E3\u538B"
3682
- });
3683
- const match = out.match(/UPDATED_COUNT=(\d+)/);
3684
- if (!match) {
3685
- fail(`\u8FDC\u7A0B\u89E3\u538B\u5931\u8D25\uFF0C\u672A\u83B7\u53D6\u66F4\u65B0\u6570\u91CF
3686
- \u8F93\u51FA: ${out || "(\u7A7A)"}`);
3703
+ function printConnectNotRunningHint() {
3704
+ console.log(`[apm] ${PM2_APP_NAME} \u672A\u5728\u8FD0\u884C`);
3705
+ console.log("[apm] \u542F\u52A8\u5B88\u62A4: apm connect --daemon");
3706
+ }
3707
+ function assertConnectNotRunning() {
3708
+ pruneStaleConnectLock();
3709
+ const lock = readConnectLock();
3710
+ if (lock && isProcessAlive(lock.pid) && lock.pid !== process.pid) {
3711
+ console.error(
3712
+ `[apm] \u5DF2\u6709 apm connect \u5728\u8FD0\u884C (pid=${lock.pid}, mode=${lock.mode})\uFF0C\u8BF7\u5148\u505C\u6B62\u540E\u518D\u542F\u52A8`
3713
+ );
3714
+ process.exit(1);
3715
+ }
3716
+ if (!isRunningUnderPm2() && isPm2ConnectOnline()) {
3717
+ console.error(
3718
+ "[apm] \u5DF2\u6709 apm connect \u5B88\u62A4\u8FDB\u7A0B\u5728\u8FD0\u884C\uFF0C\u8BF7\u5148\u6267\u884C apm daemon stop"
3719
+ );
3720
+ process.exit(1);
3687
3721
  }
3688
- const updated = Number.parseInt(match[1], 10);
3689
- log(`lib \u89E3\u538B\u5B8C\u6210: \u8986\u76D6 ${updated} \u4E2A`);
3690
- return updated;
3691
3722
  }
3692
- function springbootOutputIndicatesSuccess(action, combined) {
3693
- const lower = combined.toLowerCase();
3694
- if (action === "health") {
3695
- return combined.includes("\u5065\u5EB7\u68C0\u67E5\u901A\u8FC7");
3723
+ function resolveRuntimePaths() {
3724
+ return {
3725
+ apmScript: resolveApmEntryPath(),
3726
+ nodePath: process.execPath
3727
+ };
3728
+ }
3729
+ function reexecConnectWithResolvedPath(options) {
3730
+ const apmScript = resolveApmEntryPath();
3731
+ const args = [apmScript, "connect"];
3732
+ const server = options.server?.trim();
3733
+ if (server) {
3734
+ args.push("--server", server);
3696
3735
  }
3697
- if (action === "start" || action === "restart") {
3698
- return combined.includes("is starting") || lower.includes("is running");
3736
+ const result = spawnSync2(process.execPath, args, { stdio: "inherit" });
3737
+ if (result.error) {
3738
+ console.error("[apm] \u91CD\u542F connect \u5931\u8D25:", result.error.message);
3739
+ process.exit(1);
3699
3740
  }
3700
- if (action === "stop") {
3701
- return combined.includes("is stopping") || lower.includes("not running") || lower.includes("please check it");
3741
+ process.exit(result.status ?? 0);
3742
+ }
3743
+ async function handleConnectAfterUpdate(options) {
3744
+ console.log("[apm] \u66F4\u65B0\u5B8C\u6210\uFF0C\u6B63\u5728\u4EE5\u65B0\u7248\u672C\u91CD\u65B0\u8FDE\u63A5\u2026");
3745
+ await prepareEcosystem(options.server);
3746
+ if (isRunningUnderPm2()) {
3747
+ runPm2(["reload", PM2_ECOSYSTEM_PATH, "--update-env"], { inherit: true });
3748
+ console.log("[apm] \u5DF2\u901A\u77E5 PM2 \u4F7F\u7528\u65B0\u7248\u672C\u91CD\u65B0\u52A0\u8F7D connect");
3749
+ process.exit(0);
3702
3750
  }
3703
- if (action === "status") {
3704
- return lower.includes("running") || lower.includes("not running");
3751
+ reexecConnectWithResolvedPath(options);
3752
+ }
3753
+ async function resolveBaseUrl(server) {
3754
+ if (server?.trim()) {
3755
+ return server.trim().replace(/\/+$/, "");
3705
3756
  }
3706
- return true;
3757
+ const cfg = await tryReadApmConfig();
3758
+ return cfg?.baseUrl;
3707
3759
  }
3708
- function buildRemoteStatusScript(remoteAppDir, appName) {
3709
- const dir = shellSingleQuote(remoteAppDir);
3710
- const jar = shellSingleQuote(appName);
3711
- return `
3712
- set -e
3713
- cd ${dir}
3714
- appName=${jar}
3715
- appIds=$(ps -ef | grep java | grep "$appName" | awk '{print $2}')
3716
- if [ -z "$appIds" ]; then
3717
- echo -e "\\033[31m Not running \\033[0m"
3718
- else
3719
- echo -e "\\033[32m Running [$appIds] \\033[0m"
3720
- fi
3721
- `.trim();
3722
- }
3723
- function buildRemoteRestartScript(remoteAppDir) {
3724
- const dir = shellSingleQuote(remoteAppDir);
3725
- const javaOpts = shellSingleQuote(SPRINGBOOT_JAVA_OPTS);
3726
- return `
3727
- set -e
3728
- cd ${dir}
3729
- releaseApp=$(ls -t | grep '.jar$' | head -n1)
3730
- lastVersionApp=$(ls -t | grep '.jar$' | head -n2 | tail -n1)
3731
- appName=$lastVersionApp
3732
- appIds=$(ps -ef | grep java | grep "$appName" | awk '{print $2}')
3733
- if [ -z "$appIds" ]; then
3734
- echo "Maybe $appName not running, please check it..."
3735
- else
3736
- echo "The $appName is stopping..."
3737
- echo "$appIds" | xargs kill
3738
- fi
3739
- for i in $(seq 15 -1 1); do
3740
- echo -n "$i "
3741
- sleep 1
3742
- done
3743
- echo 0
3744
- if [ ! -d "backup" ]; then
3745
- mkdir backup
3746
- fi
3747
- for i in $(ls | grep '.jar$' | grep -vFx "$releaseApp"); do
3748
- echo "backup $i"
3749
- mv "$i" backup/
3750
- done
3751
- appName=$releaseApp
3752
- count=$(ps -ef | grep java | grep "$appName" | wc -l)
3753
- if [ "$count" != "0" ]; then
3754
- echo "Maybe $appName is running, please check it..."
3755
- else
3756
- echo "The $appName is starting..."
3757
- nohup java -jar "./$appName" ${javaOpts} > nohup.out 2>&1 &
3758
- fi
3759
- `.trim();
3760
- }
3761
- function normalizeHealthContext(context) {
3762
- let normalized = context.trim() || "/";
3763
- if (!normalized.startsWith("/")) {
3764
- normalized = `/${normalized}`;
3765
- }
3766
- if (!normalized.endsWith("/")) {
3767
- normalized = `${normalized}/`;
3760
+ function writeConnectLaunchFiles(options) {
3761
+ mkdirSync6(APM_CONFIG_DIR, { recursive: true });
3762
+ writeFileSync11(PM2_CONNECT_ENTRY_PATH, CONNECT_ENTRY_SCRIPT, "utf8");
3763
+ const env = {
3764
+ ...collectPm2LaunchEnv()
3765
+ };
3766
+ const baseUrl = options.baseUrl?.trim().replace(/\/+$/, "");
3767
+ if (baseUrl) {
3768
+ env.AI_PM_SERVER = baseUrl;
3768
3769
  }
3769
- return normalized;
3770
- }
3771
- function buildRemoteHealthScript(port, context, timeoutSecs) {
3772
- const normalizedContext = normalizeHealthContext(context);
3773
- const portStr = String(port);
3774
- const timeoutStr = String(timeoutSecs);
3775
- return `
3776
- set -e
3777
- port=${shellSingleQuote(portStr)}
3778
- context=${shellSingleQuote(normalizedContext)}
3779
- timeout=${shellSingleQuote(timeoutStr)}
3780
- check_url="http://127.0.0.1:${portStr}${normalizedContext}"
3781
- echo "\u5065\u5EB7\u68C0\u67E5: \${check_url} (\u8D85\u65F6 \${timeout}s)"
3782
- deadline=$(($(date +%s) + timeout))
3783
- attempt=0
3784
- while [ $(date +%s) -lt $deadline ]; do
3785
- attempt=$((attempt + 1))
3786
- code=$(curl -s -o /dev/null -w "%{http_code}" "$check_url" 2>/dev/null || echo "000")
3787
- code=$(echo "$code" | tail -n1 | tr -d '[:space:]')
3788
- if [ \${#code} -ge 3 ]; then
3789
- status=\${code:0:3}
3790
- if echo "$status" | grep -qE '^[0-9]+$' && [ "$status" -ge 200 ] && [ "$status" -lt 500 ]; then
3791
- echo -e "\\033[32m\u5065\u5EB7\u68C0\u67E5\u901A\u8FC7 (HTTP \${status})\\033[0m"
3792
- exit 0
3793
- fi
3794
- fi
3795
- remaining=$((deadline - $(date +%s)))
3796
- if [ $remaining -lt 0 ]; then
3797
- remaining=0
3798
- fi
3799
- echo "\u7B49\u5F85\u670D\u52A1\u542F\u52A8... \u7B2C \${attempt} \u6B21\uFF0C\u5269\u4F59 \${remaining}s"
3800
- sleep 5
3801
- done
3802
- echo -e "\\033[31m\u5065\u5EB7\u68C0\u67E5\u8D85\u65F6 (\${timeout}s): \${check_url}\\033[0m"
3803
- exit 1
3804
- `.trim();
3770
+ const launch = {
3771
+ nodePath: options.nodePath,
3772
+ apmScript: options.apmScript,
3773
+ connectArgs: options.connectArgs,
3774
+ cwd: options.cwd,
3775
+ env
3776
+ };
3777
+ writeFileSync11(
3778
+ PM2_CONNECT_LAUNCH_PATH,
3779
+ JSON.stringify(launch, null, 2) + "\n",
3780
+ "utf8"
3781
+ );
3805
3782
  }
3806
- async function runRemoteServiceScript(client, script, action) {
3807
- const { exitCode, out, err } = await runRemoteCommand(client, script, {
3808
- check: false,
3809
- label: action === "status" ? "\u8FDC\u7A0B status" : action === "restart" ? "\u8FDC\u7A0B restart" : "\u5065\u5EB7\u68C0\u67E5"
3783
+ function writeEcosystemFile(options) {
3784
+ writeConnectLaunchFiles(options);
3785
+ const ecosystem = buildConnectPm2Ecosystem({
3786
+ entryScript: PM2_CONNECT_ENTRY_PATH,
3787
+ baseUrl: options.baseUrl
3810
3788
  });
3811
- const combined = `${out}
3812
- ${err}`.trim();
3813
- const outputOk = springbootOutputIndicatesSuccess(action, combined);
3814
- if (action === "health") {
3815
- if (exitCode !== 0 || !outputOk) {
3816
- fail(`\u5065\u5EB7\u68C0\u67E5\u5931\u8D25
3817
- \u8F93\u51FA: ${combined || "(\u7A7A)"}`);
3818
- }
3819
- return combined;
3820
- }
3821
- if (exitCode !== 0 && !outputOk) {
3822
- fail(`\u8FDC\u7A0B ${action} \u5931\u8D25
3823
- ${combined}`);
3824
- }
3825
- if (action === "restart" && !outputOk) {
3826
- fail(`\u8FDC\u7A0B restart \u672A\u6210\u529F
3827
- \u8F93\u51FA: ${combined || "(\u7A7A)"}`);
3828
- }
3829
- return combined;
3830
- }
3831
- function stripAnsi(text) {
3832
- return text.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "");
3833
- }
3834
- async function getRunningJar(client, config) {
3835
- const script = buildRemoteStatusScript(
3836
- config.remoteAppDir,
3837
- config.startupJar
3789
+ writeFileSync11(
3790
+ PM2_ECOSYSTEM_PATH,
3791
+ formatConnectPm2EcosystemFile(ecosystem),
3792
+ "utf8"
3838
3793
  );
3839
- const combined = await runRemoteServiceScript(client, script, "status");
3840
- const text = stripAnsi(combined).trim().toLowerCase();
3841
- if (text.includes("not running")) {
3842
- return null;
3843
- }
3844
- if (text.includes("running")) {
3845
- return config.startupJar;
3794
+ if (existsSync13(LEGACY_PM2_ECOSYSTEM_PATH)) {
3795
+ try {
3796
+ unlinkSync2(LEGACY_PM2_ECOSYSTEM_PATH);
3797
+ } catch {
3798
+ }
3846
3799
  }
3847
- return null;
3848
- }
3849
- async function healthCheckService(client, config) {
3850
- log("\u5065\u5EB7\u68C0\u67E5...");
3851
- const script = buildRemoteHealthScript(
3852
- config.healthCheckPort,
3853
- config.healthCheckContext,
3854
- config.healthCheckTimeout
3855
- );
3856
- await runRemoteServiceScript(client, script, "health");
3857
3800
  }
3858
- async function restartRemoteService(client, config) {
3859
- const script = buildRemoteRestartScript(config.remoteAppDir);
3860
- await runRemoteServiceScript(client, script, "restart");
3861
- }
3862
- function listAllLibFilesForArchive(libDir) {
3863
- return readdirSync5(libDir).filter((name) => name.endsWith(".jar")).sort().map((jarName) => ({
3864
- path: path3.join(libDir, jarName),
3865
- arcname: jarName,
3866
- reason: "\u4EC5\u6253\u5305\u5F52\u6863"
3867
- }));
3868
- }
3869
- async function runWisdomBackendDeploy(options) {
3870
- const projectRoot = path3.resolve(options.projectRoot ?? process.cwd());
3871
- const config = options.config;
3872
- const packOnly = Boolean(options.packOnly);
3873
- log(`=== \u81EA\u52A8\u90E8\u7F72: ${config.projectName} ===`);
3874
- log(`\u914D\u7F6E\u6587\u4EF6: ${path3.join(workspaceApmDir(), "apm.config.json")}`);
3875
- log(`\u9879\u76EE\u6839\u76EE\u5F55: ${projectRoot}`);
3876
- log(
3877
- `\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"}${packOnly ? "\uFF1B\u4EC5\u6253\u5305\uFF08\u4E0D\u4E0A\u4F20\u8FDC\u7A0B\uFF09" : ""}`
3878
- );
3879
- runMavenBuild(projectRoot, config.mavenLocalRepo, {
3880
- source: config.mavenLocalRepoSource,
3881
- sourceDetail: config.mavenLocalRepoSourceDetail
3801
+ async function prepareEcosystem(server) {
3802
+ await ensureLoggedConfig();
3803
+ const cfg = await ensureApmConfig();
3804
+ const { apmScript, nodePath } = resolveRuntimePaths();
3805
+ const connectArgs = resolveConnectArgs(server);
3806
+ const baseUrl = await resolveBaseUrl(server);
3807
+ writeEcosystemFile({
3808
+ apmScript,
3809
+ nodePath,
3810
+ connectArgs,
3811
+ baseUrl,
3812
+ cwd: process.cwd()
3882
3813
  });
3883
- if (packOnly) {
3884
- const uploadWithoutRunId = !resolveDeploymentRunIdFromEnv();
3885
- if (config.mode === "full") {
3886
- const mainJar = locateMainJar(projectRoot);
3887
- const archiveZipPath = await createUpdatePackage(
3888
- [
3889
- {
3890
- path: mainJar,
3891
- arcname: path3.basename(mainJar),
3892
- reason: "\u5168\u91CF JAR \u5F52\u6863"
3893
- }
3894
- ],
3895
- `.deploy-archive-${config.projectName}.jar.zip`
3896
- );
3897
- await uploadDeployArtifactZip({
3898
- zipPath: archiveZipPath,
3899
- projectName: config.projectName,
3900
- kind: "backend",
3901
- uploadWithoutRunId
3902
- });
3903
- } else {
3904
- const libDir = locateLibDir(projectRoot);
3905
- const entries = listAllLibFilesForArchive(libDir);
3906
- if (entries.length === 0) {
3907
- fail("lib \u76EE\u5F55\u4E0B\u6CA1\u6709\u53EF\u5F52\u6863\u7684 JAR");
3908
- }
3909
- const zipPath = await createUpdatePackage(entries, config.packageName);
3910
- await uploadDeployArtifactZip({
3911
- zipPath,
3912
- projectName: config.projectName,
3913
- kind: "backend",
3914
- uploadWithoutRunId
3915
- });
3916
- }
3917
- log("\u4EC5\u6253\u5305\u5B8C\u6210\uFF08\u672A\u4E0A\u4F20\u8FDC\u7A0B\u670D\u52A1\u5668\uFF09");
3918
- return;
3919
- }
3920
- const conn = await connectSsh(config);
3921
- try {
3922
- if (config.mode === "full") {
3923
- const mainJar = locateMainJar(projectRoot);
3924
- await uploadFullJar(conn.sftp, mainJar, config);
3925
- const archiveZipPath = await createUpdatePackage(
3926
- [
3927
- {
3928
- path: mainJar,
3929
- arcname: path3.basename(mainJar),
3930
- reason: "\u5168\u91CF JAR \u5F52\u6863"
3931
- }
3932
- ],
3933
- `.deploy-archive-${config.projectName}.jar.zip`
3934
- );
3935
- await uploadDeployArtifactZip({
3936
- zipPath: archiveZipPath,
3937
- projectName: config.projectName,
3938
- kind: "backend"
3939
- });
3940
- log("\u91CD\u542F\u670D\u52A1...");
3941
- await restartRemoteService(conn.client, config);
3942
- await healthCheckService(conn.client, config);
3943
- log("\u90E8\u7F72\u5B8C\u6210");
3944
- return;
3945
- }
3946
- const libDir = locateLibDir(projectRoot);
3947
- let manifest = loadManifest3();
3948
- const remoteLibStats = await getRemoteFileStats(
3949
- conn.sftp,
3950
- config.remoteLibDir
3951
- );
3952
- log("\u6536\u96C6 JAR \u66F4\u65B0...");
3953
- const libUploadEntries = listLibFilesToUpload(
3954
- libDir,
3955
- remoteLibStats,
3956
- projectRoot,
3957
- manifest
3958
- );
3959
- let updated = 0;
3960
- if (libUploadEntries.length > 0) {
3961
- const zipPath = await createUpdatePackage(
3962
- libUploadEntries,
3963
- config.packageName
3964
- );
3965
- const remoteZipPath = await uploadUpdatePackage(
3966
- conn.sftp,
3967
- zipPath,
3968
- config
3969
- );
3970
- await uploadDeployArtifactZip({
3971
- zipPath,
3972
- projectName: config.projectName,
3973
- kind: "backend"
3974
- });
3975
- log("\u8FDC\u7A0B\u89E3\u538B lib \u76EE\u5F55\uFF08\u4EC5\u8986\u76D6\u5DF2\u6709 JAR\uFF09...");
3976
- updated = await extractUpdatePackageOnRemote(
3977
- conn.client,
3978
- config,
3979
- remoteZipPath
3980
- );
3981
- } else {
3982
- log("\u65E0 JAR \u9700\u8981\u66F4\u65B0\uFF0C\u8DF3\u8FC7\u66F4\u65B0\u5305\u4E0A\u4F20");
3983
- }
3984
- const runningJar = await getRunningJar(conn.client, config);
3985
- let needRestart = updated > 0;
3986
- if (!needRestart && !runningJar) {
3987
- log("\u670D\u52A1\u672A\u8FD0\u884C\uFF0C\u9700\u8981\u542F\u52A8");
3988
- needRestart = true;
3989
- } else if (!needRestart) {
3990
- log("\u6CA1\u6709\u6587\u4EF6\u9700\u8981\u66F4\u65B0\uFF0C\u8DF3\u8FC7\u91CD\u542F");
3991
- }
3992
- if (needRestart) {
3993
- log("\u91CD\u542F\u670D\u52A1...");
3994
- await restartRemoteService(conn.client, config);
3995
- }
3996
- await healthCheckService(conn.client, config);
3997
- if (libUploadEntries.length > 0) {
3998
- manifest = updateManifestEntries(manifest, libUploadEntries, projectRoot);
3999
- saveManifest3(manifest);
4000
- }
4001
- } finally {
4002
- await closeSsh(conn);
4003
- }
4004
- log("\u90E8\u7F72\u5B8C\u6210");
3814
+ return cfg;
4005
3815
  }
4006
-
4007
- // src/commands/deploy/internal/deploy-shell-env.ts
4008
- import { spawnSync as spawnSync4 } from "node:child_process";
4009
- import { dirname as dirname4 } from "node:path";
4010
-
4011
- // src/commands/daemon.ts
4012
- import { spawnSync as spawnSync3 } from "child_process";
4013
- import { setTimeout as delay } from "node:timers/promises";
4014
- import { existsSync as existsSync14, mkdirSync as mkdirSync7, unlinkSync as unlinkSync2, writeFileSync as writeFileSync12 } from "fs";
4015
- import { join as join15 } from "path";
4016
-
4017
- // src/commands/connect-lock.ts
4018
- import {
4019
- existsSync as existsSync13,
4020
- mkdirSync as mkdirSync6,
4021
- readFileSync as readFileSync12,
4022
- unlinkSync,
4023
- writeFileSync as writeFileSync11
4024
- } from "fs";
4025
- import { join as join14 } from "path";
4026
- var CONNECT_LOCK_PATH = join14(APM_CONFIG_DIR, "connect.lock");
4027
- function isProcessAlive(pid) {
4028
- if (!Number.isInteger(pid) || pid <= 0) return false;
4029
- try {
4030
- process.kill(pid, 0);
4031
- return true;
4032
- } catch {
4033
- return false;
3816
+ async function runDaemonStart(options) {
3817
+ assertConnectNotRunning();
3818
+ await runUpdate();
3819
+ ensureGlobalPm2();
3820
+ const cfg = await prepareEcosystem(options.server);
3821
+ removePm2ConnectProcesses();
3822
+ runPm2(["start", PM2_ECOSYSTEM_PATH, "--update-env"], { inherit: true });
3823
+ await delay2(1e3);
3824
+ if (!isPm2ConnectOnline()) {
3825
+ console.error(
3826
+ `[apm] ${PM2_APP_NAME} \u542F\u52A8\u540E\u672A\u5904\u4E8E online \u72B6\u6001\uFF0C\u8BF7\u6267\u884C: apm daemon logs -n 200`
3827
+ );
4034
3828
  }
4035
- }
4036
- function sleepSync(ms) {
4037
- const deadline = Date.now() + ms;
4038
- while (Date.now() < deadline) {
3829
+ console.log(
3830
+ `[apm] ${PM2_APP_NAME} \u5DF2\u7531 PM2 \u542F\u52A8\uFF08server=${options.server?.trim() || cfg.baseUrl}\uFF09`
3831
+ );
3832
+ if (options.follow) {
3833
+ console.log(
3834
+ "[apm] \u6B63\u5728\u8DDF\u8E2A\u65E5\u5FD7\uFF08Ctrl+C \u4EC5\u9000\u51FA\u65E5\u5FD7\u67E5\u770B\uFF0C\u5B88\u62A4\u8FDB\u7A0B\u7EE7\u7EED\u8FD0\u884C\uFF09"
3835
+ );
3836
+ runDaemonLogs({ follow: true });
3837
+ return;
4039
3838
  }
3839
+ console.log("[apm] \u67E5\u770B\u72B6\u6001: apm daemon status");
3840
+ console.log("[apm] \u67E5\u770B\u65E5\u5FD7: apm daemon logs -f");
3841
+ console.log("[apm] \u505C\u6B62: apm daemon stop");
4040
3842
  }
4041
- function waitForPm2LockHandoff(timeoutMs = 5e3) {
4042
- const deadline = Date.now() + timeoutMs;
4043
- while (Date.now() < deadline) {
4044
- pruneStaleConnectLock();
4045
- const lock = readConnectLock();
4046
- if (!lock || lock.mode !== "pm2" || lock.pid === process.pid || !isProcessAlive(lock.pid)) {
4047
- return;
4048
- }
4049
- sleepSync(100);
3843
+ async function runDaemonStop() {
3844
+ pruneStaleConnectLock();
3845
+ if (!isPm2ConnectOnline()) {
3846
+ killConnectLockProcessIfAlive();
3847
+ forceReleaseConnectLock();
3848
+ console.log(`[apm] ${PM2_APP_NAME} \u672A\u5728\u8FD0\u884C`);
3849
+ return;
4050
3850
  }
4051
- }
4052
- function readConnectLock() {
4053
- if (!existsSync13(CONNECT_LOCK_PATH)) return null;
4054
- try {
4055
- const raw = readFileSync12(CONNECT_LOCK_PATH, "utf8");
4056
- const parsed = JSON.parse(raw);
4057
- if (typeof parsed.pid !== "number" || parsed.mode !== "foreground" && parsed.mode !== "pm2" || typeof parsed.startedAt !== "string") {
4058
- return null;
4059
- }
4060
- return parsed;
4061
- } catch {
4062
- return null;
3851
+ runPm2(["stop", resolvePm2ConnectName(findPm2ConnectProcess())], {
3852
+ inherit: true
3853
+ });
3854
+ for (let i = 0; i < 10; i += 1) {
3855
+ if (!isPm2ConnectOnline()) break;
3856
+ await delay2(500);
3857
+ }
3858
+ if (isPm2ConnectOnline()) {
3859
+ console.log(`[apm] \u4F18\u96C5\u505C\u6B62\u8D85\u65F6\uFF0C\u5F3A\u5236\u79FB\u9664 ${PM2_APP_NAME}\u2026`);
3860
+ runPm2(["delete", resolvePm2ConnectName(findPm2ConnectProcess())], {
3861
+ inherit: true
3862
+ });
4063
3863
  }
3864
+ killConnectLockProcessIfAlive();
3865
+ forceReleaseConnectLock();
3866
+ console.log(`[apm] ${PM2_APP_NAME} \u5DF2\u505C\u6B62`);
4064
3867
  }
4065
- function pruneStaleConnectLock() {
3868
+ function killConnectLockProcessIfAlive() {
3869
+ pruneStaleConnectLock();
4066
3870
  const lock = readConnectLock();
4067
- if (!lock) return;
4068
- if (isProcessAlive(lock.pid)) return;
3871
+ if (!lock || !isProcessAlive(lock.pid)) return;
4069
3872
  try {
4070
- unlinkSync(CONNECT_LOCK_PATH);
3873
+ process.kill(lock.pid, "SIGTERM");
4071
3874
  } catch {
3875
+ forceReleaseConnectLock();
3876
+ return;
4072
3877
  }
4073
- }
4074
- function acquireConnectLock(mode) {
4075
- pruneStaleConnectLock();
4076
- const existing = readConnectLock();
4077
- if (existing && isProcessAlive(existing.pid)) {
4078
- if (existing.pid === process.pid) {
4079
- return;
4080
- }
4081
- if (mode === "pm2" && existing.mode === "pm2") {
4082
- forceReleaseConnectLock();
4083
- } else {
4084
- console.error(
4085
- `[apm] \u5DF2\u6709 apm connect \u5728\u8FD0\u884C (pid=${existing.pid}, mode=${existing.mode})`
4086
- );
4087
- process.exit(1);
3878
+ if (isProcessAlive(lock.pid)) {
3879
+ try {
3880
+ process.kill(lock.pid, "SIGKILL");
3881
+ } catch {
4088
3882
  }
4089
3883
  }
4090
- mkdirSync6(APM_CONFIG_DIR, { recursive: true });
4091
- const lock = {
4092
- pid: process.pid,
4093
- mode,
4094
- startedAt: (/* @__PURE__ */ new Date()).toISOString()
4095
- };
4096
- writeFileSync11(
4097
- CONNECT_LOCK_PATH,
4098
- JSON.stringify(lock, null, 2) + "\n",
4099
- "utf8"
4100
- );
3884
+ forceReleaseConnectLock();
4101
3885
  }
4102
- function releaseConnectLock() {
3886
+ async function runDaemonRestart(options) {
3887
+ pruneStaleConnectLock();
4103
3888
  const lock = readConnectLock();
4104
- if (lock?.pid !== process.pid) return;
4105
- try {
4106
- unlinkSync(CONNECT_LOCK_PATH);
4107
- } catch {
3889
+ if (lock && isProcessAlive(lock.pid) && !isPm2ConnectOnline()) {
3890
+ console.error(
3891
+ `[apm] \u5DF2\u6709\u524D\u53F0 apm connect \u5728\u8FD0\u884C (pid=${lock.pid})\uFF0C\u8BF7\u5148\u505C\u6B62\u540E\u518D\u542F\u52A8\u5B88\u62A4\u8FDB\u7A0B`
3892
+ );
3893
+ process.exit(1);
4108
3894
  }
3895
+ await runUpdate();
3896
+ await prepareEcosystem(options.server);
3897
+ removePm2ConnectProcesses();
3898
+ runPm2(["start", PM2_ECOSYSTEM_PATH, "--update-env"], { inherit: true });
3899
+ console.log(`[apm] ${PM2_APP_NAME} \u5DF2\u91CD\u542F`);
4109
3900
  }
4110
- function forceReleaseConnectLock() {
4111
- try {
4112
- if (existsSync13(CONNECT_LOCK_PATH)) {
4113
- unlinkSync(CONNECT_LOCK_PATH);
4114
- }
4115
- } catch {
3901
+ async function runDaemonDelete() {
3902
+ ensureGlobalPm2();
3903
+ const app = findPm2ConnectProcess();
3904
+ if (!app?.name) {
3905
+ console.log(`[apm] ${PM2_APP_NAME} \u672A\u5728 PM2 \u4E2D\u6CE8\u518C`);
3906
+ return;
4116
3907
  }
3908
+ runPm2(["delete", app.name], { inherit: true });
3909
+ forceReleaseConnectLock();
3910
+ console.log(`[apm] ${app.name} \u5DF2\u4ECE PM2 \u79FB\u9664`);
4117
3911
  }
4118
-
4119
- // src/commands/daemon.ts
4120
- var PM2_APP_NAME = "apm-connect";
4121
- var PM2_ECOSYSTEM_PATH = join15(
4122
- APM_CONFIG_DIR,
4123
- "connect.ecosystem.config.cjs"
4124
- );
4125
- var PM2_CONNECT_ENTRY_PATH = join15(APM_CONFIG_DIR, "connect.entry.cjs");
4126
- var PM2_CONNECT_LAUNCH_PATH = join15(
4127
- APM_CONFIG_DIR,
4128
- "connect.launch.json"
4129
- );
4130
- var LEGACY_PM2_ECOSYSTEM_PATH = join15(
4131
- APM_CONFIG_DIR,
4132
- "connect.ecosystem.cjs"
4133
- );
4134
- var LEGACY_PM2_APP_NAMES = ["connect.ecosystem"];
4135
- var CONNECT_ENTRY_SCRIPT = `'use strict';
4136
- const { spawnSync } = require('node:child_process');
4137
- const { readFileSync } = require('node:fs');
4138
- const { join } = require('node:path');
4139
-
4140
- const launchConfigPath = join(__dirname, 'connect.launch.json');
4141
- let config;
4142
- try {
4143
- config = JSON.parse(readFileSync(launchConfigPath, 'utf8'));
4144
- } catch (err) {
4145
- console.error('[apm] \u65E0\u6CD5\u8BFB\u53D6 connect.launch.json:', err instanceof Error ? err.message : err);
4146
- process.exit(1);
3912
+ async function runDaemonStatus() {
3913
+ ensureGlobalPm2();
3914
+ const app = findPm2ConnectProcess();
3915
+ if (!app?.name) {
3916
+ printConnectNotRunningHint();
3917
+ return;
3918
+ }
3919
+ runPm2(["describe", app.name], { inherit: true });
4147
3920
  }
4148
-
4149
- const nodeArgs = [config.apmScript, ...(config.connectArgs || [])];
4150
- const childEnv = {
4151
- ...process.env,
4152
- ...(config.env || {}),
4153
- APM_CONNECT_UNDER_PM2: '1',
4154
- };
4155
-
4156
- const result = spawnSync(config.nodePath, nodeArgs, {
4157
- cwd: config.cwd || undefined,
4158
- env: childEnv,
4159
- stdio: 'inherit',
4160
- windowsHide: true,
4161
- });
4162
-
4163
- if (result.error) {
4164
- console.error('[apm] connect \u542F\u52A8\u5931\u8D25:', result.error.message);
4165
- process.exit(1);
3921
+ async function runDaemonLogs(options) {
3922
+ ensureGlobalPm2();
3923
+ const app = findPm2ConnectProcess();
3924
+ if (!app?.name) {
3925
+ printConnectNotRunningHint();
3926
+ return;
3927
+ }
3928
+ const args = ["logs", app.name, "--lines", String(options.lines ?? 100)];
3929
+ if (!options.follow) {
3930
+ args.push("--nostream");
3931
+ }
3932
+ runPm2(args, { inherit: true });
4166
3933
  }
4167
- process.exit(result.status ?? 0);
4168
- `;
4169
- function resolveConnectArgs(server) {
4170
- const args = ["connect"];
4171
- const trimmed = server?.trim();
4172
- if (trimmed) {
4173
- args.push("--server", trimmed.replace(/\/+$/, ""));
3934
+
3935
+ // src/commands/deploy/internal/deploy-shell-env.ts
3936
+ var PATH_SEP = process.platform === "win32" ? ";" : ":";
3937
+ var useNpmShell3 = process.platform === "win32";
3938
+ function resolveNpmGlobalBin2() {
3939
+ const result = spawnSync3(useNpmShell3 ? "npm.cmd" : "npm", ["bin", "-g"], {
3940
+ encoding: "utf8",
3941
+ shell: useNpmShell3,
3942
+ stdio: ["ignore", "pipe", "pipe"]
3943
+ });
3944
+ if (result.status !== 0) {
3945
+ return null;
4174
3946
  }
4175
- return args;
3947
+ const bin = result.stdout?.toString().trim();
3948
+ return bin || null;
4176
3949
  }
4177
- var PM2_LAUNCH_ENV_KEYS = [
4178
- "PATH",
4179
- "HOME",
4180
- "USER",
4181
- "SHELL",
4182
- "LANG",
4183
- "LC_ALL",
4184
- "LC_CTYPE",
4185
- "NVM_DIR",
4186
- "NVM_BIN",
4187
- "NVM_INC",
4188
- "FNM_DIR",
4189
- "VOLTA_HOME",
4190
- "PNPM_HOME",
4191
- "npm_config_prefix",
4192
- "NODE_HOME"
4193
- ];
4194
- function collectPm2LaunchEnv() {
4195
- const env = {};
4196
- for (const key of PM2_LAUNCH_ENV_KEYS) {
4197
- const value = process.env[key]?.trim();
4198
- if (value) {
4199
- env[key] = value;
4200
- }
3950
+ function prependPath(pathValue, segment) {
3951
+ if (!segment) {
3952
+ return pathValue ?? "";
3953
+ }
3954
+ if (!pathValue) {
3955
+ return segment;
3956
+ }
3957
+ const segments = pathValue.split(PATH_SEP);
3958
+ if (segments.includes(segment)) {
3959
+ return pathValue;
4201
3960
  }
4202
- return env;
3961
+ return `${segment}${PATH_SEP}${pathValue}`;
4203
3962
  }
4204
- function buildConnectPm2Ecosystem(options) {
4205
- const env = {};
4206
- const baseUrl = options.baseUrl?.trim().replace(/\/+$/, "");
4207
- if (baseUrl) {
4208
- env.AI_PM_SERVER = baseUrl;
3963
+ function buildDeployShellEnv(extra) {
3964
+ const env = { ...process.env, ...extra };
3965
+ const pathSegments = [dirname4(process.execPath)];
3966
+ const globalBin = resolveNpmGlobalBin2();
3967
+ if (globalBin) {
3968
+ pathSegments.push(globalBin);
4209
3969
  }
4210
- const app = {
4211
- name: PM2_APP_NAME,
4212
- script: options.entryScript,
4213
- cwd: APM_CONFIG_DIR,
4214
- autorestart: true,
4215
- min_uptime: "10s",
4216
- max_restarts: 10,
4217
- restart_delay: 3e3,
4218
- exp_backoff_restart_delay: 1e3,
4219
- max_memory_restart: "1G",
4220
- kill_timeout: 5e3,
4221
- shutdown_with_message: true,
4222
- instances: 1,
4223
- exec_mode: "fork",
4224
- env
4225
- };
4226
- if (process.platform === "win32") {
4227
- app.windowsHide = true;
3970
+ try {
3971
+ resolveApmEntryPath();
3972
+ } catch {
4228
3973
  }
4229
- return {
4230
- apps: [app]
4231
- };
3974
+ for (const segment of pathSegments) {
3975
+ env.PATH = prependPath(env.PATH, segment);
3976
+ }
3977
+ return env;
4232
3978
  }
4233
- function formatConnectPm2EcosystemFile(ecosystem) {
4234
- return `module.exports = ${JSON.stringify(ecosystem, null, 2)};
4235
- `;
3979
+
3980
+ // src/commands/deploy/deploy-debug-log.ts
3981
+ var LOG_PREFIX = "[apm:deploy-debug]";
3982
+ var SENSITIVE_ENV_KEY = /(password|secret|token|api[_-]?key|authorization|credential|private|passwd)/i;
3983
+ var PATH_SEP2 = process.platform === "win32" ? ";" : ":";
3984
+ function logLine(message) {
3985
+ console.error(`${LOG_PREFIX} ${message}`);
3986
+ }
3987
+ function pathEntries(value) {
3988
+ if (!value?.trim()) return [];
3989
+ return value.split(PATH_SEP2).filter(Boolean);
3990
+ }
3991
+ function summarizePath(value, maxEntries = 12) {
3992
+ const entries = pathEntries(value);
3993
+ if (entries.length === 0) return "(empty)";
3994
+ const head = entries.slice(0, maxEntries).join(PATH_SEP2);
3995
+ if (entries.length <= maxEntries) return head;
3996
+ return `${head}${PATH_SEP2}\u2026 (+${entries.length - maxEntries} more)`;
3997
+ }
3998
+ function pickEnvKeys(env, keys) {
3999
+ const out = {};
4000
+ for (const key of keys) {
4001
+ const value = env[key];
4002
+ if (value !== void 0 && value !== "") {
4003
+ out[key] = value;
4004
+ }
4005
+ }
4006
+ return out;
4236
4007
  }
4237
- var useNpmShell2 = process.platform === "win32";
4238
- function runNpm2(args, options = {}) {
4239
- return spawnSync3(useNpmShell2 ? "npm.cmd" : "npm", args, {
4240
- ...options,
4241
- shell: useNpmShell2
4242
- });
4008
+ function collectInterestingEnv(env) {
4009
+ const keys = /* @__PURE__ */ new Set([
4010
+ "PATH",
4011
+ "HOME",
4012
+ "USER",
4013
+ "LOGNAME",
4014
+ "SHELL",
4015
+ "LANG",
4016
+ "LC_ALL",
4017
+ "NODE_ENV",
4018
+ "npm_config_registry",
4019
+ "NPM_CONFIG_REGISTRY",
4020
+ "npm_config_prefix",
4021
+ "NPM_CONFIG_PREFIX",
4022
+ APM_DEPLOYMENT_RUN_ID_ENV
4023
+ ]);
4024
+ for (const key of Object.keys(env)) {
4025
+ if (/^npm_config_/i.test(key) || /^NPM_CONFIG_/i.test(key)) {
4026
+ keys.add(key);
4027
+ }
4028
+ if (/^APM_/i.test(key) && !SENSITIVE_ENV_KEY.test(key)) {
4029
+ keys.add(key);
4030
+ }
4031
+ }
4032
+ const picked = {};
4033
+ for (const key of [...keys].sort()) {
4034
+ const value = env[key];
4035
+ if (value === void 0 || value === "") continue;
4036
+ picked[key] = SENSITIVE_ENV_KEY.test(key) ? "***" : value;
4037
+ }
4038
+ return picked;
4243
4039
  }
4244
- function resolveNpmGlobalBin() {
4245
- const binResult = runNpm2(["bin", "-g"], {
4246
- encoding: "utf8",
4247
- stdio: ["ignore", "pipe", "pipe"]
4248
- });
4249
- if (binResult.status !== 0) return null;
4250
- return binResult.stdout?.toString().trim() || null;
4040
+ function describePathDelta(before, after) {
4041
+ const beforeSet = new Set(pathEntries(before));
4042
+ const added = pathEntries(after).filter((entry) => !beforeSet.has(entry));
4043
+ if (added.length === 0) return "buildDeployShellEnv \u672A prepend \u65B0 PATH \u6BB5";
4044
+ return `prepend: ${added.join(PATH_SEP2)}`;
4251
4045
  }
4252
- function resolvePm2FromNpmRoot() {
4253
- const rootResult = runNpm2(["root", "-g"], {
4046
+ function probeCommand(label, command, cwd, env) {
4047
+ const result = spawnSync4(command, {
4048
+ cwd,
4049
+ shell: true,
4050
+ env,
4254
4051
  encoding: "utf8",
4255
4052
  stdio: ["ignore", "pipe", "pipe"]
4256
4053
  });
4257
- if (rootResult.status !== 0) return null;
4258
- const root = rootResult.stdout?.toString().trim();
4259
- if (!root) return null;
4260
- for (const rel of ["pm2/bin/pm2", "pm2/bin/pm2.js"]) {
4261
- const candidate = join15(root, ...rel.split("/"));
4262
- if (existsSync14(candidate)) {
4263
- return candidate;
4264
- }
4265
- }
4266
- return null;
4054
+ const stdout = String(result.stdout ?? "").trim();
4055
+ const stderr = String(result.stderr ?? "").trim();
4056
+ const detail = stdout || stderr || "(no output)";
4057
+ logLine(
4058
+ `probe ${label}: exit=${result.status ?? "null"} output=${detail.slice(
4059
+ 0,
4060
+ 500
4061
+ )}`
4062
+ );
4267
4063
  }
4268
- function isPm2NodeScript(path13) {
4269
- return path13.endsWith(".js") || /[\\/]pm2[\\/]bin[\\/]pm2$/.test(path13);
4064
+ function probeShellTooling(cwd, env) {
4065
+ logLine("--- shell \u5DE5\u5177\u63A2\u6D4B\uFF08\u4E0E runDeployShellCommand \u76F8\u540C env/cwd\uFF09---");
4066
+ probeCommand("node -v", "node -v", cwd, env);
4067
+ probeCommand("npm -v", "npm -v", cwd, env);
4068
+ if (process.platform === "win32") {
4069
+ probeCommand("where node", "where node", cwd, env);
4070
+ probeCommand("where npm", "where npm", cwd, env);
4071
+ } else {
4072
+ probeCommand("which node", "which node", cwd, env);
4073
+ probeCommand("which npm", "which npm", cwd, env);
4074
+ }
4075
+ }
4076
+ function logExecuteDeployContext(input) {
4077
+ const trigger = input.trigger ?? "cli";
4078
+ const deploymentRunId = process.env[APM_DEPLOYMENT_RUN_ID_ENV]?.trim() || null;
4079
+ const shellEnv = buildDeployShellEnv();
4080
+ const npmGlobalBin = resolveNpmGlobalBin2();
4081
+ const packageJsonPath = join16(input.cwd, "package.json");
4082
+ logLine("========== executeDeploy \u4E0A\u4E0B\u6587 ==========");
4083
+ logLine(`trigger=${trigger} deployEnv=${input.env}`);
4084
+ logLine(`cwd(input)=${input.cwdInput}`);
4085
+ logLine(`cwd(resolved)=${input.cwd}`);
4086
+ logLine(`process.cwd()=${input.processCwd}`);
4087
+ logLine(
4088
+ `cwdMatchProcess=${input.cwd === input.processCwd ? "yes" : "no"} (connect \u901A\u5E38\u4E3A no)`
4089
+ );
4090
+ logLine(
4091
+ `apm.config=${input.apmConfigPath} exists=${existsSync14(
4092
+ input.apmConfigPath
4093
+ )}`
4094
+ );
4095
+ logLine(
4096
+ `package.json=${packageJsonPath} exists=${existsSync14(packageJsonPath)}`
4097
+ );
4098
+ logLine(
4099
+ `flags captureOutput=${input.captureOutput} packOnly=${Boolean(
4100
+ input.packOnly
4101
+ )} archiveDeployArtifact=${Boolean(input.archiveDeployArtifact)}`
4102
+ );
4103
+ logLine(`configPathOption=${input.configPathOption ?? "(default)"}`);
4104
+ logLine(`${APM_DEPLOYMENT_RUN_ID_ENV}=${deploymentRunId ?? "(unset)"}`);
4105
+ logLine(
4106
+ `process execPath=${process.execPath} pid=${process.pid} platform=${process.platform} arch=${process.arch}`
4107
+ );
4108
+ logLine(`process argv=${process.argv.join(" ")}`);
4109
+ logLine(`npmGlobalBin=${npmGlobalBin ?? "(resolve failed)"}`);
4110
+ logLine(`nodeDir=${dirname5(process.execPath)}`);
4111
+ logLine("--- process.env\uFF08\u8282\u9009\uFF09---");
4112
+ for (const [key, value] of Object.entries(
4113
+ collectInterestingEnv(process.env)
4114
+ )) {
4115
+ logLine(` ${key}=${value}`);
4116
+ }
4117
+ logLine(`process.env.PATH=${summarizePath(process.env.PATH)}`);
4118
+ logLine("--- buildDeployShellEnv()\uFF08\u5B50\u8FDB\u7A0B\u5B9E\u9645 env\uFF09---");
4119
+ logLine(describePathDelta(process.env.PATH, shellEnv.PATH));
4120
+ logLine(`shellEnv.PATH=${summarizePath(shellEnv.PATH)}`);
4121
+ for (const [key, value] of Object.entries(
4122
+ pickEnvKeys(shellEnv, [APM_DEPLOYMENT_RUN_ID_ENV, "NODE_ENV"])
4123
+ )) {
4124
+ logLine(` shellEnv.${key}=${value}`);
4125
+ }
4126
+ probeShellTooling(input.cwd, shellEnv);
4127
+ logLine("==========================================");
4128
+ }
4129
+ function logRunDeployShellContext(input) {
4130
+ const shellEnv = buildDeployShellEnv();
4131
+ logLine("---------- runDeployShellCommand ----------");
4132
+ logLine(`command=${input.command}`);
4133
+ logLine(`cwd=${input.cwd}`);
4134
+ logLine(`captureOutput=${input.captureOutput}`);
4135
+ logLine(
4136
+ `stdio=${input.captureOutput ? "stdin=inherit stdout=pipe stderr=pipe" : "inherit"}`
4137
+ );
4138
+ logLine(`shell=true maxBuffer=${50 * 1024 * 1024}`);
4139
+ logLine(`shellEnv.PATH(head)=${summarizePath(shellEnv.PATH, 8)}`);
4140
+ logLine("--------------------------------------------");
4270
4141
  }
4271
- function buildPm2SpawnTarget(binPath) {
4272
- if (isPm2NodeScript(binPath)) {
4273
- return {
4274
- command: process.execPath,
4275
- prefixArgs: [binPath],
4276
- displayPath: binPath
4277
- };
4278
- }
4279
- return {
4280
- command: binPath,
4281
- prefixArgs: [],
4282
- displayPath: binPath
4283
- };
4142
+ function logRunDeployShellResult(input) {
4143
+ logLine(
4144
+ `runDeployShellCommand done command=${input.command} exit=${input.exitCode ?? "null"} signal=${input.signal ?? "null"} capturedBytes=${input.outputBytes}${input.errorMessage ? ` error=${input.errorMessage}` : ""}`
4145
+ );
4284
4146
  }
4285
- function spawnPm2Target(target, args, options = {}) {
4286
- const useShell = useNpmShell2 && target.prefixArgs.length === 0;
4287
- return spawnSync3(target.command, [...target.prefixArgs, ...args], {
4147
+ function logWisdomFrontendDeployContext(input) {
4148
+ logLine("--- wisdom Vue \u524D\u7AEF ---");
4149
+ logLine(
4150
+ `buildKey=build:${input.env} buildCmd=${input.buildCmd ?? "(missing)"}`
4151
+ );
4152
+ logLine(`distDir=${input.distDir} exists=${existsSync14(input.distDir)}`);
4153
+ logLine(
4154
+ `packOnly=${input.packOnly} archiveDeployArtifact=${input.archiveDeployArtifact}`
4155
+ );
4156
+ }
4157
+
4158
+ // src/commands/deploy/internal/wisdom-deploy.ts
4159
+ import { existsSync as existsSync16, readFileSync as readFileSync13, statSync as statSync7 } from "node:fs";
4160
+ import path4 from "node:path";
4161
+
4162
+ // src/commands/deploy/deploy-shell-run.ts
4163
+ import { spawnSync as spawnSync5 } from "node:child_process";
4164
+ function runDeployShellCommand(command, cwd, captureOutput) {
4165
+ logRunDeployShellContext({ command, cwd, captureOutput });
4166
+ const shellEnv = buildDeployShellEnv();
4167
+ const result = spawnSync5(command, {
4168
+ cwd,
4169
+ shell: true,
4170
+ env: shellEnv,
4288
4171
  encoding: "utf8",
4289
- env: process.env,
4290
- shell: useShell,
4291
- windowsHide: useNpmShell2,
4292
- ...options
4172
+ maxBuffer: DEPLOY_SPAWN_MAX_BUFFER_BYTES,
4173
+ stdio: captureOutput ? ["inherit", "pipe", "pipe"] : "inherit"
4293
4174
  });
4294
- }
4295
- function spawnPm2At(binPath, args, options = {}) {
4296
- return spawnPm2Target(buildPm2SpawnTarget(binPath), args, options);
4297
- }
4298
- function verifyPm2Bin(pm2Bin) {
4299
- if (!pm2Bin.trim() || !existsSync14(pm2Bin)) {
4300
- return false;
4301
- }
4302
- const result = spawnPm2At(pm2Bin, ["--version"], {
4303
- stdio: ["ignore", "pipe", "pipe"]
4175
+ const stdout = captureOutput ? String(result.stdout ?? "") : "";
4176
+ const stderr = captureOutput ? String(result.stderr ?? "") : "";
4177
+ const output = [stdout, stderr].filter(Boolean).join("\n");
4178
+ logRunDeployShellResult({
4179
+ command,
4180
+ exitCode: result.status,
4181
+ signal: result.signal,
4182
+ errorMessage: result.error?.message,
4183
+ outputBytes: Buffer.byteLength(output, "utf8")
4304
4184
  });
4305
- return !result.error && result.status === 0;
4185
+ if (captureOutput) {
4186
+ if (stdout) process.stdout.write(stdout);
4187
+ if (stderr) process.stderr.write(stderr);
4188
+ }
4189
+ if (result.error) {
4190
+ throw new DeployExecutionError(
4191
+ `\u90E8\u7F72\u547D\u4EE4\u6267\u884C\u5931\u8D25: ${result.error.message}`,
4192
+ 1,
4193
+ output
4194
+ );
4195
+ }
4196
+ if (result.status !== 0) {
4197
+ throw new DeployExecutionError(
4198
+ `\u90E8\u7F72\u547D\u4EE4\u9000\u51FA\u7801 ${result.status ?? "unknown"}: ${command}`,
4199
+ result.status ?? 1,
4200
+ output
4201
+ );
4202
+ }
4203
+ return output;
4306
4204
  }
4307
- function collectPm2Candidates() {
4308
- const candidates = [];
4309
- const globalBin = resolveNpmGlobalBin();
4310
- if (globalBin) {
4311
- if (useNpmShell2) {
4312
- candidates.push(join15(globalBin, "pm2.cmd"));
4313
- candidates.push(join15(globalBin, "pm2"));
4205
+
4206
+ // src/commands/deploy/internal/wisdom-backend-deploy.ts
4207
+ import {
4208
+ existsSync as existsSync15,
4209
+ mkdirSync as mkdirSync7,
4210
+ readdirSync as readdirSync5,
4211
+ readFileSync as readFileSync12,
4212
+ statSync as statSync6,
4213
+ writeFileSync as writeFileSync12
4214
+ } from "node:fs";
4215
+ import { spawnSync as spawnSync6 } from "node:child_process";
4216
+ import path3 from "node:path";
4217
+ import { Client as Client2 } from "ssh2";
4218
+ import JSZip2 from "jszip";
4219
+ import SftpClient2 from "ssh2-sftp-client";
4220
+
4221
+ // src/commands/deploy/internal/wisdom-sftp.ts
4222
+ import { readdir as readdir2, readFile as readFile3, unlink, writeFile } from "node:fs/promises";
4223
+ import path2 from "node:path";
4224
+ import JSZip from "jszip";
4225
+ import SftpClient from "ssh2-sftp-client";
4226
+ async function addDirToZip(dir, zipFolder) {
4227
+ const entries = await readdir2(dir, { withFileTypes: true });
4228
+ for (const entry of entries) {
4229
+ const fullPath = path2.join(dir, entry.name);
4230
+ if (entry.isDirectory()) {
4231
+ const folder = zipFolder.folder(entry.name);
4232
+ if (folder) {
4233
+ await addDirToZip(fullPath, folder);
4234
+ }
4314
4235
  } else {
4315
- candidates.push(join15(globalBin, "pm2"));
4236
+ const content = await readFile3(fullPath);
4237
+ zipFolder.file(entry.name, content);
4316
4238
  }
4317
4239
  }
4318
- const fromRoot = resolvePm2FromNpmRoot();
4319
- if (fromRoot) {
4320
- candidates.push(fromRoot);
4321
- }
4322
- const whichResult = spawnSync3(useNpmShell2 ? "where" : "which", ["pm2"], {
4323
- encoding: "utf8",
4324
- shell: useNpmShell2,
4325
- env: process.env,
4326
- stdio: ["ignore", "pipe", "pipe"]
4240
+ }
4241
+ async function zipDirectory(distDir, zipPath) {
4242
+ console.error(`\u538B\u7F29\u76EE\u5F55: ${distDir}`);
4243
+ const zip = new JSZip();
4244
+ await addDirToZip(distDir, zip);
4245
+ const content = await zip.generateAsync({
4246
+ type: "nodebuffer",
4247
+ compression: "DEFLATE",
4248
+ compressionOptions: { level: 6 }
4327
4249
  });
4328
- if (whichResult.status === 0) {
4329
- const fromPath = whichResult.stdout?.toString().trim().split(/\r?\n/).map((line) => line.trim()).filter(Boolean) ?? [];
4330
- if (useNpmShell2) {
4331
- fromPath.sort((a, b) => {
4332
- const aCmd = a.toLowerCase().endsWith(".cmd") ? 0 : 1;
4333
- const bCmd = b.toLowerCase().endsWith(".cmd") ? 0 : 1;
4334
- return aCmd - bCmd;
4335
- });
4250
+ await writeFile(zipPath, content);
4251
+ const sizeMb = (content.length / 1024 / 1024).toFixed(2);
4252
+ console.error(`\u5DF2\u751F\u6210: ${zipPath} (${sizeMb} MB)`);
4253
+ return content.length;
4254
+ }
4255
+ var SFTP_UPLOAD_MAX_ATTEMPTS = 3;
4256
+ var SFTP_FAST_PUT_OPTIONS = {
4257
+ chunkSize: 64 * 1024,
4258
+ concurrency: 4
4259
+ };
4260
+ function buildSftpConnectOptions(settings) {
4261
+ return {
4262
+ host: settings.host,
4263
+ port: settings.port,
4264
+ username: settings.username,
4265
+ password: settings.password,
4266
+ readyTimeout: 3e4,
4267
+ tryKeyboard: true,
4268
+ keepaliveInterval: 1e4,
4269
+ keepaliveCountMax: 3
4270
+ };
4271
+ }
4272
+ async function sleep(ms) {
4273
+ await new Promise((resolve5) => setTimeout(resolve5, ms));
4274
+ }
4275
+ async function uploadZipWithRetry(settings, localZip, remoteZipPath) {
4276
+ let lastError;
4277
+ for (let attempt = 1; attempt <= SFTP_UPLOAD_MAX_ATTEMPTS; attempt++) {
4278
+ const sftp = new SftpClient();
4279
+ try {
4280
+ if (attempt > 1) {
4281
+ console.error(
4282
+ `SFTP \u4E0A\u4F20\u91CD\u8BD5 (${attempt}/${SFTP_UPLOAD_MAX_ATTEMPTS})...`
4283
+ );
4284
+ }
4285
+ await sftp.connect(buildSftpConnectOptions(settings));
4286
+ await ensureRemoteDir(sftp, settings.remotePath);
4287
+ await sftp.fastPut(localZip, remoteZipPath, SFTP_FAST_PUT_OPTIONS);
4288
+ return sftp;
4289
+ } catch (err) {
4290
+ lastError = err;
4291
+ try {
4292
+ await sftp.end();
4293
+ } catch {
4294
+ }
4295
+ if (attempt < SFTP_UPLOAD_MAX_ATTEMPTS) {
4296
+ const message = err instanceof Error ? err.message : String(err);
4297
+ const delaySec = attempt * 2;
4298
+ console.error(`SFTP \u4E0A\u4F20\u5931\u8D25 (${message})\uFF0C${delaySec}s \u540E\u91CD\u8BD5...`);
4299
+ await sleep(delaySec * 1e3);
4300
+ }
4336
4301
  }
4337
- candidates.push(...fromPath);
4338
4302
  }
4339
- return candidates;
4303
+ throw lastError;
4340
4304
  }
4341
- function findGlobalPm2() {
4342
- const seen = /* @__PURE__ */ new Set();
4343
- for (const candidate of collectPm2Candidates()) {
4344
- const key = useNpmShell2 ? candidate.toLowerCase() : candidate;
4345
- if (seen.has(key)) continue;
4346
- seen.add(key);
4347
- if (verifyPm2Bin(candidate)) {
4348
- return candidate;
4305
+ async function ensureRemoteDir(sftp, dir) {
4306
+ const parts = dir.replace(/\\/g, "/").split("/").filter(Boolean);
4307
+ let current = dir.startsWith("/") ? "" : ".";
4308
+ for (const part of parts) {
4309
+ current = current ? `${current}/${part}` : `/${part}`;
4310
+ try {
4311
+ await sftp.mkdir(current, true);
4312
+ } catch {
4349
4313
  }
4350
4314
  }
4351
- return null;
4352
4315
  }
4353
- function installGlobalPm2() {
4354
- console.log("[apm] \u672A\u68C0\u6D4B\u5230\u5168\u5C40 pm2\uFF0C\u6B63\u5728\u5B89\u88C5 npm install -g pm2 \u2026");
4355
- const result = runNpm2(["install", "-g", "pm2"], {
4356
- encoding: "utf8",
4357
- stdio: "inherit"
4316
+ function execCommand(client, command) {
4317
+ return new Promise((resolve5, reject) => {
4318
+ client.exec(command, (err, stream) => {
4319
+ if (err) return reject(err);
4320
+ let stdout = "";
4321
+ let stderr = "";
4322
+ stream.on("close", (code) => {
4323
+ if (code !== 0) {
4324
+ reject(new Error(`\u8FDC\u7A0B\u547D\u4EE4\u5931\u8D25 (${code}): ${stderr || stdout}`));
4325
+ return;
4326
+ }
4327
+ resolve5(stdout);
4328
+ }).on("data", (data) => {
4329
+ stdout += data.toString();
4330
+ });
4331
+ stream.stderr.on("data", (data) => {
4332
+ stderr += data.toString();
4333
+ });
4334
+ });
4358
4335
  });
4359
- if (result.status !== 0) {
4360
- console.error("[apm] \u5B89\u88C5 pm2 \u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u6267\u884C: npm install -g pm2");
4361
- process.exit(result.status ?? 1);
4362
- }
4363
4336
  }
4364
- function readPm2Version(pm2Bin) {
4365
- const result = spawnPm2At(pm2Bin, ["--version"], {
4366
- stdio: ["ignore", "pipe", "pipe"]
4367
- });
4368
- if (result.error || result.status !== 0) {
4369
- return null;
4370
- }
4371
- return result.stdout?.toString().trim() || null;
4337
+ function shellSingleQuote(value) {
4338
+ return `'${value.replace(/'/g, `'"'"'`)}'`;
4372
4339
  }
4373
- function logPm2Ready(pm2Bin, installed2) {
4374
- const version = readPm2Version(pm2Bin);
4375
- const versionSuffix = version ? ` ${version}` : "";
4376
- if (installed2) {
4377
- console.log(`[apm] \u5168\u5C40 pm2 \u5DF2\u5B89\u88C5${versionSuffix}: ${pm2Bin}`);
4378
- } else {
4379
- console.log(`[apm] \u5DF2\u68C0\u6D4B\u5230\u5168\u5C40 pm2${versionSuffix}: ${pm2Bin}`);
4380
- }
4340
+ function buildClearRemoteDirExceptZipCommand(target) {
4341
+ const normalized = target.replace(/\\/g, "/").replace(/\/$/, "");
4342
+ const quotedTarget = shellSingleQuote(normalized);
4343
+ const script = [
4344
+ `T=${quotedTarget}`,
4345
+ "S=$(mktemp -d)",
4346
+ 'while IFS= read -r -d "" z; do r="${z#${T}/}"',
4347
+ 'mkdir -p "${S}/$(dirname "$r")"',
4348
+ 'mv "$z" "${S}/${r}"',
4349
+ 'done < <(find "$T" -mindepth 1 -type f -iname "*.zip" -print0)',
4350
+ 'rm -rf "${T}"/*',
4351
+ 'while IFS= read -r -d "" r; do r="${r#./}"',
4352
+ 'mkdir -p "${T}/$(dirname "$r")"',
4353
+ 'mv "${S}/${r}" "${T}/${r}"',
4354
+ 'done < <(cd "$S" 2>/dev/null && find . -type f -print0)',
4355
+ 'rm -rf "$S"'
4356
+ ].join("; ");
4357
+ return `bash -c ${shellSingleQuote(script)}`;
4381
4358
  }
4382
- function isPm2DaemonOutOfDate(output) {
4383
- return /In-memory PM2 is out-of-date/i.test(output);
4359
+ async function uploadAndMaybeExtract(settings, localZip, extract) {
4360
+ const remoteZipPath = `${settings.remotePath.replace(/\/$/, "")}/dist.zip`;
4361
+ console.error(
4362
+ `\u8FDE\u63A5 ${settings.username}@${settings.host}:${settings.port} ...`
4363
+ );
4364
+ const sftp = await uploadZipWithRetry(settings, localZip, remoteZipPath);
4365
+ try {
4366
+ console.error(` \u2713 ${localZip} -> ${remoteZipPath}`);
4367
+ if (extract) {
4368
+ const target = settings.remotePath.replace(/\/$/, "");
4369
+ const client = sftp.client;
4370
+ const clearCmd = buildClearRemoteDirExceptZipCommand(target);
4371
+ console.error(`\u8FDC\u7A0B\u6E05\u7406\u89E3\u538B\u76EE\u5F55: ${clearCmd}`);
4372
+ await execCommand(client, clearCmd);
4373
+ const unzipCmd = `unzip -o "${remoteZipPath}" -d "${target}"`;
4374
+ console.error(`\u8FDC\u7A0B\u89E3\u538B: ${unzipCmd}`);
4375
+ await execCommand(client, unzipCmd);
4376
+ console.error("\u8FDC\u7A0B\u89E3\u538B\u5B8C\u6210!");
4377
+ }
4378
+ console.error(extract ? "\u90E8\u7F72\u5B8C\u6210!" : "\u4E0A\u4F20\u5B8C\u6210!");
4379
+ } finally {
4380
+ await sftp.end();
4381
+ }
4384
4382
  }
4385
- function ensurePm2DaemonUpdated(pm2Bin) {
4386
- const probe = spawnPm2At(pm2Bin, ["jlist"], {
4387
- stdio: ["ignore", "pipe", "pipe"]
4388
- });
4389
- const combined = `${probe.stdout ?? ""}
4390
- ${probe.stderr ?? ""}`;
4391
- if (!isPm2DaemonOutOfDate(combined)) {
4392
- return;
4383
+ function resolveDeployArtifactUpload(input) {
4384
+ const { packOnly, archiveDeployArtifact, deploymentRunId } = input;
4385
+ if (deploymentRunId) {
4386
+ return { upload: true, uploadWithoutRunId: false };
4393
4387
  }
4394
- console.log("[apm] \u68C0\u6D4B\u5230 PM2 \u5185\u5B58\u7248\u672C\u4E0E\u672C\u5730\u4E0D\u4E00\u81F4\uFF0C\u6B63\u5728\u6267\u884C pm2 update \u2026");
4395
- const update = spawnPm2At(pm2Bin, ["update"], {
4396
- stdio: "inherit"
4397
- });
4398
- if (update.error || update.status !== 0) {
4399
- console.error("[apm] pm2 update \u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u6267\u884C: pm2 update");
4388
+ if (archiveDeployArtifact || packOnly) {
4389
+ return { upload: true, uploadWithoutRunId: true };
4400
4390
  }
4391
+ return { upload: false, uploadWithoutRunId: false };
4401
4392
  }
4402
- function ensureGlobalPm2(options) {
4403
- const existing = findGlobalPm2();
4404
- if (existing) {
4405
- ensurePm2DaemonUpdated(existing);
4406
- if (!options?.quiet) {
4407
- logPm2Ready(existing, false);
4393
+ async function runWisdomSftpDeploy(params) {
4394
+ const zipPath = path2.join(params.localDir, "..", ".deploy-sftp-dist.zip");
4395
+ const resolvedZipPath = path2.resolve(zipPath);
4396
+ const packOnly = Boolean(params.packOnly);
4397
+ const archiveDeployArtifact = Boolean(params.archiveDeployArtifact);
4398
+ const deploymentRunId = resolveDeploymentRunIdFromEnv();
4399
+ const artifactUpload = resolveDeployArtifactUpload({
4400
+ packOnly,
4401
+ archiveDeployArtifact,
4402
+ deploymentRunId
4403
+ });
4404
+ let zipSizeBytes = 0;
4405
+ let artifactUploaded = false;
4406
+ try {
4407
+ zipSizeBytes = await zipDirectory(params.localDir, resolvedZipPath);
4408
+ if (!packOnly) {
4409
+ await uploadAndMaybeExtract(
4410
+ params.settings,
4411
+ resolvedZipPath,
4412
+ params.extract
4413
+ );
4414
+ } else {
4415
+ console.error("[apm] \u4EC5\u6253\u5305\u6A21\u5F0F\uFF1A\u8DF3\u8FC7 SFTP \u8FDC\u7A0B\u4E0A\u4F20");
4408
4416
  }
4409
- return existing;
4410
- }
4411
- installGlobalPm2();
4412
- const installed2 = findGlobalPm2();
4413
- if (!installed2) {
4414
- const globalBin = resolveNpmGlobalBin();
4415
- const moduleEntry = resolvePm2FromNpmRoot();
4416
- console.error(
4417
- "[apm] \u5B89\u88C5 pm2 \u540E\u4ECD\u65E0\u6CD5\u627E\u5230\u53EF\u6267\u884C\u6587\u4EF6\uFF0C\u8BF7\u624B\u52A8\u6267\u884C: npm install -g pm2"
4418
- );
4419
- if (globalBin) {
4420
- console.error(`[apm] npm \u5168\u5C40 bin: ${globalBin}`);
4417
+ if (artifactUpload.upload) {
4418
+ const artifact = await uploadDeployArtifactZip({
4419
+ zipPath: resolvedZipPath,
4420
+ projectName: params.projectName,
4421
+ kind: "frontend",
4422
+ uploadWithoutRunId: artifactUpload.uploadWithoutRunId
4423
+ });
4424
+ artifactUploaded = artifact !== null;
4421
4425
  }
4422
- if (moduleEntry) {
4423
- console.error(`[apm] \u5DF2\u68C0\u6D4B\u5230 pm2 \u6A21\u5757: ${moduleEntry}`);
4424
- console.error(
4425
- "[apm] \u82E5 pm2 --version \u53EF\u7528\uFF0C\u8BF7\u68C0\u67E5 Node/npm \u5168\u5C40\u5B89\u88C5\u6743\u9650\u6216 PATH"
4426
- );
4426
+ if (artifactUploaded) {
4427
+ if (packOnly) {
4428
+ console.error("\u6253\u5305\u5E76\u4E0A\u4F20 MinIO \u5B8C\u6210\uFF08\u672A\u4E0A\u4F20\u8FDC\u7A0B\u670D\u52A1\u5668\uFF09");
4429
+ } else {
4430
+ console.error("[apm] SFTP \u90E8\u7F72\u5B8C\u6210\uFF0C\u90E8\u7F72\u4EA7\u7269\u5DF2\u5F52\u6863 MinIO");
4431
+ }
4432
+ }
4433
+ } finally {
4434
+ try {
4435
+ await unlink(resolvedZipPath);
4436
+ console.error(`\u5DF2\u6E05\u7406\u672C\u5730\u4E34\u65F6\u6587\u4EF6: ${resolvedZipPath}`);
4437
+ } catch {
4427
4438
  }
4428
- process.exit(1);
4429
- }
4430
- ensurePm2DaemonUpdated(installed2);
4431
- if (!options?.quiet) {
4432
- logPm2Ready(installed2, true);
4433
4439
  }
4434
- return installed2;
4440
+ return {
4441
+ ok: true,
4442
+ localDir: params.localDir,
4443
+ host: params.settings.host,
4444
+ remotePath: params.settings.remotePath,
4445
+ zipSizeBytes,
4446
+ extracted: packOnly ? false : params.extract,
4447
+ packOnly,
4448
+ artifactUploaded
4449
+ };
4435
4450
  }
4436
- function resolveApmEntryPath(entryArg = process.argv[1]) {
4437
- const fromArgv = entryArg?.trim();
4438
- if (fromArgv && existsSync14(fromArgv)) {
4439
- return fromArgv;
4451
+
4452
+ // src/commands/deploy/internal/wisdom-backend-deploy.ts
4453
+ var SPRINGBOOT_JAVA_OPTS = "-XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -Xms512M -Xmx1G";
4454
+ var MAVEN_MODULE = "jeecg-module-system/jeecg-system-start";
4455
+ var MAVEN_PROFILE = "dev";
4456
+ function log(message) {
4457
+ const now = /* @__PURE__ */ new Date();
4458
+ const hh = String(now.getHours()).padStart(2, "0");
4459
+ const mm = String(now.getMinutes()).padStart(2, "0");
4460
+ const ss = String(now.getSeconds()).padStart(2, "0");
4461
+ console.error(`[${hh}:${mm}:${ss}] ${message}`);
4462
+ }
4463
+ function fail(message) {
4464
+ log(`ERROR: ${message}`);
4465
+ process.exit(1);
4466
+ }
4467
+ function expandPath(pathStr) {
4468
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
4469
+ return path3.resolve(pathStr.replace(/^~(?=\/|\\|$)/, home));
4470
+ }
4471
+ function quoteForShell(value) {
4472
+ if (process.platform === "win32") {
4473
+ return `"${value.replace(/"/g, '""')}"`;
4440
4474
  }
4441
- const npmResult = spawnSync3(useNpmShell2 ? "npm.cmd" : "npm", ["root", "-g"], {
4442
- encoding: "utf8",
4443
- shell: useNpmShell2,
4444
- stdio: ["ignore", "pipe", "pipe"]
4445
- });
4446
- if (npmResult.status === 0) {
4447
- const globalRoot = npmResult.stdout?.toString().trim();
4448
- if (globalRoot) {
4449
- const candidate = join15(globalRoot, CLI_PACKAGE_NAME, "dist", "index.js");
4450
- if (existsSync14(candidate)) {
4451
- return candidate;
4452
- }
4453
- }
4475
+ return shellSingleQuote(value);
4476
+ }
4477
+ function formatMavenLocalRepoArg(repoPath) {
4478
+ if (process.platform === "win32") {
4479
+ return `-Dmaven.repo.local=${quoteForShell(repoPath)}`;
4454
4480
  }
4455
- if (fromArgv) {
4456
- return fromArgv;
4481
+ return `-Dmaven.repo.local=${repoPath}`;
4482
+ }
4483
+ function deployCacheDir() {
4484
+ return path3.join(workspaceApmDir(), "deploy", ".deploy_cache");
4485
+ }
4486
+ function manifestFilePath() {
4487
+ return path3.join(deployCacheDir(), "manifest.json");
4488
+ }
4489
+ function getTargetDir(projectRoot) {
4490
+ return path3.join(projectRoot, MAVEN_MODULE, "target");
4491
+ }
4492
+ function relativeKey(projectRoot, filePath) {
4493
+ return path3.relative(projectRoot, filePath).split(path3.sep).join("/");
4494
+ }
4495
+ function fileSignature(filePath) {
4496
+ const stat2 = statSync6(filePath);
4497
+ return { size: stat2.size, mtime: stat2.mtimeMs / 1e3 };
4498
+ }
4499
+ function loadManifest3() {
4500
+ const manifestPath2 = manifestFilePath();
4501
+ if (!existsSync15(manifestPath2)) {
4502
+ return {};
4457
4503
  }
4458
- console.error("[apm] \u65E0\u6CD5\u89E3\u6790 apm \u5165\u53E3\u8DEF\u5F84");
4459
- process.exit(1);
4504
+ return JSON.parse(readFileSync12(manifestPath2, "utf8"));
4460
4505
  }
4461
- function isRunningUnderPm2() {
4462
- if (process.env.APM_CONNECT_UNDER_PM2 === "1") {
4463
- return true;
4506
+ function saveManifest3(manifest) {
4507
+ const dir = deployCacheDir();
4508
+ mkdirSync7(dir, { recursive: true });
4509
+ writeFileSync12(manifestFilePath(), JSON.stringify(manifest, null, 2), "utf8");
4510
+ }
4511
+ function isProjectLibJar(jarName) {
4512
+ return jarName.startsWith("jeecg-");
4513
+ }
4514
+ function shouldUploadLibFile(localPath, remoteAttr, manifest, projectRoot) {
4515
+ if (!remoteAttr) {
4516
+ return [false, "\u8FDC\u7A0B\u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7"];
4464
4517
  }
4465
- if (process.env.pm_id === void 0) {
4466
- return false;
4518
+ const localSize = statSync6(localPath).size;
4519
+ const remoteSize = remoteAttr.size;
4520
+ if (localSize !== remoteSize) {
4521
+ return [true, `\u5927\u5C0F\u53D8\u5316 ${remoteSize} -> ${localSize}`];
4467
4522
  }
4468
- const name = process.env.name;
4469
- return name === PM2_APP_NAME || name !== void 0 && LEGACY_PM2_APP_NAMES.includes(name);
4470
- }
4471
- function isConnectPm2Process(app) {
4472
- if (app.name === PM2_APP_NAME) {
4473
- return true;
4523
+ if (isProjectLibJar(path3.basename(localPath)) && manifest) {
4524
+ const key = relativeKey(projectRoot, localPath);
4525
+ const current = fileSignature(localPath);
4526
+ const previous = manifest[key];
4527
+ if (!previous) {
4528
+ return [true, "\u9879\u76EE\u6A21\u5757\u672A\u8BB0\u5F55"];
4529
+ }
4530
+ if (previous.size !== current.size) {
4531
+ return [true, "\u9879\u76EE\u6A21\u5757\u5927\u5C0F\u53D8\u5316"];
4532
+ }
4533
+ if (previous.mtime < current.mtime) {
4534
+ return [true, "\u9879\u76EE\u6A21\u5757\u91CD\u65B0\u6784\u5EFA"];
4535
+ }
4474
4536
  }
4475
- return app.name !== void 0 && LEGACY_PM2_APP_NAMES.includes(app.name);
4537
+ return [false, "\u5927\u5C0F\u4E00\u81F4\uFF0C\u8DF3\u8FC7"];
4476
4538
  }
4477
- function listPm2Processes() {
4478
- try {
4479
- const raw = runPm2Json(["jlist"]);
4480
- return JSON.parse(raw);
4481
- } catch {
4482
- return [];
4539
+ function listLibFilesToUpload(localLibDir, remoteStats, projectRoot, manifest = null) {
4540
+ const entries = [];
4541
+ const jarFiles = readdirSync5(localLibDir).filter((name) => name.endsWith(".jar")).sort();
4542
+ for (const jarName of jarFiles) {
4543
+ const jarPath = path3.join(localLibDir, jarName);
4544
+ const remoteAttr = remoteStats.get(jarName);
4545
+ const [shouldUpload, reason] = shouldUploadLibFile(
4546
+ jarPath,
4547
+ remoteAttr,
4548
+ manifest,
4549
+ projectRoot
4550
+ );
4551
+ if (shouldUpload) {
4552
+ entries.push({ path: jarPath, arcname: jarName, reason });
4553
+ }
4483
4554
  }
4555
+ return entries;
4484
4556
  }
4485
- function findPm2ConnectProcess() {
4486
- return listPm2Processes().find(isConnectPm2Process) ?? null;
4487
- }
4488
- function resolvePm2ConnectName(app) {
4489
- return app?.name ?? PM2_APP_NAME;
4557
+ function updateManifestEntries(manifest, entries, projectRoot) {
4558
+ for (const entry of entries) {
4559
+ manifest[relativeKey(projectRoot, entry.path)] = fileSignature(entry.path);
4560
+ }
4561
+ return manifest;
4490
4562
  }
4491
- function deletePm2AppIfExists(name) {
4492
- const pm2Bin = findGlobalPm2();
4493
- if (!pm2Bin) {
4494
- return;
4563
+ async function createUpdatePackage(entries, packageName) {
4564
+ const dir = deployCacheDir();
4565
+ mkdirSync7(dir, { recursive: true });
4566
+ const zipPath = path3.join(dir, packageName);
4567
+ log(`\u521B\u5EFA\u66F4\u65B0\u5305: ${path3.basename(zipPath)}\uFF08${entries.length} \u4E2A\u6587\u4EF6\uFF09`);
4568
+ const zip = new JSZip2();
4569
+ for (const entry of entries) {
4570
+ const content = readFileSync12(entry.path);
4571
+ zip.file(entry.arcname, content);
4572
+ log(` \u6253\u5305: ${entry.arcname} (${entry.reason})`);
4495
4573
  }
4496
- ensurePm2DaemonUpdated(pm2Bin);
4497
- spawnPm2At(pm2Bin, ["delete", name], {
4498
- stdio: "ignore"
4574
+ const buffer = await zip.generateAsync({
4575
+ type: "nodebuffer",
4576
+ compression: "DEFLATE",
4577
+ compressionOptions: { level: 6 }
4499
4578
  });
4579
+ writeFileSync12(zipPath, buffer);
4580
+ return zipPath;
4500
4581
  }
4501
- function removePm2ConnectProcesses() {
4502
- const seen = /* @__PURE__ */ new Set();
4503
- for (const app of listPm2Processes()) {
4504
- if (!isConnectPm2Process(app) || !app.name || seen.has(app.name)) {
4505
- continue;
4582
+ function getMvnExecutable() {
4583
+ const isWin = process.platform === "win32";
4584
+ const candidates = isWin ? ["mvn.cmd", "mvn.bat", "mvn"] : ["mvn"];
4585
+ for (const name of candidates) {
4586
+ const result = spawnSync6(isWin ? `where ${name}` : `which ${name}`, {
4587
+ encoding: "utf8",
4588
+ shell: true
4589
+ });
4590
+ if (result.status === 0 && result.stdout.trim()) {
4591
+ return result.stdout.trim().split(/\r?\n/)[0].trim();
4506
4592
  }
4507
- seen.add(app.name);
4508
- deletePm2AppIfExists(app.name);
4509
- }
4510
- for (const legacyName of LEGACY_PM2_APP_NAMES) {
4511
- deletePm2AppIfExists(legacyName);
4512
4593
  }
4513
- deletePm2AppIfExists(PM2_APP_NAME);
4594
+ fail("\u672A\u627E\u5230 mvn \u547D\u4EE4\uFF0C\u8BF7\u786E\u8BA4 Maven \u5DF2\u5B89\u88C5\u5E76\u52A0\u5165 PATH");
4514
4595
  }
4515
- function runPm2(args, options) {
4516
- const pm2Bin = ensureGlobalPm2({ quiet: true });
4517
- const result = spawnPm2At(pm2Bin, args, {
4518
- stdio: options?.inherit ? "inherit" : ["ignore", "pipe", "pipe"]
4519
- });
4520
- if (result.error) {
4521
- console.error("[apm] \u6267\u884C pm2 \u5931\u8D25:", result.error.message);
4522
- process.exit(1);
4596
+ function runMavenBuild(projectRoot, mavenLocalRepo, repoSource) {
4597
+ const mavenRepo = expandPath(mavenLocalRepo);
4598
+ const mvn = getMvnExecutable();
4599
+ const command = [
4600
+ quoteForShell(mvn),
4601
+ "clean",
4602
+ "package",
4603
+ `-P${MAVEN_PROFILE}`,
4604
+ formatMavenLocalRepoArg(mavenRepo),
4605
+ "-DskipTests"
4606
+ ].join(" ");
4607
+ log("\u5F00\u59CB Maven \u6784\u5EFA...");
4608
+ if (repoSource) {
4609
+ log(
4610
+ `Maven \u672C\u5730\u4ED3\u5E93: ${mavenRepo} (\u6765\u6E90: ${repoSource.source}/${repoSource.sourceDetail})`
4611
+ );
4612
+ } else {
4613
+ log(`Maven \u672C\u5730\u4ED3\u5E93: ${mavenRepo}`);
4523
4614
  }
4615
+ log(`\u6784\u5EFA\u76EE\u5F55: ${projectRoot}`);
4616
+ const result = spawnSync6(command, {
4617
+ cwd: projectRoot,
4618
+ stdio: "inherit",
4619
+ shell: true,
4620
+ env: process.env
4621
+ });
4524
4622
  if (result.status !== 0) {
4525
- const stderr = result.stderr?.toString().trim();
4526
- const stdout = result.stdout?.toString().trim();
4527
- const detail = stderr || stdout || `exit code ${result.status}`;
4528
- console.error(`[apm] pm2 ${args.join(" ")} \u5931\u8D25: ${detail}`);
4529
- process.exit(result.status ?? 1);
4623
+ fail(`Maven \u6784\u5EFA\u5931\u8D25\uFF0C\u9000\u51FA\u7801: ${result.status ?? 1}`);
4530
4624
  }
4531
4625
  }
4532
- function runPm2Json(args) {
4533
- const pm2Bin = findGlobalPm2();
4534
- if (!pm2Bin) {
4535
- return "[]";
4626
+ function locateLibDir(projectRoot) {
4627
+ const targetDir = getTargetDir(projectRoot);
4628
+ if (!existsSync15(targetDir)) {
4629
+ fail(`\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728: ${targetDir}`);
4536
4630
  }
4537
- ensurePm2DaemonUpdated(pm2Bin);
4538
- const result = spawnPm2At(pm2Bin, args, {
4539
- stdio: ["ignore", "pipe", "pipe"]
4540
- });
4541
- if (result.status !== 0) return "[]";
4542
- return result.stdout?.toString() ?? "[]";
4543
- }
4544
- function isPm2ConnectOnline() {
4545
- const app = findPm2ConnectProcess();
4546
- if (!app) return false;
4547
- const status = app.pm2_env?.status;
4548
- return status === "online" || status === "launching";
4549
- }
4550
- function printConnectNotRunningHint() {
4551
- console.log(`[apm] ${PM2_APP_NAME} \u672A\u5728\u8FD0\u884C`);
4552
- console.log("[apm] \u542F\u52A8\u5B88\u62A4: apm connect --daemon");
4631
+ const libDir = path3.join(targetDir, "lib");
4632
+ if (!existsSync15(libDir) || !statSync6(libDir).isDirectory()) {
4633
+ fail(`lib \u76EE\u5F55\u4E0D\u5B58\u5728: ${libDir}`);
4634
+ }
4635
+ const libJars = readdirSync5(libDir).filter((name) => name.endsWith(".jar"));
4636
+ if (libJars.length === 0) {
4637
+ fail(`lib \u76EE\u5F55\u4E0B\u6CA1\u6709\u4F9D\u8D56 JAR: ${libDir}`);
4638
+ }
4639
+ log(`\u5B9A\u4F4D lib \u4EA7\u7269: ${libJars.length} \u4E2A`);
4640
+ return libDir;
4553
4641
  }
4554
- function assertConnectNotRunning() {
4555
- pruneStaleConnectLock();
4556
- const lock = readConnectLock();
4557
- if (lock && isProcessAlive(lock.pid) && lock.pid !== process.pid) {
4558
- console.error(
4559
- `[apm] \u5DF2\u6709 apm connect \u5728\u8FD0\u884C (pid=${lock.pid}, mode=${lock.mode})\uFF0C\u8BF7\u5148\u505C\u6B62\u540E\u518D\u542F\u52A8`
4560
- );
4561
- process.exit(1);
4642
+ function locateMainJar(projectRoot) {
4643
+ const targetDir = getTargetDir(projectRoot);
4644
+ if (!existsSync15(targetDir)) {
4645
+ fail(`\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728: ${targetDir}`);
4562
4646
  }
4563
- if (!isRunningUnderPm2() && isPm2ConnectOnline()) {
4564
- console.error(
4565
- "[apm] \u5DF2\u6709 apm connect \u5B88\u62A4\u8FDB\u7A0B\u5728\u8FD0\u884C\uFF0C\u8BF7\u5148\u6267\u884C apm daemon stop"
4566
- );
4567
- process.exit(1);
4647
+ 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);
4648
+ if (jarFiles.length === 0) {
4649
+ fail(`target \u76EE\u5F55\u4E0B\u6CA1\u6709\u4E3B JAR: ${targetDir}`);
4568
4650
  }
4651
+ const mainJar = jarFiles[0];
4652
+ log(`\u5B9A\u4F4D\u4E3B JAR \u4EA7\u7269: ${path3.basename(mainJar)}`);
4653
+ return mainJar;
4569
4654
  }
4570
- function resolveRuntimePaths() {
4571
- return {
4572
- apmScript: resolveApmEntryPath(),
4573
- nodePath: process.execPath
4574
- };
4655
+ async function connectSsh(config) {
4656
+ const client = new Client2();
4657
+ log(`\u8FDE\u63A5\u670D\u52A1\u5668 ${config.username}@${config.host}:${config.port}`);
4658
+ await new Promise((resolve5, reject) => {
4659
+ client.on("ready", () => resolve5()).on("error", (err) => reject(err)).connect({
4660
+ host: config.host,
4661
+ port: config.port,
4662
+ username: config.username,
4663
+ password: config.password,
4664
+ readyTimeout: 3e4,
4665
+ tryKeyboard: true
4666
+ });
4667
+ });
4668
+ const sftp = new SftpClient2();
4669
+ await sftp.connect({
4670
+ host: config.host,
4671
+ port: config.port,
4672
+ username: config.username,
4673
+ password: config.password,
4674
+ readyTimeout: 3e4,
4675
+ tryKeyboard: true
4676
+ });
4677
+ return { client, sftp };
4575
4678
  }
4576
- function reexecConnectWithResolvedPath(options) {
4577
- const apmScript = resolveApmEntryPath();
4578
- const args = [apmScript, "connect"];
4579
- const server = options.server?.trim();
4580
- if (server) {
4581
- args.push("--server", server);
4582
- }
4583
- const result = spawnSync3(process.execPath, args, { stdio: "inherit" });
4584
- if (result.error) {
4585
- console.error("[apm] \u91CD\u542F connect \u5931\u8D25:", result.error.message);
4586
- process.exit(1);
4679
+ async function closeSsh(conn) {
4680
+ try {
4681
+ await conn.sftp.end();
4682
+ } catch {
4587
4683
  }
4588
- process.exit(result.status ?? 0);
4684
+ conn.client.end();
4589
4685
  }
4590
- async function handleConnectAfterUpdate(options) {
4591
- console.log("[apm] \u66F4\u65B0\u5B8C\u6210\uFF0C\u6B63\u5728\u4EE5\u65B0\u7248\u672C\u91CD\u65B0\u8FDE\u63A5\u2026");
4592
- await prepareEcosystem(options.server);
4593
- if (isRunningUnderPm2()) {
4594
- runPm2(["reload", PM2_ECOSYSTEM_PATH, "--update-env"], { inherit: true });
4595
- console.log("[apm] \u5DF2\u901A\u77E5 PM2 \u4F7F\u7528\u65B0\u7248\u672C\u91CD\u65B0\u52A0\u8F7D connect");
4596
- process.exit(0);
4686
+ async function getRemoteFileStats(sftp, remoteDir) {
4687
+ const stats = /* @__PURE__ */ new Map();
4688
+ try {
4689
+ const listing = await sftp.list(remoteDir);
4690
+ for (const item of listing) {
4691
+ if (item.name.endsWith(".jar")) {
4692
+ stats.set(item.name, { filename: item.name, size: item.size });
4693
+ }
4694
+ }
4695
+ } catch {
4597
4696
  }
4598
- reexecConnectWithResolvedPath(options);
4697
+ return stats;
4599
4698
  }
4600
- async function resolveBaseUrl(server) {
4601
- if (server?.trim()) {
4602
- return server.trim().replace(/\/+$/, "");
4699
+ async function uploadUpdatePackage(sftp, zipPath, config) {
4700
+ const remoteDir = config.remoteVueDistDir.replace(/\/$/, "");
4701
+ const remotePath = `${remoteDir}/${path3.basename(zipPath)}`;
4702
+ log(`\u4E0A\u4F20\u66F4\u65B0\u5305 -> ${remotePath}`);
4703
+ try {
4704
+ await sftp.fastPut(zipPath, remotePath);
4705
+ log("\u66F4\u65B0\u5305\u4E0A\u4F20\u6210\u529F");
4706
+ } catch (err) {
4707
+ fail(`\u66F4\u65B0\u5305\u4E0A\u4F20\u5931\u8D25: ${err instanceof Error ? err.message : err}`);
4603
4708
  }
4604
- const cfg = await tryReadApmConfig();
4605
- return cfg?.baseUrl;
4709
+ return remotePath;
4606
4710
  }
4607
- function writeConnectLaunchFiles(options) {
4608
- mkdirSync7(APM_CONFIG_DIR, { recursive: true });
4609
- writeFileSync12(PM2_CONNECT_ENTRY_PATH, CONNECT_ENTRY_SCRIPT, "utf8");
4610
- const env = {
4611
- ...collectPm2LaunchEnv()
4612
- };
4613
- const baseUrl = options.baseUrl?.trim().replace(/\/+$/, "");
4614
- if (baseUrl) {
4615
- env.AI_PM_SERVER = baseUrl;
4711
+ async function uploadFullJar(sftp, localJarPath, config) {
4712
+ const remoteDir = config.remoteAppDir.replace(/\/$/, "");
4713
+ const remotePath = `${remoteDir}/${config.startupJar}`;
4714
+ log(`\u4E0A\u4F20\u5168\u91CF JAR (${path3.basename(localJarPath)}) -> ${remotePath}`);
4715
+ try {
4716
+ await sftp.mkdir(remoteDir, true);
4717
+ await sftp.fastPut(localJarPath, remotePath);
4718
+ log("\u5168\u91CF JAR \u4E0A\u4F20\u6210\u529F");
4719
+ } catch (err) {
4720
+ fail(`\u5168\u91CF JAR \u4E0A\u4F20\u5931\u8D25: ${err instanceof Error ? err.message : err}`);
4616
4721
  }
4617
- const launch = {
4618
- nodePath: options.nodePath,
4619
- apmScript: options.apmScript,
4620
- connectArgs: options.connectArgs,
4621
- cwd: options.cwd,
4622
- env
4623
- };
4624
- writeFileSync12(
4625
- PM2_CONNECT_LAUNCH_PATH,
4626
- JSON.stringify(launch, null, 2) + "\n",
4627
- "utf8"
4628
- );
4629
4722
  }
4630
- function writeEcosystemFile(options) {
4631
- writeConnectLaunchFiles(options);
4632
- const ecosystem = buildConnectPm2Ecosystem({
4633
- entryScript: PM2_CONNECT_ENTRY_PATH,
4634
- baseUrl: options.baseUrl
4723
+ async function runRemoteCommand(client, command, options) {
4724
+ const check = options?.check ?? true;
4725
+ const stream = options?.stream ?? false;
4726
+ const label = options?.label ?? "\u8FDC\u7A0B\u547D\u4EE4";
4727
+ return new Promise((resolve5, reject) => {
4728
+ client.exec(command, (err, execStream) => {
4729
+ if (err) {
4730
+ reject(err);
4731
+ return;
4732
+ }
4733
+ let out = "";
4734
+ let errText = "";
4735
+ execStream.on("data", (data) => {
4736
+ const text = data.toString();
4737
+ out += text;
4738
+ if (stream) {
4739
+ process.stdout.write(text);
4740
+ }
4741
+ });
4742
+ execStream.stderr.on("data", (data) => {
4743
+ errText += data.toString();
4744
+ });
4745
+ execStream.on("close", (code) => {
4746
+ if (stream && out && !out.endsWith("\n")) {
4747
+ process.stdout.write("\n");
4748
+ }
4749
+ if (check && code !== 0) {
4750
+ const combined = `${out}
4751
+ ${errText}`.trim();
4752
+ fail(
4753
+ `${label}\u5931\u8D25 (exit ${code})` + (combined ? `
4754
+ \u8F93\u51FA: ${combined}` : "")
4755
+ );
4756
+ }
4757
+ resolve5({ exitCode: code, out: out.trim(), err: errText.trim() });
4758
+ });
4759
+ });
4635
4760
  });
4636
- writeFileSync12(
4637
- PM2_ECOSYSTEM_PATH,
4638
- formatConnectPm2EcosystemFile(ecosystem),
4639
- "utf8"
4640
- );
4641
- if (existsSync14(LEGACY_PM2_ECOSYSTEM_PATH)) {
4642
- try {
4643
- unlinkSync2(LEGACY_PM2_ECOSYSTEM_PATH);
4644
- } catch {
4645
- }
4646
- }
4647
4761
  }
4648
- async function prepareEcosystem(server) {
4649
- await ensureLoggedConfig();
4650
- const cfg = await ensureApmConfig();
4651
- const { apmScript, nodePath } = resolveRuntimePaths();
4652
- const connectArgs = resolveConnectArgs(server);
4653
- const baseUrl = await resolveBaseUrl(server);
4654
- writeEcosystemFile({
4655
- apmScript,
4656
- nodePath,
4657
- connectArgs,
4658
- baseUrl,
4659
- cwd: process.cwd()
4762
+ function buildExtractUpdatePackageScript(remoteZipPath, remoteLibDir) {
4763
+ const quotedZip = shellSingleQuote(remoteZipPath);
4764
+ const quotedLib = shellSingleQuote(remoteLibDir);
4765
+ return `
4766
+ set -e
4767
+ TMP=$(mktemp -d)
4768
+ trap 'rm -rf "$TMP"' EXIT
4769
+ unzip -oq ${quotedZip} -d "$TMP"
4770
+ updated=0
4771
+ while IFS= read -r -d '' src; do
4772
+ name=$(basename "$src")
4773
+ dest=${quotedLib}/"$name"
4774
+ if [ -f "$dest" ]; then
4775
+ cp -f "$src" "$dest"
4776
+ echo "\u8986\u76D6: $name"
4777
+ updated=$((updated + 1))
4778
+ else
4779
+ echo "\u8DF3\u8FC7(\u8FDC\u7A0B\u4E0D\u5B58\u5728): $name"
4780
+ fi
4781
+ done < <(find "$TMP" -name '*.jar' -type f -print0)
4782
+ echo "UPDATED_COUNT=$updated"
4783
+ `.trim();
4784
+ }
4785
+ async function extractUpdatePackageOnRemote(client, config, remoteZipPath) {
4786
+ const script = buildExtractUpdatePackageScript(
4787
+ remoteZipPath,
4788
+ config.remoteLibDir
4789
+ );
4790
+ const { out } = await runRemoteCommand(client, script, {
4791
+ label: "\u8FDC\u7A0B\u89E3\u538B"
4660
4792
  });
4661
- return cfg;
4793
+ const match = out.match(/UPDATED_COUNT=(\d+)/);
4794
+ if (!match) {
4795
+ fail(`\u8FDC\u7A0B\u89E3\u538B\u5931\u8D25\uFF0C\u672A\u83B7\u53D6\u66F4\u65B0\u6570\u91CF
4796
+ \u8F93\u51FA: ${out || "(\u7A7A)"}`);
4797
+ }
4798
+ const updated = Number.parseInt(match[1], 10);
4799
+ log(`lib \u89E3\u538B\u5B8C\u6210: \u8986\u76D6 ${updated} \u4E2A`);
4800
+ return updated;
4662
4801
  }
4663
- async function runDaemonStart(options) {
4664
- assertConnectNotRunning();
4665
- await runUpdate();
4666
- ensureGlobalPm2();
4667
- const cfg = await prepareEcosystem(options.server);
4668
- removePm2ConnectProcesses();
4669
- runPm2(["start", PM2_ECOSYSTEM_PATH, "--update-env"], { inherit: true });
4670
- await delay(1e3);
4671
- if (!isPm2ConnectOnline()) {
4672
- console.error(
4673
- `[apm] ${PM2_APP_NAME} \u542F\u52A8\u540E\u672A\u5904\u4E8E online \u72B6\u6001\uFF0C\u8BF7\u6267\u884C: apm daemon logs -n 200`
4674
- );
4802
+ function springbootOutputIndicatesSuccess(action, combined) {
4803
+ const lower = combined.toLowerCase();
4804
+ if (action === "health") {
4805
+ return combined.includes("\u5065\u5EB7\u68C0\u67E5\u901A\u8FC7");
4675
4806
  }
4676
- console.log(
4677
- `[apm] ${PM2_APP_NAME} \u5DF2\u7531 PM2 \u542F\u52A8\uFF08server=${options.server?.trim() || cfg.baseUrl}\uFF09`
4678
- );
4679
- if (options.follow) {
4680
- console.log(
4681
- "[apm] \u6B63\u5728\u8DDF\u8E2A\u65E5\u5FD7\uFF08Ctrl+C \u4EC5\u9000\u51FA\u65E5\u5FD7\u67E5\u770B\uFF0C\u5B88\u62A4\u8FDB\u7A0B\u7EE7\u7EED\u8FD0\u884C\uFF09"
4682
- );
4683
- runDaemonLogs({ follow: true });
4684
- return;
4807
+ if (action === "start" || action === "restart") {
4808
+ return combined.includes("is starting") || lower.includes("is running");
4685
4809
  }
4686
- console.log("[apm] \u67E5\u770B\u72B6\u6001: apm daemon status");
4687
- console.log("[apm] \u67E5\u770B\u65E5\u5FD7: apm daemon logs -f");
4688
- console.log("[apm] \u505C\u6B62: apm daemon stop");
4810
+ if (action === "stop") {
4811
+ return combined.includes("is stopping") || lower.includes("not running") || lower.includes("please check it");
4812
+ }
4813
+ if (action === "status") {
4814
+ return lower.includes("running") || lower.includes("not running");
4815
+ }
4816
+ return true;
4817
+ }
4818
+ function buildRemoteStatusScript(remoteAppDir, appName) {
4819
+ const dir = shellSingleQuote(remoteAppDir);
4820
+ const jar = shellSingleQuote(appName);
4821
+ return `
4822
+ set -e
4823
+ cd ${dir}
4824
+ appName=${jar}
4825
+ appIds=$(ps -ef | grep java | grep "$appName" | awk '{print $2}')
4826
+ if [ -z "$appIds" ]; then
4827
+ echo -e "\\033[31m Not running \\033[0m"
4828
+ else
4829
+ echo -e "\\033[32m Running [$appIds] \\033[0m"
4830
+ fi
4831
+ `.trim();
4832
+ }
4833
+ function buildRemoteRestartScript(remoteAppDir) {
4834
+ const dir = shellSingleQuote(remoteAppDir);
4835
+ const javaOpts = shellSingleQuote(SPRINGBOOT_JAVA_OPTS);
4836
+ return `
4837
+ set -e
4838
+ cd ${dir}
4839
+ releaseApp=$(ls -t | grep '.jar$' | head -n1)
4840
+ lastVersionApp=$(ls -t | grep '.jar$' | head -n2 | tail -n1)
4841
+ appName=$lastVersionApp
4842
+ appIds=$(ps -ef | grep java | grep "$appName" | awk '{print $2}')
4843
+ if [ -z "$appIds" ]; then
4844
+ echo "Maybe $appName not running, please check it..."
4845
+ else
4846
+ echo "The $appName is stopping..."
4847
+ echo "$appIds" | xargs kill
4848
+ fi
4849
+ for i in $(seq 15 -1 1); do
4850
+ echo -n "$i "
4851
+ sleep 1
4852
+ done
4853
+ echo 0
4854
+ if [ ! -d "backup" ]; then
4855
+ mkdir backup
4856
+ fi
4857
+ for i in $(ls | grep '.jar$' | grep -vFx "$releaseApp"); do
4858
+ echo "backup $i"
4859
+ mv "$i" backup/
4860
+ done
4861
+ appName=$releaseApp
4862
+ count=$(ps -ef | grep java | grep "$appName" | wc -l)
4863
+ if [ "$count" != "0" ]; then
4864
+ echo "Maybe $appName is running, please check it..."
4865
+ else
4866
+ echo "The $appName is starting..."
4867
+ nohup java -jar "./$appName" ${javaOpts} > nohup.out 2>&1 &
4868
+ fi
4869
+ `.trim();
4689
4870
  }
4690
- async function runDaemonStop() {
4691
- pruneStaleConnectLock();
4692
- if (!isPm2ConnectOnline()) {
4693
- killConnectLockProcessIfAlive();
4694
- forceReleaseConnectLock();
4695
- console.log(`[apm] ${PM2_APP_NAME} \u672A\u5728\u8FD0\u884C`);
4696
- return;
4697
- }
4698
- runPm2(["stop", resolvePm2ConnectName(findPm2ConnectProcess())], {
4699
- inherit: true
4700
- });
4701
- for (let i = 0; i < 10; i += 1) {
4702
- if (!isPm2ConnectOnline()) break;
4703
- await delay(500);
4871
+ function normalizeHealthContext(context) {
4872
+ let normalized = context.trim() || "/";
4873
+ if (!normalized.startsWith("/")) {
4874
+ normalized = `/${normalized}`;
4704
4875
  }
4705
- if (isPm2ConnectOnline()) {
4706
- console.log(`[apm] \u4F18\u96C5\u505C\u6B62\u8D85\u65F6\uFF0C\u5F3A\u5236\u79FB\u9664 ${PM2_APP_NAME}\u2026`);
4707
- runPm2(["delete", resolvePm2ConnectName(findPm2ConnectProcess())], {
4708
- inherit: true
4709
- });
4876
+ if (!normalized.endsWith("/")) {
4877
+ normalized = `${normalized}/`;
4710
4878
  }
4711
- killConnectLockProcessIfAlive();
4712
- forceReleaseConnectLock();
4713
- console.log(`[apm] ${PM2_APP_NAME} \u5DF2\u505C\u6B62`);
4879
+ return normalized;
4714
4880
  }
4715
- function killConnectLockProcessIfAlive() {
4716
- pruneStaleConnectLock();
4717
- const lock = readConnectLock();
4718
- if (!lock || !isProcessAlive(lock.pid)) return;
4719
- try {
4720
- process.kill(lock.pid, "SIGTERM");
4721
- } catch {
4722
- forceReleaseConnectLock();
4723
- return;
4724
- }
4725
- if (isProcessAlive(lock.pid)) {
4726
- try {
4727
- process.kill(lock.pid, "SIGKILL");
4728
- } catch {
4881
+ function buildRemoteHealthScript(port, context, timeoutSecs) {
4882
+ const normalizedContext = normalizeHealthContext(context);
4883
+ const portStr = String(port);
4884
+ const timeoutStr = String(timeoutSecs);
4885
+ return `
4886
+ set -e
4887
+ port=${shellSingleQuote(portStr)}
4888
+ context=${shellSingleQuote(normalizedContext)}
4889
+ timeout=${shellSingleQuote(timeoutStr)}
4890
+ check_url="http://127.0.0.1:${portStr}${normalizedContext}"
4891
+ echo "\u5065\u5EB7\u68C0\u67E5: \${check_url} (\u8D85\u65F6 \${timeout}s)"
4892
+ deadline=$(($(date +%s) + timeout))
4893
+ attempt=0
4894
+ while [ $(date +%s) -lt $deadline ]; do
4895
+ attempt=$((attempt + 1))
4896
+ code=$(curl -s -o /dev/null -w "%{http_code}" "$check_url" 2>/dev/null || echo "000")
4897
+ code=$(echo "$code" | tail -n1 | tr -d '[:space:]')
4898
+ if [ \${#code} -ge 3 ]; then
4899
+ status=\${code:0:3}
4900
+ if echo "$status" | grep -qE '^[0-9]+$' && [ "$status" -ge 200 ] && [ "$status" -lt 500 ]; then
4901
+ echo -e "\\033[32m\u5065\u5EB7\u68C0\u67E5\u901A\u8FC7 (HTTP \${status})\\033[0m"
4902
+ exit 0
4903
+ fi
4904
+ fi
4905
+ remaining=$((deadline - $(date +%s)))
4906
+ if [ $remaining -lt 0 ]; then
4907
+ remaining=0
4908
+ fi
4909
+ echo "\u7B49\u5F85\u670D\u52A1\u542F\u52A8... \u7B2C \${attempt} \u6B21\uFF0C\u5269\u4F59 \${remaining}s"
4910
+ sleep 5
4911
+ done
4912
+ echo -e "\\033[31m\u5065\u5EB7\u68C0\u67E5\u8D85\u65F6 (\${timeout}s): \${check_url}\\033[0m"
4913
+ exit 1
4914
+ `.trim();
4915
+ }
4916
+ async function runRemoteServiceScript(client, script, action) {
4917
+ const { exitCode, out, err } = await runRemoteCommand(client, script, {
4918
+ check: false,
4919
+ label: action === "status" ? "\u8FDC\u7A0B status" : action === "restart" ? "\u8FDC\u7A0B restart" : "\u5065\u5EB7\u68C0\u67E5"
4920
+ });
4921
+ const combined = `${out}
4922
+ ${err}`.trim();
4923
+ const outputOk = springbootOutputIndicatesSuccess(action, combined);
4924
+ if (action === "health") {
4925
+ if (exitCode !== 0 || !outputOk) {
4926
+ fail(`\u5065\u5EB7\u68C0\u67E5\u5931\u8D25
4927
+ \u8F93\u51FA: ${combined || "(\u7A7A)"}`);
4729
4928
  }
4929
+ return combined;
4730
4930
  }
4731
- forceReleaseConnectLock();
4732
- }
4733
- async function runDaemonRestart(options) {
4734
- pruneStaleConnectLock();
4735
- const lock = readConnectLock();
4736
- if (lock && isProcessAlive(lock.pid) && !isPm2ConnectOnline()) {
4737
- console.error(
4738
- `[apm] \u5DF2\u6709\u524D\u53F0 apm connect \u5728\u8FD0\u884C (pid=${lock.pid})\uFF0C\u8BF7\u5148\u505C\u6B62\u540E\u518D\u542F\u52A8\u5B88\u62A4\u8FDB\u7A0B`
4739
- );
4740
- process.exit(1);
4931
+ if (exitCode !== 0 && !outputOk) {
4932
+ fail(`\u8FDC\u7A0B ${action} \u5931\u8D25
4933
+ ${combined}`);
4741
4934
  }
4742
- await runUpdate();
4743
- await prepareEcosystem(options.server);
4744
- removePm2ConnectProcesses();
4745
- runPm2(["start", PM2_ECOSYSTEM_PATH, "--update-env"], { inherit: true });
4746
- console.log(`[apm] ${PM2_APP_NAME} \u5DF2\u91CD\u542F`);
4747
- }
4748
- async function runDaemonDelete() {
4749
- ensureGlobalPm2();
4750
- const app = findPm2ConnectProcess();
4751
- if (!app?.name) {
4752
- console.log(`[apm] ${PM2_APP_NAME} \u672A\u5728 PM2 \u4E2D\u6CE8\u518C`);
4753
- return;
4935
+ if (action === "restart" && !outputOk) {
4936
+ fail(`\u8FDC\u7A0B restart \u672A\u6210\u529F
4937
+ \u8F93\u51FA: ${combined || "(\u7A7A)"}`);
4754
4938
  }
4755
- runPm2(["delete", app.name], { inherit: true });
4756
- forceReleaseConnectLock();
4757
- console.log(`[apm] ${app.name} \u5DF2\u4ECE PM2 \u79FB\u9664`);
4939
+ return combined;
4758
4940
  }
4759
- async function runDaemonStatus() {
4760
- ensureGlobalPm2();
4761
- const app = findPm2ConnectProcess();
4762
- if (!app?.name) {
4763
- printConnectNotRunningHint();
4764
- return;
4765
- }
4766
- runPm2(["describe", app.name], { inherit: true });
4941
+ function stripAnsi(text) {
4942
+ return text.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "");
4767
4943
  }
4768
- async function runDaemonLogs(options) {
4769
- ensureGlobalPm2();
4770
- const app = findPm2ConnectProcess();
4771
- if (!app?.name) {
4772
- printConnectNotRunningHint();
4773
- return;
4944
+ async function getRunningJar(client, config) {
4945
+ const script = buildRemoteStatusScript(
4946
+ config.remoteAppDir,
4947
+ config.startupJar
4948
+ );
4949
+ const combined = await runRemoteServiceScript(client, script, "status");
4950
+ const text = stripAnsi(combined).trim().toLowerCase();
4951
+ if (text.includes("not running")) {
4952
+ return null;
4774
4953
  }
4775
- const args = ["logs", app.name, "--lines", String(options.lines ?? 100)];
4776
- if (!options.follow) {
4777
- args.push("--nostream");
4954
+ if (text.includes("running")) {
4955
+ return config.startupJar;
4778
4956
  }
4779
- runPm2(args, { inherit: true });
4957
+ return null;
4780
4958
  }
4781
-
4782
- // src/commands/deploy/internal/deploy-shell-env.ts
4783
- var PATH_SEP = process.platform === "win32" ? ";" : ":";
4784
- var useNpmShell3 = process.platform === "win32";
4785
- function resolveNpmGlobalBin2() {
4786
- const result = spawnSync4(useNpmShell3 ? "npm.cmd" : "npm", ["bin", "-g"], {
4787
- encoding: "utf8",
4788
- shell: useNpmShell3,
4789
- stdio: ["ignore", "pipe", "pipe"]
4790
- });
4791
- if (result.status !== 0) {
4792
- return null;
4793
- }
4794
- const bin = result.stdout?.toString().trim();
4795
- return bin || null;
4959
+ async function healthCheckService(client, config) {
4960
+ log("\u5065\u5EB7\u68C0\u67E5...");
4961
+ const script = buildRemoteHealthScript(
4962
+ config.healthCheckPort,
4963
+ config.healthCheckContext,
4964
+ config.healthCheckTimeout
4965
+ );
4966
+ await runRemoteServiceScript(client, script, "health");
4796
4967
  }
4797
- function prependPath(pathValue, segment) {
4798
- if (!segment) {
4799
- return pathValue ?? "";
4800
- }
4801
- if (!pathValue) {
4802
- return segment;
4803
- }
4804
- const segments = pathValue.split(PATH_SEP);
4805
- if (segments.includes(segment)) {
4806
- return pathValue;
4807
- }
4808
- return `${segment}${PATH_SEP}${pathValue}`;
4968
+ async function restartRemoteService(client, config) {
4969
+ const script = buildRemoteRestartScript(config.remoteAppDir);
4970
+ await runRemoteServiceScript(client, script, "restart");
4809
4971
  }
4810
- function buildDeployShellEnv(extra) {
4811
- const env = { ...process.env, ...extra };
4812
- const pathSegments = [dirname4(process.execPath)];
4813
- const globalBin = resolveNpmGlobalBin2();
4814
- if (globalBin) {
4815
- pathSegments.push(globalBin);
4972
+ function listAllLibFilesForArchive(libDir) {
4973
+ return readdirSync5(libDir).filter((name) => name.endsWith(".jar")).sort().map((jarName) => ({
4974
+ path: path3.join(libDir, jarName),
4975
+ arcname: jarName,
4976
+ reason: "\u4EC5\u6253\u5305\u5F52\u6863"
4977
+ }));
4978
+ }
4979
+ async function runWisdomBackendDeploy(options) {
4980
+ const projectRoot = path3.resolve(options.projectRoot ?? process.cwd());
4981
+ const config = options.config;
4982
+ const packOnly = Boolean(options.packOnly);
4983
+ log(`=== \u81EA\u52A8\u90E8\u7F72: ${config.projectName} ===`);
4984
+ log(`\u914D\u7F6E\u6587\u4EF6: ${path3.join(workspaceApmDir(), "apm.config.json")}`);
4985
+ log(`\u9879\u76EE\u6839\u76EE\u5F55: ${projectRoot}`);
4986
+ log(
4987
+ `\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"}${packOnly ? "\uFF1B\u4EC5\u6253\u5305\uFF08\u4E0D\u4E0A\u4F20\u8FDC\u7A0B\uFF09" : ""}`
4988
+ );
4989
+ runMavenBuild(projectRoot, config.mavenLocalRepo, {
4990
+ source: config.mavenLocalRepoSource,
4991
+ sourceDetail: config.mavenLocalRepoSourceDetail
4992
+ });
4993
+ if (packOnly) {
4994
+ const uploadWithoutRunId = !resolveDeploymentRunIdFromEnv();
4995
+ if (config.mode === "full") {
4996
+ const mainJar = locateMainJar(projectRoot);
4997
+ const archiveZipPath = await createUpdatePackage(
4998
+ [
4999
+ {
5000
+ path: mainJar,
5001
+ arcname: path3.basename(mainJar),
5002
+ reason: "\u5168\u91CF JAR \u5F52\u6863"
5003
+ }
5004
+ ],
5005
+ `.deploy-archive-${config.projectName}.jar.zip`
5006
+ );
5007
+ await uploadDeployArtifactZip({
5008
+ zipPath: archiveZipPath,
5009
+ projectName: config.projectName,
5010
+ kind: "backend",
5011
+ uploadWithoutRunId
5012
+ });
5013
+ } else {
5014
+ const libDir = locateLibDir(projectRoot);
5015
+ const entries = listAllLibFilesForArchive(libDir);
5016
+ if (entries.length === 0) {
5017
+ fail("lib \u76EE\u5F55\u4E0B\u6CA1\u6709\u53EF\u5F52\u6863\u7684 JAR");
5018
+ }
5019
+ const zipPath = await createUpdatePackage(entries, config.packageName);
5020
+ await uploadDeployArtifactZip({
5021
+ zipPath,
5022
+ projectName: config.projectName,
5023
+ kind: "backend",
5024
+ uploadWithoutRunId
5025
+ });
5026
+ }
5027
+ log("\u4EC5\u6253\u5305\u5B8C\u6210\uFF08\u672A\u4E0A\u4F20\u8FDC\u7A0B\u670D\u52A1\u5668\uFF09");
5028
+ return;
4816
5029
  }
5030
+ const conn = await connectSsh(config);
4817
5031
  try {
4818
- resolveApmEntryPath();
4819
- } catch {
4820
- }
4821
- for (const segment of pathSegments) {
4822
- env.PATH = prependPath(env.PATH, segment);
5032
+ if (config.mode === "full") {
5033
+ const mainJar = locateMainJar(projectRoot);
5034
+ await uploadFullJar(conn.sftp, mainJar, config);
5035
+ const archiveZipPath = await createUpdatePackage(
5036
+ [
5037
+ {
5038
+ path: mainJar,
5039
+ arcname: path3.basename(mainJar),
5040
+ reason: "\u5168\u91CF JAR \u5F52\u6863"
5041
+ }
5042
+ ],
5043
+ `.deploy-archive-${config.projectName}.jar.zip`
5044
+ );
5045
+ await uploadDeployArtifactZip({
5046
+ zipPath: archiveZipPath,
5047
+ projectName: config.projectName,
5048
+ kind: "backend"
5049
+ });
5050
+ log("\u91CD\u542F\u670D\u52A1...");
5051
+ await restartRemoteService(conn.client, config);
5052
+ await healthCheckService(conn.client, config);
5053
+ log("\u90E8\u7F72\u5B8C\u6210");
5054
+ return;
5055
+ }
5056
+ const libDir = locateLibDir(projectRoot);
5057
+ let manifest = loadManifest3();
5058
+ const remoteLibStats = await getRemoteFileStats(
5059
+ conn.sftp,
5060
+ config.remoteLibDir
5061
+ );
5062
+ log("\u6536\u96C6 JAR \u66F4\u65B0...");
5063
+ const libUploadEntries = listLibFilesToUpload(
5064
+ libDir,
5065
+ remoteLibStats,
5066
+ projectRoot,
5067
+ manifest
5068
+ );
5069
+ let updated = 0;
5070
+ if (libUploadEntries.length > 0) {
5071
+ const zipPath = await createUpdatePackage(
5072
+ libUploadEntries,
5073
+ config.packageName
5074
+ );
5075
+ const remoteZipPath = await uploadUpdatePackage(
5076
+ conn.sftp,
5077
+ zipPath,
5078
+ config
5079
+ );
5080
+ await uploadDeployArtifactZip({
5081
+ zipPath,
5082
+ projectName: config.projectName,
5083
+ kind: "backend"
5084
+ });
5085
+ log("\u8FDC\u7A0B\u89E3\u538B lib \u76EE\u5F55\uFF08\u4EC5\u8986\u76D6\u5DF2\u6709 JAR\uFF09...");
5086
+ updated = await extractUpdatePackageOnRemote(
5087
+ conn.client,
5088
+ config,
5089
+ remoteZipPath
5090
+ );
5091
+ } else {
5092
+ log("\u65E0 JAR \u9700\u8981\u66F4\u65B0\uFF0C\u8DF3\u8FC7\u66F4\u65B0\u5305\u4E0A\u4F20");
5093
+ }
5094
+ const runningJar = await getRunningJar(conn.client, config);
5095
+ let needRestart = updated > 0;
5096
+ if (!needRestart && !runningJar) {
5097
+ log("\u670D\u52A1\u672A\u8FD0\u884C\uFF0C\u9700\u8981\u542F\u52A8");
5098
+ needRestart = true;
5099
+ } else if (!needRestart) {
5100
+ log("\u6CA1\u6709\u6587\u4EF6\u9700\u8981\u66F4\u65B0\uFF0C\u8DF3\u8FC7\u91CD\u542F");
5101
+ }
5102
+ if (needRestart) {
5103
+ log("\u91CD\u542F\u670D\u52A1...");
5104
+ await restartRemoteService(conn.client, config);
5105
+ }
5106
+ await healthCheckService(conn.client, config);
5107
+ if (libUploadEntries.length > 0) {
5108
+ manifest = updateManifestEntries(manifest, libUploadEntries, projectRoot);
5109
+ saveManifest3(manifest);
5110
+ }
5111
+ } finally {
5112
+ await closeSsh(conn);
4823
5113
  }
4824
- return env;
5114
+ log("\u90E8\u7F72\u5B8C\u6210");
4825
5115
  }
4826
5116
 
4827
- // src/commands/deploy/internal/wisdom-auto-deploy.ts
4828
- function isWisdomLegacyDeploy(cfg) {
5117
+ // src/commands/deploy/internal/wisdom-deploy.ts
5118
+ function isWisdomDeployConfigured(cfg) {
4829
5119
  const w = cfg.wisdomDeploy;
4830
- if (!w?.host?.trim() || !w.remotePath?.trim()) {
4831
- return false;
4832
- }
4833
- const hasNewFrontend = Boolean(cfg.frontendDeploy?.endpoint?.trim());
4834
- const hasNewBackend = Boolean(cfg.backendDeploy?.registryHost?.trim());
4835
- return !hasNewFrontend && !hasNewBackend;
5120
+ return Boolean(w?.host?.trim() && w?.remotePath?.trim());
4836
5121
  }
4837
5122
  function detectWisdomProjectType(cwd) {
4838
- return existsSync15(path4.join(cwd, "package.json")) ? "frontend" : "backend";
5123
+ return existsSync16(path4.join(cwd, "package.json")) ? "frontend" : "backend";
4839
5124
  }
4840
5125
  function readPackageScripts(cwd) {
4841
5126
  const pkgPath = path4.join(cwd, "package.json");
4842
- if (!existsSync15(pkgPath)) {
5127
+ if (!existsSync16(pkgPath)) {
4843
5128
  return {};
4844
5129
  }
4845
5130
  try {
@@ -4850,14 +5135,6 @@ function readPackageScripts(cwd) {
4850
5135
  return {};
4851
5136
  }
4852
5137
  }
4853
- function resolveFrontendDeployCommand(env, cwd) {
4854
- const scripts = readPackageScripts(cwd);
4855
- const deployKey = `deploy:${env}`;
4856
- if (scripts[deployKey]?.trim()) {
4857
- return `npm run deploy:${env}`;
4858
- }
4859
- return null;
4860
- }
4861
5138
  function resolveFrontendBuildCommand(env, cwd) {
4862
5139
  const scripts = readPackageScripts(cwd);
4863
5140
  const buildKey = `build:${env}`;
@@ -4866,36 +5143,12 @@ function resolveFrontendBuildCommand(env, cwd) {
4866
5143
  }
4867
5144
  return null;
4868
5145
  }
4869
- function runShellCommand(command, cwd, captureOutput) {
4870
- const result = spawnSync5(command, {
4871
- cwd,
4872
- shell: true,
4873
- env: buildDeployShellEnv(),
4874
- encoding: "utf8",
4875
- stdio: captureOutput ? ["inherit", "pipe", "pipe"] : "inherit"
4876
- });
4877
- const stdout = captureOutput ? String(result.stdout ?? "") : "";
4878
- const stderr = captureOutput ? String(result.stderr ?? "") : "";
4879
- const output = [stdout, stderr].filter(Boolean).join("\n");
4880
- if (captureOutput) {
4881
- if (stdout) process.stdout.write(stdout);
4882
- if (stderr) process.stderr.write(stderr);
4883
- }
4884
- if (result.status !== 0) {
4885
- throw new DeployExecutionError(
4886
- `\u90E8\u7F72\u547D\u4EE4\u9000\u51FA\u7801 ${result.status ?? "unknown"}: ${command}`,
4887
- result.status ?? 1,
4888
- output
4889
- );
4890
- }
4891
- return output;
4892
- }
4893
5146
  function resolveFrontendDistDir(cwd) {
4894
5147
  const candidates = ["dist", "apps/web/dist"];
4895
5148
  for (const rel of candidates) {
4896
5149
  const full = path4.join(cwd, rel);
4897
5150
  try {
4898
- if (existsSync15(full) && statSync7(full).isDirectory()) {
5151
+ if (existsSync16(full) && statSync7(full).isDirectory()) {
4899
5152
  return full;
4900
5153
  }
4901
5154
  } catch {
@@ -4903,62 +5156,57 @@ function resolveFrontendDistDir(cwd) {
4903
5156
  }
4904
5157
  return path4.join(cwd, "dist");
4905
5158
  }
4906
- async function runWisdomAutoDeploy(options) {
4907
- const cwd = path4.resolve(options.cwd ?? process.cwd());
4908
- const captureOutput = options.captureOutput ?? false;
4909
- const packOnly = Boolean(options.packOnly);
4910
- const projectType = detectWisdomProjectType(cwd);
4911
- const configPath = options.configPath ?? path4.join(workspaceApmDir(cwd), "apm.config.json");
4912
- const cfg = loadApmConfig({ configPath });
5159
+ async function executeWisdomFrontendDeploy(options) {
5160
+ const { cwd, cfg, captureOutput, packOnly, archiveDeployArtifact } = options;
5161
+ const buildCmd = resolveFrontendBuildCommand(options.env, cwd);
5162
+ const distDir = resolveFrontendDistDir(cwd);
5163
+ logWisdomFrontendDeployContext({
5164
+ env: options.env,
5165
+ cwd,
5166
+ buildCmd,
5167
+ distDir,
5168
+ packOnly,
5169
+ archiveDeployArtifact
5170
+ });
5171
+ if (!buildCmd) {
5172
+ const lines = formatWisdomFrontendBuildNotConfiguredLines(
5173
+ options.env,
5174
+ path4.join(cwd, "package.json")
5175
+ );
5176
+ throw new DeployExecutionError(lines.join("\n"), 1, lines.join("\n"));
5177
+ }
4913
5178
  console.error(
4914
- `[apm] \u533B\u52A1\u5B58\u91CF\u9879\u76EE\u81EA\u52A8\u8BC6\u522B: ${projectType === "frontend" ? "\u524D\u7AEF Vue" : "\u540E\u7AEF Java"}${packOnly ? "\uFF08\u4EC5\u6253\u5305\u6A21\u5F0F\uFF09" : ""}`
5179
+ `[apm] Vue\uFF1A${buildCmd}` + (packOnly ? "\uFF08\u4EC5\u6253\u5305\uFF0C\u8DF3\u8FC7 SFTP\uFF09" : " \u2192 SFTP \u4E0A\u4F20\u5E76\u8FDC\u7A0B\u89E3\u538B" + (archiveDeployArtifact ? " \u2192 \u5E73\u53F0\u90E8\u7F72\u4EA7\u7269\u5F52\u6863" : ""))
4915
5180
  );
4916
- if (projectType === "frontend") {
4917
- if (packOnly) {
4918
- const buildCmd = resolveFrontendBuildCommand(options.env, cwd);
4919
- if (!buildCmd) {
4920
- const lines = formatWisdomFrontendBuildNotConfiguredLines(
4921
- options.env,
4922
- path4.join(cwd, "package.json")
4923
- );
4924
- throw new DeployExecutionError(lines.join("\n"), 1, lines.join("\n"));
4925
- }
4926
- console.error(`[apm] \u524D\u7AEF\u4EC5\u6253\u5305\u6A21\u5F0F\uFF1A\u6267\u884C ${buildCmd}`);
4927
- runShellCommand(buildCmd, cwd, captureOutput);
4928
- const projectName = (cfg.name ?? "").trim();
4929
- if (!projectName) {
4930
- throw new DeployExecutionError(
4931
- "\u8BF7\u5728 .apm/apm.config.json \u9876\u5C42\u914D\u7F6E name\uFF08\u4F5C\u4E3A\u90E8\u7F72\u4EA7\u7269\u9879\u76EE\u540D\uFF09",
4932
- 1
4933
- );
4934
- }
4935
- const settings2 = resolveWisdomDeployFromApmConfig(cfg);
4936
- await runWisdomSftpDeploy({
4937
- localDir: resolveFrontendDistDir(cwd),
4938
- settings: settings2,
4939
- extract: false,
4940
- projectName,
4941
- packOnly: true
4942
- });
4943
- return { projectType };
4944
- }
4945
- const deployCmd = resolveFrontendDeployCommand(options.env, cwd);
4946
- if (!deployCmd) {
4947
- const lines = formatWisdomFrontendDeployNotConfiguredLines(
4948
- options.env,
4949
- path4.join(cwd, "package.json")
4950
- );
4951
- throw new DeployExecutionError(lines.join("\n"), 1, lines.join("\n"));
4952
- }
4953
- runShellCommand(deployCmd, cwd, captureOutput);
4954
- return { projectType };
5181
+ runDeployShellCommand(buildCmd, cwd, captureOutput);
5182
+ let projectName = (cfg.name ?? "").trim();
5183
+ if ((packOnly || archiveDeployArtifact) && !projectName) {
5184
+ throw new DeployExecutionError(
5185
+ "\u8BF7\u5728 .apm/apm.config.json \u9876\u5C42\u914D\u7F6E name\uFF08\u4F5C\u4E3A\u90E8\u7F72\u4EA7\u7269\u9879\u76EE\u540D\uFF09",
5186
+ 1
5187
+ );
5188
+ }
5189
+ if (!projectName) {
5190
+ projectName = path4.basename(cwd);
4955
5191
  }
5192
+ const settings = resolveWisdomDeployFromApmConfig(cfg);
5193
+ await runWisdomSftpDeploy({
5194
+ localDir: distDir,
5195
+ settings,
5196
+ extract: !packOnly,
5197
+ projectName,
5198
+ packOnly,
5199
+ archiveDeployArtifact
5200
+ });
5201
+ }
5202
+ async function executeWisdomBackendDeployFlow(options) {
5203
+ const { cwd, cfg, packOnly } = options;
4956
5204
  const settings = resolveWisdomBackendDeployFromApmConfig(cfg);
4957
5205
  if (packOnly) {
4958
- console.error(`[apm] \u540E\u7AEF\u4EC5\u6253\u5305\u6A21\u5F0F\uFF0C\u5FFD\u7565\u73AF\u5883\u53C2\u6570: ${options.env}`);
5206
+ console.error(`[apm] Java\uFF1AMaven \u6784\u5EFA\uFF08\u4EC5\u6253\u5305\uFF0C\u5FFD\u7565\u73AF\u5883 ${options.env}\uFF09`);
4959
5207
  } else {
4960
5208
  console.error(
4961
- `[apm] \u540E\u7AEF\u90E8\u7F72\u4E0D\u533A\u5206 test/online\uFF0C\u5FFD\u7565\u73AF\u5883\u53C2\u6570: ${options.env}`
5209
+ `[apm] Java\uFF1AMaven \u6784\u5EFA\u5E76\u90E8\u7F72\uFF08\u4E0D\u533A\u5206 test/online\uFF0C\u5FFD\u7565\u73AF\u5883 ${options.env}\uFF09`
4962
5210
  );
4963
5211
  }
4964
5212
  await runWisdomBackendDeploy({
@@ -4966,11 +5214,40 @@ async function runWisdomAutoDeploy(options) {
4966
5214
  projectRoot: cwd,
4967
5215
  packOnly
4968
5216
  });
5217
+ }
5218
+ async function executeWisdomDeploy(options) {
5219
+ const cwd = path4.resolve(options.cwd);
5220
+ const captureOutput = options.captureOutput ?? false;
5221
+ const packOnly = Boolean(options.packOnly);
5222
+ const archiveDeployArtifact = Boolean(options.archiveDeployArtifact);
5223
+ const projectType = detectWisdomProjectType(cwd);
5224
+ const configPath = options.configPath ?? path4.join(workspaceApmDir(cwd), "apm.config.json");
5225
+ const cfg = loadApmConfig({ configPath });
5226
+ console.error(
5227
+ `[apm] ${projectType === "frontend" ? "Vue \u524D\u7AEF" : "Java \u540E\u7AEF"}${packOnly ? "\uFF08\u4EC5\u6253\u5305\uFF09" : ""}`
5228
+ );
5229
+ if (projectType === "frontend") {
5230
+ await executeWisdomFrontendDeploy({
5231
+ ...options,
5232
+ cwd,
5233
+ cfg,
5234
+ captureOutput,
5235
+ packOnly,
5236
+ archiveDeployArtifact
5237
+ });
5238
+ return { projectType };
5239
+ }
5240
+ await executeWisdomBackendDeployFlow({
5241
+ ...options,
5242
+ cwd,
5243
+ cfg,
5244
+ packOnly
5245
+ });
4969
5246
  return { projectType };
4970
5247
  }
4971
5248
 
4972
5249
  // src/commands/deploy/internal/deploy-baseline-sync.ts
4973
- var LOG_PREFIX = "[apm] \u90E8\u7F72\u524D\u57FA\u7EBF\u540C\u6B65:";
5250
+ var LOG_PREFIX2 = "[apm] \u90E8\u7F72\u524D\u57FA\u7EBF\u540C\u6B65:";
4974
5251
  function formatGitError(error) {
4975
5252
  return error instanceof Error ? error.message : String(error);
4976
5253
  }
@@ -5006,20 +5283,20 @@ async function restoreWorkingTree(cwd) {
5006
5283
  }
5007
5284
  async function mergeBaselineBranch(cwd, baselineBranch, currentBranch, note) {
5008
5285
  const mergeTarget = `origin/${baselineBranch}`;
5009
- note(`${LOG_PREFIX} \u5F00\u59CB\u5408\u5E76 ${mergeTarget} \u5230\u5F53\u524D\u5206\u652F ${currentBranch}`);
5286
+ note(`${LOG_PREFIX2} \u5F00\u59CB\u5408\u5E76 ${mergeTarget} \u5230\u5F53\u524D\u5206\u652F ${currentBranch}`);
5010
5287
  try {
5011
5288
  if (currentBranch === baselineBranch) {
5012
5289
  await execGit(cwd, ["merge", "--ff-only", mergeTarget]);
5013
5290
  } else {
5014
5291
  await execGit(cwd, ["merge", mergeTarget, "--no-edit"]);
5015
5292
  }
5016
- note(`${LOG_PREFIX} \u5DF2\u5408\u5E76 ${mergeTarget} \u6700\u65B0\u4EE3\u7801`);
5293
+ note(`${LOG_PREFIX2} \u5DF2\u5408\u5E76 ${mergeTarget} \u6700\u65B0\u4EE3\u7801`);
5017
5294
  return { merged: true, conflictRollback: false };
5018
5295
  } catch (error) {
5019
5296
  await abortMergeIfNeeded(cwd);
5020
- note(`${LOG_PREFIX} \u5408\u5E76 ${mergeTarget} \u5931\u8D25: ${formatGitError(error)}`);
5021
- note(`${LOG_PREFIX} \u5DF2\u6267\u884C git merge --abort \u56DE\u9000\u5230\u5408\u5E76\u524D\u72B6\u6001`);
5022
- note(`${LOG_PREFIX} \u5C06\u4F7F\u7528\u56DE\u9000\u540E\u7684\u4EE3\u7801\u7EE7\u7EED\u90E8\u7F72`);
5297
+ note(`${LOG_PREFIX2} \u5408\u5E76 ${mergeTarget} \u5931\u8D25: ${formatGitError(error)}`);
5298
+ note(`${LOG_PREFIX2} \u5DF2\u6267\u884C git merge --abort \u56DE\u9000\u5230\u5408\u5E76\u524D\u72B6\u6001`);
5299
+ note(`${LOG_PREFIX2} \u5C06\u4F7F\u7528\u56DE\u9000\u540E\u7684\u4EE3\u7801\u7EE7\u7EED\u90E8\u7F72`);
5023
5300
  return { merged: false, conflictRollback: true };
5024
5301
  }
5025
5302
  }
@@ -5027,17 +5304,17 @@ async function restoreStashIfNeeded(cwd, stashed, note) {
5027
5304
  if (!stashed) {
5028
5305
  return false;
5029
5306
  }
5030
- note(`${LOG_PREFIX} \u6B63\u5728\u6062\u590D stash \u4E2D\u7684\u672A\u63D0\u4EA4\u6539\u52A8`);
5307
+ note(`${LOG_PREFIX2} \u6B63\u5728\u6062\u590D stash \u4E2D\u7684\u672A\u63D0\u4EA4\u6539\u52A8`);
5031
5308
  try {
5032
5309
  await execGit(cwd, ["stash", "pop"], true);
5033
- note(`${LOG_PREFIX} \u5DF2\u6062\u590D stash \u4E2D\u7684\u672A\u63D0\u4EA4\u6539\u52A8`);
5310
+ note(`${LOG_PREFIX2} \u5DF2\u6062\u590D stash \u4E2D\u7684\u672A\u63D0\u4EA4\u6539\u52A8`);
5034
5311
  return false;
5035
5312
  } catch (error) {
5036
5313
  await abortMergeIfNeeded(cwd);
5037
5314
  await restoreWorkingTree(cwd);
5038
- note(`${LOG_PREFIX} \u6062\u590D stash \u5931\u8D25: ${formatGitError(error)}`);
5039
- note(`${LOG_PREFIX} \u5DF2\u56DE\u9000\u5DE5\u4F5C\u533A\uFF08git reset --hard HEAD\uFF09\uFF0Cstash \u4ECD\u4FDD\u7559`);
5040
- note(`${LOG_PREFIX} \u5C06\u4F7F\u7528\u56DE\u9000\u540E\u7684\u4EE3\u7801\u7EE7\u7EED\u90E8\u7F72`);
5315
+ note(`${LOG_PREFIX2} \u6062\u590D stash \u5931\u8D25: ${formatGitError(error)}`);
5316
+ note(`${LOG_PREFIX2} \u5DF2\u56DE\u9000\u5DE5\u4F5C\u533A\uFF08git reset --hard HEAD\uFF09\uFF0Cstash \u4ECD\u4FDD\u7559`);
5317
+ note(`${LOG_PREFIX2} \u5C06\u4F7F\u7528\u56DE\u9000\u540E\u7684\u4EE3\u7801\u7EE7\u7EED\u90E8\u7F72`);
5041
5318
  return true;
5042
5319
  }
5043
5320
  }
@@ -5056,30 +5333,30 @@ async function syncDeployBaselineBeforeDeploy(cwd) {
5056
5333
  ...partial
5057
5334
  });
5058
5335
  if (!await isGitRepo(cwd)) {
5059
- note(`${LOG_PREFIX} \u5F53\u524D\u76EE\u5F55\u4E0D\u662F git \u4ED3\u5E93\uFF0C\u8DF3\u8FC7`);
5336
+ note(`${LOG_PREFIX2} \u5F53\u524D\u76EE\u5F55\u4E0D\u662F git \u4ED3\u5E93\uFF0C\u8DF3\u8FC7`);
5060
5337
  return emptyResult();
5061
5338
  }
5062
5339
  const cfg = await tryReadApmConfig();
5063
5340
  if (!cfg || !resolveApiKey(cfg)) {
5064
- note(`${LOG_PREFIX} \u672A\u767B\u5F55\uFF0C\u8DF3\u8FC7`);
5341
+ note(`${LOG_PREFIX2} \u672A\u767B\u5F55\uFF0C\u8DF3\u8FC7`);
5065
5342
  return emptyResult();
5066
5343
  }
5067
5344
  await ensureGitRepo(cwd);
5068
5345
  const api = createApmApiClient(cfg);
5069
5346
  const baselineBranch = await resolveBaselineBranch2(cwd, api);
5070
- note(`${LOG_PREFIX} \u57FA\u7EBF\u5206\u652F ${baselineBranch}`);
5071
- note(`${LOG_PREFIX} \u6B63\u5728 fetch origin ${baselineBranch}`);
5347
+ note(`${LOG_PREFIX2} \u57FA\u7EBF\u5206\u652F ${baselineBranch}`);
5348
+ note(`${LOG_PREFIX2} \u6B63\u5728 fetch origin ${baselineBranch}`);
5072
5349
  await execGit(cwd, ["fetch", "origin", baselineBranch], true);
5073
5350
  if (!await remoteBranchExists(cwd, baselineBranch)) {
5074
5351
  throw new Error(
5075
5352
  `\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`
5076
5353
  );
5077
5354
  }
5078
- note(`${LOG_PREFIX} fetch \u5B8C\u6210`);
5355
+ note(`${LOG_PREFIX2} fetch \u5B8C\u6210`);
5079
5356
  const currentBranch = await getCurrentBranch(cwd);
5080
5357
  let stashed = false;
5081
5358
  if (await isWorkingTreeDirty(cwd)) {
5082
- note(`${LOG_PREFIX} \u68C0\u6D4B\u5230\u672A\u63D0\u4EA4\u6539\u52A8\uFF0C\u5148 stash`);
5359
+ note(`${LOG_PREFIX2} \u68C0\u6D4B\u5230\u672A\u63D0\u4EA4\u6539\u52A8\uFF0C\u5148 stash`);
5083
5360
  await execGit(cwd, [
5084
5361
  "stash",
5085
5362
  "push",
@@ -5088,7 +5365,7 @@ async function syncDeployBaselineBeforeDeploy(cwd) {
5088
5365
  "apm: deploy baseline sync"
5089
5366
  ]);
5090
5367
  stashed = true;
5091
- note(`${LOG_PREFIX} \u5DF2 stash \u672A\u63D0\u4EA4\u6539\u52A8`);
5368
+ note(`${LOG_PREFIX2} \u5DF2 stash \u672A\u63D0\u4EA4\u6539\u52A8`);
5092
5369
  }
5093
5370
  const { merged, conflictRollback } = await mergeBaselineBranch(
5094
5371
  cwd,
@@ -5098,10 +5375,10 @@ async function syncDeployBaselineBeforeDeploy(cwd) {
5098
5375
  );
5099
5376
  const stashConflict = await restoreStashIfNeeded(cwd, stashed, note);
5100
5377
  if (conflictRollback || stashConflict) {
5101
- 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`);
5378
+ note(`${LOG_PREFIX2} \u57FA\u7EBF\u540C\u6B65\u672A\u5B8C\u6210\uFF08\u5B58\u5728\u51B2\u7A81\u5E76\u5DF2\u56DE\u9000\uFF09\uFF0C\u7EE7\u7EED\u6267\u884C\u90E8\u7F72`);
5102
5379
  } else if (merged) {
5103
5380
  note(
5104
- `${LOG_PREFIX} \u5B8C\u6210\uFF08\u5F53\u524D\u5206\u652F ${currentBranch} \u5DF2\u5305\u542B ${baselineBranch} \u6700\u65B0\u4EE3\u7801\uFF09`
5381
+ `${LOG_PREFIX2} \u5B8C\u6210\uFF08\u5F53\u524D\u5206\u652F ${currentBranch} \u5DF2\u5305\u542B ${baselineBranch} \u6700\u65B0\u4EE3\u7801\uFF09`
5105
5382
  );
5106
5383
  }
5107
5384
  return {
@@ -5114,35 +5391,27 @@ async function syncDeployBaselineBeforeDeploy(cwd) {
5114
5391
  }
5115
5392
 
5116
5393
  // src/commands/deploy/deploy-execute.ts
5117
- function runShellCommand2(command, cwd, captureOutput) {
5118
- const result = spawnSync6(command, {
5119
- cwd,
5120
- shell: true,
5121
- env: buildDeployShellEnv(),
5122
- encoding: "utf8",
5123
- stdio: captureOutput ? ["inherit", "pipe", "pipe"] : "inherit"
5124
- });
5125
- const stdout = captureOutput ? String(result.stdout ?? "") : "";
5126
- const stderr = captureOutput ? String(result.stderr ?? "") : "";
5127
- const output = [stdout, stderr].filter(Boolean).join("\n");
5128
- if (captureOutput) {
5129
- if (stdout) process.stdout.write(stdout);
5130
- if (stderr) process.stderr.write(stderr);
5131
- }
5132
- if (result.status !== 0) {
5133
- throw new DeployExecutionError(
5134
- `\u90E8\u7F72\u547D\u4EE4\u9000\u51FA\u7801 ${result.status ?? "unknown"}: ${command}`,
5135
- result.status ?? 1,
5136
- output
5137
- );
5138
- }
5139
- return output;
5140
- }
5141
5394
  async function executeDeploy(options) {
5142
- const cwd = resolveWorkdirPath(options.cwd ?? process.cwd());
5395
+ const cwdInput = options.cwd.trim();
5396
+ if (!cwdInput) {
5397
+ throw new DeployExecutionError("[apm] executeDeploy \u7F3A\u5C11\u5DE5\u4F5C\u76EE\u5F55 cwd", 1);
5398
+ }
5399
+ const cwd = resolveWorkdirPath(cwdInput);
5143
5400
  const apmConfigPath = options.configPath ?? path5.join(workspaceApmDir(cwd), "apm.config.json");
5144
5401
  const captureOutput = options.captureOutput ?? false;
5145
5402
  const outputParts = [];
5403
+ logExecuteDeployContext({
5404
+ trigger: options.deployTrigger,
5405
+ env: options.env,
5406
+ cwd,
5407
+ cwdInput,
5408
+ processCwd: process.cwd(),
5409
+ apmConfigPath,
5410
+ captureOutput,
5411
+ packOnly: options.packOnly,
5412
+ archiveDeployArtifact: options.archiveDeployArtifact,
5413
+ configPathOption: options.configPath
5414
+ });
5146
5415
  try {
5147
5416
  const configSyncResult = await syncRemoteDeploymentConfig(
5148
5417
  cwd,
@@ -5165,36 +5434,29 @@ async function executeDeploy(options) {
5165
5434
  detail
5166
5435
  );
5167
5436
  }
5437
+ const preDeployLog = outputParts.join("\n");
5168
5438
  const cfg = loadApmConfig({ configPath: apmConfigPath });
5169
- if (isWisdomLegacyDeploy(cfg)) {
5170
- try {
5171
- await runWisdomAutoDeploy({
5172
- env: options.env,
5173
- cwd,
5174
- configPath: apmConfigPath,
5175
- captureOutput,
5176
- packOnly: options.packOnly
5177
- });
5178
- return outputParts.join("\n");
5179
- } catch (error) {
5180
- if (error instanceof DeployExecutionError) {
5181
- throw error;
5182
- }
5183
- const detail = error instanceof Error ? error.message : String(error);
5184
- throw new DeployExecutionError(detail, 1);
5185
- }
5186
- }
5187
- const deployCommands = cfg.deploy ?? {};
5188
- const command = deployCommands[options.env]?.trim();
5189
- if (!command) {
5190
- const lines = formatDeployNotConfiguredLines(options.env, deployCommands);
5439
+ if (!isWisdomDeployConfigured(cfg)) {
5440
+ const lines = formatMissingWisdomDeployLines();
5191
5441
  throw new DeployExecutionError(lines.join("\n"), 1, lines.join("\n"));
5192
5442
  }
5193
- const commandOutput = runShellCommand2(command, cwd, captureOutput);
5194
- if (commandOutput.trim()) {
5195
- outputParts.push(commandOutput);
5443
+ try {
5444
+ await executeWisdomDeploy({
5445
+ env: options.env,
5446
+ cwd,
5447
+ configPath: apmConfigPath,
5448
+ captureOutput,
5449
+ packOnly: options.packOnly,
5450
+ archiveDeployArtifact: options.archiveDeployArtifact
5451
+ });
5452
+ return preDeployLog;
5453
+ } catch (error) {
5454
+ if (error instanceof DeployExecutionError) {
5455
+ throw error;
5456
+ }
5457
+ const detail = error instanceof Error ? error.message : String(error);
5458
+ throw new DeployExecutionError(detail, 1);
5196
5459
  }
5197
- return outputParts.join("\n");
5198
5460
  }
5199
5461
  function printDeployExecutionError(error) {
5200
5462
  if (error.output.trim()) {
@@ -5209,9 +5471,32 @@ var DEPLOY_LOG_SYNC_INTERVAL_MS = 3e4;
5209
5471
  function resolveDeployCommand(environment, packOnly) {
5210
5472
  return packOnly ? `apm deploy ${environment} --pack-only` : `apm deploy ${environment}`;
5211
5473
  }
5474
+ function logDeploySyncFailure(error) {
5475
+ console.error(
5476
+ "[apm] deploy log sync failed:",
5477
+ error instanceof Error ? error.message : String(error)
5478
+ );
5479
+ }
5212
5480
  function createDeployLogSyncer(api, deploymentRunId) {
5213
5481
  let lastSyncedLog = "";
5214
5482
  let latestLog = "";
5483
+ let timer = null;
5484
+ const syncDelta = async (delta, append) => {
5485
+ const chunks = splitUtf8StringByMaxBytes(
5486
+ delta,
5487
+ DEPLOY_LOG_SYNC_MAX_CHUNK_BYTES
5488
+ );
5489
+ for (let index = 0; index < chunks.length; index += 1) {
5490
+ const chunk = chunks[index];
5491
+ await retryDeployLogSync(
5492
+ () => api.cli.syncTaskDeploymentLog({
5493
+ id: deploymentRunId,
5494
+ log: chunk,
5495
+ append: append || index > 0
5496
+ })
5497
+ );
5498
+ }
5499
+ };
5215
5500
  const syncIfChanged = async () => {
5216
5501
  if (!latestLog || latestLog === lastSyncedLog) {
5217
5502
  return;
@@ -5220,34 +5505,58 @@ function createDeployLogSyncer(api, deploymentRunId) {
5220
5505
  if (!delta) {
5221
5506
  return;
5222
5507
  }
5223
- await api.cli.syncTaskDeploymentLog({
5224
- id: deploymentRunId,
5225
- log: delta,
5226
- append: lastSyncedLog.length > 0
5227
- });
5508
+ await syncDelta(delta, lastSyncedLog.length > 0);
5228
5509
  lastSyncedLog = latestLog;
5229
5510
  };
5230
- const timer = setInterval(() => {
5231
- void syncIfChanged().catch((error) => {
5232
- console.error(
5233
- "[apm] deploy log sync failed:",
5234
- error instanceof Error ? error.message : String(error)
5235
- );
5236
- });
5511
+ timer = setInterval(() => {
5512
+ void syncIfChanged().catch(logDeploySyncFailure);
5237
5513
  }, DEPLOY_LOG_SYNC_INTERVAL_MS);
5238
5514
  return {
5239
5515
  updateLog(log2) {
5240
5516
  latestLog = log2;
5241
5517
  },
5242
5518
  async flush() {
5243
- clearInterval(timer);
5519
+ if (timer) {
5520
+ clearInterval(timer);
5521
+ timer = null;
5522
+ }
5244
5523
  await syncIfChanged();
5245
5524
  },
5525
+ async flushSafe() {
5526
+ if (timer) {
5527
+ clearInterval(timer);
5528
+ timer = null;
5529
+ }
5530
+ try {
5531
+ await syncIfChanged();
5532
+ return true;
5533
+ } catch (error) {
5534
+ logDeploySyncFailure(error);
5535
+ return false;
5536
+ }
5537
+ },
5246
5538
  dispose() {
5247
- clearInterval(timer);
5539
+ if (timer) {
5540
+ clearInterval(timer);
5541
+ timer = null;
5542
+ }
5248
5543
  }
5249
5544
  };
5250
5545
  }
5546
+ async function finalizeTaskDeployment(api, deploymentRunId, logSyncer, input) {
5547
+ const synced = await logSyncer.flushSafe();
5548
+ const payload = {
5549
+ id: deploymentRunId,
5550
+ status: input.status
5551
+ };
5552
+ if (input.error) {
5553
+ payload.error = input.error;
5554
+ }
5555
+ if (!synced) {
5556
+ payload.log = truncateDeployLogForComplete(input.fullLog).log;
5557
+ }
5558
+ await retryDeployLogSync(() => api.cli.completeTaskDeployment(payload));
5559
+ }
5251
5560
  function captureDeployConsole(logSyncer) {
5252
5561
  const chunks = [];
5253
5562
  const appendLog = (line) => {
@@ -5296,7 +5605,9 @@ async function handleInboundDeploy(cfg, msg, signal) {
5296
5605
  env: msg.environment,
5297
5606
  cwd: workdir,
5298
5607
  packOnly: msg.packOnly,
5299
- captureOutput: true
5608
+ captureOutput: true,
5609
+ archiveDeployArtifact: true,
5610
+ deployTrigger: "connect"
5300
5611
  };
5301
5612
  try {
5302
5613
  if (signal.aborted) return;
@@ -5304,11 +5615,9 @@ async function handleInboundDeploy(cfg, msg, signal) {
5304
5615
  if (output.trim()) {
5305
5616
  capture.appendLog(output);
5306
5617
  }
5307
- await logSyncer.flush();
5308
- await api.cli.completeTaskDeployment({
5309
- id: deploymentRunId,
5618
+ await finalizeTaskDeployment(api, deploymentRunId, logSyncer, {
5310
5619
  status: "SUCCESS",
5311
- log: capture.getLog()
5620
+ fullLog: capture.getLog()
5312
5621
  });
5313
5622
  console.log(`[apm] deploy success id=${deploymentRunId}`);
5314
5623
  } catch (error) {
@@ -5321,13 +5630,18 @@ async function handleInboundDeploy(cfg, msg, signal) {
5321
5630
  } else {
5322
5631
  capture.appendLog(deployError.message);
5323
5632
  }
5324
- await logSyncer.flush();
5325
- await api.cli.completeTaskDeployment({
5326
- id: deploymentRunId,
5327
- status: "FAILED",
5328
- error: deployError.message,
5329
- log: capture.getLog()
5330
- });
5633
+ try {
5634
+ await finalizeTaskDeployment(api, deploymentRunId, logSyncer, {
5635
+ status: "FAILED",
5636
+ fullLog: capture.getLog(),
5637
+ error: deployError.message
5638
+ });
5639
+ } catch (finalizeError) {
5640
+ console.error(
5641
+ `[apm] deploy finalize failed id=${deploymentRunId}:`,
5642
+ finalizeError instanceof Error ? finalizeError.message : String(finalizeError)
5643
+ );
5644
+ }
5331
5645
  console.error(
5332
5646
  `[apm] deploy failed id=${deploymentRunId}: ${deployError.message}`
5333
5647
  );
@@ -5609,13 +5923,13 @@ ${JSON.stringify(event, null, 2)}
5609
5923
  }
5610
5924
 
5611
5925
  // src/commands/connect/agent-session-registry.ts
5612
- import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync14, writeFileSync as writeFileSync13 } from "node:fs";
5613
- import { dirname as dirname5, resolve as resolve4 } from "node:path";
5926
+ import { existsSync as existsSync17, mkdirSync as mkdirSync8, readFileSync as readFileSync14, writeFileSync as writeFileSync13 } from "node:fs";
5927
+ import { dirname as dirname6, resolve as resolve4 } from "node:path";
5614
5928
  function registryPath(workdir, sessionId) {
5615
5929
  return resolve4(workdir, ".apm", "sessions", sessionId, "cursor-agents.json");
5616
5930
  }
5617
5931
  function readRegistry(path13) {
5618
- if (!existsSync16(path13)) {
5932
+ if (!existsSync17(path13)) {
5619
5933
  return {};
5620
5934
  }
5621
5935
  try {
@@ -5636,7 +5950,7 @@ function readRegistry(path13) {
5636
5950
  return {};
5637
5951
  }
5638
5952
  function writeRegistry(path13, registry) {
5639
- mkdirSync8(dirname5(path13), { recursive: true });
5953
+ mkdirSync8(dirname6(path13), { recursive: true });
5640
5954
  writeFileSync13(path13, `${JSON.stringify(registry, null, 2)}
5641
5955
  `, "utf8");
5642
5956
  }
@@ -6087,15 +6401,15 @@ async function ensureMessageHasReply(cfg, sessionId, messageId, fallback) {
6087
6401
  }
6088
6402
 
6089
6403
  // src/commands/connect/cli-version-sync.ts
6090
- import { existsSync as existsSync17, readFileSync as readFileSync15, writeFileSync as writeFileSync14 } from "fs";
6091
- import { join as join16 } from "path";
6404
+ import { existsSync as existsSync18, readFileSync as readFileSync15, writeFileSync as writeFileSync14 } from "fs";
6405
+ import { join as join17 } from "path";
6092
6406
  var CLI_VERSION_FILE = ".cli-version.json";
6093
6407
  function manifestPath(apmDir) {
6094
- return join16(apmDir, CLI_VERSION_FILE);
6408
+ return join17(apmDir, CLI_VERSION_FILE);
6095
6409
  }
6096
6410
  function loadManifest4(apmDir) {
6097
6411
  const path13 = toFsPath(manifestPath(apmDir));
6098
- if (!existsSync17(path13)) {
6412
+ if (!existsSync18(path13)) {
6099
6413
  return null;
6100
6414
  }
6101
6415
  try {
@@ -6163,23 +6477,53 @@ function markPullDone(sessionId, workdir) {
6163
6477
 
6164
6478
  // src/commands/connect/run-slot-pool.ts
6165
6479
  var DEFAULT_MAX_CONCURRENT = 5;
6166
- function createRunSlotPool(maxConcurrent = DEFAULT_MAX_CONCURRENT) {
6480
+ function createRunSlotPool(maxConcurrent = DEFAULT_MAX_CONCURRENT, options = {}) {
6481
+ const label = options.label ?? "task";
6482
+ const isDeployPool = label === "deploy";
6167
6483
  let active = 0;
6168
6484
  const waiters = [];
6169
- const acquire = () => {
6485
+ const logDeploy = (message) => {
6486
+ if (isDeployPool) {
6487
+ console.error(`[apm] deploy \u4E32\u884C\u9650\u6D41\uFF1A${message}`);
6488
+ }
6489
+ };
6490
+ const acquire = (meta) => {
6170
6491
  if (active < maxConcurrent) {
6171
6492
  active += 1;
6493
+ if (isDeployPool) {
6494
+ logDeploy(
6495
+ `\u5F00\u59CB\u6267\u884C${meta ? `\uFF08${meta}\uFF09` : ""}\uFF0C\u5F53\u524D\u5360\u7528 ${active}/${maxConcurrent}`
6496
+ );
6497
+ }
6172
6498
  return Promise.resolve();
6173
6499
  }
6500
+ const queueIndex = waiters.length + 1;
6501
+ if (isDeployPool) {
6502
+ logDeploy(
6503
+ `\u5E76\u53D1\u5DF2\u6EE1\uFF08${maxConcurrent}/${maxConcurrent}\uFF09\uFF0C${meta ? `${meta} ` : ""}\u8FDB\u5165\u6392\u961F\uFF0C\u524D\u65B9 ${queueIndex} \u4E2A`
6504
+ );
6505
+ }
6506
+ const queuedAt = Date.now();
6174
6507
  return new Promise((resolve5) => {
6175
6508
  waiters.push(() => {
6176
6509
  active += 1;
6510
+ const waitedMs = Date.now() - queuedAt;
6511
+ if (isDeployPool) {
6512
+ logDeploy(
6513
+ `\u6392\u961F\u7ED3\u675F\uFF0C\u5F00\u59CB\u6267\u884C${meta ? `\uFF08${meta}\uFF09` : ""}\uFF0C\u7B49\u5F85 ${Math.ceil(
6514
+ waitedMs / 1e3
6515
+ )}s\uFF0C\u5F53\u524D\u5360\u7528 ${active}/${maxConcurrent}`
6516
+ );
6517
+ }
6177
6518
  resolve5();
6178
6519
  });
6179
6520
  });
6180
6521
  };
6181
6522
  const release = () => {
6182
6523
  active = Math.max(0, active - 1);
6524
+ if (isDeployPool) {
6525
+ logDeploy(`\u6267\u884C\u69FD\u91CA\u653E\uFF0C\u5F53\u524D\u5360\u7528 ${active}/${maxConcurrent}`);
6526
+ }
6183
6527
  const next = waiters.shift();
6184
6528
  if (next) {
6185
6529
  next();
@@ -6376,6 +6720,7 @@ function attachWsHandlers(ws, ctx, onOpen) {
6376
6720
  clientMachineId,
6377
6721
  shutdownAbort,
6378
6722
  runSlots,
6723
+ deployRunSlots,
6379
6724
  activeTasks,
6380
6725
  activeRuns,
6381
6726
  pendingCancels,
@@ -6414,12 +6759,13 @@ function attachWsHandlers(ws, ctx, onOpen) {
6414
6759
  shutdownAbort.signal,
6415
6760
  perDeployController.signal
6416
6761
  ]);
6762
+ const deploySlotMeta = `deploymentRunId=${msg2.deploymentRunId} env=${msg2.environment} workdir=${msg2.workdir}`;
6417
6763
  const task2 = (async () => {
6418
- await runSlots.acquire();
6764
+ await deployRunSlots.acquire(deploySlotMeta);
6419
6765
  try {
6420
6766
  await handleInboundDeploy(cfg, msg2, signal2);
6421
6767
  } finally {
6422
- runSlots.release();
6768
+ deployRunSlots.release();
6423
6769
  }
6424
6770
  })();
6425
6771
  activeTasks.add(task2);
@@ -6577,6 +6923,7 @@ async function runConnect(options) {
6577
6923
  const lifecycleAbort = new AbortController();
6578
6924
  const shutdownAbort = new AbortController();
6579
6925
  const runSlots = createRunSlotPool();
6926
+ const deployRunSlots = createRunSlotPool(1, { label: "deploy" });
6580
6927
  const activeTasks = /* @__PURE__ */ new Set();
6581
6928
  const activeRuns = /* @__PURE__ */ new Map();
6582
6929
  const pendingCancels = /* @__PURE__ */ new Set();
@@ -6586,6 +6933,7 @@ async function runConnect(options) {
6586
6933
  clientMachineId,
6587
6934
  shutdownAbort,
6588
6935
  runSlots,
6936
+ deployRunSlots,
6589
6937
  activeTasks,
6590
6938
  activeRuns,
6591
6939
  pendingCancels,
@@ -6599,7 +6947,7 @@ async function runConnect(options) {
6599
6947
  shutdownAbort.abort();
6600
6948
  logAbortSignalStats(shutdownAbort.signal, "connect:shutdown-after-abort");
6601
6949
  try {
6602
- await Promise.race([Promise.all(activeTasks), delay2(drainMs)]);
6950
+ await Promise.race([Promise.all(activeTasks), delay3(drainMs)]);
6603
6951
  } catch {
6604
6952
  }
6605
6953
  releaseConnectLock();
@@ -6797,10 +7145,9 @@ function normalizeDeployEnvironment(env) {
6797
7145
  }
6798
7146
  async function completeTrackedDeploy(api, deploymentRunId, logSyncer, input) {
6799
7147
  logSyncer.updateLog(input.log);
6800
- await logSyncer.flush();
6801
- await api.cli.completeTaskDeployment({
6802
- id: deploymentRunId,
7148
+ await finalizeTaskDeployment(api, deploymentRunId, logSyncer, {
6803
7149
  status: input.status,
7150
+ fullLog: input.log,
6804
7151
  error: input.error
6805
7152
  });
6806
7153
  }
@@ -6825,7 +7172,7 @@ async function runDeployWithBackendTracking(options) {
6825
7172
  const run = await api.cli.createTaskDeployment({
6826
7173
  sessionId: options.sessionId,
6827
7174
  environment,
6828
- workdirPath: options.cwd ?? process.cwd()
7175
+ workdirPath: options.cwd
6829
7176
  });
6830
7177
  deploymentRunId = run.id;
6831
7178
  console.log(
@@ -6861,7 +7208,11 @@ async function runDeployWithBackendTracking(options) {
6861
7208
  originalError.apply(console, args);
6862
7209
  };
6863
7210
  try {
6864
- const output = await executeDeploy({ ...options, captureOutput: true });
7211
+ const output = await executeDeploy({
7212
+ ...options,
7213
+ captureOutput: true,
7214
+ archiveDeployArtifact: true
7215
+ });
6865
7216
  if (output.trim()) {
6866
7217
  appendLog(output);
6867
7218
  }
@@ -6904,8 +7255,8 @@ async function runDeployWithBackendTracking(options) {
6904
7255
  // src/commands/deploy/deploy.ts
6905
7256
  function registerDeployMainCommand(program) {
6906
7257
  program.command("deploy").description(
6907
- "\u7EDF\u4E00\u90E8\u7F72\uFF08\u6267\u884C\u524D\u81EA\u52A8\u540C\u6B65\u90E8\u7F72\u914D\u7F6E\u5E76\u5408\u5E76\u57FA\u7EBF\u5206\u652F\uFF09\uFF1A\u533B\u52A1\u5B58\u91CF\u81EA\u52A8\u8BC6\u522B\u524D\u540E\u7AEF\uFF1B\u5176\u4ED6\u9879\u76EE\u6309 deploy.<\u73AF\u5883> \u6267\u884C shell \u547D\u4EE4"
6908
- ).argument("<env>", "\u90E8\u7F72\u73AF\u5883\u540D\uFF08\u5982 test\u3001online\uFF09").option(
7258
+ "\u533B\u52A1\u90E8\u7F72\uFF1AVue build + SFTP / Java Maven + SSH\uFF08\u9700 apm.config.json wisdomDeploy\uFF09"
7259
+ ).argument("<env>", "\u90E8\u7F72\u73AF\u5883\u540D\uFF08\u5982 test\u3001online\uFF1BVue \u5BF9\u5E94 build:<env>\uFF09").option(
6909
7260
  "--config <path>",
6910
7261
  "apm.config.json \u8DEF\u5F84\uFF08\u9ED8\u8BA4 .apm/apm.config.json\uFF09"
6911
7262
  ).option(
@@ -6913,7 +7264,7 @@ function registerDeployMainCommand(program) {
6913
7264
  "\u6C9F\u901A\u7FA4 ID\uFF1B\u4F20\u5165\u65F6\u5728\u5E73\u53F0\u521B\u5EFA\u90E8\u7F72\u8BB0\u5F55\u5E76\u540C\u6B65\u65E5\u5FD7"
6914
7265
  ).option(
6915
7266
  "--pack-only",
6916
- "\u4EC5\u6784\u5EFA/\u6253\u5305\u5E76\u4E0A\u4F20 MinIO \u5F52\u6863\uFF0C\u4E0D\u4E0A\u4F20\u8FDC\u7A0B\u670D\u52A1\u5668\uFF08\u524D\u7AEF build:<env>\uFF0C\u540E\u7AEF Maven \u6784\u5EFA\uFF09"
7267
+ "\u4EC5\u6784\u5EFA/\u6253\u5305\uFF0C\u4E0D\u4E0A\u4F20\u8FDC\u7A0B SFTP/SSH\uFF1B\u53EF\u5F52\u6863\u5E73\u53F0\u90E8\u7F72\u4EA7\u7269"
6917
7268
  ).action(
6918
7269
  async (env, opts) => {
6919
7270
  const cwd = process.cwd();
@@ -6926,11 +7277,18 @@ function registerDeployMainCommand(program) {
6926
7277
  cwd,
6927
7278
  configPath: opts.config,
6928
7279
  sessionId,
6929
- packOnly
7280
+ packOnly,
7281
+ deployTrigger: "session"
6930
7282
  });
6931
7283
  return;
6932
7284
  }
6933
- await executeDeploy({ env, cwd, configPath: opts.config, packOnly });
7285
+ await executeDeploy({
7286
+ env,
7287
+ cwd,
7288
+ configPath: opts.config,
7289
+ packOnly,
7290
+ deployTrigger: "cli"
7291
+ });
6934
7292
  } catch (error) {
6935
7293
  if (error instanceof DeployExecutionError) {
6936
7294
  printDeployExecutionError(error);
@@ -6952,7 +7310,7 @@ import path9 from "node:path";
6952
7310
  import Docker from "dockerode";
6953
7311
 
6954
7312
  // src/commands/deploy/internal/backend-deploy/dockerode-client/connection-options.ts
6955
- import { existsSync as existsSync18, readFileSync as readFileSync16 } from "node:fs";
7313
+ import { existsSync as existsSync19, readFileSync as readFileSync16 } from "node:fs";
6956
7314
  import path6 from "node:path";
6957
7315
  function asOptionalTlsBuffer(value) {
6958
7316
  if (typeof value !== "string") {
@@ -6964,7 +7322,7 @@ function asOptionalTlsBuffer(value) {
6964
7322
  if (normalized === "") {
6965
7323
  return void 0;
6966
7324
  }
6967
- if (existsSync18(normalized)) {
7325
+ if (existsSync19(normalized)) {
6968
7326
  return readFileSync16(normalized);
6969
7327
  }
6970
7328
  const looksLikePath = /[\\/]/.test(normalized) || normalized.endsWith(".pem");
@@ -7175,7 +7533,7 @@ var DockerodeClient = class {
7175
7533
  var createDockerodeClient = (config) => new DockerodeClient(config);
7176
7534
 
7177
7535
  // src/commands/deploy/internal/backend-deploy/dockerode-client/env.ts
7178
- import { existsSync as existsSync19, readFileSync as readFileSync17, statSync as statSync8 } from "node:fs";
7536
+ import { existsSync as existsSync20, readFileSync as readFileSync17, statSync as statSync8 } from "node:fs";
7179
7537
  import path7 from "node:path";
7180
7538
  function stripSurroundingQuotes(value) {
7181
7539
  const t = value.trim();
@@ -7192,7 +7550,7 @@ function loadEnvFromFile(envFilePath) {
7192
7550
  return {};
7193
7551
  }
7194
7552
  const targetPath = path7.resolve(envFilePath);
7195
- if (!existsSync19(targetPath) || !statSync8(targetPath).isFile()) {
7553
+ if (!existsSync20(targetPath) || !statSync8(targetPath).isFile()) {
7196
7554
  return {};
7197
7555
  }
7198
7556
  const raw = readFileSync17(targetPath, "utf-8");
@@ -7366,12 +7724,12 @@ function dockerPushImage(params, cwd) {
7366
7724
  }
7367
7725
 
7368
7726
  // src/commands/deploy/internal/backend-deploy/resolve-dockerfile.ts
7369
- import { existsSync as existsSync20 } from "node:fs";
7727
+ import { existsSync as existsSync21 } from "node:fs";
7370
7728
  import path8 from "node:path";
7371
7729
  function resolveDockerBuildPaths(cwd) {
7372
7730
  const dockerfilePath = path8.join(cwd, "Dockerfile");
7373
7731
  Logger.info(`\u67E5\u627EDockerfile\u6587\u4EF6\uFF0C\u8DEF\u5F84: ${dockerfilePath}`);
7374
- if (!existsSync20(dockerfilePath)) {
7732
+ if (!existsSync21(dockerfilePath)) {
7375
7733
  throw new Error(`Dockerfile \u4E0D\u5B58\u5728\uFF1A${dockerfilePath}`);
7376
7734
  }
7377
7735
  Logger.info("\u2713 Dockerfile \u5B58\u5728");