ai-project-manage-cli 6.0.65 → 6.0.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -110,8 +110,8 @@ function toFsPath(inputPath) {
110
110
  }
111
111
  return `\\\\?\\${normalized}`;
112
112
  }
113
- function normalizeWorkdirPath(path10) {
114
- let normalized = path10.trim().replace(/\\/g, "/").normalize("NFC");
113
+ function normalizeWorkdirPath(path12) {
114
+ let normalized = path12.trim().replace(/\\/g, "/").normalize("NFC");
115
115
  if (normalized.startsWith("//?/")) {
116
116
  normalized = normalized.slice(4);
117
117
  }
@@ -326,9 +326,9 @@ function assertTemplateCopiedToApm(apmDir, workdir) {
326
326
  "sessions"
327
327
  ];
328
328
  for (const item of required) {
329
- const path10 = join2(apmDir, item);
330
- if (!existsSync(toFsPath(path10))) {
331
- throw new Error(`[apm] \u521D\u59CB\u5316\u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5C11: ${path10}`);
329
+ const path12 = join2(apmDir, item);
330
+ if (!existsSync(toFsPath(path12))) {
331
+ throw new Error(`[apm] \u521D\u59CB\u5316\u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5C11: ${path12}`);
332
332
  }
333
333
  }
334
334
  const leakedRules = join2(workdir, "rules");
@@ -613,14 +613,14 @@ function projectDocumentLocalPath(apmRoot, documentPath) {
613
613
  const normalized = normalizeLocalDocumentPath(documentPath);
614
614
  return join4(projectDocumentsDir(apmRoot), ...normalized.split("/"));
615
615
  }
616
- function normalizeLocalDocumentPath(path10) {
617
- const trimmed = path10.trim().replace(/\\/g, "/");
616
+ function normalizeLocalDocumentPath(path12) {
617
+ const trimmed = path12.trim().replace(/\\/g, "/");
618
618
  if (!trimmed || trimmed.startsWith("/") || /^[a-zA-Z]:/.test(trimmed)) {
619
- throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path10}`);
619
+ throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path12}`);
620
620
  }
621
621
  const segments = trimmed.split("/").filter(Boolean);
622
622
  if (segments.some((segment) => segment === ".." || segment === ".")) {
623
- throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path10}`);
623
+ throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path12}`);
624
624
  }
625
625
  return segments.join("/");
626
626
  }
@@ -671,15 +671,15 @@ function diffManifestPaths(remote, local) {
671
671
  (local?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
672
672
  );
673
673
  const download = [];
674
- for (const [path10, hash] of remoteMap) {
675
- if (localMap.get(path10) !== hash) {
676
- download.push(path10);
674
+ for (const [path12, hash] of remoteMap) {
675
+ if (localMap.get(path12) !== hash) {
676
+ download.push(path12);
677
677
  }
678
678
  }
679
679
  const deleteLocal = [];
680
- for (const path10 of localMap.keys()) {
681
- if (!remoteMap.has(path10)) {
682
- deleteLocal.push(path10);
680
+ for (const path12 of localMap.keys()) {
681
+ if (!remoteMap.has(path12)) {
682
+ deleteLocal.push(path12);
683
683
  }
684
684
  }
685
685
  return { download, deleteLocal };
@@ -739,8 +739,8 @@ ${diagnostic ?? ""}`
739
739
  }
740
740
  }
741
741
  let deleted = 0;
742
- for (const path10 of deleteLocal) {
743
- const absPath = toFsPath(projectDocumentLocalPath(targetApmDir, path10));
742
+ for (const path12 of deleteLocal) {
743
+ const absPath = toFsPath(projectDocumentLocalPath(targetApmDir, path12));
744
744
  if (existsSync2(absPath)) {
745
745
  rmSync(absPath, { force: true });
746
746
  deleted += 1;
@@ -785,21 +785,21 @@ async function syncRepositoryProjectDocumentsPush(cfg, workdirPath, apmRoot) {
785
785
  ])
786
786
  );
787
787
  let synced = 0;
788
- for (const path10 of localPaths) {
789
- const absPath = toFsPath(projectDocumentLocalPath(targetApmDir, path10));
788
+ for (const path12 of localPaths) {
789
+ const absPath = toFsPath(projectDocumentLocalPath(targetApmDir, path12));
790
790
  const content = readFileSync3(absPath, "utf8");
791
791
  const contentHash = hashLocalFileContent(content);
792
- if (remoteHashByPath.get(path10) === contentHash) {
792
+ if (remoteHashByPath.get(path12) === contentHash) {
793
793
  continue;
794
794
  }
795
795
  await api.cli.upsertRepositoryProjectDocument({
796
796
  repositoryId,
797
- path: path10,
797
+ path: path12,
798
798
  content,
799
- description: remoteDescriptionByPath.get(path10) ?? void 0
799
+ description: remoteDescriptionByPath.get(path12) ?? void 0
800
800
  });
801
801
  synced += 1;
802
- console.log(`[apm] \u5DF2\u540C\u6B65\u4ED3\u5E93\u9879\u76EE\u6587\u6863: ${path10}`);
802
+ console.log(`[apm] \u5DF2\u540C\u6B65\u4ED3\u5E93\u9879\u76EE\u6587\u6863: ${path12}`);
803
803
  }
804
804
  if (synced === 0) {
805
805
  console.log("[apm] \u4ED3\u5E93\u9879\u76EE\u6587\u6863\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u63A8\u9001");
@@ -1395,13 +1395,13 @@ async function downloadAttachment(cfg, attachmentId) {
1395
1395
  return Buffer.from(await res.arrayBuffer());
1396
1396
  }
1397
1397
  function loadManifest(dir) {
1398
- const path10 = join6(dir, MANIFEST_FILE2);
1399
- if (!existsSync4(path10)) {
1398
+ const path12 = join6(dir, MANIFEST_FILE2);
1399
+ if (!existsSync4(path12)) {
1400
1400
  return { version: 1, attachments: {} };
1401
1401
  }
1402
1402
  try {
1403
1403
  const parsed = JSON.parse(
1404
- readFileSync5(path10, "utf8")
1404
+ readFileSync5(path12, "utf8")
1405
1405
  );
1406
1406
  if (parsed?.version === 1 && parsed.attachments && typeof parsed.attachments === "object") {
1407
1407
  return parsed;
@@ -1482,8 +1482,8 @@ function sanitizeSkillDirName(name) {
1482
1482
  function listBaseSkillDirNames() {
1483
1483
  if (!existsSync5(BASE_SKILLS_TEMPLATE_DIR)) return [];
1484
1484
  return readdirSync3(BASE_SKILLS_TEMPLATE_DIR).filter((name) => {
1485
- const path10 = join7(BASE_SKILLS_TEMPLATE_DIR, name);
1486
- return statSync2(path10).isDirectory();
1485
+ const path12 = join7(BASE_SKILLS_TEMPLATE_DIR, name);
1486
+ return statSync2(path12).isDirectory();
1487
1487
  });
1488
1488
  }
1489
1489
  function syncAgentsGuide(apmDir) {
@@ -1495,8 +1495,8 @@ function syncAgentsGuide(apmDir) {
1495
1495
  function listBaseRuleFileNames() {
1496
1496
  if (!existsSync5(BASE_RULES_TEMPLATE_DIR)) return [];
1497
1497
  return readdirSync3(BASE_RULES_TEMPLATE_DIR).filter((name) => {
1498
- const path10 = join7(BASE_RULES_TEMPLATE_DIR, name);
1499
- return statSync2(path10).isFile();
1498
+ const path12 = join7(BASE_RULES_TEMPLATE_DIR, name);
1499
+ return statSync2(path12).isFile();
1500
1500
  });
1501
1501
  }
1502
1502
  function syncBaseRules(rulesDir) {
@@ -1559,13 +1559,13 @@ function ruleLocalFileName(ruleName) {
1559
1559
  return `${sanitized}.md`;
1560
1560
  }
1561
1561
  function loadManifest2(rulesDir) {
1562
- const path10 = join8(rulesDir, MANIFEST_FILE3);
1563
- if (!existsSync6(toFsPath(path10))) {
1562
+ const path12 = join8(rulesDir, MANIFEST_FILE3);
1563
+ if (!existsSync6(toFsPath(path12))) {
1564
1564
  return { version: 1, rules: {} };
1565
1565
  }
1566
1566
  try {
1567
1567
  const parsed = JSON.parse(
1568
- readFileSync6(toFsPath(path10), "utf8")
1568
+ readFileSync6(toFsPath(path12), "utf8")
1569
1569
  );
1570
1570
  if (parsed?.version === 1 && parsed.rules && typeof parsed.rules === "object") {
1571
1571
  return parsed;
@@ -2102,7 +2102,7 @@ async function runUpdateMessageStatus(options) {
2102
2102
  }
2103
2103
 
2104
2104
  // src/commands/connect.ts
2105
- import { spawnSync as spawnSync2 } from "child_process";
2105
+ import { spawnSync as spawnSync4 } from "child_process";
2106
2106
  import WebSocket from "ws";
2107
2107
 
2108
2108
  // src/ws/protocol.ts
@@ -2226,19 +2226,918 @@ function validateAgentWsMessage(value, kind) {
2226
2226
 
2227
2227
  // src/commands/connect/deploy-run.ts
2228
2228
  import { spawn } from "node:child_process";
2229
- import { readFileSync as readFileSync9 } from "node:fs";
2229
+ import { existsSync as existsSync14, readFileSync as readFileSync12 } from "node:fs";
2230
2230
  import { join as join13 } from "node:path";
2231
+
2232
+ // src/commands/deploy/internal/wisdom-auto-deploy.ts
2233
+ import { existsSync as existsSync13, readFileSync as readFileSync11 } from "node:fs";
2234
+ import path3 from "node:path";
2235
+ import { spawnSync as spawnSync3 } from "node:child_process";
2236
+
2237
+ // src/commands/deploy/internal/apm-config.ts
2238
+ import { existsSync as existsSync11, readFileSync as readFileSync9 } from "node:fs";
2239
+ import { resolve as resolve3 } from "node:path";
2240
+ function loadApmConfig(options) {
2241
+ const p = resolve3(
2242
+ process.cwd(),
2243
+ options?.configPath ?? resolve3(workspaceApmDir(), "apm.config.json")
2244
+ );
2245
+ if (!existsSync11(p)) {
2246
+ console.error(`\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\uFF1A${p}`);
2247
+ process.exit(1);
2248
+ }
2249
+ try {
2250
+ const raw = readFileSync9(p, "utf8");
2251
+ return JSON.parse(raw);
2252
+ } catch (e) {
2253
+ console.error(`\u65E0\u6CD5\u89E3\u6790 apm.config.json\uFF1A${p}`, e);
2254
+ process.exit(1);
2255
+ }
2256
+ }
2257
+ function req(v, field) {
2258
+ if (v === void 0 || v === null || typeof v === "string" && !v.trim()) {
2259
+ console.error(`apm.config.json \u4E2D backendDeploy.${field} \u4E0D\u80FD\u4E3A\u7A7A`);
2260
+ process.exit(1);
2261
+ }
2262
+ return v;
2263
+ }
2264
+ function reqTopLevelName(cfg) {
2265
+ const n = (cfg.name ?? "").trim();
2266
+ if (!n) {
2267
+ console.error(
2268
+ "\u8BF7\u5728 apm.config.json \u9876\u5C42\u914D\u7F6E name\uFF08\u4E0E\u524D\u7AEF\u5236\u54C1\u524D\u7F00\u3001\u540E\u7AEF\u955C\u50CF\u540D\u5171\u7528\uFF09"
2269
+ );
2270
+ process.exit(1);
2271
+ }
2272
+ return n;
2273
+ }
2274
+ function reqBackendPositiveInt(v, field) {
2275
+ const n = Number(v);
2276
+ if (!Number.isFinite(n) || !Number.isInteger(n) || n < 1) {
2277
+ console.error(`apm.config.json \u4E2D backendDeploy.${field} \u987B\u4E3A\u6B63\u6574\u6570`);
2278
+ process.exit(1);
2279
+ }
2280
+ return n;
2281
+ }
2282
+ function resolveBackendDeployFromApmConfig(cfg) {
2283
+ const b = cfg.backendDeploy ?? {};
2284
+ const protoRaw = req(b.remoteProtocol, "remoteProtocol").trim().toLowerCase();
2285
+ if (protoRaw !== "http" && protoRaw !== "https") {
2286
+ console.error(
2287
+ "apm.config.json \u4E2D backendDeploy.remoteProtocol \u53EA\u80FD\u4E3A http \u6216 https"
2288
+ );
2289
+ process.exit(1);
2290
+ }
2291
+ const remoteProtocol = protoRaw;
2292
+ let mappings = [];
2293
+ const rawPorts = b.containerPortsMappings;
2294
+ if (rawPorts !== void 0 && rawPorts !== null) {
2295
+ if (!Array.isArray(rawPorts)) {
2296
+ console.error(
2297
+ "apm.config.json \u4E2D backendDeploy.containerPortsMappings \u987B\u4E3A\u5B57\u7B26\u4E32\u6570\u7EC4\uFF08\u53EF\u4E3A\u7A7A\uFF0C\u7701\u7565\u5219\u4E0D\u52A0\u7AEF\u53E3\u6620\u5C04\uFF09"
2298
+ );
2299
+ process.exit(1);
2300
+ }
2301
+ mappings = rawPorts.map((x) => String(x).trim()).filter(Boolean);
2302
+ }
2303
+ return {
2304
+ name: reqTopLevelName(cfg),
2305
+ registryHost: req(b.registryHost, "registryHost").trim(),
2306
+ registryNamespace: req(b.registryNamespace, "registryNamespace").trim(),
2307
+ registryUser: req(b.registryUser, "registryUser").trim(),
2308
+ registryPassword: req(b.registryPassword, "registryPassword").trim(),
2309
+ remoteHost: req(b.remoteHost, "remoteHost").trim(),
2310
+ remotePort: reqBackendPositiveInt(b.remotePort, "remotePort"),
2311
+ remoteProtocol,
2312
+ caPath: b.caPath?.trim(),
2313
+ certPath: b.certPath?.trim(),
2314
+ keyPath: b.keyPath?.trim(),
2315
+ envFilePath: typeof b.envFilePath === "string" ? b.envFilePath.trim() : "",
2316
+ containerPortsMappings: mappings,
2317
+ dockerNetwork: b.dockerNetwork?.trim() || void 0
2318
+ };
2319
+ }
2320
+ function reqFe(v, field) {
2321
+ if (v === void 0 || v === null || typeof v === "string" && !v.trim()) {
2322
+ console.error(`apm.config.json \u4E2D frontendDeploy.${field} \u4E0D\u80FD\u4E3A\u7A7A`);
2323
+ process.exit(1);
2324
+ }
2325
+ return v;
2326
+ }
2327
+ function resolveFrontendDeployFromApmConfig(cfg) {
2328
+ const f = cfg.frontendDeploy ?? {};
2329
+ const port = Number(f.port);
2330
+ return {
2331
+ endpoint: reqFe(f.endpoint, "endpoint").trim(),
2332
+ port: Number.isFinite(port) && port > 0 ? port : 9e3,
2333
+ useSsl: Boolean(f.useSsl),
2334
+ accessKey: reqFe(f.accessKey, "accessKey").trim(),
2335
+ secretKey: reqFe(f.secretKey, "secretKey").trim(),
2336
+ bucket: reqFe(f.bucket, "bucket").trim()
2337
+ };
2338
+ }
2339
+ function reqWd(v, field) {
2340
+ if (v === void 0 || v === null || typeof v === "string" && !v.trim()) {
2341
+ console.error(`apm.config.json \u4E2D wisdomDeploy.${field} \u4E0D\u80FD\u4E3A\u7A7A`);
2342
+ process.exit(1);
2343
+ }
2344
+ return v;
2345
+ }
2346
+ function reqWisdomPositiveInt(v, field) {
2347
+ const n = Number(v);
2348
+ if (!Number.isFinite(n) || !Number.isInteger(n) || n < 1) {
2349
+ console.error(`apm.config.json \u4E2D wisdomDeploy.${field} \u987B\u4E3A\u6B63\u6574\u6570`);
2350
+ process.exit(1);
2351
+ }
2352
+ return n;
2353
+ }
2354
+ function resolveWisdomDeployFromApmConfig(cfg) {
2355
+ const w = cfg.wisdomDeploy ?? {};
2356
+ return {
2357
+ host: reqWd(w.host, "host").trim(),
2358
+ port: reqWisdomPositiveInt(w.port, "port"),
2359
+ username: reqWd(w.username, "username").trim(),
2360
+ password: reqWd(w.password, "password").trim(),
2361
+ remotePath: reqWd(w.remotePath, "remotePath").trim()
2362
+ };
2363
+ }
2364
+ function reqHc(v, field) {
2365
+ if (v === void 0 || v === null || typeof v === "string" && !v.trim()) {
2366
+ console.error(`apm.config.json \u4E2D healthCheck.${field} \u4E0D\u80FD\u4E3A\u7A7A`);
2367
+ process.exit(1);
2368
+ }
2369
+ return v;
2370
+ }
2371
+ function reqWb(v, field) {
2372
+ if (v === void 0 || v === null || typeof v === "string" && !v.trim()) {
2373
+ console.error(`apm.config.json \u4E2D wisdomDeploy.${field} \u4E0D\u80FD\u4E3A\u7A7A`);
2374
+ process.exit(1);
2375
+ }
2376
+ return v;
2377
+ }
2378
+ function resolveWisdomBackendDeployFromApmConfig(cfg) {
2379
+ const projectName = reqTopLevelName(cfg);
2380
+ const w = cfg.wisdomDeploy ?? {};
2381
+ const h = cfg.healthCheck ?? {};
2382
+ const jarPath = reqWb(w.jarPath, "jarPath").trim();
2383
+ const remoteAppDir = posixDirname(jarPath);
2384
+ const healthPort = Number(reqHc(h.port, "port"));
2385
+ const healthTimeout = Number(reqHc(h.timeout, "timeout"));
2386
+ if (!Number.isFinite(healthPort) || !Number.isInteger(healthPort) || healthPort < 1) {
2387
+ console.error("apm.config.json \u4E2D healthCheck.port \u987B\u4E3A\u6B63\u6574\u6570");
2388
+ process.exit(1);
2389
+ }
2390
+ if (!Number.isFinite(healthTimeout) || !Number.isInteger(healthTimeout) || healthTimeout < 1) {
2391
+ console.error("apm.config.json \u4E2D healthCheck.timeout \u987B\u4E3A\u6B63\u6574\u6570");
2392
+ process.exit(1);
2393
+ }
2394
+ return {
2395
+ projectName,
2396
+ host: reqWd(w.host, "host").trim(),
2397
+ port: reqWisdomPositiveInt(w.port, "port"),
2398
+ username: reqWd(w.username, "username").trim(),
2399
+ password: reqWd(w.password, "password").trim(),
2400
+ remoteVueDistDir: reqWd(w.remotePath, "remotePath").trim(),
2401
+ remoteAppDir,
2402
+ remoteLibDir: `${remoteAppDir}/lib`,
2403
+ startupJar: posixBasename(jarPath),
2404
+ packageName: `${projectName}.jar.zip`,
2405
+ mavenLocalRepo: reqWb(w.mavenLocalRepo, "mavenLocalRepo").trim(),
2406
+ healthCheckPort: healthPort,
2407
+ healthCheckContext: reqHc(h.context, "context").trim(),
2408
+ healthCheckTimeout: healthTimeout
2409
+ };
2410
+ }
2411
+ function posixDirname(p) {
2412
+ const normalized = p.replace(/\\/g, "/");
2413
+ const idx = normalized.lastIndexOf("/");
2414
+ if (idx <= 0) {
2415
+ return normalized.startsWith("/") ? "/" : ".";
2416
+ }
2417
+ return normalized.slice(0, idx);
2418
+ }
2419
+ function posixBasename(p) {
2420
+ const normalized = p.replace(/\\/g, "/");
2421
+ const idx = normalized.lastIndexOf("/");
2422
+ return idx >= 0 ? normalized.slice(idx + 1) : normalized;
2423
+ }
2424
+
2425
+ // src/commands/deploy/internal/wisdom-backend-deploy.ts
2426
+ import {
2427
+ existsSync as existsSync12,
2428
+ mkdirSync as mkdirSync5,
2429
+ readdirSync as readdirSync5,
2430
+ readFileSync as readFileSync10,
2431
+ statSync as statSync5,
2432
+ writeFileSync as writeFileSync10
2433
+ } from "node:fs";
2434
+ import { spawnSync as spawnSync2 } from "node:child_process";
2435
+ import path2 from "node:path";
2436
+ import { Client } from "ssh2";
2437
+ import JSZip2 from "jszip";
2438
+ import SftpClient2 from "ssh2-sftp-client";
2439
+
2440
+ // src/commands/deploy/internal/wisdom-sftp.ts
2441
+ import { readdir, readFile, unlink, writeFile } from "node:fs/promises";
2442
+ import path from "node:path";
2443
+ import JSZip from "jszip";
2444
+ import SftpClient from "ssh2-sftp-client";
2445
+ async function addDirToZip(dir, zipFolder) {
2446
+ const entries = await readdir(dir, { withFileTypes: true });
2447
+ for (const entry of entries) {
2448
+ const fullPath = path.join(dir, entry.name);
2449
+ if (entry.isDirectory()) {
2450
+ const folder = zipFolder.folder(entry.name);
2451
+ if (folder) {
2452
+ await addDirToZip(fullPath, folder);
2453
+ }
2454
+ } else {
2455
+ const content = await readFile(fullPath);
2456
+ zipFolder.file(entry.name, content);
2457
+ }
2458
+ }
2459
+ }
2460
+ async function zipDirectory(distDir, zipPath) {
2461
+ console.error(`\u538B\u7F29\u76EE\u5F55: ${distDir}`);
2462
+ const zip = new JSZip();
2463
+ await addDirToZip(distDir, zip);
2464
+ const content = await zip.generateAsync({
2465
+ type: "nodebuffer",
2466
+ compression: "DEFLATE",
2467
+ compressionOptions: { level: 6 }
2468
+ });
2469
+ await writeFile(zipPath, content);
2470
+ const sizeMb = (content.length / 1024 / 1024).toFixed(2);
2471
+ console.error(`\u5DF2\u751F\u6210: ${zipPath} (${sizeMb} MB)`);
2472
+ return content.length;
2473
+ }
2474
+ async function ensureRemoteDir(sftp, dir) {
2475
+ const parts = dir.replace(/\\/g, "/").split("/").filter(Boolean);
2476
+ let current = dir.startsWith("/") ? "" : ".";
2477
+ for (const part of parts) {
2478
+ current = current ? `${current}/${part}` : `/${part}`;
2479
+ try {
2480
+ await sftp.mkdir(current, true);
2481
+ } catch {
2482
+ }
2483
+ }
2484
+ }
2485
+ function execCommand(client, command) {
2486
+ return new Promise((resolve5, reject) => {
2487
+ client.exec(command, (err, stream) => {
2488
+ if (err) return reject(err);
2489
+ let stdout = "";
2490
+ let stderr = "";
2491
+ stream.on("close", (code) => {
2492
+ if (code !== 0) {
2493
+ reject(new Error(`\u8FDC\u7A0B\u547D\u4EE4\u5931\u8D25 (${code}): ${stderr || stdout}`));
2494
+ return;
2495
+ }
2496
+ resolve5(stdout);
2497
+ }).on("data", (data) => {
2498
+ stdout += data.toString();
2499
+ });
2500
+ stream.stderr.on("data", (data) => {
2501
+ stderr += data.toString();
2502
+ });
2503
+ });
2504
+ });
2505
+ }
2506
+ function shellSingleQuote(value) {
2507
+ return `'${value.replace(/'/g, `'"'"'`)}'`;
2508
+ }
2509
+ function buildClearRemoteDirExceptZipCommand(target) {
2510
+ const normalized = target.replace(/\\/g, "/").replace(/\/$/, "");
2511
+ const quotedTarget = shellSingleQuote(normalized);
2512
+ const script = [
2513
+ `T=${quotedTarget}`,
2514
+ "S=$(mktemp -d)",
2515
+ 'while IFS= read -r -d "" z; do r="${z#${T}/}"',
2516
+ 'mkdir -p "${S}/$(dirname "$r")"',
2517
+ 'mv "$z" "${S}/${r}"',
2518
+ 'done < <(find "$T" -mindepth 1 -type f -iname "*.zip" -print0)',
2519
+ 'rm -rf "${T}"/*',
2520
+ 'while IFS= read -r -d "" r; do r="${r#./}"',
2521
+ 'mkdir -p "${T}/$(dirname "$r")"',
2522
+ 'mv "${S}/${r}" "${T}/${r}"',
2523
+ 'done < <(cd "$S" 2>/dev/null && find . -type f -print0)',
2524
+ 'rm -rf "$S"'
2525
+ ].join("; ");
2526
+ return `bash -c ${shellSingleQuote(script)}`;
2527
+ }
2528
+ async function uploadAndMaybeExtract(settings, localZip, extract) {
2529
+ const remoteZipPath = `${settings.remotePath.replace(/\/$/, "")}/dist.zip`;
2530
+ console.error(
2531
+ `\u8FDE\u63A5 ${settings.username}@${settings.host}:${settings.port} ...`
2532
+ );
2533
+ const sftp = new SftpClient();
2534
+ try {
2535
+ await sftp.connect({
2536
+ host: settings.host,
2537
+ port: settings.port,
2538
+ username: settings.username,
2539
+ password: settings.password,
2540
+ readyTimeout: 2e4,
2541
+ tryKeyboard: true
2542
+ });
2543
+ await ensureRemoteDir(sftp, settings.remotePath);
2544
+ await sftp.put(localZip, remoteZipPath);
2545
+ console.error(` \u2713 ${localZip} -> ${remoteZipPath}`);
2546
+ if (extract) {
2547
+ const target = settings.remotePath.replace(/\/$/, "");
2548
+ const client = sftp.client;
2549
+ const clearCmd = buildClearRemoteDirExceptZipCommand(target);
2550
+ console.error(`\u8FDC\u7A0B\u6E05\u7406\u89E3\u538B\u76EE\u5F55: ${clearCmd}`);
2551
+ await execCommand(client, clearCmd);
2552
+ const unzipCmd = `unzip -o "${remoteZipPath}" -d "${target}"`;
2553
+ console.error(`\u8FDC\u7A0B\u89E3\u538B: ${unzipCmd}`);
2554
+ await execCommand(client, unzipCmd);
2555
+ console.error("\u8FDC\u7A0B\u89E3\u538B\u5B8C\u6210!");
2556
+ }
2557
+ console.error(extract ? "\u90E8\u7F72\u5B8C\u6210!" : "\u4E0A\u4F20\u5B8C\u6210!");
2558
+ } finally {
2559
+ await sftp.end();
2560
+ }
2561
+ }
2562
+ async function runWisdomSftpDeploy(params) {
2563
+ const zipPath = path.join(params.localDir, "..", ".deploy-sftp-dist.zip");
2564
+ const resolvedZipPath = path.resolve(zipPath);
2565
+ let zipSizeBytes = 0;
2566
+ try {
2567
+ zipSizeBytes = await zipDirectory(params.localDir, resolvedZipPath);
2568
+ await uploadAndMaybeExtract(
2569
+ params.settings,
2570
+ resolvedZipPath,
2571
+ params.extract
2572
+ );
2573
+ } finally {
2574
+ try {
2575
+ await unlink(resolvedZipPath);
2576
+ console.error(`\u5DF2\u6E05\u7406\u672C\u5730\u4E34\u65F6\u6587\u4EF6: ${resolvedZipPath}`);
2577
+ } catch {
2578
+ }
2579
+ }
2580
+ return {
2581
+ ok: true,
2582
+ localDir: params.localDir,
2583
+ host: params.settings.host,
2584
+ remotePath: params.settings.remotePath,
2585
+ zipSizeBytes,
2586
+ extracted: params.extract
2587
+ };
2588
+ }
2589
+
2590
+ // src/commands/deploy/internal/wisdom-backend-deploy.ts
2591
+ var SPRINGBOOT_SCRIPT = "springboot.sh";
2592
+ var MAVEN_MODULE = "jeecg-module-system/jeecg-system-start";
2593
+ var MAVEN_PROFILE = "dev";
2594
+ function log(message) {
2595
+ const now = /* @__PURE__ */ new Date();
2596
+ const hh = String(now.getHours()).padStart(2, "0");
2597
+ const mm = String(now.getMinutes()).padStart(2, "0");
2598
+ const ss = String(now.getSeconds()).padStart(2, "0");
2599
+ console.error(`[${hh}:${mm}:${ss}] ${message}`);
2600
+ }
2601
+ function fail(message) {
2602
+ log(`ERROR: ${message}`);
2603
+ process.exit(1);
2604
+ }
2605
+ function expandPath(pathStr) {
2606
+ return path2.resolve(pathStr.replace(/^~(?=\/|$)/, process.env.HOME ?? ""));
2607
+ }
2608
+ function deployCacheDir() {
2609
+ return path2.join(workspaceApmDir(), "deploy", ".deploy_cache");
2610
+ }
2611
+ function manifestFilePath() {
2612
+ return path2.join(deployCacheDir(), "manifest.json");
2613
+ }
2614
+ function getTargetDir(projectRoot) {
2615
+ return path2.join(projectRoot, MAVEN_MODULE, "target");
2616
+ }
2617
+ function relativeKey(projectRoot, filePath) {
2618
+ return path2.relative(projectRoot, filePath).split(path2.sep).join("/");
2619
+ }
2620
+ function fileSignature(filePath) {
2621
+ const stat2 = statSync5(filePath);
2622
+ return { size: stat2.size, mtime: stat2.mtimeMs / 1e3 };
2623
+ }
2624
+ function loadManifest3() {
2625
+ const manifestPath2 = manifestFilePath();
2626
+ if (!existsSync12(manifestPath2)) {
2627
+ return {};
2628
+ }
2629
+ return JSON.parse(readFileSync10(manifestPath2, "utf8"));
2630
+ }
2631
+ function saveManifest3(manifest) {
2632
+ const dir = deployCacheDir();
2633
+ mkdirSync5(dir, { recursive: true });
2634
+ writeFileSync10(manifestFilePath(), JSON.stringify(manifest, null, 2), "utf8");
2635
+ }
2636
+ function isProjectLibJar(jarName) {
2637
+ return jarName.startsWith("jeecg-");
2638
+ }
2639
+ function shouldUploadLibFile(localPath, remoteAttr, manifest, projectRoot) {
2640
+ if (!remoteAttr) {
2641
+ return [false, "\u8FDC\u7A0B\u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7"];
2642
+ }
2643
+ const localSize = statSync5(localPath).size;
2644
+ const remoteSize = remoteAttr.size;
2645
+ if (localSize !== remoteSize) {
2646
+ return [true, `\u5927\u5C0F\u53D8\u5316 ${remoteSize} -> ${localSize}`];
2647
+ }
2648
+ if (isProjectLibJar(path2.basename(localPath)) && manifest) {
2649
+ const key = relativeKey(projectRoot, localPath);
2650
+ const current = fileSignature(localPath);
2651
+ const previous = manifest[key];
2652
+ if (!previous) {
2653
+ return [true, "\u9879\u76EE\u6A21\u5757\u672A\u8BB0\u5F55"];
2654
+ }
2655
+ if (previous.size !== current.size) {
2656
+ return [true, "\u9879\u76EE\u6A21\u5757\u5927\u5C0F\u53D8\u5316"];
2657
+ }
2658
+ if (previous.mtime < current.mtime) {
2659
+ return [true, "\u9879\u76EE\u6A21\u5757\u91CD\u65B0\u6784\u5EFA"];
2660
+ }
2661
+ }
2662
+ return [false, "\u5927\u5C0F\u4E00\u81F4\uFF0C\u8DF3\u8FC7"];
2663
+ }
2664
+ function listLibFilesToUpload(localLibDir, remoteStats, projectRoot, manifest = null) {
2665
+ const entries = [];
2666
+ const jarFiles = readdirSync5(localLibDir).filter((name) => name.endsWith(".jar")).sort();
2667
+ for (const jarName of jarFiles) {
2668
+ const jarPath = path2.join(localLibDir, jarName);
2669
+ const remoteAttr = remoteStats.get(jarName);
2670
+ const [shouldUpload, reason] = shouldUploadLibFile(
2671
+ jarPath,
2672
+ remoteAttr,
2673
+ manifest,
2674
+ projectRoot
2675
+ );
2676
+ if (shouldUpload) {
2677
+ entries.push({ path: jarPath, arcname: jarName, reason });
2678
+ }
2679
+ }
2680
+ return entries;
2681
+ }
2682
+ function updateManifestEntries(manifest, entries, projectRoot) {
2683
+ for (const entry of entries) {
2684
+ manifest[relativeKey(projectRoot, entry.path)] = fileSignature(entry.path);
2685
+ }
2686
+ return manifest;
2687
+ }
2688
+ async function createUpdatePackage(entries, packageName) {
2689
+ const dir = deployCacheDir();
2690
+ mkdirSync5(dir, { recursive: true });
2691
+ const zipPath = path2.join(dir, packageName);
2692
+ log(`\u521B\u5EFA\u66F4\u65B0\u5305: ${path2.basename(zipPath)}\uFF08${entries.length} \u4E2A\u6587\u4EF6\uFF09`);
2693
+ const zip = new JSZip2();
2694
+ for (const entry of entries) {
2695
+ const content = readFileSync10(entry.path);
2696
+ zip.file(entry.arcname, content);
2697
+ log(` \u6253\u5305: ${entry.arcname} (${entry.reason})`);
2698
+ }
2699
+ const buffer = await zip.generateAsync({
2700
+ type: "nodebuffer",
2701
+ compression: "DEFLATE",
2702
+ compressionOptions: { level: 6 }
2703
+ });
2704
+ writeFileSync10(zipPath, buffer);
2705
+ return zipPath;
2706
+ }
2707
+ function getMvnExecutable() {
2708
+ const isWin = process.platform === "win32";
2709
+ const candidates = isWin ? ["mvn.cmd", "mvn.bat", "mvn"] : ["mvn"];
2710
+ for (const name of candidates) {
2711
+ const result = spawnSync2(isWin ? "where" : "which", [name], {
2712
+ encoding: "utf8"
2713
+ });
2714
+ if (result.status === 0 && result.stdout.trim()) {
2715
+ return result.stdout.trim().split(/\r?\n/)[0];
2716
+ }
2717
+ }
2718
+ fail("\u672A\u627E\u5230 mvn \u547D\u4EE4\uFF0C\u8BF7\u786E\u8BA4 Maven \u5DF2\u5B89\u88C5\u5E76\u52A0\u5165 PATH");
2719
+ }
2720
+ function runMavenBuild(projectRoot, mavenLocalRepo) {
2721
+ const mavenRepo = expandPath(mavenLocalRepo);
2722
+ const cmd = [
2723
+ getMvnExecutable(),
2724
+ "clean",
2725
+ "package",
2726
+ `-P${MAVEN_PROFILE}`,
2727
+ `-Dmaven.repo.local=${mavenRepo}`,
2728
+ "-DskipTests"
2729
+ ];
2730
+ log(`\u5F00\u59CB Maven \u6784\u5EFA: ${cmd.join(" ")}`);
2731
+ log(`Maven \u672C\u5730\u4ED3\u5E93: ${mavenRepo}`);
2732
+ const result = spawnSync2(cmd[0], cmd.slice(1), {
2733
+ cwd: projectRoot,
2734
+ stdio: "inherit"
2735
+ });
2736
+ if (result.status !== 0) {
2737
+ fail(`Maven \u6784\u5EFA\u5931\u8D25\uFF0C\u9000\u51FA\u7801: ${result.status ?? 1}`);
2738
+ }
2739
+ }
2740
+ function locateLibDir(projectRoot) {
2741
+ const targetDir = getTargetDir(projectRoot);
2742
+ if (!existsSync12(targetDir)) {
2743
+ fail(`\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728: ${targetDir}`);
2744
+ }
2745
+ const libDir = path2.join(targetDir, "lib");
2746
+ if (!existsSync12(libDir) || !statSync5(libDir).isDirectory()) {
2747
+ fail(`lib \u76EE\u5F55\u4E0D\u5B58\u5728: ${libDir}`);
2748
+ }
2749
+ const libJars = readdirSync5(libDir).filter((name) => name.endsWith(".jar"));
2750
+ if (libJars.length === 0) {
2751
+ fail(`lib \u76EE\u5F55\u4E0B\u6CA1\u6709\u4F9D\u8D56 JAR: ${libDir}`);
2752
+ }
2753
+ log(`\u5B9A\u4F4D lib \u4EA7\u7269: ${libJars.length} \u4E2A`);
2754
+ return libDir;
2755
+ }
2756
+ async function connectSsh(config) {
2757
+ const client = new Client();
2758
+ log(`\u8FDE\u63A5\u670D\u52A1\u5668 ${config.username}@${config.host}:${config.port}`);
2759
+ await new Promise((resolve5, reject) => {
2760
+ client.on("ready", () => resolve5()).on("error", (err) => reject(err)).connect({
2761
+ host: config.host,
2762
+ port: config.port,
2763
+ username: config.username,
2764
+ password: config.password,
2765
+ readyTimeout: 3e4,
2766
+ tryKeyboard: true
2767
+ });
2768
+ });
2769
+ const sftp = new SftpClient2();
2770
+ await sftp.connect({
2771
+ host: config.host,
2772
+ port: config.port,
2773
+ username: config.username,
2774
+ password: config.password,
2775
+ readyTimeout: 3e4,
2776
+ tryKeyboard: true
2777
+ });
2778
+ return { client, sftp };
2779
+ }
2780
+ async function closeSsh(conn) {
2781
+ try {
2782
+ await conn.sftp.end();
2783
+ } catch {
2784
+ }
2785
+ conn.client.end();
2786
+ }
2787
+ async function getRemoteFileStats(sftp, remoteDir) {
2788
+ const stats = /* @__PURE__ */ new Map();
2789
+ try {
2790
+ const listing = await sftp.list(remoteDir);
2791
+ for (const item of listing) {
2792
+ if (item.name.endsWith(".jar")) {
2793
+ stats.set(item.name, { filename: item.name, size: item.size });
2794
+ }
2795
+ }
2796
+ } catch {
2797
+ }
2798
+ return stats;
2799
+ }
2800
+ async function uploadUpdatePackage(sftp, zipPath, config) {
2801
+ const remoteDir = config.remoteVueDistDir.replace(/\/$/, "");
2802
+ const remotePath = `${remoteDir}/${path2.basename(zipPath)}`;
2803
+ log(`\u4E0A\u4F20\u66F4\u65B0\u5305 -> ${remotePath}`);
2804
+ try {
2805
+ await sftp.fastPut(zipPath, remotePath);
2806
+ log("\u66F4\u65B0\u5305\u4E0A\u4F20\u6210\u529F");
2807
+ } catch (err) {
2808
+ fail(`\u66F4\u65B0\u5305\u4E0A\u4F20\u5931\u8D25: ${err instanceof Error ? err.message : err}`);
2809
+ }
2810
+ return remotePath;
2811
+ }
2812
+ async function runRemoteCommand(client, command, options) {
2813
+ const check = options?.check ?? true;
2814
+ const stream = options?.stream ?? false;
2815
+ log(`\u8FDC\u7A0B\u6267\u884C: ${command}`);
2816
+ return new Promise((resolve5, reject) => {
2817
+ client.exec(command, (err, execStream) => {
2818
+ if (err) {
2819
+ reject(err);
2820
+ return;
2821
+ }
2822
+ let out = "";
2823
+ let errText = "";
2824
+ execStream.on("data", (data) => {
2825
+ const text = data.toString();
2826
+ out += text;
2827
+ if (stream) {
2828
+ process.stdout.write(text);
2829
+ }
2830
+ });
2831
+ execStream.stderr.on("data", (data) => {
2832
+ errText += data.toString();
2833
+ });
2834
+ execStream.on("close", (code) => {
2835
+ if (!stream) {
2836
+ if (out) {
2837
+ console.log(out.trimEnd());
2838
+ }
2839
+ if (errText) {
2840
+ console.error(errText.trimEnd());
2841
+ }
2842
+ } else if (out && !out.endsWith("\n")) {
2843
+ process.stdout.write("\n");
2844
+ }
2845
+ if (check && code !== 0) {
2846
+ fail(`\u8FDC\u7A0B\u547D\u4EE4\u5931\u8D25 (exit ${code}): ${command}`);
2847
+ }
2848
+ resolve5({ exitCode: code, out: out.trim(), err: errText.trim() });
2849
+ });
2850
+ });
2851
+ });
2852
+ }
2853
+ function buildExtractUpdatePackageScript(remoteZipPath, remoteLibDir) {
2854
+ const quotedZip = shellSingleQuote(remoteZipPath);
2855
+ const quotedLib = shellSingleQuote(remoteLibDir);
2856
+ return `
2857
+ set -e
2858
+ TMP=$(mktemp -d)
2859
+ trap 'rm -rf "$TMP"' EXIT
2860
+ unzip -oq ${quotedZip} -d "$TMP"
2861
+ updated=0
2862
+ while IFS= read -r -d '' src; do
2863
+ name=$(basename "$src")
2864
+ dest=${quotedLib}/"$name"
2865
+ if [ -f "$dest" ]; then
2866
+ cp -f "$src" "$dest"
2867
+ echo "\u8986\u76D6: $name"
2868
+ updated=$((updated + 1))
2869
+ else
2870
+ echo "\u8DF3\u8FC7(\u8FDC\u7A0B\u4E0D\u5B58\u5728): $name"
2871
+ fi
2872
+ done < <(find "$TMP" -name '*.jar' -type f -print0)
2873
+ echo "UPDATED_COUNT=$updated"
2874
+ `.trim();
2875
+ }
2876
+ async function extractUpdatePackageOnRemote(client, config, remoteZipPath) {
2877
+ const script = buildExtractUpdatePackageScript(
2878
+ remoteZipPath,
2879
+ config.remoteLibDir
2880
+ );
2881
+ const { out } = await runRemoteCommand(client, script);
2882
+ const match = out.match(/UPDATED_COUNT=(\d+)/);
2883
+ if (!match) {
2884
+ fail(`\u8FDC\u7A0B\u89E3\u538B\u5931\u8D25\uFF0C\u672A\u83B7\u53D6\u66F4\u65B0\u6570\u91CF
2885
+ \u8F93\u51FA: ${out || "(\u7A7A)"}`);
2886
+ }
2887
+ const updated = Number.parseInt(match[1], 10);
2888
+ log(`lib \u89E3\u538B\u5B8C\u6210: \u8986\u76D6 ${updated} \u4E2A`);
2889
+ return updated;
2890
+ }
2891
+ function springbootOutputIndicatesSuccess(action, combined) {
2892
+ const lower = combined.toLowerCase();
2893
+ if (action === "health") {
2894
+ return combined.includes("\u5065\u5EB7\u68C0\u67E5\u901A\u8FC7");
2895
+ }
2896
+ if (action === "start" || action === "restart") {
2897
+ return combined.includes("is starting") || lower.includes("is running");
2898
+ }
2899
+ if (action === "stop") {
2900
+ return combined.includes("is stopping") || lower.includes("not running") || lower.includes("please check it");
2901
+ }
2902
+ if (action === "status") {
2903
+ return lower.includes("running") || lower.includes("not running");
2904
+ }
2905
+ return true;
2906
+ }
2907
+ async function runSpringbootAction(client, config, action, ...args) {
2908
+ if (action === "start" || action === "restart") {
2909
+ if (args.length === 0) {
2910
+ fail(`\u8FDC\u7A0B ${action} \u7F3A\u5C11 jar \u53C2\u6570`);
2911
+ }
2912
+ const jar = args[0].trim().split(/\r?\n/)[0].trim();
2913
+ if (!jar) {
2914
+ fail(`\u65E0\u6548\u7684 jar \u540D\u79F0: ${JSON.stringify(args[0])}`);
2915
+ }
2916
+ }
2917
+ const quotedArgs = [action, ...args].map((arg) => shellSingleQuote(arg));
2918
+ const command = [
2919
+ `cd ${shellSingleQuote(config.remoteAppDir)}`,
2920
+ `./${SPRINGBOOT_SCRIPT} ${quotedArgs.join(" ")}`
2921
+ ].join(" && ");
2922
+ const { exitCode, out, err } = await runRemoteCommand(client, command, {
2923
+ check: false
2924
+ });
2925
+ const combined = `${out}
2926
+ ${err}`.trim();
2927
+ const outputOk = springbootOutputIndicatesSuccess(action, combined);
2928
+ if (action === "health") {
2929
+ if (exitCode !== 0 || !outputOk) {
2930
+ fail(`\u5065\u5EB7\u68C0\u67E5\u5931\u8D25
2931
+ \u547D\u4EE4: ${command}
2932
+ \u8F93\u51FA: ${combined || "(\u7A7A)"}`);
2933
+ }
2934
+ return combined;
2935
+ }
2936
+ if (exitCode !== 0 && !outputOk) {
2937
+ fail(`\u8FDC\u7A0B ${action} \u5931\u8D25: ${args.join(" ")}
2938
+ ${combined}`);
2939
+ }
2940
+ if ((action === "start" || action === "restart") && !outputOk) {
2941
+ fail(
2942
+ `\u8FDC\u7A0B ${action} \u672A\u6210\u529F: ${args.join(" ")}
2943
+ \u547D\u4EE4: ${command}
2944
+ \u8F93\u51FA: ${combined || "(\u7A7A)"}`
2945
+ );
2946
+ }
2947
+ return combined;
2948
+ }
2949
+ function stripAnsi(text) {
2950
+ return text.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "");
2951
+ }
2952
+ async function getRunningJar(client, config) {
2953
+ const combined = await runSpringbootAction(
2954
+ client,
2955
+ config,
2956
+ "status",
2957
+ config.startupJar
2958
+ );
2959
+ const text = stripAnsi(combined).trim().toLowerCase();
2960
+ if (text.includes("not running")) {
2961
+ return null;
2962
+ }
2963
+ if (text.includes("running")) {
2964
+ return config.startupJar;
2965
+ }
2966
+ return null;
2967
+ }
2968
+ async function healthCheckService(client, config) {
2969
+ const port = String(config.healthCheckPort);
2970
+ const context = config.healthCheckContext;
2971
+ const timeout = String(config.healthCheckTimeout);
2972
+ log(`\u5065\u5EB7\u68C0\u67E5: springboot.sh health ${port} ${context} ${timeout}`);
2973
+ await runSpringbootAction(client, config, "health", port, context, timeout);
2974
+ }
2975
+ async function runWisdomBackendDeploy(options) {
2976
+ const projectRoot = path2.resolve(options.projectRoot ?? process.cwd());
2977
+ const config = options.config;
2978
+ log(`=== \u81EA\u52A8\u90E8\u7F72: ${config.projectName} ===`);
2979
+ log(`\u914D\u7F6E\u6587\u4EF6: ${path2.join(workspaceApmDir(), "apm.config.json")}`);
2980
+ log(`\u9879\u76EE\u6839\u76EE\u5F55: ${projectRoot}`);
2981
+ if (!options.skipMaven) {
2982
+ runMavenBuild(projectRoot, config.mavenLocalRepo);
2983
+ }
2984
+ const libDir = locateLibDir(projectRoot);
2985
+ let manifest = loadManifest3();
2986
+ const conn = await connectSsh(config);
2987
+ try {
2988
+ const remoteLibStats = await getRemoteFileStats(
2989
+ conn.sftp,
2990
+ config.remoteLibDir
2991
+ );
2992
+ log("\u6536\u96C6 JAR \u66F4\u65B0...");
2993
+ const libUploadEntries = listLibFilesToUpload(
2994
+ libDir,
2995
+ remoteLibStats,
2996
+ projectRoot,
2997
+ manifest
2998
+ );
2999
+ let updated = 0;
3000
+ if (libUploadEntries.length > 0) {
3001
+ const zipPath = await createUpdatePackage(
3002
+ libUploadEntries,
3003
+ config.packageName
3004
+ );
3005
+ const remoteZipPath = await uploadUpdatePackage(
3006
+ conn.sftp,
3007
+ zipPath,
3008
+ config
3009
+ );
3010
+ log("\u8FDC\u7A0B\u89E3\u538B lib \u76EE\u5F55\uFF08\u4EC5\u8986\u76D6\u5DF2\u6709 JAR\uFF09...");
3011
+ updated = await extractUpdatePackageOnRemote(
3012
+ conn.client,
3013
+ config,
3014
+ remoteZipPath
3015
+ );
3016
+ } else {
3017
+ log("\u65E0 JAR \u9700\u8981\u66F4\u65B0\uFF0C\u8DF3\u8FC7\u66F4\u65B0\u5305\u4E0A\u4F20");
3018
+ }
3019
+ const runningJar = await getRunningJar(conn.client, config);
3020
+ let needRestart = updated > 0;
3021
+ if (!needRestart && !runningJar) {
3022
+ log("\u670D\u52A1\u672A\u8FD0\u884C\uFF0C\u9700\u8981\u542F\u52A8");
3023
+ needRestart = true;
3024
+ } else if (!needRestart) {
3025
+ log("\u6CA1\u6709\u6587\u4EF6\u9700\u8981\u66F4\u65B0\uFF0C\u8DF3\u8FC7\u91CD\u542F");
3026
+ }
3027
+ if (needRestart) {
3028
+ log("\u91CD\u542F\u670D\u52A1...");
3029
+ await runSpringbootAction(
3030
+ conn.client,
3031
+ config,
3032
+ "restart",
3033
+ config.startupJar
3034
+ );
3035
+ }
3036
+ await healthCheckService(conn.client, config);
3037
+ if (libUploadEntries.length > 0) {
3038
+ manifest = updateManifestEntries(manifest, libUploadEntries, projectRoot);
3039
+ saveManifest3(manifest);
3040
+ }
3041
+ } finally {
3042
+ await closeSsh(conn);
3043
+ }
3044
+ log("\u90E8\u7F72\u5B8C\u6210");
3045
+ }
3046
+
3047
+ // src/commands/deploy/internal/wisdom-auto-deploy.ts
3048
+ function isWisdomLegacyDeploy(cfg) {
3049
+ const w = cfg.wisdomDeploy;
3050
+ if (!w?.host?.trim() || !w.remotePath?.trim()) {
3051
+ return false;
3052
+ }
3053
+ const hasNewFrontend = Boolean(cfg.frontendDeploy?.endpoint?.trim());
3054
+ const hasNewBackend = Boolean(cfg.backendDeploy?.registryHost?.trim());
3055
+ return !hasNewFrontend && !hasNewBackend;
3056
+ }
3057
+ function detectWisdomProjectType(cwd) {
3058
+ return existsSync13(path3.join(cwd, "package.json")) ? "frontend" : "backend";
3059
+ }
3060
+ function readPackageScripts(cwd) {
3061
+ const pkgPath = path3.join(cwd, "package.json");
3062
+ if (!existsSync13(pkgPath)) {
3063
+ return {};
3064
+ }
3065
+ try {
3066
+ const raw = readFileSync11(pkgPath, "utf8");
3067
+ const parsed = JSON.parse(raw);
3068
+ return parsed.scripts ?? {};
3069
+ } catch {
3070
+ return {};
3071
+ }
3072
+ }
3073
+ function resolveFrontendDeployCommand(env, cwd) {
3074
+ const scripts = readPackageScripts(cwd);
3075
+ const deployKey = `deploy:${env}`;
3076
+ if (scripts[deployKey]?.trim()) {
3077
+ return `npm run deploy:${env}`;
3078
+ }
3079
+ return null;
3080
+ }
3081
+ function runShellCommand(command, cwd) {
3082
+ console.error(`==> ${command}`);
3083
+ const result = spawnSync3(command, {
3084
+ cwd,
3085
+ stdio: "inherit",
3086
+ shell: true,
3087
+ env: process.env
3088
+ });
3089
+ if (result.status !== 0) {
3090
+ process.exit(result.status ?? 1);
3091
+ }
3092
+ }
3093
+ async function runWisdomAutoDeploy(options) {
3094
+ const cwd = path3.resolve(options.cwd ?? process.cwd());
3095
+ const projectType = detectWisdomProjectType(cwd);
3096
+ console.error(
3097
+ `[apm] \u533B\u52A1\u5B58\u91CF\u9879\u76EE\u81EA\u52A8\u8BC6\u522B: ${projectType === "frontend" ? "\u524D\u7AEF Vue" : "\u540E\u7AEF Java"}`
3098
+ );
3099
+ if (projectType === "frontend") {
3100
+ const deployCmd = resolveFrontendDeployCommand(options.env, cwd);
3101
+ if (!deployCmd) {
3102
+ console.error(
3103
+ `\u524D\u7AEF\u9879\u76EE ${path3.join(cwd, "package.json")} \u4E2D\u672A\u627E\u5230 deploy:${options.env} \u811A\u672C`
3104
+ );
3105
+ process.exit(1);
3106
+ }
3107
+ runShellCommand(deployCmd, cwd);
3108
+ return { projectType };
3109
+ }
3110
+ const configPath = options.configPath ?? path3.join(workspaceApmDir(cwd), "apm.config.json");
3111
+ const cfg = loadApmConfig({ configPath });
3112
+ const settings = resolveWisdomBackendDeployFromApmConfig(cfg);
3113
+ console.error(
3114
+ `[apm] \u540E\u7AEF\u90E8\u7F72\u4E0D\u533A\u5206 test/online\uFF0C\u5FFD\u7565\u73AF\u5883\u53C2\u6570: ${options.env}`
3115
+ );
3116
+ await runWisdomBackendDeploy({
3117
+ config: settings,
3118
+ projectRoot: cwd,
3119
+ skipMaven: Boolean(options.skipMaven)
3120
+ });
3121
+ return { projectType };
3122
+ }
3123
+
3124
+ // src/commands/connect/deploy-run.ts
2231
3125
  var DEPLOY_LOG_SYNC_INTERVAL_MS = 3e4;
2232
- function readDeployConfig(workdir) {
3126
+ function readApmConfig(workdir) {
2233
3127
  const configPath = join13(workspaceApmDir(workdir), "apm.config.json");
3128
+ if (!existsSync14(configPath)) {
3129
+ return void 0;
3130
+ }
2234
3131
  try {
2235
- const raw = readFileSync9(configPath, "utf8");
2236
- const parsed = JSON.parse(raw);
2237
- return parsed.deploy;
3132
+ const raw = readFileSync12(configPath, "utf8");
3133
+ return JSON.parse(raw);
2238
3134
  } catch {
2239
3135
  return void 0;
2240
3136
  }
2241
3137
  }
3138
+ function readDeployConfig(workdir) {
3139
+ return readApmConfig(workdir)?.deploy;
3140
+ }
2242
3141
  function resolveDeployCommand(workdir, environment) {
2243
3142
  const command = readDeployConfig(workdir)?.[environment];
2244
3143
  if (typeof command === "string" && command.trim()) {
@@ -2252,7 +3151,7 @@ function missingDeployCommandMessage(environment) {
2252
3151
  function buildDeployLog(stdout, stderr) {
2253
3152
  return [stdout, stderr].filter(Boolean).join("\n");
2254
3153
  }
2255
- function runShellCommand(command, cwd, signal, onOutput) {
3154
+ function runShellCommand2(command, cwd, signal, onOutput) {
2256
3155
  return new Promise((resolve5, reject) => {
2257
3156
  const child = spawn(command, {
2258
3157
  cwd,
@@ -2287,15 +3186,15 @@ function runShellCommand(command, cwd, signal, onOutput) {
2287
3186
  });
2288
3187
  child.on("close", (code) => {
2289
3188
  signal.removeEventListener("abort", onAbort);
2290
- const log = buildDeployLog(stdout, stderr);
3189
+ const log2 = buildDeployLog(stdout, stderr);
2291
3190
  if (code === 0) {
2292
- resolve5({ log });
3191
+ resolve5({ log: log2 });
2293
3192
  return;
2294
3193
  }
2295
3194
  const error = new Error(
2296
3195
  `\u90E8\u7F72\u547D\u4EE4\u9000\u51FA\u7801 ${code ?? "unknown"}: ${command}`
2297
3196
  );
2298
- error.log = log;
3197
+ error.log = log2;
2299
3198
  reject(error);
2300
3199
  });
2301
3200
  });
@@ -2322,8 +3221,8 @@ function createDeployLogSyncer(api, deploymentRunId) {
2322
3221
  });
2323
3222
  }, DEPLOY_LOG_SYNC_INTERVAL_MS);
2324
3223
  return {
2325
- updateLog(log) {
2326
- latestLog = log;
3224
+ updateLog(log2) {
3225
+ latestLog = log2;
2327
3226
  },
2328
3227
  async flush() {
2329
3228
  clearInterval(timer);
@@ -2343,6 +3242,39 @@ async function handleInboundDeploy(cfg, msg, signal) {
2343
3242
  status: "DEPLOYING"
2344
3243
  });
2345
3244
  const workdir = requireRemoteWorkdir(msg.workdir);
3245
+ const apmConfig = readApmConfig(workdir);
3246
+ if (apmConfig && isWisdomLegacyDeploy(apmConfig)) {
3247
+ console.log(
3248
+ `[apm] deploy start id=${deploymentRunId} env=${msg.environment} cwd=${workdir} (wisdom auto)`
3249
+ );
3250
+ const logSyncer2 = createDeployLogSyncer(api, deploymentRunId);
3251
+ try {
3252
+ await runWisdomAutoDeploy({
3253
+ env: msg.environment,
3254
+ cwd: workdir
3255
+ });
3256
+ await logSyncer2.flush();
3257
+ await api.cli.completeTaskDeployment({
3258
+ id: deploymentRunId,
3259
+ status: "SUCCESS",
3260
+ log: "\u533B\u52A1\u5B58\u91CF\u9879\u76EE\u81EA\u52A8\u90E8\u7F72\u5B8C\u6210"
3261
+ });
3262
+ console.log(`[apm] deploy success id=${deploymentRunId}`);
3263
+ } catch (error) {
3264
+ const detail = error instanceof Error ? error.message : String(error);
3265
+ await logSyncer2.flush();
3266
+ await api.cli.completeTaskDeployment({
3267
+ id: deploymentRunId,
3268
+ status: "FAILED",
3269
+ log: detail,
3270
+ error: detail
3271
+ });
3272
+ console.error(`[apm] deploy failed id=${deploymentRunId}: ${detail}`);
3273
+ } finally {
3274
+ logSyncer2.dispose();
3275
+ }
3276
+ return;
3277
+ }
2346
3278
  const command = resolveDeployCommand(workdir, msg.environment);
2347
3279
  if (!command) {
2348
3280
  const error = missingDeployCommandMessage(msg.environment);
@@ -2362,28 +3294,28 @@ async function handleInboundDeploy(cfg, msg, signal) {
2362
3294
  const logSyncer = createDeployLogSyncer(api, deploymentRunId);
2363
3295
  let latestLog = "";
2364
3296
  try {
2365
- const { log } = await runShellCommand(command, workdir, signal, (log2) => {
2366
- latestLog = log2;
2367
- logSyncer.updateLog(log2);
3297
+ const { log: log2 } = await runShellCommand2(command, workdir, signal, (log3) => {
3298
+ latestLog = log3;
3299
+ logSyncer.updateLog(log3);
2368
3300
  });
2369
- latestLog = log;
2370
- logSyncer.updateLog(log);
3301
+ latestLog = log2;
3302
+ logSyncer.updateLog(log2);
2371
3303
  await logSyncer.flush();
2372
3304
  await api.cli.completeTaskDeployment({
2373
3305
  id: deploymentRunId,
2374
3306
  status: "SUCCESS",
2375
- log
3307
+ log: log2
2376
3308
  });
2377
3309
  console.log(`[apm] deploy success id=${deploymentRunId}`);
2378
3310
  } catch (error) {
2379
3311
  const detail = error instanceof Error ? error.message : String(error);
2380
- const log = error && typeof error === "object" && "log" in error ? String(error.log ?? latestLog) : latestLog;
2381
- logSyncer.updateLog(log);
3312
+ const log2 = error && typeof error === "object" && "log" in error ? String(error.log ?? latestLog) : latestLog;
3313
+ logSyncer.updateLog(log2);
2382
3314
  await logSyncer.flush();
2383
3315
  await api.cli.completeTaskDeployment({
2384
3316
  id: deploymentRunId,
2385
3317
  status: "FAILED",
2386
- log,
3318
+ log: log2,
2387
3319
  error: detail
2388
3320
  });
2389
3321
  console.error(`[apm] deploy failed id=${deploymentRunId}: ${detail}`);
@@ -2659,17 +3591,17 @@ ${JSON.stringify(event, null, 2)}
2659
3591
  }
2660
3592
 
2661
3593
  // src/commands/connect/agent-session-registry.ts
2662
- import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync10 } from "node:fs";
2663
- import { dirname as dirname4, resolve as resolve3 } from "node:path";
3594
+ import { existsSync as existsSync15, mkdirSync as mkdirSync6, readFileSync as readFileSync13, writeFileSync as writeFileSync11 } from "node:fs";
3595
+ import { dirname as dirname4, resolve as resolve4 } from "node:path";
2664
3596
  function registryPath(workdir, sessionId) {
2665
- return resolve3(workdir, ".apm", "sessions", sessionId, "cursor-agents.json");
3597
+ return resolve4(workdir, ".apm", "sessions", sessionId, "cursor-agents.json");
2666
3598
  }
2667
- function readRegistry(path10) {
2668
- if (!existsSync11(path10)) {
3599
+ function readRegistry(path12) {
3600
+ if (!existsSync15(path12)) {
2669
3601
  return {};
2670
3602
  }
2671
3603
  try {
2672
- const parsed = JSON.parse(readFileSync10(path10, "utf8"));
3604
+ const parsed = JSON.parse(readFileSync13(path12, "utf8"));
2673
3605
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
2674
3606
  const result = {};
2675
3607
  for (const [key, value] of Object.entries(
@@ -2685,9 +3617,9 @@ function readRegistry(path10) {
2685
3617
  }
2686
3618
  return {};
2687
3619
  }
2688
- function writeRegistry(path10, registry) {
2689
- mkdirSync5(dirname4(path10), { recursive: true });
2690
- writeFileSync10(path10, `${JSON.stringify(registry, null, 2)}
3620
+ function writeRegistry(path12, registry) {
3621
+ mkdirSync6(dirname4(path12), { recursive: true });
3622
+ writeFileSync11(path12, `${JSON.stringify(registry, null, 2)}
2691
3623
  `, "utf8");
2692
3624
  }
2693
3625
  function loadSessionAgentId(workdir, sessionId, user) {
@@ -2695,19 +3627,19 @@ function loadSessionAgentId(workdir, sessionId, user) {
2695
3627
  return registry[user];
2696
3628
  }
2697
3629
  function saveSessionAgentId(workdir, sessionId, user, agentId) {
2698
- const path10 = registryPath(workdir, sessionId);
2699
- const registry = readRegistry(path10);
3630
+ const path12 = registryPath(workdir, sessionId);
3631
+ const registry = readRegistry(path12);
2700
3632
  registry[user] = agentId;
2701
- writeRegistry(path10, registry);
3633
+ writeRegistry(path12, registry);
2702
3634
  }
2703
3635
  function clearSessionAgentId(workdir, sessionId, user) {
2704
- const path10 = registryPath(workdir, sessionId);
2705
- const registry = readRegistry(path10);
3636
+ const path12 = registryPath(workdir, sessionId);
3637
+ const registry = readRegistry(path12);
2706
3638
  if (!(user in registry)) {
2707
3639
  return;
2708
3640
  }
2709
3641
  delete registry[user];
2710
- writeRegistry(path10, registry);
3642
+ writeRegistry(path12, registry);
2711
3643
  }
2712
3644
 
2713
3645
  // src/commands/connect/cursor-message-log.ts
@@ -3101,20 +4033,20 @@ async function runCursorAgent(cfg, ctx, options) {
3101
4033
  }
3102
4034
 
3103
4035
  // src/commands/connect/cli-version-sync.ts
3104
- import { existsSync as existsSync12, readFileSync as readFileSync11, writeFileSync as writeFileSync11 } from "fs";
4036
+ import { existsSync as existsSync16, readFileSync as readFileSync14, writeFileSync as writeFileSync12 } from "fs";
3105
4037
  import { join as join14 } from "path";
3106
4038
  var CLI_VERSION_FILE = ".cli-version.json";
3107
4039
  function manifestPath(apmDir) {
3108
4040
  return join14(apmDir, CLI_VERSION_FILE);
3109
4041
  }
3110
- function loadManifest3(apmDir) {
3111
- const path10 = toFsPath(manifestPath(apmDir));
3112
- if (!existsSync12(path10)) {
4042
+ function loadManifest4(apmDir) {
4043
+ const path12 = toFsPath(manifestPath(apmDir));
4044
+ if (!existsSync16(path12)) {
3113
4045
  return null;
3114
4046
  }
3115
4047
  try {
3116
4048
  const parsed = JSON.parse(
3117
- readFileSync11(path10, "utf8")
4049
+ readFileSync14(path12, "utf8")
3118
4050
  );
3119
4051
  if (parsed?.version === 1 && typeof parsed.cliVersion === "string" && parsed.cliVersion.trim()) {
3120
4052
  return parsed;
@@ -3123,9 +4055,9 @@ function loadManifest3(apmDir) {
3123
4055
  }
3124
4056
  return null;
3125
4057
  }
3126
- function saveManifest3(apmDir, cliVersion) {
4058
+ function saveManifest4(apmDir, cliVersion) {
3127
4059
  const manifest = { version: 1, cliVersion };
3128
- writeFileSync11(
4060
+ writeFileSync12(
3129
4061
  toFsPath(manifestPath(apmDir)),
3130
4062
  `${JSON.stringify(manifest, null, 2)}
3131
4063
  `,
@@ -3138,7 +4070,7 @@ function shouldSyncSkillsForCliVersion(workdir, currentVersion) {
3138
4070
  if (cached === currentVersion) {
3139
4071
  return false;
3140
4072
  }
3141
- const stored = loadManifest3(workspaceApmDir(workdir));
4073
+ const stored = loadManifest4(workspaceApmDir(workdir));
3142
4074
  if (stored?.cliVersion === currentVersion) {
3143
4075
  syncedInSession.set(workdir, currentVersion);
3144
4076
  return false;
@@ -3146,7 +4078,7 @@ function shouldSyncSkillsForCliVersion(workdir, currentVersion) {
3146
4078
  return true;
3147
4079
  }
3148
4080
  function markSkillsSyncedForCliVersion(workdir, cliVersion) {
3149
- saveManifest3(workspaceApmDir(workdir), cliVersion);
4081
+ saveManifest4(workspaceApmDir(workdir), cliVersion);
3150
4082
  syncedInSession.set(workdir, cliVersion);
3151
4083
  }
3152
4084
 
@@ -3373,7 +4305,7 @@ function reexecConnect(options) {
3373
4305
  if (server) {
3374
4306
  args.push("--server", server);
3375
4307
  }
3376
- const result = spawnSync2(process.execPath, args, { stdio: "inherit" });
4308
+ const result = spawnSync4(process.execPath, args, { stdio: "inherit" });
3377
4309
  if (result.error) {
3378
4310
  console.error("[apm] \u91CD\u542F connect \u5931\u8D25:", result.error.message);
3379
4311
  process.exit(1);
@@ -3509,202 +4441,123 @@ async function runConnect(options) {
3509
4441
  activeTasks.delete(task);
3510
4442
  });
3511
4443
  });
3512
- ws.on("close", (code, reason) => {
3513
- console.log(
3514
- `[apm] \u8FDE\u63A5\u5DF2\u65AD\u5F00 code=${code}${reason ? ` reason=${reason.toString()}` : ""}`
3515
- );
3516
- void shutdown();
3517
- });
3518
- ws.on("error", (err) => {
3519
- console.error("[apm] WebSocket \u9519\u8BEF:", err.message);
3520
- reject(err);
3521
- });
3522
- process.on("SIGINT", () => {
3523
- console.log("[apm] \u6B63\u5728\u5173\u95ED\u2026");
3524
- void shutdown();
3525
- });
3526
- process.on("SIGTERM", () => {
3527
- void shutdown();
3528
- });
3529
- });
3530
- }
3531
-
3532
- // src/commands/create-pr.ts
3533
- async function runCreatePr(options) {
3534
- const sessionId = options.sessionId.trim();
3535
- if (!sessionId) {
3536
- console.error("[apm] sessionId \u4E0D\u80FD\u4E3A\u7A7A");
3537
- process.exit(1);
3538
- }
3539
- const title = options.title.trim();
3540
- if (!title) {
3541
- console.error("[apm] \u8BF7\u901A\u8FC7 --title \u6307\u5B9A PR \u6807\u9898");
3542
- process.exit(1);
3543
- }
3544
- const cfg = await ensureLoggedConfig();
3545
- const api = createApmApiClient(cfg);
3546
- const workdir = resolveWorkdirPath(options.cwd ?? process.cwd());
3547
- const pr = await api.cli.createPullRequest({
3548
- sessionId,
3549
- workdir,
3550
- title,
3551
- content: options.content ?? ""
3552
- });
3553
- console.log(`[apm] PR \u5DF2\u5C31\u7EEA #${pr.number} (${pr.state}): ${pr.url}`);
3554
- }
3555
-
3556
- // src/commands/deploy/backend.ts
3557
- import path5 from "node:path";
3558
-
3559
- // src/commands/deploy/internal/apm-config.ts
3560
- import { existsSync as existsSync13, readFileSync as readFileSync12 } from "node:fs";
3561
- import { resolve as resolve4 } from "node:path";
3562
- function loadApmConfig(options) {
3563
- const p = resolve4(
3564
- process.cwd(),
3565
- options?.configPath ?? resolve4(workspaceApmDir(), "apm.config.json")
3566
- );
3567
- if (!existsSync13(p)) {
3568
- console.error(`\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\uFF1A${p}`);
3569
- process.exit(1);
3570
- }
3571
- try {
3572
- const raw = readFileSync12(p, "utf8");
3573
- return JSON.parse(raw);
3574
- } catch (e) {
3575
- console.error(`\u65E0\u6CD5\u89E3\u6790 apm.config.json\uFF1A${p}`, e);
3576
- process.exit(1);
3577
- }
3578
- }
3579
- function req(v, field) {
3580
- if (v === void 0 || v === null || typeof v === "string" && !v.trim()) {
3581
- console.error(`apm.config.json \u4E2D backendDeploy.${field} \u4E0D\u80FD\u4E3A\u7A7A`);
3582
- process.exit(1);
3583
- }
3584
- return v;
3585
- }
3586
- function reqTopLevelName(cfg) {
3587
- const n = (cfg.name ?? "").trim();
3588
- if (!n) {
3589
- console.error(
3590
- "\u8BF7\u5728 apm.config.json \u9876\u5C42\u914D\u7F6E name\uFF08\u4E0E\u524D\u7AEF\u5236\u54C1\u524D\u7F00\u3001\u540E\u7AEF\u955C\u50CF\u540D\u5171\u7528\uFF09"
3591
- );
3592
- process.exit(1);
3593
- }
3594
- return n;
3595
- }
3596
- function reqBackendPositiveInt(v, field) {
3597
- const n = Number(v);
3598
- if (!Number.isFinite(n) || !Number.isInteger(n) || n < 1) {
3599
- console.error(`apm.config.json \u4E2D backendDeploy.${field} \u987B\u4E3A\u6B63\u6574\u6570`);
3600
- process.exit(1);
3601
- }
3602
- return n;
3603
- }
3604
- function resolveBackendDeployFromApmConfig(cfg) {
3605
- const b = cfg.backendDeploy ?? {};
3606
- const protoRaw = req(b.remoteProtocol, "remoteProtocol").trim().toLowerCase();
3607
- if (protoRaw !== "http" && protoRaw !== "https") {
3608
- console.error(
3609
- "apm.config.json \u4E2D backendDeploy.remoteProtocol \u53EA\u80FD\u4E3A http \u6216 https"
3610
- );
3611
- process.exit(1);
3612
- }
3613
- const remoteProtocol = protoRaw;
3614
- let mappings = [];
3615
- const rawPorts = b.containerPortsMappings;
3616
- if (rawPorts !== void 0 && rawPorts !== null) {
3617
- if (!Array.isArray(rawPorts)) {
3618
- console.error(
3619
- "apm.config.json \u4E2D backendDeploy.containerPortsMappings \u987B\u4E3A\u5B57\u7B26\u4E32\u6570\u7EC4\uFF08\u53EF\u4E3A\u7A7A\uFF0C\u7701\u7565\u5219\u4E0D\u52A0\u7AEF\u53E3\u6620\u5C04\uFF09"
3620
- );
3621
- process.exit(1);
3622
- }
3623
- mappings = rawPorts.map((x) => String(x).trim()).filter(Boolean);
3624
- }
3625
- return {
3626
- name: reqTopLevelName(cfg),
3627
- registryHost: req(b.registryHost, "registryHost").trim(),
3628
- registryNamespace: req(b.registryNamespace, "registryNamespace").trim(),
3629
- registryUser: req(b.registryUser, "registryUser").trim(),
3630
- registryPassword: req(b.registryPassword, "registryPassword").trim(),
3631
- remoteHost: req(b.remoteHost, "remoteHost").trim(),
3632
- remotePort: reqBackendPositiveInt(b.remotePort, "remotePort"),
3633
- remoteProtocol,
3634
- caPath: b.caPath?.trim(),
3635
- certPath: b.certPath?.trim(),
3636
- keyPath: b.keyPath?.trim(),
3637
- envFilePath: typeof b.envFilePath === "string" ? b.envFilePath.trim() : "",
3638
- containerPortsMappings: mappings,
3639
- dockerNetwork: b.dockerNetwork?.trim() || void 0
3640
- };
4444
+ ws.on("close", (code, reason) => {
4445
+ console.log(
4446
+ `[apm] \u8FDE\u63A5\u5DF2\u65AD\u5F00 code=${code}${reason ? ` reason=${reason.toString()}` : ""}`
4447
+ );
4448
+ void shutdown();
4449
+ });
4450
+ ws.on("error", (err) => {
4451
+ console.error("[apm] WebSocket \u9519\u8BEF:", err.message);
4452
+ reject(err);
4453
+ });
4454
+ process.on("SIGINT", () => {
4455
+ console.log("[apm] \u6B63\u5728\u5173\u95ED\u2026");
4456
+ void shutdown();
4457
+ });
4458
+ process.on("SIGTERM", () => {
4459
+ void shutdown();
4460
+ });
4461
+ });
3641
4462
  }
3642
- function reqFe(v, field) {
3643
- if (v === void 0 || v === null || typeof v === "string" && !v.trim()) {
3644
- console.error(`apm.config.json \u4E2D frontendDeploy.${field} \u4E0D\u80FD\u4E3A\u7A7A`);
4463
+
4464
+ // src/commands/create-pr.ts
4465
+ async function runCreatePr(options) {
4466
+ const sessionId = options.sessionId.trim();
4467
+ if (!sessionId) {
4468
+ console.error("[apm] sessionId \u4E0D\u80FD\u4E3A\u7A7A");
3645
4469
  process.exit(1);
3646
4470
  }
3647
- return v;
3648
- }
3649
- function resolveFrontendDeployFromApmConfig(cfg) {
3650
- const f = cfg.frontendDeploy ?? {};
3651
- const port = Number(f.port);
3652
- return {
3653
- endpoint: reqFe(f.endpoint, "endpoint").trim(),
3654
- port: Number.isFinite(port) && port > 0 ? port : 9e3,
3655
- useSsl: Boolean(f.useSsl),
3656
- accessKey: reqFe(f.accessKey, "accessKey").trim(),
3657
- secretKey: reqFe(f.secretKey, "secretKey").trim(),
3658
- bucket: reqFe(f.bucket, "bucket").trim()
3659
- };
3660
- }
3661
- function reqWd(v, field) {
3662
- if (v === void 0 || v === null || typeof v === "string" && !v.trim()) {
3663
- console.error(`apm.config.json \u4E2D wisdomDeploy.${field} \u4E0D\u80FD\u4E3A\u7A7A`);
4471
+ const title = options.title.trim();
4472
+ if (!title) {
4473
+ console.error("[apm] \u8BF7\u901A\u8FC7 --title \u6307\u5B9A PR \u6807\u9898");
3664
4474
  process.exit(1);
3665
4475
  }
3666
- return v;
4476
+ const cfg = await ensureLoggedConfig();
4477
+ const api = createApmApiClient(cfg);
4478
+ const workdir = resolveWorkdirPath(options.cwd ?? process.cwd());
4479
+ const pr = await api.cli.createPullRequest({
4480
+ sessionId,
4481
+ workdir,
4482
+ title,
4483
+ content: options.content ?? ""
4484
+ });
4485
+ console.log(`[apm] PR \u5DF2\u5C31\u7EEA #${pr.number} (${pr.state}): ${pr.url}`);
3667
4486
  }
3668
- function reqWisdomPositiveInt(v, field) {
3669
- const n = Number(v);
3670
- if (!Number.isFinite(n) || !Number.isInteger(n) || n < 1) {
3671
- console.error(`apm.config.json \u4E2D wisdomDeploy.${field} \u987B\u4E3A\u6B63\u6574\u6570`);
3672
- process.exit(1);
4487
+
4488
+ // src/commands/deploy/deploy.ts
4489
+ import { spawnSync as spawnSync5 } from "node:child_process";
4490
+ function runShellCommand3(command, cwd) {
4491
+ console.error(`==> ${command}`);
4492
+ const result = spawnSync5(command, {
4493
+ cwd,
4494
+ stdio: "inherit",
4495
+ shell: true,
4496
+ env: process.env
4497
+ });
4498
+ if (result.status !== 0) {
4499
+ process.exit(result.status ?? 1);
3673
4500
  }
3674
- return n;
3675
4501
  }
3676
- function resolveWisdomDeployFromApmConfig(cfg) {
3677
- const w = cfg.wisdomDeploy ?? {};
3678
- return {
3679
- host: reqWd(w.host, "host").trim(),
3680
- port: reqWisdomPositiveInt(w.port, "port"),
3681
- username: reqWd(w.username, "username").trim(),
3682
- password: reqWd(w.password, "password").trim(),
3683
- remotePath: reqWd(w.remotePath, "remotePath").trim()
3684
- };
4502
+ function registerDeployMainCommand(program) {
4503
+ program.command("deploy").description(
4504
+ "\u7EDF\u4E00\u90E8\u7F72\uFF1A\u533B\u52A1\u5B58\u91CF\u81EA\u52A8\u8BC6\u522B\u524D\u540E\u7AEF\uFF08\u524D\u7AEF\u6309 deploy:<env> \u811A\u672C\uFF1B\u540E\u7AEF test/online \u540C\u4E00\u5957\uFF09\uFF1B\u5176\u4ED6\u9879\u76EE\u6309 deploy.<\u73AF\u5883> \u6267\u884C shell \u547D\u4EE4"
4505
+ ).argument("<env>", "\u90E8\u7F72\u73AF\u5883\u540D\uFF08\u5982 test\u3001online\uFF09").option(
4506
+ "--config <path>",
4507
+ "apm.config.json \u8DEF\u5F84\uFF08\u9ED8\u8BA4 .apm/apm.config.json\uFF09"
4508
+ ).option("--skip-maven", "\u8DF3\u8FC7 Maven \u6784\u5EFA\uFF08\u4EC5\u533B\u52A1\u540E\u7AEF\uFF09").action(
4509
+ async (env, opts) => {
4510
+ const cwd = process.cwd();
4511
+ const cfg = loadApmConfig({ configPath: opts.config });
4512
+ if (isWisdomLegacyDeploy(cfg)) {
4513
+ await runWisdomAutoDeploy({
4514
+ env,
4515
+ cwd,
4516
+ configPath: opts.config,
4517
+ skipMaven: opts.skipMaven
4518
+ });
4519
+ return;
4520
+ }
4521
+ const deployCommands = cfg.deploy ?? {};
4522
+ const command = deployCommands[env]?.trim();
4523
+ if (!command) {
4524
+ const keys = Object.keys(deployCommands);
4525
+ console.error(
4526
+ keys.length > 0 ? `apm.config.json \u4E2D\u672A\u914D\u7F6E deploy.${env}\uFF0C\u53EF\u7528\u73AF\u5883: ${keys.join(
4527
+ ", "
4528
+ )}` : `apm.config.json \u4E2D\u672A\u914D\u7F6E deploy \u6BB5`
4529
+ );
4530
+ process.exit(1);
4531
+ }
4532
+ runShellCommand3(command, cwd);
4533
+ }
4534
+ );
3685
4535
  }
3686
4536
 
4537
+ // src/commands/deploy/backend.ts
4538
+ import path8 from "node:path";
4539
+
3687
4540
  // src/commands/deploy/internal/backend-deploy/backend-deploy-workflow.ts
3688
- import path4 from "node:path";
4541
+ import path7 from "node:path";
3689
4542
 
3690
4543
  // src/commands/deploy/internal/backend-deploy/dockerode-client/client.ts
3691
4544
  import Docker from "dockerode";
3692
4545
 
3693
4546
  // src/commands/deploy/internal/backend-deploy/dockerode-client/connection-options.ts
3694
- import { existsSync as existsSync14, readFileSync as readFileSync13 } from "node:fs";
3695
- import path from "node:path";
4547
+ import { existsSync as existsSync17, readFileSync as readFileSync15 } from "node:fs";
4548
+ import path4 from "node:path";
3696
4549
  function asOptionalTlsBuffer(value) {
3697
4550
  if (typeof value !== "string") {
3698
4551
  console.log("tls filepath not exist");
3699
4552
  return void 0;
3700
4553
  }
3701
- console.log("tls filepath", path.join(process.cwd(), value));
4554
+ console.log("tls filepath", path4.join(process.cwd(), value));
3702
4555
  const normalized = value.trim();
3703
4556
  if (normalized === "") {
3704
4557
  return void 0;
3705
4558
  }
3706
- if (existsSync14(normalized)) {
3707
- return readFileSync13(normalized);
4559
+ if (existsSync17(normalized)) {
4560
+ return readFileSync15(normalized);
3708
4561
  }
3709
4562
  const looksLikePath = /[\\/]/.test(normalized) || normalized.endsWith(".pem");
3710
4563
  if (looksLikePath) {
@@ -3914,8 +4767,8 @@ var DockerodeClient = class {
3914
4767
  var createDockerodeClient = (config) => new DockerodeClient(config);
3915
4768
 
3916
4769
  // src/commands/deploy/internal/backend-deploy/dockerode-client/env.ts
3917
- import { existsSync as existsSync15, readFileSync as readFileSync14, statSync as statSync5 } from "node:fs";
3918
- import path2 from "node:path";
4770
+ import { existsSync as existsSync18, readFileSync as readFileSync16, statSync as statSync6 } from "node:fs";
4771
+ import path5 from "node:path";
3919
4772
  function stripSurroundingQuotes(value) {
3920
4773
  const t = value.trim();
3921
4774
  if (t.length >= 2) {
@@ -3930,11 +4783,11 @@ function loadEnvFromFile(envFilePath) {
3930
4783
  if (!envFilePath) {
3931
4784
  return {};
3932
4785
  }
3933
- const targetPath = path2.resolve(envFilePath);
3934
- if (!existsSync15(targetPath) || !statSync5(targetPath).isFile()) {
4786
+ const targetPath = path5.resolve(envFilePath);
4787
+ if (!existsSync18(targetPath) || !statSync6(targetPath).isFile()) {
3935
4788
  return {};
3936
4789
  }
3937
- const raw = readFileSync14(targetPath, "utf-8");
4790
+ const raw = readFileSync16(targetPath, "utf-8");
3938
4791
  const result = {};
3939
4792
  for (const line of raw.split(/\r?\n/)) {
3940
4793
  const normalized = line.trim();
@@ -4105,12 +4958,12 @@ function dockerPushImage(params, cwd) {
4105
4958
  }
4106
4959
 
4107
4960
  // src/commands/deploy/internal/backend-deploy/resolve-dockerfile.ts
4108
- import { existsSync as existsSync16 } from "node:fs";
4109
- import path3 from "node:path";
4961
+ import { existsSync as existsSync19 } from "node:fs";
4962
+ import path6 from "node:path";
4110
4963
  function resolveDockerBuildPaths(cwd) {
4111
- const dockerfilePath = path3.join(cwd, "Dockerfile");
4964
+ const dockerfilePath = path6.join(cwd, "Dockerfile");
4112
4965
  Logger.info(`\u67E5\u627EDockerfile\u6587\u4EF6\uFF0C\u8DEF\u5F84: ${dockerfilePath}`);
4113
- if (!existsSync16(dockerfilePath)) {
4966
+ if (!existsSync19(dockerfilePath)) {
4114
4967
  throw new Error(`Dockerfile \u4E0D\u5B58\u5728\uFF1A${dockerfilePath}`);
4115
4968
  }
4116
4969
  Logger.info("\u2713 Dockerfile \u5B58\u5728");
@@ -4165,7 +5018,7 @@ var BackendDeployWorkflow = class {
4165
5018
  serveraddress
4166
5019
  });
4167
5020
  Logger.success("\u8FDC\u7A0B\u62C9\u53D6\u955C\u50CF\u5B8C\u6210");
4168
- const envFilePath = path4.resolve(cwd, this.params.envFilePath || ".env");
5021
+ const envFilePath = path7.resolve(cwd, this.params.envFilePath || ".env");
4169
5022
  const envObj = loadEnvFromFile(envFilePath);
4170
5023
  if (this.params.dockerNetwork?.trim()) {
4171
5024
  Logger.info(`\u8FDC\u7A0B\u5BB9\u5668\u5C06\u52A0\u5165 Docker \u7F51\u7EDC\uFF1A${this.params.dockerNetwork.trim()}`);
@@ -4205,7 +5058,7 @@ function registerDeployBackendCommands(program) {
4205
5058
  assertDeployImageTag(tag);
4206
5059
  const cfg = loadApmConfig({ configPath: opts.config });
4207
5060
  const fromApm = resolveBackendDeployFromApmConfig(cfg);
4208
- const dirAbs = path5.resolve(process.cwd(), opts.dir || "servers/api");
5061
+ const dirAbs = path8.resolve(process.cwd(), opts.dir || "servers/api");
4209
5062
  const params = {
4210
5063
  image: fromApm.name,
4211
5064
  tag,
@@ -4235,18 +5088,18 @@ function registerDeployBackendCommands(program) {
4235
5088
  }
4236
5089
 
4237
5090
  // src/commands/deploy/frontend.ts
4238
- import { copyFile, readdir as readdir2, stat } from "node:fs/promises";
4239
- import path7 from "node:path";
5091
+ import { copyFile, readdir as readdir3, stat } from "node:fs/promises";
5092
+ import path10 from "node:path";
4240
5093
 
4241
5094
  // src/commands/deploy/internal/minio.ts
4242
- import { statSync as statSync6 } from "node:fs";
4243
- import { readdir, readFile } from "node:fs/promises";
4244
- import path6 from "node:path";
5095
+ import { statSync as statSync7 } from "node:fs";
5096
+ import { readdir as readdir2, readFile as readFile2 } from "node:fs/promises";
5097
+ import path9 from "node:path";
4245
5098
  import * as Minio from "minio";
4246
5099
  var DEFAULT_MAX_FILE_SIZE_MB = 50;
4247
5100
  async function isDirectoryPath(dir) {
4248
5101
  try {
4249
- const st = statSync6(dir);
5102
+ const st = statSync7(dir);
4250
5103
  return st.isDirectory();
4251
5104
  } catch {
4252
5105
  return false;
@@ -4265,18 +5118,18 @@ function sanitizeRelativePath(rel) {
4265
5118
  async function collectFiles(root) {
4266
5119
  const out = [];
4267
5120
  async function walk(dir, prefix) {
4268
- const entries = await readdir(dir, { withFileTypes: true });
5121
+ const entries = await readdir2(dir, { withFileTypes: true });
4269
5122
  for (const e of entries) {
4270
5123
  const name = e.name;
4271
5124
  if (name === "." || name === "..") {
4272
5125
  continue;
4273
5126
  }
4274
- const abs = path6.join(dir, name);
5127
+ const abs = path9.join(dir, name);
4275
5128
  const rel = prefix ? `${prefix}/${name}` : name;
4276
5129
  if (e.isDirectory()) {
4277
5130
  await walk(abs, rel);
4278
5131
  } else if (e.isFile()) {
4279
- const st = statSync6(abs);
5132
+ const st = statSync7(abs);
4280
5133
  out.push({
4281
5134
  absPath: abs,
4282
5135
  relativePath: rel.replace(/\\/g, "/"),
@@ -4290,7 +5143,7 @@ async function collectFiles(root) {
4290
5143
  return out;
4291
5144
  }
4292
5145
  async function readArtifactFile(absPath) {
4293
- return readFile(absPath);
5146
+ return readFile2(absPath);
4294
5147
  }
4295
5148
  function toMB(bytes) {
4296
5149
  return Math.round(bytes / (1024 * 1024) * 1e3) / 1e3;
@@ -4314,7 +5167,7 @@ var MIME = {
4314
5167
  ".map": "application/json"
4315
5168
  };
4316
5169
  function detectMimeType(filePath) {
4317
- const ext = path6.extname(filePath).toLowerCase();
5170
+ const ext = path9.extname(filePath).toLowerCase();
4318
5171
  return MIME[ext] ?? "";
4319
5172
  }
4320
5173
  var MinioClient = class {
@@ -4402,7 +5255,7 @@ function artifactObjectKey(namePrefix, branchSegment, relativePath) {
4402
5255
  return `${base}/${branchSegment}/dist/${rel}`;
4403
5256
  }
4404
5257
  async function ensureArtifactRootIndexHtml(root) {
4405
- const indexHtmlPath = path7.join(root, "index.html");
5258
+ const indexHtmlPath = path10.join(root, "index.html");
4406
5259
  try {
4407
5260
  const st = await stat(indexHtmlPath);
4408
5261
  if (st.isFile()) {
@@ -4412,14 +5265,14 @@ async function ensureArtifactRootIndexHtml(root) {
4412
5265
  }
4413
5266
  let entries;
4414
5267
  try {
4415
- entries = await readdir2(root, { withFileTypes: true });
5268
+ entries = await readdir3(root, { withFileTypes: true });
4416
5269
  } catch (e) {
4417
5270
  console.error(`\u65E0\u6CD5\u8BFB\u53D6\u4EA7\u7269\u76EE\u5F55\uFF1A${root}`, e);
4418
5271
  process.exit(1);
4419
5272
  }
4420
5273
  const dirNames = entries.filter((e) => e.isDirectory()).map((e) => String(e.name)).sort();
4421
5274
  for (const name of dirNames) {
4422
- const candidate = path7.join(root, name, "index.html");
5275
+ const candidate = path10.join(root, name, "index.html");
4423
5276
  try {
4424
5277
  const st = await stat(candidate);
4425
5278
  if (!st.isFile()) {
@@ -4430,7 +5283,7 @@ async function ensureArtifactRootIndexHtml(root) {
4430
5283
  }
4431
5284
  await copyFile(candidate, indexHtmlPath);
4432
5285
  console.error(
4433
- `\u4EA7\u7269\u534F\u8BAE\uFF1A\u5DF2\u5C06 ${path7.join(name, "index.html")} \u590D\u5236\u4E3A\u6839\u76EE\u5F55 index.html`
5286
+ `\u4EA7\u7269\u534F\u8BAE\uFF1A\u5DF2\u5C06 ${path10.join(name, "index.html")} \u590D\u5236\u4E3A\u6839\u76EE\u5F55 index.html`
4434
5287
  );
4435
5288
  return;
4436
5289
  }
@@ -4466,7 +5319,7 @@ function registerDeployFrontendCommands(program) {
4466
5319
  secretKey: settings.secretKey
4467
5320
  });
4468
5321
  const bucket = settings.bucket;
4469
- const root = path7.resolve(process.cwd(), opts.dir || "apps/web/dist");
5322
+ const root = path10.resolve(process.cwd(), opts.dir || "apps/web/dist");
4470
5323
  if (!await isDirectoryPath(root)) {
4471
5324
  console.error(`\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${root}`);
4472
5325
  process.exit(1);
@@ -4535,159 +5388,7 @@ function registerDeployFrontendCommands(program) {
4535
5388
  }
4536
5389
 
4537
5390
  // src/commands/deploy/sftp.ts
4538
- import path9 from "node:path";
4539
-
4540
- // src/commands/deploy/internal/wisdom-sftp.ts
4541
- import { readdir as readdir3, readFile as readFile2, unlink, writeFile } from "node:fs/promises";
4542
- import path8 from "node:path";
4543
- import JSZip from "jszip";
4544
- import SftpClient from "ssh2-sftp-client";
4545
- async function addDirToZip(dir, zipFolder) {
4546
- const entries = await readdir3(dir, { withFileTypes: true });
4547
- for (const entry of entries) {
4548
- const fullPath = path8.join(dir, entry.name);
4549
- if (entry.isDirectory()) {
4550
- const folder = zipFolder.folder(entry.name);
4551
- if (folder) {
4552
- await addDirToZip(fullPath, folder);
4553
- }
4554
- } else {
4555
- const content = await readFile2(fullPath);
4556
- zipFolder.file(entry.name, content);
4557
- }
4558
- }
4559
- }
4560
- async function zipDirectory(distDir, zipPath) {
4561
- console.error(`\u538B\u7F29\u76EE\u5F55: ${distDir}`);
4562
- const zip = new JSZip();
4563
- await addDirToZip(distDir, zip);
4564
- const content = await zip.generateAsync({
4565
- type: "nodebuffer",
4566
- compression: "DEFLATE",
4567
- compressionOptions: { level: 6 }
4568
- });
4569
- await writeFile(zipPath, content);
4570
- const sizeMb = (content.length / 1024 / 1024).toFixed(2);
4571
- console.error(`\u5DF2\u751F\u6210: ${zipPath} (${sizeMb} MB)`);
4572
- return content.length;
4573
- }
4574
- async function ensureRemoteDir(sftp, dir) {
4575
- const parts = dir.replace(/\\/g, "/").split("/").filter(Boolean);
4576
- let current = dir.startsWith("/") ? "" : ".";
4577
- for (const part of parts) {
4578
- current = current ? `${current}/${part}` : `/${part}`;
4579
- try {
4580
- await sftp.mkdir(current, true);
4581
- } catch {
4582
- }
4583
- }
4584
- }
4585
- function execCommand(client, command) {
4586
- return new Promise((resolve5, reject) => {
4587
- client.exec(command, (err, stream) => {
4588
- if (err) return reject(err);
4589
- let stdout = "";
4590
- let stderr = "";
4591
- stream.on("close", (code) => {
4592
- if (code !== 0) {
4593
- reject(new Error(`\u8FDC\u7A0B\u547D\u4EE4\u5931\u8D25 (${code}): ${stderr || stdout}`));
4594
- return;
4595
- }
4596
- resolve5(stdout);
4597
- }).on("data", (data) => {
4598
- stdout += data.toString();
4599
- });
4600
- stream.stderr.on("data", (data) => {
4601
- stderr += data.toString();
4602
- });
4603
- });
4604
- });
4605
- }
4606
- function shellSingleQuote(value) {
4607
- return `'${value.replace(/'/g, `'"'"'`)}'`;
4608
- }
4609
- function buildClearRemoteDirExceptZipCommand(target) {
4610
- const normalized = target.replace(/\\/g, "/").replace(/\/$/, "");
4611
- const quotedTarget = shellSingleQuote(normalized);
4612
- const script = [
4613
- `T=${quotedTarget}`,
4614
- "S=$(mktemp -d)",
4615
- 'while IFS= read -r -d "" z; do r="${z#${T}/}"',
4616
- 'mkdir -p "${S}/$(dirname "$r")"',
4617
- 'mv "$z" "${S}/${r}"',
4618
- 'done < <(find "$T" -mindepth 1 -type f -iname "*.zip" -print0)',
4619
- 'rm -rf "${T}"/*',
4620
- 'while IFS= read -r -d "" r; do r="${r#./}"',
4621
- 'mkdir -p "${T}/$(dirname "$r")"',
4622
- 'mv "${S}/${r}" "${T}/${r}"',
4623
- 'done < <(cd "$S" 2>/dev/null && find . -type f -print0)',
4624
- 'rm -rf "$S"'
4625
- ].join("; ");
4626
- return `bash -c ${shellSingleQuote(script)}`;
4627
- }
4628
- async function uploadAndMaybeExtract(settings, localZip, extract) {
4629
- const remoteZipPath = `${settings.remotePath.replace(/\/$/, "")}/dist.zip`;
4630
- console.error(
4631
- `\u8FDE\u63A5 ${settings.username}@${settings.host}:${settings.port} ...`
4632
- );
4633
- const sftp = new SftpClient();
4634
- try {
4635
- await sftp.connect({
4636
- host: settings.host,
4637
- port: settings.port,
4638
- username: settings.username,
4639
- password: settings.password,
4640
- readyTimeout: 2e4,
4641
- tryKeyboard: true
4642
- });
4643
- await ensureRemoteDir(sftp, settings.remotePath);
4644
- await sftp.put(localZip, remoteZipPath);
4645
- console.error(` \u2713 ${localZip} -> ${remoteZipPath}`);
4646
- if (extract) {
4647
- const target = settings.remotePath.replace(/\/$/, "");
4648
- const client = sftp.client;
4649
- const clearCmd = buildClearRemoteDirExceptZipCommand(target);
4650
- console.error(`\u8FDC\u7A0B\u6E05\u7406\u89E3\u538B\u76EE\u5F55: ${clearCmd}`);
4651
- await execCommand(client, clearCmd);
4652
- const unzipCmd = `unzip -o "${remoteZipPath}" -d "${target}"`;
4653
- console.error(`\u8FDC\u7A0B\u89E3\u538B: ${unzipCmd}`);
4654
- await execCommand(client, unzipCmd);
4655
- console.error("\u8FDC\u7A0B\u89E3\u538B\u5B8C\u6210!");
4656
- }
4657
- console.error(extract ? "\u90E8\u7F72\u5B8C\u6210!" : "\u4E0A\u4F20\u5B8C\u6210!");
4658
- } finally {
4659
- await sftp.end();
4660
- }
4661
- }
4662
- async function runWisdomSftpDeploy(params) {
4663
- const zipPath = path8.join(params.localDir, "..", ".deploy-sftp-dist.zip");
4664
- const resolvedZipPath = path8.resolve(zipPath);
4665
- let zipSizeBytes = 0;
4666
- try {
4667
- zipSizeBytes = await zipDirectory(params.localDir, resolvedZipPath);
4668
- await uploadAndMaybeExtract(
4669
- params.settings,
4670
- resolvedZipPath,
4671
- params.extract
4672
- );
4673
- } finally {
4674
- try {
4675
- await unlink(resolvedZipPath);
4676
- console.error(`\u5DF2\u6E05\u7406\u672C\u5730\u4E34\u65F6\u6587\u4EF6: ${resolvedZipPath}`);
4677
- } catch {
4678
- }
4679
- }
4680
- return {
4681
- ok: true,
4682
- localDir: params.localDir,
4683
- host: params.settings.host,
4684
- remotePath: params.settings.remotePath,
4685
- zipSizeBytes,
4686
- extracted: params.extract
4687
- };
4688
- }
4689
-
4690
- // src/commands/deploy/sftp.ts
5391
+ import path11 from "node:path";
4691
5392
  function registerDeploySftpCommands(program) {
4692
5393
  program.command("deploy-sftp").description(
4693
5394
  "\u5C06\u6307\u5B9A\u76EE\u5F55\u538B\u7F29\u4E3A zip \u540E\u901A\u8FC7 SFTP \u4E0A\u4F20\u5230\u8FDC\u7A0B\u670D\u52A1\u5668\uFF08\u8FDE\u63A5\u4FE1\u606F\u89C1 apm.config.json wisdomDeploy\uFF09"
@@ -4702,7 +5403,7 @@ function registerDeploySftpCommands(program) {
4702
5403
  async (opts) => {
4703
5404
  const cfg = loadApmConfig({ configPath: opts.config });
4704
5405
  const settings = resolveWisdomDeployFromApmConfig(cfg);
4705
- const root = path9.resolve(process.cwd(), opts.dir || "apps/web/dist");
5406
+ const root = path11.resolve(process.cwd(), opts.dir || "apps/web/dist");
4706
5407
  if (!await isDirectoryPath(root)) {
4707
5408
  console.error(`\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${root}`);
4708
5409
  process.exit(1);
@@ -4723,11 +5424,30 @@ function registerDeploySftpCommands(program) {
4723
5424
  );
4724
5425
  }
4725
5426
 
5427
+ // src/commands/deploy/wisdom-backend.ts
5428
+ function registerDeployWisdomBackendCommands(program) {
5429
+ program.command("deploy-wisdom-backend").description(
5430
+ "Maven \u6784\u5EFA\u540E\u901A\u8FC7 SFTP \u589E\u91CF\u66F4\u65B0\u8FDC\u7A0B lib \u76EE\u5F55\u5E76\u91CD\u542F Spring Boot \u670D\u52A1\uFF08\u914D\u7F6E\u89C1 apm.config.json \u7684 wisdomDeploy \u4E0E healthCheck\uFF09"
5431
+ ).option(
5432
+ "--config <path>",
5433
+ "apm.config.json \u8DEF\u5F84\uFF08\u9ED8\u8BA4 .apm/apm.config.json\uFF09"
5434
+ ).option("--skip-maven", "\u8DF3\u8FC7 Maven \u6784\u5EFA\uFF0C\u4EC5\u4E0A\u4F20\u5E76\u91CD\u542F").action(async (opts) => {
5435
+ const cfg = loadApmConfig({ configPath: opts.config });
5436
+ const settings = resolveWisdomBackendDeployFromApmConfig(cfg);
5437
+ await runWisdomBackendDeploy({
5438
+ config: settings,
5439
+ skipMaven: Boolean(opts.skipMaven)
5440
+ });
5441
+ });
5442
+ }
5443
+
4726
5444
  // src/commands/deploy/index.ts
4727
5445
  function registerDeployCommands(program) {
5446
+ registerDeployMainCommand(program);
4728
5447
  registerDeployBackendCommands(program);
4729
5448
  registerDeployFrontendCommands(program);
4730
5449
  registerDeploySftpCommands(program);
5450
+ registerDeployWisdomBackendCommands(program);
4731
5451
  }
4732
5452
 
4733
5453
  // src/index.ts