ai-project-manage-cli 5.0.19 → 5.0.21

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
@@ -483,10 +483,9 @@ async function runBranch(sessionId, options = {}) {
483
483
  workdirPath
484
484
  });
485
485
  if (!baseline.repositoryId) {
486
- throw new Error(
487
- `[apm] \u672A\u5728\u5E73\u53F0\u627E\u5230\u4E0E\u5F53\u524D\u76EE\u5F55\u5339\u914D\u7684\u5DE5\u4F5C\u76EE\u5F55\u767B\u8BB0\uFF1A${workdirPath}
488
- \u8BF7\u5148\u5728\u5E73\u53F0\u767B\u8BB0\u8BE5\u8DEF\u5F84\u5BF9\u5E94\u7684\u5DE5\u4F5C\u76EE\u5F55\u4E0E\u4ED3\u5E93\u3002`
489
- );
486
+ const detail = baseline.diagnostic?.message ?? `\u672A\u5728\u5E73\u53F0\u627E\u5230\u4E0E\u5F53\u524D\u76EE\u5F55\u5339\u914D\u7684\u5DE5\u4F5C\u76EE\u5F55\u767B\u8BB0\uFF1A${workdirPath}\uFF08\u89C4\u8303\u5316\uFF1A${baseline.workdirPath}\uFF09
487
+ \u8BF7\u5148\u5728\u5E73\u53F0\u767B\u8BB0\u8BE5\u8DEF\u5F84\u5BF9\u5E94\u7684\u5DE5\u4F5C\u76EE\u5F55\u4E0E\u4ED3\u5E93\u3002`;
488
+ throw new Error(`[apm] ${detail}`);
490
489
  }
491
490
  const baselineBranch = (baseline.defaultBranch ?? "").trim();
492
491
  if (!baselineBranch) {
@@ -1050,13 +1049,19 @@ import { Agent, CursorAgentError } from "@cursor/sdk";
1050
1049
  import { resolve as resolve3 } from "path";
1051
1050
 
1052
1051
  // src/session-utils.ts
1052
+ import { formatCursorMessageLogEvent } from "@opc/shared";
1053
1053
  var EventSession = class {
1054
1054
  events = [];
1055
+ dirtyIndices = /* @__PURE__ */ new Set();
1055
1056
  constructor(prompt) {
1056
1057
  this.events.push({
1057
1058
  type: "input",
1058
1059
  content: prompt
1059
1060
  });
1061
+ this.markDirty(0);
1062
+ }
1063
+ markDirty(index) {
1064
+ this.dirtyIndices.add(index);
1060
1065
  }
1061
1066
  addEvent(event) {
1062
1067
  const latestEvent = this.events[this.events.length - 1];
@@ -1065,16 +1070,19 @@ var EventSession = class {
1065
1070
  return;
1066
1071
  }
1067
1072
  if (formatedEvent.type === "tool_call") {
1068
- const existingToolCall = this.events.find(
1073
+ const existingIndex = this.events.findIndex(
1069
1074
  (e) => e.type === "tool_call" && e.call_id === formatedEvent.call_id
1070
1075
  );
1071
- if (existingToolCall) {
1076
+ if (existingIndex >= 0) {
1077
+ const existingToolCall = this.events[existingIndex];
1072
1078
  existingToolCall.args = formatedEvent.args;
1073
1079
  existingToolCall.result = formatedEvent.result;
1074
1080
  existingToolCall.status = formatedEvent.status;
1081
+ this.markDirty(existingIndex);
1075
1082
  return;
1076
1083
  }
1077
1084
  this.events.push(formatedEvent);
1085
+ this.markDirty(this.events.length - 1);
1078
1086
  return;
1079
1087
  }
1080
1088
  if (formatedEvent.type === "status") {
@@ -1082,6 +1090,7 @@ var EventSession = class {
1082
1090
  }
1083
1091
  if (formatedEvent.type === "request") {
1084
1092
  this.events.push(formatedEvent);
1093
+ this.markDirty(this.events.length - 1);
1085
1094
  return;
1086
1095
  }
1087
1096
  if (latestEvent?.type === formatedEvent.type) {
@@ -1097,9 +1106,11 @@ var EventSession = class {
1097
1106
  latestEvent.text = formatedEvent.text;
1098
1107
  break;
1099
1108
  }
1109
+ this.markDirty(this.events.length - 1);
1100
1110
  return;
1101
1111
  }
1102
1112
  this.events.push(formatedEvent);
1113
+ this.markDirty(this.events.length - 1);
1103
1114
  }
1104
1115
  formatEvent(event) {
1105
1116
  switch (event.type) {
@@ -1144,43 +1155,29 @@ var EventSession = class {
1144
1155
  return { type: "status", status: event.status, message: event.message };
1145
1156
  }
1146
1157
  }
1158
+ getDirtyEvents() {
1159
+ return [...this.dirtyIndices].sort((a, b) => a - b).map((index) => {
1160
+ const event = this.events[index];
1161
+ return {
1162
+ index,
1163
+ type: event.type,
1164
+ data: JSON.stringify(event)
1165
+ };
1166
+ });
1167
+ }
1168
+ clearDirty(indices) {
1169
+ for (const index of indices) {
1170
+ this.dirtyIndices.delete(index);
1171
+ }
1172
+ }
1147
1173
  /** 合并所有 assistant 片段,供剧场成员回传等场景使用 */
1148
1174
  getAssistantText() {
1149
1175
  return this.events.filter((e) => e.type === "assistant").map((e) => String(e.content ?? "")).join("\n").trim();
1150
1176
  }
1151
1177
  resolveLogContent() {
1152
- return this.events.map((event) => {
1153
- const type = event.type;
1154
- const content = (function(type2) {
1155
- if (type2 === "input") {
1156
- return `## \u7528\u6237\u8F93\u5165
1157
-
1158
- ${event.content}
1159
- `;
1160
- }
1161
- if (type2 === "assistant") {
1162
- return `## \u6A21\u578B\u8F93\u51FA
1163
-
1164
- ${event.content}
1165
- `;
1166
- }
1167
- if (type2 === "thinking") {
1168
- return `## \u6A21\u578B\u601D\u8003
1169
-
1170
- ${event.content}
1171
- `;
1172
- }
1173
- if (type2 === "tool_call") {
1174
- return "````toolcall\n" + JSON.stringify(event, null, 2) + "\n````\n";
1175
- }
1176
- return `## \u672A\u77E5\u4E8B\u4EF6\uFF1A${type2}
1177
-
1178
- \`\`\`json
1179
- ${JSON.stringify(event, null, 2)}
1180
- \`\`\``;
1181
- })(type);
1182
- return content;
1183
- }).join("\n");
1178
+ return this.events.map(
1179
+ (event) => formatCursorMessageLogEvent(event.type, JSON.stringify(event))
1180
+ ).join("\n");
1184
1181
  }
1185
1182
  };
1186
1183
 
@@ -1202,14 +1199,15 @@ function createThrottledCursorMessageLogSync(cfg, ctx, onError) {
1202
1199
  let lastRunAt = 0;
1203
1200
  let timer;
1204
1201
  let latestSession;
1205
- const execute = async () => {
1206
- const session = latestSession;
1207
- if (!session) {
1202
+ const syncDirtyEvents = async (session) => {
1203
+ const events = session.getDirtyEvents();
1204
+ if (events.length === 0) {
1208
1205
  return;
1209
1206
  }
1210
1207
  lastRunAt = Date.now();
1211
1208
  try {
1212
- await syncCursorMessageLog(cfg, ctx, session);
1209
+ await syncCursorMessageLog(cfg, ctx, events);
1210
+ session.clearDirty(events.map((event) => event.index));
1213
1211
  } catch (err) {
1214
1212
  onError(err);
1215
1213
  }
@@ -1224,7 +1222,7 @@ function createThrottledCursorMessageLogSync(cfg, ctx, onError) {
1224
1222
  clearTimeout(timer);
1225
1223
  timer = void 0;
1226
1224
  }
1227
- void execute();
1225
+ void syncDirtyEvents(session);
1228
1226
  return;
1229
1227
  }
1230
1228
  if (timer) {
@@ -1232,7 +1230,7 @@ function createThrottledCursorMessageLogSync(cfg, ctx, onError) {
1232
1230
  }
1233
1231
  timer = setTimeout(() => {
1234
1232
  timer = void 0;
1235
- void execute();
1233
+ void syncDirtyEvents(latestSession);
1236
1234
  }, CURSOR_MESSAGE_LOG_SYNC_INTERVAL_MS - elapsed);
1237
1235
  },
1238
1236
  async flush(session) {
@@ -1241,14 +1239,13 @@ function createThrottledCursorMessageLogSync(cfg, ctx, onError) {
1241
1239
  clearTimeout(timer);
1242
1240
  timer = void 0;
1243
1241
  }
1244
- await execute();
1242
+ await syncDirtyEvents(session);
1245
1243
  }
1246
1244
  };
1247
1245
  }
1248
- async function syncCursorMessageLog(cfg, ctx, session) {
1246
+ async function syncCursorMessageLog(cfg, ctx, events) {
1249
1247
  const agentId = ctx.agentId.trim();
1250
- const data = session.resolveLogContent().trim();
1251
- if (!agentId || !data) {
1248
+ if (!agentId || events.length === 0) {
1252
1249
  return;
1253
1250
  }
1254
1251
  const api = createApmApiClient(cfg);
@@ -1256,7 +1253,7 @@ async function syncCursorMessageLog(cfg, ctx, session) {
1256
1253
  sessionId: ctx.sessionId,
1257
1254
  messageId: ctx.messageId,
1258
1255
  agentId,
1259
- data
1256
+ events
1260
1257
  });
1261
1258
  }
1262
1259
 
@@ -1302,9 +1299,6 @@ async function runCursorAgent(cfg, ctx) {
1302
1299
  for await (const event of run.stream()) {
1303
1300
  eventSession.addEvent(event);
1304
1301
  const chunk = assistantStreamChunk(event);
1305
- if (chunk) {
1306
- process.stdout.write(chunk);
1307
- }
1308
1302
  syncRemoteLog.schedule(eventSession);
1309
1303
  }
1310
1304
  await syncRemoteLog.flush(eventSession);
@@ -1425,6 +1419,7 @@ async function runConnect(options) {
1425
1419
  const ws = new WebSocket(url);
1426
1420
  let stopHeartbeat;
1427
1421
  let shuttingDown = false;
1422
+ let inboundQueue = Promise.resolve();
1428
1423
  const shutdown = (code = 0) => {
1429
1424
  if (shuttingDown) return;
1430
1425
  shuttingDown = true;
@@ -1443,7 +1438,7 @@ async function runConnect(options) {
1443
1438
  });
1444
1439
  ws.on("message", (data) => {
1445
1440
  const text = Buffer.isBuffer(data) ? data.toString("utf8") : String(data);
1446
- void handleInboundMessage(cfg, text);
1441
+ inboundQueue = inboundQueue.then(() => handleInboundMessage(cfg, text));
1447
1442
  });
1448
1443
  ws.on("close", (code, reason) => {
1449
1444
  console.log(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-project-manage-cli",
3
- "version": "5.0.19",
3
+ "version": "5.0.21",
4
4
  "description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
5
5
  "type": "module",
6
6
  "private": false,
@@ -29,7 +29,8 @@
29
29
  "node": ">=18"
30
30
  },
31
31
  "dependencies": {
32
- "@cursor/sdk": "~1.0.17",
32
+ "@cursor/sdk": "~1.0.18",
33
+ "@opc/shared": "workspace:*",
33
34
  "ws": "~8.18.0",
34
35
  "listpage-http": "~0.0.318",
35
36
  "commander": "~14.0.3",
@@ -37,4 +38,4 @@
37
38
  "minio": "~8.0.7",
38
39
  "dockerode": "~5.0.0"
39
40
  }
40
- }
41
+ }
@@ -3,6 +3,7 @@
3
3
  1. 如果回复的内容包含文档地址,不要把前缀也写出来,直接用文档名称即可。例如: 我已经把待处理的内容更新到了 `PRD.md` 中,请查阅。
4
4
  2. 回复的内容并非对你工作的总结,要尽可能简洁,不要长篇大论,如果同步的内容确实较多,你可以先写文档,然后指引用户去阅读你写的文档。
5
5
  3. 只要你有任何进展都想都可以调用下面的命令进行回复,你可以一直补充内容。
6
+ 4. 如果有需要指定相关人,必须在消息内容中@指定出来
6
7
 
7
8
  ## 执行回复的方法
8
9