ai-project-manage-cli 7.1.31 → 7.1.33

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,118 @@ 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 raw = parsed;
7263
+ const state = {};
7264
+ if (typeof raw.agentId === "string" && raw.agentId.trim()) {
7265
+ state.agentId = raw.agentId.trim();
7266
+ }
7267
+ if (typeof raw.taskId === "string" && raw.taskId.trim()) {
7268
+ state.taskId = raw.taskId.trim();
7269
+ }
7270
+ if (typeof raw.messageId === "string" && raw.messageId.trim()) {
7271
+ state.messageId = raw.messageId.trim();
7272
+ }
7273
+ if (typeof raw.action === "string" && raw.action.trim()) {
7274
+ state.action = raw.action.trim();
7275
+ }
7276
+ if (raw.status === "TYPING" || raw.status === "SUCCESS" || raw.status === "FAILED" || raw.status === "CANCELLED") {
7277
+ state.status = raw.status;
7278
+ }
7279
+ if (typeof raw.updatedAt === "string" && raw.updatedAt.trim()) {
7280
+ state.updatedAt = raw.updatedAt.trim();
7281
+ }
7282
+ return state;
7283
+ }
7284
+ } catch {
7285
+ }
7286
+ return {};
7287
+ }
7288
+ function loadWebIdeAgentId(workdir, taskId) {
7289
+ return readRegistry(registryPath(workdir, taskId)).agentId;
7290
+ }
7291
+
7292
+ // src/commands/clean-webide-cache.ts
7293
+ function cursorAgentStoreDir(workdir) {
7294
+ return resolve7(workdir, ".apm", "cursor-agent-store");
7295
+ }
7296
+ async function purgeCursorAgentStoreForAgent(workdir, agentId) {
7297
+ const trimmedAgentId = agentId.trim();
7298
+ const trimmedWorkdir = workdir.trim();
7299
+ if (!trimmedAgentId || !trimmedWorkdir) return false;
7300
+ const rootDir = cursorAgentStoreDir(trimmedWorkdir);
7301
+ if (!existsSync24(rootDir)) return false;
7302
+ const store = new JsonlLocalAgentStore(rootDir);
7303
+ const runIds = [];
7304
+ let cursor;
7305
+ do {
7306
+ const page = await store.runs.list({
7307
+ filter: {
7308
+ agentIds: [trimmedAgentId],
7309
+ ...cursor ? { cursor } : {},
7310
+ limit: 200
7311
+ }
7312
+ });
7313
+ for (const run of page.items) {
7314
+ runIds.push(run.runId);
7315
+ }
7316
+ cursor = page.nextCursor;
7317
+ } while (cursor);
7318
+ if (runIds.length > 0) {
7319
+ await store.runEvents.delete({ filter: { runIds } });
7320
+ }
7321
+ await store.runs.delete({ filter: { agentIds: [trimmedAgentId] } });
7322
+ await store.checkpoints.delete({ filter: { agentIds: [trimmedAgentId] } });
7323
+ await store.agents.delete({ filter: { agentIds: [trimmedAgentId] } });
7324
+ return true;
7325
+ }
7326
+ async function cleanWebIdeWorkspaceCache(taskId, workdir) {
7251
7327
  const trimmedTaskId = taskId.trim();
7252
7328
  const trimmedWorkdir = workdir.trim();
7253
7329
  if (!trimmedTaskId || !trimmedWorkdir) {
7254
7330
  return;
7255
7331
  }
7256
- const dir = resolve6(trimmedWorkdir, ".apm", "webide", trimmedTaskId);
7257
- if (existsSync23(dir)) {
7332
+ const agentId = loadWebIdeAgentId(trimmedWorkdir, trimmedTaskId);
7333
+ if (agentId) {
7334
+ try {
7335
+ const purged = await purgeCursorAgentStoreForAgent(
7336
+ trimmedWorkdir,
7337
+ agentId
7338
+ );
7339
+ if (purged) {
7340
+ console.log(
7341
+ `[apm] \u5DF2\u6E05\u7406 Cursor agent store agentId=${agentId} taskId=${trimmedTaskId}`
7342
+ );
7343
+ }
7344
+ } catch (err) {
7345
+ console.warn(
7346
+ `[apm] \u6E05\u7406 Cursor agent store \u5931\u8D25 agentId=${agentId}:`,
7347
+ err instanceof Error ? err.message : err
7348
+ );
7349
+ }
7350
+ } else {
7351
+ console.log(
7352
+ `[apm] \u65E0 WebIDE agentId\uFF0C\u8DF3\u8FC7 cursor-agent-store taskId=${trimmedTaskId}`
7353
+ );
7354
+ }
7355
+ const dir = resolve7(trimmedWorkdir, ".apm", "webide", trimmedTaskId);
7356
+ if (existsSync24(dir)) {
7258
7357
  rmSync5(dir, { recursive: true, force: true });
7259
7358
  console.log(`[apm] \u5DF2\u6E05\u7406 WebIDE \u7F13\u5B58 ${dir}`);
7260
7359
  } else {
@@ -7636,17 +7735,17 @@ ${JSON.stringify(event, null, 2)}
7636
7735
  }
7637
7736
 
7638
7737
  // 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");
7738
+ import { existsSync as existsSync25, mkdirSync as mkdirSync11, readFileSync as readFileSync18, writeFileSync as writeFileSync16 } from "node:fs";
7739
+ import { dirname as dirname8, resolve as resolve8 } from "node:path";
7740
+ function registryPath2(workdir, sessionId) {
7741
+ return resolve8(workdir, ".apm", "sessions", sessionId, "cursor-agents.json");
7643
7742
  }
7644
- function readRegistry(path19) {
7645
- if (!existsSync24(path19)) {
7743
+ function readRegistry2(path19) {
7744
+ if (!existsSync25(path19)) {
7646
7745
  return {};
7647
7746
  }
7648
7747
  try {
7649
- const parsed = JSON.parse(readFileSync17(path19, "utf8"));
7748
+ const parsed = JSON.parse(readFileSync18(path19, "utf8"));
7650
7749
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
7651
7750
  const result = {};
7652
7751
  for (const [key, value] of Object.entries(
@@ -7663,23 +7762,23 @@ function readRegistry(path19) {
7663
7762
  return {};
7664
7763
  }
7665
7764
  function writeRegistry(path19, registry) {
7666
- mkdirSync10(dirname7(path19), { recursive: true });
7667
- writeFileSync15(path19, `${JSON.stringify(registry, null, 2)}
7765
+ mkdirSync11(dirname8(path19), { recursive: true });
7766
+ writeFileSync16(path19, `${JSON.stringify(registry, null, 2)}
7668
7767
  `, "utf8");
7669
7768
  }
7670
7769
  function loadSessionAgentId(workdir, sessionId, user) {
7671
- const registry = readRegistry(registryPath(workdir, sessionId));
7770
+ const registry = readRegistry2(registryPath2(workdir, sessionId));
7672
7771
  return registry[user];
7673
7772
  }
7674
7773
  function saveSessionAgentId(workdir, sessionId, user, agentId) {
7675
- const path19 = registryPath(workdir, sessionId);
7676
- const registry = readRegistry(path19);
7774
+ const path19 = registryPath2(workdir, sessionId);
7775
+ const registry = readRegistry2(path19);
7677
7776
  registry[user] = agentId;
7678
7777
  writeRegistry(path19, registry);
7679
7778
  }
7680
7779
  function clearSessionAgentId(workdir, sessionId, user) {
7681
- const path19 = registryPath(workdir, sessionId);
7682
- const registry = readRegistry(path19);
7780
+ const path19 = registryPath2(workdir, sessionId);
7781
+ const registry = readRegistry2(path19);
7683
7782
  if (!(user in registry)) {
7684
7783
  return;
7685
7784
  }
@@ -8267,13 +8366,13 @@ ${WORKSPACE_BOUNDARY_HINT}`;
8267
8366
  }
8268
8367
 
8269
8368
  // src/commands/connect/local-agent-store.ts
8270
- import { mkdirSync as mkdirSync11 } from "node:fs";
8369
+ import { mkdirSync as mkdirSync12 } from "node:fs";
8271
8370
  import { join as join19 } from "node:path";
8272
- import { JsonlLocalAgentStore } from "@cursor/sdk";
8371
+ import { JsonlLocalAgentStore as JsonlLocalAgentStore2 } from "@cursor/sdk";
8273
8372
  function createWorkspaceLocalAgentStore(workdir) {
8274
8373
  const rootDir = join19(workdir, ".apm", "cursor-agent-store");
8275
- mkdirSync11(rootDir, { recursive: true });
8276
- return new JsonlLocalAgentStore(rootDir);
8374
+ mkdirSync12(rootDir, { recursive: true });
8375
+ return new JsonlLocalAgentStore2(rootDir);
8277
8376
  }
8278
8377
 
8279
8378
  // src/commands/connect/cursor-agent.ts
@@ -8303,6 +8402,19 @@ function formatCursorRunFailure(runId, options) {
8303
8402
  }
8304
8403
  return `Cursor run \u5931\u8D25: ${runId} \u2014 ${details.join("\uFF1B")}`;
8305
8404
  }
8405
+ function persistSessionAgentId(ctx, agentId) {
8406
+ if (ctx.skipSessionAgentRegistry || !ctx.user) return;
8407
+ saveSessionAgentId(ctx.workdir, ctx.sessionId, ctx.user, agentId);
8408
+ }
8409
+ function invalidatePersistedAgentId(ctx) {
8410
+ if (ctx.skipSessionAgentRegistry) {
8411
+ ctx.onInvalidatePersistedAgentId?.();
8412
+ return;
8413
+ }
8414
+ if (ctx.user) {
8415
+ clearSessionAgentId(ctx.workdir, ctx.sessionId, ctx.user);
8416
+ }
8417
+ }
8306
8418
  async function obtainAgent(ctx) {
8307
8419
  const agentOptions = {
8308
8420
  apiKey: ctx.apiKey,
@@ -8321,32 +8433,28 @@ async function obtainAgent(ctx) {
8321
8433
  ...ctx.mcpServers ? { mcpServers: ctx.mcpServers } : {}
8322
8434
  };
8323
8435
  const explicitAgentId = ctx.resumeAgentId?.trim();
8324
- const savedAgentId = explicitAgentId || (ctx.user ? loadSessionAgentId(ctx.workdir, ctx.sessionId, ctx.user) : void 0);
8436
+ const savedAgentId = explicitAgentId || (!ctx.skipSessionAgentRegistry && ctx.user ? loadSessionAgentId(ctx.workdir, ctx.sessionId, ctx.user) : void 0);
8325
8437
  if (savedAgentId) {
8326
8438
  try {
8327
8439
  const agent = await Agent.resume(savedAgentId, agentOptions);
8328
8440
  console.log(
8329
8441
  `[apm] \u590D\u7528 Agent user=${ctx.user} agentId=${savedAgentId}${explicitAgentId ? "\uFF08\u53C2\u6570\u6307\u5B9A\uFF09" : ""}`
8330
8442
  );
8331
- if (ctx.user) {
8332
- saveSessionAgentId(ctx.workdir, ctx.sessionId, ctx.user, agent.agentId);
8333
- }
8443
+ persistSessionAgentId(ctx, agent.agentId);
8334
8444
  return { agent, resumed: true };
8335
8445
  } catch (err) {
8336
8446
  console.warn(
8337
8447
  `[apm] \u590D\u7528 Agent \u5931\u8D25\uFF08agentId=${savedAgentId}\uFF09\uFF0C\u56DE\u9000\u4E3A\u65B0\u5EFA:`,
8338
8448
  err instanceof Error ? err.message : err
8339
8449
  );
8340
- if (!explicitAgentId && ctx.user) {
8341
- clearSessionAgentId(ctx.workdir, ctx.sessionId, ctx.user);
8450
+ if (!explicitAgentId) {
8451
+ invalidatePersistedAgentId(ctx);
8342
8452
  }
8343
8453
  }
8344
8454
  }
8345
8455
  try {
8346
8456
  const agent = await Agent.create(agentOptions);
8347
- if (ctx.user) {
8348
- saveSessionAgentId(ctx.workdir, ctx.sessionId, ctx.user, agent.agentId);
8349
- }
8457
+ persistSessionAgentId(ctx, agent.agentId);
8350
8458
  return { agent, resumed: false };
8351
8459
  } catch (err) {
8352
8460
  if (ctx.enableSandbox && err instanceof Error && /sandbox/i.test(err.message)) {
@@ -8392,8 +8500,19 @@ async function runCursorAgent(cfg, ctx, options) {
8392
8500
  mode: ctx.mode,
8393
8501
  resumeAgentId: ctx.resumeAgentId,
8394
8502
  customTools,
8395
- enableSandbox
8503
+ enableSandbox,
8504
+ skipSessionAgentRegistry: options?.skipSessionAgentRegistry,
8505
+ onInvalidatePersistedAgentId: options?.onInvalidatePersistedAgentId
8396
8506
  });
8507
+ const invalidateAgentMapping = () => {
8508
+ invalidatePersistedAgentId({
8509
+ workdir,
8510
+ sessionId: ctx.sessionId,
8511
+ user: ctx.user,
8512
+ skipSessionAgentRegistry: options?.skipSessionAgentRegistry,
8513
+ onInvalidatePersistedAgentId: options?.onInvalidatePersistedAgentId
8514
+ });
8515
+ };
8397
8516
  const eventSession = options?.eventSession ?? new EventSession(prompt);
8398
8517
  const syncRemoteLog = options?.createRemoteLogSync ? options.createRemoteLogSync(agent.agentId) : options?.skipRemoteLogSync ? noopRemoteLogSync : createThrottledCursorMessageLogSync(
8399
8518
  cfg,
@@ -8461,7 +8580,7 @@ async function runCursorAgent(cfg, ctx, options) {
8461
8580
  });
8462
8581
  console.error(`[apm] ${failureMessage}`);
8463
8582
  if (resumed) {
8464
- clearSessionAgentId(workdir, ctx.sessionId, ctx.user);
8583
+ invalidateAgentMapping();
8465
8584
  }
8466
8585
  throw new Error(failureMessage);
8467
8586
  }
@@ -8500,7 +8619,7 @@ async function runCursorAgent(cfg, ctx, options) {
8500
8619
  } catch (err) {
8501
8620
  if (err instanceof CursorAgentError) {
8502
8621
  if (resumed) {
8503
- clearSessionAgentId(workdir, ctx.sessionId, ctx.user);
8622
+ invalidateAgentMapping();
8504
8623
  }
8505
8624
  throw new Error(
8506
8625
  `Cursor \u542F\u52A8\u5931\u8D25: ${err.message}${err.isRetryable ? "\uFF08\u53EF\u91CD\u8BD5\uFF09" : ""}`
@@ -8553,7 +8672,7 @@ async function ensureMessageHasReply(cfg, sessionId, messageId, fallback) {
8553
8672
  }
8554
8673
 
8555
8674
  // src/commands/connect/cli-version-sync.ts
8556
- import { existsSync as existsSync25, readFileSync as readFileSync18, writeFileSync as writeFileSync16 } from "fs";
8675
+ import { existsSync as existsSync26, readFileSync as readFileSync19, writeFileSync as writeFileSync17 } from "fs";
8557
8676
  import { join as join20 } from "path";
8558
8677
  var CLI_VERSION_FILE = ".cli-version.json";
8559
8678
  function manifestPath2(apmDir) {
@@ -8561,12 +8680,12 @@ function manifestPath2(apmDir) {
8561
8680
  }
8562
8681
  function loadManifest4(apmDir) {
8563
8682
  const path19 = toFsPath(manifestPath2(apmDir));
8564
- if (!existsSync25(path19)) {
8683
+ if (!existsSync26(path19)) {
8565
8684
  return null;
8566
8685
  }
8567
8686
  try {
8568
8687
  const parsed = JSON.parse(
8569
- readFileSync18(path19, "utf8")
8688
+ readFileSync19(path19, "utf8")
8570
8689
  );
8571
8690
  if (parsed?.version === 1 && typeof parsed.cliVersion === "string" && parsed.cliVersion.trim()) {
8572
8691
  return parsed;
@@ -8577,7 +8696,7 @@ function loadManifest4(apmDir) {
8577
8696
  }
8578
8697
  function saveManifest4(apmDir, cliVersion) {
8579
8698
  const manifest = { version: 1, cliVersion };
8580
- writeFileSync16(
8699
+ writeFileSync17(
8581
8700
  toFsPath(manifestPath2(apmDir)),
8582
8701
  `${JSON.stringify(manifest, null, 2)}
8583
8702
  `,
@@ -8631,7 +8750,7 @@ function createRunSlotPool(maxConcurrent = DEFAULT_MAX_CONCURRENT, options = {})
8631
8750
  );
8632
8751
  }
8633
8752
  const queuedAt = Date.now();
8634
- return new Promise((resolve8) => {
8753
+ return new Promise((resolve9) => {
8635
8754
  waiters.push(() => {
8636
8755
  active += 1;
8637
8756
  const waitedMs = Date.now() - queuedAt;
@@ -8642,7 +8761,7 @@ function createRunSlotPool(maxConcurrent = DEFAULT_MAX_CONCURRENT, options = {})
8642
8761
  )}s\uFF0C\u5F53\u524D\u5360\u7528 ${active}/${maxConcurrent}`
8643
8762
  );
8644
8763
  }
8645
- resolve8();
8764
+ resolve9();
8646
8765
  });
8647
8766
  });
8648
8767
  };
@@ -8662,21 +8781,46 @@ function createRunSlotPool(maxConcurrent = DEFAULT_MAX_CONCURRENT, options = {})
8662
8781
  // src/commands/connect/webide-worker-pool.ts
8663
8782
  init_webide_terminal_registry();
8664
8783
  import { Worker } from "node:worker_threads";
8784
+ import { totalmem } from "node:os";
8665
8785
  import { fileURLToPath as fileURLToPath3 } from "node:url";
8666
- import { dirname as dirname8, join as join21 } from "node:path";
8786
+ import { dirname as dirname9, join as join21 } from "node:path";
8667
8787
  var workerFile = join21(
8668
- dirname8(fileURLToPath3(import.meta.url)),
8788
+ dirname9(fileURLToPath3(import.meta.url)),
8669
8789
  "webide-message-worker.js"
8670
8790
  );
8791
+ var SYSTEM_RESERVE_MB = 3072;
8792
+ var MIN_WEBIDE_WORKER_HEAP_MB = 1536;
8793
+ var MAX_WEBIDE_WORKER_HEAP_MB = 8192;
8794
+ function defaultWebIdeWorkerHeapMb(totalMemoryBytes = totalmem()) {
8795
+ const totalMb = Math.floor(totalMemoryBytes / (1024 * 1024));
8796
+ return Math.min(
8797
+ MAX_WEBIDE_WORKER_HEAP_MB,
8798
+ Math.max(MIN_WEBIDE_WORKER_HEAP_MB, totalMb - SYSTEM_RESERVE_MB)
8799
+ );
8800
+ }
8801
+ function resolveWebIdeWorkerHeapMb(env = process.env, totalMemoryBytes = totalmem()) {
8802
+ const fallback = defaultWebIdeWorkerHeapMb(totalMemoryBytes);
8803
+ const raw = env.APM_WEBIDE_WORKER_HEAP_MB?.trim();
8804
+ if (!raw) return fallback;
8805
+ const n = Number.parseInt(raw, 10);
8806
+ if (!Number.isFinite(n)) return fallback;
8807
+ return Math.min(16384, Math.max(MIN_WEBIDE_WORKER_HEAP_MB, n));
8808
+ }
8671
8809
  function spawnWebIdeMessageWorker(cfg, msg) {
8810
+ const maxOldGenerationSizeMb = resolveWebIdeWorkerHeapMb();
8672
8811
  const worker = new Worker(workerFile, {
8673
- workerData: { cfg, msg }
8812
+ workerData: { cfg, msg },
8813
+ // worker_threads 不支持 execArgv 里的 --max-old-space-size,必须用 resourceLimits
8814
+ resourceLimits: { maxOldGenerationSizeMb }
8674
8815
  });
8816
+ console.log(
8817
+ `[apm] webide worker spawn messageId=${msg.messageId} heapMb=${maxOldGenerationSizeMb}`
8818
+ );
8675
8819
  const registry = getWebIdeTerminalRegistry(cfg);
8676
8820
  let settled = false;
8677
8821
  let resolveDone;
8678
- const done = new Promise((resolve8) => {
8679
- resolveDone = resolve8;
8822
+ const done = new Promise((resolve9) => {
8823
+ resolveDone = resolve9;
8680
8824
  });
8681
8825
  const failIfStillQueued = async (reason) => {
8682
8826
  if (settled) return;
@@ -8764,10 +8908,12 @@ function spawnWebIdeMessageWorker(cfg, msg) {
8764
8908
  }
8765
8909
  });
8766
8910
  worker.on("error", (err) => {
8911
+ const detail = err instanceof Error ? err.message : String(err);
8767
8912
  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());
8913
+ 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` : "";
8914
+ void failIfStillQueued(`WebIDE worker \u5D29\u6E83: ${detail}${oomHint}`).finally(
8915
+ () => resolveDone()
8916
+ );
8771
8917
  });
8772
8918
  worker.on("exit", (code) => {
8773
8919
  if (!settled && code !== 0) {
@@ -8948,11 +9094,11 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
8948
9094
  }
8949
9095
  function interruptibleSleep(ms, signal) {
8950
9096
  if (signal.aborted) return Promise.resolve();
8951
- return new Promise((resolve8) => {
8952
- const timer = setTimeout(resolve8, ms);
9097
+ return new Promise((resolve9) => {
9098
+ const timer = setTimeout(resolve9, ms);
8953
9099
  const onAbort = () => {
8954
9100
  clearTimeout(timer);
8955
- resolve8();
9101
+ resolve9();
8956
9102
  };
8957
9103
  signal.addEventListener("abort", onAbort, { once: true });
8958
9104
  });
@@ -9053,7 +9199,7 @@ function attachWsHandlers(ws, ctx, onOpen) {
9053
9199
  msg2.taskId,
9054
9200
  "cache-cleanup"
9055
9201
  );
9056
- cleanWebIdeWorkspaceCache(msg2.taskId, workdir);
9202
+ await cleanWebIdeWorkspaceCache(msg2.taskId, workdir);
9057
9203
  })();
9058
9204
  activeTasks.add(task2);
9059
9205
  void task2.finally(() => {
@@ -9182,7 +9328,7 @@ function attachWsHandlers(ws, ctx, onOpen) {
9182
9328
  });
9183
9329
  }
9184
9330
  function connectOnce(url, ctx, connectionAbort, onConnected) {
9185
- return new Promise((resolve8, reject) => {
9331
+ return new Promise((resolve9, reject) => {
9186
9332
  const ws = new WebSocket(url);
9187
9333
  let stopHeartbeat;
9188
9334
  let settled = false;
@@ -9211,7 +9357,7 @@ function connectOnce(url, ctx, connectionAbort, onConnected) {
9211
9357
  finish(() => reject(new Error("shutdown")));
9212
9358
  return;
9213
9359
  }
9214
- finish(resolve8);
9360
+ finish(resolve9);
9215
9361
  });
9216
9362
  ws.on("error", (err) => {
9217
9363
  console.error("[apm] WebSocket \u9519\u8BEF:", err.message);
@@ -9706,7 +9852,7 @@ import path15 from "node:path";
9706
9852
  import Docker from "dockerode";
9707
9853
 
9708
9854
  // src/commands/deploy/internal/backend-deploy/dockerode-client/connection-options.ts
9709
- import { existsSync as existsSync26, readFileSync as readFileSync19 } from "node:fs";
9855
+ import { existsSync as existsSync27, readFileSync as readFileSync20 } from "node:fs";
9710
9856
  import path12 from "node:path";
9711
9857
  function asOptionalTlsBuffer(value) {
9712
9858
  if (typeof value !== "string") {
@@ -9718,8 +9864,8 @@ function asOptionalTlsBuffer(value) {
9718
9864
  if (normalized === "") {
9719
9865
  return void 0;
9720
9866
  }
9721
- if (existsSync26(normalized)) {
9722
- return readFileSync19(normalized);
9867
+ if (existsSync27(normalized)) {
9868
+ return readFileSync20(normalized);
9723
9869
  }
9724
9870
  const looksLikePath = /[\\/]/.test(normalized) || normalized.endsWith(".pem");
9725
9871
  if (looksLikePath) {
@@ -9844,17 +9990,17 @@ var DockerodeClient = class {
9844
9990
  await this.client.getImage(image).remove({ force: true });
9845
9991
  }
9846
9992
  async pullImage(image, auth) {
9847
- const stream = await new Promise((resolve8, reject) => {
9993
+ const stream = await new Promise((resolve9, reject) => {
9848
9994
  const pullOptions = auth ? { authconfig: auth } : void 0;
9849
9995
  this.client.pull(image, pullOptions, (err, output) => {
9850
9996
  if (err || !output) {
9851
9997
  reject(err ?? new Error("docker pull \u8FD4\u56DE\u7A7A\u8F93\u51FA"));
9852
9998
  return;
9853
9999
  }
9854
- resolve8(output);
10000
+ resolve9(output);
9855
10001
  });
9856
10002
  });
9857
- await new Promise((resolve8, reject) => {
10003
+ await new Promise((resolve9, reject) => {
9858
10004
  this.client.modem.followProgress(
9859
10005
  stream,
9860
10006
  (err) => {
@@ -9862,7 +10008,7 @@ var DockerodeClient = class {
9862
10008
  reject(err);
9863
10009
  return;
9864
10010
  }
9865
- resolve8();
10011
+ resolve9();
9866
10012
  },
9867
10013
  () => void 0
9868
10014
  );
@@ -9929,7 +10075,7 @@ var DockerodeClient = class {
9929
10075
  var createDockerodeClient = (config) => new DockerodeClient(config);
9930
10076
 
9931
10077
  // src/commands/deploy/internal/backend-deploy/dockerode-client/env.ts
9932
- import { existsSync as existsSync27, readFileSync as readFileSync20, statSync as statSync11 } from "node:fs";
10078
+ import { existsSync as existsSync28, readFileSync as readFileSync21, statSync as statSync11 } from "node:fs";
9933
10079
  import path13 from "node:path";
9934
10080
  function stripSurroundingQuotes(value) {
9935
10081
  const t = value.trim();
@@ -9946,10 +10092,10 @@ function loadEnvFromFile(envFilePath) {
9946
10092
  return {};
9947
10093
  }
9948
10094
  const targetPath = path13.resolve(envFilePath);
9949
- if (!existsSync27(targetPath) || !statSync11(targetPath).isFile()) {
10095
+ if (!existsSync28(targetPath) || !statSync11(targetPath).isFile()) {
9950
10096
  return {};
9951
10097
  }
9952
- const raw = readFileSync20(targetPath, "utf-8");
10098
+ const raw = readFileSync21(targetPath, "utf-8");
9953
10099
  const result = {};
9954
10100
  for (const line of raw.split(/\r?\n/)) {
9955
10101
  const normalized = line.trim();
@@ -10120,12 +10266,12 @@ function dockerPushImage(params, cwd) {
10120
10266
  }
10121
10267
 
10122
10268
  // src/commands/deploy/internal/backend-deploy/resolve-dockerfile.ts
10123
- import { existsSync as existsSync28 } from "node:fs";
10269
+ import { existsSync as existsSync29 } from "node:fs";
10124
10270
  import path14 from "node:path";
10125
10271
  function resolveDockerBuildPaths(cwd) {
10126
10272
  const dockerfilePath = path14.join(cwd, "Dockerfile");
10127
10273
  Logger.info(`\u67E5\u627EDockerfile\u6587\u4EF6\uFF0C\u8DEF\u5F84: ${dockerfilePath}`);
10128
- if (!existsSync28(dockerfilePath)) {
10274
+ if (!existsSync29(dockerfilePath)) {
10129
10275
  throw new Error(`Dockerfile \u4E0D\u5B58\u5728\uFF1A${dockerfilePath}`);
10130
10276
  }
10131
10277
  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"
@@ -2304,6 +2300,19 @@ function formatCursorRunFailure(runId, options) {
2304
2300
  }
2305
2301
  return `Cursor run \u5931\u8D25: ${runId} \u2014 ${details.join("\uFF1B")}`;
2306
2302
  }
2303
+ function persistSessionAgentId(ctx, agentId) {
2304
+ if (ctx.skipSessionAgentRegistry || !ctx.user) return;
2305
+ saveSessionAgentId(ctx.workdir, ctx.sessionId, ctx.user, agentId);
2306
+ }
2307
+ function invalidatePersistedAgentId(ctx) {
2308
+ if (ctx.skipSessionAgentRegistry) {
2309
+ ctx.onInvalidatePersistedAgentId?.();
2310
+ return;
2311
+ }
2312
+ if (ctx.user) {
2313
+ clearSessionAgentId(ctx.workdir, ctx.sessionId, ctx.user);
2314
+ }
2315
+ }
2307
2316
  async function obtainAgent(ctx) {
2308
2317
  const agentOptions = {
2309
2318
  apiKey: ctx.apiKey,
@@ -2322,32 +2331,28 @@ async function obtainAgent(ctx) {
2322
2331
  ...ctx.mcpServers ? { mcpServers: ctx.mcpServers } : {}
2323
2332
  };
2324
2333
  const explicitAgentId = ctx.resumeAgentId?.trim();
2325
- const savedAgentId = explicitAgentId || (ctx.user ? loadSessionAgentId(ctx.workdir, ctx.sessionId, ctx.user) : void 0);
2334
+ const savedAgentId = explicitAgentId || (!ctx.skipSessionAgentRegistry && ctx.user ? loadSessionAgentId(ctx.workdir, ctx.sessionId, ctx.user) : void 0);
2326
2335
  if (savedAgentId) {
2327
2336
  try {
2328
2337
  const agent = await Agent.resume(savedAgentId, agentOptions);
2329
2338
  console.log(
2330
2339
  `[apm] \u590D\u7528 Agent user=${ctx.user} agentId=${savedAgentId}${explicitAgentId ? "\uFF08\u53C2\u6570\u6307\u5B9A\uFF09" : ""}`
2331
2340
  );
2332
- if (ctx.user) {
2333
- saveSessionAgentId(ctx.workdir, ctx.sessionId, ctx.user, agent.agentId);
2334
- }
2341
+ persistSessionAgentId(ctx, agent.agentId);
2335
2342
  return { agent, resumed: true };
2336
2343
  } catch (err) {
2337
2344
  console.warn(
2338
2345
  `[apm] \u590D\u7528 Agent \u5931\u8D25\uFF08agentId=${savedAgentId}\uFF09\uFF0C\u56DE\u9000\u4E3A\u65B0\u5EFA:`,
2339
2346
  err instanceof Error ? err.message : err
2340
2347
  );
2341
- if (!explicitAgentId && ctx.user) {
2342
- clearSessionAgentId(ctx.workdir, ctx.sessionId, ctx.user);
2348
+ if (!explicitAgentId) {
2349
+ invalidatePersistedAgentId(ctx);
2343
2350
  }
2344
2351
  }
2345
2352
  }
2346
2353
  try {
2347
2354
  const agent = await Agent.create(agentOptions);
2348
- if (ctx.user) {
2349
- saveSessionAgentId(ctx.workdir, ctx.sessionId, ctx.user, agent.agentId);
2350
- }
2355
+ persistSessionAgentId(ctx, agent.agentId);
2351
2356
  return { agent, resumed: false };
2352
2357
  } catch (err) {
2353
2358
  if (ctx.enableSandbox && err instanceof Error && /sandbox/i.test(err.message)) {
@@ -2393,8 +2398,19 @@ async function runCursorAgent(cfg, ctx, options) {
2393
2398
  mode: ctx.mode,
2394
2399
  resumeAgentId: ctx.resumeAgentId,
2395
2400
  customTools,
2396
- enableSandbox
2401
+ enableSandbox,
2402
+ skipSessionAgentRegistry: options?.skipSessionAgentRegistry,
2403
+ onInvalidatePersistedAgentId: options?.onInvalidatePersistedAgentId
2397
2404
  });
2405
+ const invalidateAgentMapping = () => {
2406
+ invalidatePersistedAgentId({
2407
+ workdir,
2408
+ sessionId: ctx.sessionId,
2409
+ user: ctx.user,
2410
+ skipSessionAgentRegistry: options?.skipSessionAgentRegistry,
2411
+ onInvalidatePersistedAgentId: options?.onInvalidatePersistedAgentId
2412
+ });
2413
+ };
2398
2414
  const eventSession = options?.eventSession ?? new EventSession(prompt);
2399
2415
  const syncRemoteLog = options?.createRemoteLogSync ? options.createRemoteLogSync(agent.agentId) : options?.skipRemoteLogSync ? noopRemoteLogSync : createThrottledCursorMessageLogSync(
2400
2416
  cfg,
@@ -2462,7 +2478,7 @@ async function runCursorAgent(cfg, ctx, options) {
2462
2478
  });
2463
2479
  console.error(`[apm] ${failureMessage}`);
2464
2480
  if (resumed) {
2465
- clearSessionAgentId(workdir, ctx.sessionId, ctx.user);
2481
+ invalidateAgentMapping();
2466
2482
  }
2467
2483
  throw new Error(failureMessage);
2468
2484
  }
@@ -2501,7 +2517,7 @@ async function runCursorAgent(cfg, ctx, options) {
2501
2517
  } catch (err) {
2502
2518
  if (err instanceof CursorAgentError) {
2503
2519
  if (resumed) {
2504
- clearSessionAgentId(workdir, ctx.sessionId, ctx.user);
2520
+ invalidateAgentMapping();
2505
2521
  }
2506
2522
  throw new Error(
2507
2523
  `Cursor \u542F\u52A8\u5931\u8D25: ${err.message}${err.isRetryable ? "\uFF08\u53EF\u91CD\u8BD5\uFF09" : ""}`
@@ -2529,10 +2545,27 @@ function readRegistry2(path) {
2529
2545
  try {
2530
2546
  const parsed = JSON.parse(readFileSync5(path, "utf8"));
2531
2547
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
2532
- const agentId = parsed.agentId;
2533
- if (typeof agentId === "string" && agentId.trim()) {
2534
- return { agentId: agentId.trim() };
2548
+ const raw = parsed;
2549
+ const state = {};
2550
+ if (typeof raw.agentId === "string" && raw.agentId.trim()) {
2551
+ state.agentId = raw.agentId.trim();
2552
+ }
2553
+ if (typeof raw.taskId === "string" && raw.taskId.trim()) {
2554
+ state.taskId = raw.taskId.trim();
2555
+ }
2556
+ if (typeof raw.messageId === "string" && raw.messageId.trim()) {
2557
+ state.messageId = raw.messageId.trim();
2558
+ }
2559
+ if (typeof raw.action === "string" && raw.action.trim()) {
2560
+ state.action = raw.action.trim();
2561
+ }
2562
+ if (raw.status === "TYPING" || raw.status === "SUCCESS" || raw.status === "FAILED" || raw.status === "CANCELLED") {
2563
+ state.status = raw.status;
2564
+ }
2565
+ if (typeof raw.updatedAt === "string" && raw.updatedAt.trim()) {
2566
+ state.updatedAt = raw.updatedAt.trim();
2535
2567
  }
2568
+ return state;
2536
2569
  }
2537
2570
  } catch {
2538
2571
  }
@@ -2543,16 +2576,121 @@ function writeRegistry2(path, registry) {
2543
2576
  writeFileSync5(path, `${JSON.stringify(registry, null, 2)}
2544
2577
  `, "utf8");
2545
2578
  }
2579
+ function syncWebIdeTaskState(workdir, taskId, patch) {
2580
+ const trimmedTaskId = taskId.trim();
2581
+ if (!trimmedTaskId) return;
2582
+ const path = registryPath2(workdir, trimmedTaskId);
2583
+ const current = readRegistry2(path);
2584
+ const next = {
2585
+ ...current,
2586
+ taskId: trimmedTaskId,
2587
+ updatedAt: patch.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString()
2588
+ };
2589
+ if (patch.agentId !== void 0) {
2590
+ const agentId = patch.agentId.trim();
2591
+ if (agentId) next.agentId = agentId;
2592
+ else delete next.agentId;
2593
+ }
2594
+ if (patch.messageId !== void 0) {
2595
+ const messageId = patch.messageId.trim();
2596
+ if (messageId) next.messageId = messageId;
2597
+ else delete next.messageId;
2598
+ }
2599
+ if (patch.action !== void 0) {
2600
+ const action = patch.action.trim();
2601
+ if (action) next.action = action;
2602
+ else delete next.action;
2603
+ }
2604
+ if (patch.status !== void 0) {
2605
+ next.status = patch.status;
2606
+ }
2607
+ writeRegistry2(path, next);
2608
+ }
2546
2609
  function loadWebIdeAgentId(workdir, taskId) {
2547
2610
  return readRegistry2(registryPath2(workdir, taskId)).agentId;
2548
2611
  }
2549
2612
  function saveWebIdeAgentId(workdir, taskId, agentId) {
2550
- writeRegistry2(registryPath2(workdir, taskId), { agentId });
2613
+ syncWebIdeTaskState(workdir, taskId, { agentId });
2551
2614
  }
2552
2615
  function clearWebIdeAgentId(workdir, taskId) {
2553
2616
  const path = registryPath2(workdir, taskId);
2554
2617
  if (!existsSync4(path)) return;
2555
- writeRegistry2(path, {});
2618
+ syncWebIdeTaskState(workdir, taskId, { agentId: "" });
2619
+ }
2620
+
2621
+ // src/commands/clean-webide-cache.ts
2622
+ import { existsSync as existsSync5, rmSync } from "node:fs";
2623
+ import { resolve as resolve6 } from "node:path";
2624
+ import { JsonlLocalAgentStore as JsonlLocalAgentStore2 } from "@cursor/sdk";
2625
+ function cursorAgentStoreDir(workdir) {
2626
+ return resolve6(workdir, ".apm", "cursor-agent-store");
2627
+ }
2628
+ async function purgeCursorAgentStoreForAgent(workdir, agentId) {
2629
+ const trimmedAgentId = agentId.trim();
2630
+ const trimmedWorkdir = workdir.trim();
2631
+ if (!trimmedAgentId || !trimmedWorkdir) return false;
2632
+ const rootDir = cursorAgentStoreDir(trimmedWorkdir);
2633
+ if (!existsSync5(rootDir)) return false;
2634
+ const store = new JsonlLocalAgentStore2(rootDir);
2635
+ const runIds = [];
2636
+ let cursor;
2637
+ do {
2638
+ const page = await store.runs.list({
2639
+ filter: {
2640
+ agentIds: [trimmedAgentId],
2641
+ ...cursor ? { cursor } : {},
2642
+ limit: 200
2643
+ }
2644
+ });
2645
+ for (const run of page.items) {
2646
+ runIds.push(run.runId);
2647
+ }
2648
+ cursor = page.nextCursor;
2649
+ } while (cursor);
2650
+ if (runIds.length > 0) {
2651
+ await store.runEvents.delete({ filter: { runIds } });
2652
+ }
2653
+ await store.runs.delete({ filter: { agentIds: [trimmedAgentId] } });
2654
+ await store.checkpoints.delete({ filter: { agentIds: [trimmedAgentId] } });
2655
+ await store.agents.delete({ filter: { agentIds: [trimmedAgentId] } });
2656
+ return true;
2657
+ }
2658
+ async function cleanWebIdeWorkspaceCache(taskId, workdir) {
2659
+ const trimmedTaskId = taskId.trim();
2660
+ const trimmedWorkdir = workdir.trim();
2661
+ if (!trimmedTaskId || !trimmedWorkdir) {
2662
+ return;
2663
+ }
2664
+ const agentId = loadWebIdeAgentId(trimmedWorkdir, trimmedTaskId);
2665
+ if (agentId) {
2666
+ try {
2667
+ const purged = await purgeCursorAgentStoreForAgent(
2668
+ trimmedWorkdir,
2669
+ agentId
2670
+ );
2671
+ if (purged) {
2672
+ console.log(
2673
+ `[apm] \u5DF2\u6E05\u7406 Cursor agent store agentId=${agentId} taskId=${trimmedTaskId}`
2674
+ );
2675
+ }
2676
+ } catch (err) {
2677
+ console.warn(
2678
+ `[apm] \u6E05\u7406 Cursor agent store \u5931\u8D25 agentId=${agentId}:`,
2679
+ err instanceof Error ? err.message : err
2680
+ );
2681
+ }
2682
+ } else {
2683
+ console.log(
2684
+ `[apm] \u65E0 WebIDE agentId\uFF0C\u8DF3\u8FC7 cursor-agent-store taskId=${trimmedTaskId}`
2685
+ );
2686
+ }
2687
+ const dir = resolve6(trimmedWorkdir, ".apm", "webide", trimmedTaskId);
2688
+ if (existsSync5(dir)) {
2689
+ rmSync(dir, { recursive: true, force: true });
2690
+ console.log(`[apm] \u5DF2\u6E05\u7406 WebIDE \u7F13\u5B58 ${dir}`);
2691
+ } else {
2692
+ console.log(`[apm] WebIDE \u7F13\u5B58\u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7 ${dir}`);
2693
+ }
2556
2694
  }
2557
2695
 
2558
2696
  // src/commands/connect/webide-ask-question.ts
@@ -2669,14 +2807,14 @@ var MinioClient = class {
2669
2807
  async deleteObjectsByPrefix(bucket, prefix) {
2670
2808
  const objectsStream = this.inner.listObjectsV2(bucket, prefix, true);
2671
2809
  const keys = [];
2672
- await new Promise((resolve6, reject) => {
2810
+ await new Promise((resolve7, reject) => {
2673
2811
  objectsStream.on("data", (obj) => {
2674
2812
  if (obj.name) {
2675
2813
  keys.push(obj.name);
2676
2814
  }
2677
2815
  });
2678
2816
  objectsStream.on("error", reject);
2679
- objectsStream.on("end", resolve6);
2817
+ objectsStream.on("end", resolve7);
2680
2818
  });
2681
2819
  const chunkSize = 500;
2682
2820
  for (let i = 0; i < keys.length; i += chunkSize) {
@@ -2950,10 +3088,10 @@ ${diagnostic ?? ""}
2950
3088
 
2951
3089
  // src/project-documents-sync.ts
2952
3090
  import {
2953
- existsSync as existsSync5,
3091
+ existsSync as existsSync6,
2954
3092
  readdirSync as readdirSync3,
2955
3093
  readFileSync as readFileSync6,
2956
- rmSync,
3094
+ rmSync as rmSync2,
2957
3095
  writeFileSync as writeFileSync7
2958
3096
  } from "fs";
2959
3097
  import { createHash } from "crypto";
@@ -2999,7 +3137,7 @@ function readLocalManifest(apmRoot, projectId) {
2999
3137
  projectDocumentsDir(apmRoot, projectId),
3000
3138
  MANIFEST_FILE
3001
3139
  );
3002
- if (!existsSync5(manifestPath3)) {
3140
+ if (!existsSync6(manifestPath3)) {
3003
3141
  return null;
3004
3142
  }
3005
3143
  try {
@@ -3012,7 +3150,7 @@ function readLocalManifest(apmRoot, projectId) {
3012
3150
  }
3013
3151
  function listLocalDocumentPaths(apmRoot, projectId) {
3014
3152
  const root = projectDocumentsDir(apmRoot, projectId);
3015
- if (!existsSync5(root)) {
3153
+ if (!existsSync6(root)) {
3016
3154
  return [];
3017
3155
  }
3018
3156
  const paths = [];
@@ -3146,8 +3284,8 @@ ${diagnostic ?? ""}`);
3146
3284
  const absPath = toFsPath(
3147
3285
  projectDocumentLocalPath(targetApmDir, projectId, path)
3148
3286
  );
3149
- if (existsSync5(absPath)) {
3150
- rmSync(absPath, { force: true });
3287
+ if (existsSync6(absPath)) {
3288
+ rmSync2(absPath, { force: true });
3151
3289
  deleted += 1;
3152
3290
  }
3153
3291
  }
@@ -3513,17 +3651,17 @@ function readCliVersion() {
3513
3651
  }
3514
3652
 
3515
3653
  // src/commands/update-skills.ts
3516
- import { existsSync as existsSync7, mkdirSync as mkdirSync8, statSync as statSync4 } from "fs";
3654
+ import { existsSync as existsSync8, mkdirSync as mkdirSync8, statSync as statSync4 } from "fs";
3517
3655
  import { join as join10 } from "path";
3518
3656
 
3519
3657
  // src/skills-sync.ts
3520
3658
  import {
3521
3659
  copyFileSync as copyFileSync2,
3522
3660
  cpSync,
3523
- existsSync as existsSync6,
3661
+ existsSync as existsSync7,
3524
3662
  mkdirSync as mkdirSync7,
3525
3663
  readdirSync as readdirSync4,
3526
- rmSync as rmSync2,
3664
+ rmSync as rmSync3,
3527
3665
  statSync as statSync3,
3528
3666
  writeFileSync as writeFileSync9
3529
3667
  } from "fs";
@@ -3537,20 +3675,20 @@ function sanitizeSkillDirName(name) {
3537
3675
  return trimmed.replace(/[/\\:*?"<>|]/g, "_");
3538
3676
  }
3539
3677
  function listBaseSkillDirNames() {
3540
- if (!existsSync6(BASE_SKILLS_TEMPLATE_DIR)) return [];
3678
+ if (!existsSync7(BASE_SKILLS_TEMPLATE_DIR)) return [];
3541
3679
  return readdirSync4(BASE_SKILLS_TEMPLATE_DIR).filter((name) => {
3542
3680
  const path = join9(BASE_SKILLS_TEMPLATE_DIR, name);
3543
3681
  return statSync3(path).isDirectory();
3544
3682
  });
3545
3683
  }
3546
3684
  function syncAgentsGuide(apmDir) {
3547
- if (!existsSync6(AGENTS_TEMPLATE_PATH)) return false;
3685
+ if (!existsSync7(AGENTS_TEMPLATE_PATH)) return false;
3548
3686
  mkdirSync7(apmDir, { recursive: true });
3549
3687
  copyFileSync2(AGENTS_TEMPLATE_PATH, join9(apmDir, "AGENTS.md"));
3550
3688
  return true;
3551
3689
  }
3552
3690
  function listBaseRuleFileNames() {
3553
- if (!existsSync6(BASE_RULES_TEMPLATE_DIR)) return [];
3691
+ if (!existsSync7(BASE_RULES_TEMPLATE_DIR)) return [];
3554
3692
  return readdirSync4(BASE_RULES_TEMPLATE_DIR).filter((name) => {
3555
3693
  const path = join9(BASE_RULES_TEMPLATE_DIR, name);
3556
3694
  return statSync3(path).isFile();
@@ -3594,13 +3732,13 @@ function syncSupplementarySkills(skillsDir, list) {
3594
3732
  written.push(dirName);
3595
3733
  }
3596
3734
  const removed = [];
3597
- if (!existsSync6(skillsDir)) return { written, skipped, removed };
3735
+ if (!existsSync7(skillsDir)) return { written, skipped, removed };
3598
3736
  for (const entry of readdirSync4(skillsDir)) {
3599
3737
  const full = join9(skillsDir, entry);
3600
3738
  if (!statSync3(full).isDirectory()) continue;
3601
3739
  if (baseNames.has(entry)) continue;
3602
3740
  if (apiDirNames.has(entry)) continue;
3603
- rmSync2(full, { recursive: true, force: true });
3741
+ rmSync3(full, { recursive: true, force: true });
3604
3742
  removed.push(entry);
3605
3743
  }
3606
3744
  return { written, skipped, removed };
@@ -3610,7 +3748,7 @@ function syncSupplementarySkills(skillsDir, list) {
3610
3748
  async function syncWorkspaceSkills(cfg, workdir) {
3611
3749
  const apmDir = workspaceApmDir(workdir);
3612
3750
  const fsApmDir = toFsPath(apmDir);
3613
- if (!existsSync7(fsApmDir)) {
3751
+ if (!existsSync8(fsApmDir)) {
3614
3752
  throw new Error("[apm] \u672A\u627E\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5148\u6267\u884C apm init");
3615
3753
  }
3616
3754
  const apmStat = statSync4(fsApmDir);
@@ -3654,7 +3792,7 @@ async function syncWorkspaceSkills(cfg, workdir) {
3654
3792
  }
3655
3793
 
3656
3794
  // src/commands/sync-session-attachments.ts
3657
- import { existsSync as existsSync8, readFileSync as readFileSync9, writeFileSync as writeFileSync10 } from "fs";
3795
+ import { existsSync as existsSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync10 } from "fs";
3658
3796
  import { join as join11 } from "path";
3659
3797
  var MANIFEST_FILE2 = ".sync-manifest.json";
3660
3798
  async function downloadAttachment(cfg, attachmentId) {
@@ -3672,7 +3810,7 @@ async function downloadAttachment(cfg, attachmentId) {
3672
3810
  }
3673
3811
  function loadManifest(dir) {
3674
3812
  const path = join11(dir, MANIFEST_FILE2);
3675
- if (!existsSync8(path)) {
3813
+ if (!existsSync9(path)) {
3676
3814
  return { version: 1, attachments: {} };
3677
3815
  }
3678
3816
  try {
@@ -3695,7 +3833,7 @@ function saveManifest(dir, manifest) {
3695
3833
  );
3696
3834
  }
3697
3835
  function isAttachmentUpToDate(entry, item, dest) {
3698
- if (!entry || !existsSync8(dest)) return false;
3836
+ if (!entry || !existsSync9(dest)) return false;
3699
3837
  if (entry.name !== item.name) return false;
3700
3838
  const createdAt = item.createdAt ?? "";
3701
3839
  return entry.createdAt === createdAt;
@@ -3741,7 +3879,7 @@ async function syncWebIdeAttachments(cfg, taskId, workdir, attachments) {
3741
3879
  }
3742
3880
 
3743
3881
  // src/commands/connect/cli-version-sync.ts
3744
- import { existsSync as existsSync9, readFileSync as readFileSync10, writeFileSync as writeFileSync11 } from "fs";
3882
+ import { existsSync as existsSync10, readFileSync as readFileSync10, writeFileSync as writeFileSync11 } from "fs";
3745
3883
  import { join as join12 } from "path";
3746
3884
  var CLI_VERSION_FILE = ".cli-version.json";
3747
3885
  function manifestPath2(apmDir) {
@@ -3749,7 +3887,7 @@ function manifestPath2(apmDir) {
3749
3887
  }
3750
3888
  function loadManifest2(apmDir) {
3751
3889
  const path = toFsPath(manifestPath2(apmDir));
3752
- if (!existsSync9(path)) {
3890
+ if (!existsSync10(path)) {
3753
3891
  return null;
3754
3892
  }
3755
3893
  try {
@@ -3802,6 +3940,13 @@ var WEBIDE_CODE_CHANGE_ACTIONS = /* @__PURE__ */ new Set([
3802
3940
  function shouldCommitAfterWebIdeMessage(action) {
3803
3941
  return WEBIDE_CODE_CHANGE_ACTIONS.has(action);
3804
3942
  }
3943
+ function syncLocalTaskStatus(workdir, taskId, msg, status) {
3944
+ syncWebIdeTaskState(workdir, taskId, {
3945
+ messageId: msg.messageId,
3946
+ action: msg.action,
3947
+ status
3948
+ });
3949
+ }
3805
3950
  async function updateStatus(cfg, messageId, status) {
3806
3951
  const api = createApmApiClient(cfg);
3807
3952
  await api.cli.webideUpdateMessageStatus({ id: messageId, status });
@@ -3823,6 +3968,7 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
3823
3968
  `[apm] webide-message action=${msg.action} taskId=${taskId} messageId=${messageId}`
3824
3969
  );
3825
3970
  await updateStatus(cfg, messageId, "TYPING");
3971
+ syncLocalTaskStatus(workdir, taskId, msg, "TYPING");
3826
3972
  const eventSession = new EventSession(msg.content);
3827
3973
  const prepAgentId = loadWebIdeAgentId(workdir, taskId) ?? "webide-cli-prep";
3828
3974
  const logSyncRef = {
@@ -4030,6 +4176,8 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
4030
4176
  }),
4031
4177
  enableWebIdePlanTools: true,
4032
4178
  enableSandbox: false,
4179
+ skipSessionAgentRegistry: true,
4180
+ onInvalidatePersistedAgentId: () => clearWebIdeAgentId(workdir, taskId),
4033
4181
  taskId,
4034
4182
  terminalRpc: options?.terminalRpc,
4035
4183
  createRemoteLogSync: (agentId) => {
@@ -4080,6 +4228,7 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
4080
4228
  tokenUsage
4081
4229
  );
4082
4230
  await setError(cfg, messageId, detail);
4231
+ syncLocalTaskStatus(workdir, taskId, msg, "FAILED");
4083
4232
  return;
4084
4233
  }
4085
4234
  if (outcome.status === "cancelled" || signal.aborted) {
@@ -4090,6 +4239,7 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
4090
4239
  tokenUsage
4091
4240
  );
4092
4241
  await updateStatus(cfg, messageId, "CANCELLED");
4242
+ syncLocalTaskStatus(workdir, taskId, msg, "CANCELLED");
4093
4243
  return;
4094
4244
  }
4095
4245
  const fallback = resolveMessageReplyFallback({
@@ -4189,9 +4339,20 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
4189
4339
  tokenUsage
4190
4340
  );
4191
4341
  await updateStatus(cfg, messageId, "SUCCESS");
4342
+ syncLocalTaskStatus(workdir, taskId, msg, "SUCCESS");
4192
4343
  console.log(
4193
4344
  `[apm] webide-message \u5B8C\u6210 action=${msg.action} messageId=${messageId} agentId=${outcome.agentId}`
4194
4345
  );
4346
+ if (msg.action === "accept") {
4347
+ try {
4348
+ await cleanWebIdeWorkspaceCache(taskId, workdir);
4349
+ } catch (err) {
4350
+ console.warn(
4351
+ "[apm] \u5408\u5E76 PR \u540E\u6E05\u7406 WebIDE agent \u7F13\u5B58\u5931\u8D25:",
4352
+ err instanceof Error ? err.message : err
4353
+ );
4354
+ }
4355
+ }
4195
4356
  } catch (err) {
4196
4357
  const detail = err instanceof Error ? err.message : String(err);
4197
4358
  console.error(`[apm] webide-message \u5931\u8D25: ${detail}`);
@@ -4206,6 +4367,7 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
4206
4367
  statusErr instanceof Error ? statusErr.message : statusErr
4207
4368
  );
4208
4369
  }
4370
+ syncLocalTaskStatus(workdir, taskId, msg, "FAILED");
4209
4371
  }
4210
4372
  }
4211
4373
 
@@ -4225,12 +4387,12 @@ function installTerminalRpcListener() {
4225
4387
  }
4226
4388
  function call(op, args) {
4227
4389
  const id = nextId++;
4228
- return new Promise((resolve6, reject) => {
4390
+ return new Promise((resolve7, reject) => {
4229
4391
  if (!parentPort) {
4230
4392
  reject(new Error("parentPort \u4E0D\u53EF\u7528"));
4231
4393
  return;
4232
4394
  }
4233
- pending.set(id, { resolve: resolve6, reject });
4395
+ pending.set(id, { resolve: resolve7, reject });
4234
4396
  parentPort.postMessage({
4235
4397
  type: "terminal-rpc",
4236
4398
  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.33",
4
4
  "description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
5
5
  "type": "module",
6
6
  "private": false,