ai-project-manage-cli 6.0.82 → 6.0.84

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(path12) {
114
- let normalized = path12.trim().replace(/\\/g, "/").normalize("NFC");
113
+ function normalizeWorkdirPath(path13) {
114
+ let normalized = path13.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 path12 = join2(apmDir, item);
330
- if (!existsSync(toFsPath(path12))) {
331
- throw new Error(`[apm] \u521D\u59CB\u5316\u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5C11: ${path12}`);
329
+ const path13 = join2(apmDir, item);
330
+ if (!existsSync(toFsPath(path13))) {
331
+ throw new Error(`[apm] \u521D\u59CB\u5316\u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5C11: ${path13}`);
332
332
  }
333
333
  }
334
334
  const leakedRules = join2(workdir, "rules");
@@ -473,6 +473,10 @@ var requestConfig = {
473
473
  method: "DELETE",
474
474
  path: "/cli/repository-project-documents"
475
475
  }),
476
+ createTaskDeployment: defineEndpoint({
477
+ method: "POST",
478
+ path: "/cli/task-deployments"
479
+ }),
476
480
  updateTaskDeploymentStatus: defineEndpoint({
477
481
  method: "PUT",
478
482
  path: "/cli/task-deployments/status"
@@ -606,14 +610,14 @@ function projectDocumentLocalPath(apmRoot, documentPath) {
606
610
  const normalized = normalizeLocalDocumentPath(documentPath);
607
611
  return join4(projectDocumentsDir(apmRoot), ...normalized.split("/"));
608
612
  }
609
- function normalizeLocalDocumentPath(path12) {
610
- const trimmed = path12.trim().replace(/\\/g, "/");
613
+ function normalizeLocalDocumentPath(path13) {
614
+ const trimmed = path13.trim().replace(/\\/g, "/");
611
615
  if (!trimmed || trimmed.startsWith("/") || /^[a-zA-Z]:/.test(trimmed)) {
612
- throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path12}`);
616
+ throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path13}`);
613
617
  }
614
618
  const segments = trimmed.split("/").filter(Boolean);
615
619
  if (segments.some((segment) => segment === ".." || segment === ".")) {
616
- throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path12}`);
620
+ throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path13}`);
617
621
  }
618
622
  return segments.join("/");
619
623
  }
@@ -664,15 +668,15 @@ function diffManifestPaths(remote, local) {
664
668
  (local?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
665
669
  );
666
670
  const download = [];
667
- for (const [path12, hash] of remoteMap) {
668
- if (localMap.get(path12) !== hash) {
669
- download.push(path12);
671
+ for (const [path13, hash] of remoteMap) {
672
+ if (localMap.get(path13) !== hash) {
673
+ download.push(path13);
670
674
  }
671
675
  }
672
676
  const deleteLocal = [];
673
- for (const path12 of localMap.keys()) {
674
- if (!remoteMap.has(path12)) {
675
- deleteLocal.push(path12);
677
+ for (const path13 of localMap.keys()) {
678
+ if (!remoteMap.has(path13)) {
679
+ deleteLocal.push(path13);
676
680
  }
677
681
  }
678
682
  return { download, deleteLocal };
@@ -732,8 +736,8 @@ ${diagnostic ?? ""}`
732
736
  }
733
737
  }
734
738
  let deleted = 0;
735
- for (const path12 of deleteLocal) {
736
- const absPath = toFsPath(projectDocumentLocalPath(targetApmDir, path12));
739
+ for (const path13 of deleteLocal) {
740
+ const absPath = toFsPath(projectDocumentLocalPath(targetApmDir, path13));
737
741
  if (existsSync2(absPath)) {
738
742
  rmSync(absPath, { force: true });
739
743
  deleted += 1;
@@ -778,21 +782,21 @@ async function syncRepositoryProjectDocumentsPush(cfg, workdirPath, apmRoot) {
778
782
  ])
779
783
  );
780
784
  let synced = 0;
781
- for (const path12 of localPaths) {
782
- const absPath = toFsPath(projectDocumentLocalPath(targetApmDir, path12));
785
+ for (const path13 of localPaths) {
786
+ const absPath = toFsPath(projectDocumentLocalPath(targetApmDir, path13));
783
787
  const content = readFileSync3(absPath, "utf8");
784
788
  const contentHash = hashLocalFileContent(content);
785
- if (remoteHashByPath.get(path12) === contentHash) {
789
+ if (remoteHashByPath.get(path13) === contentHash) {
786
790
  continue;
787
791
  }
788
792
  await api.cli.upsertRepositoryProjectDocument({
789
793
  repositoryId,
790
- path: path12,
794
+ path: path13,
791
795
  content,
792
- description: remoteDescriptionByPath.get(path12) ?? void 0
796
+ description: remoteDescriptionByPath.get(path13) ?? void 0
793
797
  });
794
798
  synced += 1;
795
- console.log(`[apm] \u5DF2\u540C\u6B65\u4ED3\u5E93\u9879\u76EE\u6587\u6863: ${path12}`);
799
+ console.log(`[apm] \u5DF2\u540C\u6B65\u4ED3\u5E93\u9879\u76EE\u6587\u6863: ${path13}`);
796
800
  }
797
801
  if (synced === 0) {
798
802
  console.log("[apm] \u4ED3\u5E93\u9879\u76EE\u6587\u6863\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u63A8\u9001");
@@ -1415,13 +1419,13 @@ async function downloadAttachment(cfg, attachmentId) {
1415
1419
  return Buffer.from(await res.arrayBuffer());
1416
1420
  }
1417
1421
  function loadManifest(dir) {
1418
- const path12 = join6(dir, MANIFEST_FILE2);
1419
- if (!existsSync4(path12)) {
1422
+ const path13 = join6(dir, MANIFEST_FILE2);
1423
+ if (!existsSync4(path13)) {
1420
1424
  return { version: 1, attachments: {} };
1421
1425
  }
1422
1426
  try {
1423
1427
  const parsed = JSON.parse(
1424
- readFileSync5(path12, "utf8")
1428
+ readFileSync5(path13, "utf8")
1425
1429
  );
1426
1430
  if (parsed?.version === 1 && parsed.attachments && typeof parsed.attachments === "object") {
1427
1431
  return parsed;
@@ -1502,8 +1506,8 @@ function sanitizeSkillDirName(name) {
1502
1506
  function listBaseSkillDirNames() {
1503
1507
  if (!existsSync5(BASE_SKILLS_TEMPLATE_DIR)) return [];
1504
1508
  return readdirSync3(BASE_SKILLS_TEMPLATE_DIR).filter((name) => {
1505
- const path12 = join7(BASE_SKILLS_TEMPLATE_DIR, name);
1506
- return statSync2(path12).isDirectory();
1509
+ const path13 = join7(BASE_SKILLS_TEMPLATE_DIR, name);
1510
+ return statSync2(path13).isDirectory();
1507
1511
  });
1508
1512
  }
1509
1513
  function syncAgentsGuide(apmDir) {
@@ -1515,8 +1519,8 @@ function syncAgentsGuide(apmDir) {
1515
1519
  function listBaseRuleFileNames() {
1516
1520
  if (!existsSync5(BASE_RULES_TEMPLATE_DIR)) return [];
1517
1521
  return readdirSync3(BASE_RULES_TEMPLATE_DIR).filter((name) => {
1518
- const path12 = join7(BASE_RULES_TEMPLATE_DIR, name);
1519
- return statSync2(path12).isFile();
1522
+ const path13 = join7(BASE_RULES_TEMPLATE_DIR, name);
1523
+ return statSync2(path13).isFile();
1520
1524
  });
1521
1525
  }
1522
1526
  function syncBaseRules(rulesDir) {
@@ -1579,13 +1583,13 @@ function ruleLocalFileName(ruleName) {
1579
1583
  return `${sanitized}.md`;
1580
1584
  }
1581
1585
  function loadManifest2(rulesDir) {
1582
- const path12 = join8(rulesDir, MANIFEST_FILE3);
1583
- if (!existsSync6(toFsPath(path12))) {
1586
+ const path13 = join8(rulesDir, MANIFEST_FILE3);
1587
+ if (!existsSync6(toFsPath(path13))) {
1584
1588
  return { version: 1, rules: {} };
1585
1589
  }
1586
1590
  try {
1587
1591
  const parsed = JSON.parse(
1588
- readFileSync6(toFsPath(path12), "utf8")
1592
+ readFileSync6(toFsPath(path13), "utf8")
1589
1593
  );
1590
1594
  if (parsed?.version === 1 && parsed.rules && typeof parsed.rules === "object") {
1591
1595
  return parsed;
@@ -2684,12 +2688,12 @@ import { dirname as dirname4, resolve as resolve3 } from "node:path";
2684
2688
  function registryPath(workdir, sessionId) {
2685
2689
  return resolve3(workdir, ".apm", "sessions", sessionId, "cursor-agents.json");
2686
2690
  }
2687
- function readRegistry(path12) {
2688
- if (!existsSync11(path12)) {
2691
+ function readRegistry(path13) {
2692
+ if (!existsSync11(path13)) {
2689
2693
  return {};
2690
2694
  }
2691
2695
  try {
2692
- const parsed = JSON.parse(readFileSync9(path12, "utf8"));
2696
+ const parsed = JSON.parse(readFileSync9(path13, "utf8"));
2693
2697
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
2694
2698
  const result = {};
2695
2699
  for (const [key, value] of Object.entries(
@@ -2705,9 +2709,9 @@ function readRegistry(path12) {
2705
2709
  }
2706
2710
  return {};
2707
2711
  }
2708
- function writeRegistry(path12, registry) {
2709
- mkdirSync5(dirname4(path12), { recursive: true });
2710
- writeFileSync10(path12, `${JSON.stringify(registry, null, 2)}
2712
+ function writeRegistry(path13, registry) {
2713
+ mkdirSync5(dirname4(path13), { recursive: true });
2714
+ writeFileSync10(path13, `${JSON.stringify(registry, null, 2)}
2711
2715
  `, "utf8");
2712
2716
  }
2713
2717
  function loadSessionAgentId(workdir, sessionId, user) {
@@ -2715,19 +2719,19 @@ function loadSessionAgentId(workdir, sessionId, user) {
2715
2719
  return registry[user];
2716
2720
  }
2717
2721
  function saveSessionAgentId(workdir, sessionId, user, agentId) {
2718
- const path12 = registryPath(workdir, sessionId);
2719
- const registry = readRegistry(path12);
2722
+ const path13 = registryPath(workdir, sessionId);
2723
+ const registry = readRegistry(path13);
2720
2724
  registry[user] = agentId;
2721
- writeRegistry(path12, registry);
2725
+ writeRegistry(path13, registry);
2722
2726
  }
2723
2727
  function clearSessionAgentId(workdir, sessionId, user) {
2724
- const path12 = registryPath(workdir, sessionId);
2725
- const registry = readRegistry(path12);
2728
+ const path13 = registryPath(workdir, sessionId);
2729
+ const registry = readRegistry(path13);
2726
2730
  if (!(user in registry)) {
2727
2731
  return;
2728
2732
  }
2729
2733
  delete registry[user];
2730
- writeRegistry(path12, registry);
2734
+ writeRegistry(path13, registry);
2731
2735
  }
2732
2736
 
2733
2737
  // src/commands/connect/cursor-message-log.ts
@@ -3164,13 +3168,13 @@ function manifestPath(apmDir) {
3164
3168
  return join13(apmDir, CLI_VERSION_FILE);
3165
3169
  }
3166
3170
  function loadManifest3(apmDir) {
3167
- const path12 = toFsPath(manifestPath(apmDir));
3168
- if (!existsSync12(path12)) {
3171
+ const path13 = toFsPath(manifestPath(apmDir));
3172
+ if (!existsSync12(path13)) {
3169
3173
  return null;
3170
3174
  }
3171
3175
  try {
3172
3176
  const parsed = JSON.parse(
3173
- readFileSync10(path12, "utf8")
3177
+ readFileSync10(path13, "utf8")
3174
3178
  );
3175
3179
  if (parsed?.version === 1 && typeof parsed.cliVersion === "string" && parsed.cliVersion.trim()) {
3176
3180
  return parsed;
@@ -3935,6 +3939,99 @@ async function runConnect(options) {
3935
3939
  });
3936
3940
  }
3937
3941
 
3942
+ // src/commands/create-pr-errors.ts
3943
+ import { ApiError as ApiError2 } from "listpage-http";
3944
+ function extractMessage(error) {
3945
+ if (error instanceof ApiError2) {
3946
+ return String(error.message ?? "").trim();
3947
+ }
3948
+ if (error instanceof Error) {
3949
+ return error.message.trim();
3950
+ }
3951
+ return String(error).trim();
3952
+ }
3953
+ function extractHttpStatus(error) {
3954
+ if (error instanceof ApiError2) {
3955
+ return error.httpStatus;
3956
+ }
3957
+ return void 0;
3958
+ }
3959
+ function buildHints(message, httpStatus) {
3960
+ const hints = [];
3961
+ if (/未配置.*Gitee.*API Key/i.test(message)) {
3962
+ hints.push(
3963
+ "\u8BF7\u5728 APM Web \u2192 \u5BA2\u6237\u673A\u7BA1\u7406 \u2192 \u7F16\u8F91\u5F53\u524D\u5BA2\u6237\u673A \u2192 \u586B\u5199\u300CGitee API Key\u300D\uFF08Gitee \u79C1\u4EBA\u4EE4\u724C\uFF0C\u9700\u6709\u4ED3\u5E93 PR \u6743\u9650\uFF09"
3964
+ );
3965
+ } else if (/未配置.*GitHub.*API Key/i.test(message)) {
3966
+ hints.push(
3967
+ "\u8BF7\u5728 APM Web \u2192 \u5BA2\u6237\u673A\u7BA1\u7406 \u2192 \u7F16\u8F91\u5F53\u524D\u5BA2\u6237\u673A \u2192 \u586B\u5199\u300CGithub API Key\u300D\uFF08GitHub Personal Access Token\uFF0C\u9700\u6709 repo \u6743\u9650\uFF09"
3968
+ );
3969
+ } else if (httpStatus === 401 || /access token does not exist/i.test(message) || /bad credentials/i.test(message) || /Git 平台返回 401|认证失败 \(401\)/i.test(message)) {
3970
+ hints.push(
3971
+ "Git \u5E73\u53F0 Token \u65E0\u6548\u6216\u5DF2\u8FC7\u671F\uFF1A\u8BF7\u5728 Gitee/GitHub \u91CD\u65B0\u751F\u6210\u79C1\u4EBA\u4EE4\u724C\uFF0C\u5E76\u66F4\u65B0\u5230 APM\u300C\u5BA2\u6237\u673A\u7BA1\u7406\u300D"
3972
+ );
3973
+ }
3974
+ if (/未绑定仓库|未关联仓库|选择关联仓库/i.test(message)) {
3975
+ hints.push("\u5728 APM Web \u2192 \u5DE5\u4F5C\u76EE\u5F55 \u2192 \u4E3A\u5F53\u524D\u8DEF\u5F84\u5173\u8054\u5BF9\u5E94 Git \u4ED3\u5E93");
3976
+ }
3977
+ if (/路径.*不匹配|尚未登记任何工作目录/i.test(message)) {
3978
+ hints.push(
3979
+ "\u5728 APM Web \u2192 \u5DE5\u4F5C\u76EE\u5F55 \u2192 \u767B\u8BB0\u5F53\u524D\u9879\u76EE\u8DEF\u5F84\uFF08\u9700\u4E0E apm create-pr \u6267\u884C\u65F6\u7684 cwd \u4E00\u81F4\uFF09"
3980
+ );
3981
+ }
3982
+ if (/no commits between|分支不存在|branch.*not found|找不到.*分支/i.test(
3983
+ message
3984
+ )) {
3985
+ hints.push(
3986
+ "\u7279\u6027\u5206\u652F\u53EF\u80FD\u672A\u63A8\u9001\uFF1A\u5148\u6267\u884C apm branch <sessionId> \u521B\u5EFA\u5206\u652F\u5E76 push\uFF0C\u518D\u91CD\u8BD5 create-pr"
3987
+ );
3988
+ }
3989
+ if (/pull request already exists|已存在.*pull/i.test(message)) {
3990
+ hints.push("\u8BE5\u5206\u652F\u53EF\u80FD\u5DF2\u6709\u5F00\u542F\u7684 PR\uFF0C\u8BF7\u5230 Git \u5E73\u53F0\u6216 APM \u4F1A\u8BDD\u8BE6\u60C5\u67E5\u770B");
3991
+ }
3992
+ if (/403|无权限|forbidden/i.test(message)) {
3993
+ hints.push("\u786E\u8BA4 Token \u5BF9\u76EE\u6807\u4ED3\u5E93\u6709\u521B\u5EFA Pull Request \u7684\u6743\u9650");
3994
+ }
3995
+ if (hints.length === 0) {
3996
+ hints.push(
3997
+ "\u82E5\u65E0\u6CD5\u81EA\u884C\u6392\u67E5\uFF0C\u8BF7\u5728\u4F1A\u8BDD\u4E2D @\u9879\u76EE\u7ECF\u7406\uFF0C\u5E76\u63D0\u4F9B\u4E0A\u8FF0\u4F1A\u8BDD ID \u4E0E\u5DE5\u4F5C\u76EE\u5F55"
3998
+ );
3999
+ }
4000
+ return hints;
4001
+ }
4002
+ function formatCreatePrErrorLines(error, context) {
4003
+ const message = extractMessage(error);
4004
+ const httpStatus = extractHttpStatus(error);
4005
+ const headBranch = `feat/session-${context.sessionId}`;
4006
+ const lines = [
4007
+ "[apm] \u521B\u5EFA PR \u5931\u8D25",
4008
+ "",
4009
+ `\u4F1A\u8BDD ID: ${context.sessionId}`,
4010
+ `\u5DE5\u4F5C\u76EE\u5F55: ${context.workdir}`,
4011
+ `\u7279\u6027\u5206\u652F: ${headBranch}`
4012
+ ];
4013
+ if (context.clientMachineId) {
4014
+ lines.push(`\u5BA2\u6237\u673A ID: ${context.clientMachineId}`);
4015
+ }
4016
+ lines.push("", "\u539F\u56E0:", message || "(\u672A\u77E5\u9519\u8BEF)");
4017
+ const hints = buildHints(message, httpStatus);
4018
+ if (hints.length > 0) {
4019
+ lines.push("", "\u5EFA\u8BAE:");
4020
+ hints.forEach((hint, index) => {
4021
+ lines.push(`${index + 1}. ${hint}`);
4022
+ });
4023
+ }
4024
+ if (httpStatus !== void 0) {
4025
+ lines.push("", `HTTP \u72B6\u6001: ${httpStatus}`);
4026
+ }
4027
+ return lines;
4028
+ }
4029
+ function printCreatePrError(error, context) {
4030
+ for (const line of formatCreatePrErrorLines(error, context)) {
4031
+ console.error(line);
4032
+ }
4033
+ }
4034
+
3938
4035
  // src/commands/create-pr.ts
3939
4036
  async function runCreatePr(options) {
3940
4037
  const sessionId = options.sessionId.trim();
@@ -3950,17 +4047,27 @@ async function runCreatePr(options) {
3950
4047
  const cfg = await ensureLoggedConfig();
3951
4048
  const api = createApmApiClient(cfg);
3952
4049
  const workdir = resolveWorkdirPath(options.cwd ?? process.cwd());
3953
- const pr = await api.cli.createPullRequest({
3954
- sessionId,
3955
- workdir,
3956
- title,
3957
- content: options.content ?? ""
3958
- });
3959
- console.log(`[apm] PR \u5DF2\u5C31\u7EEA #${pr.number} (${pr.state}): ${pr.url}`);
4050
+ try {
4051
+ const pr = await api.cli.createPullRequest({
4052
+ sessionId,
4053
+ workdir,
4054
+ title,
4055
+ content: options.content ?? ""
4056
+ });
4057
+ console.log(`[apm] PR \u5DF2\u5C31\u7EEA #${pr.number} (${pr.state}): ${pr.url}`);
4058
+ } catch (error) {
4059
+ printCreatePrError(error, {
4060
+ sessionId,
4061
+ workdir,
4062
+ clientMachineId: resolveClientMachineId(cfg)
4063
+ });
4064
+ process.exit(1);
4065
+ }
3960
4066
  }
3961
4067
 
3962
- // src/commands/deploy/deploy.ts
4068
+ // src/commands/deploy/deploy-execute.ts
3963
4069
  import { spawnSync as spawnSync5 } from "node:child_process";
4070
+ import path4 from "node:path";
3964
4071
 
3965
4072
  // src/commands/deploy/internal/apm-config.ts
3966
4073
  import { existsSync as existsSync15, readFileSync as readFileSync12 } from "node:fs";
@@ -4152,9 +4259,9 @@ function readMavenLocalRepoFromEnv(env = process.env) {
4152
4259
  }
4153
4260
  const mavenOpts = readEnvVar(env, "MAVEN_OPTS")?.trim();
4154
4261
  if (mavenOpts) {
4155
- const path12 = readMavenLocalRepoFromMavenOpts(mavenOpts, env);
4156
- if (path12) {
4157
- return { path: path12, key: "MAVEN_OPTS" };
4262
+ const path13 = readMavenLocalRepoFromMavenOpts(mavenOpts, env);
4263
+ if (path13) {
4264
+ return { path: path13, key: "MAVEN_OPTS" };
4158
4265
  }
4159
4266
  }
4160
4267
  return null;
@@ -4287,19 +4394,18 @@ function formatWisdomFrontendDeployNotConfiguredLines(env, packageJsonPath) {
4287
4394
  "[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"
4288
4395
  ];
4289
4396
  }
4290
- function printDeployNotConfiguredHint(env, deployCommands) {
4291
- for (const line of formatDeployNotConfiguredLines(env, deployCommands)) {
4292
- console.error(line);
4293
- }
4294
- }
4295
- function printWisdomFrontendDeployNotConfiguredHint(env, packageJsonPath) {
4296
- for (const line of formatWisdomFrontendDeployNotConfiguredLines(
4297
- env,
4298
- packageJsonPath
4299
- )) {
4300
- console.error(line);
4397
+
4398
+ // src/commands/deploy/deploy-errors.ts
4399
+ var DeployExecutionError = class extends Error {
4400
+ exitCode;
4401
+ output;
4402
+ constructor(message, exitCode, output = "") {
4403
+ super(message);
4404
+ this.name = "DeployExecutionError";
4405
+ this.exitCode = exitCode;
4406
+ this.output = output;
4301
4407
  }
4302
- }
4408
+ };
4303
4409
 
4304
4410
  // src/commands/deploy/internal/wisdom-auto-deploy.ts
4305
4411
  import { existsSync as existsSync17, readFileSync as readFileSync14 } from "node:fs";
@@ -5105,19 +5211,33 @@ function resolveFrontendDeployCommand(env, cwd) {
5105
5211
  }
5106
5212
  return null;
5107
5213
  }
5108
- function runShellCommand2(command, cwd) {
5214
+ function runShellCommand2(command, cwd, captureOutput) {
5109
5215
  const result = spawnSync4(command, {
5110
5216
  cwd,
5111
- stdio: "inherit",
5112
5217
  shell: true,
5113
- env: process.env
5218
+ env: process.env,
5219
+ encoding: "utf8",
5220
+ stdio: captureOutput ? ["inherit", "pipe", "pipe"] : "inherit"
5114
5221
  });
5222
+ const stdout = captureOutput ? String(result.stdout ?? "") : "";
5223
+ const stderr = captureOutput ? String(result.stderr ?? "") : "";
5224
+ const output = [stdout, stderr].filter(Boolean).join("\n");
5225
+ if (captureOutput) {
5226
+ if (stdout) process.stdout.write(stdout);
5227
+ if (stderr) process.stderr.write(stderr);
5228
+ }
5115
5229
  if (result.status !== 0) {
5116
- process.exit(result.status ?? 1);
5230
+ throw new DeployExecutionError(
5231
+ `\u90E8\u7F72\u547D\u4EE4\u9000\u51FA\u7801 ${result.status ?? "unknown"}: ${command}`,
5232
+ result.status ?? 1,
5233
+ output
5234
+ );
5117
5235
  }
5236
+ return output;
5118
5237
  }
5119
5238
  async function runWisdomAutoDeploy(options) {
5120
5239
  const cwd = path3.resolve(options.cwd ?? process.cwd());
5240
+ const captureOutput = options.captureOutput ?? false;
5121
5241
  const projectType = detectWisdomProjectType(cwd);
5122
5242
  console.error(
5123
5243
  `[apm] \u533B\u52A1\u5B58\u91CF\u9879\u76EE\u81EA\u52A8\u8BC6\u522B: ${projectType === "frontend" ? "\u524D\u7AEF Vue" : "\u540E\u7AEF Java"}`
@@ -5125,13 +5245,13 @@ async function runWisdomAutoDeploy(options) {
5125
5245
  if (projectType === "frontend") {
5126
5246
  const deployCmd = resolveFrontendDeployCommand(options.env, cwd);
5127
5247
  if (!deployCmd) {
5128
- printWisdomFrontendDeployNotConfiguredHint(
5248
+ const lines = formatWisdomFrontendDeployNotConfiguredLines(
5129
5249
  options.env,
5130
5250
  path3.join(cwd, "package.json")
5131
5251
  );
5132
- process.exit(1);
5252
+ throw new DeployExecutionError(lines.join("\n"), 1, lines.join("\n"));
5133
5253
  }
5134
- runShellCommand2(deployCmd, cwd);
5254
+ runShellCommand2(deployCmd, cwd, captureOutput);
5135
5255
  return { projectType };
5136
5256
  }
5137
5257
  const configPath = options.configPath ?? path3.join(workspaceApmDir(cwd), "apm.config.json");
@@ -5147,63 +5267,229 @@ async function runWisdomAutoDeploy(options) {
5147
5267
  return { projectType };
5148
5268
  }
5149
5269
 
5150
- // src/commands/deploy/deploy.ts
5151
- function runShellCommand3(command, cwd) {
5270
+ // src/commands/deploy/deploy-execute.ts
5271
+ function runShellCommand3(command, cwd, captureOutput) {
5152
5272
  const result = spawnSync5(command, {
5153
5273
  cwd,
5154
- stdio: "inherit",
5155
5274
  shell: true,
5156
- env: process.env
5275
+ env: process.env,
5276
+ encoding: "utf8",
5277
+ stdio: captureOutput ? ["inherit", "pipe", "pipe"] : "inherit"
5157
5278
  });
5279
+ const stdout = captureOutput ? String(result.stdout ?? "") : "";
5280
+ const stderr = captureOutput ? String(result.stderr ?? "") : "";
5281
+ const output = [stdout, stderr].filter(Boolean).join("\n");
5282
+ if (captureOutput) {
5283
+ if (stdout) process.stdout.write(stdout);
5284
+ if (stderr) process.stderr.write(stderr);
5285
+ }
5158
5286
  if (result.status !== 0) {
5159
- process.exit(result.status ?? 1);
5287
+ throw new DeployExecutionError(
5288
+ `\u90E8\u7F72\u547D\u4EE4\u9000\u51FA\u7801 ${result.status ?? "unknown"}: ${command}`,
5289
+ result.status ?? 1,
5290
+ output
5291
+ );
5292
+ }
5293
+ return output;
5294
+ }
5295
+ async function executeDeploy(options) {
5296
+ const cwd = path4.resolve(options.cwd ?? process.cwd());
5297
+ const captureOutput = options.captureOutput ?? false;
5298
+ const cfg = loadApmConfig({ configPath: options.configPath });
5299
+ if (isWisdomLegacyDeploy(cfg)) {
5300
+ try {
5301
+ await runWisdomAutoDeploy({
5302
+ env: options.env,
5303
+ cwd,
5304
+ configPath: options.configPath,
5305
+ captureOutput
5306
+ });
5307
+ return "";
5308
+ } catch (error) {
5309
+ if (error instanceof DeployExecutionError) {
5310
+ throw error;
5311
+ }
5312
+ const detail = error instanceof Error ? error.message : String(error);
5313
+ throw new DeployExecutionError(detail, 1);
5314
+ }
5315
+ }
5316
+ const deployCommands = cfg.deploy ?? {};
5317
+ const command = deployCommands[options.env]?.trim();
5318
+ if (!command) {
5319
+ const lines = formatDeployNotConfiguredLines(options.env, deployCommands);
5320
+ throw new DeployExecutionError(lines.join("\n"), 1, lines.join("\n"));
5321
+ }
5322
+ return runShellCommand3(command, cwd, captureOutput);
5323
+ }
5324
+ function printDeployExecutionError(error) {
5325
+ if (error.output.trim()) {
5326
+ console.error(error.output);
5327
+ return;
5328
+ }
5329
+ console.error(error.message);
5330
+ }
5331
+
5332
+ // src/commands/deploy/deploy-tracking.ts
5333
+ function normalizeDeployEnvironment(env) {
5334
+ const normalized = env.trim().toLowerCase();
5335
+ if (normalized === "test") return "TEST";
5336
+ if (normalized === "online") return "ONLINE";
5337
+ return null;
5338
+ }
5339
+ async function completeTrackedDeploy(api, deploymentRunId, logSyncer, input) {
5340
+ logSyncer.updateLog(input.log);
5341
+ await logSyncer.flush();
5342
+ await api.cli.completeTaskDeployment({
5343
+ id: deploymentRunId,
5344
+ status: input.status,
5345
+ log: input.log,
5346
+ error: input.error
5347
+ });
5348
+ }
5349
+ async function runDeployWithBackendTracking(options) {
5350
+ const cfg = await tryReadApmConfig();
5351
+ if (!cfg || !resolveApiKey(cfg)) {
5352
+ console.warn("[apm] \u672A\u767B\u5F55\uFF0C\u8DF3\u8FC7\u540E\u7AEF\u90E8\u7F72\u8BB0\u5F55");
5353
+ await executeDeploy(options);
5354
+ return;
5355
+ }
5356
+ const environment = normalizeDeployEnvironment(options.env);
5357
+ if (!environment) {
5358
+ console.warn(
5359
+ `[apm] \u672A\u77E5\u90E8\u7F72\u73AF\u5883 ${options.env}\uFF0C\u8DF3\u8FC7\u540E\u7AEF\u90E8\u7F72\u8BB0\u5F55\uFF08\u4EC5\u652F\u6301 test/online\uFF09`
5360
+ );
5361
+ await executeDeploy(options);
5362
+ return;
5363
+ }
5364
+ const api = createApmApiClient(cfg);
5365
+ let deploymentRunId;
5366
+ try {
5367
+ const run = await api.cli.createTaskDeployment({
5368
+ sessionId: options.sessionId,
5369
+ environment,
5370
+ workdirPath: options.cwd ?? process.cwd()
5371
+ });
5372
+ deploymentRunId = run.id;
5373
+ console.log(
5374
+ `[apm] \u5DF2\u521B\u5EFA\u90E8\u7F72\u8BB0\u5F55 id=${deploymentRunId} env=${environment}`
5375
+ );
5376
+ } catch (error) {
5377
+ const detail = error instanceof Error ? error.message : String(error);
5378
+ console.warn(`[apm] \u521B\u5EFA\u90E8\u7F72\u8BB0\u5F55\u5931\u8D25\uFF0C\u7EE7\u7EED\u90E8\u7F72: ${detail}`);
5379
+ await executeDeploy(options);
5380
+ return;
5381
+ }
5382
+ await api.cli.updateTaskDeploymentStatus({
5383
+ id: deploymentRunId,
5384
+ status: "DEPLOYING"
5385
+ });
5386
+ const logSyncer = createDeployLogSyncer(api, deploymentRunId);
5387
+ const chunks = [];
5388
+ const appendLog = (line) => {
5389
+ if (!line) return;
5390
+ chunks.push(line);
5391
+ logSyncer.updateLog(chunks.join("\n"));
5392
+ };
5393
+ const originalLog = console.log;
5394
+ const originalError = console.error;
5395
+ console.log = (...args) => {
5396
+ appendLog(args.map(String).join(" "));
5397
+ originalLog.apply(console, args);
5398
+ };
5399
+ console.error = (...args) => {
5400
+ appendLog(args.map(String).join(" "));
5401
+ originalError.apply(console, args);
5402
+ };
5403
+ try {
5404
+ const output = await executeDeploy({ ...options, captureOutput: true });
5405
+ if (output.trim()) {
5406
+ appendLog(output);
5407
+ }
5408
+ const log2 = chunks.join("\n");
5409
+ await completeTrackedDeploy(api, deploymentRunId, logSyncer, {
5410
+ status: "SUCCESS",
5411
+ log: log2
5412
+ });
5413
+ console.log(`[apm] deploy success id=${deploymentRunId}`);
5414
+ } catch (error) {
5415
+ const deployError = error instanceof DeployExecutionError ? error : new DeployExecutionError(
5416
+ error instanceof Error ? error.message : String(error),
5417
+ 1
5418
+ );
5419
+ if (deployError.output.trim()) {
5420
+ appendLog(deployError.output);
5421
+ } else {
5422
+ appendLog(deployError.message);
5423
+ }
5424
+ const log2 = chunks.join("\n");
5425
+ await completeTrackedDeploy(api, deploymentRunId, logSyncer, {
5426
+ status: "FAILED",
5427
+ log: log2,
5428
+ error: deployError.message
5429
+ });
5430
+ printDeployExecutionError(deployError);
5431
+ process.exit(deployError.exitCode);
5432
+ } finally {
5433
+ console.log = originalLog;
5434
+ console.error = originalError;
5435
+ logSyncer.dispose();
5160
5436
  }
5161
5437
  }
5438
+
5439
+ // src/commands/deploy/deploy.ts
5162
5440
  function registerDeployMainCommand(program) {
5163
5441
  program.command("deploy").description(
5164
5442
  "\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"
5165
5443
  ).argument("<env>", "\u90E8\u7F72\u73AF\u5883\u540D\uFF08\u5982 test\u3001online\uFF09").option(
5166
5444
  "--config <path>",
5167
5445
  "apm.config.json \u8DEF\u5F84\uFF08\u9ED8\u8BA4 .apm/apm.config.json\uFF09"
5168
- ).action(async (env, opts) => {
5169
- const cwd = process.cwd();
5170
- const cfg = loadApmConfig({ configPath: opts.config });
5171
- if (isWisdomLegacyDeploy(cfg)) {
5172
- await runWisdomAutoDeploy({
5173
- env,
5174
- cwd,
5175
- configPath: opts.config
5176
- });
5177
- return;
5178
- }
5179
- const deployCommands = cfg.deploy ?? {};
5180
- const command = deployCommands[env]?.trim();
5181
- if (!command) {
5182
- printDeployNotConfiguredHint(env, deployCommands);
5183
- process.exit(1);
5446
+ ).option(
5447
+ "--session <sessionId>",
5448
+ "\u6C9F\u901A\u7FA4 ID\uFF1B\u4F20\u5165\u65F6\u5728\u5E73\u53F0\u521B\u5EFA\u90E8\u7F72\u8BB0\u5F55\u5E76\u540C\u6B65\u65E5\u5FD7"
5449
+ ).action(
5450
+ async (env, opts) => {
5451
+ const cwd = process.cwd();
5452
+ const sessionId = opts.session?.trim();
5453
+ try {
5454
+ if (sessionId) {
5455
+ await runDeployWithBackendTracking({
5456
+ env,
5457
+ cwd,
5458
+ configPath: opts.config,
5459
+ sessionId
5460
+ });
5461
+ return;
5462
+ }
5463
+ await executeDeploy({ env, cwd, configPath: opts.config });
5464
+ } catch (error) {
5465
+ if (error instanceof DeployExecutionError) {
5466
+ printDeployExecutionError(error);
5467
+ process.exit(error.exitCode);
5468
+ }
5469
+ throw error;
5470
+ }
5184
5471
  }
5185
- runShellCommand3(command, cwd);
5186
- });
5472
+ );
5187
5473
  }
5188
5474
 
5189
5475
  // src/commands/deploy/backend.ts
5190
- import path8 from "node:path";
5476
+ import path9 from "node:path";
5191
5477
 
5192
5478
  // src/commands/deploy/internal/backend-deploy/backend-deploy-workflow.ts
5193
- import path7 from "node:path";
5479
+ import path8 from "node:path";
5194
5480
 
5195
5481
  // src/commands/deploy/internal/backend-deploy/dockerode-client/client.ts
5196
5482
  import Docker from "dockerode";
5197
5483
 
5198
5484
  // src/commands/deploy/internal/backend-deploy/dockerode-client/connection-options.ts
5199
5485
  import { existsSync as existsSync18, readFileSync as readFileSync15 } from "node:fs";
5200
- import path4 from "node:path";
5486
+ import path5 from "node:path";
5201
5487
  function asOptionalTlsBuffer(value) {
5202
5488
  if (typeof value !== "string") {
5203
5489
  console.log("tls filepath not exist");
5204
5490
  return void 0;
5205
5491
  }
5206
- console.log("tls filepath", path4.join(process.cwd(), value));
5492
+ console.log("tls filepath", path5.join(process.cwd(), value));
5207
5493
  const normalized = value.trim();
5208
5494
  if (normalized === "") {
5209
5495
  return void 0;
@@ -5420,7 +5706,7 @@ var createDockerodeClient = (config) => new DockerodeClient(config);
5420
5706
 
5421
5707
  // src/commands/deploy/internal/backend-deploy/dockerode-client/env.ts
5422
5708
  import { existsSync as existsSync19, readFileSync as readFileSync16, statSync as statSync6 } from "node:fs";
5423
- import path5 from "node:path";
5709
+ import path6 from "node:path";
5424
5710
  function stripSurroundingQuotes(value) {
5425
5711
  const t = value.trim();
5426
5712
  if (t.length >= 2) {
@@ -5435,7 +5721,7 @@ function loadEnvFromFile(envFilePath) {
5435
5721
  if (!envFilePath) {
5436
5722
  return {};
5437
5723
  }
5438
- const targetPath = path5.resolve(envFilePath);
5724
+ const targetPath = path6.resolve(envFilePath);
5439
5725
  if (!existsSync19(targetPath) || !statSync6(targetPath).isFile()) {
5440
5726
  return {};
5441
5727
  }
@@ -5611,9 +5897,9 @@ function dockerPushImage(params, cwd) {
5611
5897
 
5612
5898
  // src/commands/deploy/internal/backend-deploy/resolve-dockerfile.ts
5613
5899
  import { existsSync as existsSync20 } from "node:fs";
5614
- import path6 from "node:path";
5900
+ import path7 from "node:path";
5615
5901
  function resolveDockerBuildPaths(cwd) {
5616
- const dockerfilePath = path6.join(cwd, "Dockerfile");
5902
+ const dockerfilePath = path7.join(cwd, "Dockerfile");
5617
5903
  Logger.info(`\u67E5\u627EDockerfile\u6587\u4EF6\uFF0C\u8DEF\u5F84: ${dockerfilePath}`);
5618
5904
  if (!existsSync20(dockerfilePath)) {
5619
5905
  throw new Error(`Dockerfile \u4E0D\u5B58\u5728\uFF1A${dockerfilePath}`);
@@ -5670,7 +5956,7 @@ var BackendDeployWorkflow = class {
5670
5956
  serveraddress
5671
5957
  });
5672
5958
  Logger.success("\u8FDC\u7A0B\u62C9\u53D6\u955C\u50CF\u5B8C\u6210");
5673
- const envFilePath = path7.resolve(cwd, this.params.envFilePath || ".env");
5959
+ const envFilePath = path8.resolve(cwd, this.params.envFilePath || ".env");
5674
5960
  const envObj = loadEnvFromFile(envFilePath);
5675
5961
  if (this.params.dockerNetwork?.trim()) {
5676
5962
  Logger.info(`\u8FDC\u7A0B\u5BB9\u5668\u5C06\u52A0\u5165 Docker \u7F51\u7EDC\uFF1A${this.params.dockerNetwork.trim()}`);
@@ -5710,7 +5996,7 @@ function registerDeployBackendCommands(program) {
5710
5996
  assertDeployImageTag(tag);
5711
5997
  const cfg = loadApmConfig({ configPath: opts.config });
5712
5998
  const fromApm = resolveBackendDeployFromApmConfig(cfg);
5713
- const dirAbs = path8.resolve(process.cwd(), opts.dir || "servers/api");
5999
+ const dirAbs = path9.resolve(process.cwd(), opts.dir || "servers/api");
5714
6000
  const params = {
5715
6001
  image: fromApm.name,
5716
6002
  tag,
@@ -5741,12 +6027,12 @@ function registerDeployBackendCommands(program) {
5741
6027
 
5742
6028
  // src/commands/deploy/frontend.ts
5743
6029
  import { copyFile, readdir as readdir3, stat } from "node:fs/promises";
5744
- import path10 from "node:path";
6030
+ import path11 from "node:path";
5745
6031
 
5746
6032
  // src/commands/deploy/internal/minio.ts
5747
6033
  import { statSync as statSync7 } from "node:fs";
5748
6034
  import { readdir as readdir2, readFile as readFile2 } from "node:fs/promises";
5749
- import path9 from "node:path";
6035
+ import path10 from "node:path";
5750
6036
  import * as Minio from "minio";
5751
6037
  var DEFAULT_MAX_FILE_SIZE_MB = 50;
5752
6038
  async function isDirectoryPath(dir) {
@@ -5776,7 +6062,7 @@ async function collectFiles(root) {
5776
6062
  if (name === "." || name === "..") {
5777
6063
  continue;
5778
6064
  }
5779
- const abs = path9.join(dir, name);
6065
+ const abs = path10.join(dir, name);
5780
6066
  const rel = prefix ? `${prefix}/${name}` : name;
5781
6067
  if (e.isDirectory()) {
5782
6068
  await walk(abs, rel);
@@ -5819,7 +6105,7 @@ var MIME = {
5819
6105
  ".map": "application/json"
5820
6106
  };
5821
6107
  function detectMimeType(filePath) {
5822
- const ext = path9.extname(filePath).toLowerCase();
6108
+ const ext = path10.extname(filePath).toLowerCase();
5823
6109
  return MIME[ext] ?? "";
5824
6110
  }
5825
6111
  var MinioClient = class {
@@ -5907,7 +6193,7 @@ function artifactObjectKey(namePrefix, branchSegment, relativePath) {
5907
6193
  return `${base}/${branchSegment}/dist/${rel}`;
5908
6194
  }
5909
6195
  async function ensureArtifactRootIndexHtml(root) {
5910
- const indexHtmlPath = path10.join(root, "index.html");
6196
+ const indexHtmlPath = path11.join(root, "index.html");
5911
6197
  try {
5912
6198
  const st = await stat(indexHtmlPath);
5913
6199
  if (st.isFile()) {
@@ -5924,7 +6210,7 @@ async function ensureArtifactRootIndexHtml(root) {
5924
6210
  }
5925
6211
  const dirNames = entries.filter((e) => e.isDirectory()).map((e) => String(e.name)).sort();
5926
6212
  for (const name of dirNames) {
5927
- const candidate = path10.join(root, name, "index.html");
6213
+ const candidate = path11.join(root, name, "index.html");
5928
6214
  try {
5929
6215
  const st = await stat(candidate);
5930
6216
  if (!st.isFile()) {
@@ -5935,7 +6221,7 @@ async function ensureArtifactRootIndexHtml(root) {
5935
6221
  }
5936
6222
  await copyFile(candidate, indexHtmlPath);
5937
6223
  console.error(
5938
- `\u4EA7\u7269\u534F\u8BAE\uFF1A\u5DF2\u5C06 ${path10.join(name, "index.html")} \u590D\u5236\u4E3A\u6839\u76EE\u5F55 index.html`
6224
+ `\u4EA7\u7269\u534F\u8BAE\uFF1A\u5DF2\u5C06 ${path11.join(name, "index.html")} \u590D\u5236\u4E3A\u6839\u76EE\u5F55 index.html`
5939
6225
  );
5940
6226
  return;
5941
6227
  }
@@ -5971,7 +6257,7 @@ function registerDeployFrontendCommands(program) {
5971
6257
  secretKey: settings.secretKey
5972
6258
  });
5973
6259
  const bucket = settings.bucket;
5974
- const root = path10.resolve(process.cwd(), opts.dir || "apps/web/dist");
6260
+ const root = path11.resolve(process.cwd(), opts.dir || "apps/web/dist");
5975
6261
  if (!await isDirectoryPath(root)) {
5976
6262
  console.error(`\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${root}`);
5977
6263
  process.exit(1);
@@ -6040,7 +6326,7 @@ function registerDeployFrontendCommands(program) {
6040
6326
  }
6041
6327
 
6042
6328
  // src/commands/deploy/sftp.ts
6043
- import path11 from "node:path";
6329
+ import path12 from "node:path";
6044
6330
  function registerDeploySftpCommands(program) {
6045
6331
  program.command("deploy-sftp").description(
6046
6332
  "\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"
@@ -6055,7 +6341,7 @@ function registerDeploySftpCommands(program) {
6055
6341
  async (opts) => {
6056
6342
  const cfg = loadApmConfig({ configPath: opts.config });
6057
6343
  const settings = resolveWisdomDeployFromApmConfig(cfg);
6058
- const root = path11.resolve(process.cwd(), opts.dir || "apps/web/dist");
6344
+ const root = path12.resolve(process.cwd(), opts.dir || "apps/web/dist");
6059
6345
  if (!await isDirectoryPath(root)) {
6060
6346
  console.error(`\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${root}`);
6061
6347
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-project-manage-cli",
3
- "version": "6.0.82",
3
+ "version": "6.0.84",
4
4
  "description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
5
5
  "type": "module",
6
6
  "private": false,
@@ -2,7 +2,9 @@
2
2
 
3
3
  ## 工作流程
4
4
 
5
- 在项目根目录执行 `apm deploy <环境名称>`,**以命令执行结果为准**:
5
+ 在项目根目录执行 `apm deploy <环境名称> --session <sessionId>`,**以命令执行结果为准**。
6
+
7
+ **sessionId**:从当前工作项目录 `.apm/sessions/<sessionId>/` 或 `session.yaml` 获取;必须传入 `--session`,平台才会记录部署。
6
8
 
7
9
  - **成功**:部署完成,在回信中说明测试环境地址或部署结果。
8
10
  - **失败且输出含「未配置自动化部署」或「可跳过部署」**:本项目不支持自动化部署,属正常情况;放弃部署,在回信说明已跳过即可,无需修复配置或重试。
@@ -10,5 +12,5 @@
10
12
 
11
13
  ## 示例
12
14
 
13
- 1. 部署测试环境:`apm deploy test`
14
- 2. 部署正式环境:`apm deploy online`
15
+ 1. 部署测试环境:`apm deploy test --session <sessionId>`
16
+ 2. 部署正式环境:`apm deploy online --session <sessionId>`