ai-project-manage-cli 7.1.31 → 7.1.32

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/README.md CHANGED
@@ -58,5 +58,3 @@ apm daemon start -f
58
58
  使用全局 PM2(未安装时会自动执行 `npm install -g pm2`);同一时刻仅允许一个 connect。启动时会自动检测 CLI 更新并刷新守护配置。
59
59
 
60
60
  `apm pull` 会自动将平台登记的**项目文档**同步到 `.apm/project/<项目ID>/`(含 `manifest.json`)。Agent 修改该目录下文件后,`apm connect` 处理完消息会自动推回平台。
61
-
62
- 详见仓库根目录 [docs/CLI.md](../../docs/CLI.md)。
package/dist/index.js CHANGED
@@ -216,10 +216,6 @@ var init_request_config = __esm({
216
216
  method: "PUT",
217
217
  path: "/cli/webide/terminals/meta"
218
218
  }),
219
- acquireSteelBrowser: defineEndpoint({
220
- method: "POST",
221
- path: "/cli/steel-browser/acquire"
222
- }),
223
219
  branchBaseline: defineEndpoint({
224
220
  method: "GET",
225
221
  path: "/cli/tasks/branch-baseline"
@@ -444,14 +440,14 @@ var init_minio = __esm({
444
440
  async deleteObjectsByPrefix(bucket, prefix) {
445
441
  const objectsStream = this.inner.listObjectsV2(bucket, prefix, true);
446
442
  const keys = [];
447
- await new Promise((resolve8, reject) => {
443
+ await new Promise((resolve9, reject) => {
448
444
  objectsStream.on("data", (obj) => {
449
445
  if (obj.name) {
450
446
  keys.push(obj.name);
451
447
  }
452
448
  });
453
449
  objectsStream.on("error", reject);
454
- objectsStream.on("end", resolve8);
450
+ objectsStream.on("end", resolve9);
455
451
  });
456
452
  const chunkSize = 500;
457
453
  for (let i = 0; i < keys.length; i += chunkSize) {
@@ -4608,7 +4604,8 @@ function buildConnectPm2Ecosystem(options) {
4608
4604
  max_restarts: 10,
4609
4605
  restart_delay: 3e3,
4610
4606
  exp_backoff_restart_delay: 1e3,
4611
- max_memory_restart: "2G",
4607
+ // WebIDE Worker 与主进程同 RSS;8G 机约可到 ~6G,避免把整机打满
4608
+ max_memory_restart: "6G",
4612
4609
  kill_timeout: 5e3,
4613
4610
  shutdown_with_message: true,
4614
4611
  instances: 1,
@@ -5716,7 +5713,7 @@ function buildSftpConnectOptions(settings) {
5716
5713
  };
5717
5714
  }
5718
5715
  async function sleep(ms) {
5719
- await new Promise((resolve8) => setTimeout(resolve8, ms));
5716
+ await new Promise((resolve9) => setTimeout(resolve9, ms));
5720
5717
  }
5721
5718
  async function uploadZipWithRetry(settings, localZip, remoteZipPath) {
5722
5719
  let lastError;
@@ -5760,7 +5757,7 @@ async function ensureRemoteDir(sftp, dir) {
5760
5757
  }
5761
5758
  }
5762
5759
  function execCommand(client, command) {
5763
- return new Promise((resolve8, reject) => {
5760
+ return new Promise((resolve9, reject) => {
5764
5761
  client.exec(command, (err, stream) => {
5765
5762
  if (err) return reject(err);
5766
5763
  let stdout = "";
@@ -5770,7 +5767,7 @@ function execCommand(client, command) {
5770
5767
  reject(new Error(`\u8FDC\u7A0B\u547D\u4EE4\u5931\u8D25 (${code}): ${stderr || stdout}`));
5771
5768
  return;
5772
5769
  }
5773
- resolve8(stdout);
5770
+ resolve9(stdout);
5774
5771
  }).on("data", (data) => {
5775
5772
  stdout += data.toString();
5776
5773
  });
@@ -6245,8 +6242,8 @@ function springbootOutputIndicatesSuccess(action, combined) {
6245
6242
  async function connectSsh(config) {
6246
6243
  const client = new Client2();
6247
6244
  log(`\u8FDE\u63A5\u670D\u52A1\u5668 ${config.username}@${config.host}:${config.port}`);
6248
- await new Promise((resolve8, reject) => {
6249
- client.on("ready", () => resolve8()).on("error", (err) => reject(err)).connect({
6245
+ await new Promise((resolve9, reject) => {
6246
+ client.on("ready", () => resolve9()).on("error", (err) => reject(err)).connect({
6250
6247
  host: config.host,
6251
6248
  port: config.port,
6252
6249
  username: config.username,
@@ -6314,7 +6311,7 @@ async function runRemoteCommand(client, command, options) {
6314
6311
  const check = options?.check ?? true;
6315
6312
  const stream = options?.stream ?? false;
6316
6313
  const label = options?.label ?? "\u8FDC\u7A0B\u547D\u4EE4";
6317
- return new Promise((resolve8, reject) => {
6314
+ return new Promise((resolve9, reject) => {
6318
6315
  client.exec(command, (err, execStream) => {
6319
6316
  if (err) {
6320
6317
  reject(err);
@@ -6344,7 +6341,7 @@ ${errText}`.trim();
6344
6341
  \u8F93\u51FA: ${combined}` : "")
6345
6342
  );
6346
6343
  }
6347
- resolve8({ exitCode: code, out: out.trim(), err: errText.trim() });
6344
+ resolve9({ exitCode: code, out: out.trim(), err: errText.trim() });
6348
6345
  });
6349
6346
  });
6350
6347
  });
@@ -7245,16 +7242,101 @@ function cleanSessionWorkspaceCache(sessionId, workdir) {
7245
7242
  }
7246
7243
 
7247
7244
  // src/commands/clean-webide-cache.ts
7248
- import { existsSync as existsSync23, rmSync as rmSync5 } from "node:fs";
7249
- import { resolve as resolve6 } from "node:path";
7250
- function cleanWebIdeWorkspaceCache(taskId, workdir) {
7245
+ import { existsSync as existsSync24, rmSync as rmSync5 } from "node:fs";
7246
+ import { resolve as resolve7 } from "node:path";
7247
+ import { JsonlLocalAgentStore } from "@cursor/sdk";
7248
+
7249
+ // src/commands/connect/webide-agent-registry.ts
7250
+ import { existsSync as existsSync23, mkdirSync as mkdirSync10, readFileSync as readFileSync17, writeFileSync as writeFileSync15 } from "node:fs";
7251
+ import { dirname as dirname7, resolve as resolve6 } from "node:path";
7252
+ function registryPath(workdir, taskId) {
7253
+ return resolve6(workdir, ".apm", "webide", taskId, "cursor-agent.json");
7254
+ }
7255
+ function readRegistry(path19) {
7256
+ if (!existsSync23(path19)) {
7257
+ return {};
7258
+ }
7259
+ try {
7260
+ const parsed = JSON.parse(readFileSync17(path19, "utf8"));
7261
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
7262
+ const agentId = parsed.agentId;
7263
+ if (typeof agentId === "string" && agentId.trim()) {
7264
+ return { agentId: agentId.trim() };
7265
+ }
7266
+ }
7267
+ } catch {
7268
+ }
7269
+ return {};
7270
+ }
7271
+ function loadWebIdeAgentId(workdir, taskId) {
7272
+ return readRegistry(registryPath(workdir, taskId)).agentId;
7273
+ }
7274
+
7275
+ // src/commands/clean-webide-cache.ts
7276
+ function cursorAgentStoreDir(workdir) {
7277
+ return resolve7(workdir, ".apm", "cursor-agent-store");
7278
+ }
7279
+ async function purgeCursorAgentStoreForAgent(workdir, agentId) {
7280
+ const trimmedAgentId = agentId.trim();
7281
+ const trimmedWorkdir = workdir.trim();
7282
+ if (!trimmedAgentId || !trimmedWorkdir) return false;
7283
+ const rootDir = cursorAgentStoreDir(trimmedWorkdir);
7284
+ if (!existsSync24(rootDir)) return false;
7285
+ const store = new JsonlLocalAgentStore(rootDir);
7286
+ const runIds = [];
7287
+ let cursor;
7288
+ do {
7289
+ const page = await store.runs.list({
7290
+ filter: {
7291
+ agentIds: [trimmedAgentId],
7292
+ ...cursor ? { cursor } : {},
7293
+ limit: 200
7294
+ }
7295
+ });
7296
+ for (const run of page.items) {
7297
+ runIds.push(run.runId);
7298
+ }
7299
+ cursor = page.nextCursor;
7300
+ } while (cursor);
7301
+ if (runIds.length > 0) {
7302
+ await store.runEvents.delete({ filter: { runIds } });
7303
+ }
7304
+ await store.runs.delete({ filter: { agentIds: [trimmedAgentId] } });
7305
+ await store.checkpoints.delete({ filter: { agentIds: [trimmedAgentId] } });
7306
+ await store.agents.delete({ filter: { agentIds: [trimmedAgentId] } });
7307
+ return true;
7308
+ }
7309
+ async function cleanWebIdeWorkspaceCache(taskId, workdir) {
7251
7310
  const trimmedTaskId = taskId.trim();
7252
7311
  const trimmedWorkdir = workdir.trim();
7253
7312
  if (!trimmedTaskId || !trimmedWorkdir) {
7254
7313
  return;
7255
7314
  }
7256
- const dir = resolve6(trimmedWorkdir, ".apm", "webide", trimmedTaskId);
7257
- if (existsSync23(dir)) {
7315
+ const agentId = loadWebIdeAgentId(trimmedWorkdir, trimmedTaskId);
7316
+ if (agentId) {
7317
+ try {
7318
+ const purged = await purgeCursorAgentStoreForAgent(
7319
+ trimmedWorkdir,
7320
+ agentId
7321
+ );
7322
+ if (purged) {
7323
+ console.log(
7324
+ `[apm] \u5DF2\u6E05\u7406 Cursor agent store agentId=${agentId} taskId=${trimmedTaskId}`
7325
+ );
7326
+ }
7327
+ } catch (err) {
7328
+ console.warn(
7329
+ `[apm] \u6E05\u7406 Cursor agent store \u5931\u8D25 agentId=${agentId}:`,
7330
+ err instanceof Error ? err.message : err
7331
+ );
7332
+ }
7333
+ } else {
7334
+ console.log(
7335
+ `[apm] \u65E0 WebIDE agentId\uFF0C\u8DF3\u8FC7 cursor-agent-store taskId=${trimmedTaskId}`
7336
+ );
7337
+ }
7338
+ const dir = resolve7(trimmedWorkdir, ".apm", "webide", trimmedTaskId);
7339
+ if (existsSync24(dir)) {
7258
7340
  rmSync5(dir, { recursive: true, force: true });
7259
7341
  console.log(`[apm] \u5DF2\u6E05\u7406 WebIDE \u7F13\u5B58 ${dir}`);
7260
7342
  } else {
@@ -7636,17 +7718,17 @@ ${JSON.stringify(event, null, 2)}
7636
7718
  }
7637
7719
 
7638
7720
  // src/commands/connect/agent-session-registry.ts
7639
- import { existsSync as existsSync24, mkdirSync as mkdirSync10, readFileSync as readFileSync17, writeFileSync as writeFileSync15 } from "node:fs";
7640
- import { dirname as dirname7, resolve as resolve7 } from "node:path";
7641
- function registryPath(workdir, sessionId) {
7642
- return resolve7(workdir, ".apm", "sessions", sessionId, "cursor-agents.json");
7721
+ import { existsSync as existsSync25, mkdirSync as mkdirSync11, readFileSync as readFileSync18, writeFileSync as writeFileSync16 } from "node:fs";
7722
+ import { dirname as dirname8, resolve as resolve8 } from "node:path";
7723
+ function registryPath2(workdir, sessionId) {
7724
+ return resolve8(workdir, ".apm", "sessions", sessionId, "cursor-agents.json");
7643
7725
  }
7644
- function readRegistry(path19) {
7645
- if (!existsSync24(path19)) {
7726
+ function readRegistry2(path19) {
7727
+ if (!existsSync25(path19)) {
7646
7728
  return {};
7647
7729
  }
7648
7730
  try {
7649
- const parsed = JSON.parse(readFileSync17(path19, "utf8"));
7731
+ const parsed = JSON.parse(readFileSync18(path19, "utf8"));
7650
7732
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
7651
7733
  const result = {};
7652
7734
  for (const [key, value] of Object.entries(
@@ -7663,23 +7745,23 @@ function readRegistry(path19) {
7663
7745
  return {};
7664
7746
  }
7665
7747
  function writeRegistry(path19, registry) {
7666
- mkdirSync10(dirname7(path19), { recursive: true });
7667
- writeFileSync15(path19, `${JSON.stringify(registry, null, 2)}
7748
+ mkdirSync11(dirname8(path19), { recursive: true });
7749
+ writeFileSync16(path19, `${JSON.stringify(registry, null, 2)}
7668
7750
  `, "utf8");
7669
7751
  }
7670
7752
  function loadSessionAgentId(workdir, sessionId, user) {
7671
- const registry = readRegistry(registryPath(workdir, sessionId));
7753
+ const registry = readRegistry2(registryPath2(workdir, sessionId));
7672
7754
  return registry[user];
7673
7755
  }
7674
7756
  function saveSessionAgentId(workdir, sessionId, user, agentId) {
7675
- const path19 = registryPath(workdir, sessionId);
7676
- const registry = readRegistry(path19);
7757
+ const path19 = registryPath2(workdir, sessionId);
7758
+ const registry = readRegistry2(path19);
7677
7759
  registry[user] = agentId;
7678
7760
  writeRegistry(path19, registry);
7679
7761
  }
7680
7762
  function clearSessionAgentId(workdir, sessionId, user) {
7681
- const path19 = registryPath(workdir, sessionId);
7682
- const registry = readRegistry(path19);
7763
+ const path19 = registryPath2(workdir, sessionId);
7764
+ const registry = readRegistry2(path19);
7683
7765
  if (!(user in registry)) {
7684
7766
  return;
7685
7767
  }
@@ -8267,13 +8349,13 @@ ${WORKSPACE_BOUNDARY_HINT}`;
8267
8349
  }
8268
8350
 
8269
8351
  // src/commands/connect/local-agent-store.ts
8270
- import { mkdirSync as mkdirSync11 } from "node:fs";
8352
+ import { mkdirSync as mkdirSync12 } from "node:fs";
8271
8353
  import { join as join19 } from "node:path";
8272
- import { JsonlLocalAgentStore } from "@cursor/sdk";
8354
+ import { JsonlLocalAgentStore as JsonlLocalAgentStore2 } from "@cursor/sdk";
8273
8355
  function createWorkspaceLocalAgentStore(workdir) {
8274
8356
  const rootDir = join19(workdir, ".apm", "cursor-agent-store");
8275
- mkdirSync11(rootDir, { recursive: true });
8276
- return new JsonlLocalAgentStore(rootDir);
8357
+ mkdirSync12(rootDir, { recursive: true });
8358
+ return new JsonlLocalAgentStore2(rootDir);
8277
8359
  }
8278
8360
 
8279
8361
  // src/commands/connect/cursor-agent.ts
@@ -8553,7 +8635,7 @@ async function ensureMessageHasReply(cfg, sessionId, messageId, fallback) {
8553
8635
  }
8554
8636
 
8555
8637
  // src/commands/connect/cli-version-sync.ts
8556
- import { existsSync as existsSync25, readFileSync as readFileSync18, writeFileSync as writeFileSync16 } from "fs";
8638
+ import { existsSync as existsSync26, readFileSync as readFileSync19, writeFileSync as writeFileSync17 } from "fs";
8557
8639
  import { join as join20 } from "path";
8558
8640
  var CLI_VERSION_FILE = ".cli-version.json";
8559
8641
  function manifestPath2(apmDir) {
@@ -8561,12 +8643,12 @@ function manifestPath2(apmDir) {
8561
8643
  }
8562
8644
  function loadManifest4(apmDir) {
8563
8645
  const path19 = toFsPath(manifestPath2(apmDir));
8564
- if (!existsSync25(path19)) {
8646
+ if (!existsSync26(path19)) {
8565
8647
  return null;
8566
8648
  }
8567
8649
  try {
8568
8650
  const parsed = JSON.parse(
8569
- readFileSync18(path19, "utf8")
8651
+ readFileSync19(path19, "utf8")
8570
8652
  );
8571
8653
  if (parsed?.version === 1 && typeof parsed.cliVersion === "string" && parsed.cliVersion.trim()) {
8572
8654
  return parsed;
@@ -8577,7 +8659,7 @@ function loadManifest4(apmDir) {
8577
8659
  }
8578
8660
  function saveManifest4(apmDir, cliVersion) {
8579
8661
  const manifest = { version: 1, cliVersion };
8580
- writeFileSync16(
8662
+ writeFileSync17(
8581
8663
  toFsPath(manifestPath2(apmDir)),
8582
8664
  `${JSON.stringify(manifest, null, 2)}
8583
8665
  `,
@@ -8631,7 +8713,7 @@ function createRunSlotPool(maxConcurrent = DEFAULT_MAX_CONCURRENT, options = {})
8631
8713
  );
8632
8714
  }
8633
8715
  const queuedAt = Date.now();
8634
- return new Promise((resolve8) => {
8716
+ return new Promise((resolve9) => {
8635
8717
  waiters.push(() => {
8636
8718
  active += 1;
8637
8719
  const waitedMs = Date.now() - queuedAt;
@@ -8642,7 +8724,7 @@ function createRunSlotPool(maxConcurrent = DEFAULT_MAX_CONCURRENT, options = {})
8642
8724
  )}s\uFF0C\u5F53\u524D\u5360\u7528 ${active}/${maxConcurrent}`
8643
8725
  );
8644
8726
  }
8645
- resolve8();
8727
+ resolve9();
8646
8728
  });
8647
8729
  });
8648
8730
  };
@@ -8662,21 +8744,46 @@ function createRunSlotPool(maxConcurrent = DEFAULT_MAX_CONCURRENT, options = {})
8662
8744
  // src/commands/connect/webide-worker-pool.ts
8663
8745
  init_webide_terminal_registry();
8664
8746
  import { Worker } from "node:worker_threads";
8747
+ import { totalmem } from "node:os";
8665
8748
  import { fileURLToPath as fileURLToPath3 } from "node:url";
8666
- import { dirname as dirname8, join as join21 } from "node:path";
8749
+ import { dirname as dirname9, join as join21 } from "node:path";
8667
8750
  var workerFile = join21(
8668
- dirname8(fileURLToPath3(import.meta.url)),
8751
+ dirname9(fileURLToPath3(import.meta.url)),
8669
8752
  "webide-message-worker.js"
8670
8753
  );
8754
+ var SYSTEM_RESERVE_MB = 3072;
8755
+ var MIN_WEBIDE_WORKER_HEAP_MB = 1536;
8756
+ var MAX_WEBIDE_WORKER_HEAP_MB = 8192;
8757
+ function defaultWebIdeWorkerHeapMb(totalMemoryBytes = totalmem()) {
8758
+ const totalMb = Math.floor(totalMemoryBytes / (1024 * 1024));
8759
+ return Math.min(
8760
+ MAX_WEBIDE_WORKER_HEAP_MB,
8761
+ Math.max(MIN_WEBIDE_WORKER_HEAP_MB, totalMb - SYSTEM_RESERVE_MB)
8762
+ );
8763
+ }
8764
+ function resolveWebIdeWorkerHeapMb(env = process.env, totalMemoryBytes = totalmem()) {
8765
+ const fallback = defaultWebIdeWorkerHeapMb(totalMemoryBytes);
8766
+ const raw = env.APM_WEBIDE_WORKER_HEAP_MB?.trim();
8767
+ if (!raw) return fallback;
8768
+ const n = Number.parseInt(raw, 10);
8769
+ if (!Number.isFinite(n)) return fallback;
8770
+ return Math.min(16384, Math.max(MIN_WEBIDE_WORKER_HEAP_MB, n));
8771
+ }
8671
8772
  function spawnWebIdeMessageWorker(cfg, msg) {
8773
+ const maxOldGenerationSizeMb = resolveWebIdeWorkerHeapMb();
8672
8774
  const worker = new Worker(workerFile, {
8673
- workerData: { cfg, msg }
8775
+ workerData: { cfg, msg },
8776
+ // worker_threads 不支持 execArgv 里的 --max-old-space-size,必须用 resourceLimits
8777
+ resourceLimits: { maxOldGenerationSizeMb }
8674
8778
  });
8779
+ console.log(
8780
+ `[apm] webide worker spawn messageId=${msg.messageId} heapMb=${maxOldGenerationSizeMb}`
8781
+ );
8675
8782
  const registry = getWebIdeTerminalRegistry(cfg);
8676
8783
  let settled = false;
8677
8784
  let resolveDone;
8678
- const done = new Promise((resolve8) => {
8679
- resolveDone = resolve8;
8785
+ const done = new Promise((resolve9) => {
8786
+ resolveDone = resolve9;
8680
8787
  });
8681
8788
  const failIfStillQueued = async (reason) => {
8682
8789
  if (settled) return;
@@ -8764,10 +8871,12 @@ function spawnWebIdeMessageWorker(cfg, msg) {
8764
8871
  }
8765
8872
  });
8766
8873
  worker.on("error", (err) => {
8874
+ const detail = err instanceof Error ? err.message : String(err);
8767
8875
  console.error("[apm] webide worker crashed:", err);
8768
- void failIfStillQueued(
8769
- `WebIDE worker \u5D29\u6E83: ${err instanceof Error ? err.message : String(err)}`
8770
- ).finally(() => resolveDone());
8876
+ const oomHint = /memory limit|heap out of memory/i.test(detail) ? `\uFF08\u5F53\u524D\u5806\u4E0A\u9650 ${maxOldGenerationSizeMb}MB\uFF1B8G \u53CA\u4EE5\u4E0B\u673A\u5668\u8C03\u9AD8 APM_WEBIDE_WORKER_HEAP_MB \u6613\u89E6\u53D1\u6574\u673A OOM\uFF0C\u5EFA\u8BAE\u52A0\u5185\u5B58/swap \u6216\u7F29\u77ED\u5355\u6B21\u4EFB\u52A1\uFF09` : "";
8877
+ void failIfStillQueued(`WebIDE worker \u5D29\u6E83: ${detail}${oomHint}`).finally(
8878
+ () => resolveDone()
8879
+ );
8771
8880
  });
8772
8881
  worker.on("exit", (code) => {
8773
8882
  if (!settled && code !== 0) {
@@ -8948,11 +9057,11 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
8948
9057
  }
8949
9058
  function interruptibleSleep(ms, signal) {
8950
9059
  if (signal.aborted) return Promise.resolve();
8951
- return new Promise((resolve8) => {
8952
- const timer = setTimeout(resolve8, ms);
9060
+ return new Promise((resolve9) => {
9061
+ const timer = setTimeout(resolve9, ms);
8953
9062
  const onAbort = () => {
8954
9063
  clearTimeout(timer);
8955
- resolve8();
9064
+ resolve9();
8956
9065
  };
8957
9066
  signal.addEventListener("abort", onAbort, { once: true });
8958
9067
  });
@@ -9053,7 +9162,7 @@ function attachWsHandlers(ws, ctx, onOpen) {
9053
9162
  msg2.taskId,
9054
9163
  "cache-cleanup"
9055
9164
  );
9056
- cleanWebIdeWorkspaceCache(msg2.taskId, workdir);
9165
+ await cleanWebIdeWorkspaceCache(msg2.taskId, workdir);
9057
9166
  })();
9058
9167
  activeTasks.add(task2);
9059
9168
  void task2.finally(() => {
@@ -9182,7 +9291,7 @@ function attachWsHandlers(ws, ctx, onOpen) {
9182
9291
  });
9183
9292
  }
9184
9293
  function connectOnce(url, ctx, connectionAbort, onConnected) {
9185
- return new Promise((resolve8, reject) => {
9294
+ return new Promise((resolve9, reject) => {
9186
9295
  const ws = new WebSocket(url);
9187
9296
  let stopHeartbeat;
9188
9297
  let settled = false;
@@ -9211,7 +9320,7 @@ function connectOnce(url, ctx, connectionAbort, onConnected) {
9211
9320
  finish(() => reject(new Error("shutdown")));
9212
9321
  return;
9213
9322
  }
9214
- finish(resolve8);
9323
+ finish(resolve9);
9215
9324
  });
9216
9325
  ws.on("error", (err) => {
9217
9326
  console.error("[apm] WebSocket \u9519\u8BEF:", err.message);
@@ -9706,7 +9815,7 @@ import path15 from "node:path";
9706
9815
  import Docker from "dockerode";
9707
9816
 
9708
9817
  // src/commands/deploy/internal/backend-deploy/dockerode-client/connection-options.ts
9709
- import { existsSync as existsSync26, readFileSync as readFileSync19 } from "node:fs";
9818
+ import { existsSync as existsSync27, readFileSync as readFileSync20 } from "node:fs";
9710
9819
  import path12 from "node:path";
9711
9820
  function asOptionalTlsBuffer(value) {
9712
9821
  if (typeof value !== "string") {
@@ -9718,8 +9827,8 @@ function asOptionalTlsBuffer(value) {
9718
9827
  if (normalized === "") {
9719
9828
  return void 0;
9720
9829
  }
9721
- if (existsSync26(normalized)) {
9722
- return readFileSync19(normalized);
9830
+ if (existsSync27(normalized)) {
9831
+ return readFileSync20(normalized);
9723
9832
  }
9724
9833
  const looksLikePath = /[\\/]/.test(normalized) || normalized.endsWith(".pem");
9725
9834
  if (looksLikePath) {
@@ -9844,17 +9953,17 @@ var DockerodeClient = class {
9844
9953
  await this.client.getImage(image).remove({ force: true });
9845
9954
  }
9846
9955
  async pullImage(image, auth) {
9847
- const stream = await new Promise((resolve8, reject) => {
9956
+ const stream = await new Promise((resolve9, reject) => {
9848
9957
  const pullOptions = auth ? { authconfig: auth } : void 0;
9849
9958
  this.client.pull(image, pullOptions, (err, output) => {
9850
9959
  if (err || !output) {
9851
9960
  reject(err ?? new Error("docker pull \u8FD4\u56DE\u7A7A\u8F93\u51FA"));
9852
9961
  return;
9853
9962
  }
9854
- resolve8(output);
9963
+ resolve9(output);
9855
9964
  });
9856
9965
  });
9857
- await new Promise((resolve8, reject) => {
9966
+ await new Promise((resolve9, reject) => {
9858
9967
  this.client.modem.followProgress(
9859
9968
  stream,
9860
9969
  (err) => {
@@ -9862,7 +9971,7 @@ var DockerodeClient = class {
9862
9971
  reject(err);
9863
9972
  return;
9864
9973
  }
9865
- resolve8();
9974
+ resolve9();
9866
9975
  },
9867
9976
  () => void 0
9868
9977
  );
@@ -9929,7 +10038,7 @@ var DockerodeClient = class {
9929
10038
  var createDockerodeClient = (config) => new DockerodeClient(config);
9930
10039
 
9931
10040
  // src/commands/deploy/internal/backend-deploy/dockerode-client/env.ts
9932
- import { existsSync as existsSync27, readFileSync as readFileSync20, statSync as statSync11 } from "node:fs";
10041
+ import { existsSync as existsSync28, readFileSync as readFileSync21, statSync as statSync11 } from "node:fs";
9933
10042
  import path13 from "node:path";
9934
10043
  function stripSurroundingQuotes(value) {
9935
10044
  const t = value.trim();
@@ -9946,10 +10055,10 @@ function loadEnvFromFile(envFilePath) {
9946
10055
  return {};
9947
10056
  }
9948
10057
  const targetPath = path13.resolve(envFilePath);
9949
- if (!existsSync27(targetPath) || !statSync11(targetPath).isFile()) {
10058
+ if (!existsSync28(targetPath) || !statSync11(targetPath).isFile()) {
9950
10059
  return {};
9951
10060
  }
9952
- const raw = readFileSync20(targetPath, "utf-8");
10061
+ const raw = readFileSync21(targetPath, "utf-8");
9953
10062
  const result = {};
9954
10063
  for (const line of raw.split(/\r?\n/)) {
9955
10064
  const normalized = line.trim();
@@ -10120,12 +10229,12 @@ function dockerPushImage(params, cwd) {
10120
10229
  }
10121
10230
 
10122
10231
  // src/commands/deploy/internal/backend-deploy/resolve-dockerfile.ts
10123
- import { existsSync as existsSync28 } from "node:fs";
10232
+ import { existsSync as existsSync29 } from "node:fs";
10124
10233
  import path14 from "node:path";
10125
10234
  function resolveDockerBuildPaths(cwd) {
10126
10235
  const dockerfilePath = path14.join(cwd, "Dockerfile");
10127
10236
  Logger.info(`\u67E5\u627EDockerfile\u6587\u4EF6\uFF0C\u8DEF\u5F84: ${dockerfilePath}`);
10128
- if (!existsSync28(dockerfilePath)) {
10237
+ if (!existsSync29(dockerfilePath)) {
10129
10238
  throw new Error(`Dockerfile \u4E0D\u5B58\u5728\uFF1A${dockerfilePath}`);
10130
10239
  }
10131
10240
  Logger.info("\u2713 Dockerfile \u5B58\u5728");
@@ -120,10 +120,6 @@ var requestConfig = {
120
120
  method: "PUT",
121
121
  path: "/cli/webide/terminals/meta"
122
122
  }),
123
- acquireSteelBrowser: defineEndpoint({
124
- method: "POST",
125
- path: "/cli/steel-browser/acquire"
126
- }),
127
123
  branchBaseline: defineEndpoint({
128
124
  method: "GET",
129
125
  path: "/cli/tasks/branch-baseline"
@@ -2555,6 +2551,81 @@ function clearWebIdeAgentId(workdir, taskId) {
2555
2551
  writeRegistry2(path, {});
2556
2552
  }
2557
2553
 
2554
+ // src/commands/clean-webide-cache.ts
2555
+ import { existsSync as existsSync5, rmSync } from "node:fs";
2556
+ import { resolve as resolve6 } from "node:path";
2557
+ import { JsonlLocalAgentStore as JsonlLocalAgentStore2 } from "@cursor/sdk";
2558
+ function cursorAgentStoreDir(workdir) {
2559
+ return resolve6(workdir, ".apm", "cursor-agent-store");
2560
+ }
2561
+ async function purgeCursorAgentStoreForAgent(workdir, agentId) {
2562
+ const trimmedAgentId = agentId.trim();
2563
+ const trimmedWorkdir = workdir.trim();
2564
+ if (!trimmedAgentId || !trimmedWorkdir) return false;
2565
+ const rootDir = cursorAgentStoreDir(trimmedWorkdir);
2566
+ if (!existsSync5(rootDir)) return false;
2567
+ const store = new JsonlLocalAgentStore2(rootDir);
2568
+ const runIds = [];
2569
+ let cursor;
2570
+ do {
2571
+ const page = await store.runs.list({
2572
+ filter: {
2573
+ agentIds: [trimmedAgentId],
2574
+ ...cursor ? { cursor } : {},
2575
+ limit: 200
2576
+ }
2577
+ });
2578
+ for (const run of page.items) {
2579
+ runIds.push(run.runId);
2580
+ }
2581
+ cursor = page.nextCursor;
2582
+ } while (cursor);
2583
+ if (runIds.length > 0) {
2584
+ await store.runEvents.delete({ filter: { runIds } });
2585
+ }
2586
+ await store.runs.delete({ filter: { agentIds: [trimmedAgentId] } });
2587
+ await store.checkpoints.delete({ filter: { agentIds: [trimmedAgentId] } });
2588
+ await store.agents.delete({ filter: { agentIds: [trimmedAgentId] } });
2589
+ return true;
2590
+ }
2591
+ async function cleanWebIdeWorkspaceCache(taskId, workdir) {
2592
+ const trimmedTaskId = taskId.trim();
2593
+ const trimmedWorkdir = workdir.trim();
2594
+ if (!trimmedTaskId || !trimmedWorkdir) {
2595
+ return;
2596
+ }
2597
+ const agentId = loadWebIdeAgentId(trimmedWorkdir, trimmedTaskId);
2598
+ if (agentId) {
2599
+ try {
2600
+ const purged = await purgeCursorAgentStoreForAgent(
2601
+ trimmedWorkdir,
2602
+ agentId
2603
+ );
2604
+ if (purged) {
2605
+ console.log(
2606
+ `[apm] \u5DF2\u6E05\u7406 Cursor agent store agentId=${agentId} taskId=${trimmedTaskId}`
2607
+ );
2608
+ }
2609
+ } catch (err) {
2610
+ console.warn(
2611
+ `[apm] \u6E05\u7406 Cursor agent store \u5931\u8D25 agentId=${agentId}:`,
2612
+ err instanceof Error ? err.message : err
2613
+ );
2614
+ }
2615
+ } else {
2616
+ console.log(
2617
+ `[apm] \u65E0 WebIDE agentId\uFF0C\u8DF3\u8FC7 cursor-agent-store taskId=${trimmedTaskId}`
2618
+ );
2619
+ }
2620
+ const dir = resolve6(trimmedWorkdir, ".apm", "webide", trimmedTaskId);
2621
+ if (existsSync5(dir)) {
2622
+ rmSync(dir, { recursive: true, force: true });
2623
+ console.log(`[apm] \u5DF2\u6E05\u7406 WebIDE \u7F13\u5B58 ${dir}`);
2624
+ } else {
2625
+ console.log(`[apm] WebIDE \u7F13\u5B58\u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7 ${dir}`);
2626
+ }
2627
+ }
2628
+
2558
2629
  // src/commands/connect/webide-ask-question.ts
2559
2630
  import { setTimeout as delay } from "node:timers/promises";
2560
2631
  var POLL_INTERVAL_MS = 2e3;
@@ -2669,14 +2740,14 @@ var MinioClient = class {
2669
2740
  async deleteObjectsByPrefix(bucket, prefix) {
2670
2741
  const objectsStream = this.inner.listObjectsV2(bucket, prefix, true);
2671
2742
  const keys = [];
2672
- await new Promise((resolve6, reject) => {
2743
+ await new Promise((resolve7, reject) => {
2673
2744
  objectsStream.on("data", (obj) => {
2674
2745
  if (obj.name) {
2675
2746
  keys.push(obj.name);
2676
2747
  }
2677
2748
  });
2678
2749
  objectsStream.on("error", reject);
2679
- objectsStream.on("end", resolve6);
2750
+ objectsStream.on("end", resolve7);
2680
2751
  });
2681
2752
  const chunkSize = 500;
2682
2753
  for (let i = 0; i < keys.length; i += chunkSize) {
@@ -2950,10 +3021,10 @@ ${diagnostic ?? ""}
2950
3021
 
2951
3022
  // src/project-documents-sync.ts
2952
3023
  import {
2953
- existsSync as existsSync5,
3024
+ existsSync as existsSync6,
2954
3025
  readdirSync as readdirSync3,
2955
3026
  readFileSync as readFileSync6,
2956
- rmSync,
3027
+ rmSync as rmSync2,
2957
3028
  writeFileSync as writeFileSync7
2958
3029
  } from "fs";
2959
3030
  import { createHash } from "crypto";
@@ -2999,7 +3070,7 @@ function readLocalManifest(apmRoot, projectId) {
2999
3070
  projectDocumentsDir(apmRoot, projectId),
3000
3071
  MANIFEST_FILE
3001
3072
  );
3002
- if (!existsSync5(manifestPath3)) {
3073
+ if (!existsSync6(manifestPath3)) {
3003
3074
  return null;
3004
3075
  }
3005
3076
  try {
@@ -3012,7 +3083,7 @@ function readLocalManifest(apmRoot, projectId) {
3012
3083
  }
3013
3084
  function listLocalDocumentPaths(apmRoot, projectId) {
3014
3085
  const root = projectDocumentsDir(apmRoot, projectId);
3015
- if (!existsSync5(root)) {
3086
+ if (!existsSync6(root)) {
3016
3087
  return [];
3017
3088
  }
3018
3089
  const paths = [];
@@ -3146,8 +3217,8 @@ ${diagnostic ?? ""}`);
3146
3217
  const absPath = toFsPath(
3147
3218
  projectDocumentLocalPath(targetApmDir, projectId, path)
3148
3219
  );
3149
- if (existsSync5(absPath)) {
3150
- rmSync(absPath, { force: true });
3220
+ if (existsSync6(absPath)) {
3221
+ rmSync2(absPath, { force: true });
3151
3222
  deleted += 1;
3152
3223
  }
3153
3224
  }
@@ -3513,17 +3584,17 @@ function readCliVersion() {
3513
3584
  }
3514
3585
 
3515
3586
  // src/commands/update-skills.ts
3516
- import { existsSync as existsSync7, mkdirSync as mkdirSync8, statSync as statSync4 } from "fs";
3587
+ import { existsSync as existsSync8, mkdirSync as mkdirSync8, statSync as statSync4 } from "fs";
3517
3588
  import { join as join10 } from "path";
3518
3589
 
3519
3590
  // src/skills-sync.ts
3520
3591
  import {
3521
3592
  copyFileSync as copyFileSync2,
3522
3593
  cpSync,
3523
- existsSync as existsSync6,
3594
+ existsSync as existsSync7,
3524
3595
  mkdirSync as mkdirSync7,
3525
3596
  readdirSync as readdirSync4,
3526
- rmSync as rmSync2,
3597
+ rmSync as rmSync3,
3527
3598
  statSync as statSync3,
3528
3599
  writeFileSync as writeFileSync9
3529
3600
  } from "fs";
@@ -3537,20 +3608,20 @@ function sanitizeSkillDirName(name) {
3537
3608
  return trimmed.replace(/[/\\:*?"<>|]/g, "_");
3538
3609
  }
3539
3610
  function listBaseSkillDirNames() {
3540
- if (!existsSync6(BASE_SKILLS_TEMPLATE_DIR)) return [];
3611
+ if (!existsSync7(BASE_SKILLS_TEMPLATE_DIR)) return [];
3541
3612
  return readdirSync4(BASE_SKILLS_TEMPLATE_DIR).filter((name) => {
3542
3613
  const path = join9(BASE_SKILLS_TEMPLATE_DIR, name);
3543
3614
  return statSync3(path).isDirectory();
3544
3615
  });
3545
3616
  }
3546
3617
  function syncAgentsGuide(apmDir) {
3547
- if (!existsSync6(AGENTS_TEMPLATE_PATH)) return false;
3618
+ if (!existsSync7(AGENTS_TEMPLATE_PATH)) return false;
3548
3619
  mkdirSync7(apmDir, { recursive: true });
3549
3620
  copyFileSync2(AGENTS_TEMPLATE_PATH, join9(apmDir, "AGENTS.md"));
3550
3621
  return true;
3551
3622
  }
3552
3623
  function listBaseRuleFileNames() {
3553
- if (!existsSync6(BASE_RULES_TEMPLATE_DIR)) return [];
3624
+ if (!existsSync7(BASE_RULES_TEMPLATE_DIR)) return [];
3554
3625
  return readdirSync4(BASE_RULES_TEMPLATE_DIR).filter((name) => {
3555
3626
  const path = join9(BASE_RULES_TEMPLATE_DIR, name);
3556
3627
  return statSync3(path).isFile();
@@ -3594,13 +3665,13 @@ function syncSupplementarySkills(skillsDir, list) {
3594
3665
  written.push(dirName);
3595
3666
  }
3596
3667
  const removed = [];
3597
- if (!existsSync6(skillsDir)) return { written, skipped, removed };
3668
+ if (!existsSync7(skillsDir)) return { written, skipped, removed };
3598
3669
  for (const entry of readdirSync4(skillsDir)) {
3599
3670
  const full = join9(skillsDir, entry);
3600
3671
  if (!statSync3(full).isDirectory()) continue;
3601
3672
  if (baseNames.has(entry)) continue;
3602
3673
  if (apiDirNames.has(entry)) continue;
3603
- rmSync2(full, { recursive: true, force: true });
3674
+ rmSync3(full, { recursive: true, force: true });
3604
3675
  removed.push(entry);
3605
3676
  }
3606
3677
  return { written, skipped, removed };
@@ -3610,7 +3681,7 @@ function syncSupplementarySkills(skillsDir, list) {
3610
3681
  async function syncWorkspaceSkills(cfg, workdir) {
3611
3682
  const apmDir = workspaceApmDir(workdir);
3612
3683
  const fsApmDir = toFsPath(apmDir);
3613
- if (!existsSync7(fsApmDir)) {
3684
+ if (!existsSync8(fsApmDir)) {
3614
3685
  throw new Error("[apm] \u672A\u627E\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5148\u6267\u884C apm init");
3615
3686
  }
3616
3687
  const apmStat = statSync4(fsApmDir);
@@ -3654,7 +3725,7 @@ async function syncWorkspaceSkills(cfg, workdir) {
3654
3725
  }
3655
3726
 
3656
3727
  // src/commands/sync-session-attachments.ts
3657
- import { existsSync as existsSync8, readFileSync as readFileSync9, writeFileSync as writeFileSync10 } from "fs";
3728
+ import { existsSync as existsSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync10 } from "fs";
3658
3729
  import { join as join11 } from "path";
3659
3730
  var MANIFEST_FILE2 = ".sync-manifest.json";
3660
3731
  async function downloadAttachment(cfg, attachmentId) {
@@ -3672,7 +3743,7 @@ async function downloadAttachment(cfg, attachmentId) {
3672
3743
  }
3673
3744
  function loadManifest(dir) {
3674
3745
  const path = join11(dir, MANIFEST_FILE2);
3675
- if (!existsSync8(path)) {
3746
+ if (!existsSync9(path)) {
3676
3747
  return { version: 1, attachments: {} };
3677
3748
  }
3678
3749
  try {
@@ -3695,7 +3766,7 @@ function saveManifest(dir, manifest) {
3695
3766
  );
3696
3767
  }
3697
3768
  function isAttachmentUpToDate(entry, item, dest) {
3698
- if (!entry || !existsSync8(dest)) return false;
3769
+ if (!entry || !existsSync9(dest)) return false;
3699
3770
  if (entry.name !== item.name) return false;
3700
3771
  const createdAt = item.createdAt ?? "";
3701
3772
  return entry.createdAt === createdAt;
@@ -3741,7 +3812,7 @@ async function syncWebIdeAttachments(cfg, taskId, workdir, attachments) {
3741
3812
  }
3742
3813
 
3743
3814
  // src/commands/connect/cli-version-sync.ts
3744
- import { existsSync as existsSync9, readFileSync as readFileSync10, writeFileSync as writeFileSync11 } from "fs";
3815
+ import { existsSync as existsSync10, readFileSync as readFileSync10, writeFileSync as writeFileSync11 } from "fs";
3745
3816
  import { join as join12 } from "path";
3746
3817
  var CLI_VERSION_FILE = ".cli-version.json";
3747
3818
  function manifestPath2(apmDir) {
@@ -3749,7 +3820,7 @@ function manifestPath2(apmDir) {
3749
3820
  }
3750
3821
  function loadManifest2(apmDir) {
3751
3822
  const path = toFsPath(manifestPath2(apmDir));
3752
- if (!existsSync9(path)) {
3823
+ if (!existsSync10(path)) {
3753
3824
  return null;
3754
3825
  }
3755
3826
  try {
@@ -4192,6 +4263,16 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
4192
4263
  console.log(
4193
4264
  `[apm] webide-message \u5B8C\u6210 action=${msg.action} messageId=${messageId} agentId=${outcome.agentId}`
4194
4265
  );
4266
+ if (msg.action === "accept") {
4267
+ try {
4268
+ await cleanWebIdeWorkspaceCache(taskId, workdir);
4269
+ } catch (err) {
4270
+ console.warn(
4271
+ "[apm] \u5408\u5E76 PR \u540E\u6E05\u7406 WebIDE agent \u7F13\u5B58\u5931\u8D25:",
4272
+ err instanceof Error ? err.message : err
4273
+ );
4274
+ }
4275
+ }
4195
4276
  } catch (err) {
4196
4277
  const detail = err instanceof Error ? err.message : String(err);
4197
4278
  console.error(`[apm] webide-message \u5931\u8D25: ${detail}`);
@@ -4225,12 +4306,12 @@ function installTerminalRpcListener() {
4225
4306
  }
4226
4307
  function call(op, args) {
4227
4308
  const id = nextId++;
4228
- return new Promise((resolve6, reject) => {
4309
+ return new Promise((resolve7, reject) => {
4229
4310
  if (!parentPort) {
4230
4311
  reject(new Error("parentPort \u4E0D\u53EF\u7528"));
4231
4312
  return;
4232
4313
  }
4233
- pending.set(id, { resolve: resolve6, reject });
4314
+ pending.set(id, { resolve: resolve7, reject });
4234
4315
  parentPort.postMessage({
4235
4316
  type: "terminal-rpc",
4236
4317
  id,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-project-manage-cli",
3
- "version": "7.1.31",
3
+ "version": "7.1.32",
4
4
  "description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
5
5
  "type": "module",
6
6
  "private": false,