ai-project-manage-cli 8.0.4 → 8.0.7

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
@@ -1119,6 +1119,21 @@ function validateCancel(o) {
1119
1119
  data: { type: "cancel", messageId: o.messageId.trim() }
1120
1120
  };
1121
1121
  }
1122
+ function validateWebIdeStopTerminals(o) {
1123
+ if (o.type !== "webide-stop-terminals") {
1124
+ return { ok: false, reason: "\u671F\u671B webide-stop-terminals" };
1125
+ }
1126
+ if (!nonEmptyString(o.taskId)) {
1127
+ return { ok: false, reason: "webide-stop-terminals \u7F3A\u5C11 taskId" };
1128
+ }
1129
+ return {
1130
+ ok: true,
1131
+ data: {
1132
+ type: "webide-stop-terminals",
1133
+ taskId: o.taskId.trim()
1134
+ }
1135
+ };
1136
+ }
1122
1137
  function validateWebIdeStartProjectPush(o) {
1123
1138
  if (o.type !== "webide-start-project") {
1124
1139
  return { ok: false, reason: "\u671F\u671B webide-start-project" };
@@ -1169,7 +1184,7 @@ function validateAgentWsMessage(value, kind) {
1169
1184
  }
1170
1185
  const o = value;
1171
1186
  const type = o.type;
1172
- if (type !== "heartbeat" && type !== "webide-message" && type !== "cancel" && type !== "webide-start-project") {
1187
+ if (type !== "heartbeat" && type !== "webide-message" && type !== "cancel" && type !== "webide-start-project" && type !== "webide-stop-terminals") {
1173
1188
  return { ok: false, reason: `\u672A\u77E5 type: ${String(type)}` };
1174
1189
  }
1175
1190
  if (kind === "heartbeat" || type === "heartbeat") {
@@ -1181,6 +1196,9 @@ function validateAgentWsMessage(value, kind) {
1181
1196
  if (type === "webide-start-project") {
1182
1197
  return validateWebIdeStartProjectPush(o);
1183
1198
  }
1199
+ if (type === "webide-stop-terminals") {
1200
+ return validateWebIdeStopTerminals(o);
1201
+ }
1184
1202
  return validateWebIdeMessagePush(o);
1185
1203
  }
1186
1204
 
@@ -1382,6 +1400,102 @@ function handleStartProjectAction(payload, ctx) {
1382
1400
  trackActionTask(ctx, task);
1383
1401
  }
1384
1402
 
1403
+ // src/commands/connect/pty-session-mcp.ts
1404
+ init_config();
1405
+ var DEFAULT_PTY_SESSION_PORT = "7788";
1406
+ function resolvePtySessionBaseUrl(env = process.env) {
1407
+ const explicit = env.LOCAL_TERMINAL_URL?.trim();
1408
+ if (explicit) return explicit.replace(/\/+$/, "");
1409
+ const port = env.LOCAL_TERMINAL_PORT?.trim() || DEFAULT_PTY_SESSION_PORT;
1410
+ return `http://127.0.0.1:${port}`;
1411
+ }
1412
+ function asSessionList(data) {
1413
+ if (Array.isArray(data)) return data;
1414
+ if (data && typeof data === "object") {
1415
+ const sessions = data.sessions;
1416
+ if (Array.isArray(sessions)) return sessions;
1417
+ }
1418
+ return [];
1419
+ }
1420
+ async function requestPtyJson(url, token, init) {
1421
+ const res = await fetch(url, {
1422
+ ...init,
1423
+ headers: {
1424
+ Authorization: `Bearer ${token}`,
1425
+ ...init?.headers ?? {}
1426
+ }
1427
+ });
1428
+ if (!res.ok) {
1429
+ throw new Error(`pty-session HTTP ${res.status}`);
1430
+ }
1431
+ try {
1432
+ return await res.json();
1433
+ } catch {
1434
+ return null;
1435
+ }
1436
+ }
1437
+ async function killAllPtySessions(cfg) {
1438
+ const token = resolveApiKey(cfg);
1439
+ if (!token) return 0;
1440
+ const baseUrl = resolvePtySessionBaseUrl();
1441
+ let body;
1442
+ try {
1443
+ body = await requestPtyJson(`${baseUrl}/sessions`, token);
1444
+ } catch (err) {
1445
+ console.warn(
1446
+ "[apm] \u5217\u51FA PTY \u4F1A\u8BDD\u5931\u8D25:",
1447
+ err instanceof Error ? err.message : err
1448
+ );
1449
+ return 0;
1450
+ }
1451
+ const payload = body;
1452
+ if (payload && typeof payload === "object" && payload.ok === false) {
1453
+ return 0;
1454
+ }
1455
+ const data = payload && typeof payload === "object" && "data" in payload ? payload.data : body;
1456
+ const sessions = asSessionList(data);
1457
+ let killed = 0;
1458
+ for (const session of sessions) {
1459
+ const id = session.id?.trim();
1460
+ if (!id) continue;
1461
+ if (session.running === false) continue;
1462
+ try {
1463
+ await requestPtyJson(
1464
+ `${baseUrl}/sessions/${encodeURIComponent(id)}`,
1465
+ token,
1466
+ {
1467
+ method: "DELETE"
1468
+ }
1469
+ );
1470
+ killed += 1;
1471
+ } catch (err) {
1472
+ console.warn(
1473
+ `[apm] \u7ED3\u675F PTY \u4F1A\u8BDD\u5931\u8D25 id=${id}:`,
1474
+ err instanceof Error ? err.message : err
1475
+ );
1476
+ }
1477
+ }
1478
+ return killed;
1479
+ }
1480
+
1481
+ // src/commands/connect/actions/stop-terminals.ts
1482
+ function handleStopTerminalsAction(payload, ctx) {
1483
+ const task = (async () => {
1484
+ try {
1485
+ const killed = await killAllPtySessions(ctx.cfg);
1486
+ console.log(
1487
+ `[apm] webide-stop-terminals taskId=${payload.taskId} killed=${killed}`
1488
+ );
1489
+ } catch (err) {
1490
+ console.warn(
1491
+ `[apm] webide-stop-terminals \u5931\u8D25 taskId=${payload.taskId}:`,
1492
+ err instanceof Error ? err.message : err
1493
+ );
1494
+ }
1495
+ })();
1496
+ trackActionTask(ctx, task);
1497
+ }
1498
+
1385
1499
  // src/commands/connect/actions/index.ts
1386
1500
  function dispatchConnectAction(payload, ctx) {
1387
1501
  switch (payload.type) {
@@ -1396,6 +1510,9 @@ function dispatchConnectAction(payload, ctx) {
1396
1510
  case "webide-start-project":
1397
1511
  handleStartProjectAction(payload, ctx);
1398
1512
  return;
1513
+ case "webide-stop-terminals":
1514
+ handleStopTerminalsAction(payload, ctx);
1515
+ return;
1399
1516
  }
1400
1517
  }
1401
1518
 
@@ -2921,6 +2921,9 @@ var WEBIDE_CODE_CHANGE_ACTIONS = /* @__PURE__ */ new Set([
2921
2921
  function shouldCommitAfterWebIdeMessage(action) {
2922
2922
  return WEBIDE_CODE_CHANGE_ACTIONS.has(action);
2923
2923
  }
2924
+ function isStartLocalServicesAction(action) {
2925
+ return action === "enter-manual-test" || action === "skip-test";
2926
+ }
2924
2927
  function syncLocalTaskStatus(workdir, taskId, msg, status) {
2925
2928
  syncWebIdeTaskState(workdir, taskId, {
2926
2929
  messageId: msg.messageId,
@@ -3129,7 +3132,8 @@ async function handleWebIdeInboundMessage(cfg, msg, signal) {
3129
3132
  "running"
3130
3133
  );
3131
3134
  await syncPrepLog();
3132
- const savedAgentId = loadWebIdeAgentId(workdir, taskId);
3135
+ const startLocalServices = isStartLocalServicesAction(msg.action);
3136
+ const savedAgentId = startLocalServices ? void 0 : loadWebIdeAgentId(workdir, taskId);
3133
3137
  const outcome = await runCursorAgent(
3134
3138
  cfg,
3135
3139
  {
@@ -3146,17 +3150,21 @@ async function handleWebIdeInboundMessage(cfg, msg, signal) {
3146
3150
  forceSend: true,
3147
3151
  eventSession,
3148
3152
  appendMessageContent: (content) => appendContent(cfg, messageId, content),
3149
- askQuestionExecute: createWebIdeAskQuestionExecute({
3153
+ askQuestionExecute: startLocalServices ? void 0 : createWebIdeAskQuestionExecute({
3150
3154
  cfg,
3151
3155
  taskId,
3152
3156
  signal
3153
3157
  }),
3154
- enableWebIdePlanTools: true,
3158
+ enableAskQuestion: !startLocalServices,
3159
+ enableWebIdePlanTools: !startLocalServices,
3160
+ enablePtySessionMcp: startLocalServices,
3155
3161
  enableSandbox: false,
3156
- onInvalidatePersistedAgentId: () => clearWebIdeAgentId(workdir, taskId),
3162
+ onInvalidatePersistedAgentId: startLocalServices ? void 0 : () => clearWebIdeAgentId(workdir, taskId),
3157
3163
  taskId,
3158
3164
  createRemoteLogSync: (agentId) => {
3159
- saveWebIdeAgentId(workdir, taskId, agentId);
3165
+ if (!startLocalServices) {
3166
+ saveWebIdeAgentId(workdir, taskId, agentId);
3167
+ }
3160
3168
  logSyncRef.current = createThrottledWebIdeMessageLogSync(
3161
3169
  cfg,
3162
3170
  { taskId, messageId, agentId },
@@ -3170,7 +3178,9 @@ async function handleWebIdeInboundMessage(cfg, msg, signal) {
3170
3178
  return logSyncRef.current;
3171
3179
  },
3172
3180
  onRunStarted: async ({ agentId, runId }) => {
3173
- saveWebIdeAgentId(workdir, taskId, agentId);
3181
+ if (!startLocalServices) {
3182
+ saveWebIdeAgentId(workdir, taskId, agentId);
3183
+ }
3174
3184
  eventSession.addCliStep(
3175
3185
  "\u542F\u52A8 Cursor Agent",
3176
3186
  `agentId=${agentId} runId=${runId}`,
@@ -3182,7 +3192,9 @@ async function handleWebIdeInboundMessage(cfg, msg, signal) {
3182
3192
  }
3183
3193
  }
3184
3194
  );
3185
- saveWebIdeAgentId(workdir, taskId, outcome.agentId);
3195
+ if (!startLocalServices) {
3196
+ saveWebIdeAgentId(workdir, taskId, outcome.agentId);
3197
+ }
3186
3198
  const tokenUsage = outcome.usage != null ? {
3187
3199
  modelId: outcome.modelId ?? (msg.model?.trim() || void 0),
3188
3200
  inputTokens: outcome.usage.inputTokens,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-project-manage-cli",
3
- "version": "8.0.4",
3
+ "version": "8.0.7",
4
4
  "description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
5
5
  "type": "module",
6
6
  "private": false,
@@ -4,7 +4,7 @@
4
4
 
5
5
  会话与 WebIDE「终端」面板共用同一套 `pty-session-mcp serve`。工具名:`create` / `write` / `read` / `resize` / `kill` / `list`。
6
6
 
7
- 本地服务由用户在顶栏「启动项目」触发(独立 Agent),**开发/修码任务不要自行起或重启长驻进程**。
7
+ 本地服务由用户在顶栏「启动项目」触发,或在进入手工测试时由启动指令触发(独立 Agent)。**开发/修码任务不要自行起或重启长驻进程**。
8
8
 
9
9
  ## 工具
10
10
 
@@ -26,14 +26,15 @@
26
26
  3. 按文档中的 cwd / command / 服务划分创建会话。
27
27
  4. 文档不存在或未写明启动方式 → 结束任务,**禁止猜测命令**。
28
28
 
29
- ## 时机(顶栏启动 / 重启)
29
+ ## 时机(顶栏启动 / 重启 / 进入手工测试)
30
30
 
31
31
  固定顺序:
32
32
 
33
33
  1. **`list`**
34
- 2. 启动:无对应 `name` 的活会话 → 按部署文档 `create` + `write`;已有同名活会话则结束,不重起
35
- 3. 重启:先 `kill` 目标 `name`,再按部署文档 `create` + `write`(不要杀掉其它端)
36
- 4. `read` 等待就绪日志 / 端口即可
34
+ 2. 顶栏启动:无对应 `name` 的活会话 → 按部署文档 `create` + `write`;已有同名活会话则结束,不重起
35
+ 3. 顶栏重启:先 `kill` 目标 `name`,再按部署文档 `create` + `write`(不要杀掉其它端)
36
+ 4. 进入手工测试:文档写明的每一端,无对应 `name` 则启动;**已有同名活会话则重启**(只 kill 该 name,再 `create` + `write`)
37
+ 5. `read` 等待就绪日志 / 端口即可
37
38
 
38
39
  用户在「终端」面板看日志,在「浏览器」面板看客户机外网地址。
39
40