ai-project-manage-cli 7.1.12 → 7.1.15
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 +1 -1
- package/dist/index.js +1531 -607
- package/dist/webide-message-worker.js +382 -160
- package/package.json +1 -1
- package/template/AGENTS.md +4 -4
- package/template/rules/webide_merge.md +5 -28
- package/template/rules/webide_terminal.md +37 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/commands/connect/webide-message-worker.ts
|
|
2
|
-
import { parentPort, workerData } from "node:worker_threads";
|
|
2
|
+
import { parentPort as parentPort2, workerData } from "node:worker_threads";
|
|
3
3
|
|
|
4
4
|
// src/api/client.ts
|
|
5
5
|
import { createApiClient } from "listpage-http";
|
|
@@ -100,10 +100,34 @@ var requestConfig = {
|
|
|
100
100
|
method: "PUT",
|
|
101
101
|
path: "/cli/webide/test-cases"
|
|
102
102
|
}),
|
|
103
|
+
webideListTerminals: defineEndpoint({
|
|
104
|
+
method: "GET",
|
|
105
|
+
path: "/cli/webide/terminals"
|
|
106
|
+
}),
|
|
107
|
+
webideUpsertTerminal: defineEndpoint({
|
|
108
|
+
method: "POST",
|
|
109
|
+
path: "/cli/webide/terminals"
|
|
110
|
+
}),
|
|
111
|
+
webideUpdateTerminalStatus: defineEndpoint({
|
|
112
|
+
method: "PUT",
|
|
113
|
+
path: "/cli/webide/terminals/status"
|
|
114
|
+
}),
|
|
115
|
+
webideUpdateTerminalMeta: defineEndpoint({
|
|
116
|
+
method: "PUT",
|
|
117
|
+
path: "/cli/webide/terminals/meta"
|
|
118
|
+
}),
|
|
119
|
+
acquireSteelBrowser: defineEndpoint({
|
|
120
|
+
method: "POST",
|
|
121
|
+
path: "/cli/steel-browser/acquire"
|
|
122
|
+
}),
|
|
103
123
|
branchBaseline: defineEndpoint({
|
|
104
124
|
method: "GET",
|
|
105
125
|
path: "/cli/tasks/branch-baseline"
|
|
106
126
|
}),
|
|
127
|
+
projectBaseBranch: defineEndpoint({
|
|
128
|
+
method: "GET",
|
|
129
|
+
path: "/cli/tasks/project-base-branch"
|
|
130
|
+
}),
|
|
107
131
|
listSessionsForBranchCleanup: defineEndpoint({
|
|
108
132
|
method: "GET",
|
|
109
133
|
path: "/cli/sessions/branch-cleanup"
|
|
@@ -140,21 +164,21 @@ var requestConfig = {
|
|
|
140
164
|
method: "POST",
|
|
141
165
|
path: "/cli/webide/pull-requests/merge"
|
|
142
166
|
}),
|
|
143
|
-
|
|
167
|
+
getProjectDocumentManifest: defineEndpoint({
|
|
144
168
|
method: "GET",
|
|
145
|
-
path: "/cli/
|
|
169
|
+
path: "/cli/project-documents/manifest"
|
|
146
170
|
}),
|
|
147
|
-
|
|
171
|
+
listProjectDocuments: defineEndpoint({
|
|
148
172
|
method: "GET",
|
|
149
|
-
path: "/cli/
|
|
173
|
+
path: "/cli/project-documents"
|
|
150
174
|
}),
|
|
151
|
-
|
|
175
|
+
upsertProjectDocument: defineEndpoint({
|
|
152
176
|
method: "PUT",
|
|
153
|
-
path: "/cli/
|
|
177
|
+
path: "/cli/project-documents/upsert"
|
|
154
178
|
}),
|
|
155
|
-
|
|
179
|
+
removeProjectDocument: defineEndpoint({
|
|
156
180
|
method: "DELETE",
|
|
157
|
-
path: "/cli/
|
|
181
|
+
path: "/cli/project-documents"
|
|
158
182
|
}),
|
|
159
183
|
createTaskDeployment: defineEndpoint({
|
|
160
184
|
method: "POST",
|
|
@@ -191,6 +215,7 @@ import { homedir } from "os";
|
|
|
191
215
|
import { join } from "path";
|
|
192
216
|
var APM_CONFIG_DIR = join(homedir(), ".config", "apm");
|
|
193
217
|
var APM_CONFIG_PATH = join(APM_CONFIG_DIR, "config.json");
|
|
218
|
+
var DEFAULT_BASE_URL = "http://127.0.0.1:3000";
|
|
194
219
|
function resolveClientMachineId(cfg) {
|
|
195
220
|
return (cfg.clientMachineId ?? cfg.userId ?? "").trim();
|
|
196
221
|
}
|
|
@@ -221,6 +246,33 @@ async function tryReadApmConfig() {
|
|
|
221
246
|
return null;
|
|
222
247
|
}
|
|
223
248
|
}
|
|
249
|
+
function defaultApmConfig() {
|
|
250
|
+
return {
|
|
251
|
+
baseUrl: DEFAULT_BASE_URL,
|
|
252
|
+
clientMachineId: "",
|
|
253
|
+
apiKey: ""
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
async function ensureApmConfig() {
|
|
257
|
+
const existing = await tryReadApmConfig();
|
|
258
|
+
if (existing) return existing;
|
|
259
|
+
const defaults = defaultApmConfig();
|
|
260
|
+
await writeApmConfig(defaults);
|
|
261
|
+
return defaults;
|
|
262
|
+
}
|
|
263
|
+
async function writeApmConfig(cfg) {
|
|
264
|
+
const clientMachineId = resolveClientMachineId(cfg);
|
|
265
|
+
const apiKey = resolveApiKey(cfg);
|
|
266
|
+
const normalized = {
|
|
267
|
+
baseUrl: cfg.baseUrl.trim().replace(/\/+$/, ""),
|
|
268
|
+
apiKey,
|
|
269
|
+
...clientMachineId ? { clientMachineId } : {}
|
|
270
|
+
};
|
|
271
|
+
mkdirSync(APM_CONFIG_DIR, { recursive: true });
|
|
272
|
+
writeFileSync(APM_CONFIG_PATH, JSON.stringify(normalized, null, 2) + "\n", {
|
|
273
|
+
mode: 384
|
|
274
|
+
});
|
|
275
|
+
}
|
|
224
276
|
|
|
225
277
|
// src/api/client.ts
|
|
226
278
|
function createApmApiClient(cfg) {
|
|
@@ -359,24 +411,6 @@ async function ensureRemoteBaselineBranch(cwd, baselineBranch) {
|
|
|
359
411
|
`[apm] \u8FDC\u7A0B\u4E0D\u5B58\u5728\u57FA\u7EBF\u5206\u652F origin/${baselineBranch}\uFF0C\u8BF7\u786E\u8BA4\u4ED3\u5E93\u9ED8\u8BA4\u5206\u652F\u5DF2\u63A8\u9001\u5230 origin`
|
|
360
412
|
);
|
|
361
413
|
}
|
|
362
|
-
async function resolveDefaultRemoteBranch(cwd) {
|
|
363
|
-
try {
|
|
364
|
-
const ref = (await execGit(cwd, ["symbolic-ref", "refs/remotes/origin/HEAD"], true)).trim();
|
|
365
|
-
const match = ref.match(/^refs\/remotes\/origin\/(.+)$/);
|
|
366
|
-
if (match?.[1]) {
|
|
367
|
-
return match[1];
|
|
368
|
-
}
|
|
369
|
-
} catch {
|
|
370
|
-
}
|
|
371
|
-
for (const candidate of ["main", "master"]) {
|
|
372
|
-
if (await remoteBranchExists(cwd, candidate)) {
|
|
373
|
-
return candidate;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
throw new Error(
|
|
377
|
-
"[apm] \u65E0\u6CD5\u786E\u5B9A\u8FDC\u7A0B\u9ED8\u8BA4\u5206\u652F\uFF08origin/HEAD\u3001main\u3001master \u5747\u4E0D\u53EF\u7528\uFF09"
|
|
378
|
-
);
|
|
379
|
-
}
|
|
380
414
|
async function hasUpstream(cwd) {
|
|
381
415
|
try {
|
|
382
416
|
await execGit(cwd, ["rev-parse", "--abbrev-ref", "@{upstream}"], true);
|
|
@@ -557,6 +591,16 @@ function assertApmGitignoredInRepo(workdir) {
|
|
|
557
591
|
);
|
|
558
592
|
}
|
|
559
593
|
}
|
|
594
|
+
async function ensureLoggedConfig() {
|
|
595
|
+
const cfg = await ensureApmConfig();
|
|
596
|
+
if (!resolveApiKey(cfg)) {
|
|
597
|
+
console.error(
|
|
598
|
+
"[apm] \u672A\u68C0\u6D4B\u5230 API Key\uFF0C\u8BF7\u5148\u6267\u884C: apm login --api-key cm_xxx"
|
|
599
|
+
);
|
|
600
|
+
process.exit(1);
|
|
601
|
+
}
|
|
602
|
+
return cfg;
|
|
603
|
+
}
|
|
560
604
|
async function ensureDirExists(dir) {
|
|
561
605
|
mkdirSync2(dir, { recursive: true });
|
|
562
606
|
}
|
|
@@ -961,6 +1005,17 @@ async function runTaskBranch(taskId, options = {}) {
|
|
|
961
1005
|
if (!trimmedTaskId) {
|
|
962
1006
|
throw new Error("[apm] taskId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
963
1007
|
}
|
|
1008
|
+
const cfg = await ensureLoggedConfig();
|
|
1009
|
+
const api = createApmApiClient(cfg);
|
|
1010
|
+
const projectBaseline = await api.cli.projectBaseBranch({
|
|
1011
|
+
taskId: trimmedTaskId
|
|
1012
|
+
});
|
|
1013
|
+
const baselineBranch = projectBaseline.baseBranch?.trim() ?? "";
|
|
1014
|
+
if (!baselineBranch) {
|
|
1015
|
+
throw new Error(
|
|
1016
|
+
"[apm] \u9879\u76EE\u672A\u914D\u7F6E\u57FA\u7840\u5206\u652F\uFF0C\u65E0\u6CD5\u521B\u5EFA WebIDE \u7279\u6027\u5206\u652F"
|
|
1017
|
+
);
|
|
1018
|
+
}
|
|
964
1019
|
const cwd = options.cwd ?? process.cwd();
|
|
965
1020
|
const workdirPath = resolveWorkdirPath(cwd);
|
|
966
1021
|
const manifest = loadWorkspaceReposCache(workdirPath);
|
|
@@ -968,9 +1023,8 @@ async function runTaskBranch(taskId, options = {}) {
|
|
|
968
1023
|
const branch = branchNameForTask(trimmedTaskId);
|
|
969
1024
|
for (const gitRoot of repoRoots) {
|
|
970
1025
|
const label = formatRepoLabel(workdirPath, gitRoot);
|
|
971
|
-
const baselineBranch = await resolveDefaultRemoteBranch(gitRoot);
|
|
972
1026
|
console.log(
|
|
973
|
-
`[apm] \u4EFB\u52A1\u5206\u652F ${branch} @ ${label} (baseline=${baselineBranch})`
|
|
1027
|
+
`[apm] \u4EFB\u52A1\u5206\u652F ${branch} @ ${label} (project baseline=${baselineBranch})`
|
|
974
1028
|
);
|
|
975
1029
|
await ensureFeatureBranch(branch, baselineBranch, {
|
|
976
1030
|
...options,
|
|
@@ -1559,13 +1613,13 @@ ${payload}`;
|
|
|
1559
1613
|
function createMergeWebIdePullRequestsTool(options) {
|
|
1560
1614
|
const { cfg, taskId } = options;
|
|
1561
1615
|
return {
|
|
1562
|
-
description: "Merge all open pull requests for this WebIDE task
|
|
1616
|
+
description: "Merge all open pull requests for this WebIDE task. Call once with confirmedConflictFree=true. No baseline sync or review is required before calling.",
|
|
1563
1617
|
inputSchema: {
|
|
1564
1618
|
type: "object",
|
|
1565
1619
|
properties: {
|
|
1566
1620
|
confirmedConflictFree: {
|
|
1567
1621
|
type: "boolean",
|
|
1568
|
-
description: "Must be true
|
|
1622
|
+
description: "Must be true to confirm you intend to merge this task's open PRs now."
|
|
1569
1623
|
}
|
|
1570
1624
|
},
|
|
1571
1625
|
required: ["confirmedConflictFree"]
|
|
@@ -1573,7 +1627,7 @@ function createMergeWebIdePullRequestsTool(options) {
|
|
|
1573
1627
|
execute: async (args) => {
|
|
1574
1628
|
if (args.confirmedConflictFree !== true) {
|
|
1575
1629
|
throw new Error(
|
|
1576
|
-
"MergeWebIdePullRequests \u8981\u6C42 confirmedConflictFree=true\u3002
|
|
1630
|
+
"MergeWebIdePullRequests \u8981\u6C42 confirmedConflictFree=true\u3002"
|
|
1577
1631
|
);
|
|
1578
1632
|
}
|
|
1579
1633
|
const api = createApmApiClient(cfg);
|
|
@@ -1627,7 +1681,7 @@ function createRecommendPlanTool(options) {
|
|
|
1627
1681
|
function createUpsertWebIdePlanTool(options) {
|
|
1628
1682
|
const { cfg, taskId } = options;
|
|
1629
1683
|
return {
|
|
1630
|
-
description: "Persist the implementation plan markdown for this WebIDE task and mark
|
|
1684
|
+
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.",
|
|
1631
1685
|
inputSchema: {
|
|
1632
1686
|
type: "object",
|
|
1633
1687
|
properties: {
|
|
@@ -1738,6 +1792,164 @@ function createUpsertWebIdeTestCasesTool(options) {
|
|
|
1738
1792
|
};
|
|
1739
1793
|
}
|
|
1740
1794
|
|
|
1795
|
+
// src/commands/connect/webide-terminal-tools.ts
|
|
1796
|
+
function asString3(value) {
|
|
1797
|
+
return typeof value === "string" ? value.trim() : "";
|
|
1798
|
+
}
|
|
1799
|
+
function asPorts(value) {
|
|
1800
|
+
if (!Array.isArray(value)) return void 0;
|
|
1801
|
+
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));
|
|
1802
|
+
return ports.length ? ports : void 0;
|
|
1803
|
+
}
|
|
1804
|
+
function createWebIdeTerminalTools(options) {
|
|
1805
|
+
const { cfg, taskId, workdir, rpc } = options;
|
|
1806
|
+
const StartWebIdeTerminal = {
|
|
1807
|
+
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.",
|
|
1808
|
+
inputSchema: {
|
|
1809
|
+
type: "object",
|
|
1810
|
+
properties: {
|
|
1811
|
+
key: {
|
|
1812
|
+
type: "string",
|
|
1813
|
+
description: "Stable key, e.g. frontend / backend"
|
|
1814
|
+
},
|
|
1815
|
+
name: { type: "string", description: "Display name" },
|
|
1816
|
+
cwd: {
|
|
1817
|
+
type: "string",
|
|
1818
|
+
description: "Working directory (absolute or under workspace)"
|
|
1819
|
+
},
|
|
1820
|
+
command: {
|
|
1821
|
+
type: "string",
|
|
1822
|
+
description: "Shell command to run, e.g. pnpm dev"
|
|
1823
|
+
},
|
|
1824
|
+
readyPattern: {
|
|
1825
|
+
type: "string",
|
|
1826
|
+
description: "Optional regex; when matched in logs, status becomes READY"
|
|
1827
|
+
},
|
|
1828
|
+
portsHint: {
|
|
1829
|
+
type: "array",
|
|
1830
|
+
items: { type: "number" },
|
|
1831
|
+
description: "Optional expected listen ports"
|
|
1832
|
+
}
|
|
1833
|
+
},
|
|
1834
|
+
required: ["key", "cwd", "command"]
|
|
1835
|
+
},
|
|
1836
|
+
execute: async (args) => {
|
|
1837
|
+
const key = asString3(args.key);
|
|
1838
|
+
const cwd = asString3(args.cwd) || workdir;
|
|
1839
|
+
const command = asString3(args.command);
|
|
1840
|
+
if (!key || !command) {
|
|
1841
|
+
return {
|
|
1842
|
+
content: [{ type: "text", text: "key / command \u4E0D\u80FD\u4E3A\u7A7A" }],
|
|
1843
|
+
isError: true
|
|
1844
|
+
};
|
|
1845
|
+
}
|
|
1846
|
+
try {
|
|
1847
|
+
const result = rpc ? await rpc.start({
|
|
1848
|
+
taskId,
|
|
1849
|
+
key,
|
|
1850
|
+
name: asString3(args.name) || void 0,
|
|
1851
|
+
cwd,
|
|
1852
|
+
command,
|
|
1853
|
+
readyPattern: asString3(args.readyPattern) || void 0,
|
|
1854
|
+
portsHint: asPorts(args.portsHint)
|
|
1855
|
+
}) : await (async () => {
|
|
1856
|
+
throw new Error("Terminal RPC \u4E0D\u53EF\u7528\uFF08\u9700\u5728 connect \u4E3B\u8FDB\u7A0B\uFF09");
|
|
1857
|
+
})();
|
|
1858
|
+
return JSON.stringify(result, null, 2);
|
|
1859
|
+
} catch (err) {
|
|
1860
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1861
|
+
return {
|
|
1862
|
+
content: [{ type: "text", text: `\u542F\u52A8\u7EC8\u7AEF\u5931\u8D25: ${detail}` }],
|
|
1863
|
+
isError: true
|
|
1864
|
+
};
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
};
|
|
1868
|
+
const StopWebIdeTerminal = {
|
|
1869
|
+
description: "Stop a WebIDE terminal and kill its process tree. Soft-terminates (status EXITED, record kept).",
|
|
1870
|
+
inputSchema: {
|
|
1871
|
+
type: "object",
|
|
1872
|
+
properties: {
|
|
1873
|
+
key: { type: "string", description: "Terminal key" },
|
|
1874
|
+
terminalId: { type: "string", description: "Server terminal id" }
|
|
1875
|
+
}
|
|
1876
|
+
},
|
|
1877
|
+
execute: async (args) => {
|
|
1878
|
+
const key = asString3(args.key) || void 0;
|
|
1879
|
+
const terminalId = asString3(args.terminalId) || void 0;
|
|
1880
|
+
if (!key && !terminalId) {
|
|
1881
|
+
return {
|
|
1882
|
+
content: [{ type: "text", text: "\u9700\u8981 key \u6216 terminalId" }],
|
|
1883
|
+
isError: true
|
|
1884
|
+
};
|
|
1885
|
+
}
|
|
1886
|
+
try {
|
|
1887
|
+
const result = rpc ? await rpc.stop({ taskId, key, terminalId }) : await (async () => {
|
|
1888
|
+
throw new Error("Terminal RPC \u4E0D\u53EF\u7528\uFF08\u9700\u5728 connect \u4E3B\u8FDB\u7A0B\uFF09");
|
|
1889
|
+
})();
|
|
1890
|
+
return JSON.stringify(result, null, 2);
|
|
1891
|
+
} catch (err) {
|
|
1892
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1893
|
+
return {
|
|
1894
|
+
content: [{ type: "text", text: `\u505C\u6B62\u7EC8\u7AEF\u5931\u8D25: ${detail}` }],
|
|
1895
|
+
isError: true
|
|
1896
|
+
};
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
};
|
|
1900
|
+
const StopAllWebIdeTerminals = {
|
|
1901
|
+
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.",
|
|
1902
|
+
inputSchema: {
|
|
1903
|
+
type: "object",
|
|
1904
|
+
properties: {}
|
|
1905
|
+
},
|
|
1906
|
+
execute: async () => {
|
|
1907
|
+
try {
|
|
1908
|
+
const result = rpc ? await rpc.stopAll({ taskId }) : await (async () => {
|
|
1909
|
+
throw new Error("Terminal RPC \u4E0D\u53EF\u7528\uFF08\u9700\u5728 connect \u4E3B\u8FDB\u7A0B\uFF09");
|
|
1910
|
+
})();
|
|
1911
|
+
return JSON.stringify(result, null, 2);
|
|
1912
|
+
} catch (err) {
|
|
1913
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1914
|
+
return {
|
|
1915
|
+
content: [{ type: "text", text: `\u5168\u90E8\u505C\u6B62\u5931\u8D25: ${detail}` }],
|
|
1916
|
+
isError: true
|
|
1917
|
+
};
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
};
|
|
1921
|
+
const ListWebIdeTerminals = {
|
|
1922
|
+
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.",
|
|
1923
|
+
inputSchema: {
|
|
1924
|
+
type: "object",
|
|
1925
|
+
properties: {}
|
|
1926
|
+
},
|
|
1927
|
+
execute: async () => {
|
|
1928
|
+
try {
|
|
1929
|
+
if (rpc) {
|
|
1930
|
+
const result2 = await rpc.list({ taskId });
|
|
1931
|
+
return JSON.stringify(result2, null, 2);
|
|
1932
|
+
}
|
|
1933
|
+
const api = createApmApiClient(cfg);
|
|
1934
|
+
const result = await api.cli.webideListTerminals({ taskId });
|
|
1935
|
+
return JSON.stringify(result, null, 2);
|
|
1936
|
+
} catch (err) {
|
|
1937
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1938
|
+
return {
|
|
1939
|
+
content: [{ type: "text", text: `\u5217\u51FA\u7EC8\u7AEF\u5931\u8D25: ${detail}` }],
|
|
1940
|
+
isError: true
|
|
1941
|
+
};
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
};
|
|
1945
|
+
return {
|
|
1946
|
+
StartWebIdeTerminal,
|
|
1947
|
+
StopWebIdeTerminal,
|
|
1948
|
+
StopAllWebIdeTerminals,
|
|
1949
|
+
ListWebIdeTerminals
|
|
1950
|
+
};
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1741
1953
|
// src/commands/connect/cursor-custom-tools.ts
|
|
1742
1954
|
var PLAN_MODE_ASK_QUESTION_HINT = `[SDK \u73AF\u5883\u8BF4\u660E]
|
|
1743
1955
|
AskQuestion \u5DF2\u901A\u8FC7 MCP \u670D\u52A1\u5668 custom-user-tools \u6CE8\u518C\uFF0C\u5DE5\u5177\u540D\u4E3A AskQuestion\u3002
|
|
@@ -1775,6 +1987,17 @@ function createCursorCustomTools(cfg, messageId, options) {
|
|
|
1775
1987
|
cfg,
|
|
1776
1988
|
taskId: options.taskId
|
|
1777
1989
|
});
|
|
1990
|
+
if (options.workdir) {
|
|
1991
|
+
Object.assign(
|
|
1992
|
+
tools,
|
|
1993
|
+
createWebIdeTerminalTools({
|
|
1994
|
+
cfg,
|
|
1995
|
+
taskId: options.taskId,
|
|
1996
|
+
workdir: options.workdir,
|
|
1997
|
+
rpc: options.terminalRpc
|
|
1998
|
+
})
|
|
1999
|
+
);
|
|
2000
|
+
}
|
|
1778
2001
|
}
|
|
1779
2002
|
return tools;
|
|
1780
2003
|
}
|
|
@@ -1843,8 +2066,8 @@ async function obtainAgent(ctx) {
|
|
|
1843
2066
|
settingSources: ["project"]
|
|
1844
2067
|
} : {}
|
|
1845
2068
|
},
|
|
1846
|
-
...ctx.mode ? { mode: ctx.mode } : {}
|
|
1847
|
-
|
|
2069
|
+
...ctx.mode ? { mode: ctx.mode } : {},
|
|
2070
|
+
...ctx.mcpServers ? { mcpServers: ctx.mcpServers } : {}
|
|
1848
2071
|
};
|
|
1849
2072
|
const explicitAgentId = ctx.resumeAgentId?.trim();
|
|
1850
2073
|
const savedAgentId = explicitAgentId || (ctx.user ? loadSessionAgentId(ctx.workdir, ctx.sessionId, ctx.user) : void 0);
|
|
@@ -1898,7 +2121,9 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
1898
2121
|
appendMessageContent: options?.appendMessageContent,
|
|
1899
2122
|
askQuestionExecute: options?.askQuestionExecute,
|
|
1900
2123
|
enableWebIdePlanTools: options?.enableWebIdePlanTools,
|
|
1901
|
-
taskId: options?.taskId
|
|
2124
|
+
taskId: options?.taskId,
|
|
2125
|
+
workdir,
|
|
2126
|
+
terminalRpc: options?.terminalRpc
|
|
1902
2127
|
});
|
|
1903
2128
|
const enableSandbox = Boolean(options?.enableSandbox);
|
|
1904
2129
|
let prompt = withPlanModeToolHint(ctx.prompt, ctx.mode);
|
|
@@ -1939,7 +2164,6 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
1939
2164
|
try {
|
|
1940
2165
|
const run = await agent.send(prompt, {
|
|
1941
2166
|
...ctx.mode ? { mode: ctx.mode } : {},
|
|
1942
|
-
// mcpServers: createPlaywrightMcpServers(),
|
|
1943
2167
|
local: {
|
|
1944
2168
|
...options?.forceSend ? { force: true } : {},
|
|
1945
2169
|
customTools
|
|
@@ -2081,11 +2305,11 @@ function clearWebIdeAgentId(workdir, taskId) {
|
|
|
2081
2305
|
// src/commands/connect/webide-ask-question.ts
|
|
2082
2306
|
import { setTimeout as delay } from "node:timers/promises";
|
|
2083
2307
|
var POLL_INTERVAL_MS = 2e3;
|
|
2084
|
-
function
|
|
2308
|
+
function asString4(value) {
|
|
2085
2309
|
return typeof value === "string" ? value.trim() : "";
|
|
2086
2310
|
}
|
|
2087
2311
|
function parseQuestions(args) {
|
|
2088
|
-
const title =
|
|
2312
|
+
const title = asString4(args.title) || void 0;
|
|
2089
2313
|
const raw = args.questions;
|
|
2090
2314
|
if (!Array.isArray(raw) || raw.length === 0) {
|
|
2091
2315
|
throw new Error("AskQuestion \u7F3A\u5C11 questions");
|
|
@@ -2094,16 +2318,16 @@ function parseQuestions(args) {
|
|
|
2094
2318
|
for (const item of raw) {
|
|
2095
2319
|
if (!item || typeof item !== "object" || Array.isArray(item)) continue;
|
|
2096
2320
|
const row = item;
|
|
2097
|
-
const id =
|
|
2098
|
-
const prompt =
|
|
2321
|
+
const id = asString4(row.id);
|
|
2322
|
+
const prompt = asString4(row.prompt);
|
|
2099
2323
|
const optionsRaw = row.options;
|
|
2100
2324
|
if (!id || !prompt || !Array.isArray(optionsRaw)) continue;
|
|
2101
2325
|
const options = [];
|
|
2102
2326
|
for (const opt of optionsRaw) {
|
|
2103
2327
|
if (!opt || typeof opt !== "object" || Array.isArray(opt)) continue;
|
|
2104
2328
|
const o = opt;
|
|
2105
|
-
const oid =
|
|
2106
|
-
const label =
|
|
2329
|
+
const oid = asString4(o.id);
|
|
2330
|
+
const label = asString4(o.label);
|
|
2107
2331
|
if (oid && label) options.push({ id: oid, label });
|
|
2108
2332
|
}
|
|
2109
2333
|
if (options.length < 2) {
|
|
@@ -2137,11 +2361,11 @@ function createWebIdeAskQuestionExecute(options) {
|
|
|
2137
2361
|
const byId = new Map(
|
|
2138
2362
|
list.assumptions.map((a) => [a.questionId, a])
|
|
2139
2363
|
);
|
|
2140
|
-
const
|
|
2364
|
+
const pending2 = parsed.questions.filter((q) => {
|
|
2141
2365
|
const row = byId.get(q.id);
|
|
2142
2366
|
return !row || row.status !== "RESOLVED";
|
|
2143
2367
|
});
|
|
2144
|
-
if (
|
|
2368
|
+
if (pending2.length === 0) {
|
|
2145
2369
|
const answers = parsed.questions.map((q) => {
|
|
2146
2370
|
const row = byId.get(q.id);
|
|
2147
2371
|
return {
|
|
@@ -2435,7 +2659,7 @@ ${diagnostic ?? ""}
|
|
|
2435
2659
|
return { synced: true, repositoryId, configName: config.name };
|
|
2436
2660
|
}
|
|
2437
2661
|
|
|
2438
|
-
// src/
|
|
2662
|
+
// src/project-documents-sync.ts
|
|
2439
2663
|
import {
|
|
2440
2664
|
existsSync as existsSync5,
|
|
2441
2665
|
readdirSync as readdirSync3,
|
|
@@ -2446,7 +2670,7 @@ import {
|
|
|
2446
2670
|
import { dirname as dirname4, join as join6, relative as relative2, sep } from "path";
|
|
2447
2671
|
var MANIFEST_FILE = "manifest.json";
|
|
2448
2672
|
function projectDocumentsDir(apmRoot) {
|
|
2449
|
-
return join6(apmRoot ?? workspaceApmDir(), "
|
|
2673
|
+
return join6(apmRoot ?? workspaceApmDir(), "docs");
|
|
2450
2674
|
}
|
|
2451
2675
|
function projectDocumentLocalPath(apmRoot, documentPath) {
|
|
2452
2676
|
const normalized = normalizeLocalDocumentPath(documentPath);
|
|
@@ -2497,41 +2721,65 @@ function diffManifestPaths(remote, local) {
|
|
|
2497
2721
|
}
|
|
2498
2722
|
return { download, deleteLocal };
|
|
2499
2723
|
}
|
|
2500
|
-
async function
|
|
2724
|
+
async function resolveProjectIdForSync(api, workdirPath) {
|
|
2725
|
+
try {
|
|
2726
|
+
const baseline = await api.cli.workspaceBaseline({ workdirPath });
|
|
2727
|
+
const projectId = baseline.projectId?.trim() || null;
|
|
2728
|
+
if (projectId) {
|
|
2729
|
+
return { projectId, diagnostic: null };
|
|
2730
|
+
}
|
|
2731
|
+
const projectIds = baseline.projectIds ?? [];
|
|
2732
|
+
if (projectIds.length === 0) {
|
|
2733
|
+
return {
|
|
2734
|
+
projectId: null,
|
|
2735
|
+
diagnostic: baseline.diagnostic?.message ?? `\u5DE5\u4F5C\u7A7A\u95F4\u672A\u5173\u8054\u9879\u76EE\uFF0C\u65E0\u6CD5\u540C\u6B65\u9879\u76EE\u6587\u6863\uFF08\u76EE\u5F55\uFF1A${workdirPath}\uFF09\u3002
|
|
2736
|
+
\u8BF7\u5728\u5E73\u53F0\u5C06\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u5230\u552F\u4E00\u9879\u76EE\u3002`
|
|
2737
|
+
};
|
|
2738
|
+
}
|
|
2739
|
+
return {
|
|
2740
|
+
projectId: null,
|
|
2741
|
+
diagnostic: `\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u4E86 ${projectIds.length} \u4E2A\u9879\u76EE\uFF0C\u65E0\u6CD5\u786E\u5B9A\u540C\u6B65\u76EE\u6807\u3002
|
|
2742
|
+
\u8BF7\u786E\u4FDD\u8BE5\u5DE5\u4F5C\u7A7A\u95F4\u53EA\u5173\u8054\u4E00\u4E2A\u9879\u76EE\u540E\u518D\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002`
|
|
2743
|
+
};
|
|
2744
|
+
} catch (err) {
|
|
2745
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
2746
|
+
return {
|
|
2747
|
+
projectId: null,
|
|
2748
|
+
diagnostic: detail.replace(/^\[apm\]\s*/, "")
|
|
2749
|
+
};
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2752
|
+
async function syncProjectDocumentsPull(workdirPath, apmDir) {
|
|
2501
2753
|
const empty = {
|
|
2502
2754
|
synced: false,
|
|
2503
|
-
|
|
2755
|
+
projectId: null,
|
|
2504
2756
|
downloaded: 0,
|
|
2505
2757
|
deleted: 0
|
|
2506
2758
|
};
|
|
2507
2759
|
const cfg = await tryReadApmConfig();
|
|
2508
2760
|
if (!cfg || !resolveApiKey(cfg)) {
|
|
2509
2761
|
console.log(
|
|
2510
|
-
"[apm] \u672A\u68C0\u6D4B\u5230\u767B\u5F55\u4FE1\u606F\uFF0C\u8DF3\u8FC7\
|
|
2762
|
+
"[apm] \u672A\u68C0\u6D4B\u5230\u767B\u5F55\u4FE1\u606F\uFF0C\u8DF3\u8FC7\u9879\u76EE\u6587\u6863\u540C\u6B65\u3002\n[apm] \u8BF7\u5148\u6267\u884C apm login\u3002"
|
|
2511
2763
|
);
|
|
2512
2764
|
return empty;
|
|
2513
2765
|
}
|
|
2514
2766
|
const api = createApmApiClient(cfg);
|
|
2515
|
-
const {
|
|
2767
|
+
const { projectId, diagnostic } = await resolveProjectIdForSync(
|
|
2516
2768
|
api,
|
|
2517
2769
|
workdirPath
|
|
2518
2770
|
);
|
|
2519
|
-
if (!
|
|
2520
|
-
console.log(
|
|
2521
|
-
|
|
2522
|
-
${diagnostic ?? ""}`
|
|
2523
|
-
);
|
|
2771
|
+
if (!projectId) {
|
|
2772
|
+
console.log(`[apm] \u672A\u80FD\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002
|
|
2773
|
+
${diagnostic ?? ""}`);
|
|
2524
2774
|
return empty;
|
|
2525
2775
|
}
|
|
2526
2776
|
const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
|
|
2527
|
-
const
|
|
2528
|
-
await ensureDirExists(
|
|
2529
|
-
const { manifest: remoteManifest } = await api.cli.
|
|
2777
|
+
const docsDir = projectDocumentsDir(targetApmDir);
|
|
2778
|
+
await ensureDirExists(docsDir);
|
|
2779
|
+
const { manifest: remoteManifest } = await api.cli.getProjectDocumentManifest({ projectId });
|
|
2530
2780
|
if (!remoteManifest) {
|
|
2531
|
-
console.log(
|
|
2532
|
-
|
|
2533
|
-
);
|
|
2534
|
-
return { ...empty, repositoryId };
|
|
2781
|
+
console.log(`[apm] \u9879\u76EE ${projectId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`);
|
|
2782
|
+
return { ...empty, projectId };
|
|
2535
2783
|
}
|
|
2536
2784
|
const localManifest = readLocalManifest(targetApmDir);
|
|
2537
2785
|
const { download, deleteLocal } = diffManifestPaths(
|
|
@@ -2540,12 +2788,14 @@ ${diagnostic ?? ""}`
|
|
|
2540
2788
|
);
|
|
2541
2789
|
let downloaded = 0;
|
|
2542
2790
|
if (download.length > 0) {
|
|
2543
|
-
const { list } = await api.cli.
|
|
2544
|
-
|
|
2791
|
+
const { list } = await api.cli.listProjectDocuments({
|
|
2792
|
+
projectId,
|
|
2545
2793
|
paths: download.join(",")
|
|
2546
2794
|
});
|
|
2547
2795
|
for (const doc of list) {
|
|
2548
|
-
const absPath = toFsPath(
|
|
2796
|
+
const absPath = toFsPath(
|
|
2797
|
+
projectDocumentLocalPath(targetApmDir, doc.path)
|
|
2798
|
+
);
|
|
2549
2799
|
await ensureDirExists(dirname4(absPath));
|
|
2550
2800
|
writeFileSync7(absPath, doc.content, "utf8");
|
|
2551
2801
|
downloaded += 1;
|
|
@@ -2560,17 +2810,17 @@ ${diagnostic ?? ""}`
|
|
|
2560
2810
|
}
|
|
2561
2811
|
}
|
|
2562
2812
|
writeFileSync7(
|
|
2563
|
-
toFsPath(join6(
|
|
2813
|
+
toFsPath(join6(docsDir, MANIFEST_FILE)),
|
|
2564
2814
|
`${JSON.stringify(remoteManifest, null, 2)}
|
|
2565
2815
|
`,
|
|
2566
2816
|
"utf8"
|
|
2567
2817
|
);
|
|
2568
2818
|
console.log(
|
|
2569
|
-
`[apm] \u5DF2\u540C\u6B65\
|
|
2819
|
+
`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted}`
|
|
2570
2820
|
);
|
|
2571
2821
|
return {
|
|
2572
2822
|
synced: true,
|
|
2573
|
-
|
|
2823
|
+
projectId,
|
|
2574
2824
|
downloaded,
|
|
2575
2825
|
deleted
|
|
2576
2826
|
};
|
|
@@ -2592,7 +2842,7 @@ async function ensureWorkspaceInitialized(workdir, options) {
|
|
|
2592
2842
|
const apmDir = workspaceApmDir(workdir);
|
|
2593
2843
|
await copyTemplateFiles(apmDir, workdir);
|
|
2594
2844
|
const syncResult = await syncRemoteDeploymentConfig(workdir, apmDir);
|
|
2595
|
-
await
|
|
2845
|
+
await syncProjectDocumentsPull(workdir, apmDir);
|
|
2596
2846
|
const trimmedName = options?.name?.trim();
|
|
2597
2847
|
if (trimmedName) {
|
|
2598
2848
|
const apmConfigPath = toFsPath(join7(apmDir, "apm.config.json"));
|
|
@@ -2626,8 +2876,9 @@ function markBranchDone(sessionId, workdir) {
|
|
|
2626
2876
|
var TEST_START_ACTIONS = /* @__PURE__ */ new Set([
|
|
2627
2877
|
"generate-cases",
|
|
2628
2878
|
"run-auto-test",
|
|
2629
|
-
|
|
2630
|
-
"
|
|
2879
|
+
// 跳过/直接进手工测试不发 Cursor,PR 延后到部署或验收
|
|
2880
|
+
"deploy",
|
|
2881
|
+
"accept"
|
|
2631
2882
|
]);
|
|
2632
2883
|
function shouldEnsureWebIdePullRequests(action) {
|
|
2633
2884
|
return TEST_START_ACTIONS.has(action);
|
|
@@ -2680,70 +2931,6 @@ async function ensureWebIdePullRequests(cfg, taskId, workdir) {
|
|
|
2680
2931
|
}
|
|
2681
2932
|
}
|
|
2682
2933
|
|
|
2683
|
-
// src/commands/connect/webide-baseline-sync.ts
|
|
2684
|
-
async function abortMergeOrRebase(cwd) {
|
|
2685
|
-
try {
|
|
2686
|
-
await execGit(cwd, ["merge", "--abort"], true);
|
|
2687
|
-
} catch {
|
|
2688
|
-
}
|
|
2689
|
-
try {
|
|
2690
|
-
await execGit(cwd, ["rebase", "--abort"], true);
|
|
2691
|
-
} catch {
|
|
2692
|
-
}
|
|
2693
|
-
}
|
|
2694
|
-
async function syncWebIdeBaselineOrFail(workdir) {
|
|
2695
|
-
let repoRoots;
|
|
2696
|
-
try {
|
|
2697
|
-
const manifest = loadWorkspaceReposCache(workdir);
|
|
2698
|
-
repoRoots = resolveWorkspaceRepoAbsolutePaths(manifest);
|
|
2699
|
-
} catch (err) {
|
|
2700
|
-
return {
|
|
2701
|
-
ok: false,
|
|
2702
|
-
error: `\u8BFB\u53D6\u5DE5\u4F5C\u533A\u4ED3\u5E93\u6E05\u5355\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}`
|
|
2703
|
-
};
|
|
2704
|
-
}
|
|
2705
|
-
const failures = [];
|
|
2706
|
-
for (const repoRoot of repoRoots) {
|
|
2707
|
-
const label = formatRepoLabel(workdir, repoRoot);
|
|
2708
|
-
try {
|
|
2709
|
-
const gitRoot = await resolveGitRepoRoot(repoRoot);
|
|
2710
|
-
const baselineBranch = await resolveDefaultRemoteBranch(gitRoot);
|
|
2711
|
-
await ensureRemoteBaselineBranch(gitRoot, baselineBranch);
|
|
2712
|
-
const currentBranch = await getCurrentBranch(gitRoot);
|
|
2713
|
-
console.log(
|
|
2714
|
-
`[apm] webide accept sync ${label}: ${currentBranch} \u2190 origin/${baselineBranch}`
|
|
2715
|
-
);
|
|
2716
|
-
try {
|
|
2717
|
-
await execGit(
|
|
2718
|
-
gitRoot,
|
|
2719
|
-
["merge", `origin/${baselineBranch}`, "--no-edit"],
|
|
2720
|
-
true
|
|
2721
|
-
);
|
|
2722
|
-
} catch (mergeErr) {
|
|
2723
|
-
await abortMergeOrRebase(gitRoot);
|
|
2724
|
-
const detail = mergeErr instanceof Error ? mergeErr.message : String(mergeErr);
|
|
2725
|
-
failures.push(
|
|
2726
|
-
`${label}\uFF1A\u5408\u5E76 origin/${baselineBranch} \u51B2\u7A81\uFF0C\u5DF2\u6267\u884C merge --abort \u64A4\u56DE\u672C\u6B21\u540C\u6B65\u3002${detail}`
|
|
2727
|
-
);
|
|
2728
|
-
}
|
|
2729
|
-
} catch (err) {
|
|
2730
|
-
failures.push(
|
|
2731
|
-
`${label}\uFF1A${err instanceof Error ? err.message : String(err)}`
|
|
2732
|
-
);
|
|
2733
|
-
}
|
|
2734
|
-
}
|
|
2735
|
-
if (failures.length > 0) {
|
|
2736
|
-
return {
|
|
2737
|
-
ok: false,
|
|
2738
|
-
error: [
|
|
2739
|
-
"\u62C9\u53D6\u6700\u65B0\u57FA\u7EBF\u5931\u8D25\uFF0C\u5DF2\u64A4\u56DE\u672C\u6B21\u540C\u6B65\u64CD\u4F5C\uFF0C\u8BF7\u4EBA\u5DE5\u5904\u7406\u51B2\u7A81\u540E\u91CD\u8BD5\u9A8C\u6536\u5408\u5E76\u3002",
|
|
2740
|
-
...failures.map((f) => `- ${f}`)
|
|
2741
|
-
].join("\n")
|
|
2742
|
-
};
|
|
2743
|
-
}
|
|
2744
|
-
return { ok: true };
|
|
2745
|
-
}
|
|
2746
|
-
|
|
2747
2934
|
// src/version.ts
|
|
2748
2935
|
import { readFileSync as readFileSync8 } from "fs";
|
|
2749
2936
|
import { dirname as dirname5, join as join8 } from "path";
|
|
@@ -2955,7 +3142,8 @@ var WEBIDE_CODE_CHANGE_ACTIONS = /* @__PURE__ */ new Set([
|
|
|
2955
3142
|
"skip-plan",
|
|
2956
3143
|
"start-develop",
|
|
2957
3144
|
"fix-and-retest",
|
|
2958
|
-
"report-defect"
|
|
3145
|
+
"report-defect",
|
|
3146
|
+
"deploy"
|
|
2959
3147
|
]);
|
|
2960
3148
|
function shouldCommitAfterWebIdeMessage(action) {
|
|
2961
3149
|
return WEBIDE_CODE_CHANGE_ACTIONS.has(action);
|
|
@@ -2972,7 +3160,7 @@ async function appendContent(cfg, messageId, content) {
|
|
|
2972
3160
|
const api = createApmApiClient(cfg);
|
|
2973
3161
|
await api.cli.webideAppendMessageContent({ id: messageId, content });
|
|
2974
3162
|
}
|
|
2975
|
-
async function handleWebIdeInboundMessage(cfg, msg, signal) {
|
|
3163
|
+
async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
|
|
2976
3164
|
const workdir = requireRemoteWorkdir(msg.workdir);
|
|
2977
3165
|
const messageId = msg.messageId;
|
|
2978
3166
|
const taskId = msg.taskId;
|
|
@@ -3018,22 +3206,6 @@ async function handleWebIdeInboundMessage(cfg, msg, signal) {
|
|
|
3018
3206
|
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
3019
3207
|
await ensureWebIdePullRequests(cfg, taskId, workdir);
|
|
3020
3208
|
}
|
|
3021
|
-
if (msg.action === "accept") {
|
|
3022
|
-
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
3023
|
-
const sync = await syncWebIdeBaselineOrFail(workdir);
|
|
3024
|
-
if (!sync.ok) {
|
|
3025
|
-
await setError(cfg, messageId, sync.error);
|
|
3026
|
-
return;
|
|
3027
|
-
}
|
|
3028
|
-
try {
|
|
3029
|
-
await pushWorkspaceRepos(workdir);
|
|
3030
|
-
} catch (err) {
|
|
3031
|
-
console.warn(
|
|
3032
|
-
"[apm] webide accept \u540C\u6B65\u540E push \u5931\u8D25:",
|
|
3033
|
-
err instanceof Error ? err.message : err
|
|
3034
|
-
);
|
|
3035
|
-
}
|
|
3036
|
-
}
|
|
3037
3209
|
const savedAgentId = loadWebIdeAgentId(workdir, taskId);
|
|
3038
3210
|
const logSyncRef = { current: null };
|
|
3039
3211
|
const outcome = await runCursorAgent(
|
|
@@ -3060,6 +3232,7 @@ async function handleWebIdeInboundMessage(cfg, msg, signal) {
|
|
|
3060
3232
|
// 本地 SDK sandbox 会拦截 custom-user-tools MCP,WebIDE 先关闭
|
|
3061
3233
|
enableSandbox: false,
|
|
3062
3234
|
taskId,
|
|
3235
|
+
terminalRpc: options?.terminalRpc,
|
|
3063
3236
|
createRemoteLogSync: (agentId) => {
|
|
3064
3237
|
saveWebIdeAgentId(workdir, taskId, agentId);
|
|
3065
3238
|
logSyncRef.current = createThrottledWebIdeMessageLogSync(
|
|
@@ -3104,7 +3277,7 @@ async function handleWebIdeInboundMessage(cfg, msg, signal) {
|
|
|
3104
3277
|
id: messageId,
|
|
3105
3278
|
content: fallback
|
|
3106
3279
|
});
|
|
3107
|
-
if (msg.action === "write-plan" && outcome.createPlan && outcome.createPlan.trim()) {
|
|
3280
|
+
if ((msg.action === "write-plan" || msg.action === "revise-plan") && outcome.createPlan && outcome.createPlan.trim()) {
|
|
3108
3281
|
try {
|
|
3109
3282
|
await api.cli.webideUpsertPlan({
|
|
3110
3283
|
taskId,
|
|
@@ -3112,7 +3285,7 @@ async function handleWebIdeInboundMessage(cfg, msg, signal) {
|
|
|
3112
3285
|
});
|
|
3113
3286
|
} catch (err) {
|
|
3114
3287
|
console.warn(
|
|
3115
|
-
|
|
3288
|
+
`[apm] ${msg.action} \u8865\u5199\u8BA1\u5212\u5931\u8D25:`,
|
|
3116
3289
|
err instanceof Error ? err.message : err
|
|
3117
3290
|
);
|
|
3118
3291
|
}
|
|
@@ -3154,19 +3327,68 @@ async function handleWebIdeInboundMessage(cfg, msg, signal) {
|
|
|
3154
3327
|
}
|
|
3155
3328
|
}
|
|
3156
3329
|
|
|
3330
|
+
// src/commands/connect/webide-terminal-rpc.ts
|
|
3331
|
+
import { parentPort } from "node:worker_threads";
|
|
3332
|
+
var nextId = 1;
|
|
3333
|
+
var pending = /* @__PURE__ */ new Map();
|
|
3334
|
+
function installTerminalRpcListener() {
|
|
3335
|
+
parentPort?.on("message", (raw) => {
|
|
3336
|
+
if (!raw || raw.type !== "terminal-rpc-result") return;
|
|
3337
|
+
const p = pending.get(raw.id);
|
|
3338
|
+
if (!p) return;
|
|
3339
|
+
pending.delete(raw.id);
|
|
3340
|
+
if (raw.ok) p.resolve(raw.result);
|
|
3341
|
+
else p.reject(new Error(raw.error || "terminal rpc failed"));
|
|
3342
|
+
});
|
|
3343
|
+
}
|
|
3344
|
+
function call(op, args) {
|
|
3345
|
+
const id = nextId++;
|
|
3346
|
+
return new Promise((resolve6, reject) => {
|
|
3347
|
+
if (!parentPort) {
|
|
3348
|
+
reject(new Error("parentPort \u4E0D\u53EF\u7528"));
|
|
3349
|
+
return;
|
|
3350
|
+
}
|
|
3351
|
+
pending.set(id, { resolve: resolve6, reject });
|
|
3352
|
+
parentPort.postMessage({
|
|
3353
|
+
type: "terminal-rpc",
|
|
3354
|
+
id,
|
|
3355
|
+
op,
|
|
3356
|
+
args
|
|
3357
|
+
});
|
|
3358
|
+
setTimeout(() => {
|
|
3359
|
+
if (pending.has(id)) {
|
|
3360
|
+
pending.delete(id);
|
|
3361
|
+
reject(new Error(`terminal rpc timeout op=${op}`));
|
|
3362
|
+
}
|
|
3363
|
+
}, 12e4);
|
|
3364
|
+
});
|
|
3365
|
+
}
|
|
3366
|
+
function createWorkerTerminalRpc() {
|
|
3367
|
+
return {
|
|
3368
|
+
start: (args) => call("start", args),
|
|
3369
|
+
stop: (args) => call("stop", args),
|
|
3370
|
+
stopAll: (args) => call("stopAll", args),
|
|
3371
|
+
list: (args) => call("list", args)
|
|
3372
|
+
};
|
|
3373
|
+
}
|
|
3374
|
+
|
|
3157
3375
|
// src/commands/connect/webide-message-worker.ts
|
|
3158
3376
|
var controllers = /* @__PURE__ */ new Map();
|
|
3377
|
+
installTerminalRpcListener();
|
|
3378
|
+
var terminalRpc = createWorkerTerminalRpc();
|
|
3159
3379
|
async function runJob(cfg, msg) {
|
|
3160
3380
|
const controller = new AbortController();
|
|
3161
3381
|
controllers.set(msg.messageId, controller);
|
|
3162
3382
|
try {
|
|
3163
|
-
await handleWebIdeInboundMessage(cfg, msg, controller.signal
|
|
3164
|
-
|
|
3383
|
+
await handleWebIdeInboundMessage(cfg, msg, controller.signal, {
|
|
3384
|
+
terminalRpc
|
|
3385
|
+
});
|
|
3386
|
+
parentPort2?.postMessage({
|
|
3165
3387
|
type: "done",
|
|
3166
3388
|
messageId: msg.messageId
|
|
3167
3389
|
});
|
|
3168
3390
|
} catch (err) {
|
|
3169
|
-
|
|
3391
|
+
parentPort2?.postMessage({
|
|
3170
3392
|
type: "error",
|
|
3171
3393
|
messageId: msg.messageId,
|
|
3172
3394
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -3175,7 +3397,7 @@ async function runJob(cfg, msg) {
|
|
|
3175
3397
|
controllers.delete(msg.messageId);
|
|
3176
3398
|
}
|
|
3177
3399
|
}
|
|
3178
|
-
|
|
3400
|
+
parentPort2?.on("message", (raw) => {
|
|
3179
3401
|
if (raw.type === "cancel") {
|
|
3180
3402
|
controllers.get(raw.messageId)?.abort();
|
|
3181
3403
|
return;
|