ai-project-manage-cli 8.0.13 → 8.0.15

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
@@ -196,10 +196,6 @@ var init_request_config = __esm({
196
196
  method: "GET",
197
197
  path: "/cli/apm-log-storage"
198
198
  }),
199
- getDeployArtifactStorage: defineEndpoint({
200
- method: "GET",
201
- path: "/cli/deploy-artifact-storage"
202
- }),
203
199
  projectPackageUpdateStatus: defineEndpoint({
204
200
  method: "PUT",
205
201
  path: "/cli/project-packages/status"
@@ -100,10 +100,6 @@ var requestConfig = {
100
100
  method: "GET",
101
101
  path: "/cli/apm-log-storage"
102
102
  }),
103
- getDeployArtifactStorage: defineEndpoint({
104
- method: "GET",
105
- path: "/cli/deploy-artifact-storage"
106
- }),
107
103
  projectPackageUpdateStatus: defineEndpoint({
108
104
  method: "PUT",
109
105
  path: "/cli/project-packages/status"
@@ -209,8 +205,8 @@ function toFsPath(inputPath) {
209
205
  }
210
206
  return `\\\\?\\${normalized}`;
211
207
  }
212
- function normalizeWorkdirPath(path2) {
213
- let normalized = path2.trim().replace(/\\/g, "/").normalize("NFC");
208
+ function normalizeWorkdirPath(path3) {
209
+ let normalized = path3.trim().replace(/\\/g, "/").normalize("NFC");
214
210
  if (normalized.startsWith("//?/")) {
215
211
  normalized = normalized.slice(4);
216
212
  }
@@ -332,9 +328,9 @@ function listWebIdeRuleFileNames(rulesDir) {
332
328
  function assertWebIdeTemplateCopiedToApm(apmDir, workdir) {
333
329
  const required = ["AGENTS.md", "rules"];
334
330
  for (const item of required) {
335
- const path2 = join2(apmDir, item);
336
- if (!existsSync(toFsPath(path2))) {
337
- throw new Error(`[apm] WebIDE \u521D\u59CB\u5316\u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5C11: ${path2}`);
331
+ const path3 = join2(apmDir, item);
332
+ if (!existsSync(toFsPath(path3))) {
333
+ throw new Error(`[apm] WebIDE \u521D\u59CB\u5316\u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5C11: ${path3}`);
338
334
  }
339
335
  }
340
336
  const rulesDir = join2(apmDir, "rules");
@@ -618,13 +614,13 @@ function normalizeRepoEntry(raw) {
618
614
  return entry;
619
615
  }
620
616
  function readManifest(workdir) {
621
- const path2 = toFsPath(manifestPath(workdir));
622
- if (!existsSync2(path2)) {
617
+ const path3 = toFsPath(manifestPath(workdir));
618
+ if (!existsSync2(path3)) {
623
619
  return null;
624
620
  }
625
621
  try {
626
622
  const raw = JSON.parse(
627
- readFileSync2(path2, "utf8")
623
+ readFileSync2(path3, "utf8")
628
624
  );
629
625
  if (raw?.version !== WORKSPACE_REPOS_VERSION) {
630
626
  return null;
@@ -656,8 +652,8 @@ function readManifest(workdir) {
656
652
  function writeManifest(workdir, manifest) {
657
653
  const apmDir = toFsPath(workspaceApmDir(workdir));
658
654
  mkdirSync3(apmDir, { recursive: true });
659
- const path2 = toFsPath(manifestPath(workdir));
660
- writeFileSync2(path2, `${JSON.stringify(manifest, null, 2)}
655
+ const path3 = toFsPath(manifestPath(workdir));
656
+ writeFileSync2(path3, `${JSON.stringify(manifest, null, 2)}
661
657
  `, "utf8");
662
658
  }
663
659
  function manifestStillValid(workdir, manifest) {
@@ -2230,12 +2226,12 @@ import { dirname as dirname3, resolve as resolve5 } from "node:path";
2230
2226
  function registryPath(workdir, taskId) {
2231
2227
  return resolve5(workdir, ".apm", "webide", taskId, "cursor-agent.json");
2232
2228
  }
2233
- function readRegistry(path2) {
2234
- if (!existsSync4(path2)) {
2229
+ function readRegistry(path3) {
2230
+ if (!existsSync4(path3)) {
2235
2231
  return {};
2236
2232
  }
2237
2233
  try {
2238
- const parsed = JSON.parse(readFileSync4(path2, "utf8"));
2234
+ const parsed = JSON.parse(readFileSync4(path3, "utf8"));
2239
2235
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
2240
2236
  const raw = parsed;
2241
2237
  const state = {};
@@ -2263,16 +2259,16 @@ function readRegistry(path2) {
2263
2259
  }
2264
2260
  return {};
2265
2261
  }
2266
- function writeRegistry(path2, registry) {
2267
- mkdirSync4(dirname3(path2), { recursive: true });
2268
- writeFileSync3(path2, `${JSON.stringify(registry, null, 2)}
2262
+ function writeRegistry(path3, registry) {
2263
+ mkdirSync4(dirname3(path3), { recursive: true });
2264
+ writeFileSync3(path3, `${JSON.stringify(registry, null, 2)}
2269
2265
  `, "utf8");
2270
2266
  }
2271
2267
  function syncWebIdeTaskState(workdir, taskId, patch) {
2272
2268
  const trimmedTaskId = taskId.trim();
2273
2269
  if (!trimmedTaskId) return;
2274
- const path2 = registryPath(workdir, trimmedTaskId);
2275
- const current = readRegistry(path2);
2270
+ const path3 = registryPath(workdir, trimmedTaskId);
2271
+ const current = readRegistry(path3);
2276
2272
  const next = {
2277
2273
  ...current,
2278
2274
  taskId: trimmedTaskId,
@@ -2296,7 +2292,7 @@ function syncWebIdeTaskState(workdir, taskId, patch) {
2296
2292
  if (patch.status !== void 0) {
2297
2293
  next.status = patch.status;
2298
2294
  }
2299
- writeRegistry(path2, next);
2295
+ writeRegistry(path3, next);
2300
2296
  }
2301
2297
  function loadWebIdeAgentId(workdir, taskId) {
2302
2298
  return readRegistry(registryPath(workdir, taskId)).agentId;
@@ -2305,8 +2301,8 @@ function saveWebIdeAgentId(workdir, taskId, agentId) {
2305
2301
  syncWebIdeTaskState(workdir, taskId, { agentId });
2306
2302
  }
2307
2303
  function clearWebIdeAgentId(workdir, taskId) {
2308
- const path2 = registryPath(workdir, taskId);
2309
- if (!existsSync4(path2)) return;
2304
+ const path3 = registryPath(workdir, taskId);
2305
+ if (!existsSync4(path3)) return;
2310
2306
  syncWebIdeTaskState(workdir, taskId, { agentId: "" });
2311
2307
  }
2312
2308
 
@@ -2510,6 +2506,34 @@ var MinioClient = class {
2510
2506
  async fPutObject(bucket, objectKey, filePath, meta) {
2511
2507
  await this.inner.fPutObject(bucket, objectKey, filePath, meta);
2512
2508
  }
2509
+ async listObjects(bucket, prefix) {
2510
+ const objects = [];
2511
+ const stream = this.inner.listObjectsV2(bucket, prefix, true);
2512
+ await new Promise((resolve7, reject) => {
2513
+ stream.on("data", (obj) => {
2514
+ if (!obj.name) return;
2515
+ objects.push({
2516
+ name: obj.name,
2517
+ size: typeof obj.size === "number" ? obj.size : 0,
2518
+ lastModified: obj.lastModified ?? null,
2519
+ ...typeof obj.etag === "string" ? { etag: obj.etag } : {}
2520
+ });
2521
+ });
2522
+ stream.on("error", reject);
2523
+ stream.on("end", () => resolve7());
2524
+ });
2525
+ return objects;
2526
+ }
2527
+ async statObject(bucket, objectKey) {
2528
+ const stat = await this.inner.statObject(bucket, objectKey);
2529
+ return {
2530
+ size: typeof stat.size === "number" ? stat.size : 0,
2531
+ metaData: stat.metaData
2532
+ };
2533
+ }
2534
+ async removeObject(bucket, objectKey) {
2535
+ await this.inner.removeObject(bucket, objectKey);
2536
+ }
2513
2537
  };
2514
2538
 
2515
2539
  // src/commands/connect/apm-log-minio.ts
@@ -2527,7 +2551,8 @@ async function createApmLogMinioClient(cfg) {
2527
2551
  accessKey: storage.accessKey,
2528
2552
  secretKey: storage.secretKey
2529
2553
  }),
2530
- bucket: storage.bucket
2554
+ bucket: storage.bucket,
2555
+ ...storage.publicEndpoint ? { publicEndpoint: storage.publicEndpoint } : {}
2531
2556
  };
2532
2557
  }
2533
2558
 
@@ -2957,13 +2982,13 @@ async function downloadAttachment(cfg, attachmentId) {
2957
2982
  return Buffer.from(await res.arrayBuffer());
2958
2983
  }
2959
2984
  function loadManifest(dir) {
2960
- const path2 = join5(dir, MANIFEST_FILE);
2961
- if (!existsSync6(path2)) {
2985
+ const path3 = join5(dir, MANIFEST_FILE);
2986
+ if (!existsSync6(path3)) {
2962
2987
  return { version: 1, attachments: {} };
2963
2988
  }
2964
2989
  try {
2965
2990
  const parsed = JSON.parse(
2966
- readFileSync6(path2, "utf8")
2991
+ readFileSync6(path3, "utf8")
2967
2992
  );
2968
2993
  if (parsed?.version === 1 && parsed.attachments && typeof parsed.attachments === "object") {
2969
2994
  return parsed;
@@ -3058,14 +3083,14 @@ function projectDocumentLocalPath(apmRoot, projectId, documentPath) {
3058
3083
  ...normalized.split("/")
3059
3084
  );
3060
3085
  }
3061
- function normalizeLocalDocumentPath(path2) {
3062
- const trimmed = path2.trim().replace(/\\/g, "/");
3086
+ function normalizeLocalDocumentPath(path3) {
3087
+ const trimmed = path3.trim().replace(/\\/g, "/");
3063
3088
  if (!trimmed || trimmed.startsWith("/") || /^[a-zA-Z]:/.test(trimmed)) {
3064
- throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path2}`);
3089
+ throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path3}`);
3065
3090
  }
3066
3091
  const segments = trimmed.split("/").filter(Boolean);
3067
3092
  if (segments.some((segment) => segment === ".." || segment === ".")) {
3068
- throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path2}`);
3093
+ throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path3}`);
3069
3094
  }
3070
3095
  return segments.join("/");
3071
3096
  }
@@ -3119,15 +3144,15 @@ function diffManifestPaths(remote, local) {
3119
3144
  (local?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
3120
3145
  );
3121
3146
  const download = [];
3122
- for (const [path2, hash] of remoteMap) {
3123
- if (localMap.get(path2) !== hash) {
3124
- download.push(path2);
3147
+ for (const [path3, hash] of remoteMap) {
3148
+ if (localMap.get(path3) !== hash) {
3149
+ download.push(path3);
3125
3150
  }
3126
3151
  }
3127
3152
  const deleteLocal = [];
3128
- for (const path2 of localMap.keys()) {
3129
- if (!remoteMap.has(path2)) {
3130
- deleteLocal.push(path2);
3153
+ for (const path3 of localMap.keys()) {
3154
+ if (!remoteMap.has(path3)) {
3155
+ deleteLocal.push(path3);
3131
3156
  }
3132
3157
  }
3133
3158
  return { download, deleteLocal };
@@ -3221,9 +3246,9 @@ ${diagnostic ?? ""}`);
3221
3246
  }
3222
3247
  }
3223
3248
  let deleted = 0;
3224
- for (const path2 of deleteLocal) {
3249
+ for (const path3 of deleteLocal) {
3225
3250
  const absPath = toFsPath(
3226
- projectDocumentLocalPath(targetApmDir, projectId, path2)
3251
+ projectDocumentLocalPath(targetApmDir, projectId, path3)
3227
3252
  );
3228
3253
  if (existsSync7(absPath)) {
3229
3254
  rmSync2(absPath, { force: true });
@@ -3270,23 +3295,23 @@ async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot, options) {
3270
3295
  (remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.description])
3271
3296
  );
3272
3297
  let synced = 0;
3273
- for (const path2 of localPaths) {
3298
+ for (const path3 of localPaths) {
3274
3299
  const absPath = toFsPath(
3275
- projectDocumentLocalPath(targetApmDir, projectId, path2)
3300
+ projectDocumentLocalPath(targetApmDir, projectId, path3)
3276
3301
  );
3277
3302
  const content = readFileSync7(absPath, "utf8");
3278
3303
  const contentHash = hashLocalFileContent(content);
3279
- if (remoteHashByPath.get(path2) === contentHash) {
3304
+ if (remoteHashByPath.get(path3) === contentHash) {
3280
3305
  continue;
3281
3306
  }
3282
3307
  await api.cli.upsertProjectDocument({
3283
3308
  projectId,
3284
- path: path2,
3309
+ path: path3,
3285
3310
  content,
3286
- description: remoteDescriptionByPath.get(path2) ?? void 0
3311
+ description: remoteDescriptionByPath.get(path3) ?? void 0
3287
3312
  });
3288
3313
  synced += 1;
3289
- console.log(`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: ${path2}`);
3314
+ console.log(`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: ${path3}`);
3290
3315
  }
3291
3316
  if (synced === 0) {
3292
3317
  console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u63A8\u9001");
@@ -3302,13 +3327,13 @@ function manifestPath2(apmDir) {
3302
3327
  return join7(apmDir, CLI_VERSION_FILE);
3303
3328
  }
3304
3329
  function loadManifest2(apmDir) {
3305
- const path2 = toFsPath(manifestPath2(apmDir));
3306
- if (!existsSync8(path2)) {
3330
+ const path3 = toFsPath(manifestPath2(apmDir));
3331
+ if (!existsSync8(path3)) {
3307
3332
  return null;
3308
3333
  }
3309
3334
  try {
3310
3335
  const parsed = JSON.parse(
3311
- readFileSync8(path2, "utf8")
3336
+ readFileSync8(path3, "utf8")
3312
3337
  );
3313
3338
  if (parsed?.version === 1 && typeof parsed.cliVersion === "string" && parsed.cliVersion.trim()) {
3314
3339
  return parsed;
@@ -3993,28 +4018,6 @@ async function handleStartProject(cfg, msg, signal) {
3993
4018
  // src/commands/connect/upload-package-artifact.ts
3994
4019
  import fs from "node:fs";
3995
4020
  import path from "node:path";
3996
-
3997
- // src/commands/connect/deploy-artifact-minio.ts
3998
- async function fetchDeployArtifactStorage(cfg) {
3999
- const api = createApmApiClient(cfg);
4000
- return api.cli.getDeployArtifactStorage(void 0);
4001
- }
4002
- async function createDeployArtifactMinioClient(cfg) {
4003
- const storage = await fetchDeployArtifactStorage(cfg);
4004
- return {
4005
- client: new MinioClient({
4006
- endPoint: storage.endpoint,
4007
- port: storage.port,
4008
- useSSL: storage.useSsl,
4009
- accessKey: storage.accessKey,
4010
- secretKey: storage.secretKey
4011
- }),
4012
- bucket: storage.bucket,
4013
- ...storage.publicEndpoint ? { publicEndpoint: storage.publicEndpoint } : {}
4014
- };
4015
- }
4016
-
4017
- // src/commands/connect/upload-package-artifact.ts
4018
4021
  function packageTmpDir(packageId) {
4019
4022
  return `/data/package-tmp/${packageId}`;
4020
4023
  }
@@ -4022,9 +4025,10 @@ function expectedLocalArtifactPath(target, packageId) {
4022
4025
  const fileName = target === "frontend" ? "dist.zip" : "jars.zip";
4023
4026
  return path.join(packageTmpDir(packageId), fileName);
4024
4027
  }
4025
- function packageArtifactObjectKey(target, packageId) {
4026
- const fileName = target === "frontend" ? "dist.zip" : "jars.zip";
4027
- return `packages/${target}/${packageId}/${fileName}`;
4028
+ function packageArtifactObjectKey(target, projectId, timestampMs = Date.now()) {
4029
+ const folder = target === "frontend" ? "vue" : "java";
4030
+ const suffix = target === "frontend" ? "dist.zip" : "jar.zip";
4031
+ return `packages/${projectId}/${folder}/${timestampMs}.${suffix}`;
4028
4032
  }
4029
4033
  function removePackageTmpDir(packageId) {
4030
4034
  const dir = packageTmpDir(packageId);
@@ -4037,7 +4041,7 @@ function removePackageTmpDir(packageId) {
4037
4041
  );
4038
4042
  }
4039
4043
  }
4040
- async function uploadPackageArtifact(cfg, target, packageId) {
4044
+ async function uploadPackageArtifact(cfg, target, packageId, projectId) {
4041
4045
  const localPath = expectedLocalArtifactPath(target, packageId);
4042
4046
  if (!fs.existsSync(localPath)) {
4043
4047
  throw new Error(`\u6253\u5305\u4EA7\u7269\u4E0D\u5B58\u5728: ${localPath}`);
@@ -4046,8 +4050,8 @@ async function uploadPackageArtifact(cfg, target, packageId) {
4046
4050
  if (!stat.isFile() || stat.size <= 0) {
4047
4051
  throw new Error(`\u6253\u5305\u4EA7\u7269\u65E0\u6548\u6216\u4E3A\u7A7A: ${localPath}`);
4048
4052
  }
4049
- const objectKey = packageArtifactObjectKey(target, packageId);
4050
- const { client, bucket, publicEndpoint } = await createDeployArtifactMinioClient(cfg);
4053
+ const objectKey = packageArtifactObjectKey(target, projectId);
4054
+ const { client, bucket, publicEndpoint } = await createApmLogMinioClient(cfg);
4051
4055
  await client.ensureBucket(bucket);
4052
4056
  await client.fPutObject(bucket, objectKey, localPath, {
4053
4057
  "Content-Type": "application/zip"
@@ -4058,6 +4062,151 @@ async function uploadPackageArtifact(cfg, target, packageId) {
4058
4062
  return { artifactPath: objectKey, artifactUrl };
4059
4063
  }
4060
4064
 
4065
+ // src/commands/connect/sync-package-sql.ts
4066
+ import { createHash as createHash2 } from "node:crypto";
4067
+ import fs2 from "node:fs";
4068
+ import path2 from "node:path";
4069
+ var SKIP_DIR_NAMES2 = /* @__PURE__ */ new Set([
4070
+ ".apm",
4071
+ ".git",
4072
+ ".idea",
4073
+ ".vscode",
4074
+ "node_modules",
4075
+ "target",
4076
+ "dist",
4077
+ "build",
4078
+ "out",
4079
+ "coverage",
4080
+ "__pycache__"
4081
+ ]);
4082
+ var MIGRATION_MARKER = "/db/migration/";
4083
+ function isSqlFileName(name) {
4084
+ return name.toLowerCase().endsWith(".sql");
4085
+ }
4086
+ function sha256File(absPath) {
4087
+ const hash = createHash2("sha256");
4088
+ hash.update(fs2.readFileSync(absPath));
4089
+ return hash.digest("hex");
4090
+ }
4091
+ function scanLocalMigrationSqlFiles(workdir) {
4092
+ const root = path2.resolve(workdir);
4093
+ const byRelative = /* @__PURE__ */ new Map();
4094
+ const walk = (dir) => {
4095
+ let entries;
4096
+ try {
4097
+ entries = fs2.readdirSync(dir, { withFileTypes: true });
4098
+ } catch {
4099
+ return;
4100
+ }
4101
+ for (const entry of entries) {
4102
+ const name = entry.name;
4103
+ if (entry.isDirectory()) {
4104
+ if (SKIP_DIR_NAMES2.has(name) || name.startsWith(".")) continue;
4105
+ walk(path2.join(dir, name));
4106
+ continue;
4107
+ }
4108
+ if (!entry.isFile() || !isSqlFileName(name)) continue;
4109
+ const absPath = path2.join(dir, name);
4110
+ const normalized = absPath.replace(/\\/g, "/");
4111
+ const idx = normalized.toLowerCase().lastIndexOf(MIGRATION_MARKER);
4112
+ if (idx < 0) continue;
4113
+ const relativePath = normalized.slice(idx + MIGRATION_MARKER.length);
4114
+ if (!relativePath || relativePath.includes("..")) continue;
4115
+ const stat = fs2.statSync(absPath);
4116
+ if (!stat.isFile() || stat.size <= 0) continue;
4117
+ const item = {
4118
+ absPath,
4119
+ relativePath,
4120
+ size: stat.size,
4121
+ sha256: sha256File(absPath)
4122
+ };
4123
+ const prev = byRelative.get(relativePath);
4124
+ if (prev) {
4125
+ console.warn(
4126
+ `[apm] SQL relative path conflict, overwrite: ${relativePath}
4127
+ old: ${prev.absPath}
4128
+ new: ${absPath}`
4129
+ );
4130
+ }
4131
+ byRelative.set(relativePath, item);
4132
+ }
4133
+ };
4134
+ walk(root);
4135
+ return [...byRelative.values()].sort(
4136
+ (a, b) => a.relativePath.localeCompare(b.relativePath)
4137
+ );
4138
+ }
4139
+ function packageSqlObjectKey(projectId, relativePath) {
4140
+ return `packages/${projectId}/sql/${relativePath}`;
4141
+ }
4142
+ function packageSqlPrefix(projectId) {
4143
+ return `packages/${projectId}/sql/`;
4144
+ }
4145
+ async function syncPackageSqlToMinio(cfg, workdir, projectId) {
4146
+ const id = projectId.trim();
4147
+ if (!id) {
4148
+ throw new Error("projectId \u4E0D\u80FD\u4E3A\u7A7A");
4149
+ }
4150
+ const localFiles = scanLocalMigrationSqlFiles(workdir);
4151
+ const { client, bucket } = await createApmLogMinioClient(cfg);
4152
+ await client.ensureBucket(bucket);
4153
+ const prefix = packageSqlPrefix(id);
4154
+ const remoteObjects = await client.listObjects(bucket, prefix);
4155
+ const remoteByRelative = /* @__PURE__ */ new Map();
4156
+ for (const obj of remoteObjects) {
4157
+ if (!obj.name.startsWith(prefix) || obj.name.endsWith("/")) continue;
4158
+ if (!obj.name.toLowerCase().endsWith(".sql")) continue;
4159
+ const relativePath = obj.name.slice(prefix.length);
4160
+ if (!relativePath) continue;
4161
+ remoteByRelative.set(relativePath, {
4162
+ objectKey: obj.name,
4163
+ size: obj.size
4164
+ });
4165
+ }
4166
+ for (const [relativePath, remote] of remoteByRelative) {
4167
+ const local = localFiles.find((f) => f.relativePath === relativePath);
4168
+ if (!local) continue;
4169
+ if (local.size !== remote.size) continue;
4170
+ try {
4171
+ const stat = await client.statObject(bucket, remote.objectKey);
4172
+ const meta = stat.metaData ?? {};
4173
+ const sha = meta["sha256"] || meta["x-amz-meta-sha256"] || meta["Sha256"] || void 0;
4174
+ if (typeof sha === "string" && sha.trim()) {
4175
+ remote.sha256 = sha.trim().toLowerCase();
4176
+ }
4177
+ } catch {
4178
+ }
4179
+ }
4180
+ let uploaded = 0;
4181
+ let skipped = 0;
4182
+ const localRelativeSet = new Set(localFiles.map((f) => f.relativePath));
4183
+ for (const file of localFiles) {
4184
+ const objectKey = packageSqlObjectKey(id, file.relativePath);
4185
+ const remote = remoteByRelative.get(file.relativePath);
4186
+ if (remote?.sha256 && remote.sha256 === file.sha256) {
4187
+ skipped += 1;
4188
+ continue;
4189
+ }
4190
+ await client.fPutObject(bucket, objectKey, file.absPath, {
4191
+ "Content-Type": "application/sql",
4192
+ sha256: file.sha256
4193
+ });
4194
+ uploaded += 1;
4195
+ }
4196
+ let deleted = 0;
4197
+ for (const [relativePath, remote] of remoteByRelative) {
4198
+ if (localRelativeSet.has(relativePath)) continue;
4199
+ await client.removeObject(bucket, remote.objectKey);
4200
+ deleted += 1;
4201
+ }
4202
+ return {
4203
+ scanned: localFiles.length,
4204
+ uploaded,
4205
+ skipped,
4206
+ deleted
4207
+ };
4208
+ }
4209
+
4061
4210
  // src/commands/connect/handle-package.ts
4062
4211
  async function updatePackageStatus(cfg, packageId, status, extra) {
4063
4212
  const api = createApmApiClient(cfg);
@@ -4188,6 +4337,21 @@ async function handleWebIdePackage(cfg, msg, signal) {
4188
4337
  return `downloaded=${result.downloaded} deleted=${result.deleted}`;
4189
4338
  }
4190
4339
  );
4340
+ if (msg.target === "backend") {
4341
+ if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
4342
+ try {
4343
+ await runPrepStep(
4344
+ "\u540C\u6B65 SQL \u5230 MinIO",
4345
+ () => syncPackageSqlToMinio(cfg, workdir, projectId),
4346
+ (result) => `scanned=${result.scanned} uploaded=${result.uploaded} skipped=${result.skipped} deleted=${result.deleted}`
4347
+ );
4348
+ } catch (err) {
4349
+ console.warn(
4350
+ `[apm] \u540C\u6B65 SQL \u5230 MinIO \u5931\u8D25\uFF08\u4E0D\u963B\u65AD\u6253\u5305\uFF09:`,
4351
+ err instanceof Error ? err.message : err
4352
+ );
4353
+ }
4354
+ }
4191
4355
  eventSession.addCliStep("\u542F\u52A8\u6253\u5305 Agent", "\u51C6\u5907 create\u2026", "running");
4192
4356
  await syncPrepLog();
4193
4357
  let failedByTool = false;
@@ -4298,7 +4462,7 @@ async function handleWebIdePackage(cfg, msg, signal) {
4298
4462
  }
4299
4463
  const uploaded = await runPrepStep(
4300
4464
  "\u4E0A\u4F20\u6253\u5305\u4EA7\u7269\u5230 MinIO",
4301
- () => uploadPackageArtifact(cfg, msg.target, packageId),
4465
+ () => uploadPackageArtifact(cfg, msg.target, packageId, projectId),
4302
4466
  (result) => `objectKey=${result.artifactPath}${result.artifactUrl ? ` url=${result.artifactUrl}` : ""}`
4303
4467
  );
4304
4468
  await updatePackageStatus(cfg, packageId, "SUCCESS", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-project-manage-cli",
3
- "version": "8.0.13",
3
+ "version": "8.0.15",
4
4
  "description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
5
5
  "type": "module",
6
6
  "private": false,
@@ -40,7 +40,7 @@
40
40
  ### target=backend(SpringBoot)
41
41
 
42
42
  1. 按 deploy 文档进入后端目录,执行文档写明的构建命令(通常 `mvn clean package`)。
43
- 2. **只**收集 deploy 文档标明的交付 jar(路径/文件名/glob 以文档为准)。**禁止**全量收集 `target/lib`、**禁止**打包 SQL、**禁止**在规则或命令里写死具体 jar 名。
43
+ 2. **只**收集 deploy 文档标明的交付 jar(路径/文件名/glob 以文档为准)。**禁止**全量收集 `target/lib`、**禁止**把 SQL 打进 jars.zip、**禁止**在规则或命令里写死具体 jar 名。SQL 镜像由 CLI 自动扫描本地 `db/migration` 下的 `.sql` 同步到 MinIO,Agent **不要**上传 SQL。
44
44
  3. 确认文档列出的每个 jar 均已生成;缺任一文件 → SetPackageFailed。
45
45
  4. 在临时目录将上述 jar **打成一个 zip**(勿在工作区生成交付 zip),落到:
46
46