ai-project-manage-cli 8.0.12 → 8.0.14

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.
@@ -205,8 +205,8 @@ function toFsPath(inputPath) {
205
205
  }
206
206
  return `\\\\?\\${normalized}`;
207
207
  }
208
- function normalizeWorkdirPath(path) {
209
- let normalized = path.trim().replace(/\\/g, "/").normalize("NFC");
208
+ function normalizeWorkdirPath(path2) {
209
+ let normalized = path2.trim().replace(/\\/g, "/").normalize("NFC");
210
210
  if (normalized.startsWith("//?/")) {
211
211
  normalized = normalized.slice(4);
212
212
  }
@@ -328,9 +328,9 @@ function listWebIdeRuleFileNames(rulesDir) {
328
328
  function assertWebIdeTemplateCopiedToApm(apmDir, workdir) {
329
329
  const required = ["AGENTS.md", "rules"];
330
330
  for (const item of required) {
331
- const path = join2(apmDir, item);
332
- if (!existsSync(toFsPath(path))) {
333
- throw new Error(`[apm] WebIDE \u521D\u59CB\u5316\u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5C11: ${path}`);
331
+ const path2 = join2(apmDir, item);
332
+ if (!existsSync(toFsPath(path2))) {
333
+ throw new Error(`[apm] WebIDE \u521D\u59CB\u5316\u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5C11: ${path2}`);
334
334
  }
335
335
  }
336
336
  const rulesDir = join2(apmDir, "rules");
@@ -614,13 +614,13 @@ function normalizeRepoEntry(raw) {
614
614
  return entry;
615
615
  }
616
616
  function readManifest(workdir) {
617
- const path = toFsPath(manifestPath(workdir));
618
- if (!existsSync2(path)) {
617
+ const path2 = toFsPath(manifestPath(workdir));
618
+ if (!existsSync2(path2)) {
619
619
  return null;
620
620
  }
621
621
  try {
622
622
  const raw = JSON.parse(
623
- readFileSync2(path, "utf8")
623
+ readFileSync2(path2, "utf8")
624
624
  );
625
625
  if (raw?.version !== WORKSPACE_REPOS_VERSION) {
626
626
  return null;
@@ -652,8 +652,8 @@ function readManifest(workdir) {
652
652
  function writeManifest(workdir, manifest) {
653
653
  const apmDir = toFsPath(workspaceApmDir(workdir));
654
654
  mkdirSync3(apmDir, { recursive: true });
655
- const path = toFsPath(manifestPath(workdir));
656
- writeFileSync2(path, `${JSON.stringify(manifest, null, 2)}
655
+ const path2 = toFsPath(manifestPath(workdir));
656
+ writeFileSync2(path2, `${JSON.stringify(manifest, null, 2)}
657
657
  `, "utf8");
658
658
  }
659
659
  function manifestStillValid(workdir, manifest) {
@@ -2226,12 +2226,12 @@ import { dirname as dirname3, resolve as resolve5 } from "node:path";
2226
2226
  function registryPath(workdir, taskId) {
2227
2227
  return resolve5(workdir, ".apm", "webide", taskId, "cursor-agent.json");
2228
2228
  }
2229
- function readRegistry(path) {
2230
- if (!existsSync4(path)) {
2229
+ function readRegistry(path2) {
2230
+ if (!existsSync4(path2)) {
2231
2231
  return {};
2232
2232
  }
2233
2233
  try {
2234
- const parsed = JSON.parse(readFileSync4(path, "utf8"));
2234
+ const parsed = JSON.parse(readFileSync4(path2, "utf8"));
2235
2235
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
2236
2236
  const raw = parsed;
2237
2237
  const state = {};
@@ -2259,16 +2259,16 @@ function readRegistry(path) {
2259
2259
  }
2260
2260
  return {};
2261
2261
  }
2262
- function writeRegistry(path, registry) {
2263
- mkdirSync4(dirname3(path), { recursive: true });
2264
- writeFileSync3(path, `${JSON.stringify(registry, null, 2)}
2262
+ function writeRegistry(path2, registry) {
2263
+ mkdirSync4(dirname3(path2), { recursive: true });
2264
+ writeFileSync3(path2, `${JSON.stringify(registry, null, 2)}
2265
2265
  `, "utf8");
2266
2266
  }
2267
2267
  function syncWebIdeTaskState(workdir, taskId, patch) {
2268
2268
  const trimmedTaskId = taskId.trim();
2269
2269
  if (!trimmedTaskId) return;
2270
- const path = registryPath(workdir, trimmedTaskId);
2271
- const current = readRegistry(path);
2270
+ const path2 = registryPath(workdir, trimmedTaskId);
2271
+ const current = readRegistry(path2);
2272
2272
  const next = {
2273
2273
  ...current,
2274
2274
  taskId: trimmedTaskId,
@@ -2292,7 +2292,7 @@ function syncWebIdeTaskState(workdir, taskId, patch) {
2292
2292
  if (patch.status !== void 0) {
2293
2293
  next.status = patch.status;
2294
2294
  }
2295
- writeRegistry(path, next);
2295
+ writeRegistry(path2, next);
2296
2296
  }
2297
2297
  function loadWebIdeAgentId(workdir, taskId) {
2298
2298
  return readRegistry(registryPath(workdir, taskId)).agentId;
@@ -2301,8 +2301,8 @@ function saveWebIdeAgentId(workdir, taskId, agentId) {
2301
2301
  syncWebIdeTaskState(workdir, taskId, { agentId });
2302
2302
  }
2303
2303
  function clearWebIdeAgentId(workdir, taskId) {
2304
- const path = registryPath(workdir, taskId);
2305
- if (!existsSync4(path)) return;
2304
+ const path2 = registryPath(workdir, taskId);
2305
+ if (!existsSync4(path2)) return;
2306
2306
  syncWebIdeTaskState(workdir, taskId, { agentId: "" });
2307
2307
  }
2308
2308
 
@@ -2502,6 +2502,10 @@ var MinioClient = class {
2502
2502
  async putObject(bucket, objectKey, body, meta) {
2503
2503
  await this.inner.putObject(bucket, objectKey, body, body.length, meta);
2504
2504
  }
2505
+ /** 大文件:从本地路径流式上传,避免整包读入内存 */
2506
+ async fPutObject(bucket, objectKey, filePath, meta) {
2507
+ await this.inner.fPutObject(bucket, objectKey, filePath, meta);
2508
+ }
2505
2509
  };
2506
2510
 
2507
2511
  // src/commands/connect/apm-log-minio.ts
@@ -2519,7 +2523,8 @@ async function createApmLogMinioClient(cfg) {
2519
2523
  accessKey: storage.accessKey,
2520
2524
  secretKey: storage.secretKey
2521
2525
  }),
2522
- bucket: storage.bucket
2526
+ bucket: storage.bucket,
2527
+ ...storage.publicEndpoint ? { publicEndpoint: storage.publicEndpoint } : {}
2523
2528
  };
2524
2529
  }
2525
2530
 
@@ -2949,13 +2954,13 @@ async function downloadAttachment(cfg, attachmentId) {
2949
2954
  return Buffer.from(await res.arrayBuffer());
2950
2955
  }
2951
2956
  function loadManifest(dir) {
2952
- const path = join5(dir, MANIFEST_FILE);
2953
- if (!existsSync6(path)) {
2957
+ const path2 = join5(dir, MANIFEST_FILE);
2958
+ if (!existsSync6(path2)) {
2954
2959
  return { version: 1, attachments: {} };
2955
2960
  }
2956
2961
  try {
2957
2962
  const parsed = JSON.parse(
2958
- readFileSync6(path, "utf8")
2963
+ readFileSync6(path2, "utf8")
2959
2964
  );
2960
2965
  if (parsed?.version === 1 && parsed.attachments && typeof parsed.attachments === "object") {
2961
2966
  return parsed;
@@ -3050,14 +3055,14 @@ function projectDocumentLocalPath(apmRoot, projectId, documentPath) {
3050
3055
  ...normalized.split("/")
3051
3056
  );
3052
3057
  }
3053
- function normalizeLocalDocumentPath(path) {
3054
- const trimmed = path.trim().replace(/\\/g, "/");
3058
+ function normalizeLocalDocumentPath(path2) {
3059
+ const trimmed = path2.trim().replace(/\\/g, "/");
3055
3060
  if (!trimmed || trimmed.startsWith("/") || /^[a-zA-Z]:/.test(trimmed)) {
3056
- throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path}`);
3061
+ throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path2}`);
3057
3062
  }
3058
3063
  const segments = trimmed.split("/").filter(Boolean);
3059
3064
  if (segments.some((segment) => segment === ".." || segment === ".")) {
3060
- throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path}`);
3065
+ throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path2}`);
3061
3066
  }
3062
3067
  return segments.join("/");
3063
3068
  }
@@ -3111,15 +3116,15 @@ function diffManifestPaths(remote, local) {
3111
3116
  (local?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
3112
3117
  );
3113
3118
  const download = [];
3114
- for (const [path, hash] of remoteMap) {
3115
- if (localMap.get(path) !== hash) {
3116
- download.push(path);
3119
+ for (const [path2, hash] of remoteMap) {
3120
+ if (localMap.get(path2) !== hash) {
3121
+ download.push(path2);
3117
3122
  }
3118
3123
  }
3119
3124
  const deleteLocal = [];
3120
- for (const path of localMap.keys()) {
3121
- if (!remoteMap.has(path)) {
3122
- deleteLocal.push(path);
3125
+ for (const path2 of localMap.keys()) {
3126
+ if (!remoteMap.has(path2)) {
3127
+ deleteLocal.push(path2);
3123
3128
  }
3124
3129
  }
3125
3130
  return { download, deleteLocal };
@@ -3213,9 +3218,9 @@ ${diagnostic ?? ""}`);
3213
3218
  }
3214
3219
  }
3215
3220
  let deleted = 0;
3216
- for (const path of deleteLocal) {
3221
+ for (const path2 of deleteLocal) {
3217
3222
  const absPath = toFsPath(
3218
- projectDocumentLocalPath(targetApmDir, projectId, path)
3223
+ projectDocumentLocalPath(targetApmDir, projectId, path2)
3219
3224
  );
3220
3225
  if (existsSync7(absPath)) {
3221
3226
  rmSync2(absPath, { force: true });
@@ -3262,23 +3267,23 @@ async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot, options) {
3262
3267
  (remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.description])
3263
3268
  );
3264
3269
  let synced = 0;
3265
- for (const path of localPaths) {
3270
+ for (const path2 of localPaths) {
3266
3271
  const absPath = toFsPath(
3267
- projectDocumentLocalPath(targetApmDir, projectId, path)
3272
+ projectDocumentLocalPath(targetApmDir, projectId, path2)
3268
3273
  );
3269
3274
  const content = readFileSync7(absPath, "utf8");
3270
3275
  const contentHash = hashLocalFileContent(content);
3271
- if (remoteHashByPath.get(path) === contentHash) {
3276
+ if (remoteHashByPath.get(path2) === contentHash) {
3272
3277
  continue;
3273
3278
  }
3274
3279
  await api.cli.upsertProjectDocument({
3275
3280
  projectId,
3276
- path,
3281
+ path: path2,
3277
3282
  content,
3278
- description: remoteDescriptionByPath.get(path) ?? void 0
3283
+ description: remoteDescriptionByPath.get(path2) ?? void 0
3279
3284
  });
3280
3285
  synced += 1;
3281
- console.log(`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: ${path}`);
3286
+ console.log(`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: ${path2}`);
3282
3287
  }
3283
3288
  if (synced === 0) {
3284
3289
  console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u63A8\u9001");
@@ -3294,13 +3299,13 @@ function manifestPath2(apmDir) {
3294
3299
  return join7(apmDir, CLI_VERSION_FILE);
3295
3300
  }
3296
3301
  function loadManifest2(apmDir) {
3297
- const path = toFsPath(manifestPath2(apmDir));
3298
- if (!existsSync8(path)) {
3302
+ const path2 = toFsPath(manifestPath2(apmDir));
3303
+ if (!existsSync8(path2)) {
3299
3304
  return null;
3300
3305
  }
3301
3306
  try {
3302
3307
  const parsed = JSON.parse(
3303
- readFileSync8(path, "utf8")
3308
+ readFileSync8(path2, "utf8")
3304
3309
  );
3305
3310
  if (parsed?.version === 1 && typeof parsed.cliVersion === "string" && parsed.cliVersion.trim()) {
3306
3311
  return parsed;
@@ -3982,6 +3987,53 @@ async function handleStartProject(cfg, msg, signal) {
3982
3987
  }
3983
3988
  }
3984
3989
 
3990
+ // src/commands/connect/upload-package-artifact.ts
3991
+ import fs from "node:fs";
3992
+ import path from "node:path";
3993
+ function packageTmpDir(packageId) {
3994
+ return `/data/package-tmp/${packageId}`;
3995
+ }
3996
+ function expectedLocalArtifactPath(target, packageId) {
3997
+ const fileName = target === "frontend" ? "dist.zip" : "jars.zip";
3998
+ return path.join(packageTmpDir(packageId), fileName);
3999
+ }
4000
+ function packageArtifactObjectKey(target, projectId, timestampMs = Date.now()) {
4001
+ const folder = target === "frontend" ? "vue" : "java";
4002
+ const suffix = target === "frontend" ? "dist.zip" : "jar.zip";
4003
+ return `packages/${projectId}/${folder}/${timestampMs}.${suffix}`;
4004
+ }
4005
+ function removePackageTmpDir(packageId) {
4006
+ const dir = packageTmpDir(packageId);
4007
+ try {
4008
+ fs.rmSync(dir, { recursive: true, force: true });
4009
+ } catch (err) {
4010
+ console.warn(
4011
+ `[apm] \u6E05\u7406\u6253\u5305\u4E34\u65F6\u76EE\u5F55\u5931\u8D25 ${dir}:`,
4012
+ err instanceof Error ? err.message : err
4013
+ );
4014
+ }
4015
+ }
4016
+ async function uploadPackageArtifact(cfg, target, packageId, projectId) {
4017
+ const localPath = expectedLocalArtifactPath(target, packageId);
4018
+ if (!fs.existsSync(localPath)) {
4019
+ throw new Error(`\u6253\u5305\u4EA7\u7269\u4E0D\u5B58\u5728: ${localPath}`);
4020
+ }
4021
+ const stat = fs.statSync(localPath);
4022
+ if (!stat.isFile() || stat.size <= 0) {
4023
+ throw new Error(`\u6253\u5305\u4EA7\u7269\u65E0\u6548\u6216\u4E3A\u7A7A: ${localPath}`);
4024
+ }
4025
+ const objectKey = packageArtifactObjectKey(target, projectId);
4026
+ const { client, bucket, publicEndpoint } = await createApmLogMinioClient(cfg);
4027
+ await client.ensureBucket(bucket);
4028
+ await client.fPutObject(bucket, objectKey, localPath, {
4029
+ "Content-Type": "application/zip"
4030
+ });
4031
+ removePackageTmpDir(packageId);
4032
+ const base = publicEndpoint?.replace(/\/$/, "") ?? "";
4033
+ const artifactUrl = base ? `${base}/${bucket}/${objectKey}` : null;
4034
+ return { artifactPath: objectKey, artifactUrl };
4035
+ }
4036
+
3985
4037
  // src/commands/connect/handle-package.ts
3986
4038
  async function updatePackageStatus(cfg, packageId, status, extra) {
3987
4039
  const api = createApmApiClient(cfg);
@@ -3990,15 +4042,10 @@ async function updatePackageStatus(cfg, packageId, status, extra) {
3990
4042
  status,
3991
4043
  ...extra?.error != null ? { error: extra.error } : {},
3992
4044
  ...extra?.content != null ? { content: extra.content } : {},
3993
- ...extra?.artifactPath != null ? { artifactPath: extra.artifactPath } : {}
4045
+ ...extra?.artifactPath != null ? { artifactPath: extra.artifactPath } : {},
4046
+ ...extra?.artifactUrl != null ? { artifactUrl: extra.artifactUrl } : {}
3994
4047
  });
3995
4048
  }
3996
- function expectedArtifactPath(target, packageId) {
3997
- if (target === "frontend") {
3998
- return `/data/artifacts/vue/${packageId}/dist.zip`;
3999
- }
4000
- return `/data/artifacts/springboot/${packageId}/jars.zip`;
4001
- }
4002
4049
  async function handleWebIdePackage(cfg, msg, signal) {
4003
4050
  const workdir = requireRemoteWorkdir(msg.workdir);
4004
4051
  const messageId = msg.messageId;
@@ -4119,7 +4166,6 @@ async function handleWebIdePackage(cfg, msg, signal) {
4119
4166
  );
4120
4167
  eventSession.addCliStep("\u542F\u52A8\u6253\u5305 Agent", "\u51C6\u5907 create\u2026", "running");
4121
4168
  await syncPrepLog();
4122
- const artifactHint = expectedArtifactPath(msg.target, packageId);
4123
4169
  let failedByTool = false;
4124
4170
  const outcome = await runCursorAgent(
4125
4171
  cfg,
@@ -4226,11 +4272,17 @@ async function handleWebIdePackage(cfg, msg, signal) {
4226
4272
  );
4227
4273
  return;
4228
4274
  }
4275
+ const uploaded = await runPrepStep(
4276
+ "\u4E0A\u4F20\u6253\u5305\u4EA7\u7269\u5230 MinIO",
4277
+ () => uploadPackageArtifact(cfg, msg.target, packageId, projectId),
4278
+ (result) => `objectKey=${result.artifactPath}${result.artifactUrl ? ` url=${result.artifactUrl}` : ""}`
4279
+ );
4229
4280
  await updatePackageStatus(cfg, packageId, "SUCCESS", {
4230
- artifactPath: artifactHint
4281
+ artifactPath: uploaded.artifactPath,
4282
+ ...uploaded.artifactUrl ? { artifactUrl: uploaded.artifactUrl } : {}
4231
4283
  });
4232
4284
  console.log(
4233
- `[apm] webide-package \u5B8C\u6210 target=${msg.target} packageId=${packageId} agentId=${outcome.agentId}`
4285
+ `[apm] webide-package \u5B8C\u6210 target=${msg.target} packageId=${packageId} agentId=${outcome.agentId} artifact=${uploaded.artifactPath}`
4234
4286
  );
4235
4287
  } catch (err) {
4236
4288
  const detail = err instanceof Error ? err.message : String(err);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-project-manage-cli",
3
- "version": "8.0.12",
3
+ "version": "8.0.14",
4
4
  "description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
5
5
  "type": "module",
6
6
  "private": false,
@@ -2,6 +2,8 @@
2
2
 
3
3
  适用:独立打包 Agent(不 resume 任务对话)。只打 payload 指定的 **一个** target(`frontend` / `backend`)。
4
4
 
5
+ 产物最终由 CLI 上传 MinIO;本地**只**使用临时目录,不要写 `/data/artifacts/` 持久路径。
6
+
5
7
  ### 开始前
6
8
 
7
9
  1. Read 本规则全文。
@@ -12,8 +14,8 @@
12
14
 
13
15
  - **禁止**修改业务代码、提交 Git、创建 PR。
14
16
  - **禁止**为修依赖或修编译错误而改代码 / 装依赖 / 改 lockfile;构建失败 → 以错误结束。
15
- - **禁止**在仓库工作区内留下交付用 zip;临时文件只用 `/data/` 下目录(如 `/data/artifacts/.tmp/<packageId>/`),成功后可删临时目录。
16
- - 临时目录与最终产物均在 `/data` 下,不要用 `/tmp`。
17
+ - **禁止**在仓库工作区内留下交付用 zip
18
+ - 临时目录统一用:`/data/package-tmp/<packageId>/`(不要用 `/tmp`,不要用 `/data/artifacts/`)。
17
19
  - 后端交付 jar 的路径与文件名**只**来自 deploy 文档;禁止在本规则中写死具体项目的 jar 清单。
18
20
 
19
21
  ### target=frontend(Vue)
@@ -22,14 +24,14 @@
22
24
  2. **不要**执行 `npm i` / `pnpm i` 等;分支切换后依赖已就绪。
23
25
  3. 执行文档中的打包命令(通常 `npm run build`)。
24
26
  4. 确认产出 `dist/`(或文档写明的目录)。
25
- 5. `/data` 下压缩(勿在工作区生成 `dist.zip`),落到:
27
+ 5. 在临时目录压缩(勿在工作区生成 `dist.zip`),落到:
26
28
 
27
- `/data/artifacts/vue/<packageId>/dist.zip`
29
+ `/data/package-tmp/<packageId>/dist.zip`
28
30
 
29
31
  **压缩结构(必须)**:zip 内顶层就是名为 `dist` 的目录,解压后得到 `dist/…`,**禁止**出现 `dist/dist/…`。
30
32
 
31
33
  - 正确:在**含有 `dist` 的父目录**执行,例如
32
- `zip -r /data/artifacts/vue/<packageId>/dist.zip dist`
34
+ `mkdir -p /data/package-tmp/<packageId> && zip -r /data/package-tmp/<packageId>/dist.zip dist`
33
35
  (把目录名 `dist` 打进包,不要先 `cd dist` 再压当前目录)。
34
36
  - 错误:`cd dist && zip -r … .`(解压后没有顶层 `dist`);或把整个前端仓/多套一层 `dist` 再压(解压成 `dist/dist`)。
35
37
 
@@ -40,25 +42,25 @@
40
42
  1. 按 deploy 文档进入后端目录,执行文档写明的构建命令(通常 `mvn clean package`)。
41
43
  2. **只**收集 deploy 文档标明的交付 jar(路径/文件名/glob 以文档为准)。**禁止**全量收集 `target/lib`、**禁止**打包 SQL、**禁止**在规则或命令里写死具体 jar 名。
42
44
  3. 确认文档列出的每个 jar 均已生成;缺任一文件 → SetPackageFailed。
43
- 4. `/data` 下将上述 jar **打成一个 zip**(勿在工作区生成交付 zip),落到:
45
+ 4. 在临时目录将上述 jar **打成一个 zip**(勿在工作区生成交付 zip),落到:
44
46
 
45
- `/data/artifacts/springboot/<packageId>/jars.zip`
47
+ `/data/package-tmp/<packageId>/jars.zip`
46
48
 
47
49
  **压缩结构(必须)**:zip 内顶层直接是各个 `.jar` 文件(解压后同级列出 jar),**禁止**多套一层无意义目录,也禁止把整个 `lib` 目录原样打进包。
48
50
 
49
51
  示例(路径与文件列表须来自 deploy,勿照抄本例文件名):
50
52
 
51
53
  ```bash
52
- mkdir -p /data/artifacts/springboot/<packageId> /data/artifacts/.tmp/<packageId>
54
+ mkdir -p /data/package-tmp/<packageId>/jars
53
55
  # 按 deploy 文档把待交付 jar 拷到临时目录后:
54
- cd /data/artifacts/.tmp/<packageId>
55
- zip /data/artifacts/springboot/<packageId>/jars.zip ./*.jar
56
+ cd /data/package-tmp/<packageId>/jars
57
+ zip /data/package-tmp/<packageId>/jars.zip ./*.jar
56
58
  ```
57
59
 
58
- 5. 可选:同目录写 `commitId.txt`(后端仓 `git rev-parse HEAD` 一行),非必须。
60
+ 5. 可选:同临时目录写 `commitId.txt`(后端仓 `git rev-parse HEAD` 一行),非必须。
59
61
  6. 构建或落盘失败 → 调用 **SetPackageFailed**(传入失败原因)后结束。
60
62
 
61
63
  ### 结束
62
64
 
63
- - 成功:确认产物已落到约定路径后正常结束(不要调用 SetPackageFailed)。
65
+ - 成功:确认 zip 已落到 `/data/package-tmp/<packageId>/` 约定文件名后正常结束(不要调用 SetPackageFailed)。CLI 会上传 MinIO 并清理该临时目录。
64
66
  - 失败:先调用 SetPackageFailed 写入原因,再结束。