ai-project-manage-cli 8.0.8 → 8.0.10

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,49 @@ 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(`[apm] \u5DE5\u4F5C\u533A\u6709\u672A\u63D0\u4EA4\u53D8\u66F4\uFF0C\u65E0\u6CD5\u5207\u6362\u5230\u57FA\u7EBF\u5206\u652F ${baseline}`);
987
+ }
988
+ await ensureRemoteBaselineBranch(gitRoot, baseline);
989
+ const current = await getCurrentBranch(gitRoot);
990
+ if (current !== baseline) {
991
+ await execGit(gitRoot, ["checkout", "-B", baseline, `origin/${baseline}`]);
992
+ }
993
+ await execGit(gitRoot, ["reset", "--hard", `origin/${baseline}`]);
994
+ const head = (await execGit(gitRoot, ["rev-parse", "--short", "HEAD"], true)).trim();
995
+ console.log(
996
+ `[apm] \u5DF2\u5207\u6362\u5230\u57FA\u7EBF ${baseline}\uFF0C\u5E76\u5BF9\u9F50 origin/${baseline}\uFF08${head}\uFF09`
997
+ );
998
+ return baseline;
999
+ }
945
1000
 
946
1001
  // src/commands/connect/cursor-agent.ts
947
1002
  import {
@@ -1803,6 +1858,45 @@ function createMysqlExecuteTool(options) {
1803
1858
  };
1804
1859
  }
1805
1860
 
1861
+ // src/commands/connect/tools/package-status-tool.ts
1862
+ function createSetPackageFailedTool(options) {
1863
+ const { packageId, setFailed } = options;
1864
+ return {
1865
+ SetPackageFailed: {
1866
+ 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",
1867
+ inputSchema: {
1868
+ type: "object",
1869
+ properties: {
1870
+ error: {
1871
+ type: "string",
1872
+ description: "\u5931\u8D25\u539F\u56E0\u6458\u8981\uFF08\u4F1A\u5199\u5165\u6253\u5305\u8BB0\u5F55\uFF0C\u4F9B\u5217\u8868/\u65E5\u5FD7\u5C55\u793A\uFF09"
1873
+ }
1874
+ },
1875
+ required: ["error"]
1876
+ },
1877
+ execute: async (args) => {
1878
+ const error = typeof args.error === "string" ? args.error.trim() : "";
1879
+ if (!error) {
1880
+ return {
1881
+ content: [{ type: "text", text: "error \u4E0D\u80FD\u4E3A\u7A7A" }],
1882
+ isError: true
1883
+ };
1884
+ }
1885
+ try {
1886
+ await setFailed(error);
1887
+ return `\u5DF2\u5C06\u6253\u5305\u8BB0\u5F55 ${packageId} \u6807\u4E3A FAILED`;
1888
+ } catch (err) {
1889
+ const detail = err instanceof Error ? err.message : String(err);
1890
+ return {
1891
+ content: [{ type: "text", text: `\u5199\u5165\u5931\u8D25\u72B6\u6001\u5931\u8D25: ${detail}` }],
1892
+ isError: true
1893
+ };
1894
+ }
1895
+ }
1896
+ }
1897
+ };
1898
+ }
1899
+
1806
1900
  // src/commands/connect/tools/index.ts
1807
1901
  var PLAN_MODE_ASK_QUESTION_HINT = `[SDK \u73AF\u5883\u8BF4\u660E]
1808
1902
  AskQuestion \u5DF2\u901A\u8FC7 MCP \u670D\u52A1\u5668 custom-user-tools \u6CE8\u518C\uFF0C\u5DE5\u5177\u540D\u4E3A AskQuestion\u3002
@@ -1853,6 +1947,24 @@ function createCursorCustomTools(cfg, options) {
1853
1947
  );
1854
1948
  }
1855
1949
  }
1950
+ if (options.enablePackageStatusTools && options.packageId) {
1951
+ const packageId = options.packageId;
1952
+ const { cli } = createApmApiClient(cfg);
1953
+ Object.assign(
1954
+ tools,
1955
+ createSetPackageFailedTool({
1956
+ packageId,
1957
+ setFailed: async (error) => {
1958
+ await cli.projectPackageUpdateStatus({
1959
+ id: packageId,
1960
+ status: "FAILED",
1961
+ error
1962
+ });
1963
+ await options.onPackageFailed?.(error);
1964
+ }
1965
+ })
1966
+ );
1967
+ }
1856
1968
  return tools;
1857
1969
  }
1858
1970
  function withPlanModeToolHint(prompt, mode) {
@@ -1973,7 +2085,10 @@ async function runCursorAgent(cfg, ctx, options) {
1973
2085
  enableMysqlTools: options.enableMysqlTools,
1974
2086
  sqlExecutionId: options.sqlExecutionId,
1975
2087
  taskId: options.taskId,
1976
- workdir
2088
+ workdir,
2089
+ enablePackageStatusTools: options.enablePackageStatusTools,
2090
+ packageId: options.packageId,
2091
+ onPackageFailed: options.onPackageFailed
1977
2092
  });
1978
2093
  const enableSandbox = Boolean(options.enableSandbox);
1979
2094
  const mcpServers = options.enablePtySessionMcp ? createPtySessionMcpServers(cfg) : void 0;
@@ -2429,12 +2544,12 @@ async function putDirtyEvents(minio, bucket, prefix, events) {
2429
2544
  });
2430
2545
  }
2431
2546
  }
2432
- async function upsertLogHeader(cfg, ctx, patch) {
2547
+ async function upsertLogHeader(cfg, ctx, patch, objectPrefix) {
2433
2548
  const api = createApmApiClient(cfg);
2434
2549
  await api.cli.webideUpsertMessageLog({
2435
2550
  messageId: ctx.messageId,
2436
2551
  agentId: ctx.agentId,
2437
- objectPrefix: webIdeEventsObjectPrefix(ctx.taskId, ctx.messageId),
2552
+ objectPrefix,
2438
2553
  ...patch
2439
2554
  });
2440
2555
  }
@@ -2446,6 +2561,7 @@ function createThrottledWebIdeMessageLogSync(cfg, ctx, onError, options) {
2446
2561
  let minio;
2447
2562
  let bucket = "";
2448
2563
  let firstLogNotified = false;
2564
+ const resolvePrefix = () => options?.objectPrefixOverride?.trim() || webIdeEventsObjectPrefix(ctx.taskId, ctx.messageId);
2449
2565
  const ensureMinio = async () => {
2450
2566
  if (minio) return;
2451
2567
  const created = await createApmLogMinioClient(cfg);
@@ -2469,11 +2585,16 @@ function createThrottledWebIdeMessageLogSync(cfg, ctx, onError, options) {
2469
2585
  lastRunAt = Date.now();
2470
2586
  try {
2471
2587
  await ensureMinio();
2472
- const prefix = webIdeEventsObjectPrefix(ctx.taskId, ctx.messageId);
2588
+ const prefix = resolvePrefix();
2473
2589
  await putDirtyEvents(minio, bucket, prefix, events);
2474
- await upsertLogHeader(cfg, ctx, {
2475
- eventCount: session.getEventCount()
2476
- });
2590
+ await upsertLogHeader(
2591
+ cfg,
2592
+ ctx,
2593
+ {
2594
+ eventCount: session.getEventCount()
2595
+ },
2596
+ prefix
2597
+ );
2477
2598
  session.clearDirtyIfUnchanged(events);
2478
2599
  await notifyFirstLog();
2479
2600
  } catch (err) {
@@ -2491,12 +2612,17 @@ function createThrottledWebIdeMessageLogSync(cfg, ctx, onError, options) {
2491
2612
  return {
2492
2613
  async markRun(runId, runStatus, lastError, tokenUsage) {
2493
2614
  try {
2494
- await upsertLogHeader(cfg, ctx, {
2495
- runId,
2496
- runStatus,
2497
- lastError: lastError ?? null,
2498
- ...tokenUsage ? { tokenUsage } : {}
2499
- });
2615
+ await upsertLogHeader(
2616
+ cfg,
2617
+ ctx,
2618
+ {
2619
+ runId,
2620
+ runStatus,
2621
+ lastError: lastError ?? null,
2622
+ ...tokenUsage ? { tokenUsage } : {}
2623
+ },
2624
+ resolvePrefix()
2625
+ );
2500
2626
  } catch (err) {
2501
2627
  onError(err);
2502
2628
  }
@@ -3856,6 +3982,276 @@ async function handleStartProject(cfg, msg, signal) {
3856
3982
  }
3857
3983
  }
3858
3984
 
3985
+ // src/commands/connect/handle-package.ts
3986
+ async function updatePackageStatus(cfg, packageId, status, extra) {
3987
+ const api = createApmApiClient(cfg);
3988
+ await api.cli.projectPackageUpdateStatus({
3989
+ id: packageId,
3990
+ status,
3991
+ ...extra?.error != null ? { error: extra.error } : {},
3992
+ ...extra?.content != null ? { content: extra.content } : {},
3993
+ ...extra?.artifactPath != null ? { artifactPath: extra.artifactPath } : {}
3994
+ });
3995
+ }
3996
+ function expectedArtifactPath(target, packageId) {
3997
+ if (target === "frontend") {
3998
+ return `/data/artifacts/vue/${packageId}/dist.zip`;
3999
+ }
4000
+ return `/data/artifacts/springboot/${packageId}/manifest.json`;
4001
+ }
4002
+ async function handleWebIdePackage(cfg, msg, signal) {
4003
+ const workdir = requireRemoteWorkdir(msg.workdir);
4004
+ const messageId = msg.messageId;
4005
+ const packageId = msg.packageId;
4006
+ const projectId = msg.projectId;
4007
+ console.log(
4008
+ `[apm] webide-package target=${msg.target} packageId=${packageId} projectId=${projectId} baseBranch=${msg.baseBranch}`
4009
+ );
4010
+ await updatePackageStatus(cfg, packageId, "RUNNING");
4011
+ const eventSession = new EventSession(msg.content);
4012
+ const logSyncRef = {
4013
+ current: createThrottledWebIdeMessageLogSync(
4014
+ cfg,
4015
+ {
4016
+ taskId: `package:${packageId}`,
4017
+ messageId,
4018
+ agentId: "webide-package"
4019
+ },
4020
+ (err) => {
4021
+ console.warn(
4022
+ "[apm] WebIDE \u6253\u5305\u65E5\u5FD7\u540C\u6B65\u5931\u8D25:",
4023
+ err instanceof Error ? err.message : err
4024
+ );
4025
+ },
4026
+ {
4027
+ objectPrefixOverride: `events/webide-package/${packageId}/`
4028
+ }
4029
+ )
4030
+ };
4031
+ const syncPrepLog = async () => {
4032
+ const sync = logSyncRef.current;
4033
+ if (!sync) return;
4034
+ sync.schedule(eventSession);
4035
+ await sync.flush(eventSession);
4036
+ };
4037
+ const runPrepStep = async (step, work, formatOk) => {
4038
+ eventSession.addCliStep(step, "\u8FDB\u884C\u4E2D\u2026", "running");
4039
+ await syncPrepLog();
4040
+ try {
4041
+ const result = await work();
4042
+ eventSession.addCliStep(step, formatOk(result), "ok");
4043
+ await syncPrepLog();
4044
+ return result;
4045
+ } catch (err) {
4046
+ const detail = err instanceof Error ? err.message : String(err);
4047
+ eventSession.addCliStep(step, detail, "error");
4048
+ await syncPrepLog();
4049
+ throw err;
4050
+ }
4051
+ };
4052
+ try {
4053
+ if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
4054
+ await runPrepStep(
4055
+ "\u521D\u59CB\u5316\u5DE5\u4F5C\u533A",
4056
+ async () => {
4057
+ const { didInit } = await ensureWorkspaceInitialized(workdir);
4058
+ return didInit;
4059
+ },
4060
+ (didInit) => didInit ? "\u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A" : "\u5DE5\u4F5C\u533A\u5DF2\u5C31\u7EEA"
4061
+ );
4062
+ const cliVersion = readCliVersion();
4063
+ if (shouldSyncSkillsForCliVersion(workdir, cliVersion)) {
4064
+ if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
4065
+ await runPrepStep(
4066
+ "\u540C\u6B65 WebIDE \u89C4\u5219",
4067
+ async () => {
4068
+ await copyWebIdeTemplateFiles(workspaceApmDir(workdir), workdir);
4069
+ markSkillsSyncedForCliVersion(workdir, cliVersion);
4070
+ return cliVersion;
4071
+ },
4072
+ (version) => `\u5DF2\u540C\u6B65\u81F3 CLI ${version}`
4073
+ );
4074
+ } else {
4075
+ eventSession.addCliStep(
4076
+ "\u540C\u6B65 WebIDE \u89C4\u5219",
4077
+ `\u7248\u672C\u4E00\u81F4\uFF08${cliVersion}\uFF09\uFF0C\u8DF3\u8FC7`,
4078
+ "skip"
4079
+ );
4080
+ await syncPrepLog();
4081
+ }
4082
+ await runPrepStep(
4083
+ "\u5DE5\u4F5C\u533A\u4ED3\u5E93\u6E05\u5355",
4084
+ async () => {
4085
+ const workspaceRepos = resolveWorkspaceRepos(workdir);
4086
+ try {
4087
+ await enrichWorkspaceReposRemoteUrls(workspaceRepos);
4088
+ } catch (err) {
4089
+ console.warn(
4090
+ "[apm] \u8865\u9F50 workspace-repos remoteUrl \u5931\u8D25:",
4091
+ err instanceof Error ? err.message : err
4092
+ );
4093
+ }
4094
+ return workspaceRepos;
4095
+ },
4096
+ (repos) => `kind=${repos.kind} repos=${repos.repos.length}`
4097
+ );
4098
+ if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
4099
+ await runPrepStep(
4100
+ "\u5207\u6362\u57FA\u7EBF\u5E76\u62C9\u53D6\u6700\u65B0",
4101
+ async () => checkoutBaselineBranches(msg.baseBranch, { cwd: workdir }),
4102
+ (result) => `baseline=${result.baseline} kind=${result.kind} repos=${result.repos.length}`
4103
+ );
4104
+ if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
4105
+ await runPrepStep(
4106
+ "\u540C\u6B65\u9879\u76EE\u6587\u6863",
4107
+ async () => {
4108
+ const docs = await syncProjectDocumentsPull(workdir, void 0, {
4109
+ projectId
4110
+ });
4111
+ return docs;
4112
+ },
4113
+ (result) => {
4114
+ if (!result.synced) {
4115
+ return result.projectId ? "\u65E0\u9700\u540C\u6B65\u6216\u8DF3\u8FC7" : "\u8DF3\u8FC7\uFF1A\u65E0 projectId";
4116
+ }
4117
+ return `downloaded=${result.downloaded} deleted=${result.deleted}`;
4118
+ }
4119
+ );
4120
+ eventSession.addCliStep("\u542F\u52A8\u6253\u5305 Agent", "\u51C6\u5907 create\u2026", "running");
4121
+ await syncPrepLog();
4122
+ const artifactHint = expectedArtifactPath(msg.target, packageId);
4123
+ let failedByTool = false;
4124
+ const outcome = await runCursorAgent(
4125
+ cfg,
4126
+ {
4127
+ messageId,
4128
+ prompt: msg.content,
4129
+ model: msg.model,
4130
+ apiKey: msg.apiKey,
4131
+ workdir,
4132
+ user: msg.user || "webide"
4133
+ },
4134
+ {
4135
+ signal,
4136
+ forceSend: true,
4137
+ eventSession,
4138
+ enableAppendMessage: false,
4139
+ enableAskQuestion: false,
4140
+ enableWebIdePlanTools: false,
4141
+ enablePtySessionMcp: false,
4142
+ enableSandbox: false,
4143
+ enablePackageStatusTools: true,
4144
+ packageId,
4145
+ onPackageFailed: async () => {
4146
+ failedByTool = true;
4147
+ },
4148
+ taskId: `package:${packageId}`,
4149
+ createRemoteLogSync: (agentId) => {
4150
+ logSyncRef.current = createThrottledWebIdeMessageLogSync(
4151
+ cfg,
4152
+ {
4153
+ taskId: `package:${packageId}`,
4154
+ messageId,
4155
+ agentId
4156
+ },
4157
+ (err) => {
4158
+ console.warn(
4159
+ "[apm] WebIDE \u6253\u5305\u65E5\u5FD7\u540C\u6B65\u5931\u8D25:",
4160
+ err instanceof Error ? err.message : err
4161
+ );
4162
+ },
4163
+ {
4164
+ objectPrefixOverride: `events/webide-package/${packageId}/`
4165
+ }
4166
+ );
4167
+ return logSyncRef.current;
4168
+ },
4169
+ onRunStarted: async ({ agentId, runId }) => {
4170
+ eventSession.addCliStep(
4171
+ "\u542F\u52A8\u6253\u5305 Agent",
4172
+ `agentId=${agentId} runId=${runId}`,
4173
+ "ok"
4174
+ );
4175
+ logSyncRef.current?.schedule(eventSession);
4176
+ await logSyncRef.current?.markRun(runId, "running");
4177
+ await logSyncRef.current?.flush(eventSession);
4178
+ }
4179
+ }
4180
+ );
4181
+ const tokenUsage = outcome.usage != null ? {
4182
+ modelId: outcome.modelId ?? (msg.model?.trim() || void 0),
4183
+ inputTokens: outcome.usage.inputTokens,
4184
+ outputTokens: outcome.usage.outputTokens,
4185
+ cacheReadTokens: outcome.usage.cacheReadTokens,
4186
+ cacheWriteTokens: outcome.usage.cacheWriteTokens,
4187
+ totalTokens: outcome.usage.totalTokens,
4188
+ ...outcome.usage.reasoningTokens != null ? { reasoningTokens: outcome.usage.reasoningTokens } : {}
4189
+ } : void 0;
4190
+ if (outcome.status === "error") {
4191
+ const detail = formatCursorRunFailure(outcome.runId, {
4192
+ resultText: outcome.result
4193
+ });
4194
+ await logSyncRef.current?.markRun(
4195
+ outcome.runId,
4196
+ "error",
4197
+ detail,
4198
+ tokenUsage
4199
+ );
4200
+ if (!failedByTool) {
4201
+ await updatePackageStatus(cfg, packageId, "FAILED", { error: detail });
4202
+ }
4203
+ return;
4204
+ }
4205
+ if (outcome.status === "cancelled" || signal.aborted) {
4206
+ await logSyncRef.current?.markRun(
4207
+ outcome.runId,
4208
+ "cancelled",
4209
+ "\u5DF2\u53D6\u6D88",
4210
+ tokenUsage
4211
+ );
4212
+ await updatePackageStatus(cfg, packageId, "CANCELLED", {
4213
+ error: "\u5DF2\u53D6\u6D88"
4214
+ });
4215
+ return;
4216
+ }
4217
+ await logSyncRef.current?.markRun(
4218
+ outcome.runId,
4219
+ "finished",
4220
+ null,
4221
+ tokenUsage
4222
+ );
4223
+ if (failedByTool) {
4224
+ console.log(
4225
+ `[apm] webide-package Agent \u5DF2\u6807 FAILED packageId=${packageId}`
4226
+ );
4227
+ return;
4228
+ }
4229
+ await updatePackageStatus(cfg, packageId, "SUCCESS", {
4230
+ artifactPath: artifactHint
4231
+ });
4232
+ console.log(
4233
+ `[apm] webide-package \u5B8C\u6210 target=${msg.target} packageId=${packageId} agentId=${outcome.agentId}`
4234
+ );
4235
+ } catch (err) {
4236
+ const detail = err instanceof Error ? err.message : String(err);
4237
+ console.error(`[apm] webide-package \u5931\u8D25: ${detail}`);
4238
+ try {
4239
+ if (signal.aborted) {
4240
+ await updatePackageStatus(cfg, packageId, "CANCELLED", {
4241
+ error: detail
4242
+ });
4243
+ } else {
4244
+ await updatePackageStatus(cfg, packageId, "FAILED", { error: detail });
4245
+ }
4246
+ } catch (statusErr) {
4247
+ console.error(
4248
+ "[apm] \u5199\u5165\u6253\u5305 FAILED \u5931\u8D25:",
4249
+ statusErr instanceof Error ? statusErr.message : statusErr
4250
+ );
4251
+ }
4252
+ }
4253
+ }
4254
+
3859
4255
  // src/commands/connect/webide-message-worker.ts
3860
4256
  var controllers = /* @__PURE__ */ new Map();
3861
4257
  async function runJob(cfg, msg) {
@@ -3864,6 +4260,8 @@ async function runJob(cfg, msg) {
3864
4260
  try {
3865
4261
  if (msg.type === "webide-start-project") {
3866
4262
  await handleStartProject(cfg, msg, controller.signal);
4263
+ } else if (msg.type === "webide-package") {
4264
+ await handleWebIdePackage(cfg, msg, controller.signal);
3867
4265
  } else {
3868
4266
  await handleWebIdeInboundMessage(cfg, msg, controller.signal);
3869
4267
  }
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.10",
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 写入原因,再结束。