ai-project-manage-cli 6.0.81 → 6.0.83

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;
@@ -3959,8 +3963,9 @@ async function runCreatePr(options) {
3959
3963
  console.log(`[apm] PR \u5DF2\u5C31\u7EEA #${pr.number} (${pr.state}): ${pr.url}`);
3960
3964
  }
3961
3965
 
3962
- // src/commands/deploy/deploy.ts
3966
+ // src/commands/deploy/deploy-execute.ts
3963
3967
  import { spawnSync as spawnSync5 } from "node:child_process";
3968
+ import path4 from "node:path";
3964
3969
 
3965
3970
  // src/commands/deploy/internal/apm-config.ts
3966
3971
  import { existsSync as existsSync15, readFileSync as readFileSync12 } from "node:fs";
@@ -4152,9 +4157,9 @@ function readMavenLocalRepoFromEnv(env = process.env) {
4152
4157
  }
4153
4158
  const mavenOpts = readEnvVar(env, "MAVEN_OPTS")?.trim();
4154
4159
  if (mavenOpts) {
4155
- const path12 = readMavenLocalRepoFromMavenOpts(mavenOpts, env);
4156
- if (path12) {
4157
- return { path: path12, key: "MAVEN_OPTS" };
4160
+ const path13 = readMavenLocalRepoFromMavenOpts(mavenOpts, env);
4161
+ if (path13) {
4162
+ return { path: path13, key: "MAVEN_OPTS" };
4158
4163
  }
4159
4164
  }
4160
4165
  return null;
@@ -4265,6 +4270,41 @@ function posixBasename(p) {
4265
4270
  return idx >= 0 ? normalized.slice(idx + 1) : normalized;
4266
4271
  }
4267
4272
 
4273
+ // src/commands/deploy/internal/deploy-hints.ts
4274
+ function formatDeployNotConfiguredLines(env, deployCommands) {
4275
+ const configuredEnvs = Object.keys(deployCommands).filter(
4276
+ (key) => deployCommands[key]?.trim()
4277
+ );
4278
+ if (configuredEnvs.length > 0) {
4279
+ return [
4280
+ `[apm] \u65E0\u6CD5\u6267\u884C\u90E8\u7F72\uFF1Aapm.config.json \u4E2D\u672A\u914D\u7F6E deploy.${env}`,
4281
+ `[apm] \u5F53\u524D\u5DF2\u914D\u7F6E\u7684\u73AF\u5883: ${configuredEnvs.join(", ")}`
4282
+ ];
4283
+ }
4284
+ return [
4285
+ "[apm] \u65E0\u6CD5\u6267\u884C\u90E8\u7F72\uFF1A\u5F53\u524D\u9879\u76EE\u672A\u914D\u7F6E\u81EA\u52A8\u5316\u90E8\u7F72\uFF08apm.config.json \u4E2D\u65E0 deploy \u6BB5\uFF09",
4286
+ "[apm] \u90E8\u5206\u9879\u76EE\u4E0D\u652F\u6301\u6216\u672A\u542F\u7528\u81EA\u52A8\u5316\u90E8\u7F72\uFF0C\u5C5E\u6B63\u5E38\u60C5\u51B5\uFF0C\u53EF\u8DF3\u8FC7\u90E8\u7F72"
4287
+ ];
4288
+ }
4289
+ function formatWisdomFrontendDeployNotConfiguredLines(env, packageJsonPath) {
4290
+ return [
4291
+ `[apm] \u65E0\u6CD5\u6267\u884C\u90E8\u7F72\uFF1A${packageJsonPath} \u4E2D\u672A\u627E\u5230 deploy:${env} \u811A\u672C`,
4292
+ "[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"
4293
+ ];
4294
+ }
4295
+
4296
+ // src/commands/deploy/deploy-errors.ts
4297
+ var DeployExecutionError = class extends Error {
4298
+ exitCode;
4299
+ output;
4300
+ constructor(message, exitCode, output = "") {
4301
+ super(message);
4302
+ this.name = "DeployExecutionError";
4303
+ this.exitCode = exitCode;
4304
+ this.output = output;
4305
+ }
4306
+ };
4307
+
4268
4308
  // src/commands/deploy/internal/wisdom-auto-deploy.ts
4269
4309
  import { existsSync as existsSync17, readFileSync as readFileSync14 } from "node:fs";
4270
4310
  import path3 from "node:path";
@@ -5069,19 +5109,33 @@ function resolveFrontendDeployCommand(env, cwd) {
5069
5109
  }
5070
5110
  return null;
5071
5111
  }
5072
- function runShellCommand2(command, cwd) {
5112
+ function runShellCommand2(command, cwd, captureOutput) {
5073
5113
  const result = spawnSync4(command, {
5074
5114
  cwd,
5075
- stdio: "inherit",
5076
5115
  shell: true,
5077
- env: process.env
5116
+ env: process.env,
5117
+ encoding: "utf8",
5118
+ stdio: captureOutput ? ["inherit", "pipe", "pipe"] : "inherit"
5078
5119
  });
5120
+ const stdout = captureOutput ? String(result.stdout ?? "") : "";
5121
+ const stderr = captureOutput ? String(result.stderr ?? "") : "";
5122
+ const output = [stdout, stderr].filter(Boolean).join("\n");
5123
+ if (captureOutput) {
5124
+ if (stdout) process.stdout.write(stdout);
5125
+ if (stderr) process.stderr.write(stderr);
5126
+ }
5079
5127
  if (result.status !== 0) {
5080
- process.exit(result.status ?? 1);
5128
+ throw new DeployExecutionError(
5129
+ `\u90E8\u7F72\u547D\u4EE4\u9000\u51FA\u7801 ${result.status ?? "unknown"}: ${command}`,
5130
+ result.status ?? 1,
5131
+ output
5132
+ );
5081
5133
  }
5134
+ return output;
5082
5135
  }
5083
5136
  async function runWisdomAutoDeploy(options) {
5084
5137
  const cwd = path3.resolve(options.cwd ?? process.cwd());
5138
+ const captureOutput = options.captureOutput ?? false;
5085
5139
  const projectType = detectWisdomProjectType(cwd);
5086
5140
  console.error(
5087
5141
  `[apm] \u533B\u52A1\u5B58\u91CF\u9879\u76EE\u81EA\u52A8\u8BC6\u522B: ${projectType === "frontend" ? "\u524D\u7AEF Vue" : "\u540E\u7AEF Java"}`
@@ -5089,12 +5143,13 @@ async function runWisdomAutoDeploy(options) {
5089
5143
  if (projectType === "frontend") {
5090
5144
  const deployCmd = resolveFrontendDeployCommand(options.env, cwd);
5091
5145
  if (!deployCmd) {
5092
- console.error(
5093
- `\u524D\u7AEF\u9879\u76EE ${path3.join(cwd, "package.json")} \u4E2D\u672A\u627E\u5230 deploy:${options.env} \u811A\u672C`
5146
+ const lines = formatWisdomFrontendDeployNotConfiguredLines(
5147
+ options.env,
5148
+ path3.join(cwd, "package.json")
5094
5149
  );
5095
- process.exit(1);
5150
+ throw new DeployExecutionError(lines.join("\n"), 1, lines.join("\n"));
5096
5151
  }
5097
- runShellCommand2(deployCmd, cwd);
5152
+ runShellCommand2(deployCmd, cwd, captureOutput);
5098
5153
  return { projectType };
5099
5154
  }
5100
5155
  const configPath = options.configPath ?? path3.join(workspaceApmDir(cwd), "apm.config.json");
@@ -5110,68 +5165,229 @@ async function runWisdomAutoDeploy(options) {
5110
5165
  return { projectType };
5111
5166
  }
5112
5167
 
5113
- // src/commands/deploy/deploy.ts
5114
- function runShellCommand3(command, cwd) {
5168
+ // src/commands/deploy/deploy-execute.ts
5169
+ function runShellCommand3(command, cwd, captureOutput) {
5115
5170
  const result = spawnSync5(command, {
5116
5171
  cwd,
5117
- stdio: "inherit",
5118
5172
  shell: true,
5119
- env: process.env
5173
+ env: process.env,
5174
+ encoding: "utf8",
5175
+ stdio: captureOutput ? ["inherit", "pipe", "pipe"] : "inherit"
5120
5176
  });
5177
+ const stdout = captureOutput ? String(result.stdout ?? "") : "";
5178
+ const stderr = captureOutput ? String(result.stderr ?? "") : "";
5179
+ const output = [stdout, stderr].filter(Boolean).join("\n");
5180
+ if (captureOutput) {
5181
+ if (stdout) process.stdout.write(stdout);
5182
+ if (stderr) process.stderr.write(stderr);
5183
+ }
5121
5184
  if (result.status !== 0) {
5122
- process.exit(result.status ?? 1);
5185
+ throw new DeployExecutionError(
5186
+ `\u90E8\u7F72\u547D\u4EE4\u9000\u51FA\u7801 ${result.status ?? "unknown"}: ${command}`,
5187
+ result.status ?? 1,
5188
+ output
5189
+ );
5190
+ }
5191
+ return output;
5192
+ }
5193
+ async function executeDeploy(options) {
5194
+ const cwd = path4.resolve(options.cwd ?? process.cwd());
5195
+ const captureOutput = options.captureOutput ?? false;
5196
+ const cfg = loadApmConfig({ configPath: options.configPath });
5197
+ if (isWisdomLegacyDeploy(cfg)) {
5198
+ try {
5199
+ await runWisdomAutoDeploy({
5200
+ env: options.env,
5201
+ cwd,
5202
+ configPath: options.configPath,
5203
+ captureOutput
5204
+ });
5205
+ return "";
5206
+ } catch (error) {
5207
+ if (error instanceof DeployExecutionError) {
5208
+ throw error;
5209
+ }
5210
+ const detail = error instanceof Error ? error.message : String(error);
5211
+ throw new DeployExecutionError(detail, 1);
5212
+ }
5213
+ }
5214
+ const deployCommands = cfg.deploy ?? {};
5215
+ const command = deployCommands[options.env]?.trim();
5216
+ if (!command) {
5217
+ const lines = formatDeployNotConfiguredLines(options.env, deployCommands);
5218
+ throw new DeployExecutionError(lines.join("\n"), 1, lines.join("\n"));
5219
+ }
5220
+ return runShellCommand3(command, cwd, captureOutput);
5221
+ }
5222
+ function printDeployExecutionError(error) {
5223
+ if (error.output.trim()) {
5224
+ console.error(error.output);
5225
+ return;
5226
+ }
5227
+ console.error(error.message);
5228
+ }
5229
+
5230
+ // src/commands/deploy/deploy-tracking.ts
5231
+ function normalizeDeployEnvironment(env) {
5232
+ const normalized = env.trim().toLowerCase();
5233
+ if (normalized === "test") return "TEST";
5234
+ if (normalized === "online") return "ONLINE";
5235
+ return null;
5236
+ }
5237
+ async function completeTrackedDeploy(api, deploymentRunId, logSyncer, input) {
5238
+ logSyncer.updateLog(input.log);
5239
+ await logSyncer.flush();
5240
+ await api.cli.completeTaskDeployment({
5241
+ id: deploymentRunId,
5242
+ status: input.status,
5243
+ log: input.log,
5244
+ error: input.error
5245
+ });
5246
+ }
5247
+ async function runDeployWithBackendTracking(options) {
5248
+ const cfg = await tryReadApmConfig();
5249
+ if (!cfg || !resolveApiKey(cfg)) {
5250
+ console.warn("[apm] \u672A\u767B\u5F55\uFF0C\u8DF3\u8FC7\u540E\u7AEF\u90E8\u7F72\u8BB0\u5F55");
5251
+ await executeDeploy(options);
5252
+ return;
5253
+ }
5254
+ const environment = normalizeDeployEnvironment(options.env);
5255
+ if (!environment) {
5256
+ console.warn(
5257
+ `[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`
5258
+ );
5259
+ await executeDeploy(options);
5260
+ return;
5261
+ }
5262
+ const api = createApmApiClient(cfg);
5263
+ let deploymentRunId;
5264
+ try {
5265
+ const run = await api.cli.createTaskDeployment({
5266
+ sessionId: options.sessionId,
5267
+ environment,
5268
+ workdirPath: options.cwd ?? process.cwd()
5269
+ });
5270
+ deploymentRunId = run.id;
5271
+ console.log(
5272
+ `[apm] \u5DF2\u521B\u5EFA\u90E8\u7F72\u8BB0\u5F55 id=${deploymentRunId} env=${environment}`
5273
+ );
5274
+ } catch (error) {
5275
+ const detail = error instanceof Error ? error.message : String(error);
5276
+ console.warn(`[apm] \u521B\u5EFA\u90E8\u7F72\u8BB0\u5F55\u5931\u8D25\uFF0C\u7EE7\u7EED\u90E8\u7F72: ${detail}`);
5277
+ await executeDeploy(options);
5278
+ return;
5279
+ }
5280
+ await api.cli.updateTaskDeploymentStatus({
5281
+ id: deploymentRunId,
5282
+ status: "DEPLOYING"
5283
+ });
5284
+ const logSyncer = createDeployLogSyncer(api, deploymentRunId);
5285
+ const chunks = [];
5286
+ const appendLog = (line) => {
5287
+ if (!line) return;
5288
+ chunks.push(line);
5289
+ logSyncer.updateLog(chunks.join("\n"));
5290
+ };
5291
+ const originalLog = console.log;
5292
+ const originalError = console.error;
5293
+ console.log = (...args) => {
5294
+ appendLog(args.map(String).join(" "));
5295
+ originalLog.apply(console, args);
5296
+ };
5297
+ console.error = (...args) => {
5298
+ appendLog(args.map(String).join(" "));
5299
+ originalError.apply(console, args);
5300
+ };
5301
+ try {
5302
+ const output = await executeDeploy({ ...options, captureOutput: true });
5303
+ if (output.trim()) {
5304
+ appendLog(output);
5305
+ }
5306
+ const log2 = chunks.join("\n");
5307
+ await completeTrackedDeploy(api, deploymentRunId, logSyncer, {
5308
+ status: "SUCCESS",
5309
+ log: log2
5310
+ });
5311
+ console.log(`[apm] deploy success id=${deploymentRunId}`);
5312
+ } catch (error) {
5313
+ const deployError = error instanceof DeployExecutionError ? error : new DeployExecutionError(
5314
+ error instanceof Error ? error.message : String(error),
5315
+ 1
5316
+ );
5317
+ if (deployError.output.trim()) {
5318
+ appendLog(deployError.output);
5319
+ } else {
5320
+ appendLog(deployError.message);
5321
+ }
5322
+ const log2 = chunks.join("\n");
5323
+ await completeTrackedDeploy(api, deploymentRunId, logSyncer, {
5324
+ status: "FAILED",
5325
+ log: log2,
5326
+ error: deployError.message
5327
+ });
5328
+ printDeployExecutionError(deployError);
5329
+ process.exit(deployError.exitCode);
5330
+ } finally {
5331
+ console.log = originalLog;
5332
+ console.error = originalError;
5333
+ logSyncer.dispose();
5123
5334
  }
5124
5335
  }
5336
+
5337
+ // src/commands/deploy/deploy.ts
5125
5338
  function registerDeployMainCommand(program) {
5126
5339
  program.command("deploy").description(
5127
5340
  "\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"
5128
5341
  ).argument("<env>", "\u90E8\u7F72\u73AF\u5883\u540D\uFF08\u5982 test\u3001online\uFF09").option(
5129
5342
  "--config <path>",
5130
5343
  "apm.config.json \u8DEF\u5F84\uFF08\u9ED8\u8BA4 .apm/apm.config.json\uFF09"
5131
- ).action(async (env, opts) => {
5132
- const cwd = process.cwd();
5133
- const cfg = loadApmConfig({ configPath: opts.config });
5134
- if (isWisdomLegacyDeploy(cfg)) {
5135
- await runWisdomAutoDeploy({
5136
- env,
5137
- cwd,
5138
- configPath: opts.config
5139
- });
5140
- return;
5141
- }
5142
- const deployCommands = cfg.deploy ?? {};
5143
- const command = deployCommands[env]?.trim();
5144
- if (!command) {
5145
- const keys = Object.keys(deployCommands);
5146
- console.error(
5147
- keys.length > 0 ? `apm.config.json \u4E2D\u672A\u914D\u7F6E deploy.${env}\uFF0C\u53EF\u7528\u73AF\u5883: ${keys.join(
5148
- ", "
5149
- )}` : `apm.config.json \u4E2D\u672A\u914D\u7F6E deploy \u6BB5`
5150
- );
5151
- process.exit(1);
5344
+ ).option(
5345
+ "--session <sessionId>",
5346
+ "\u6C9F\u901A\u7FA4 ID\uFF1B\u4F20\u5165\u65F6\u5728\u5E73\u53F0\u521B\u5EFA\u90E8\u7F72\u8BB0\u5F55\u5E76\u540C\u6B65\u65E5\u5FD7"
5347
+ ).action(
5348
+ async (env, opts) => {
5349
+ const cwd = process.cwd();
5350
+ const sessionId = opts.session?.trim();
5351
+ try {
5352
+ if (sessionId) {
5353
+ await runDeployWithBackendTracking({
5354
+ env,
5355
+ cwd,
5356
+ configPath: opts.config,
5357
+ sessionId
5358
+ });
5359
+ return;
5360
+ }
5361
+ await executeDeploy({ env, cwd, configPath: opts.config });
5362
+ } catch (error) {
5363
+ if (error instanceof DeployExecutionError) {
5364
+ printDeployExecutionError(error);
5365
+ process.exit(error.exitCode);
5366
+ }
5367
+ throw error;
5368
+ }
5152
5369
  }
5153
- runShellCommand3(command, cwd);
5154
- });
5370
+ );
5155
5371
  }
5156
5372
 
5157
5373
  // src/commands/deploy/backend.ts
5158
- import path8 from "node:path";
5374
+ import path9 from "node:path";
5159
5375
 
5160
5376
  // src/commands/deploy/internal/backend-deploy/backend-deploy-workflow.ts
5161
- import path7 from "node:path";
5377
+ import path8 from "node:path";
5162
5378
 
5163
5379
  // src/commands/deploy/internal/backend-deploy/dockerode-client/client.ts
5164
5380
  import Docker from "dockerode";
5165
5381
 
5166
5382
  // src/commands/deploy/internal/backend-deploy/dockerode-client/connection-options.ts
5167
5383
  import { existsSync as existsSync18, readFileSync as readFileSync15 } from "node:fs";
5168
- import path4 from "node:path";
5384
+ import path5 from "node:path";
5169
5385
  function asOptionalTlsBuffer(value) {
5170
5386
  if (typeof value !== "string") {
5171
5387
  console.log("tls filepath not exist");
5172
5388
  return void 0;
5173
5389
  }
5174
- console.log("tls filepath", path4.join(process.cwd(), value));
5390
+ console.log("tls filepath", path5.join(process.cwd(), value));
5175
5391
  const normalized = value.trim();
5176
5392
  if (normalized === "") {
5177
5393
  return void 0;
@@ -5388,7 +5604,7 @@ var createDockerodeClient = (config) => new DockerodeClient(config);
5388
5604
 
5389
5605
  // src/commands/deploy/internal/backend-deploy/dockerode-client/env.ts
5390
5606
  import { existsSync as existsSync19, readFileSync as readFileSync16, statSync as statSync6 } from "node:fs";
5391
- import path5 from "node:path";
5607
+ import path6 from "node:path";
5392
5608
  function stripSurroundingQuotes(value) {
5393
5609
  const t = value.trim();
5394
5610
  if (t.length >= 2) {
@@ -5403,7 +5619,7 @@ function loadEnvFromFile(envFilePath) {
5403
5619
  if (!envFilePath) {
5404
5620
  return {};
5405
5621
  }
5406
- const targetPath = path5.resolve(envFilePath);
5622
+ const targetPath = path6.resolve(envFilePath);
5407
5623
  if (!existsSync19(targetPath) || !statSync6(targetPath).isFile()) {
5408
5624
  return {};
5409
5625
  }
@@ -5579,9 +5795,9 @@ function dockerPushImage(params, cwd) {
5579
5795
 
5580
5796
  // src/commands/deploy/internal/backend-deploy/resolve-dockerfile.ts
5581
5797
  import { existsSync as existsSync20 } from "node:fs";
5582
- import path6 from "node:path";
5798
+ import path7 from "node:path";
5583
5799
  function resolveDockerBuildPaths(cwd) {
5584
- const dockerfilePath = path6.join(cwd, "Dockerfile");
5800
+ const dockerfilePath = path7.join(cwd, "Dockerfile");
5585
5801
  Logger.info(`\u67E5\u627EDockerfile\u6587\u4EF6\uFF0C\u8DEF\u5F84: ${dockerfilePath}`);
5586
5802
  if (!existsSync20(dockerfilePath)) {
5587
5803
  throw new Error(`Dockerfile \u4E0D\u5B58\u5728\uFF1A${dockerfilePath}`);
@@ -5638,7 +5854,7 @@ var BackendDeployWorkflow = class {
5638
5854
  serveraddress
5639
5855
  });
5640
5856
  Logger.success("\u8FDC\u7A0B\u62C9\u53D6\u955C\u50CF\u5B8C\u6210");
5641
- const envFilePath = path7.resolve(cwd, this.params.envFilePath || ".env");
5857
+ const envFilePath = path8.resolve(cwd, this.params.envFilePath || ".env");
5642
5858
  const envObj = loadEnvFromFile(envFilePath);
5643
5859
  if (this.params.dockerNetwork?.trim()) {
5644
5860
  Logger.info(`\u8FDC\u7A0B\u5BB9\u5668\u5C06\u52A0\u5165 Docker \u7F51\u7EDC\uFF1A${this.params.dockerNetwork.trim()}`);
@@ -5678,7 +5894,7 @@ function registerDeployBackendCommands(program) {
5678
5894
  assertDeployImageTag(tag);
5679
5895
  const cfg = loadApmConfig({ configPath: opts.config });
5680
5896
  const fromApm = resolveBackendDeployFromApmConfig(cfg);
5681
- const dirAbs = path8.resolve(process.cwd(), opts.dir || "servers/api");
5897
+ const dirAbs = path9.resolve(process.cwd(), opts.dir || "servers/api");
5682
5898
  const params = {
5683
5899
  image: fromApm.name,
5684
5900
  tag,
@@ -5709,12 +5925,12 @@ function registerDeployBackendCommands(program) {
5709
5925
 
5710
5926
  // src/commands/deploy/frontend.ts
5711
5927
  import { copyFile, readdir as readdir3, stat } from "node:fs/promises";
5712
- import path10 from "node:path";
5928
+ import path11 from "node:path";
5713
5929
 
5714
5930
  // src/commands/deploy/internal/minio.ts
5715
5931
  import { statSync as statSync7 } from "node:fs";
5716
5932
  import { readdir as readdir2, readFile as readFile2 } from "node:fs/promises";
5717
- import path9 from "node:path";
5933
+ import path10 from "node:path";
5718
5934
  import * as Minio from "minio";
5719
5935
  var DEFAULT_MAX_FILE_SIZE_MB = 50;
5720
5936
  async function isDirectoryPath(dir) {
@@ -5744,7 +5960,7 @@ async function collectFiles(root) {
5744
5960
  if (name === "." || name === "..") {
5745
5961
  continue;
5746
5962
  }
5747
- const abs = path9.join(dir, name);
5963
+ const abs = path10.join(dir, name);
5748
5964
  const rel = prefix ? `${prefix}/${name}` : name;
5749
5965
  if (e.isDirectory()) {
5750
5966
  await walk(abs, rel);
@@ -5787,7 +6003,7 @@ var MIME = {
5787
6003
  ".map": "application/json"
5788
6004
  };
5789
6005
  function detectMimeType(filePath) {
5790
- const ext = path9.extname(filePath).toLowerCase();
6006
+ const ext = path10.extname(filePath).toLowerCase();
5791
6007
  return MIME[ext] ?? "";
5792
6008
  }
5793
6009
  var MinioClient = class {
@@ -5875,7 +6091,7 @@ function artifactObjectKey(namePrefix, branchSegment, relativePath) {
5875
6091
  return `${base}/${branchSegment}/dist/${rel}`;
5876
6092
  }
5877
6093
  async function ensureArtifactRootIndexHtml(root) {
5878
- const indexHtmlPath = path10.join(root, "index.html");
6094
+ const indexHtmlPath = path11.join(root, "index.html");
5879
6095
  try {
5880
6096
  const st = await stat(indexHtmlPath);
5881
6097
  if (st.isFile()) {
@@ -5892,7 +6108,7 @@ async function ensureArtifactRootIndexHtml(root) {
5892
6108
  }
5893
6109
  const dirNames = entries.filter((e) => e.isDirectory()).map((e) => String(e.name)).sort();
5894
6110
  for (const name of dirNames) {
5895
- const candidate = path10.join(root, name, "index.html");
6111
+ const candidate = path11.join(root, name, "index.html");
5896
6112
  try {
5897
6113
  const st = await stat(candidate);
5898
6114
  if (!st.isFile()) {
@@ -5903,7 +6119,7 @@ async function ensureArtifactRootIndexHtml(root) {
5903
6119
  }
5904
6120
  await copyFile(candidate, indexHtmlPath);
5905
6121
  console.error(
5906
- `\u4EA7\u7269\u534F\u8BAE\uFF1A\u5DF2\u5C06 ${path10.join(name, "index.html")} \u590D\u5236\u4E3A\u6839\u76EE\u5F55 index.html`
6122
+ `\u4EA7\u7269\u534F\u8BAE\uFF1A\u5DF2\u5C06 ${path11.join(name, "index.html")} \u590D\u5236\u4E3A\u6839\u76EE\u5F55 index.html`
5907
6123
  );
5908
6124
  return;
5909
6125
  }
@@ -5939,7 +6155,7 @@ function registerDeployFrontendCommands(program) {
5939
6155
  secretKey: settings.secretKey
5940
6156
  });
5941
6157
  const bucket = settings.bucket;
5942
- const root = path10.resolve(process.cwd(), opts.dir || "apps/web/dist");
6158
+ const root = path11.resolve(process.cwd(), opts.dir || "apps/web/dist");
5943
6159
  if (!await isDirectoryPath(root)) {
5944
6160
  console.error(`\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${root}`);
5945
6161
  process.exit(1);
@@ -6008,7 +6224,7 @@ function registerDeployFrontendCommands(program) {
6008
6224
  }
6009
6225
 
6010
6226
  // src/commands/deploy/sftp.ts
6011
- import path11 from "node:path";
6227
+ import path12 from "node:path";
6012
6228
  function registerDeploySftpCommands(program) {
6013
6229
  program.command("deploy-sftp").description(
6014
6230
  "\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"
@@ -6023,7 +6239,7 @@ function registerDeploySftpCommands(program) {
6023
6239
  async (opts) => {
6024
6240
  const cfg = loadApmConfig({ configPath: opts.config });
6025
6241
  const settings = resolveWisdomDeployFromApmConfig(cfg);
6026
- const root = path11.resolve(process.cwd(), opts.dir || "apps/web/dist");
6242
+ const root = path12.resolve(process.cwd(), opts.dir || "apps/web/dist");
6027
6243
  if (!await isDirectoryPath(root)) {
6028
6244
  console.error(`\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${root}`);
6029
6245
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-project-manage-cli",
3
- "version": "6.0.81",
3
+ "version": "6.0.83",
4
4
  "description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
5
5
  "type": "module",
6
6
  "private": false,
@@ -1,8 +1,16 @@
1
+ # apm-deploy:自动化部署
2
+
1
3
  ## 工作流程
2
4
 
3
- 在项目根目录执行 `apm deploy <环境名称>`
5
+ 在项目根目录执行 `apm deploy <环境名称> --session <sessionId>`,**以命令执行结果为准**。
6
+
7
+ **sessionId**:从当前工作项目录 `.apm/sessions/<sessionId>/` 或 `session.yaml` 获取;必须传入 `--session`,平台才会记录部署。
8
+
9
+ - **成功**:部署完成,在回信中说明测试环境地址或部署结果。
10
+ - **失败且输出含「未配置自动化部署」或「可跳过部署」**:本项目不支持自动化部署,属正常情况;放弃部署,在回信说明已跳过即可,无需修复配置或重试。
11
+ - **其他失败**:按输出排查并修复后重试。
4
12
 
5
13
  ## 示例
6
14
 
7
- 1. 部署测试环境:`apm deploy test`
8
- 2. 部署正式环境: `apm deploy online`
15
+ 1. 部署测试环境:`apm deploy test --session <sessionId>`
16
+ 2. 部署正式环境:`apm deploy online --session <sessionId>`
@@ -86,7 +86,7 @@ ALTER TABLE inspection_class ADD COLUMN is_project_add VARCHAR(1) DEFAULT '0';
86
86
  开发完成的定义是以下三项**全部满足**,缺一不可:
87
87
 
88
88
  1. **构建通过**:执行本仓库的构建/检查命令(见 AGENTS.md 或部署文档),失败必须修复后重试。
89
- 2. **发布测试环境**:**Read** `.apm/skills/apm-deploy/SKILL.md` 并按其流程部署;**后端**涉及 SQL 变更时,须在回复中引用 `docs/SQL.md`,并写明待执行的 SQL 文件名或执行顺序。
89
+ 2. **发布测试环境**:**Read** `.apm/skills/apm-deploy/SKILL.md` 并按其流程执行 `apm deploy`(以命令结果判断是否跳过);**后端**涉及 SQL 变更时,须在回复中引用 `docs/SQL.md`,并写明待执行的 SQL 文件名或执行顺序。
90
90
  3. **白名单对账**:在回复中逐文件列出本次改动与计划白名单的对应关系。
91
91
 
92
92
  **注意:不做联调。** 前后端各自按 `API.md` 交付,接口对不上属于契约或实现问题,由 diff 评审与人工验收暴露后打回修复;禁止自行发起「联调」「接口实测」类的开放式动作。