ai-project-manage-cli 8.0.2 → 8.0.4
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 +4 -18
- package/dist/index.js +467 -5215
- package/dist/webide-message-worker.js +372 -539
- package/package.json +3 -10
- package/template/AGENTS.webide.md +3 -3
- package/template/rules/webide_terminal.md +25 -18
- package/template/skills/webide-run-retro/SKILL.md +103 -0
- package/template/skills/webide-run-retro/attribution.md +26 -0
- package/template/skills/webide-run-retro/scripts/inspect-run.mjs +335 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/commands/connect/webide-message-worker.ts
|
|
2
|
-
import { parentPort
|
|
2
|
+
import { parentPort, workerData } from "node:worker_threads";
|
|
3
3
|
|
|
4
4
|
// src/api/client.ts
|
|
5
5
|
import { createApiClient } from "listpage-http";
|
|
@@ -58,32 +58,12 @@ var requestConfig = {
|
|
|
58
58
|
method: "PUT",
|
|
59
59
|
path: "/cli/webide/test-cases"
|
|
60
60
|
}),
|
|
61
|
-
webideListTerminals: defineEndpoint({
|
|
62
|
-
method: "GET",
|
|
63
|
-
path: "/cli/webide/terminals"
|
|
64
|
-
}),
|
|
65
|
-
webideUpsertTerminal: defineEndpoint({
|
|
66
|
-
method: "POST",
|
|
67
|
-
path: "/cli/webide/terminals"
|
|
68
|
-
}),
|
|
69
|
-
webideUpdateTerminalStatus: defineEndpoint({
|
|
70
|
-
method: "PUT",
|
|
71
|
-
path: "/cli/webide/terminals/status"
|
|
72
|
-
}),
|
|
73
|
-
webideUpdateTerminalMeta: defineEndpoint({
|
|
74
|
-
method: "PUT",
|
|
75
|
-
path: "/cli/webide/terminals/meta"
|
|
76
|
-
}),
|
|
77
61
|
projectBaseBranch: defineEndpoint(
|
|
78
62
|
{
|
|
79
63
|
method: "GET",
|
|
80
64
|
path: "/cli/tasks/project-base-branch"
|
|
81
65
|
}
|
|
82
66
|
),
|
|
83
|
-
workspaceBaseline: defineEndpoint({
|
|
84
|
-
method: "GET",
|
|
85
|
-
path: "/cli/workspaces/baseline"
|
|
86
|
-
}),
|
|
87
67
|
createWebIdeDraftPullRequest: defineEndpoint({
|
|
88
68
|
method: "POST",
|
|
89
69
|
path: "/cli/webide/draft-pull-requests"
|
|
@@ -108,12 +88,6 @@ var requestConfig = {
|
|
|
108
88
|
method: "DELETE",
|
|
109
89
|
path: "/cli/project-documents"
|
|
110
90
|
}),
|
|
111
|
-
getDeployArtifactStorage: defineEndpoint(
|
|
112
|
-
{
|
|
113
|
-
method: "GET",
|
|
114
|
-
path: "/cli/deploy-artifact-storage"
|
|
115
|
-
}
|
|
116
|
-
),
|
|
117
91
|
getApmLogStorage: defineEndpoint({
|
|
118
92
|
method: "GET",
|
|
119
93
|
path: "/cli/apm-log-storage"
|
|
@@ -197,7 +171,7 @@ function createApmApiClient(cfg) {
|
|
|
197
171
|
});
|
|
198
172
|
}
|
|
199
173
|
|
|
200
|
-
// src/
|
|
174
|
+
// src/utils/path.ts
|
|
201
175
|
import { realpathSync } from "fs";
|
|
202
176
|
import { platform } from "os";
|
|
203
177
|
import { resolve } from "path";
|
|
@@ -244,7 +218,7 @@ function requireRemoteWorkdir(workdir) {
|
|
|
244
218
|
return resolveWorkdirPath(trimmed);
|
|
245
219
|
}
|
|
246
220
|
|
|
247
|
-
// src/
|
|
221
|
+
// src/utils/workspace.ts
|
|
248
222
|
import { copyFileSync, existsSync, mkdirSync as mkdirSync2, readdirSync, statSync } from "fs";
|
|
249
223
|
import { dirname, join as join2, resolve as resolve2 } from "path";
|
|
250
224
|
import { fileURLToPath } from "url";
|
|
@@ -381,11 +355,11 @@ async function copyWebIdeTemplateFiles(targetDir, workdir = resolveWorkdirPath()
|
|
|
381
355
|
assertWebIdeTemplateCopiedToApm(resolvedTarget, resolve2(workdir));
|
|
382
356
|
}
|
|
383
357
|
|
|
384
|
-
// src/git
|
|
358
|
+
// src/utils/git.ts
|
|
385
359
|
import { execFile as execFile2 } from "child_process";
|
|
386
360
|
import { promisify as promisify2 } from "util";
|
|
387
361
|
|
|
388
|
-
// src/git-remote.ts
|
|
362
|
+
// src/utils/git-remote.ts
|
|
389
363
|
import { execFile } from "child_process";
|
|
390
364
|
import { promisify } from "util";
|
|
391
365
|
var execFileAsync = promisify(execFile);
|
|
@@ -438,7 +412,7 @@ async function tryReadHttpsGitOriginUrl(cwd) {
|
|
|
438
412
|
return toHttpsGitRemoteUrl(raw);
|
|
439
413
|
}
|
|
440
414
|
|
|
441
|
-
// src/git
|
|
415
|
+
// src/utils/git.ts
|
|
442
416
|
var execFileAsync2 = promisify2(execFile2);
|
|
443
417
|
function gitExecEnv() {
|
|
444
418
|
const existing = process.env.GIT_SSH_COMMAND?.trim();
|
|
@@ -536,10 +510,16 @@ async function branchHasChangesAgainstBase(cwd, baseBranch, headBranch) {
|
|
|
536
510
|
return true;
|
|
537
511
|
}
|
|
538
512
|
}
|
|
539
|
-
var FEATURE_BRANCH_RE = /^feat\/task
|
|
513
|
+
var FEATURE_BRANCH_RE = /^feat\/task-(.+)$/;
|
|
540
514
|
function isFeatureBranchName(branch) {
|
|
541
515
|
return FEATURE_BRANCH_RE.test(branch.trim().replace(/^origin\//, ""));
|
|
542
516
|
}
|
|
517
|
+
function taskIdFromFeatureBranch(branch) {
|
|
518
|
+
const name = branch.trim().replace(/^origin\//, "");
|
|
519
|
+
const match = name.match(FEATURE_BRANCH_RE);
|
|
520
|
+
const id = match?.[1]?.trim() ?? "";
|
|
521
|
+
return id || null;
|
|
522
|
+
}
|
|
543
523
|
async function hasUpstream(cwd) {
|
|
544
524
|
try {
|
|
545
525
|
await execGit(cwd, ["rev-parse", "--abbrev-ref", "@{upstream}"], true);
|
|
@@ -565,7 +545,7 @@ async function pushCurrentBranch(cwd) {
|
|
|
565
545
|
return true;
|
|
566
546
|
}
|
|
567
547
|
|
|
568
|
-
// src/workspace-repos.ts
|
|
548
|
+
// src/utils/workspace-repos.ts
|
|
569
549
|
import {
|
|
570
550
|
existsSync as existsSync2,
|
|
571
551
|
mkdirSync as mkdirSync3,
|
|
@@ -947,7 +927,12 @@ async function runTaskBranch(taskId, options = {}) {
|
|
|
947
927
|
cwd: gitRoot
|
|
948
928
|
});
|
|
949
929
|
}
|
|
950
|
-
return {
|
|
930
|
+
return {
|
|
931
|
+
branch,
|
|
932
|
+
baseline: baselineBranch,
|
|
933
|
+
kind: manifest.kind,
|
|
934
|
+
repos: repoRoots
|
|
935
|
+
};
|
|
951
936
|
}
|
|
952
937
|
|
|
953
938
|
// src/commands/connect/cursor-agent.ts
|
|
@@ -955,9 +940,8 @@ import {
|
|
|
955
940
|
Agent,
|
|
956
941
|
CursorAgentError
|
|
957
942
|
} from "@cursor/sdk";
|
|
958
|
-
import { setMaxListeners as setMaxListeners2 } from "node:events";
|
|
959
943
|
|
|
960
|
-
// src/plan-format.ts
|
|
944
|
+
// src/utils/plan-format.ts
|
|
961
945
|
function formatPlanMarkdown(raw) {
|
|
962
946
|
let text = raw.trim();
|
|
963
947
|
if (!text) {
|
|
@@ -978,7 +962,7 @@ function formatPlanMarkdown(raw) {
|
|
|
978
962
|
return text.replace(/\r\n/g, "\n").trimEnd() + "\n";
|
|
979
963
|
}
|
|
980
964
|
|
|
981
|
-
// src/session
|
|
965
|
+
// src/utils/session.ts
|
|
982
966
|
function serializeTokenUsage(usage) {
|
|
983
967
|
return {
|
|
984
968
|
inputTokens: usage.inputTokens,
|
|
@@ -1260,71 +1244,9 @@ ${JSON.stringify(event, null, 2)}
|
|
|
1260
1244
|
\`\`\``;
|
|
1261
1245
|
}
|
|
1262
1246
|
|
|
1263
|
-
// src/commands/connect/
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
getMaxListeners,
|
|
1267
|
-
setMaxListeners
|
|
1268
|
-
} from "node:events";
|
|
1269
|
-
function isAbortSignalDebugEnabled() {
|
|
1270
|
-
const v = process.env.APM_DEBUG_ABORT_SIGNAL?.trim().toLowerCase();
|
|
1271
|
-
return v === "1" || v === "true" || v === "yes";
|
|
1272
|
-
}
|
|
1273
|
-
function formatAbortSignalStats(signal, label) {
|
|
1274
|
-
if (!signal) {
|
|
1275
|
-
return `[apm:abort-debug] ${label}: (no signal)`;
|
|
1276
|
-
}
|
|
1277
|
-
const listeners = getEventListeners(signal, "abort");
|
|
1278
|
-
const max = getMaxListeners(signal);
|
|
1279
|
-
return `[apm:abort-debug] ${label}: abortListeners=${listeners.length} maxListeners=${max} aborted=${signal.aborted}`;
|
|
1280
|
-
}
|
|
1281
|
-
function logAbortSignalStats(signal, label) {
|
|
1282
|
-
if (!isAbortSignalDebugEnabled()) return;
|
|
1283
|
-
console.log(formatAbortSignalStats(signal, label));
|
|
1284
|
-
}
|
|
1285
|
-
var installed = false;
|
|
1286
|
-
function installAbortSignalDebug() {
|
|
1287
|
-
if (!isAbortSignalDebugEnabled() || installed) return;
|
|
1288
|
-
installed = true;
|
|
1289
|
-
const maxFromEnv = Number.parseInt(
|
|
1290
|
-
process.env.APM_ABORT_SIGNAL_MAX_LISTENERS ?? "",
|
|
1291
|
-
10
|
|
1292
|
-
);
|
|
1293
|
-
if (Number.isFinite(maxFromEnv) && maxFromEnv > 0) {
|
|
1294
|
-
setMaxListeners(maxFromEnv);
|
|
1295
|
-
console.log(
|
|
1296
|
-
`[apm:abort-debug] setMaxListeners(${maxFromEnv}) via APM_ABORT_SIGNAL_MAX_LISTENERS`
|
|
1297
|
-
);
|
|
1298
|
-
}
|
|
1299
|
-
process.on("warning", (warning) => {
|
|
1300
|
-
if (warning.name !== "MaxListenersExceededWarning") return;
|
|
1301
|
-
console.warn(`[apm:abort-debug] ${warning.name}: ${warning.message}`);
|
|
1302
|
-
if (warning.stack) {
|
|
1303
|
-
console.warn(warning.stack);
|
|
1304
|
-
}
|
|
1305
|
-
});
|
|
1306
|
-
const proto = AbortSignal.prototype;
|
|
1307
|
-
const original = proto.addEventListener;
|
|
1308
|
-
proto.addEventListener = function(type, listener, options) {
|
|
1309
|
-
if (type === "abort") {
|
|
1310
|
-
const sig = this;
|
|
1311
|
-
const before = getEventListeners(sig, "abort").length;
|
|
1312
|
-
const max = getMaxListeners(sig);
|
|
1313
|
-
const stack = new Error("[apm:abort-debug] addEventListener stack").stack?.split("\n").slice(2, 8).join("\n") ?? "";
|
|
1314
|
-
console.log(
|
|
1315
|
-
`[apm:abort-debug] addEventListener("abort") before=${before} max=${max}
|
|
1316
|
-
${stack}`
|
|
1317
|
-
);
|
|
1318
|
-
}
|
|
1319
|
-
return original.call(this, type, listener, options);
|
|
1320
|
-
};
|
|
1321
|
-
console.log(
|
|
1322
|
-
"[apm:abort-debug] \u5DF2\u542F\u7528 AbortSignal \u8C03\u8BD5\uFF08APM_DEBUG_ABORT_SIGNAL\uFF09"
|
|
1323
|
-
);
|
|
1324
|
-
}
|
|
1325
|
-
|
|
1326
|
-
// src/commands/connect/append-message-tool.ts
|
|
1327
|
-
function createAppendMessageCustomTools(messageId, appendContent2) {
|
|
1247
|
+
// src/commands/connect/tools/append-message-tool.ts
|
|
1248
|
+
function createAppendMessageCustomTools(options) {
|
|
1249
|
+
const { appendContent: appendContent2 } = options;
|
|
1328
1250
|
return {
|
|
1329
1251
|
AppendMessage: {
|
|
1330
1252
|
description: "\u5411\u5F53\u524D WebIDE \u6D88\u606F\u8FFD\u52A0\u7528\u6237\u53EF\u89C1\u56DE\u590D\u3002\u53EF\u591A\u6B21\u8C03\u7528\u8865\u5145\u8FDB\u5C55\uFF1B\u88AB @ \u65F6\u6536\u5230\u540E\u5E94\u5148\u7B80\u77ED\u786E\u8BA4\u518D\u6267\u884C\u4EFB\u52A1\u3002\u7ED3\u8BBA\u4E0E\u8FDB\u5C55\u5E94\u5199\u5728\u672C\u5DE5\u5177\u4E2D\uFF1B\u6700\u7EC8\u81EA\u7136\u8BED\u8A00\u6536\u5C3E\u52FF\u518D\u590D\u8FF0\u6B64\u5904\u5185\u5BB9\u3002",
|
|
@@ -1348,7 +1270,6 @@ function createAppendMessageCustomTools(messageId, appendContent2) {
|
|
|
1348
1270
|
}
|
|
1349
1271
|
try {
|
|
1350
1272
|
await appendContent2(content);
|
|
1351
|
-
console.log(`[apm] AppendMessage \u5DF2\u8FFD\u52A0: messageId=${messageId}`);
|
|
1352
1273
|
return "\u5DF2\u8FFD\u52A0\u6D88\u606F\u5185\u5BB9";
|
|
1353
1274
|
} catch (err) {
|
|
1354
1275
|
const detail = err instanceof Error ? err.message : String(err);
|
|
@@ -1362,7 +1283,7 @@ function createAppendMessageCustomTools(messageId, appendContent2) {
|
|
|
1362
1283
|
};
|
|
1363
1284
|
}
|
|
1364
1285
|
|
|
1365
|
-
// src/commands/connect/ask-question-tool.ts
|
|
1286
|
+
// src/commands/connect/tools/ask-question-tool.ts
|
|
1366
1287
|
var INPUT_SCHEMA = {
|
|
1367
1288
|
type: "object",
|
|
1368
1289
|
properties: {
|
|
@@ -1400,121 +1321,118 @@ var INPUT_SCHEMA = {
|
|
|
1400
1321
|
};
|
|
1401
1322
|
function createAskQuestionTool(options) {
|
|
1402
1323
|
return {
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1324
|
+
AskQuestion: {
|
|
1325
|
+
description: "Collect structured multiple-choice answers from the user. Use when blocked on a decision that is genuinely the user's to make.",
|
|
1326
|
+
inputSchema: INPUT_SCHEMA,
|
|
1327
|
+
execute: async (args) => {
|
|
1328
|
+
options?.onInvoke?.(args);
|
|
1329
|
+
if (options?.execute) {
|
|
1330
|
+
return options.execute(args);
|
|
1331
|
+
}
|
|
1332
|
+
const payload = JSON.stringify(args, null, 2);
|
|
1333
|
+
console.log(`[apm] AskQuestion\uFF08\u672C\u5730\u5360\u4F4D\uFF0C\u672A\u7B49\u5F85\u7528\u6237\uFF09:
|
|
1412
1334
|
${payload}`);
|
|
1413
|
-
|
|
1335
|
+
return `[local] AskQuestion \u5DF2\u8BB0\u5F55\uFF08\u672A\u521B\u5EFA\u4EFB\u52A1\u95EE\u9898\u3001\u672A\u7B49\u5F85\u7528\u6237\u56DE\u7B54\uFF09\u3002\u53C2\u6570:
|
|
1414
1336
|
${payload}`;
|
|
1337
|
+
}
|
|
1415
1338
|
}
|
|
1416
1339
|
};
|
|
1417
1340
|
}
|
|
1418
1341
|
|
|
1419
|
-
// src/commands/connect/webide-merge-tools.ts
|
|
1342
|
+
// src/commands/connect/tools/webide-merge-tools.ts
|
|
1420
1343
|
function createMergeWebIdePullRequestsTool(options) {
|
|
1421
|
-
const {
|
|
1344
|
+
const { webideMergePullRequests } = options;
|
|
1422
1345
|
return {
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1346
|
+
MergeWebIdePullRequests: {
|
|
1347
|
+
description: "Merge all open pull requests for this WebIDE task. Call once with confirmedConflictFree=true. No baseline sync or review is required before calling.",
|
|
1348
|
+
inputSchema: {
|
|
1349
|
+
type: "object",
|
|
1350
|
+
properties: {
|
|
1351
|
+
confirmedConflictFree: {
|
|
1352
|
+
type: "boolean",
|
|
1353
|
+
description: "Must be true to confirm you intend to merge this task's open PRs now."
|
|
1354
|
+
}
|
|
1355
|
+
},
|
|
1356
|
+
required: ["confirmedConflictFree"]
|
|
1431
1357
|
},
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
);
|
|
1358
|
+
execute: async (args) => {
|
|
1359
|
+
if (args.confirmedConflictFree !== true) {
|
|
1360
|
+
throw new Error(
|
|
1361
|
+
"MergeWebIdePullRequests \u8981\u6C42 confirmedConflictFree=true\u3002"
|
|
1362
|
+
);
|
|
1363
|
+
}
|
|
1364
|
+
const result = await webideMergePullRequests();
|
|
1365
|
+
return JSON.stringify(result, null, 2);
|
|
1439
1366
|
}
|
|
1440
|
-
const api = createApmApiClient(cfg);
|
|
1441
|
-
console.log(`[apm] MergeWebIdePullRequests taskId=${taskId}`);
|
|
1442
|
-
const result = await api.cli.webideMergePullRequests({ taskId });
|
|
1443
|
-
return JSON.stringify(result, null, 2);
|
|
1444
1367
|
}
|
|
1445
1368
|
};
|
|
1446
1369
|
}
|
|
1447
1370
|
|
|
1448
|
-
// src/commands/connect/webide-plan-tools.ts
|
|
1371
|
+
// src/commands/connect/tools/webide-plan-tools.ts
|
|
1449
1372
|
function asString(value) {
|
|
1450
1373
|
return typeof value === "string" ? value.trim() : "";
|
|
1451
1374
|
}
|
|
1452
1375
|
function createRecommendPlanTool(options) {
|
|
1453
|
-
const {
|
|
1376
|
+
const { webideRecommendPlan } = options;
|
|
1454
1377
|
return {
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1378
|
+
RecommendPlan: {
|
|
1379
|
+
description: "Recommend whether the human should write an implementation plan or skip it. Does not change workflow phase; the human decides in the WebIDE UI.",
|
|
1380
|
+
inputSchema: {
|
|
1381
|
+
type: "object",
|
|
1382
|
+
properties: {
|
|
1383
|
+
recommendation: {
|
|
1384
|
+
type: "string",
|
|
1385
|
+
enum: ["write", "skip", "unknown"],
|
|
1386
|
+
description: "write = suggest generating a plan; skip = plan optional/unnecessary; unknown = unsure"
|
|
1387
|
+
},
|
|
1388
|
+
reason: {
|
|
1389
|
+
type: "string",
|
|
1390
|
+
description: "Short Chinese reason for the recommendation"
|
|
1391
|
+
}
|
|
1463
1392
|
},
|
|
1464
|
-
|
|
1465
|
-
type: "string",
|
|
1466
|
-
description: "Short Chinese reason for the recommendation"
|
|
1467
|
-
}
|
|
1393
|
+
required: ["recommendation"]
|
|
1468
1394
|
},
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
reason
|
|
1480
|
-
});
|
|
1481
|
-
console.log(
|
|
1482
|
-
`[apm] RecommendPlan taskId=${taskId} recommendation=${recommendation}`
|
|
1483
|
-
);
|
|
1484
|
-
return JSON.stringify({ ok: true, recommendation, reason }, null, 2);
|
|
1395
|
+
execute: async (args) => {
|
|
1396
|
+
const raw = asString(args.recommendation);
|
|
1397
|
+
const recommendation = raw === "write" || raw === "skip" || raw === "unknown" ? raw : "unknown";
|
|
1398
|
+
const reason = asString(args.reason) || void 0;
|
|
1399
|
+
await webideRecommendPlan({
|
|
1400
|
+
recommendation,
|
|
1401
|
+
reason
|
|
1402
|
+
});
|
|
1403
|
+
return JSON.stringify({ ok: true, recommendation, reason }, null, 2);
|
|
1404
|
+
}
|
|
1485
1405
|
}
|
|
1486
1406
|
};
|
|
1487
1407
|
}
|
|
1488
1408
|
function createUpsertWebIdePlanTool(options) {
|
|
1489
|
-
const {
|
|
1409
|
+
const { webideUpsertPlan } = options;
|
|
1490
1410
|
return {
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1411
|
+
UpsertWebIdePlan: {
|
|
1412
|
+
description: "Persist or replace the implementation plan markdown for this WebIDE task and mark it ready for human confirmation. Call with the full plan document (not a diff). Use after generating or revising the plan.",
|
|
1413
|
+
inputSchema: {
|
|
1414
|
+
type: "object",
|
|
1415
|
+
properties: {
|
|
1416
|
+
content: {
|
|
1417
|
+
type: "string",
|
|
1418
|
+
description: "Full implementation plan in Markdown (Chinese)"
|
|
1419
|
+
}
|
|
1420
|
+
},
|
|
1421
|
+
required: ["content"]
|
|
1499
1422
|
},
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1423
|
+
execute: async (args) => {
|
|
1424
|
+
const content = asString(args.content);
|
|
1425
|
+
if (!content) {
|
|
1426
|
+
throw new Error("UpsertWebIdePlan \u7F3A\u5C11 content");
|
|
1427
|
+
}
|
|
1428
|
+
await webideUpsertPlan({ content });
|
|
1429
|
+
return JSON.stringify({ ok: true, chars: content.length }, null, 2);
|
|
1506
1430
|
}
|
|
1507
|
-
const api = createApmApiClient(cfg);
|
|
1508
|
-
await api.cli.webideUpsertPlan({ taskId, content });
|
|
1509
|
-
console.log(
|
|
1510
|
-
`[apm] UpsertWebIdePlan taskId=${taskId} chars=${content.length}`
|
|
1511
|
-
);
|
|
1512
|
-
return JSON.stringify({ ok: true, chars: content.length }, null, 2);
|
|
1513
1431
|
}
|
|
1514
1432
|
};
|
|
1515
1433
|
}
|
|
1516
1434
|
|
|
1517
|
-
// src/commands/connect/webide-test-case-tools.ts
|
|
1435
|
+
// src/commands/connect/tools/webide-test-case-tools.ts
|
|
1518
1436
|
function asString2(value) {
|
|
1519
1437
|
return typeof value === "string" ? value.trim() : "";
|
|
1520
1438
|
}
|
|
@@ -1542,222 +1460,62 @@ function parseCases(raw) {
|
|
|
1542
1460
|
return cases;
|
|
1543
1461
|
}
|
|
1544
1462
|
function createUpsertWebIdeTestCasesTool(options) {
|
|
1545
|
-
const {
|
|
1463
|
+
const { webideReplaceTestCases } = options;
|
|
1546
1464
|
return {
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1465
|
+
UpsertWebIdeTestCases: {
|
|
1466
|
+
description: "Persist generated automated test cases for this WebIDE task (full replace) and mark the workflow TEST_READY. Call once with the complete case list after reviewing the task and code changes.",
|
|
1467
|
+
inputSchema: {
|
|
1468
|
+
type: "object",
|
|
1469
|
+
properties: {
|
|
1470
|
+
cases: {
|
|
1471
|
+
type: "array",
|
|
1472
|
+
description: "Complete list of test cases to store on the platform",
|
|
1473
|
+
items: {
|
|
1474
|
+
type: "object",
|
|
1475
|
+
properties: {
|
|
1476
|
+
name: {
|
|
1477
|
+
type: "string",
|
|
1478
|
+
description: "Short stable case id / name"
|
|
1479
|
+
},
|
|
1480
|
+
description: {
|
|
1481
|
+
type: "string",
|
|
1482
|
+
description: "What this case verifies (Chinese ok)"
|
|
1483
|
+
},
|
|
1484
|
+
steps: {
|
|
1485
|
+
type: "array",
|
|
1486
|
+
items: { type: "string" },
|
|
1487
|
+
description: "Ordered human-readable steps"
|
|
1488
|
+
},
|
|
1489
|
+
sortOrder: {
|
|
1490
|
+
type: "number",
|
|
1491
|
+
description: "Optional display order (0-based)"
|
|
1492
|
+
}
|
|
1569
1493
|
},
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
description: "Optional display order (0-based)"
|
|
1573
|
-
}
|
|
1574
|
-
},
|
|
1575
|
-
required: ["name", "description", "steps"]
|
|
1494
|
+
required: ["name", "description", "steps"]
|
|
1495
|
+
}
|
|
1576
1496
|
}
|
|
1577
|
-
}
|
|
1578
|
-
},
|
|
1579
|
-
required: ["cases"]
|
|
1580
|
-
},
|
|
1581
|
-
execute: async (args) => {
|
|
1582
|
-
const cases = parseCases(args.cases);
|
|
1583
|
-
if (cases.length === 0) {
|
|
1584
|
-
throw new Error(
|
|
1585
|
-
"UpsertWebIdeTestCases \u7F3A\u5C11\u6709\u6548 cases\uFF08\u6BCF\u6761\u9700 name \u4E0E\u975E\u7A7A steps\uFF09"
|
|
1586
|
-
);
|
|
1587
|
-
}
|
|
1588
|
-
const api = createApmApiClient(cfg);
|
|
1589
|
-
const result = await api.cli.webideReplaceTestCases({ taskId, cases });
|
|
1590
|
-
console.log(
|
|
1591
|
-
`[apm] UpsertWebIdeTestCases taskId=${taskId} count=${cases.length}`
|
|
1592
|
-
);
|
|
1593
|
-
return JSON.stringify(
|
|
1594
|
-
{ ok: true, count: result.count ?? cases.length },
|
|
1595
|
-
null,
|
|
1596
|
-
2
|
|
1597
|
-
);
|
|
1598
|
-
}
|
|
1599
|
-
};
|
|
1600
|
-
}
|
|
1601
|
-
|
|
1602
|
-
// src/commands/connect/webide-terminal-tools.ts
|
|
1603
|
-
function asString3(value) {
|
|
1604
|
-
return typeof value === "string" ? value.trim() : "";
|
|
1605
|
-
}
|
|
1606
|
-
function asPorts(value) {
|
|
1607
|
-
if (!Array.isArray(value)) return void 0;
|
|
1608
|
-
const ports = value.map((p) => typeof p === "number" ? p : Number(p)).filter((p) => Number.isFinite(p) && p > 0 && p < 65536).map((p) => Math.trunc(p));
|
|
1609
|
-
return ports.length ? ports : void 0;
|
|
1610
|
-
}
|
|
1611
|
-
function createWebIdeTerminalTools(options) {
|
|
1612
|
-
const { cfg, taskId, workdir, rpc } = options;
|
|
1613
|
-
const StartWebIdeTerminal = {
|
|
1614
|
-
description: "Start a long-running WebIDE terminal (e.g. frontend/backend dev server). Process outlives this message. Same key already RUNNING/READY returns idempotently. Do NOT use Shell for long-lived dev servers. Returns terminalId, pid, ports.",
|
|
1615
|
-
inputSchema: {
|
|
1616
|
-
type: "object",
|
|
1617
|
-
properties: {
|
|
1618
|
-
key: {
|
|
1619
|
-
type: "string",
|
|
1620
|
-
description: "Stable key, e.g. frontend / backend"
|
|
1621
|
-
},
|
|
1622
|
-
name: { type: "string", description: "Display name" },
|
|
1623
|
-
cwd: {
|
|
1624
|
-
type: "string",
|
|
1625
|
-
description: "Working directory (absolute or under workspace)"
|
|
1626
|
-
},
|
|
1627
|
-
command: {
|
|
1628
|
-
type: "string",
|
|
1629
|
-
description: "Shell command to run, e.g. pnpm dev"
|
|
1630
1497
|
},
|
|
1631
|
-
|
|
1632
|
-
type: "string",
|
|
1633
|
-
description: "Optional regex; when matched in logs, status becomes READY"
|
|
1634
|
-
},
|
|
1635
|
-
portsHint: {
|
|
1636
|
-
type: "array",
|
|
1637
|
-
items: { type: "number" },
|
|
1638
|
-
description: "Optional expected listen ports"
|
|
1639
|
-
}
|
|
1498
|
+
required: ["cases"]
|
|
1640
1499
|
},
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
if (!key || !command) {
|
|
1648
|
-
return {
|
|
1649
|
-
content: [{ type: "text", text: "key / command \u4E0D\u80FD\u4E3A\u7A7A" }],
|
|
1650
|
-
isError: true
|
|
1651
|
-
};
|
|
1652
|
-
}
|
|
1653
|
-
try {
|
|
1654
|
-
const result = rpc ? await rpc.start({
|
|
1655
|
-
taskId,
|
|
1656
|
-
key,
|
|
1657
|
-
name: asString3(args.name) || void 0,
|
|
1658
|
-
cwd,
|
|
1659
|
-
command,
|
|
1660
|
-
readyPattern: asString3(args.readyPattern) || void 0,
|
|
1661
|
-
portsHint: asPorts(args.portsHint)
|
|
1662
|
-
}) : await (async () => {
|
|
1663
|
-
throw new Error("Terminal RPC \u4E0D\u53EF\u7528\uFF08\u9700\u5728 connect \u4E3B\u8FDB\u7A0B\uFF09");
|
|
1664
|
-
})();
|
|
1665
|
-
return JSON.stringify(result, null, 2);
|
|
1666
|
-
} catch (err) {
|
|
1667
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
1668
|
-
return {
|
|
1669
|
-
content: [{ type: "text", text: `\u542F\u52A8\u7EC8\u7AEF\u5931\u8D25: ${detail}` }],
|
|
1670
|
-
isError: true
|
|
1671
|
-
};
|
|
1672
|
-
}
|
|
1673
|
-
}
|
|
1674
|
-
};
|
|
1675
|
-
const StopWebIdeTerminal = {
|
|
1676
|
-
description: "Stop a WebIDE terminal and kill its process tree. Soft-terminates (status EXITED, record kept).",
|
|
1677
|
-
inputSchema: {
|
|
1678
|
-
type: "object",
|
|
1679
|
-
properties: {
|
|
1680
|
-
key: { type: "string", description: "Terminal key" },
|
|
1681
|
-
terminalId: { type: "string", description: "Server terminal id" }
|
|
1682
|
-
}
|
|
1683
|
-
},
|
|
1684
|
-
execute: async (args) => {
|
|
1685
|
-
const key = asString3(args.key) || void 0;
|
|
1686
|
-
const terminalId = asString3(args.terminalId) || void 0;
|
|
1687
|
-
if (!key && !terminalId) {
|
|
1688
|
-
return {
|
|
1689
|
-
content: [{ type: "text", text: "\u9700\u8981 key \u6216 terminalId" }],
|
|
1690
|
-
isError: true
|
|
1691
|
-
};
|
|
1692
|
-
}
|
|
1693
|
-
try {
|
|
1694
|
-
const result = rpc ? await rpc.stop({ taskId, key, terminalId }) : await (async () => {
|
|
1695
|
-
throw new Error("Terminal RPC \u4E0D\u53EF\u7528\uFF08\u9700\u5728 connect \u4E3B\u8FDB\u7A0B\uFF09");
|
|
1696
|
-
})();
|
|
1697
|
-
return JSON.stringify(result, null, 2);
|
|
1698
|
-
} catch (err) {
|
|
1699
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
1700
|
-
return {
|
|
1701
|
-
content: [{ type: "text", text: `\u505C\u6B62\u7EC8\u7AEF\u5931\u8D25: ${detail}` }],
|
|
1702
|
-
isError: true
|
|
1703
|
-
};
|
|
1704
|
-
}
|
|
1705
|
-
}
|
|
1706
|
-
};
|
|
1707
|
-
const StopAllWebIdeTerminals = {
|
|
1708
|
-
description: "Stop all living WebIDE terminals for the current task (kill process trees). Use before re-running local dev to avoid port conflicts. Soft-terminates records.",
|
|
1709
|
-
inputSchema: {
|
|
1710
|
-
type: "object",
|
|
1711
|
-
properties: {}
|
|
1712
|
-
},
|
|
1713
|
-
execute: async () => {
|
|
1714
|
-
try {
|
|
1715
|
-
const result = rpc ? await rpc.stopAll({ taskId }) : await (async () => {
|
|
1716
|
-
throw new Error("Terminal RPC \u4E0D\u53EF\u7528\uFF08\u9700\u5728 connect \u4E3B\u8FDB\u7A0B\uFF09");
|
|
1717
|
-
})();
|
|
1718
|
-
return JSON.stringify(result, null, 2);
|
|
1719
|
-
} catch (err) {
|
|
1720
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
1721
|
-
return {
|
|
1722
|
-
content: [{ type: "text", text: `\u5168\u90E8\u505C\u6B62\u5931\u8D25: ${detail}` }],
|
|
1723
|
-
isError: true
|
|
1724
|
-
};
|
|
1725
|
-
}
|
|
1726
|
-
}
|
|
1727
|
-
};
|
|
1728
|
-
const ListWebIdeTerminals = {
|
|
1729
|
-
description: "List living WebIDE terminals for the current task (STARTING/RUNNING/READY/STOPPING only). Does NOT include EXITED/FAILED. Returns id, key, status, pid, ports.",
|
|
1730
|
-
inputSchema: {
|
|
1731
|
-
type: "object",
|
|
1732
|
-
properties: {}
|
|
1733
|
-
},
|
|
1734
|
-
execute: async () => {
|
|
1735
|
-
try {
|
|
1736
|
-
if (rpc) {
|
|
1737
|
-
const result2 = await rpc.list({ taskId });
|
|
1738
|
-
return JSON.stringify(result2, null, 2);
|
|
1500
|
+
execute: async (args) => {
|
|
1501
|
+
const cases = parseCases(args.cases);
|
|
1502
|
+
if (cases.length === 0) {
|
|
1503
|
+
throw new Error(
|
|
1504
|
+
"UpsertWebIdeTestCases \u7F3A\u5C11\u6709\u6548 cases\uFF08\u6BCF\u6761\u9700 name \u4E0E\u975E\u7A7A steps\uFF09"
|
|
1505
|
+
);
|
|
1739
1506
|
}
|
|
1740
|
-
const
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
content: [{ type: "text", text: `\u5217\u51FA\u7EC8\u7AEF\u5931\u8D25: ${detail}` }],
|
|
1747
|
-
isError: true
|
|
1748
|
-
};
|
|
1507
|
+
const result = await webideReplaceTestCases({ cases });
|
|
1508
|
+
return JSON.stringify(
|
|
1509
|
+
{ ok: true, count: result.count ?? cases.length },
|
|
1510
|
+
null,
|
|
1511
|
+
2
|
|
1512
|
+
);
|
|
1749
1513
|
}
|
|
1750
1514
|
}
|
|
1751
1515
|
};
|
|
1752
|
-
return {
|
|
1753
|
-
StartWebIdeTerminal,
|
|
1754
|
-
StopWebIdeTerminal,
|
|
1755
|
-
StopAllWebIdeTerminals,
|
|
1756
|
-
ListWebIdeTerminals
|
|
1757
|
-
};
|
|
1758
1516
|
}
|
|
1759
1517
|
|
|
1760
|
-
// src/commands/connect/
|
|
1518
|
+
// src/commands/connect/tools/index.ts
|
|
1761
1519
|
var PLAN_MODE_ASK_QUESTION_HINT = `[SDK \u73AF\u5883\u8BF4\u660E]
|
|
1762
1520
|
AskQuestion \u5DF2\u901A\u8FC7 MCP \u670D\u52A1\u5668 custom-user-tools \u6CE8\u518C\uFF0C\u5DE5\u5177\u540D\u4E3A AskQuestion\u3002
|
|
1763
1521
|
\u9700\u8981\u5411\u7528\u6237\u786E\u8BA4\u65F6\uFF0C\u8BF7\u8C03\u7528 AskQuestion\uFF08\u7ECF CallMcpTool / custom-user-tools\uFF09\uFF0C\u4E0D\u8981\u5047\u8BBE IDE \u5185\u7F6E AskQuestion \u4E0D\u53EF\u7528\u3002
|
|
@@ -1765,39 +1523,33 @@ AskQuestion \u5DF2\u901A\u8FC7 MCP \u670D\u52A1\u5668 custom-user-tools \u6CE8\u
|
|
|
1765
1523
|
var WORKSPACE_BOUNDARY_HINT = `[\u5DE5\u4F5C\u533A\u8FB9\u754C]
|
|
1766
1524
|
\u6240\u6709\u6587\u4EF6\u8BFB\u5199\u3001\u641C\u7D22\uFF08Grep/Glob/Read\uFF09\u3001Shell \u5FC5\u987B\u4E25\u683C\u9650\u5236\u5728\u5F53\u524D\u5DE5\u4F5C\u533A cwd \u5185\u3002
|
|
1767
1525
|
\u7981\u6B62\u8BBF\u95EE\u5DE5\u4F5C\u533A\u5916\u8DEF\u5F84\uFF08\u5982\u5176\u5B83\u9879\u76EE\u3001~/.cursor\u3001agent-transcripts\u3001\u7CFB\u7EDF\u76EE\u5F55\u7B49\uFF09\u3002`;
|
|
1768
|
-
function createCursorCustomTools(cfg,
|
|
1526
|
+
function createCursorCustomTools(cfg, options) {
|
|
1769
1527
|
const tools = {
|
|
1770
|
-
...
|
|
1771
|
-
|
|
1528
|
+
...options.enableAppendMessage !== false && options.appendMessageContent ? createAppendMessageCustomTools({
|
|
1529
|
+
appendContent: options.appendMessageContent
|
|
1530
|
+
}) : {},
|
|
1531
|
+
...options.enableAskQuestion !== false ? createAskQuestionTool({
|
|
1772
1532
|
onInvoke: options?.onAskQuestion,
|
|
1773
1533
|
execute: options?.askQuestionExecute
|
|
1774
|
-
})
|
|
1534
|
+
}) : {}
|
|
1775
1535
|
};
|
|
1776
|
-
if (options?.
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
});
|
|
1781
|
-
tools.UpsertWebIdePlan = createUpsertWebIdePlanTool({
|
|
1782
|
-
cfg,
|
|
1783
|
-
taskId: options.taskId
|
|
1784
|
-
});
|
|
1785
|
-
tools.UpsertWebIdeTestCases = createUpsertWebIdeTestCasesTool({
|
|
1786
|
-
cfg,
|
|
1787
|
-
taskId: options.taskId
|
|
1788
|
-
});
|
|
1789
|
-
tools.MergeWebIdePullRequests = createMergeWebIdePullRequestsTool({
|
|
1790
|
-
cfg,
|
|
1791
|
-
taskId: options.taskId
|
|
1792
|
-
});
|
|
1793
|
-
if (options.workdir) {
|
|
1536
|
+
if (options?.taskId && options.workdir) {
|
|
1537
|
+
const { cli } = createApmApiClient(cfg);
|
|
1538
|
+
const taskId = options.taskId;
|
|
1539
|
+
if (options.enableWebIdePlanTools) {
|
|
1794
1540
|
Object.assign(
|
|
1795
1541
|
tools,
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1542
|
+
createRecommendPlanTool({
|
|
1543
|
+
webideRecommendPlan: (args) => cli.webideRecommendPlan({ taskId, ...args })
|
|
1544
|
+
}),
|
|
1545
|
+
createUpsertWebIdePlanTool({
|
|
1546
|
+
webideUpsertPlan: (args) => cli.webideUpsertPlan({ taskId, ...args })
|
|
1547
|
+
}),
|
|
1548
|
+
createUpsertWebIdeTestCasesTool({
|
|
1549
|
+
webideReplaceTestCases: (args) => cli.webideReplaceTestCases({ taskId, ...args })
|
|
1550
|
+
}),
|
|
1551
|
+
createMergeWebIdePullRequestsTool({
|
|
1552
|
+
webideMergePullRequests: () => cli.webideMergePullRequests({ taskId })
|
|
1801
1553
|
})
|
|
1802
1554
|
);
|
|
1803
1555
|
}
|
|
@@ -1818,9 +1570,30 @@ function withWorkspaceBoundaryHint(prompt) {
|
|
|
1818
1570
|
${WORKSPACE_BOUNDARY_HINT}`;
|
|
1819
1571
|
}
|
|
1820
1572
|
|
|
1573
|
+
// src/commands/connect/pty-session-mcp.ts
|
|
1574
|
+
var PTY_SESSION_MCP_NAME = "pty-session";
|
|
1575
|
+
var DEFAULT_PTY_SESSION_PORT = "7788";
|
|
1576
|
+
function resolvePtySessionBaseUrl(env = process.env) {
|
|
1577
|
+
const explicit = env.LOCAL_TERMINAL_URL?.trim();
|
|
1578
|
+
if (explicit) return explicit.replace(/\/+$/, "");
|
|
1579
|
+
const port = env.LOCAL_TERMINAL_PORT?.trim() || DEFAULT_PTY_SESSION_PORT;
|
|
1580
|
+
return `http://127.0.0.1:${port}`;
|
|
1581
|
+
}
|
|
1582
|
+
function createPtySessionMcpServers(cfg) {
|
|
1583
|
+
const token = resolveApiKey(cfg);
|
|
1584
|
+
if (!token) return void 0;
|
|
1585
|
+
return {
|
|
1586
|
+
[PTY_SESSION_MCP_NAME]: {
|
|
1587
|
+
type: "http",
|
|
1588
|
+
url: `${resolvePtySessionBaseUrl()}/mcp`,
|
|
1589
|
+
headers: {
|
|
1590
|
+
Authorization: `Bearer ${token}`
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
};
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1821
1596
|
// src/commands/connect/cursor-agent.ts
|
|
1822
|
-
setMaxListeners2(100);
|
|
1823
|
-
installAbortSignalDebug();
|
|
1824
1597
|
var noopRemoteLogSync = {
|
|
1825
1598
|
schedule(_session) {
|
|
1826
1599
|
},
|
|
@@ -1883,7 +1656,6 @@ async function obtainAgent(ctx) {
|
|
|
1883
1656
|
}
|
|
1884
1657
|
async function runCursorAgent(cfg, ctx, options) {
|
|
1885
1658
|
const signal = options.signal;
|
|
1886
|
-
logAbortSignalStats(signal, "runCursorAgent:start");
|
|
1887
1659
|
if (signal?.aborted) {
|
|
1888
1660
|
throw new Error("\u8FDE\u63A5\u5DF2\u5173\u95ED\uFF0C\u4EFB\u52A1\u4E2D\u65AD");
|
|
1889
1661
|
}
|
|
@@ -1892,16 +1664,18 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
1892
1664
|
throw new Error("\u7F3A\u5C11 apiKey\uFF0C\u65E0\u6CD5\u8C03\u7528 Cursor SDK");
|
|
1893
1665
|
}
|
|
1894
1666
|
const workdir = resolveWorkdirPath(ctx.workdir);
|
|
1895
|
-
const customTools = createCursorCustomTools(cfg,
|
|
1667
|
+
const customTools = createCursorCustomTools(cfg, {
|
|
1896
1668
|
onAskQuestion: options.onAskQuestion,
|
|
1897
1669
|
appendMessageContent: options.appendMessageContent,
|
|
1670
|
+
enableAppendMessage: options.enableAppendMessage,
|
|
1671
|
+
enableAskQuestion: options.enableAskQuestion,
|
|
1898
1672
|
askQuestionExecute: options.askQuestionExecute,
|
|
1899
1673
|
enableWebIdePlanTools: options.enableWebIdePlanTools,
|
|
1900
1674
|
taskId: options.taskId,
|
|
1901
|
-
workdir
|
|
1902
|
-
terminalRpc: options.terminalRpc
|
|
1675
|
+
workdir
|
|
1903
1676
|
});
|
|
1904
1677
|
const enableSandbox = Boolean(options.enableSandbox);
|
|
1678
|
+
const mcpServers = options.enablePtySessionMcp ? createPtySessionMcpServers(cfg) : void 0;
|
|
1905
1679
|
let prompt = withPlanModeToolHint(ctx.prompt, ctx.mode);
|
|
1906
1680
|
prompt = withWorkspaceBoundaryHint(prompt);
|
|
1907
1681
|
console.log(
|
|
@@ -1916,6 +1690,7 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
1916
1690
|
resumeAgentId: ctx.resumeAgentId,
|
|
1917
1691
|
customTools,
|
|
1918
1692
|
enableSandbox,
|
|
1693
|
+
mcpServers,
|
|
1919
1694
|
onInvalidatePersistedAgentId: options.onInvalidatePersistedAgentId
|
|
1920
1695
|
});
|
|
1921
1696
|
const invalidateAgentMapping = () => {
|
|
@@ -1929,7 +1704,6 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
1929
1704
|
void activeRun.cancel().catch(() => void 0);
|
|
1930
1705
|
};
|
|
1931
1706
|
signal?.addEventListener("abort", abortRun, { once: true });
|
|
1932
|
-
logAbortSignalStats(signal, "runCursorAgent:after-addListener");
|
|
1933
1707
|
try {
|
|
1934
1708
|
const run = await agent.send(prompt, {
|
|
1935
1709
|
...ctx.mode ? { mode: ctx.mode } : {},
|
|
@@ -1939,7 +1713,6 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
1939
1713
|
}
|
|
1940
1714
|
});
|
|
1941
1715
|
activeRun = run;
|
|
1942
|
-
logAbortSignalStats(signal, "runCursorAgent:after-send");
|
|
1943
1716
|
console.log(`[apm] Cursor run id=${run.id} agentId=${agent.agentId}`);
|
|
1944
1717
|
await options.onRunStarted?.({ agentId: agent.agentId, runId: run.id });
|
|
1945
1718
|
let lastRunErrorStatus;
|
|
@@ -2026,9 +1799,7 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
2026
1799
|
}
|
|
2027
1800
|
throw err;
|
|
2028
1801
|
} finally {
|
|
2029
|
-
logAbortSignalStats(signal, "runCursorAgent:finally-before-cleanup");
|
|
2030
1802
|
signal?.removeEventListener("abort", abortRun);
|
|
2031
|
-
logAbortSignalStats(signal, "runCursorAgent:finally-after-cleanup");
|
|
2032
1803
|
await agent[Symbol.asyncDispose]();
|
|
2033
1804
|
}
|
|
2034
1805
|
}
|
|
@@ -2204,11 +1975,11 @@ async function cleanWebIdeWorkspaceCache(taskId, workdir) {
|
|
|
2204
1975
|
// src/commands/connect/webide-ask-question.ts
|
|
2205
1976
|
import { setTimeout as delay } from "node:timers/promises";
|
|
2206
1977
|
var POLL_INTERVAL_MS = 2e3;
|
|
2207
|
-
function
|
|
1978
|
+
function asString3(value) {
|
|
2208
1979
|
return typeof value === "string" ? value.trim() : "";
|
|
2209
1980
|
}
|
|
2210
1981
|
function parseQuestions(args) {
|
|
2211
|
-
const title =
|
|
1982
|
+
const title = asString3(args.title) || void 0;
|
|
2212
1983
|
const raw = args.questions;
|
|
2213
1984
|
if (!Array.isArray(raw) || raw.length === 0) {
|
|
2214
1985
|
throw new Error("AskQuestion \u7F3A\u5C11 questions");
|
|
@@ -2217,16 +1988,16 @@ function parseQuestions(args) {
|
|
|
2217
1988
|
for (const item of raw) {
|
|
2218
1989
|
if (!item || typeof item !== "object" || Array.isArray(item)) continue;
|
|
2219
1990
|
const row = item;
|
|
2220
|
-
const id =
|
|
2221
|
-
const prompt =
|
|
1991
|
+
const id = asString3(row.id);
|
|
1992
|
+
const prompt = asString3(row.prompt);
|
|
2222
1993
|
const optionsRaw = row.options;
|
|
2223
1994
|
if (!id || !prompt || !Array.isArray(optionsRaw)) continue;
|
|
2224
1995
|
const options = [];
|
|
2225
1996
|
for (const opt of optionsRaw) {
|
|
2226
1997
|
if (!opt || typeof opt !== "object" || Array.isArray(opt)) continue;
|
|
2227
1998
|
const o = opt;
|
|
2228
|
-
const oid =
|
|
2229
|
-
const label =
|
|
1999
|
+
const oid = asString3(o.id);
|
|
2000
|
+
const label = asString3(o.label);
|
|
2230
2001
|
if (oid && label) options.push({ id: oid, label });
|
|
2231
2002
|
}
|
|
2232
2003
|
if (options.length < 2) {
|
|
@@ -2260,11 +2031,11 @@ function createWebIdeAskQuestionExecute(options) {
|
|
|
2260
2031
|
const byId = new Map(
|
|
2261
2032
|
list.assumptions.map((a) => [a.questionId, a])
|
|
2262
2033
|
);
|
|
2263
|
-
const
|
|
2034
|
+
const pending = parsed.questions.filter((q) => {
|
|
2264
2035
|
const row = byId.get(q.id);
|
|
2265
2036
|
return !row || row.status !== "RESOLVED";
|
|
2266
2037
|
});
|
|
2267
|
-
if (
|
|
2038
|
+
if (pending.length === 0) {
|
|
2268
2039
|
const answers = parsed.questions.map((q) => {
|
|
2269
2040
|
const row = byId.get(q.id);
|
|
2270
2041
|
return {
|
|
@@ -2292,7 +2063,7 @@ function createWebIdeAskQuestionExecute(options) {
|
|
|
2292
2063
|
};
|
|
2293
2064
|
}
|
|
2294
2065
|
|
|
2295
|
-
// src/
|
|
2066
|
+
// src/utils/minio.ts
|
|
2296
2067
|
import * as Minio from "minio";
|
|
2297
2068
|
var MinioClient = class {
|
|
2298
2069
|
inner;
|
|
@@ -2312,45 +2083,9 @@ var MinioClient = class {
|
|
|
2312
2083
|
await this.inner.makeBucket(bucket);
|
|
2313
2084
|
}
|
|
2314
2085
|
}
|
|
2315
|
-
async deleteObjectsByPrefix(bucket, prefix) {
|
|
2316
|
-
const objectsStream = this.inner.listObjectsV2(bucket, prefix, true);
|
|
2317
|
-
const keys = [];
|
|
2318
|
-
await new Promise((resolve6, reject) => {
|
|
2319
|
-
objectsStream.on("data", (obj) => {
|
|
2320
|
-
if (obj.name) {
|
|
2321
|
-
keys.push(obj.name);
|
|
2322
|
-
}
|
|
2323
|
-
});
|
|
2324
|
-
objectsStream.on("error", reject);
|
|
2325
|
-
objectsStream.on("end", resolve6);
|
|
2326
|
-
});
|
|
2327
|
-
const chunkSize = 500;
|
|
2328
|
-
for (let i = 0; i < keys.length; i += chunkSize) {
|
|
2329
|
-
const chunk = keys.slice(i, i + chunkSize);
|
|
2330
|
-
await this.inner.removeObjects(
|
|
2331
|
-
bucket,
|
|
2332
|
-
chunk.map((name) => name)
|
|
2333
|
-
);
|
|
2334
|
-
}
|
|
2335
|
-
}
|
|
2336
2086
|
async putObject(bucket, objectKey, body, meta) {
|
|
2337
2087
|
await this.inner.putObject(bucket, objectKey, body, body.length, meta);
|
|
2338
2088
|
}
|
|
2339
|
-
/** 匿名可读当前桶全部对象(便于静态站点直链) */
|
|
2340
|
-
async setBucketPublicRead(bucket) {
|
|
2341
|
-
const policy = {
|
|
2342
|
-
Version: "2012-10-17",
|
|
2343
|
-
Statement: [
|
|
2344
|
-
{
|
|
2345
|
-
Effect: "Allow",
|
|
2346
|
-
Principal: { AWS: ["*"] },
|
|
2347
|
-
Action: ["s3:GetObject"],
|
|
2348
|
-
Resource: [`arn:aws:s3:::${bucket}/*`]
|
|
2349
|
-
}
|
|
2350
|
-
]
|
|
2351
|
-
};
|
|
2352
|
-
await this.inner.setBucketPolicy(bucket, JSON.stringify(policy));
|
|
2353
|
-
}
|
|
2354
2089
|
};
|
|
2355
2090
|
|
|
2356
2091
|
// src/commands/connect/apm-log-minio.ts
|
|
@@ -2856,7 +2591,7 @@ async function syncWebIdeAttachments(cfg, taskId, workdir, attachments) {
|
|
|
2856
2591
|
);
|
|
2857
2592
|
}
|
|
2858
2593
|
|
|
2859
|
-
// src/project-documents
|
|
2594
|
+
// src/utils/project-documents.ts
|
|
2860
2595
|
import {
|
|
2861
2596
|
existsSync as existsSync6,
|
|
2862
2597
|
readdirSync as readdirSync3,
|
|
@@ -2968,24 +2703,25 @@ async function resolveProjectIdForSync(api, workdirPath, options) {
|
|
|
2968
2703
|
return { projectId: explicit, diagnostic: null };
|
|
2969
2704
|
}
|
|
2970
2705
|
try {
|
|
2971
|
-
const
|
|
2972
|
-
const
|
|
2973
|
-
|
|
2974
|
-
|
|
2706
|
+
const gitRoot = await resolveGitRepoRoot(workdirPath);
|
|
2707
|
+
const current = await getCurrentBranch(gitRoot);
|
|
2708
|
+
const taskId = taskIdFromFeatureBranch(current);
|
|
2709
|
+
if (!taskId) {
|
|
2710
|
+
return {
|
|
2711
|
+
projectId: null,
|
|
2712
|
+
diagnostic: `\u5F53\u524D\u5206\u652F ${current} \u4E0D\u662F feat/task-*\uFF0C\u65E0\u6CD5\u89E3\u6790\u6240\u5C5E\u9879\u76EE\u3002
|
|
2713
|
+
\u8BF7\u5728\u7279\u6027\u5206\u652F\u4E0A\u6267\u884C\uFF0C\u6216\u663E\u5F0F\u6307\u5B9A projectId\u3002`
|
|
2714
|
+
};
|
|
2975
2715
|
}
|
|
2976
|
-
const
|
|
2977
|
-
|
|
2716
|
+
const baseline = await api.cli.projectBaseBranch({ taskId });
|
|
2717
|
+
const projectId = baseline.projectId?.trim() || null;
|
|
2718
|
+
if (!projectId) {
|
|
2978
2719
|
return {
|
|
2979
2720
|
projectId: null,
|
|
2980
|
-
diagnostic:
|
|
2981
|
-
\u8BF7\u5728\u5E73\u53F0\u5C06\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u5230\u552F\u4E00\u9879\u76EE\u3002`
|
|
2721
|
+
diagnostic: `\u4EFB\u52A1 ${taskId} \u672A\u5173\u8054\u9879\u76EE\uFF0C\u65E0\u6CD5\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002`
|
|
2982
2722
|
};
|
|
2983
2723
|
}
|
|
2984
|
-
return {
|
|
2985
|
-
projectId: null,
|
|
2986
|
-
diagnostic: `\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u4E86 ${projectIds.length} \u4E2A\u9879\u76EE\uFF0C\u65E0\u6CD5\u786E\u5B9A\u540C\u6B65\u76EE\u6807\u3002
|
|
2987
|
-
\u8BF7\u786E\u4FDD\u8BE5\u5DE5\u4F5C\u7A7A\u95F4\u53EA\u5173\u8054\u4E00\u4E2A\u9879\u76EE\u540E\u518D\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002`
|
|
2988
|
-
};
|
|
2724
|
+
return { projectId, diagnostic: null };
|
|
2989
2725
|
} catch (err) {
|
|
2990
2726
|
const detail = err instanceof Error ? err.message : String(err);
|
|
2991
2727
|
return {
|
|
@@ -3204,7 +2940,7 @@ async function appendContent(cfg, messageId, content) {
|
|
|
3204
2940
|
const api = createApmApiClient(cfg);
|
|
3205
2941
|
await api.cli.webideAppendMessageContent({ id: messageId, content });
|
|
3206
2942
|
}
|
|
3207
|
-
async function handleWebIdeInboundMessage(cfg, msg, signal
|
|
2943
|
+
async function handleWebIdeInboundMessage(cfg, msg, signal) {
|
|
3208
2944
|
const workdir = requireRemoteWorkdir(msg.workdir);
|
|
3209
2945
|
const messageId = msg.messageId;
|
|
3210
2946
|
const taskId = msg.taskId;
|
|
@@ -3302,7 +3038,7 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
|
|
|
3302
3038
|
async () => runTaskBranch(taskId, { cwd: workdir }),
|
|
3303
3039
|
(branchResult) => {
|
|
3304
3040
|
markBranchDone(branchCacheKey, workdir);
|
|
3305
|
-
return `kind=${branchResult.kind} branch=${branchResult.branch} repos=${branchResult.repos.length}`;
|
|
3041
|
+
return `kind=${branchResult.kind} branch=${branchResult.branch} baseline=${branchResult.baseline} repos=${branchResult.repos.length}`;
|
|
3306
3042
|
}
|
|
3307
3043
|
);
|
|
3308
3044
|
} else {
|
|
@@ -3419,7 +3155,6 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
|
|
|
3419
3155
|
enableSandbox: false,
|
|
3420
3156
|
onInvalidatePersistedAgentId: () => clearWebIdeAgentId(workdir, taskId),
|
|
3421
3157
|
taskId,
|
|
3422
|
-
terminalRpc: options?.terminalRpc,
|
|
3423
3158
|
createRemoteLogSync: (agentId) => {
|
|
3424
3159
|
saveWebIdeAgentId(workdir, taskId, agentId);
|
|
3425
3160
|
logSyncRef.current = createThrottledWebIdeMessageLogSync(
|
|
@@ -3611,68 +3346,166 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
|
|
|
3611
3346
|
}
|
|
3612
3347
|
}
|
|
3613
3348
|
|
|
3614
|
-
// src/commands/connect/
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
if (!p) return;
|
|
3623
|
-
pending.delete(raw.id);
|
|
3624
|
-
if (raw.ok) p.resolve(raw.result);
|
|
3625
|
-
else p.reject(new Error(raw.error || "terminal rpc failed"));
|
|
3626
|
-
});
|
|
3349
|
+
// src/commands/connect/handle-start-project.ts
|
|
3350
|
+
async function updateStatus2(cfg, messageId, status) {
|
|
3351
|
+
const api = createApmApiClient(cfg);
|
|
3352
|
+
await api.cli.webideUpdateMessageStatus({ id: messageId, status });
|
|
3353
|
+
}
|
|
3354
|
+
async function setError2(cfg, messageId, error) {
|
|
3355
|
+
const api = createApmApiClient(cfg);
|
|
3356
|
+
await api.cli.webideSetMessageError({ id: messageId, error });
|
|
3627
3357
|
}
|
|
3628
|
-
function
|
|
3629
|
-
const
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3358
|
+
async function handleStartProject(cfg, msg, signal) {
|
|
3359
|
+
const workdir = requireRemoteWorkdir(msg.workdir);
|
|
3360
|
+
const messageId = msg.messageId;
|
|
3361
|
+
const taskId = msg.taskId;
|
|
3362
|
+
console.log(
|
|
3363
|
+
`[apm] webide-start-project target=${msg.target} restart=${msg.restart} taskId=${taskId} messageId=${messageId}`
|
|
3364
|
+
);
|
|
3365
|
+
await updateStatus2(cfg, messageId, "TYPING");
|
|
3366
|
+
const eventSession = new EventSession(msg.content);
|
|
3367
|
+
const logSyncRef = {
|
|
3368
|
+
current: createThrottledWebIdeMessageLogSync(
|
|
3369
|
+
cfg,
|
|
3370
|
+
{ taskId, messageId, agentId: "webide-start-project" },
|
|
3371
|
+
(err) => {
|
|
3372
|
+
console.warn(
|
|
3373
|
+
"[apm] WebIDE \u542F\u52A8\u9879\u76EE\u65E5\u5FD7\u540C\u6B65\u5931\u8D25:",
|
|
3374
|
+
err instanceof Error ? err.message : err
|
|
3375
|
+
);
|
|
3376
|
+
}
|
|
3377
|
+
)
|
|
3378
|
+
};
|
|
3379
|
+
try {
|
|
3380
|
+
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
3381
|
+
await ensureWorkspaceInitialized(workdir);
|
|
3382
|
+
const api = createApmApiClient(cfg);
|
|
3383
|
+
const outcome = await runCursorAgent(
|
|
3384
|
+
cfg,
|
|
3385
|
+
{
|
|
3386
|
+
messageId,
|
|
3387
|
+
prompt: msg.content,
|
|
3388
|
+
model: msg.model,
|
|
3389
|
+
apiKey: msg.apiKey,
|
|
3390
|
+
workdir,
|
|
3391
|
+
user: msg.user || "webide"
|
|
3392
|
+
},
|
|
3393
|
+
{
|
|
3394
|
+
signal,
|
|
3395
|
+
forceSend: true,
|
|
3396
|
+
eventSession,
|
|
3397
|
+
enableAppendMessage: false,
|
|
3398
|
+
enableAskQuestion: false,
|
|
3399
|
+
enableWebIdePlanTools: false,
|
|
3400
|
+
enablePtySessionMcp: true,
|
|
3401
|
+
enableSandbox: false,
|
|
3402
|
+
taskId,
|
|
3403
|
+
createRemoteLogSync: (agentId) => {
|
|
3404
|
+
logSyncRef.current = createThrottledWebIdeMessageLogSync(
|
|
3405
|
+
cfg,
|
|
3406
|
+
{ taskId, messageId, agentId },
|
|
3407
|
+
(err) => {
|
|
3408
|
+
console.warn(
|
|
3409
|
+
"[apm] WebIDE \u542F\u52A8\u9879\u76EE\u65E5\u5FD7\u540C\u6B65\u5931\u8D25:",
|
|
3410
|
+
err instanceof Error ? err.message : err
|
|
3411
|
+
);
|
|
3412
|
+
}
|
|
3413
|
+
);
|
|
3414
|
+
return logSyncRef.current;
|
|
3415
|
+
},
|
|
3416
|
+
onRunStarted: async ({ agentId, runId }) => {
|
|
3417
|
+
eventSession.addCliStep(
|
|
3418
|
+
"\u542F\u52A8\u72EC\u7ACB Agent",
|
|
3419
|
+
`agentId=${agentId} runId=${runId}`,
|
|
3420
|
+
"ok"
|
|
3421
|
+
);
|
|
3422
|
+
logSyncRef.current?.schedule(eventSession);
|
|
3423
|
+
await logSyncRef.current?.markRun(runId, "running");
|
|
3424
|
+
await logSyncRef.current?.flush(eventSession);
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3427
|
+
);
|
|
3428
|
+
const tokenUsage = outcome.usage != null ? {
|
|
3429
|
+
modelId: outcome.modelId ?? (msg.model?.trim() || void 0),
|
|
3430
|
+
inputTokens: outcome.usage.inputTokens,
|
|
3431
|
+
outputTokens: outcome.usage.outputTokens,
|
|
3432
|
+
cacheReadTokens: outcome.usage.cacheReadTokens,
|
|
3433
|
+
cacheWriteTokens: outcome.usage.cacheWriteTokens,
|
|
3434
|
+
totalTokens: outcome.usage.totalTokens,
|
|
3435
|
+
...outcome.usage.reasoningTokens != null ? { reasoningTokens: outcome.usage.reasoningTokens } : {}
|
|
3436
|
+
} : void 0;
|
|
3437
|
+
if (outcome.status === "error") {
|
|
3438
|
+
const detail = formatCursorRunFailure(outcome.runId, {
|
|
3439
|
+
resultText: outcome.result
|
|
3440
|
+
});
|
|
3441
|
+
await logSyncRef.current?.markRun(
|
|
3442
|
+
outcome.runId,
|
|
3443
|
+
"error",
|
|
3444
|
+
detail,
|
|
3445
|
+
tokenUsage
|
|
3446
|
+
);
|
|
3447
|
+
await setError2(cfg, messageId, detail);
|
|
3633
3448
|
return;
|
|
3634
3449
|
}
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3450
|
+
if (outcome.status === "cancelled" || signal.aborted) {
|
|
3451
|
+
await logSyncRef.current?.markRun(
|
|
3452
|
+
outcome.runId,
|
|
3453
|
+
"cancelled",
|
|
3454
|
+
"\u5DF2\u53D6\u6D88",
|
|
3455
|
+
tokenUsage
|
|
3456
|
+
);
|
|
3457
|
+
await updateStatus2(cfg, messageId, "CANCELLED");
|
|
3458
|
+
return;
|
|
3459
|
+
}
|
|
3460
|
+
const fallback = resolveMessageReplyFallback({
|
|
3461
|
+
assistantText: outcome.assistantText,
|
|
3462
|
+
result: outcome.result
|
|
3641
3463
|
});
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
}
|
|
3464
|
+
await api.cli.webideEnsureMessageContent({
|
|
3465
|
+
id: messageId,
|
|
3466
|
+
content: fallback
|
|
3467
|
+
});
|
|
3468
|
+
await logSyncRef.current?.markRun(
|
|
3469
|
+
outcome.runId,
|
|
3470
|
+
"finished",
|
|
3471
|
+
null,
|
|
3472
|
+
tokenUsage
|
|
3473
|
+
);
|
|
3474
|
+
await updateStatus2(cfg, messageId, "SUCCESS");
|
|
3475
|
+
console.log(
|
|
3476
|
+
`[apm] webide-start-project \u5B8C\u6210 target=${msg.target} messageId=${messageId} agentId=${outcome.agentId}`
|
|
3477
|
+
);
|
|
3478
|
+
} catch (err) {
|
|
3479
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
3480
|
+
console.error(`[apm] webide-start-project \u5931\u8D25: ${detail}`);
|
|
3481
|
+
try {
|
|
3482
|
+
await setError2(cfg, messageId, detail);
|
|
3483
|
+
} catch (statusErr) {
|
|
3484
|
+
console.error(
|
|
3485
|
+
"[apm] \u5199\u5165\u542F\u52A8\u9879\u76EE FAILED \u5931\u8D25:",
|
|
3486
|
+
statusErr instanceof Error ? statusErr.message : statusErr
|
|
3487
|
+
);
|
|
3488
|
+
}
|
|
3489
|
+
}
|
|
3657
3490
|
}
|
|
3658
3491
|
|
|
3659
3492
|
// src/commands/connect/webide-message-worker.ts
|
|
3660
3493
|
var controllers = /* @__PURE__ */ new Map();
|
|
3661
|
-
installTerminalRpcListener();
|
|
3662
|
-
var terminalRpc = createWorkerTerminalRpc();
|
|
3663
3494
|
async function runJob(cfg, msg) {
|
|
3664
3495
|
const controller = new AbortController();
|
|
3665
3496
|
controllers.set(msg.messageId, controller);
|
|
3666
3497
|
try {
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
}
|
|
3670
|
-
|
|
3498
|
+
if (msg.type === "webide-start-project") {
|
|
3499
|
+
await handleStartProject(cfg, msg, controller.signal);
|
|
3500
|
+
} else {
|
|
3501
|
+
await handleWebIdeInboundMessage(cfg, msg, controller.signal);
|
|
3502
|
+
}
|
|
3503
|
+
parentPort?.postMessage({
|
|
3671
3504
|
type: "done",
|
|
3672
3505
|
messageId: msg.messageId
|
|
3673
3506
|
});
|
|
3674
3507
|
} catch (err) {
|
|
3675
|
-
|
|
3508
|
+
parentPort?.postMessage({
|
|
3676
3509
|
type: "error",
|
|
3677
3510
|
messageId: msg.messageId,
|
|
3678
3511
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -3681,7 +3514,7 @@ async function runJob(cfg, msg) {
|
|
|
3681
3514
|
controllers.delete(msg.messageId);
|
|
3682
3515
|
}
|
|
3683
3516
|
}
|
|
3684
|
-
|
|
3517
|
+
parentPort?.on("message", (raw) => {
|
|
3685
3518
|
if (raw.type === "cancel") {
|
|
3686
3519
|
controllers.get(raw.messageId)?.abort();
|
|
3687
3520
|
return;
|