ai-project-manage-cli 8.0.8 → 8.0.9

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
@@ -195,6 +195,18 @@ var init_request_config = __esm({
195
195
  getApmLogStorage: defineEndpoint({
196
196
  method: "GET",
197
197
  path: "/cli/apm-log-storage"
198
+ }),
199
+ projectPackageUpdateStatus: defineEndpoint({
200
+ method: "PUT",
201
+ path: "/cli/project-packages/status"
202
+ }),
203
+ projectPackageAppendContent: defineEndpoint({
204
+ method: "PUT",
205
+ path: "/cli/project-packages/content"
206
+ }),
207
+ projectPackageEnsureContent: defineEndpoint({
208
+ method: "PUT",
209
+ path: "/cli/project-packages/ensure-content"
198
210
  })
199
211
  }
200
212
  };
@@ -1050,6 +1062,9 @@ init_config();
1050
1062
  function isWebIdeStartProjectTarget(value) {
1051
1063
  return value === "frontend" || value === "backend" || value === "mobile";
1052
1064
  }
1065
+ function isWebIdePackageTarget(value) {
1066
+ return value === "frontend" || value === "backend";
1067
+ }
1053
1068
  function nonEmptyString(v) {
1054
1069
  return typeof v === "string" && v.trim().length > 0;
1055
1070
  }
@@ -1186,13 +1201,64 @@ function validateWebIdeStartProjectPush(o) {
1186
1201
  }
1187
1202
  };
1188
1203
  }
1204
+ function validateWebIdePackagePush(o) {
1205
+ if (o.type !== "webide-package") {
1206
+ return { ok: false, reason: "\u671F\u671B webide-package" };
1207
+ }
1208
+ if (!nonEmptyString(o.packageId)) {
1209
+ return { ok: false, reason: "webide-package \u7F3A\u5C11 packageId" };
1210
+ }
1211
+ if (!nonEmptyString(o.messageId)) {
1212
+ return { ok: false, reason: "webide-package \u7F3A\u5C11 messageId" };
1213
+ }
1214
+ if (!nonEmptyString(o.projectId)) {
1215
+ return { ok: false, reason: "webide-package \u7F3A\u5C11 projectId" };
1216
+ }
1217
+ if (!isWebIdePackageTarget(o.target)) {
1218
+ return { ok: false, reason: "webide-package \u7F3A\u5C11\u6709\u6548 target" };
1219
+ }
1220
+ if (!nonEmptyString(o.baseBranch)) {
1221
+ return { ok: false, reason: "webide-package \u7F3A\u5C11 baseBranch" };
1222
+ }
1223
+ if (!nonEmptyString(o.content)) {
1224
+ return { ok: false, reason: "webide-package \u7F3A\u5C11 content" };
1225
+ }
1226
+ if (!nonEmptyString(o.model)) {
1227
+ return { ok: false, reason: "webide-package \u7F3A\u5C11 model" };
1228
+ }
1229
+ if (!nonEmptyString(o.apiKey)) {
1230
+ return { ok: false, reason: "webide-package \u7F3A\u5C11 apiKey" };
1231
+ }
1232
+ if (!nonEmptyString(o.workdir)) {
1233
+ return { ok: false, reason: "webide-package \u7F3A\u5C11 workdir" };
1234
+ }
1235
+ if (!nonEmptyString(o.user)) {
1236
+ return { ok: false, reason: "webide-package \u7F3A\u5C11 user" };
1237
+ }
1238
+ return {
1239
+ ok: true,
1240
+ data: {
1241
+ type: "webide-package",
1242
+ packageId: o.packageId.trim(),
1243
+ messageId: o.messageId.trim(),
1244
+ projectId: o.projectId.trim(),
1245
+ target: o.target,
1246
+ baseBranch: o.baseBranch.trim().replace(/^origin\//, ""),
1247
+ content: o.content,
1248
+ model: o.model.trim(),
1249
+ apiKey: o.apiKey.trim(),
1250
+ workdir: o.workdir.trim(),
1251
+ user: o.user.trim()
1252
+ }
1253
+ };
1254
+ }
1189
1255
  function validateAgentWsMessage(value, kind) {
1190
1256
  if (typeof value !== "object" || value === null) {
1191
1257
  return { ok: false, reason: "\u6D88\u606F\u4F53\u4E0D\u662F JSON \u5BF9\u8C61" };
1192
1258
  }
1193
1259
  const o = value;
1194
1260
  const type = o.type;
1195
- if (type !== "heartbeat" && type !== "webide-message" && type !== "cancel" && type !== "webide-start-project" && type !== "webide-stop-terminals") {
1261
+ if (type !== "heartbeat" && type !== "webide-message" && type !== "cancel" && type !== "webide-start-project" && type !== "webide-stop-terminals" && type !== "webide-package") {
1196
1262
  return { ok: false, reason: `\u672A\u77E5 type: ${String(type)}` };
1197
1263
  }
1198
1264
  if (kind === "heartbeat" || type === "heartbeat") {
@@ -1207,6 +1273,9 @@ function validateAgentWsMessage(value, kind) {
1207
1273
  if (type === "webide-stop-terminals") {
1208
1274
  return validateWebIdeStopTerminals(o);
1209
1275
  }
1276
+ if (type === "webide-package") {
1277
+ return validateWebIdePackagePush(o);
1278
+ }
1210
1279
  return validateWebIdeMessagePush(o);
1211
1280
  }
1212
1281
 
@@ -1267,6 +1336,14 @@ function spawnWebIdeMessageWorker(cfg, msg) {
1267
1336
  try {
1268
1337
  const { createApmApiClient: createApmApiClient2 } = await Promise.resolve().then(() => (init_client(), client_exports));
1269
1338
  const api = createApmApiClient2(cfg);
1339
+ if (msg.type === "webide-package") {
1340
+ await api.cli.projectPackageUpdateStatus({
1341
+ id: msg.packageId,
1342
+ status: "CANCELLED",
1343
+ error: reason
1344
+ });
1345
+ return;
1346
+ }
1270
1347
  await api.cli.webideSetMessageError({
1271
1348
  id: msg.messageId,
1272
1349
  error: reason,
@@ -1504,6 +1581,48 @@ function handleStopTerminalsAction(payload, ctx) {
1504
1581
  trackActionTask(ctx, task);
1505
1582
  }
1506
1583
 
1584
+ // src/commands/connect/actions/package.ts
1585
+ init_client();
1586
+ function handleWebIdePackageAction(payload, ctx) {
1587
+ const perMessageController = new AbortController();
1588
+ ctx.activeRuns.set(payload.messageId, perMessageController);
1589
+ if (ctx.pendingCancels.has(payload.messageId)) {
1590
+ ctx.activeRuns.delete(payload.messageId);
1591
+ ctx.pendingCancels.delete(payload.messageId);
1592
+ const cancelTask = (async () => {
1593
+ try {
1594
+ const api = createApmApiClient(ctx.cfg);
1595
+ await api.cli.projectPackageUpdateStatus({
1596
+ id: payload.packageId,
1597
+ status: "CANCELLED",
1598
+ error: "\u5DF2\u53D6\u6D88"
1599
+ });
1600
+ } catch (err) {
1601
+ console.error(
1602
+ `[apm] webide-package \u9884\u53D6\u6D88\u5199\u7EC8\u6001\u5931\u8D25 packageId=${payload.packageId}:`,
1603
+ err instanceof Error ? err.message : err
1604
+ );
1605
+ }
1606
+ })();
1607
+ trackActionTask(ctx, cancelTask);
1608
+ return;
1609
+ }
1610
+ const task = (async () => {
1611
+ const handle = spawnWebIdeMessageWorker(ctx.cfg, payload);
1612
+ const onAbort = () => handle.cancel(payload.messageId);
1613
+ perMessageController.signal.addEventListener("abort", onAbort, {
1614
+ once: true
1615
+ });
1616
+ try {
1617
+ await handle.done;
1618
+ } finally {
1619
+ perMessageController.signal.removeEventListener("abort", onAbort);
1620
+ ctx.activeRuns.delete(payload.messageId);
1621
+ }
1622
+ })();
1623
+ trackActionTask(ctx, task);
1624
+ }
1625
+
1507
1626
  // src/commands/connect/actions/index.ts
1508
1627
  function dispatchConnectAction(payload, ctx) {
1509
1628
  switch (payload.type) {
@@ -1521,6 +1640,9 @@ function dispatchConnectAction(payload, ctx) {
1521
1640
  case "webide-stop-terminals":
1522
1641
  handleStopTerminalsAction(payload, ctx);
1523
1642
  return;
1643
+ case "webide-package":
1644
+ handleWebIdePackageAction(payload, ctx);
1645
+ return;
1524
1646
  }
1525
1647
  }
1526
1648
 
@@ -99,6 +99,18 @@ var requestConfig = {
99
99
  getApmLogStorage: defineEndpoint({
100
100
  method: "GET",
101
101
  path: "/cli/apm-log-storage"
102
+ }),
103
+ projectPackageUpdateStatus: defineEndpoint({
104
+ method: "PUT",
105
+ path: "/cli/project-packages/status"
106
+ }),
107
+ projectPackageAppendContent: defineEndpoint({
108
+ method: "PUT",
109
+ path: "/cli/project-packages/content"
110
+ }),
111
+ projectPackageEnsureContent: defineEndpoint({
112
+ method: "PUT",
113
+ path: "/cli/project-packages/ensure-content"
102
114
  })
103
115
  }
104
116
  };
@@ -942,6 +954,55 @@ async function runTaskBranch(taskId, options = {}) {
942
954
  repos: repoRoots
943
955
  };
944
956
  }
957
+ async function checkoutBaselineBranches(baselineBranch, options = {}) {
958
+ const baseline = baselineBranch.trim().replace(/^origin\//, "");
959
+ if (!baseline) {
960
+ throw new Error("[apm] \u57FA\u7EBF\u5206\u652F\u4E0D\u80FD\u4E3A\u7A7A");
961
+ }
962
+ const cwd = options.cwd ?? process.cwd();
963
+ const workdirPath = resolveWorkdirPath(cwd);
964
+ const manifest = loadWorkspaceReposCache(workdirPath);
965
+ const repoRoots = resolveWorkspaceRepoAbsolutePaths(manifest);
966
+ for (const gitRoot of repoRoots) {
967
+ const label = formatRepoLabel(workdirPath, gitRoot);
968
+ console.log(`[apm] \u5207\u6362\u57FA\u7EBF\u5206\u652F ${baseline} @ ${label}`);
969
+ await checkoutBaselineBranch(baseline, { ...options, cwd: gitRoot });
970
+ }
971
+ return {
972
+ baseline,
973
+ kind: manifest.kind,
974
+ repos: repoRoots
975
+ };
976
+ }
977
+ async function checkoutBaselineBranch(baselineBranch, options = {}) {
978
+ const baseline = baselineBranch.trim().replace(/^origin\//, "");
979
+ if (!baseline) {
980
+ throw new Error("[apm] \u57FA\u7EBF\u5206\u652F\u4E0D\u80FD\u4E3A\u7A7A");
981
+ }
982
+ const cwd = options.cwd ?? process.cwd();
983
+ const gitRoot = await resolveGitRepoRoot(cwd);
984
+ await ensureGitRepo(gitRoot);
985
+ if (await isWorkingTreeDirty(gitRoot)) {
986
+ throw new Error(
987
+ `[apm] \u5DE5\u4F5C\u533A\u6709\u672A\u63D0\u4EA4\u53D8\u66F4\uFF0C\u65E0\u6CD5\u5207\u6362\u5230\u57FA\u7EBF\u5206\u652F ${baseline}`
988
+ );
989
+ }
990
+ await ensureRemoteBaselineBranch(gitRoot, baseline);
991
+ const current = await getCurrentBranch(gitRoot);
992
+ if (current === baseline) {
993
+ await execGit(gitRoot, ["reset", "--hard", `origin/${baseline}`]);
994
+ console.log(`[apm] \u5DF2\u5728\u57FA\u7EBF ${baseline}\uFF0C\u5DF2\u5BF9\u9F50 origin/${baseline}`);
995
+ return baseline;
996
+ }
997
+ await execGit(gitRoot, [
998
+ "checkout",
999
+ "-B",
1000
+ baseline,
1001
+ `origin/${baseline}`
1002
+ ]);
1003
+ console.log(`[apm] \u5DF2\u5207\u6362\u5230\u57FA\u7EBF\u5206\u652F ${baseline}`);
1004
+ return baseline;
1005
+ }
945
1006
 
946
1007
  // src/commands/connect/cursor-agent.ts
947
1008
  import {
@@ -1803,6 +1864,45 @@ function createMysqlExecuteTool(options) {
1803
1864
  };
1804
1865
  }
1805
1866
 
1867
+ // src/commands/connect/tools/package-status-tool.ts
1868
+ function createSetPackageFailedTool(options) {
1869
+ const { packageId, setFailed } = options;
1870
+ return {
1871
+ SetPackageFailed: {
1872
+ description: "\u6253\u5305\u5931\u8D25\u65F6\u8C03\u7528\uFF1A\u5C06\u5F53\u524D\u6253\u5305\u8BB0\u5F55\u6807\u4E3A FAILED\uFF0C\u5E76\u5199\u5165\u5931\u8D25\u539F\u56E0\u3002\u6784\u5EFA\u5931\u8D25\u3001\u7F3A\u4EA7\u7269\u3001\u6587\u6863\u7F3A\u5931\u7B49\u5747\u5E94\u8C03\u7528\uFF1B\u6210\u529F\u65F6\u4E0D\u8981\u8C03\u7528\u3002",
1873
+ inputSchema: {
1874
+ type: "object",
1875
+ properties: {
1876
+ error: {
1877
+ type: "string",
1878
+ description: "\u5931\u8D25\u539F\u56E0\u6458\u8981\uFF08\u4F1A\u5199\u5165\u6253\u5305\u8BB0\u5F55\uFF0C\u4F9B\u5217\u8868/\u65E5\u5FD7\u5C55\u793A\uFF09"
1879
+ }
1880
+ },
1881
+ required: ["error"]
1882
+ },
1883
+ execute: async (args) => {
1884
+ const error = typeof args.error === "string" ? args.error.trim() : "";
1885
+ if (!error) {
1886
+ return {
1887
+ content: [{ type: "text", text: "error \u4E0D\u80FD\u4E3A\u7A7A" }],
1888
+ isError: true
1889
+ };
1890
+ }
1891
+ try {
1892
+ await setFailed(error);
1893
+ return `\u5DF2\u5C06\u6253\u5305\u8BB0\u5F55 ${packageId} \u6807\u4E3A FAILED`;
1894
+ } catch (err) {
1895
+ const detail = err instanceof Error ? err.message : String(err);
1896
+ return {
1897
+ content: [{ type: "text", text: `\u5199\u5165\u5931\u8D25\u72B6\u6001\u5931\u8D25: ${detail}` }],
1898
+ isError: true
1899
+ };
1900
+ }
1901
+ }
1902
+ }
1903
+ };
1904
+ }
1905
+
1806
1906
  // src/commands/connect/tools/index.ts
1807
1907
  var PLAN_MODE_ASK_QUESTION_HINT = `[SDK \u73AF\u5883\u8BF4\u660E]
1808
1908
  AskQuestion \u5DF2\u901A\u8FC7 MCP \u670D\u52A1\u5668 custom-user-tools \u6CE8\u518C\uFF0C\u5DE5\u5177\u540D\u4E3A AskQuestion\u3002
@@ -1853,6 +1953,24 @@ function createCursorCustomTools(cfg, options) {
1853
1953
  );
1854
1954
  }
1855
1955
  }
1956
+ if (options.enablePackageStatusTools && options.packageId) {
1957
+ const packageId = options.packageId;
1958
+ const { cli } = createApmApiClient(cfg);
1959
+ Object.assign(
1960
+ tools,
1961
+ createSetPackageFailedTool({
1962
+ packageId,
1963
+ setFailed: async (error) => {
1964
+ await cli.projectPackageUpdateStatus({
1965
+ id: packageId,
1966
+ status: "FAILED",
1967
+ error
1968
+ });
1969
+ await options.onPackageFailed?.(error);
1970
+ }
1971
+ })
1972
+ );
1973
+ }
1856
1974
  return tools;
1857
1975
  }
1858
1976
  function withPlanModeToolHint(prompt, mode) {
@@ -1973,7 +2091,10 @@ async function runCursorAgent(cfg, ctx, options) {
1973
2091
  enableMysqlTools: options.enableMysqlTools,
1974
2092
  sqlExecutionId: options.sqlExecutionId,
1975
2093
  taskId: options.taskId,
1976
- workdir
2094
+ workdir,
2095
+ enablePackageStatusTools: options.enablePackageStatusTools,
2096
+ packageId: options.packageId,
2097
+ onPackageFailed: options.onPackageFailed
1977
2098
  });
1978
2099
  const enableSandbox = Boolean(options.enableSandbox);
1979
2100
  const mcpServers = options.enablePtySessionMcp ? createPtySessionMcpServers(cfg) : void 0;
@@ -2429,12 +2550,12 @@ async function putDirtyEvents(minio, bucket, prefix, events) {
2429
2550
  });
2430
2551
  }
2431
2552
  }
2432
- async function upsertLogHeader(cfg, ctx, patch) {
2553
+ async function upsertLogHeader(cfg, ctx, patch, objectPrefix) {
2433
2554
  const api = createApmApiClient(cfg);
2434
2555
  await api.cli.webideUpsertMessageLog({
2435
2556
  messageId: ctx.messageId,
2436
2557
  agentId: ctx.agentId,
2437
- objectPrefix: webIdeEventsObjectPrefix(ctx.taskId, ctx.messageId),
2558
+ objectPrefix,
2438
2559
  ...patch
2439
2560
  });
2440
2561
  }
@@ -2446,6 +2567,7 @@ function createThrottledWebIdeMessageLogSync(cfg, ctx, onError, options) {
2446
2567
  let minio;
2447
2568
  let bucket = "";
2448
2569
  let firstLogNotified = false;
2570
+ const resolvePrefix = () => options?.objectPrefixOverride?.trim() || webIdeEventsObjectPrefix(ctx.taskId, ctx.messageId);
2449
2571
  const ensureMinio = async () => {
2450
2572
  if (minio) return;
2451
2573
  const created = await createApmLogMinioClient(cfg);
@@ -2469,11 +2591,16 @@ function createThrottledWebIdeMessageLogSync(cfg, ctx, onError, options) {
2469
2591
  lastRunAt = Date.now();
2470
2592
  try {
2471
2593
  await ensureMinio();
2472
- const prefix = webIdeEventsObjectPrefix(ctx.taskId, ctx.messageId);
2594
+ const prefix = resolvePrefix();
2473
2595
  await putDirtyEvents(minio, bucket, prefix, events);
2474
- await upsertLogHeader(cfg, ctx, {
2475
- eventCount: session.getEventCount()
2476
- });
2596
+ await upsertLogHeader(
2597
+ cfg,
2598
+ ctx,
2599
+ {
2600
+ eventCount: session.getEventCount()
2601
+ },
2602
+ prefix
2603
+ );
2477
2604
  session.clearDirtyIfUnchanged(events);
2478
2605
  await notifyFirstLog();
2479
2606
  } catch (err) {
@@ -2491,12 +2618,17 @@ function createThrottledWebIdeMessageLogSync(cfg, ctx, onError, options) {
2491
2618
  return {
2492
2619
  async markRun(runId, runStatus, lastError, tokenUsage) {
2493
2620
  try {
2494
- await upsertLogHeader(cfg, ctx, {
2495
- runId,
2496
- runStatus,
2497
- lastError: lastError ?? null,
2498
- ...tokenUsage ? { tokenUsage } : {}
2499
- });
2621
+ await upsertLogHeader(
2622
+ cfg,
2623
+ ctx,
2624
+ {
2625
+ runId,
2626
+ runStatus,
2627
+ lastError: lastError ?? null,
2628
+ ...tokenUsage ? { tokenUsage } : {}
2629
+ },
2630
+ resolvePrefix()
2631
+ );
2500
2632
  } catch (err) {
2501
2633
  onError(err);
2502
2634
  }
@@ -3856,6 +3988,276 @@ async function handleStartProject(cfg, msg, signal) {
3856
3988
  }
3857
3989
  }
3858
3990
 
3991
+ // src/commands/connect/handle-package.ts
3992
+ async function updatePackageStatus(cfg, packageId, status, extra) {
3993
+ const api = createApmApiClient(cfg);
3994
+ await api.cli.projectPackageUpdateStatus({
3995
+ id: packageId,
3996
+ status,
3997
+ ...extra?.error != null ? { error: extra.error } : {},
3998
+ ...extra?.content != null ? { content: extra.content } : {},
3999
+ ...extra?.artifactPath != null ? { artifactPath: extra.artifactPath } : {}
4000
+ });
4001
+ }
4002
+ function expectedArtifactPath(target, packageId) {
4003
+ if (target === "frontend") {
4004
+ return `/data/artifacts/vue/${packageId}/dist.zip`;
4005
+ }
4006
+ return `/data/artifacts/springboot/${packageId}/manifest.json`;
4007
+ }
4008
+ async function handleWebIdePackage(cfg, msg, signal) {
4009
+ const workdir = requireRemoteWorkdir(msg.workdir);
4010
+ const messageId = msg.messageId;
4011
+ const packageId = msg.packageId;
4012
+ const projectId = msg.projectId;
4013
+ console.log(
4014
+ `[apm] webide-package target=${msg.target} packageId=${packageId} projectId=${projectId} baseBranch=${msg.baseBranch}`
4015
+ );
4016
+ await updatePackageStatus(cfg, packageId, "RUNNING");
4017
+ const eventSession = new EventSession(msg.content);
4018
+ const logSyncRef = {
4019
+ current: createThrottledWebIdeMessageLogSync(
4020
+ cfg,
4021
+ {
4022
+ taskId: `package:${packageId}`,
4023
+ messageId,
4024
+ agentId: "webide-package"
4025
+ },
4026
+ (err) => {
4027
+ console.warn(
4028
+ "[apm] WebIDE \u6253\u5305\u65E5\u5FD7\u540C\u6B65\u5931\u8D25:",
4029
+ err instanceof Error ? err.message : err
4030
+ );
4031
+ },
4032
+ {
4033
+ objectPrefixOverride: `events/webide-package/${packageId}/`
4034
+ }
4035
+ )
4036
+ };
4037
+ const syncPrepLog = async () => {
4038
+ const sync = logSyncRef.current;
4039
+ if (!sync) return;
4040
+ sync.schedule(eventSession);
4041
+ await sync.flush(eventSession);
4042
+ };
4043
+ const runPrepStep = async (step, work, formatOk) => {
4044
+ eventSession.addCliStep(step, "\u8FDB\u884C\u4E2D\u2026", "running");
4045
+ await syncPrepLog();
4046
+ try {
4047
+ const result = await work();
4048
+ eventSession.addCliStep(step, formatOk(result), "ok");
4049
+ await syncPrepLog();
4050
+ return result;
4051
+ } catch (err) {
4052
+ const detail = err instanceof Error ? err.message : String(err);
4053
+ eventSession.addCliStep(step, detail, "error");
4054
+ await syncPrepLog();
4055
+ throw err;
4056
+ }
4057
+ };
4058
+ try {
4059
+ if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
4060
+ await runPrepStep(
4061
+ "\u521D\u59CB\u5316\u5DE5\u4F5C\u533A",
4062
+ async () => {
4063
+ const { didInit } = await ensureWorkspaceInitialized(workdir);
4064
+ return didInit;
4065
+ },
4066
+ (didInit) => didInit ? "\u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A" : "\u5DE5\u4F5C\u533A\u5DF2\u5C31\u7EEA"
4067
+ );
4068
+ const cliVersion = readCliVersion();
4069
+ if (shouldSyncSkillsForCliVersion(workdir, cliVersion)) {
4070
+ if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
4071
+ await runPrepStep(
4072
+ "\u540C\u6B65 WebIDE \u89C4\u5219",
4073
+ async () => {
4074
+ await copyWebIdeTemplateFiles(workspaceApmDir(workdir), workdir);
4075
+ markSkillsSyncedForCliVersion(workdir, cliVersion);
4076
+ return cliVersion;
4077
+ },
4078
+ (version) => `\u5DF2\u540C\u6B65\u81F3 CLI ${version}`
4079
+ );
4080
+ } else {
4081
+ eventSession.addCliStep(
4082
+ "\u540C\u6B65 WebIDE \u89C4\u5219",
4083
+ `\u7248\u672C\u4E00\u81F4\uFF08${cliVersion}\uFF09\uFF0C\u8DF3\u8FC7`,
4084
+ "skip"
4085
+ );
4086
+ await syncPrepLog();
4087
+ }
4088
+ await runPrepStep(
4089
+ "\u5DE5\u4F5C\u533A\u4ED3\u5E93\u6E05\u5355",
4090
+ async () => {
4091
+ const workspaceRepos = resolveWorkspaceRepos(workdir);
4092
+ try {
4093
+ await enrichWorkspaceReposRemoteUrls(workspaceRepos);
4094
+ } catch (err) {
4095
+ console.warn(
4096
+ "[apm] \u8865\u9F50 workspace-repos remoteUrl \u5931\u8D25:",
4097
+ err instanceof Error ? err.message : err
4098
+ );
4099
+ }
4100
+ return workspaceRepos;
4101
+ },
4102
+ (repos) => `kind=${repos.kind} repos=${repos.repos.length}`
4103
+ );
4104
+ if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
4105
+ await runPrepStep(
4106
+ "\u5207\u6362\u57FA\u7EBF\u5206\u652F",
4107
+ async () => checkoutBaselineBranches(msg.baseBranch, { cwd: workdir }),
4108
+ (result) => `baseline=${result.baseline} kind=${result.kind} repos=${result.repos.length}`
4109
+ );
4110
+ if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
4111
+ await runPrepStep(
4112
+ "\u540C\u6B65\u9879\u76EE\u6587\u6863",
4113
+ async () => {
4114
+ const docs = await syncProjectDocumentsPull(workdir, void 0, {
4115
+ projectId
4116
+ });
4117
+ return docs;
4118
+ },
4119
+ (result) => {
4120
+ if (!result.synced) {
4121
+ return result.projectId ? "\u65E0\u9700\u540C\u6B65\u6216\u8DF3\u8FC7" : "\u8DF3\u8FC7\uFF1A\u65E0 projectId";
4122
+ }
4123
+ return `downloaded=${result.downloaded} deleted=${result.deleted}`;
4124
+ }
4125
+ );
4126
+ eventSession.addCliStep("\u542F\u52A8\u6253\u5305 Agent", "\u51C6\u5907 create\u2026", "running");
4127
+ await syncPrepLog();
4128
+ const artifactHint = expectedArtifactPath(msg.target, packageId);
4129
+ let failedByTool = false;
4130
+ const outcome = await runCursorAgent(
4131
+ cfg,
4132
+ {
4133
+ messageId,
4134
+ prompt: msg.content,
4135
+ model: msg.model,
4136
+ apiKey: msg.apiKey,
4137
+ workdir,
4138
+ user: msg.user || "webide"
4139
+ },
4140
+ {
4141
+ signal,
4142
+ forceSend: true,
4143
+ eventSession,
4144
+ enableAppendMessage: false,
4145
+ enableAskQuestion: false,
4146
+ enableWebIdePlanTools: false,
4147
+ enablePtySessionMcp: false,
4148
+ enableSandbox: false,
4149
+ enablePackageStatusTools: true,
4150
+ packageId,
4151
+ onPackageFailed: async () => {
4152
+ failedByTool = true;
4153
+ },
4154
+ taskId: `package:${packageId}`,
4155
+ createRemoteLogSync: (agentId) => {
4156
+ logSyncRef.current = createThrottledWebIdeMessageLogSync(
4157
+ cfg,
4158
+ {
4159
+ taskId: `package:${packageId}`,
4160
+ messageId,
4161
+ agentId
4162
+ },
4163
+ (err) => {
4164
+ console.warn(
4165
+ "[apm] WebIDE \u6253\u5305\u65E5\u5FD7\u540C\u6B65\u5931\u8D25:",
4166
+ err instanceof Error ? err.message : err
4167
+ );
4168
+ },
4169
+ {
4170
+ objectPrefixOverride: `events/webide-package/${packageId}/`
4171
+ }
4172
+ );
4173
+ return logSyncRef.current;
4174
+ },
4175
+ onRunStarted: async ({ agentId, runId }) => {
4176
+ eventSession.addCliStep(
4177
+ "\u542F\u52A8\u6253\u5305 Agent",
4178
+ `agentId=${agentId} runId=${runId}`,
4179
+ "ok"
4180
+ );
4181
+ logSyncRef.current?.schedule(eventSession);
4182
+ await logSyncRef.current?.markRun(runId, "running");
4183
+ await logSyncRef.current?.flush(eventSession);
4184
+ }
4185
+ }
4186
+ );
4187
+ const tokenUsage = outcome.usage != null ? {
4188
+ modelId: outcome.modelId ?? (msg.model?.trim() || void 0),
4189
+ inputTokens: outcome.usage.inputTokens,
4190
+ outputTokens: outcome.usage.outputTokens,
4191
+ cacheReadTokens: outcome.usage.cacheReadTokens,
4192
+ cacheWriteTokens: outcome.usage.cacheWriteTokens,
4193
+ totalTokens: outcome.usage.totalTokens,
4194
+ ...outcome.usage.reasoningTokens != null ? { reasoningTokens: outcome.usage.reasoningTokens } : {}
4195
+ } : void 0;
4196
+ if (outcome.status === "error") {
4197
+ const detail = formatCursorRunFailure(outcome.runId, {
4198
+ resultText: outcome.result
4199
+ });
4200
+ await logSyncRef.current?.markRun(
4201
+ outcome.runId,
4202
+ "error",
4203
+ detail,
4204
+ tokenUsage
4205
+ );
4206
+ if (!failedByTool) {
4207
+ await updatePackageStatus(cfg, packageId, "FAILED", { error: detail });
4208
+ }
4209
+ return;
4210
+ }
4211
+ if (outcome.status === "cancelled" || signal.aborted) {
4212
+ await logSyncRef.current?.markRun(
4213
+ outcome.runId,
4214
+ "cancelled",
4215
+ "\u5DF2\u53D6\u6D88",
4216
+ tokenUsage
4217
+ );
4218
+ await updatePackageStatus(cfg, packageId, "CANCELLED", {
4219
+ error: "\u5DF2\u53D6\u6D88"
4220
+ });
4221
+ return;
4222
+ }
4223
+ await logSyncRef.current?.markRun(
4224
+ outcome.runId,
4225
+ "finished",
4226
+ null,
4227
+ tokenUsage
4228
+ );
4229
+ if (failedByTool) {
4230
+ console.log(
4231
+ `[apm] webide-package Agent \u5DF2\u6807 FAILED packageId=${packageId}`
4232
+ );
4233
+ return;
4234
+ }
4235
+ await updatePackageStatus(cfg, packageId, "SUCCESS", {
4236
+ artifactPath: artifactHint
4237
+ });
4238
+ console.log(
4239
+ `[apm] webide-package \u5B8C\u6210 target=${msg.target} packageId=${packageId} agentId=${outcome.agentId}`
4240
+ );
4241
+ } catch (err) {
4242
+ const detail = err instanceof Error ? err.message : String(err);
4243
+ console.error(`[apm] webide-package \u5931\u8D25: ${detail}`);
4244
+ try {
4245
+ if (signal.aborted) {
4246
+ await updatePackageStatus(cfg, packageId, "CANCELLED", {
4247
+ error: detail
4248
+ });
4249
+ } else {
4250
+ await updatePackageStatus(cfg, packageId, "FAILED", { error: detail });
4251
+ }
4252
+ } catch (statusErr) {
4253
+ console.error(
4254
+ "[apm] \u5199\u5165\u6253\u5305 FAILED \u5931\u8D25:",
4255
+ statusErr instanceof Error ? statusErr.message : statusErr
4256
+ );
4257
+ }
4258
+ }
4259
+ }
4260
+
3859
4261
  // src/commands/connect/webide-message-worker.ts
3860
4262
  var controllers = /* @__PURE__ */ new Map();
3861
4263
  async function runJob(cfg, msg) {
@@ -3864,6 +4266,8 @@ async function runJob(cfg, msg) {
3864
4266
  try {
3865
4267
  if (msg.type === "webide-start-project") {
3866
4268
  await handleStartProject(cfg, msg, controller.signal);
4269
+ } else if (msg.type === "webide-package") {
4270
+ await handleWebIdePackage(cfg, msg, controller.signal);
3867
4271
  } else {
3868
4272
  await handleWebIdeInboundMessage(cfg, msg, controller.signal);
3869
4273
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-project-manage-cli",
3
- "version": "8.0.8",
3
+ "version": "8.0.9",
4
4
  "description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
5
5
  "type": "module",
6
6
  "private": false,
@@ -0,0 +1,75 @@
1
+ ## WebIDE 项目打包(webide-package)
2
+
3
+ 适用:独立打包 Agent(不 resume 任务对话)。只打 payload 指定的 **一个** target(`frontend` / `backend`)。
4
+
5
+ ### 开始前
6
+
7
+ 1. Read 本规则全文。
8
+ 2. Read `.apm/project/<项目ID>/` 下 path/文件名含 `deploy` 的文档(先 manifest,再全文)。命令与仓路径**只**来自文档,禁止猜测。
9
+ 3. Read `.apm/workspace-repos.json`,按多仓清单定位本轮要打的前端/后端仓库路径。
10
+
11
+ ### 通用硬性约束
12
+
13
+ - **禁止**修改业务代码、提交 Git、创建 PR。
14
+ - **禁止**为修依赖或修编译错误而改代码 / 装依赖 / 改 lockfile;构建失败 → 以错误结束。
15
+ - **禁止**在仓库工作区内留下交付用 zip / 拷贝后的 jar;临时文件只用 `/data/` 下目录(如 `/data/artifacts/.tmp/<packageId>/`),成功后可删临时目录。
16
+ - 临时目录与最终产物均在 `/data` 下,不要用 `/tmp`。
17
+
18
+ ### target=frontend(Vue)
19
+
20
+ 1. 按 deploy 文档进入前端目录。
21
+ 2. **不要**执行 `npm i` / `pnpm i` 等;分支切换后依赖已就绪。
22
+ 3. 执行文档中的打包命令(通常 `npm run build`)。
23
+ 4. 确认产出 `dist/`(或文档写明的目录)。
24
+ 5. 在 `/data` 下压缩(勿在工作区生成 `dist.zip`),落到:
25
+
26
+ `/data/artifacts/vue/<packageId>/dist.zip`
27
+
28
+ 6. 缺产物或构建失败 → 调用 **SetPackageFailed**(传入失败原因)后结束。
29
+
30
+ ### target=backend(SpringBoot)
31
+
32
+ 1. 按 deploy 文档进入后端目录,执行 `mvn clean package`(或以文档为准)。
33
+ 2. 按文档收集**全量**需交付的 jar 与 sql(增量规则若文档另有说明则从其规定;无说明则收文档列出的全部)。
34
+ 3. 对每个文件算内容 hash(sha256),写入全局池(已存在同 hash 则跳过复制):
35
+
36
+ - jar → `/data/artifacts/springboot/jars/<hash>.jar`
37
+ - sql → `/data/artifacts/springboot/sqls/<hash>.sql`(或保留扩展名,文件名主体为 hash)
38
+
39
+ 4. 写入清单(**仅**此目录放 manifest,不要堆 jar/sql 副本):
40
+
41
+ `/data/artifacts/springboot/<packageId>/manifest.json`
42
+
43
+ 5. manifest 示例(字段名与结构须一致;值按实际填写):
44
+
45
+ ```json
46
+ {
47
+ "packageId": "pkg_…",
48
+ "projectId": "clx…",
49
+ "target": "backend",
50
+ "baseBranch": "release/1.0",
51
+ "createdAt": "2026-09-02T07:00:00.000Z",
52
+ "commitId": "a1b2c3d4…",
53
+ "jars": [
54
+ {
55
+ "name": "app-1.2.3.jar",
56
+ "hash": "e3b0…b855",
57
+ "path": "/data/artifacts/springboot/jars/e3b0…b855.jar"
58
+ }
59
+ ],
60
+ "sqls": [
61
+ {
62
+ "name": "20260301_add_col.sql",
63
+ "hash": "2c26…e7ae",
64
+ "path": "/data/artifacts/springboot/sqls/2c26…e7ae.sql"
65
+ }
66
+ ]
67
+ }
68
+ ```
69
+
70
+ 6. 构建或落盘失败 → 调用 **SetPackageFailed**(传入失败原因)后结束。
71
+
72
+ ### 结束
73
+
74
+ - 成功:确认产物已落到约定路径后正常结束(不要调用 SetPackageFailed)。
75
+ - 失败:先调用 SetPackageFailed 写入原因,再结束。