ai-project-manage-cli 8.0.17 → 8.1.0
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 +263 -372
- package/dist/{webide-message-worker.js → worker-script.js} +1926 -1603
- package/package.json +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// src/commands/connect/
|
|
1
|
+
// src/commands/connect/core/worker/worker-script.ts
|
|
2
2
|
import { parentPort, workerData } from "node:worker_threads";
|
|
3
3
|
|
|
4
4
|
// src/api/client.ts
|
|
@@ -30,6 +30,11 @@ var requestConfig = {
|
|
|
30
30
|
method: "PUT",
|
|
31
31
|
path: "/cli/webide/messages/status"
|
|
32
32
|
}),
|
|
33
|
+
/** CLI 改工作流状态统一入口(start-/complete- 等) */
|
|
34
|
+
webideRunAction: defineEndpoint({
|
|
35
|
+
method: "POST",
|
|
36
|
+
path: "/cli/webide/actions"
|
|
37
|
+
}),
|
|
33
38
|
webideUpsertMessageLog: defineEndpoint({
|
|
34
39
|
method: "PUT",
|
|
35
40
|
path: "/cli/webide/message-logs"
|
|
@@ -46,6 +51,14 @@ var requestConfig = {
|
|
|
46
51
|
method: "GET",
|
|
47
52
|
path: "/cli/webide/attachments"
|
|
48
53
|
}),
|
|
54
|
+
webideListDesignArtifacts: defineEndpoint({
|
|
55
|
+
method: "GET",
|
|
56
|
+
path: "/cli/webide/design-artifacts"
|
|
57
|
+
}),
|
|
58
|
+
webideRegisterDesignArtifacts: defineEndpoint({
|
|
59
|
+
method: "PUT",
|
|
60
|
+
path: "/cli/webide/design-artifacts"
|
|
61
|
+
}),
|
|
49
62
|
webideRecommendPlan: defineEndpoint({
|
|
50
63
|
method: "PUT",
|
|
51
64
|
path: "/cli/webide/plan-recommendation"
|
|
@@ -122,13 +135,13 @@ import { join } from "path";
|
|
|
122
135
|
var APM_CONFIG_DIR = join(homedir(), ".config", "apm");
|
|
123
136
|
var APM_CONFIG_PATH = join(APM_CONFIG_DIR, "config.json");
|
|
124
137
|
var DEFAULT_BASE_URL = "http://127.0.0.1:3000";
|
|
125
|
-
function resolveClientMachineId(
|
|
126
|
-
return (
|
|
138
|
+
function resolveClientMachineId(cfg2) {
|
|
139
|
+
return (cfg2.clientMachineId ?? cfg2.userId ?? "").trim();
|
|
127
140
|
}
|
|
128
|
-
function resolveApiKey(
|
|
129
|
-
return (
|
|
141
|
+
function resolveApiKey(cfg2) {
|
|
142
|
+
return (cfg2.apiKey ?? cfg2.token ?? "").trim();
|
|
130
143
|
}
|
|
131
|
-
|
|
144
|
+
function tryReadApmConfig() {
|
|
132
145
|
try {
|
|
133
146
|
const raw = readFileSync(APM_CONFIG_PATH, "utf8");
|
|
134
147
|
const v = JSON.parse(raw);
|
|
@@ -141,10 +154,10 @@ async function tryReadApmConfig() {
|
|
|
141
154
|
}
|
|
142
155
|
const apiKey = resolveApiKey(rawCfg);
|
|
143
156
|
if (!apiKey) return null;
|
|
144
|
-
const
|
|
145
|
-
const clientMachineId = resolveClientMachineId(
|
|
157
|
+
const cfg2 = v;
|
|
158
|
+
const clientMachineId = resolveClientMachineId(cfg2);
|
|
146
159
|
return {
|
|
147
|
-
baseUrl:
|
|
160
|
+
baseUrl: cfg2.baseUrl.trim().replace(/\/+$/, ""),
|
|
148
161
|
apiKey,
|
|
149
162
|
...clientMachineId ? { clientMachineId } : {}
|
|
150
163
|
};
|
|
@@ -166,11 +179,11 @@ async function ensureApmConfig() {
|
|
|
166
179
|
await writeApmConfig(defaults);
|
|
167
180
|
return defaults;
|
|
168
181
|
}
|
|
169
|
-
async function writeApmConfig(
|
|
170
|
-
const clientMachineId = resolveClientMachineId(
|
|
171
|
-
const apiKey = resolveApiKey(
|
|
182
|
+
async function writeApmConfig(cfg2) {
|
|
183
|
+
const clientMachineId = resolveClientMachineId(cfg2);
|
|
184
|
+
const apiKey = resolveApiKey(cfg2);
|
|
172
185
|
const normalized = {
|
|
173
|
-
baseUrl:
|
|
186
|
+
baseUrl: cfg2.baseUrl.trim().replace(/\/+$/, ""),
|
|
174
187
|
apiKey,
|
|
175
188
|
...clientMachineId ? { clientMachineId } : {}
|
|
176
189
|
};
|
|
@@ -181,11 +194,11 @@ async function writeApmConfig(cfg) {
|
|
|
181
194
|
}
|
|
182
195
|
|
|
183
196
|
// src/api/client.ts
|
|
184
|
-
function createApmApiClient(
|
|
185
|
-
const baseURL = `${
|
|
197
|
+
function createApmApiClient(cfg2) {
|
|
198
|
+
const baseURL = `${cfg2.baseUrl.trim().replace(/\/+$/, "")}/api/v1`;
|
|
186
199
|
return createApiClient(requestConfig, {
|
|
187
200
|
baseURL,
|
|
188
|
-
getToken: () => resolveApiKey(
|
|
201
|
+
getToken: () => resolveApiKey(cfg2) || void 0,
|
|
189
202
|
successCodes: [0],
|
|
190
203
|
unauthorizedCodes: [401]
|
|
191
204
|
});
|
|
@@ -263,21 +276,25 @@ function isWorkspaceApmInitialized(workdir) {
|
|
|
263
276
|
}
|
|
264
277
|
var WEBIDE_SUBDIR = "webide";
|
|
265
278
|
var WEBIDE_ATTACHMENTS_SUBDIR = "attachments";
|
|
279
|
+
var WEBIDE_DESIGN_SUBDIR = "design";
|
|
266
280
|
function webideTaskDir(taskId, workdir) {
|
|
267
281
|
return join2(workdir, ".apm", WEBIDE_SUBDIR, taskId);
|
|
268
282
|
}
|
|
269
283
|
function webideAttachmentsDir(taskId, workdir) {
|
|
270
284
|
return join2(webideTaskDir(taskId, workdir), WEBIDE_ATTACHMENTS_SUBDIR);
|
|
271
285
|
}
|
|
286
|
+
function webideDesignDir(taskId, workdir) {
|
|
287
|
+
return join2(webideTaskDir(taskId, workdir), WEBIDE_DESIGN_SUBDIR);
|
|
288
|
+
}
|
|
272
289
|
async function ensureLoggedConfig() {
|
|
273
|
-
const
|
|
274
|
-
if (!resolveApiKey(
|
|
290
|
+
const cfg2 = await ensureApmConfig();
|
|
291
|
+
if (!resolveApiKey(cfg2)) {
|
|
275
292
|
console.error(
|
|
276
293
|
"[apm] \u672A\u68C0\u6D4B\u5230 API Key\uFF0C\u8BF7\u5148\u6267\u884C: apm login --api-key cm_xxx"
|
|
277
294
|
);
|
|
278
295
|
process.exit(1);
|
|
279
296
|
}
|
|
280
|
-
return
|
|
297
|
+
return cfg2;
|
|
281
298
|
}
|
|
282
299
|
async function ensureDirExists(dir) {
|
|
283
300
|
mkdirSync2(dir, { recursive: true });
|
|
@@ -923,8 +940,8 @@ async function runTaskBranch(taskId, options = {}) {
|
|
|
923
940
|
if (!trimmedTaskId) {
|
|
924
941
|
throw new Error("[apm] taskId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
925
942
|
}
|
|
926
|
-
const
|
|
927
|
-
const api = createApmApiClient(
|
|
943
|
+
const cfg2 = await ensureLoggedConfig();
|
|
944
|
+
const api = createApmApiClient(cfg2);
|
|
928
945
|
const projectBaseline = await api.cli.projectBaseBranch({
|
|
929
946
|
taskId: trimmedTaskId
|
|
930
947
|
});
|
|
@@ -1708,7 +1725,7 @@ function createMysqlExecuteTool(options) {
|
|
|
1708
1725
|
const password = asString3(args.password);
|
|
1709
1726
|
const database = asString3(args.database);
|
|
1710
1727
|
const pathArg = asString3(args.path);
|
|
1711
|
-
const
|
|
1728
|
+
const port2 = asNumber(args.port) ?? 3306;
|
|
1712
1729
|
if (!host || !user || !database || !pathArg) {
|
|
1713
1730
|
return {
|
|
1714
1731
|
content: [
|
|
@@ -1730,7 +1747,7 @@ function createMysqlExecuteTool(options) {
|
|
|
1730
1747
|
artifactPath: pathArg,
|
|
1731
1748
|
error: resolved.error,
|
|
1732
1749
|
host,
|
|
1733
|
-
port,
|
|
1750
|
+
port: port2,
|
|
1734
1751
|
database,
|
|
1735
1752
|
dbUser: user
|
|
1736
1753
|
});
|
|
@@ -1754,7 +1771,7 @@ function createMysqlExecuteTool(options) {
|
|
|
1754
1771
|
artifactPath: resolved.displayPath,
|
|
1755
1772
|
error: `\u8BFB\u53D6 SQL \u6587\u4EF6\u5931\u8D25: ${detail}`,
|
|
1756
1773
|
host,
|
|
1757
|
-
port,
|
|
1774
|
+
port: port2,
|
|
1758
1775
|
database,
|
|
1759
1776
|
dbUser: user
|
|
1760
1777
|
});
|
|
@@ -1780,7 +1797,7 @@ function createMysqlExecuteTool(options) {
|
|
|
1780
1797
|
sql: "",
|
|
1781
1798
|
error: "SQL \u6587\u4EF6\u4E3A\u7A7A",
|
|
1782
1799
|
host,
|
|
1783
|
-
port,
|
|
1800
|
+
port: port2,
|
|
1784
1801
|
database,
|
|
1785
1802
|
dbUser: user
|
|
1786
1803
|
});
|
|
@@ -1795,7 +1812,7 @@ function createMysqlExecuteTool(options) {
|
|
|
1795
1812
|
try {
|
|
1796
1813
|
connection = await mysql.createConnection({
|
|
1797
1814
|
host,
|
|
1798
|
-
port,
|
|
1815
|
+
port: port2,
|
|
1799
1816
|
user,
|
|
1800
1817
|
password,
|
|
1801
1818
|
database,
|
|
@@ -1812,7 +1829,7 @@ function createMysqlExecuteTool(options) {
|
|
|
1812
1829
|
path: resolved.displayPath,
|
|
1813
1830
|
statements,
|
|
1814
1831
|
host,
|
|
1815
|
-
port,
|
|
1832
|
+
port: port2,
|
|
1816
1833
|
database,
|
|
1817
1834
|
dbUser: user
|
|
1818
1835
|
};
|
|
@@ -1824,7 +1841,7 @@ function createMysqlExecuteTool(options) {
|
|
|
1824
1841
|
sql,
|
|
1825
1842
|
result: resultPayload,
|
|
1826
1843
|
host,
|
|
1827
|
-
port,
|
|
1844
|
+
port: port2,
|
|
1828
1845
|
database,
|
|
1829
1846
|
dbUser: user
|
|
1830
1847
|
});
|
|
@@ -1840,7 +1857,7 @@ function createMysqlExecuteTool(options) {
|
|
|
1840
1857
|
sql,
|
|
1841
1858
|
error: detail,
|
|
1842
1859
|
host: host || void 0,
|
|
1843
|
-
port,
|
|
1860
|
+
port: port2,
|
|
1844
1861
|
database: database || void 0,
|
|
1845
1862
|
dbUser: user || void 0
|
|
1846
1863
|
});
|
|
@@ -1925,12 +1942,12 @@ var MinioClient = class {
|
|
|
1925
1942
|
};
|
|
1926
1943
|
|
|
1927
1944
|
// src/commands/connect/apm-log-minio.ts
|
|
1928
|
-
async function fetchApmLogStorage(
|
|
1929
|
-
const api = createApmApiClient(
|
|
1945
|
+
async function fetchApmLogStorage(cfg2) {
|
|
1946
|
+
const api = createApmApiClient(cfg2);
|
|
1930
1947
|
return api.cli.getApmLogStorage(void 0);
|
|
1931
1948
|
}
|
|
1932
|
-
async function createApmLogMinioClient(
|
|
1933
|
-
const storage = await fetchApmLogStorage(
|
|
1949
|
+
async function createApmLogMinioClient(cfg2) {
|
|
1950
|
+
const storage = await fetchApmLogStorage(cfg2);
|
|
1934
1951
|
return {
|
|
1935
1952
|
client: new MinioClient({
|
|
1936
1953
|
endPoint: storage.endpoint,
|
|
@@ -2116,7 +2133,7 @@ function resolveLocalSqlDir(workdir, localDirArg) {
|
|
|
2116
2133
|
return { ok: true, absPath: targetAbs };
|
|
2117
2134
|
}
|
|
2118
2135
|
function createUploadPackageSqlTool(options) {
|
|
2119
|
-
const { cfg, workdir, projectId } = options;
|
|
2136
|
+
const { cfg: cfg2, workdir, projectId } = options;
|
|
2120
2137
|
return {
|
|
2121
2138
|
UploadPackageSql: {
|
|
2122
2139
|
description: "Upload SQL scripts from a local directory to project MinIO mirror. Call after reading deploy docs: pass repo (directory name from\u300C\u8FDB\u5165 xxx \u76EE\u5F55\u300D) and localDir (absolute path to the SQL script directory). Syncs **/*.sql under localDir to packages/{projectId}/{repo}/.... Do not put SQL into jars.zip.",
|
|
@@ -2168,7 +2185,7 @@ function createUploadPackageSqlTool(options) {
|
|
|
2168
2185
|
}
|
|
2169
2186
|
try {
|
|
2170
2187
|
const result = await syncPackageSqlToMinio({
|
|
2171
|
-
cfg,
|
|
2188
|
+
cfg: cfg2,
|
|
2172
2189
|
projectId,
|
|
2173
2190
|
repo,
|
|
2174
2191
|
localDir: resolved.absPath
|
|
@@ -2233,7 +2250,7 @@ AskQuestion \u5DF2\u901A\u8FC7 MCP \u670D\u52A1\u5668 custom-user-tools \u6CE8\u
|
|
|
2233
2250
|
var WORKSPACE_BOUNDARY_HINT = `[\u5DE5\u4F5C\u533A\u8FB9\u754C]
|
|
2234
2251
|
\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
|
|
2235
2252
|
\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`;
|
|
2236
|
-
function createCursorCustomTools(
|
|
2253
|
+
function createCursorCustomTools(cfg2, options) {
|
|
2237
2254
|
const tools = {
|
|
2238
2255
|
...options.enableAppendMessage !== false && options.appendMessageContent ? createAppendMessageCustomTools({
|
|
2239
2256
|
appendContent: options.appendMessageContent
|
|
@@ -2244,7 +2261,7 @@ function createCursorCustomTools(cfg, options) {
|
|
|
2244
2261
|
}) : {}
|
|
2245
2262
|
};
|
|
2246
2263
|
if (options?.taskId && options.workdir) {
|
|
2247
|
-
const { cli } = createApmApiClient(
|
|
2264
|
+
const { cli } = createApmApiClient(cfg2);
|
|
2248
2265
|
const taskId = options.taskId;
|
|
2249
2266
|
if (options.enableWebIdePlanTools) {
|
|
2250
2267
|
Object.assign(
|
|
@@ -2277,7 +2294,7 @@ function createCursorCustomTools(cfg, options) {
|
|
|
2277
2294
|
}
|
|
2278
2295
|
if (options.enablePackageStatusTools && options.packageId) {
|
|
2279
2296
|
const packageId = options.packageId;
|
|
2280
|
-
const { cli } = createApmApiClient(
|
|
2297
|
+
const { cli } = createApmApiClient(cfg2);
|
|
2281
2298
|
Object.assign(
|
|
2282
2299
|
tools,
|
|
2283
2300
|
createSetPackageFailedTool({
|
|
@@ -2297,7 +2314,7 @@ function createCursorCustomTools(cfg, options) {
|
|
|
2297
2314
|
Object.assign(
|
|
2298
2315
|
tools,
|
|
2299
2316
|
createUploadPackageSqlTool({
|
|
2300
|
-
cfg,
|
|
2317
|
+
cfg: cfg2,
|
|
2301
2318
|
workdir: options.workdir,
|
|
2302
2319
|
projectId
|
|
2303
2320
|
})
|
|
@@ -2320,17 +2337,17 @@ function withWorkspaceBoundaryHint(prompt) {
|
|
|
2320
2337
|
${WORKSPACE_BOUNDARY_HINT}`;
|
|
2321
2338
|
}
|
|
2322
2339
|
|
|
2323
|
-
// src/commands/connect/pty-session-mcp.ts
|
|
2340
|
+
// src/commands/connect/mcps/pty-session-mcp.ts
|
|
2324
2341
|
var PTY_SESSION_MCP_NAME = "pty-session";
|
|
2325
2342
|
var DEFAULT_PTY_SESSION_PORT = "7788";
|
|
2326
2343
|
function resolvePtySessionBaseUrl(env = process.env) {
|
|
2327
2344
|
const explicit = env.LOCAL_TERMINAL_URL?.trim();
|
|
2328
2345
|
if (explicit) return explicit.replace(/\/+$/, "");
|
|
2329
|
-
const
|
|
2330
|
-
return `http://127.0.0.1:${
|
|
2346
|
+
const port2 = env.LOCAL_TERMINAL_PORT?.trim() || DEFAULT_PTY_SESSION_PORT;
|
|
2347
|
+
return `http://127.0.0.1:${port2}`;
|
|
2331
2348
|
}
|
|
2332
|
-
function createPtySessionMcpServers(
|
|
2333
|
-
const token = resolveApiKey(
|
|
2349
|
+
function createPtySessionMcpServers(cfg2) {
|
|
2350
|
+
const token = resolveApiKey(cfg2);
|
|
2334
2351
|
if (!token) return void 0;
|
|
2335
2352
|
return {
|
|
2336
2353
|
[PTY_SESSION_MCP_NAME]: {
|
|
@@ -2404,7 +2421,7 @@ async function obtainAgent(ctx) {
|
|
|
2404
2421
|
throw err;
|
|
2405
2422
|
}
|
|
2406
2423
|
}
|
|
2407
|
-
async function runCursorAgent(
|
|
2424
|
+
async function runCursorAgent(cfg2, ctx, options) {
|
|
2408
2425
|
const signal = options.signal;
|
|
2409
2426
|
if (signal?.aborted) {
|
|
2410
2427
|
throw new Error("\u8FDE\u63A5\u5DF2\u5173\u95ED\uFF0C\u4EFB\u52A1\u4E2D\u65AD");
|
|
@@ -2414,7 +2431,7 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
2414
2431
|
throw new Error("\u7F3A\u5C11 apiKey\uFF0C\u65E0\u6CD5\u8C03\u7528 Cursor SDK");
|
|
2415
2432
|
}
|
|
2416
2433
|
const workdir = resolveWorkdirPath(ctx.workdir);
|
|
2417
|
-
const customTools = createCursorCustomTools(
|
|
2434
|
+
const customTools = createCursorCustomTools(cfg2, {
|
|
2418
2435
|
onAskQuestion: options.onAskQuestion,
|
|
2419
2436
|
appendMessageContent: options.appendMessageContent,
|
|
2420
2437
|
enableAppendMessage: options.enableAppendMessage,
|
|
@@ -2431,7 +2448,7 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
2431
2448
|
onPackageFailed: options.onPackageFailed
|
|
2432
2449
|
});
|
|
2433
2450
|
const enableSandbox = Boolean(options.enableSandbox);
|
|
2434
|
-
const mcpServers = options.enablePtySessionMcp ? createPtySessionMcpServers(
|
|
2451
|
+
const mcpServers = options.enablePtySessionMcp ? createPtySessionMcpServers(cfg2) : void 0;
|
|
2435
2452
|
let prompt = withPlanModeToolHint(ctx.prompt, ctx.mode);
|
|
2436
2453
|
prompt = withWorkspaceBoundaryHint(prompt);
|
|
2437
2454
|
console.log(
|
|
@@ -2560,265 +2577,6 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
2560
2577
|
}
|
|
2561
2578
|
}
|
|
2562
2579
|
|
|
2563
|
-
// src/commands/connect/webide-agent-registry.ts
|
|
2564
|
-
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
2565
|
-
import { dirname as dirname3, resolve as resolve6 } from "node:path";
|
|
2566
|
-
function registryPath(workdir, taskId) {
|
|
2567
|
-
return resolve6(workdir, ".apm", "webide", taskId, "cursor-agent.json");
|
|
2568
|
-
}
|
|
2569
|
-
function readRegistry(path3) {
|
|
2570
|
-
if (!existsSync5(path3)) {
|
|
2571
|
-
return {};
|
|
2572
|
-
}
|
|
2573
|
-
try {
|
|
2574
|
-
const parsed = JSON.parse(readFileSync4(path3, "utf8"));
|
|
2575
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
2576
|
-
const raw = parsed;
|
|
2577
|
-
const state = {};
|
|
2578
|
-
if (typeof raw.agentId === "string" && raw.agentId.trim()) {
|
|
2579
|
-
state.agentId = raw.agentId.trim();
|
|
2580
|
-
}
|
|
2581
|
-
if (typeof raw.taskId === "string" && raw.taskId.trim()) {
|
|
2582
|
-
state.taskId = raw.taskId.trim();
|
|
2583
|
-
}
|
|
2584
|
-
if (typeof raw.messageId === "string" && raw.messageId.trim()) {
|
|
2585
|
-
state.messageId = raw.messageId.trim();
|
|
2586
|
-
}
|
|
2587
|
-
if (typeof raw.action === "string" && raw.action.trim()) {
|
|
2588
|
-
state.action = raw.action.trim();
|
|
2589
|
-
}
|
|
2590
|
-
if (raw.status === "TYPING" || raw.status === "SUCCESS" || raw.status === "FAILED" || raw.status === "CANCELLED") {
|
|
2591
|
-
state.status = raw.status;
|
|
2592
|
-
}
|
|
2593
|
-
if (typeof raw.updatedAt === "string" && raw.updatedAt.trim()) {
|
|
2594
|
-
state.updatedAt = raw.updatedAt.trim();
|
|
2595
|
-
}
|
|
2596
|
-
return state;
|
|
2597
|
-
}
|
|
2598
|
-
} catch {
|
|
2599
|
-
}
|
|
2600
|
-
return {};
|
|
2601
|
-
}
|
|
2602
|
-
function writeRegistry(path3, registry) {
|
|
2603
|
-
mkdirSync4(dirname3(path3), { recursive: true });
|
|
2604
|
-
writeFileSync3(path3, `${JSON.stringify(registry, null, 2)}
|
|
2605
|
-
`, "utf8");
|
|
2606
|
-
}
|
|
2607
|
-
function syncWebIdeTaskState(workdir, taskId, patch) {
|
|
2608
|
-
const trimmedTaskId = taskId.trim();
|
|
2609
|
-
if (!trimmedTaskId) return;
|
|
2610
|
-
const path3 = registryPath(workdir, trimmedTaskId);
|
|
2611
|
-
const current = readRegistry(path3);
|
|
2612
|
-
const next = {
|
|
2613
|
-
...current,
|
|
2614
|
-
taskId: trimmedTaskId,
|
|
2615
|
-
updatedAt: patch.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
2616
|
-
};
|
|
2617
|
-
if (patch.agentId !== void 0) {
|
|
2618
|
-
const agentId = patch.agentId.trim();
|
|
2619
|
-
if (agentId) next.agentId = agentId;
|
|
2620
|
-
else delete next.agentId;
|
|
2621
|
-
}
|
|
2622
|
-
if (patch.messageId !== void 0) {
|
|
2623
|
-
const messageId = patch.messageId.trim();
|
|
2624
|
-
if (messageId) next.messageId = messageId;
|
|
2625
|
-
else delete next.messageId;
|
|
2626
|
-
}
|
|
2627
|
-
if (patch.action !== void 0) {
|
|
2628
|
-
const action = patch.action.trim();
|
|
2629
|
-
if (action) next.action = action;
|
|
2630
|
-
else delete next.action;
|
|
2631
|
-
}
|
|
2632
|
-
if (patch.status !== void 0) {
|
|
2633
|
-
next.status = patch.status;
|
|
2634
|
-
}
|
|
2635
|
-
writeRegistry(path3, next);
|
|
2636
|
-
}
|
|
2637
|
-
function loadWebIdeAgentId(workdir, taskId) {
|
|
2638
|
-
return readRegistry(registryPath(workdir, taskId)).agentId;
|
|
2639
|
-
}
|
|
2640
|
-
function saveWebIdeAgentId(workdir, taskId, agentId) {
|
|
2641
|
-
syncWebIdeTaskState(workdir, taskId, { agentId });
|
|
2642
|
-
}
|
|
2643
|
-
function clearWebIdeAgentId(workdir, taskId) {
|
|
2644
|
-
const path3 = registryPath(workdir, taskId);
|
|
2645
|
-
if (!existsSync5(path3)) return;
|
|
2646
|
-
syncWebIdeTaskState(workdir, taskId, { agentId: "" });
|
|
2647
|
-
}
|
|
2648
|
-
|
|
2649
|
-
// src/commands/clean-webide-cache.ts
|
|
2650
|
-
import { existsSync as existsSync6, rmSync } from "node:fs";
|
|
2651
|
-
import { resolve as resolve7 } from "node:path";
|
|
2652
|
-
import { getDefaultSdkStateRoot } from "@cursor/sdk";
|
|
2653
|
-
async function purgeCursorAgentStoreForAgent(workdir, agentId) {
|
|
2654
|
-
const trimmedAgentId = agentId.trim();
|
|
2655
|
-
const trimmedWorkdir = workdir.trim();
|
|
2656
|
-
if (!trimmedAgentId || !trimmedWorkdir) return false;
|
|
2657
|
-
const stateRoot = getDefaultSdkStateRoot(trimmedWorkdir);
|
|
2658
|
-
if (!existsSync6(stateRoot)) return false;
|
|
2659
|
-
const { SqliteLocalAgentStore } = await import(
|
|
2660
|
-
/* @vite-ignore */
|
|
2661
|
-
"@cursor/sdk/sqlite"
|
|
2662
|
-
);
|
|
2663
|
-
const store = await SqliteLocalAgentStore.open({
|
|
2664
|
-
workspaceRef: trimmedWorkdir
|
|
2665
|
-
});
|
|
2666
|
-
try {
|
|
2667
|
-
const runIds = [];
|
|
2668
|
-
let cursor;
|
|
2669
|
-
do {
|
|
2670
|
-
const page = await store.runs.list({
|
|
2671
|
-
filter: {
|
|
2672
|
-
agentIds: [trimmedAgentId],
|
|
2673
|
-
...cursor ? { cursor } : {},
|
|
2674
|
-
limit: 200
|
|
2675
|
-
}
|
|
2676
|
-
});
|
|
2677
|
-
for (const run of page.items) {
|
|
2678
|
-
runIds.push(run.runId);
|
|
2679
|
-
}
|
|
2680
|
-
cursor = page.nextCursor;
|
|
2681
|
-
} while (cursor);
|
|
2682
|
-
if (runIds.length > 0) {
|
|
2683
|
-
await store.runEvents.delete({ filter: { runIds } });
|
|
2684
|
-
}
|
|
2685
|
-
await store.runs.delete({ filter: { agentIds: [trimmedAgentId] } });
|
|
2686
|
-
await store.checkpoints.delete({ filter: { agentIds: [trimmedAgentId] } });
|
|
2687
|
-
await store.agents.delete({ filter: { agentIds: [trimmedAgentId] } });
|
|
2688
|
-
} finally {
|
|
2689
|
-
await store.dispose();
|
|
2690
|
-
}
|
|
2691
|
-
return true;
|
|
2692
|
-
}
|
|
2693
|
-
async function cleanWebIdeWorkspaceCache(taskId, workdir) {
|
|
2694
|
-
const trimmedTaskId = taskId.trim();
|
|
2695
|
-
const trimmedWorkdir = workdir.trim();
|
|
2696
|
-
if (!trimmedTaskId || !trimmedWorkdir) {
|
|
2697
|
-
return;
|
|
2698
|
-
}
|
|
2699
|
-
const agentId = loadWebIdeAgentId(trimmedWorkdir, trimmedTaskId);
|
|
2700
|
-
if (agentId) {
|
|
2701
|
-
try {
|
|
2702
|
-
const purged = await purgeCursorAgentStoreForAgent(
|
|
2703
|
-
trimmedWorkdir,
|
|
2704
|
-
agentId
|
|
2705
|
-
);
|
|
2706
|
-
if (purged) {
|
|
2707
|
-
console.log(
|
|
2708
|
-
`[apm] \u5DF2\u6E05\u7406 Cursor agent store agentId=${agentId} taskId=${trimmedTaskId}`
|
|
2709
|
-
);
|
|
2710
|
-
}
|
|
2711
|
-
} catch (err) {
|
|
2712
|
-
console.warn(
|
|
2713
|
-
`[apm] \u6E05\u7406 Cursor agent store \u5931\u8D25 agentId=${agentId}:`,
|
|
2714
|
-
err instanceof Error ? err.message : err
|
|
2715
|
-
);
|
|
2716
|
-
}
|
|
2717
|
-
} else {
|
|
2718
|
-
console.log(
|
|
2719
|
-
`[apm] \u65E0 WebIDE agentId\uFF0C\u8DF3\u8FC7 agent store \u6E05\u7406 taskId=${trimmedTaskId}`
|
|
2720
|
-
);
|
|
2721
|
-
}
|
|
2722
|
-
const dir = resolve7(trimmedWorkdir, ".apm", "webide", trimmedTaskId);
|
|
2723
|
-
if (existsSync6(dir)) {
|
|
2724
|
-
rmSync(dir, { recursive: true, force: true });
|
|
2725
|
-
console.log(`[apm] \u5DF2\u6E05\u7406 WebIDE \u7F13\u5B58 ${dir}`);
|
|
2726
|
-
} else {
|
|
2727
|
-
console.log(`[apm] WebIDE \u7F13\u5B58\u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7 ${dir}`);
|
|
2728
|
-
}
|
|
2729
|
-
}
|
|
2730
|
-
|
|
2731
|
-
// src/commands/connect/webide-ask-question.ts
|
|
2732
|
-
import { setTimeout as delay } from "node:timers/promises";
|
|
2733
|
-
var POLL_INTERVAL_MS = 2e3;
|
|
2734
|
-
function asString5(value) {
|
|
2735
|
-
return typeof value === "string" ? value.trim() : "";
|
|
2736
|
-
}
|
|
2737
|
-
function parseQuestions(args) {
|
|
2738
|
-
const title = asString5(args.title) || void 0;
|
|
2739
|
-
const raw = args.questions;
|
|
2740
|
-
if (!Array.isArray(raw) || raw.length === 0) {
|
|
2741
|
-
throw new Error("AskQuestion \u7F3A\u5C11 questions");
|
|
2742
|
-
}
|
|
2743
|
-
const questions = [];
|
|
2744
|
-
for (const item of raw) {
|
|
2745
|
-
if (!item || typeof item !== "object" || Array.isArray(item)) continue;
|
|
2746
|
-
const row = item;
|
|
2747
|
-
const id = asString5(row.id);
|
|
2748
|
-
const prompt = asString5(row.prompt);
|
|
2749
|
-
const optionsRaw = row.options;
|
|
2750
|
-
if (!id || !prompt || !Array.isArray(optionsRaw)) continue;
|
|
2751
|
-
const options = [];
|
|
2752
|
-
for (const opt of optionsRaw) {
|
|
2753
|
-
if (!opt || typeof opt !== "object" || Array.isArray(opt)) continue;
|
|
2754
|
-
const o = opt;
|
|
2755
|
-
const oid = asString5(o.id);
|
|
2756
|
-
const label = asString5(o.label);
|
|
2757
|
-
if (oid && label) options.push({ id: oid, label });
|
|
2758
|
-
}
|
|
2759
|
-
if (options.length < 2) {
|
|
2760
|
-
throw new Error(`AskQuestion \u95EE\u9898 ${id} \u81F3\u5C11\u9700\u8981 2 \u4E2A\u9009\u9879`);
|
|
2761
|
-
}
|
|
2762
|
-
questions.push({ id, prompt, options });
|
|
2763
|
-
}
|
|
2764
|
-
if (questions.length === 0) {
|
|
2765
|
-
throw new Error("AskQuestion \u65E0\u6709\u6548\u95EE\u9898");
|
|
2766
|
-
}
|
|
2767
|
-
return { title, questions };
|
|
2768
|
-
}
|
|
2769
|
-
function createWebIdeAskQuestionExecute(options) {
|
|
2770
|
-
const { cfg, taskId, signal } = options;
|
|
2771
|
-
return async (args) => {
|
|
2772
|
-
const parsed = parseQuestions(args);
|
|
2773
|
-
const api = createApmApiClient(cfg);
|
|
2774
|
-
console.log(
|
|
2775
|
-
`[apm] AskQuestion \u521B\u5EFA\u5F85\u786E\u8BA4\u9879 taskId=${taskId} count=${parsed.questions.length}`
|
|
2776
|
-
);
|
|
2777
|
-
await api.cli.webideReplaceAssumptions({
|
|
2778
|
-
taskId,
|
|
2779
|
-
title: parsed.title,
|
|
2780
|
-
questions: parsed.questions
|
|
2781
|
-
});
|
|
2782
|
-
while (true) {
|
|
2783
|
-
if (signal?.aborted) {
|
|
2784
|
-
throw new Error("AskQuestion \u5DF2\u53D6\u6D88");
|
|
2785
|
-
}
|
|
2786
|
-
const list = await api.cli.webideListAssumptions({ taskId });
|
|
2787
|
-
const byId = new Map(
|
|
2788
|
-
list.assumptions.map((a) => [a.questionId, a])
|
|
2789
|
-
);
|
|
2790
|
-
const pending = parsed.questions.filter((q) => {
|
|
2791
|
-
const row = byId.get(q.id);
|
|
2792
|
-
return !row || row.status !== "RESOLVED";
|
|
2793
|
-
});
|
|
2794
|
-
if (pending.length === 0) {
|
|
2795
|
-
const answers = parsed.questions.map((q) => {
|
|
2796
|
-
const row = byId.get(q.id);
|
|
2797
|
-
return {
|
|
2798
|
-
questionId: q.id,
|
|
2799
|
-
prompt: q.prompt,
|
|
2800
|
-
selectedOptionId: row.selectedOptionId,
|
|
2801
|
-
customAnswer: row.customAnswer,
|
|
2802
|
-
resolution: row.resolution
|
|
2803
|
-
};
|
|
2804
|
-
});
|
|
2805
|
-
console.log(
|
|
2806
|
-
`[apm] AskQuestion \u7528\u6237\u5DF2\u786E\u8BA4 taskId=${taskId} count=${answers.length}`
|
|
2807
|
-
);
|
|
2808
|
-
return JSON.stringify(
|
|
2809
|
-
{
|
|
2810
|
-
title: parsed.title,
|
|
2811
|
-
answers
|
|
2812
|
-
},
|
|
2813
|
-
null,
|
|
2814
|
-
2
|
|
2815
|
-
);
|
|
2816
|
-
}
|
|
2817
|
-
await delay(POLL_INTERVAL_MS);
|
|
2818
|
-
}
|
|
2819
|
-
};
|
|
2820
|
-
}
|
|
2821
|
-
|
|
2822
2580
|
// src/commands/connect/webide-message-log.ts
|
|
2823
2581
|
var SYNC_INTERVAL_MS = 2e3;
|
|
2824
2582
|
function webIdeEventsObjectPrefix(taskId, messageId) {
|
|
@@ -2840,8 +2598,8 @@ async function putDirtyEvents(minio, bucket, prefix, events) {
|
|
|
2840
2598
|
});
|
|
2841
2599
|
}
|
|
2842
2600
|
}
|
|
2843
|
-
async function upsertLogHeader(
|
|
2844
|
-
const api = createApmApiClient(
|
|
2601
|
+
async function upsertLogHeader(cfg2, ctx, patch, objectPrefix) {
|
|
2602
|
+
const api = createApmApiClient(cfg2);
|
|
2845
2603
|
await api.cli.webideUpsertMessageLog({
|
|
2846
2604
|
messageId: ctx.messageId,
|
|
2847
2605
|
agentId: ctx.agentId,
|
|
@@ -2849,7 +2607,7 @@ async function upsertLogHeader(cfg, ctx, patch, objectPrefix) {
|
|
|
2849
2607
|
...patch
|
|
2850
2608
|
});
|
|
2851
2609
|
}
|
|
2852
|
-
function createThrottledWebIdeMessageLogSync(
|
|
2610
|
+
function createThrottledWebIdeMessageLogSync(cfg2, ctx, onError, options) {
|
|
2853
2611
|
let lastRunAt = 0;
|
|
2854
2612
|
let timer;
|
|
2855
2613
|
let latestSession;
|
|
@@ -2860,7 +2618,7 @@ function createThrottledWebIdeMessageLogSync(cfg, ctx, onError, options) {
|
|
|
2860
2618
|
const resolvePrefix = () => options?.objectPrefixOverride?.trim() || webIdeEventsObjectPrefix(ctx.taskId, ctx.messageId);
|
|
2861
2619
|
const ensureMinio = async () => {
|
|
2862
2620
|
if (minio) return;
|
|
2863
|
-
const created = await createApmLogMinioClient(
|
|
2621
|
+
const created = await createApmLogMinioClient(cfg2);
|
|
2864
2622
|
minio = created.client;
|
|
2865
2623
|
bucket = created.bucket;
|
|
2866
2624
|
await minio.ensureBucket(bucket);
|
|
@@ -2884,7 +2642,7 @@ function createThrottledWebIdeMessageLogSync(cfg, ctx, onError, options) {
|
|
|
2884
2642
|
const prefix = resolvePrefix();
|
|
2885
2643
|
await putDirtyEvents(minio, bucket, prefix, events);
|
|
2886
2644
|
await upsertLogHeader(
|
|
2887
|
-
|
|
2645
|
+
cfg2,
|
|
2888
2646
|
ctx,
|
|
2889
2647
|
{
|
|
2890
2648
|
eventCount: session.getEventCount()
|
|
@@ -2909,7 +2667,7 @@ function createThrottledWebIdeMessageLogSync(cfg, ctx, onError, options) {
|
|
|
2909
2667
|
async markRun(runId, runStatus, lastError, tokenUsage) {
|
|
2910
2668
|
try {
|
|
2911
2669
|
await upsertLogHeader(
|
|
2912
|
-
|
|
2670
|
+
cfg2,
|
|
2913
2671
|
ctx,
|
|
2914
2672
|
{
|
|
2915
2673
|
runId,
|
|
@@ -2952,22 +2710,6 @@ function createThrottledWebIdeMessageLogSync(cfg, ctx, onError, options) {
|
|
|
2952
2710
|
};
|
|
2953
2711
|
}
|
|
2954
2712
|
|
|
2955
|
-
// src/commands/connect/ensure-message-reply.ts
|
|
2956
|
-
var DEFAULT_REPLY = "\u4EFB\u52A1\u5DF2\u5B8C\u6210\u3002";
|
|
2957
|
-
function resolveMessageReplyFallback(fallback) {
|
|
2958
|
-
for (const candidate of [
|
|
2959
|
-
fallback.assistantText,
|
|
2960
|
-
fallback.result,
|
|
2961
|
-
fallback.createPlan
|
|
2962
|
-
]) {
|
|
2963
|
-
const trimmed = candidate?.trim();
|
|
2964
|
-
if (trimmed) {
|
|
2965
|
-
return trimmed;
|
|
2966
|
-
}
|
|
2967
|
-
}
|
|
2968
|
-
return DEFAULT_REPLY;
|
|
2969
|
-
}
|
|
2970
|
-
|
|
2971
2713
|
// src/commands/init.ts
|
|
2972
2714
|
async function initializeWorkspace(workdir) {
|
|
2973
2715
|
await ensureWorkspaceApmDirForInit(workdir);
|
|
@@ -2986,240 +2728,282 @@ async function ensureWorkspaceInitialized(workdir) {
|
|
|
2986
2728
|
return initializeWorkspace(workdir);
|
|
2987
2729
|
}
|
|
2988
2730
|
|
|
2989
|
-
// src/
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
}
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
function shouldEnsureWebIdePullRequests(action) {
|
|
3010
|
-
return PRE_AGENT_PR_ACTIONS.has(action);
|
|
2731
|
+
// src/utils/project-documents.ts
|
|
2732
|
+
import {
|
|
2733
|
+
existsSync as existsSync5,
|
|
2734
|
+
readdirSync as readdirSync3,
|
|
2735
|
+
readFileSync as readFileSync4,
|
|
2736
|
+
rmSync,
|
|
2737
|
+
writeFileSync as writeFileSync3
|
|
2738
|
+
} from "fs";
|
|
2739
|
+
import { createHash as createHash2 } from "crypto";
|
|
2740
|
+
import { dirname as dirname3, join as join4, relative as relative4, sep } from "path";
|
|
2741
|
+
var MANIFEST_FILE = "manifest.json";
|
|
2742
|
+
function normalizeProjectIdForPath(projectId) {
|
|
2743
|
+
const id = projectId.trim();
|
|
2744
|
+
if (!id) {
|
|
2745
|
+
throw new Error("projectId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
2746
|
+
}
|
|
2747
|
+
if (id.includes("..") || id.includes("/") || id.includes("\\") || id.includes("\0")) {
|
|
2748
|
+
throw new Error(`\u975E\u6CD5 projectId: ${projectId}`);
|
|
2749
|
+
}
|
|
2750
|
+
return id;
|
|
3011
2751
|
}
|
|
3012
|
-
function
|
|
3013
|
-
|
|
2752
|
+
function projectDocumentsDir(apmRoot, projectId) {
|
|
2753
|
+
const id = normalizeProjectIdForPath(projectId);
|
|
2754
|
+
return join4(apmRoot ?? workspaceApmDir(), "project", id);
|
|
3014
2755
|
}
|
|
3015
|
-
function
|
|
3016
|
-
const
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
2756
|
+
function projectDocumentLocalPath(apmRoot, projectId, documentPath) {
|
|
2757
|
+
const normalized = normalizeLocalDocumentPath(documentPath);
|
|
2758
|
+
return join4(
|
|
2759
|
+
projectDocumentsDir(apmRoot, projectId),
|
|
2760
|
+
...normalized.split("/")
|
|
3020
2761
|
);
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
2762
|
+
}
|
|
2763
|
+
function normalizeLocalDocumentPath(path3) {
|
|
2764
|
+
const trimmed = path3.trim().replace(/\\/g, "/");
|
|
2765
|
+
if (!trimmed || trimmed.startsWith("/") || /^[a-zA-Z]:/.test(trimmed)) {
|
|
2766
|
+
throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path3}`);
|
|
3024
2767
|
}
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
2768
|
+
const segments = trimmed.split("/").filter(Boolean);
|
|
2769
|
+
if (segments.some((segment) => segment === ".." || segment === ".")) {
|
|
2770
|
+
throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path3}`);
|
|
2771
|
+
}
|
|
2772
|
+
return segments.join("/");
|
|
2773
|
+
}
|
|
2774
|
+
function hashLocalFileContent(content) {
|
|
2775
|
+
return createHash2("sha256").update(content, "utf8").digest("hex");
|
|
2776
|
+
}
|
|
2777
|
+
function readLocalManifest(apmRoot, projectId) {
|
|
2778
|
+
const manifestPath3 = join4(
|
|
2779
|
+
projectDocumentsDir(apmRoot, projectId),
|
|
2780
|
+
MANIFEST_FILE
|
|
2781
|
+
);
|
|
2782
|
+
if (!existsSync5(manifestPath3)) {
|
|
2783
|
+
return null;
|
|
2784
|
+
}
|
|
2785
|
+
try {
|
|
2786
|
+
return JSON.parse(
|
|
2787
|
+
readFileSync4(manifestPath3, "utf8")
|
|
2788
|
+
);
|
|
2789
|
+
} catch {
|
|
2790
|
+
return null;
|
|
2791
|
+
}
|
|
2792
|
+
}
|
|
2793
|
+
function listLocalDocumentPaths(apmRoot, projectId) {
|
|
2794
|
+
const root = projectDocumentsDir(apmRoot, projectId);
|
|
2795
|
+
if (!existsSync5(root)) {
|
|
2796
|
+
return [];
|
|
2797
|
+
}
|
|
2798
|
+
const paths = [];
|
|
2799
|
+
const walk = (dir) => {
|
|
2800
|
+
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
2801
|
+
const abs = join4(dir, entry.name);
|
|
2802
|
+
if (entry.isDirectory()) {
|
|
2803
|
+
walk(abs);
|
|
2804
|
+
continue;
|
|
2805
|
+
}
|
|
2806
|
+
if (entry.isFile() && entry.name === MANIFEST_FILE) {
|
|
2807
|
+
continue;
|
|
2808
|
+
}
|
|
2809
|
+
const rel = relative4(root, abs).split(sep).join("/");
|
|
2810
|
+
paths.push(rel);
|
|
3036
2811
|
}
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
2812
|
+
};
|
|
2813
|
+
walk(root);
|
|
2814
|
+
return paths.sort();
|
|
2815
|
+
}
|
|
2816
|
+
function diffManifestPaths(remote, local) {
|
|
2817
|
+
const remoteMap = new Map(
|
|
2818
|
+
(remote?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
2819
|
+
);
|
|
2820
|
+
const localMap = new Map(
|
|
2821
|
+
(local?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
2822
|
+
);
|
|
2823
|
+
const download = [];
|
|
2824
|
+
for (const [path3, hash] of remoteMap) {
|
|
2825
|
+
if (localMap.get(path3) !== hash) {
|
|
2826
|
+
download.push(path3);
|
|
3042
2827
|
}
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
2828
|
+
}
|
|
2829
|
+
const deleteLocal = [];
|
|
2830
|
+
for (const path3 of localMap.keys()) {
|
|
2831
|
+
if (!remoteMap.has(path3)) {
|
|
2832
|
+
deleteLocal.push(path3);
|
|
3046
2833
|
}
|
|
3047
|
-
lines.push(`- **${item.label}**\uFF1A\u521B\u5EFA\u5931\u8D25 \u2014 ${item.detail}`);
|
|
3048
2834
|
}
|
|
3049
|
-
return
|
|
2835
|
+
return { download, deleteLocal };
|
|
3050
2836
|
}
|
|
3051
|
-
async function
|
|
3052
|
-
const
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
skipped: 0,
|
|
3056
|
-
skippedNoChanges: 0,
|
|
3057
|
-
failed: 0,
|
|
3058
|
-
items: [],
|
|
3059
|
-
tips: []
|
|
3060
|
-
});
|
|
3061
|
-
let repoRoots;
|
|
3062
|
-
try {
|
|
3063
|
-
const manifest = loadWorkspaceReposCache(workdir);
|
|
3064
|
-
repoRoots = resolveWorkspaceRepoAbsolutePaths(manifest);
|
|
3065
|
-
} catch (err) {
|
|
3066
|
-
const tip = `\u8BFB\u53D6\u4ED3\u5E93\u6E05\u5355\u5931\u8D25: ${err instanceof Error ? err.message : err}`;
|
|
3067
|
-
console.warn(`[apm] WebIDE PR\uFF1A${tip}`);
|
|
3068
|
-
return {
|
|
3069
|
-
...empty(),
|
|
3070
|
-
failed: 1,
|
|
3071
|
-
tips: [tip],
|
|
3072
|
-
items: [
|
|
3073
|
-
{
|
|
3074
|
-
label: "\u5DE5\u4F5C\u533A",
|
|
3075
|
-
outcome: "failed",
|
|
3076
|
-
detail: tip
|
|
3077
|
-
}
|
|
3078
|
-
]
|
|
3079
|
-
};
|
|
2837
|
+
async function resolveProjectIdForSync(api, workdirPath, options) {
|
|
2838
|
+
const explicit = options?.projectId?.trim();
|
|
2839
|
+
if (explicit) {
|
|
2840
|
+
return { projectId: explicit, diagnostic: null };
|
|
3080
2841
|
}
|
|
3081
|
-
let projectBaseBranch = "";
|
|
3082
2842
|
try {
|
|
2843
|
+
const gitRoot = await resolveGitRepoRoot(workdirPath);
|
|
2844
|
+
const current = await getCurrentBranch(gitRoot);
|
|
2845
|
+
const taskId = taskIdFromFeatureBranch(current);
|
|
2846
|
+
if (!taskId) {
|
|
2847
|
+
return {
|
|
2848
|
+
projectId: null,
|
|
2849
|
+
diagnostic: `\u5F53\u524D\u5206\u652F ${current} \u4E0D\u662F feat/task-*\uFF0C\u65E0\u6CD5\u89E3\u6790\u6240\u5C5E\u9879\u76EE\u3002
|
|
2850
|
+
\u8BF7\u5728\u7279\u6027\u5206\u652F\u4E0A\u6267\u884C\uFF0C\u6216\u663E\u5F0F\u6307\u5B9A projectId\u3002`
|
|
2851
|
+
};
|
|
2852
|
+
}
|
|
3083
2853
|
const baseline = await api.cli.projectBaseBranch({ taskId });
|
|
3084
|
-
|
|
2854
|
+
const projectId = baseline.projectId?.trim() || null;
|
|
2855
|
+
if (!projectId) {
|
|
2856
|
+
return {
|
|
2857
|
+
projectId: null,
|
|
2858
|
+
diagnostic: `\u4EFB\u52A1 ${taskId} \u672A\u5173\u8054\u9879\u76EE\uFF0C\u65E0\u6CD5\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002`
|
|
2859
|
+
};
|
|
2860
|
+
}
|
|
2861
|
+
return { projectId, diagnostic: null };
|
|
3085
2862
|
} catch (err) {
|
|
3086
|
-
const
|
|
3087
|
-
console.warn(`[apm] WebIDE PR\uFF1A${tip}`);
|
|
2863
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
3088
2864
|
return {
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
tips: [tip],
|
|
3092
|
-
items: [{ label: "\u5DE5\u4F5C\u533A", outcome: "failed", detail: tip }]
|
|
2865
|
+
projectId: null,
|
|
2866
|
+
diagnostic: detail.replace(/^\[apm\]\s*/, "")
|
|
3093
2867
|
};
|
|
3094
2868
|
}
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
2869
|
+
}
|
|
2870
|
+
async function syncProjectDocumentsPull(workdirPath, apmDir, options) {
|
|
2871
|
+
const empty = {
|
|
2872
|
+
synced: false,
|
|
2873
|
+
projectId: null,
|
|
2874
|
+
downloaded: 0,
|
|
2875
|
+
deleted: 0
|
|
2876
|
+
};
|
|
2877
|
+
const cfg2 = await tryReadApmConfig();
|
|
2878
|
+
if (!cfg2 || !resolveApiKey(cfg2)) {
|
|
2879
|
+
console.log(
|
|
2880
|
+
"[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"
|
|
2881
|
+
);
|
|
2882
|
+
return empty;
|
|
3104
2883
|
}
|
|
3105
|
-
const
|
|
2884
|
+
const api = createApmApiClient(cfg2);
|
|
2885
|
+
const { projectId, diagnostic } = await resolveProjectIdForSync(
|
|
2886
|
+
api,
|
|
2887
|
+
workdirPath,
|
|
2888
|
+
options
|
|
2889
|
+
);
|
|
2890
|
+
if (!projectId) {
|
|
2891
|
+
console.log(`[apm] \u672A\u80FD\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002
|
|
2892
|
+
${diagnostic ?? ""}`);
|
|
2893
|
+
return empty;
|
|
2894
|
+
}
|
|
2895
|
+
const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
|
|
2896
|
+
const docsDir = projectDocumentsDir(targetApmDir, projectId);
|
|
2897
|
+
await ensureDirExists(docsDir);
|
|
2898
|
+
const { manifest: remoteManifest } = await api.cli.getProjectDocumentManifest(
|
|
2899
|
+
{ projectId }
|
|
2900
|
+
);
|
|
2901
|
+
if (!remoteManifest) {
|
|
2902
|
+
console.log(`[apm] \u9879\u76EE ${projectId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`);
|
|
2903
|
+
return { ...empty, projectId };
|
|
2904
|
+
}
|
|
2905
|
+
const localManifest = readLocalManifest(targetApmDir, projectId);
|
|
2906
|
+
const { download, deleteLocal } = diffManifestPaths(
|
|
2907
|
+
remoteManifest,
|
|
2908
|
+
localManifest
|
|
2909
|
+
);
|
|
2910
|
+
let downloaded = 0;
|
|
2911
|
+
if (download.length > 0) {
|
|
2912
|
+
const { list } = await api.cli.listProjectDocuments({
|
|
2913
|
+
projectId,
|
|
2914
|
+
paths: download.join(",")
|
|
2915
|
+
});
|
|
2916
|
+
for (const doc of list) {
|
|
2917
|
+
const absPath = toFsPath(
|
|
2918
|
+
projectDocumentLocalPath(targetApmDir, projectId, doc.path)
|
|
2919
|
+
);
|
|
2920
|
+
await ensureDirExists(dirname3(absPath));
|
|
2921
|
+
writeFileSync3(absPath, doc.content, "utf8");
|
|
2922
|
+
downloaded += 1;
|
|
2923
|
+
}
|
|
2924
|
+
}
|
|
2925
|
+
let deleted = 0;
|
|
2926
|
+
for (const path3 of deleteLocal) {
|
|
2927
|
+
const absPath = toFsPath(
|
|
2928
|
+
projectDocumentLocalPath(targetApmDir, projectId, path3)
|
|
2929
|
+
);
|
|
2930
|
+
if (existsSync5(absPath)) {
|
|
2931
|
+
rmSync(absPath, { force: true });
|
|
2932
|
+
deleted += 1;
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2935
|
+
writeFileSync3(
|
|
2936
|
+
toFsPath(join4(docsDir, MANIFEST_FILE)),
|
|
2937
|
+
`${JSON.stringify(remoteManifest, null, 2)}
|
|
2938
|
+
`,
|
|
2939
|
+
"utf8"
|
|
2940
|
+
);
|
|
3106
2941
|
console.log(
|
|
3107
|
-
`[apm]
|
|
2942
|
+
`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: projectId=${projectId} \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted} \u2192 .apm/project/${projectId}/`
|
|
3108
2943
|
);
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
const tip = `${label}\uFF1A\u65E0 remote.origin.url`;
|
|
3149
|
-
console.warn(`[apm] WebIDE PR\uFF1A\u8DF3\u8FC7 ${tip}`);
|
|
3150
|
-
tips.push(tip);
|
|
3151
|
-
skipped += 1;
|
|
3152
|
-
items.push({
|
|
3153
|
-
label,
|
|
3154
|
-
outcome: "skipped",
|
|
3155
|
-
headBranch,
|
|
3156
|
-
detail: tip
|
|
3157
|
-
});
|
|
3158
|
-
continue;
|
|
3159
|
-
}
|
|
3160
|
-
const baseBranch = projectBaseBranch;
|
|
3161
|
-
const hasChanges = await branchHasChangesAgainstBase(
|
|
3162
|
-
gitRoot,
|
|
3163
|
-
baseBranch,
|
|
3164
|
-
headBranch
|
|
3165
|
-
);
|
|
3166
|
-
if (!hasChanges) {
|
|
3167
|
-
const tip = `${label}\uFF1A\u76F8\u5BF9\u57FA\u7EBF ${baseBranch} \u65E0\u4EE3\u7801\u53D8\u66F4\uFF0C\u65E0\u9700\u521B\u5EFA PR`;
|
|
3168
|
-
console.log(`[apm] WebIDE PR\uFF1A${tip}`);
|
|
3169
|
-
tips.push(tip);
|
|
3170
|
-
skippedNoChanges += 1;
|
|
3171
|
-
items.push({
|
|
3172
|
-
label,
|
|
3173
|
-
outcome: "skipped_no_changes",
|
|
3174
|
-
headBranch,
|
|
3175
|
-
baseBranch,
|
|
3176
|
-
detail: tip
|
|
3177
|
-
});
|
|
3178
|
-
continue;
|
|
3179
|
-
}
|
|
3180
|
-
const pr = await api.cli.createWebIdeDraftPullRequest({
|
|
3181
|
-
taskId,
|
|
3182
|
-
originUrl,
|
|
3183
|
-
headBranch,
|
|
3184
|
-
baseBranch
|
|
3185
|
-
});
|
|
3186
|
-
ensured += 1;
|
|
3187
|
-
items.push({
|
|
3188
|
-
label,
|
|
3189
|
-
outcome: "created",
|
|
3190
|
-
headBranch,
|
|
3191
|
-
baseBranch,
|
|
3192
|
-
number: pr.number,
|
|
3193
|
-
state: pr.state,
|
|
3194
|
-
url: pr.url,
|
|
3195
|
-
detail: `\u5DF2\u521B\u5EFA #${pr.number} ${headBranch} \u2192 ${baseBranch}`
|
|
3196
|
-
});
|
|
3197
|
-
console.log(
|
|
3198
|
-
`[apm] WebIDE PR\uFF1A${label} \u2192 #${pr.number} ${pr.state} ${headBranch} \u2192 ${baseBranch} ${pr.url || ""}`
|
|
3199
|
-
);
|
|
3200
|
-
} catch (err) {
|
|
3201
|
-
failed += 1;
|
|
3202
|
-
const tip = `${label}\uFF1A${err instanceof Error ? err.message : err}`;
|
|
3203
|
-
console.warn(`[apm] WebIDE PR\uFF1A\u5931\u8D25 ${tip}`);
|
|
3204
|
-
tips.push(tip);
|
|
3205
|
-
items.push({
|
|
3206
|
-
label,
|
|
3207
|
-
outcome: "failed",
|
|
3208
|
-
detail: tip
|
|
3209
|
-
});
|
|
2944
|
+
return {
|
|
2945
|
+
synced: true,
|
|
2946
|
+
projectId,
|
|
2947
|
+
downloaded,
|
|
2948
|
+
deleted
|
|
2949
|
+
};
|
|
2950
|
+
}
|
|
2951
|
+
async function syncProjectDocumentsPush(cfg2, workdirPath, apmRoot, options) {
|
|
2952
|
+
const api = createApmApiClient(cfg2);
|
|
2953
|
+
const { projectId } = await resolveProjectIdForSync(
|
|
2954
|
+
api,
|
|
2955
|
+
workdirPath,
|
|
2956
|
+
options
|
|
2957
|
+
);
|
|
2958
|
+
if (!projectId) {
|
|
2959
|
+
return 0;
|
|
2960
|
+
}
|
|
2961
|
+
const targetApmDir = apmRoot ?? workspaceApmDir(workdirPath);
|
|
2962
|
+
const localPaths = listLocalDocumentPaths(targetApmDir, projectId);
|
|
2963
|
+
if (localPaths.length === 0) {
|
|
2964
|
+
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u672C\u5730\u6587\u4EF6\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
2965
|
+
return 0;
|
|
2966
|
+
}
|
|
2967
|
+
const remoteManifest = (await api.cli.getProjectDocumentManifest({ projectId })).manifest ?? null;
|
|
2968
|
+
const remoteHashByPath = new Map(
|
|
2969
|
+
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
2970
|
+
);
|
|
2971
|
+
const remoteDescriptionByPath = new Map(
|
|
2972
|
+
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.description])
|
|
2973
|
+
);
|
|
2974
|
+
let synced = 0;
|
|
2975
|
+
for (const path3 of localPaths) {
|
|
2976
|
+
const absPath = toFsPath(
|
|
2977
|
+
projectDocumentLocalPath(targetApmDir, projectId, path3)
|
|
2978
|
+
);
|
|
2979
|
+
const content = readFileSync4(absPath, "utf8");
|
|
2980
|
+
const contentHash = hashLocalFileContent(content);
|
|
2981
|
+
if (remoteHashByPath.get(path3) === contentHash) {
|
|
2982
|
+
continue;
|
|
3210
2983
|
}
|
|
2984
|
+
await api.cli.upsertProjectDocument({
|
|
2985
|
+
projectId,
|
|
2986
|
+
path: path3,
|
|
2987
|
+
content,
|
|
2988
|
+
description: remoteDescriptionByPath.get(path3) ?? void 0
|
|
2989
|
+
});
|
|
2990
|
+
synced += 1;
|
|
2991
|
+
console.log(`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: ${path3}`);
|
|
3211
2992
|
}
|
|
3212
|
-
|
|
2993
|
+
if (synced === 0) {
|
|
2994
|
+
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
2995
|
+
}
|
|
2996
|
+
return synced;
|
|
3213
2997
|
}
|
|
3214
2998
|
|
|
3215
2999
|
// src/version.ts
|
|
3216
3000
|
import { readFileSync as readFileSync5 } from "fs";
|
|
3217
|
-
import { dirname as dirname4, join as
|
|
3001
|
+
import { dirname as dirname4, join as join5 } from "path";
|
|
3218
3002
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3219
3003
|
function readCliVersion() {
|
|
3220
3004
|
try {
|
|
3221
3005
|
const dir = dirname4(fileURLToPath2(import.meta.url));
|
|
3222
|
-
const pkgPath =
|
|
3006
|
+
const pkgPath = join5(dir, "..", "package.json");
|
|
3223
3007
|
const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
|
|
3224
3008
|
return pkg.version ?? "0.0.0";
|
|
3225
3009
|
} catch {
|
|
@@ -3227,699 +3011,451 @@ function readCliVersion() {
|
|
|
3227
3011
|
}
|
|
3228
3012
|
}
|
|
3229
3013
|
|
|
3230
|
-
// src/commands/
|
|
3231
|
-
import { existsSync as
|
|
3232
|
-
import { join as
|
|
3233
|
-
var
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
const url = `${base}/api/v1/tasks/attachments/file?${new URLSearchParams({
|
|
3237
|
-
attachmentId
|
|
3238
|
-
})}`;
|
|
3239
|
-
const res = await fetch(url);
|
|
3240
|
-
if (!res.ok) {
|
|
3241
|
-
throw new Error(
|
|
3242
|
-
`[apm] \u4E0B\u8F7D\u9644\u4EF6\u5931\u8D25 (${res.status}): attachmentId=${attachmentId}`
|
|
3243
|
-
);
|
|
3244
|
-
}
|
|
3245
|
-
return Buffer.from(await res.arrayBuffer());
|
|
3014
|
+
// src/commands/connect/cli-version-sync.ts
|
|
3015
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
3016
|
+
import { join as join6 } from "path";
|
|
3017
|
+
var CLI_VERSION_FILE = ".cli-version.json";
|
|
3018
|
+
function manifestPath2(apmDir) {
|
|
3019
|
+
return join6(apmDir, CLI_VERSION_FILE);
|
|
3246
3020
|
}
|
|
3247
|
-
function loadManifest(
|
|
3248
|
-
const path3 =
|
|
3249
|
-
if (!
|
|
3250
|
-
return
|
|
3021
|
+
function loadManifest(apmDir) {
|
|
3022
|
+
const path3 = toFsPath(manifestPath2(apmDir));
|
|
3023
|
+
if (!existsSync6(path3)) {
|
|
3024
|
+
return null;
|
|
3251
3025
|
}
|
|
3252
3026
|
try {
|
|
3253
3027
|
const parsed = JSON.parse(
|
|
3254
3028
|
readFileSync6(path3, "utf8")
|
|
3255
3029
|
);
|
|
3256
|
-
if (parsed?.version === 1 &&
|
|
3030
|
+
if (parsed?.version === 1 && typeof parsed.cliVersion === "string" && parsed.cliVersion.trim()) {
|
|
3257
3031
|
return parsed;
|
|
3258
3032
|
}
|
|
3259
3033
|
} catch {
|
|
3260
3034
|
}
|
|
3261
|
-
return
|
|
3035
|
+
return null;
|
|
3262
3036
|
}
|
|
3263
|
-
function saveManifest(
|
|
3037
|
+
function saveManifest(apmDir, cliVersion) {
|
|
3038
|
+
const manifest = { version: 1, cliVersion };
|
|
3264
3039
|
writeFileSync4(
|
|
3265
|
-
|
|
3040
|
+
toFsPath(manifestPath2(apmDir)),
|
|
3266
3041
|
`${JSON.stringify(manifest, null, 2)}
|
|
3267
3042
|
`,
|
|
3268
3043
|
"utf8"
|
|
3269
3044
|
);
|
|
3270
3045
|
}
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
}
|
|
3277
|
-
async function syncAttachmentsToDirectory(cfg, attachments, dir, logLabel) {
|
|
3278
|
-
await ensureDirExists(dir);
|
|
3279
|
-
if (attachments.length === 0) {
|
|
3280
|
-
saveManifest(dir, { version: 1, attachments: {} });
|
|
3281
|
-
return [];
|
|
3046
|
+
var syncedInSession = /* @__PURE__ */ new Map();
|
|
3047
|
+
function shouldSyncSkillsForCliVersion(workdir, currentVersion) {
|
|
3048
|
+
const cached = syncedInSession.get(workdir);
|
|
3049
|
+
if (cached === currentVersion) {
|
|
3050
|
+
return false;
|
|
3282
3051
|
}
|
|
3283
|
-
const
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
const dest = join5(dir, item.name);
|
|
3288
|
-
const entry = manifest.attachments[item.id];
|
|
3289
|
-
const createdAt = item.createdAt ?? "";
|
|
3290
|
-
names.push(item.name);
|
|
3291
|
-
if (isAttachmentUpToDate(entry, item, dest)) {
|
|
3292
|
-
nextManifest.attachments[item.id] = entry;
|
|
3293
|
-
console.log(`[apm] \u9644\u4EF6\u65E0\u53D8\u5316\uFF0C\u5DF2\u8DF3\u8FC7: ${logLabel}/${item.name}`);
|
|
3294
|
-
continue;
|
|
3295
|
-
}
|
|
3296
|
-
const buffer = await downloadAttachment(cfg, item.id);
|
|
3297
|
-
writeFileSync4(dest, buffer);
|
|
3298
|
-
nextManifest.attachments[item.id] = {
|
|
3299
|
-
name: item.name,
|
|
3300
|
-
createdAt
|
|
3301
|
-
};
|
|
3302
|
-
console.log(`[apm] \u5DF2\u4E0B\u8F7D\u9644\u4EF6: ${logLabel}/${item.name}`);
|
|
3052
|
+
const stored = loadManifest(workspaceApmDir(workdir));
|
|
3053
|
+
if (stored?.cliVersion === currentVersion) {
|
|
3054
|
+
syncedInSession.set(workdir, currentVersion);
|
|
3055
|
+
return false;
|
|
3303
3056
|
}
|
|
3304
|
-
|
|
3305
|
-
return names;
|
|
3057
|
+
return true;
|
|
3306
3058
|
}
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
cfg,
|
|
3311
|
-
attachments,
|
|
3312
|
-
dir,
|
|
3313
|
-
`.apm/webide/${taskId}/attachments`
|
|
3314
|
-
);
|
|
3059
|
+
function markSkillsSyncedForCliVersion(workdir, cliVersion) {
|
|
3060
|
+
saveManifest(workspaceApmDir(workdir), cliVersion);
|
|
3061
|
+
syncedInSession.set(workdir, cliVersion);
|
|
3315
3062
|
}
|
|
3316
3063
|
|
|
3317
|
-
// src/
|
|
3318
|
-
import
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
rmSync as rmSync2,
|
|
3323
|
-
writeFileSync as writeFileSync5
|
|
3324
|
-
} from "fs";
|
|
3325
|
-
import { createHash as createHash2 } from "crypto";
|
|
3326
|
-
import { dirname as dirname5, join as join6, relative as relative4, sep } from "path";
|
|
3327
|
-
var MANIFEST_FILE2 = "manifest.json";
|
|
3328
|
-
function normalizeProjectIdForPath(projectId) {
|
|
3329
|
-
const id = projectId.trim();
|
|
3330
|
-
if (!id) {
|
|
3331
|
-
throw new Error("projectId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
3332
|
-
}
|
|
3333
|
-
if (id.includes("..") || id.includes("/") || id.includes("\\") || id.includes("\0")) {
|
|
3334
|
-
throw new Error(`\u975E\u6CD5 projectId: ${projectId}`);
|
|
3335
|
-
}
|
|
3336
|
-
return id;
|
|
3337
|
-
}
|
|
3338
|
-
function projectDocumentsDir(apmRoot, projectId) {
|
|
3339
|
-
const id = normalizeProjectIdForPath(projectId);
|
|
3340
|
-
return join6(apmRoot ?? workspaceApmDir(), "project", id);
|
|
3064
|
+
// src/commands/connect/upload-package-artifact.ts
|
|
3065
|
+
import fs2 from "node:fs";
|
|
3066
|
+
import path2 from "node:path";
|
|
3067
|
+
function packageTmpDir(packageId) {
|
|
3068
|
+
return `/data/package-tmp/${packageId}`;
|
|
3341
3069
|
}
|
|
3342
|
-
function
|
|
3343
|
-
const
|
|
3344
|
-
return
|
|
3345
|
-
projectDocumentsDir(apmRoot, projectId),
|
|
3346
|
-
...normalized.split("/")
|
|
3347
|
-
);
|
|
3070
|
+
function expectedLocalArtifactPath(target, packageId) {
|
|
3071
|
+
const fileName = target === "frontend" ? "dist.zip" : "jars.zip";
|
|
3072
|
+
return path2.join(packageTmpDir(packageId), fileName);
|
|
3348
3073
|
}
|
|
3349
|
-
function
|
|
3350
|
-
const
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
}
|
|
3354
|
-
const segments = trimmed.split("/").filter(Boolean);
|
|
3355
|
-
if (segments.some((segment) => segment === ".." || segment === ".")) {
|
|
3356
|
-
throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path3}`);
|
|
3357
|
-
}
|
|
3358
|
-
return segments.join("/");
|
|
3074
|
+
function packageArtifactObjectKey(target, projectId, timestampMs = Date.now()) {
|
|
3075
|
+
const folder = target === "frontend" ? "vue" : "java";
|
|
3076
|
+
const suffix = target === "frontend" ? "dist.zip" : "jar.zip";
|
|
3077
|
+
return `packages/${projectId}/${folder}/${timestampMs}.${suffix}`;
|
|
3359
3078
|
}
|
|
3360
|
-
function
|
|
3361
|
-
|
|
3362
|
-
}
|
|
3363
|
-
function readLocalManifest(apmRoot, projectId) {
|
|
3364
|
-
const manifestPath3 = join6(
|
|
3365
|
-
projectDocumentsDir(apmRoot, projectId),
|
|
3366
|
-
MANIFEST_FILE2
|
|
3367
|
-
);
|
|
3368
|
-
if (!existsSync8(manifestPath3)) {
|
|
3369
|
-
return null;
|
|
3370
|
-
}
|
|
3079
|
+
function removePackageTmpDir(packageId) {
|
|
3080
|
+
const dir = packageTmpDir(packageId);
|
|
3371
3081
|
try {
|
|
3372
|
-
|
|
3373
|
-
|
|
3082
|
+
fs2.rmSync(dir, { recursive: true, force: true });
|
|
3083
|
+
} catch (err) {
|
|
3084
|
+
console.warn(
|
|
3085
|
+
`[apm] \u6E05\u7406\u6253\u5305\u4E34\u65F6\u76EE\u5F55\u5931\u8D25 ${dir}:`,
|
|
3086
|
+
err instanceof Error ? err.message : err
|
|
3374
3087
|
);
|
|
3375
|
-
} catch {
|
|
3376
|
-
return null;
|
|
3377
|
-
}
|
|
3378
|
-
}
|
|
3379
|
-
function listLocalDocumentPaths(apmRoot, projectId) {
|
|
3380
|
-
const root = projectDocumentsDir(apmRoot, projectId);
|
|
3381
|
-
if (!existsSync8(root)) {
|
|
3382
|
-
return [];
|
|
3383
3088
|
}
|
|
3384
|
-
const paths = [];
|
|
3385
|
-
const walk = (dir) => {
|
|
3386
|
-
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
3387
|
-
const abs = join6(dir, entry.name);
|
|
3388
|
-
if (entry.isDirectory()) {
|
|
3389
|
-
walk(abs);
|
|
3390
|
-
continue;
|
|
3391
|
-
}
|
|
3392
|
-
if (entry.isFile() && entry.name === MANIFEST_FILE2) {
|
|
3393
|
-
continue;
|
|
3394
|
-
}
|
|
3395
|
-
const rel = relative4(root, abs).split(sep).join("/");
|
|
3396
|
-
paths.push(rel);
|
|
3397
|
-
}
|
|
3398
|
-
};
|
|
3399
|
-
walk(root);
|
|
3400
|
-
return paths.sort();
|
|
3401
3089
|
}
|
|
3402
|
-
function
|
|
3403
|
-
const
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
const localMap = new Map(
|
|
3407
|
-
(local?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
3408
|
-
);
|
|
3409
|
-
const download = [];
|
|
3410
|
-
for (const [path3, hash] of remoteMap) {
|
|
3411
|
-
if (localMap.get(path3) !== hash) {
|
|
3412
|
-
download.push(path3);
|
|
3413
|
-
}
|
|
3090
|
+
async function uploadPackageArtifact(cfg2, target, packageId, projectId) {
|
|
3091
|
+
const localPath = expectedLocalArtifactPath(target, packageId);
|
|
3092
|
+
if (!fs2.existsSync(localPath)) {
|
|
3093
|
+
throw new Error(`\u6253\u5305\u4EA7\u7269\u4E0D\u5B58\u5728: ${localPath}`);
|
|
3414
3094
|
}
|
|
3415
|
-
const
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
deleteLocal.push(path3);
|
|
3419
|
-
}
|
|
3095
|
+
const stat = fs2.statSync(localPath);
|
|
3096
|
+
if (!stat.isFile() || stat.size <= 0) {
|
|
3097
|
+
throw new Error(`\u6253\u5305\u4EA7\u7269\u65E0\u6548\u6216\u4E3A\u7A7A: ${localPath}`);
|
|
3420
3098
|
}
|
|
3421
|
-
|
|
3099
|
+
const objectKey = packageArtifactObjectKey(target, projectId);
|
|
3100
|
+
const { client, bucket, publicEndpoint } = await createApmLogMinioClient(cfg2);
|
|
3101
|
+
await client.ensureBucket(bucket);
|
|
3102
|
+
await client.fPutObject(bucket, objectKey, localPath, {
|
|
3103
|
+
"Content-Type": "application/zip"
|
|
3104
|
+
});
|
|
3105
|
+
removePackageTmpDir(packageId);
|
|
3106
|
+
const base = publicEndpoint?.replace(/\/$/, "") ?? "";
|
|
3107
|
+
const artifactUrl = base ? `${base}/${bucket}/${objectKey}` : null;
|
|
3108
|
+
return { artifactPath: objectKey, artifactUrl };
|
|
3422
3109
|
}
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
diagnostic: `\u5F53\u524D\u5206\u652F ${current} \u4E0D\u662F feat/task-*\uFF0C\u65E0\u6CD5\u89E3\u6790\u6240\u5C5E\u9879\u76EE\u3002
|
|
3436
|
-
\u8BF7\u5728\u7279\u6027\u5206\u652F\u4E0A\u6267\u884C\uFF0C\u6216\u663E\u5F0F\u6307\u5B9A projectId\u3002`
|
|
3437
|
-
};
|
|
3438
|
-
}
|
|
3439
|
-
const baseline = await api.cli.projectBaseBranch({ taskId });
|
|
3440
|
-
const projectId = baseline.projectId?.trim() || null;
|
|
3441
|
-
if (!projectId) {
|
|
3442
|
-
return {
|
|
3443
|
-
projectId: null,
|
|
3444
|
-
diagnostic: `\u4EFB\u52A1 ${taskId} \u672A\u5173\u8054\u9879\u76EE\uFF0C\u65E0\u6CD5\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002`
|
|
3445
|
-
};
|
|
3446
|
-
}
|
|
3447
|
-
return { projectId, diagnostic: null };
|
|
3448
|
-
} catch (err) {
|
|
3449
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
3450
|
-
return {
|
|
3451
|
-
projectId: null,
|
|
3452
|
-
diagnostic: detail.replace(/^\[apm\]\s*/, "")
|
|
3453
|
-
};
|
|
3454
|
-
}
|
|
3110
|
+
|
|
3111
|
+
// src/commands/connect/core/worker/handlers/package.ts
|
|
3112
|
+
async function updatePackageStatus(cfg2, packageId, status, extra) {
|
|
3113
|
+
const api = createApmApiClient(cfg2);
|
|
3114
|
+
await api.cli.projectPackageUpdateStatus({
|
|
3115
|
+
id: packageId,
|
|
3116
|
+
status,
|
|
3117
|
+
...extra?.error != null ? { error: extra.error } : {},
|
|
3118
|
+
...extra?.content != null ? { content: extra.content } : {},
|
|
3119
|
+
...extra?.artifactPath != null ? { artifactPath: extra.artifactPath } : {},
|
|
3120
|
+
...extra?.artifactUrl != null ? { artifactUrl: extra.artifactUrl } : {}
|
|
3121
|
+
});
|
|
3455
3122
|
}
|
|
3456
|
-
async function
|
|
3457
|
-
const
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3123
|
+
async function handleWebIdePackage(cfg2, msg2, signal) {
|
|
3124
|
+
const workdir = requireRemoteWorkdir(msg2.workdir);
|
|
3125
|
+
const messageId = msg2.messageId;
|
|
3126
|
+
const packageId = msg2.packageId;
|
|
3127
|
+
const projectId = msg2.projectId;
|
|
3128
|
+
console.log(
|
|
3129
|
+
`[apm] webide-package target=${msg2.target} packageId=${packageId} projectId=${projectId} baseBranch=${msg2.baseBranch}`
|
|
3130
|
+
);
|
|
3131
|
+
await updatePackageStatus(cfg2, packageId, "RUNNING");
|
|
3132
|
+
const eventSession = new EventSession(msg2.content);
|
|
3133
|
+
const logSyncRef = {
|
|
3134
|
+
current: createThrottledWebIdeMessageLogSync(
|
|
3135
|
+
cfg2,
|
|
3136
|
+
{
|
|
3137
|
+
taskId: `package:${packageId}`,
|
|
3138
|
+
messageId,
|
|
3139
|
+
agentId: "webide-package"
|
|
3140
|
+
},
|
|
3141
|
+
(err) => {
|
|
3142
|
+
console.warn(
|
|
3143
|
+
"[apm] WebIDE \u6253\u5305\u65E5\u5FD7\u540C\u6B65\u5931\u8D25:",
|
|
3144
|
+
err instanceof Error ? err.message : err
|
|
3145
|
+
);
|
|
3146
|
+
},
|
|
3147
|
+
{
|
|
3148
|
+
objectPrefixOverride: `events/webide-package/${packageId}/`
|
|
3149
|
+
}
|
|
3150
|
+
)
|
|
3462
3151
|
};
|
|
3463
|
-
const
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3152
|
+
const syncPrepLog = async () => {
|
|
3153
|
+
const sync = logSyncRef.current;
|
|
3154
|
+
if (!sync) return;
|
|
3155
|
+
sync.schedule(eventSession);
|
|
3156
|
+
await sync.flush(eventSession);
|
|
3157
|
+
};
|
|
3158
|
+
const runPrepStep = async (step, work, formatOk) => {
|
|
3159
|
+
eventSession.addCliStep(step, "\u8FDB\u884C\u4E2D\u2026", "running");
|
|
3160
|
+
await syncPrepLog();
|
|
3161
|
+
try {
|
|
3162
|
+
const result = await work();
|
|
3163
|
+
eventSession.addCliStep(step, formatOk(result), "ok");
|
|
3164
|
+
await syncPrepLog();
|
|
3165
|
+
return result;
|
|
3166
|
+
} catch (err) {
|
|
3167
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
3168
|
+
eventSession.addCliStep(step, detail, "error");
|
|
3169
|
+
await syncPrepLog();
|
|
3170
|
+
throw err;
|
|
3171
|
+
}
|
|
3172
|
+
};
|
|
3173
|
+
try {
|
|
3174
|
+
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
3175
|
+
await runPrepStep(
|
|
3176
|
+
"\u521D\u59CB\u5316\u5DE5\u4F5C\u533A",
|
|
3177
|
+
async () => {
|
|
3178
|
+
const { didInit } = await ensureWorkspaceInitialized(workdir);
|
|
3179
|
+
return didInit;
|
|
3180
|
+
},
|
|
3181
|
+
(didInit) => didInit ? "\u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A" : "\u5DE5\u4F5C\u533A\u5DF2\u5C31\u7EEA"
|
|
3467
3182
|
);
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
${
|
|
3479
|
-
return empty;
|
|
3480
|
-
}
|
|
3481
|
-
const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
|
|
3482
|
-
const docsDir = projectDocumentsDir(targetApmDir, projectId);
|
|
3483
|
-
await ensureDirExists(docsDir);
|
|
3484
|
-
const { manifest: remoteManifest } = await api.cli.getProjectDocumentManifest(
|
|
3485
|
-
{ projectId }
|
|
3486
|
-
);
|
|
3487
|
-
if (!remoteManifest) {
|
|
3488
|
-
console.log(`[apm] \u9879\u76EE ${projectId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`);
|
|
3489
|
-
return { ...empty, projectId };
|
|
3490
|
-
}
|
|
3491
|
-
const localManifest = readLocalManifest(targetApmDir, projectId);
|
|
3492
|
-
const { download, deleteLocal } = diffManifestPaths(
|
|
3493
|
-
remoteManifest,
|
|
3494
|
-
localManifest
|
|
3495
|
-
);
|
|
3496
|
-
let downloaded = 0;
|
|
3497
|
-
if (download.length > 0) {
|
|
3498
|
-
const { list } = await api.cli.listProjectDocuments({
|
|
3499
|
-
projectId,
|
|
3500
|
-
paths: download.join(",")
|
|
3501
|
-
});
|
|
3502
|
-
for (const doc of list) {
|
|
3503
|
-
const absPath = toFsPath(
|
|
3504
|
-
projectDocumentLocalPath(targetApmDir, projectId, doc.path)
|
|
3183
|
+
const cliVersion = readCliVersion();
|
|
3184
|
+
if (shouldSyncSkillsForCliVersion(workdir, cliVersion)) {
|
|
3185
|
+
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
3186
|
+
await runPrepStep(
|
|
3187
|
+
"\u540C\u6B65 WebIDE \u89C4\u5219",
|
|
3188
|
+
async () => {
|
|
3189
|
+
await copyWebIdeTemplateFiles(workspaceApmDir(workdir), workdir);
|
|
3190
|
+
markSkillsSyncedForCliVersion(workdir, cliVersion);
|
|
3191
|
+
return cliVersion;
|
|
3192
|
+
},
|
|
3193
|
+
(version) => `\u5DF2\u540C\u6B65\u81F3 CLI ${version}`
|
|
3505
3194
|
);
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3195
|
+
} else {
|
|
3196
|
+
eventSession.addCliStep(
|
|
3197
|
+
"\u540C\u6B65 WebIDE \u89C4\u5219",
|
|
3198
|
+
`\u7248\u672C\u4E00\u81F4\uFF08${cliVersion}\uFF09\uFF0C\u8DF3\u8FC7`,
|
|
3199
|
+
"skip"
|
|
3200
|
+
);
|
|
3201
|
+
await syncPrepLog();
|
|
3509
3202
|
}
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3203
|
+
await runPrepStep(
|
|
3204
|
+
"\u5DE5\u4F5C\u533A\u4ED3\u5E93\u6E05\u5355",
|
|
3205
|
+
async () => {
|
|
3206
|
+
const workspaceRepos = resolveWorkspaceRepos(workdir);
|
|
3207
|
+
try {
|
|
3208
|
+
await enrichWorkspaceReposRemoteUrls(workspaceRepos);
|
|
3209
|
+
} catch (err) {
|
|
3210
|
+
console.warn(
|
|
3211
|
+
"[apm] \u8865\u9F50 workspace-repos remoteUrl \u5931\u8D25:",
|
|
3212
|
+
err instanceof Error ? err.message : err
|
|
3213
|
+
);
|
|
3214
|
+
}
|
|
3215
|
+
return workspaceRepos;
|
|
3216
|
+
},
|
|
3217
|
+
(repos) => `kind=${repos.kind} repos=${repos.repos.length}`
|
|
3515
3218
|
);
|
|
3516
|
-
if (
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
writeFileSync5(
|
|
3522
|
-
toFsPath(join6(docsDir, MANIFEST_FILE2)),
|
|
3523
|
-
`${JSON.stringify(remoteManifest, null, 2)}
|
|
3524
|
-
`,
|
|
3525
|
-
"utf8"
|
|
3526
|
-
);
|
|
3527
|
-
console.log(
|
|
3528
|
-
`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: projectId=${projectId} \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted} \u2192 .apm/project/${projectId}/`
|
|
3529
|
-
);
|
|
3530
|
-
return {
|
|
3531
|
-
synced: true,
|
|
3532
|
-
projectId,
|
|
3533
|
-
downloaded,
|
|
3534
|
-
deleted
|
|
3535
|
-
};
|
|
3536
|
-
}
|
|
3537
|
-
async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot, options) {
|
|
3538
|
-
const api = createApmApiClient(cfg);
|
|
3539
|
-
const { projectId } = await resolveProjectIdForSync(
|
|
3540
|
-
api,
|
|
3541
|
-
workdirPath,
|
|
3542
|
-
options
|
|
3543
|
-
);
|
|
3544
|
-
if (!projectId) {
|
|
3545
|
-
return 0;
|
|
3546
|
-
}
|
|
3547
|
-
const targetApmDir = apmRoot ?? workspaceApmDir(workdirPath);
|
|
3548
|
-
const localPaths = listLocalDocumentPaths(targetApmDir, projectId);
|
|
3549
|
-
if (localPaths.length === 0) {
|
|
3550
|
-
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u672C\u5730\u6587\u4EF6\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
3551
|
-
return 0;
|
|
3552
|
-
}
|
|
3553
|
-
const remoteManifest = (await api.cli.getProjectDocumentManifest({ projectId })).manifest ?? null;
|
|
3554
|
-
const remoteHashByPath = new Map(
|
|
3555
|
-
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
3556
|
-
);
|
|
3557
|
-
const remoteDescriptionByPath = new Map(
|
|
3558
|
-
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.description])
|
|
3559
|
-
);
|
|
3560
|
-
let synced = 0;
|
|
3561
|
-
for (const path3 of localPaths) {
|
|
3562
|
-
const absPath = toFsPath(
|
|
3563
|
-
projectDocumentLocalPath(targetApmDir, projectId, path3)
|
|
3219
|
+
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
3220
|
+
await runPrepStep(
|
|
3221
|
+
"\u5207\u6362\u57FA\u7EBF\u5E76\u62C9\u53D6\u6700\u65B0",
|
|
3222
|
+
async () => checkoutBaselineBranches(msg2.baseBranch, { cwd: workdir }),
|
|
3223
|
+
(result) => `baseline=${result.baseline} kind=${result.kind} repos=${result.repos.length}`
|
|
3564
3224
|
);
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3225
|
+
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
3226
|
+
await runPrepStep(
|
|
3227
|
+
"\u540C\u6B65\u9879\u76EE\u6587\u6863",
|
|
3228
|
+
async () => {
|
|
3229
|
+
const docs = await syncProjectDocumentsPull(workdir, void 0, {
|
|
3230
|
+
projectId
|
|
3231
|
+
});
|
|
3232
|
+
return docs;
|
|
3233
|
+
},
|
|
3234
|
+
(result) => {
|
|
3235
|
+
if (!result.synced) {
|
|
3236
|
+
return result.projectId ? "\u65E0\u9700\u540C\u6B65\u6216\u8DF3\u8FC7" : "\u8DF3\u8FC7\uFF1A\u65E0 projectId";
|
|
3237
|
+
}
|
|
3238
|
+
return `downloaded=${result.downloaded} deleted=${result.deleted}`;
|
|
3239
|
+
}
|
|
3240
|
+
);
|
|
3241
|
+
eventSession.addCliStep("\u542F\u52A8\u6253\u5305 Agent", "\u51C6\u5907 create\u2026", "running");
|
|
3242
|
+
await syncPrepLog();
|
|
3243
|
+
let failedByTool = false;
|
|
3244
|
+
const outcome = await runCursorAgent(
|
|
3245
|
+
cfg2,
|
|
3246
|
+
{
|
|
3247
|
+
messageId,
|
|
3248
|
+
prompt: msg2.content,
|
|
3249
|
+
model: msg2.model,
|
|
3250
|
+
apiKey: msg2.apiKey,
|
|
3251
|
+
workdir,
|
|
3252
|
+
user: msg2.user || "webide"
|
|
3253
|
+
},
|
|
3254
|
+
{
|
|
3255
|
+
signal,
|
|
3256
|
+
forceSend: true,
|
|
3257
|
+
eventSession,
|
|
3258
|
+
enableAppendMessage: false,
|
|
3259
|
+
enableAskQuestion: false,
|
|
3260
|
+
enableWebIdePlanTools: false,
|
|
3261
|
+
enablePtySessionMcp: false,
|
|
3262
|
+
enableSandbox: false,
|
|
3263
|
+
enablePackageStatusTools: true,
|
|
3264
|
+
packageId,
|
|
3265
|
+
projectId,
|
|
3266
|
+
onPackageFailed: async () => {
|
|
3267
|
+
failedByTool = true;
|
|
3268
|
+
},
|
|
3269
|
+
taskId: `package:${packageId}`,
|
|
3270
|
+
createRemoteLogSync: (agentId) => {
|
|
3271
|
+
logSyncRef.current = createThrottledWebIdeMessageLogSync(
|
|
3272
|
+
cfg2,
|
|
3273
|
+
{
|
|
3274
|
+
taskId: `package:${packageId}`,
|
|
3275
|
+
messageId,
|
|
3276
|
+
agentId
|
|
3277
|
+
},
|
|
3278
|
+
(err) => {
|
|
3279
|
+
console.warn(
|
|
3280
|
+
"[apm] WebIDE \u6253\u5305\u65E5\u5FD7\u540C\u6B65\u5931\u8D25:",
|
|
3281
|
+
err instanceof Error ? err.message : err
|
|
3282
|
+
);
|
|
3283
|
+
},
|
|
3284
|
+
{
|
|
3285
|
+
objectPrefixOverride: `events/webide-package/${packageId}/`
|
|
3286
|
+
}
|
|
3287
|
+
);
|
|
3288
|
+
return logSyncRef.current;
|
|
3289
|
+
},
|
|
3290
|
+
onRunStarted: async ({ agentId, runId }) => {
|
|
3291
|
+
eventSession.addCliStep(
|
|
3292
|
+
"\u542F\u52A8\u6253\u5305 Agent",
|
|
3293
|
+
`agentId=${agentId} runId=${runId}`,
|
|
3294
|
+
"ok"
|
|
3295
|
+
);
|
|
3296
|
+
logSyncRef.current?.schedule(eventSession);
|
|
3297
|
+
await logSyncRef.current?.markRun(runId, "running");
|
|
3298
|
+
await logSyncRef.current?.flush(eventSession);
|
|
3299
|
+
}
|
|
3300
|
+
}
|
|
3301
|
+
);
|
|
3302
|
+
const tokenUsage = outcome.usage != null ? {
|
|
3303
|
+
modelId: outcome.modelId ?? (msg2.model?.trim() || void 0),
|
|
3304
|
+
inputTokens: outcome.usage.inputTokens,
|
|
3305
|
+
outputTokens: outcome.usage.outputTokens,
|
|
3306
|
+
cacheReadTokens: outcome.usage.cacheReadTokens,
|
|
3307
|
+
cacheWriteTokens: outcome.usage.cacheWriteTokens,
|
|
3308
|
+
totalTokens: outcome.usage.totalTokens,
|
|
3309
|
+
...outcome.usage.reasoningTokens != null ? { reasoningTokens: outcome.usage.reasoningTokens } : {}
|
|
3310
|
+
} : void 0;
|
|
3311
|
+
if (outcome.status === "error") {
|
|
3312
|
+
const detail = formatCursorRunFailure(outcome.runId, {
|
|
3313
|
+
resultText: outcome.result
|
|
3314
|
+
});
|
|
3315
|
+
await logSyncRef.current?.markRun(
|
|
3316
|
+
outcome.runId,
|
|
3317
|
+
"error",
|
|
3318
|
+
detail,
|
|
3319
|
+
tokenUsage
|
|
3320
|
+
);
|
|
3321
|
+
if (!failedByTool) {
|
|
3322
|
+
await updatePackageStatus(cfg2, packageId, "FAILED", { error: detail });
|
|
3323
|
+
}
|
|
3324
|
+
return;
|
|
3569
3325
|
}
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3326
|
+
if (outcome.status === "cancelled" || signal.aborted) {
|
|
3327
|
+
await logSyncRef.current?.markRun(
|
|
3328
|
+
outcome.runId,
|
|
3329
|
+
"cancelled",
|
|
3330
|
+
"\u5DF2\u53D6\u6D88",
|
|
3331
|
+
tokenUsage
|
|
3332
|
+
);
|
|
3333
|
+
await updatePackageStatus(cfg2, packageId, "CANCELLED", {
|
|
3334
|
+
error: "\u5DF2\u53D6\u6D88"
|
|
3335
|
+
});
|
|
3336
|
+
return;
|
|
3337
|
+
}
|
|
3338
|
+
await logSyncRef.current?.markRun(
|
|
3339
|
+
outcome.runId,
|
|
3340
|
+
"finished",
|
|
3341
|
+
null,
|
|
3342
|
+
tokenUsage
|
|
3343
|
+
);
|
|
3344
|
+
if (failedByTool) {
|
|
3345
|
+
console.log(
|
|
3346
|
+
`[apm] webide-package Agent \u5DF2\u6807 FAILED packageId=${packageId}`
|
|
3347
|
+
);
|
|
3348
|
+
return;
|
|
3349
|
+
}
|
|
3350
|
+
const uploaded = await runPrepStep(
|
|
3351
|
+
"\u4E0A\u4F20\u6253\u5305\u4EA7\u7269\u5230 MinIO",
|
|
3352
|
+
() => uploadPackageArtifact(cfg2, msg2.target, packageId, projectId),
|
|
3353
|
+
(result) => `objectKey=${result.artifactPath}${result.artifactUrl ? ` url=${result.artifactUrl}` : ""}`
|
|
3354
|
+
);
|
|
3355
|
+
await updatePackageStatus(cfg2, packageId, "SUCCESS", {
|
|
3356
|
+
artifactPath: uploaded.artifactPath,
|
|
3357
|
+
...uploaded.artifactUrl ? { artifactUrl: uploaded.artifactUrl } : {}
|
|
3575
3358
|
});
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
}
|
|
3579
|
-
if (synced === 0) {
|
|
3580
|
-
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
3581
|
-
}
|
|
3582
|
-
return synced;
|
|
3583
|
-
}
|
|
3584
|
-
|
|
3585
|
-
// src/commands/connect/cli-version-sync.ts
|
|
3586
|
-
import { existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
|
|
3587
|
-
import { join as join7 } from "path";
|
|
3588
|
-
var CLI_VERSION_FILE = ".cli-version.json";
|
|
3589
|
-
function manifestPath2(apmDir) {
|
|
3590
|
-
return join7(apmDir, CLI_VERSION_FILE);
|
|
3591
|
-
}
|
|
3592
|
-
function loadManifest2(apmDir) {
|
|
3593
|
-
const path3 = toFsPath(manifestPath2(apmDir));
|
|
3594
|
-
if (!existsSync9(path3)) {
|
|
3595
|
-
return null;
|
|
3596
|
-
}
|
|
3597
|
-
try {
|
|
3598
|
-
const parsed = JSON.parse(
|
|
3599
|
-
readFileSync8(path3, "utf8")
|
|
3359
|
+
console.log(
|
|
3360
|
+
`[apm] webide-package \u5B8C\u6210 target=${msg2.target} packageId=${packageId} agentId=${outcome.agentId} artifact=${uploaded.artifactPath}`
|
|
3600
3361
|
);
|
|
3601
|
-
|
|
3602
|
-
|
|
3362
|
+
} catch (err) {
|
|
3363
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
3364
|
+
console.error(`[apm] webide-package \u5931\u8D25: ${detail}`);
|
|
3365
|
+
try {
|
|
3366
|
+
if (signal.aborted) {
|
|
3367
|
+
await updatePackageStatus(cfg2, packageId, "CANCELLED", {
|
|
3368
|
+
error: detail
|
|
3369
|
+
});
|
|
3370
|
+
} else {
|
|
3371
|
+
await updatePackageStatus(cfg2, packageId, "FAILED", { error: detail });
|
|
3372
|
+
}
|
|
3373
|
+
} catch (statusErr) {
|
|
3374
|
+
console.error(
|
|
3375
|
+
"[apm] \u5199\u5165\u6253\u5305 FAILED \u5931\u8D25:",
|
|
3376
|
+
statusErr instanceof Error ? statusErr.message : statusErr
|
|
3377
|
+
);
|
|
3603
3378
|
}
|
|
3604
|
-
} catch {
|
|
3605
|
-
}
|
|
3606
|
-
return null;
|
|
3607
|
-
}
|
|
3608
|
-
function saveManifest2(apmDir, cliVersion) {
|
|
3609
|
-
const manifest = { version: 1, cliVersion };
|
|
3610
|
-
writeFileSync6(
|
|
3611
|
-
toFsPath(manifestPath2(apmDir)),
|
|
3612
|
-
`${JSON.stringify(manifest, null, 2)}
|
|
3613
|
-
`,
|
|
3614
|
-
"utf8"
|
|
3615
|
-
);
|
|
3616
|
-
}
|
|
3617
|
-
var syncedInSession = /* @__PURE__ */ new Map();
|
|
3618
|
-
function shouldSyncSkillsForCliVersion(workdir, currentVersion) {
|
|
3619
|
-
const cached = syncedInSession.get(workdir);
|
|
3620
|
-
if (cached === currentVersion) {
|
|
3621
|
-
return false;
|
|
3622
|
-
}
|
|
3623
|
-
const stored = loadManifest2(workspaceApmDir(workdir));
|
|
3624
|
-
if (stored?.cliVersion === currentVersion) {
|
|
3625
|
-
syncedInSession.set(workdir, currentVersion);
|
|
3626
|
-
return false;
|
|
3627
3379
|
}
|
|
3628
|
-
return true;
|
|
3629
|
-
}
|
|
3630
|
-
function markSkillsSyncedForCliVersion(workdir, cliVersion) {
|
|
3631
|
-
saveManifest2(workspaceApmDir(workdir), cliVersion);
|
|
3632
|
-
syncedInSession.set(workdir, cliVersion);
|
|
3633
3380
|
}
|
|
3634
3381
|
|
|
3635
|
-
// src/commands/connect/
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
const
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
const
|
|
3644
|
-
if (
|
|
3645
|
-
return
|
|
3382
|
+
// src/commands/connect/ensure-message-reply.ts
|
|
3383
|
+
var DEFAULT_REPLY = "\u4EFB\u52A1\u5DF2\u5B8C\u6210\u3002";
|
|
3384
|
+
function resolveMessageReplyFallback(fallback) {
|
|
3385
|
+
for (const candidate of [
|
|
3386
|
+
fallback.assistantText,
|
|
3387
|
+
fallback.result,
|
|
3388
|
+
fallback.createPlan
|
|
3389
|
+
]) {
|
|
3390
|
+
const trimmed = candidate?.trim();
|
|
3391
|
+
if (trimmed) {
|
|
3392
|
+
return trimmed;
|
|
3646
3393
|
}
|
|
3647
|
-
} catch {
|
|
3648
|
-
return null;
|
|
3649
3394
|
}
|
|
3650
|
-
return
|
|
3395
|
+
return DEFAULT_REPLY;
|
|
3651
3396
|
}
|
|
3652
3397
|
|
|
3653
|
-
// src/commands/connect/
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
"start-develop",
|
|
3657
|
-
"fix-and-retest",
|
|
3658
|
-
"report-defect",
|
|
3659
|
-
"create-pr",
|
|
3660
|
-
"finish-manual-test"
|
|
3661
|
-
]);
|
|
3662
|
-
function shouldCommitAfterWebIdeMessage(action) {
|
|
3663
|
-
return WEBIDE_CODE_CHANGE_ACTIONS.has(action);
|
|
3664
|
-
}
|
|
3665
|
-
function isStartLocalServicesAction(action) {
|
|
3666
|
-
return action === "enter-manual-test" || action === "skip-test";
|
|
3667
|
-
}
|
|
3668
|
-
function isExecuteSqlAction(action) {
|
|
3669
|
-
return action === "execute-sql";
|
|
3670
|
-
}
|
|
3671
|
-
function syncLocalTaskStatus(workdir, taskId, msg, status) {
|
|
3672
|
-
syncWebIdeTaskState(workdir, taskId, {
|
|
3673
|
-
messageId: msg.messageId,
|
|
3674
|
-
action: msg.action,
|
|
3675
|
-
status
|
|
3676
|
-
});
|
|
3677
|
-
}
|
|
3678
|
-
async function updateStatus(cfg, messageId, status) {
|
|
3679
|
-
const api = createApmApiClient(cfg);
|
|
3398
|
+
// src/commands/connect/core/worker/handlers/start-project.ts
|
|
3399
|
+
async function updateStatus(cfg2, messageId, status) {
|
|
3400
|
+
const api = createApmApiClient(cfg2);
|
|
3680
3401
|
await api.cli.webideUpdateMessageStatus({ id: messageId, status });
|
|
3681
3402
|
}
|
|
3682
|
-
async function setError(
|
|
3683
|
-
const api = createApmApiClient(
|
|
3403
|
+
async function setError(cfg2, messageId, error) {
|
|
3404
|
+
const api = createApmApiClient(cfg2);
|
|
3684
3405
|
await api.cli.webideSetMessageError({ id: messageId, error });
|
|
3685
3406
|
}
|
|
3686
|
-
async function
|
|
3687
|
-
const
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
async function handleWebIdeInboundMessage(cfg, msg, signal) {
|
|
3691
|
-
const workdir = requireRemoteWorkdir(msg.workdir);
|
|
3692
|
-
const messageId = msg.messageId;
|
|
3693
|
-
const taskId = msg.taskId;
|
|
3694
|
-
const branchCacheKey = taskId;
|
|
3407
|
+
async function handleStartProject(cfg2, msg2, signal) {
|
|
3408
|
+
const workdir = requireRemoteWorkdir(msg2.workdir);
|
|
3409
|
+
const messageId = msg2.messageId;
|
|
3410
|
+
const taskId = msg2.taskId;
|
|
3695
3411
|
console.log(
|
|
3696
|
-
`[apm] webide-
|
|
3412
|
+
`[apm] webide-start-project target=${msg2.target} restart=${msg2.restart} taskId=${taskId} messageId=${messageId}`
|
|
3697
3413
|
);
|
|
3698
|
-
await updateStatus(
|
|
3699
|
-
|
|
3700
|
-
const eventSession = new EventSession(msg.content);
|
|
3701
|
-
const prepAgentId = loadWebIdeAgentId(workdir, taskId) ?? "webide-cli-prep";
|
|
3414
|
+
await updateStatus(cfg2, messageId, "TYPING");
|
|
3415
|
+
const eventSession = new EventSession(msg2.content);
|
|
3702
3416
|
const logSyncRef = {
|
|
3703
3417
|
current: createThrottledWebIdeMessageLogSync(
|
|
3704
|
-
|
|
3705
|
-
{ taskId, messageId, agentId:
|
|
3418
|
+
cfg2,
|
|
3419
|
+
{ taskId, messageId, agentId: "webide-start-project" },
|
|
3706
3420
|
(err) => {
|
|
3707
3421
|
console.warn(
|
|
3708
|
-
"[apm] WebIDE \u65E5\u5FD7\u540C\u6B65\u5931\u8D25:",
|
|
3422
|
+
"[apm] WebIDE \u542F\u52A8\u9879\u76EE\u65E5\u5FD7\u540C\u6B65\u5931\u8D25:",
|
|
3709
3423
|
err instanceof Error ? err.message : err
|
|
3710
3424
|
);
|
|
3711
3425
|
}
|
|
3712
3426
|
)
|
|
3713
3427
|
};
|
|
3714
|
-
const syncPrepLog = async () => {
|
|
3715
|
-
const sync = logSyncRef.current;
|
|
3716
|
-
if (!sync) return;
|
|
3717
|
-
sync.schedule(eventSession);
|
|
3718
|
-
await sync.flush(eventSession);
|
|
3719
|
-
};
|
|
3720
|
-
const runPrepStep = async (step, work, formatOk) => {
|
|
3721
|
-
eventSession.addCliStep(step, "\u8FDB\u884C\u4E2D\u2026", "running");
|
|
3722
|
-
await syncPrepLog();
|
|
3723
|
-
try {
|
|
3724
|
-
const result = await work();
|
|
3725
|
-
eventSession.addCliStep(step, formatOk(result), "ok");
|
|
3726
|
-
await syncPrepLog();
|
|
3727
|
-
return result;
|
|
3728
|
-
} catch (err) {
|
|
3729
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
3730
|
-
eventSession.addCliStep(step, detail, "error");
|
|
3731
|
-
await syncPrepLog();
|
|
3732
|
-
throw err;
|
|
3733
|
-
}
|
|
3734
|
-
};
|
|
3735
3428
|
try {
|
|
3736
3429
|
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
3737
|
-
await
|
|
3738
|
-
|
|
3739
|
-
async () => {
|
|
3740
|
-
const { didInit } = await ensureWorkspaceInitialized(workdir);
|
|
3741
|
-
return didInit;
|
|
3742
|
-
},
|
|
3743
|
-
(didInit) => didInit ? "\u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A" : "\u5DE5\u4F5C\u533A\u5DF2\u5C31\u7EEA"
|
|
3744
|
-
);
|
|
3745
|
-
const cliVersion = readCliVersion();
|
|
3746
|
-
if (shouldSyncSkillsForCliVersion(workdir, cliVersion)) {
|
|
3747
|
-
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
3748
|
-
await runPrepStep(
|
|
3749
|
-
"\u540C\u6B65 WebIDE \u89C4\u5219",
|
|
3750
|
-
async () => {
|
|
3751
|
-
await copyWebIdeTemplateFiles(workspaceApmDir(workdir), workdir);
|
|
3752
|
-
markSkillsSyncedForCliVersion(workdir, cliVersion);
|
|
3753
|
-
return cliVersion;
|
|
3754
|
-
},
|
|
3755
|
-
(version) => `\u5DF2\u540C\u6B65\u81F3 CLI ${version}`
|
|
3756
|
-
);
|
|
3757
|
-
} else {
|
|
3758
|
-
eventSession.addCliStep(
|
|
3759
|
-
"\u540C\u6B65 WebIDE \u89C4\u5219",
|
|
3760
|
-
`\u7248\u672C\u4E00\u81F4\uFF08${cliVersion}\uFF09\uFF0C\u8DF3\u8FC7`,
|
|
3761
|
-
"skip"
|
|
3762
|
-
);
|
|
3763
|
-
await syncPrepLog();
|
|
3764
|
-
}
|
|
3765
|
-
await runPrepStep(
|
|
3766
|
-
"\u5DE5\u4F5C\u533A\u4ED3\u5E93\u6E05\u5355",
|
|
3767
|
-
async () => {
|
|
3768
|
-
const workspaceRepos = resolveWorkspaceRepos(workdir);
|
|
3769
|
-
try {
|
|
3770
|
-
await enrichWorkspaceReposRemoteUrls(workspaceRepos);
|
|
3771
|
-
} catch (err) {
|
|
3772
|
-
console.warn(
|
|
3773
|
-
"[apm] \u8865\u9F50 workspace-repos remoteUrl \u5931\u8D25:",
|
|
3774
|
-
err instanceof Error ? err.message : err
|
|
3775
|
-
);
|
|
3776
|
-
}
|
|
3777
|
-
return workspaceRepos;
|
|
3778
|
-
},
|
|
3779
|
-
(repos) => `kind=${repos.kind} repos=${repos.repos.length}`
|
|
3780
|
-
);
|
|
3781
|
-
if (shouldRunBranch(branchCacheKey, workdir)) {
|
|
3782
|
-
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
3783
|
-
await runPrepStep(
|
|
3784
|
-
"\u51C6\u5907\u7279\u6027\u5206\u652F",
|
|
3785
|
-
async () => runTaskBranch(taskId, { cwd: workdir }),
|
|
3786
|
-
(branchResult) => {
|
|
3787
|
-
markBranchDone(branchCacheKey, workdir);
|
|
3788
|
-
return `kind=${branchResult.kind} branch=${branchResult.branch} baseline=${branchResult.baseline} repos=${branchResult.repos.length}`;
|
|
3789
|
-
}
|
|
3790
|
-
);
|
|
3791
|
-
} else {
|
|
3792
|
-
eventSession.addCliStep("\u51C6\u5907\u7279\u6027\u5206\u652F", "\u5DF2\u7F13\u5B58\uFF0C\u8DF3\u8FC7", "skip");
|
|
3793
|
-
await syncPrepLog();
|
|
3794
|
-
}
|
|
3795
|
-
if (shouldEnsureWebIdePullRequests(msg.action)) {
|
|
3796
|
-
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
3797
|
-
await runPrepStep(
|
|
3798
|
-
"\u6D4B\u8BD5\u524D\u63A8\u9001",
|
|
3799
|
-
async () => pushWorkspaceRepos(workdir),
|
|
3800
|
-
(pushed) => `\u5DF2 push ${pushed} \u4E2A\u4ED3\u5E93`
|
|
3801
|
-
);
|
|
3802
|
-
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
3803
|
-
await runPrepStep(
|
|
3804
|
-
"\u786E\u4FDD PR",
|
|
3805
|
-
async () => {
|
|
3806
|
-
await ensureWebIdePullRequests(cfg, taskId, workdir);
|
|
3807
|
-
return true;
|
|
3808
|
-
},
|
|
3809
|
-
() => "PR \u68C0\u67E5\u5B8C\u6210"
|
|
3810
|
-
);
|
|
3811
|
-
}
|
|
3812
|
-
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
3813
|
-
const api = createApmApiClient(cfg);
|
|
3814
|
-
let projectId = null;
|
|
3815
|
-
await runPrepStep(
|
|
3816
|
-
"\u540C\u6B65\u9879\u76EE\u6587\u6863",
|
|
3817
|
-
async () => {
|
|
3818
|
-
try {
|
|
3819
|
-
const baseline = await api.cli.projectBaseBranch({ taskId });
|
|
3820
|
-
projectId = baseline.projectId?.trim() || null;
|
|
3821
|
-
if (!projectId) {
|
|
3822
|
-
return { synced: false, reason: "\u65E0 projectId" };
|
|
3823
|
-
}
|
|
3824
|
-
const docs = await syncProjectDocumentsPull(workdir, void 0, {
|
|
3825
|
-
projectId
|
|
3826
|
-
});
|
|
3827
|
-
return {
|
|
3828
|
-
synced: docs.synced,
|
|
3829
|
-
downloaded: docs.downloaded,
|
|
3830
|
-
deleted: docs.deleted
|
|
3831
|
-
};
|
|
3832
|
-
} catch (err) {
|
|
3833
|
-
console.warn(
|
|
3834
|
-
"[apm] WebIDE \u9879\u76EE\u6587\u6863\u540C\u6B65\u5931\u8D25:",
|
|
3835
|
-
err instanceof Error ? err.message : err
|
|
3836
|
-
);
|
|
3837
|
-
return {
|
|
3838
|
-
synced: false,
|
|
3839
|
-
reason: err instanceof Error ? err.message : String(err)
|
|
3840
|
-
};
|
|
3841
|
-
}
|
|
3842
|
-
},
|
|
3843
|
-
(result) => {
|
|
3844
|
-
if ("reason" in result && result.reason) {
|
|
3845
|
-
return `\u8DF3\u8FC7\uFF1A${result.reason}`;
|
|
3846
|
-
}
|
|
3847
|
-
if (!result.synced) return "\u65E0\u9700\u540C\u6B65";
|
|
3848
|
-
return `downloaded=${result.downloaded ?? 0} deleted=${result.deleted ?? 0}`;
|
|
3849
|
-
}
|
|
3850
|
-
);
|
|
3851
|
-
await runPrepStep(
|
|
3852
|
-
"\u540C\u6B65\u4EFB\u52A1\u9644\u4EF6",
|
|
3853
|
-
async () => {
|
|
3854
|
-
try {
|
|
3855
|
-
const attachments = await api.cli.webideListAttachments({ taskId });
|
|
3856
|
-
const names = await syncWebIdeAttachments(
|
|
3857
|
-
cfg,
|
|
3858
|
-
taskId,
|
|
3859
|
-
workdir,
|
|
3860
|
-
attachments ?? []
|
|
3861
|
-
);
|
|
3862
|
-
return names;
|
|
3863
|
-
} catch (err) {
|
|
3864
|
-
console.warn(
|
|
3865
|
-
"[apm] WebIDE \u9644\u4EF6\u540C\u6B65\u5931\u8D25:",
|
|
3866
|
-
err instanceof Error ? err.message : err
|
|
3867
|
-
);
|
|
3868
|
-
return [];
|
|
3869
|
-
}
|
|
3870
|
-
},
|
|
3871
|
-
(names) => names.length > 0 ? `\u5DF2\u540C\u6B65 ${names.length} \u4E2A\u9644\u4EF6` : "\u65E0\u9644\u4EF6\u6216\u540C\u6B65\u8DF3\u8FC7"
|
|
3872
|
-
);
|
|
3873
|
-
eventSession.addCliStep(
|
|
3874
|
-
"\u542F\u52A8 Cursor Agent",
|
|
3875
|
-
"\u51C6\u5907 resume/create\u2026",
|
|
3876
|
-
"running"
|
|
3877
|
-
);
|
|
3878
|
-
await syncPrepLog();
|
|
3879
|
-
const startLocalServices = isStartLocalServicesAction(msg.action);
|
|
3880
|
-
const executeSql = isExecuteSqlAction(msg.action);
|
|
3881
|
-
const savedAgentId = startLocalServices ? void 0 : loadWebIdeAgentId(workdir, taskId);
|
|
3882
|
-
const promptPayload = executeSql ? parseWebIdePromptPayload(msg.content) : null;
|
|
3883
|
-
const sqlExecutionId = executeSql && typeof promptPayload?.executionId === "string" ? promptPayload.executionId.trim() : void 0;
|
|
3430
|
+
await ensureWorkspaceInitialized(workdir);
|
|
3431
|
+
const api = createApmApiClient(cfg2);
|
|
3884
3432
|
const outcome = await runCursorAgent(
|
|
3885
|
-
|
|
3433
|
+
cfg2,
|
|
3886
3434
|
{
|
|
3887
3435
|
messageId,
|
|
3888
|
-
prompt:
|
|
3889
|
-
model:
|
|
3890
|
-
apiKey:
|
|
3436
|
+
prompt: msg2.content,
|
|
3437
|
+
model: msg2.model,
|
|
3438
|
+
apiKey: msg2.apiKey,
|
|
3891
3439
|
workdir,
|
|
3892
|
-
user:
|
|
3893
|
-
resumeAgentId: savedAgentId
|
|
3440
|
+
user: msg2.user || "webide"
|
|
3894
3441
|
},
|
|
3895
3442
|
{
|
|
3896
3443
|
signal,
|
|
3897
3444
|
forceSend: true,
|
|
3898
3445
|
eventSession,
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
signal
|
|
3904
|
-
}),
|
|
3905
|
-
enableAskQuestion: !startLocalServices && !executeSql,
|
|
3906
|
-
enableWebIdePlanTools: !startLocalServices && !executeSql,
|
|
3907
|
-
enablePtySessionMcp: startLocalServices,
|
|
3908
|
-
enableMysqlTools: executeSql,
|
|
3909
|
-
sqlExecutionId,
|
|
3446
|
+
enableAppendMessage: false,
|
|
3447
|
+
enableAskQuestion: false,
|
|
3448
|
+
enableWebIdePlanTools: false,
|
|
3449
|
+
enablePtySessionMcp: true,
|
|
3910
3450
|
enableSandbox: false,
|
|
3911
|
-
onInvalidatePersistedAgentId: startLocalServices ? void 0 : () => clearWebIdeAgentId(workdir, taskId),
|
|
3912
3451
|
taskId,
|
|
3913
3452
|
createRemoteLogSync: (agentId) => {
|
|
3914
|
-
if (!startLocalServices) {
|
|
3915
|
-
saveWebIdeAgentId(workdir, taskId, agentId);
|
|
3916
|
-
}
|
|
3917
3453
|
logSyncRef.current = createThrottledWebIdeMessageLogSync(
|
|
3918
|
-
|
|
3454
|
+
cfg2,
|
|
3919
3455
|
{ taskId, messageId, agentId },
|
|
3920
3456
|
(err) => {
|
|
3921
3457
|
console.warn(
|
|
3922
|
-
"[apm] WebIDE \u65E5\u5FD7\u540C\u6B65\u5931\u8D25:",
|
|
3458
|
+
"[apm] WebIDE \u542F\u52A8\u9879\u76EE\u65E5\u5FD7\u540C\u6B65\u5931\u8D25:",
|
|
3923
3459
|
err instanceof Error ? err.message : err
|
|
3924
3460
|
);
|
|
3925
3461
|
}
|
|
@@ -3927,11 +3463,8 @@ async function handleWebIdeInboundMessage(cfg, msg, signal) {
|
|
|
3927
3463
|
return logSyncRef.current;
|
|
3928
3464
|
},
|
|
3929
3465
|
onRunStarted: async ({ agentId, runId }) => {
|
|
3930
|
-
if (!startLocalServices) {
|
|
3931
|
-
saveWebIdeAgentId(workdir, taskId, agentId);
|
|
3932
|
-
}
|
|
3933
3466
|
eventSession.addCliStep(
|
|
3934
|
-
"\u542F\u52A8
|
|
3467
|
+
"\u542F\u52A8\u72EC\u7ACB Agent",
|
|
3935
3468
|
`agentId=${agentId} runId=${runId}`,
|
|
3936
3469
|
"ok"
|
|
3937
3470
|
);
|
|
@@ -3941,11 +3474,8 @@ async function handleWebIdeInboundMessage(cfg, msg, signal) {
|
|
|
3941
3474
|
}
|
|
3942
3475
|
}
|
|
3943
3476
|
);
|
|
3944
|
-
if (!startLocalServices) {
|
|
3945
|
-
saveWebIdeAgentId(workdir, taskId, outcome.agentId);
|
|
3946
|
-
}
|
|
3947
3477
|
const tokenUsage = outcome.usage != null ? {
|
|
3948
|
-
modelId: outcome.modelId ?? (
|
|
3478
|
+
modelId: outcome.modelId ?? (msg2.model?.trim() || void 0),
|
|
3949
3479
|
inputTokens: outcome.usage.inputTokens,
|
|
3950
3480
|
outputTokens: outcome.usage.outputTokens,
|
|
3951
3481
|
cacheReadTokens: outcome.usage.cacheReadTokens,
|
|
@@ -3963,22 +3493,7 @@ async function handleWebIdeInboundMessage(cfg, msg, signal) {
|
|
|
3963
3493
|
detail,
|
|
3964
3494
|
tokenUsage
|
|
3965
3495
|
);
|
|
3966
|
-
|
|
3967
|
-
try {
|
|
3968
|
-
await api.cli.webideFinalizeSqlExecution({
|
|
3969
|
-
executionId: sqlExecutionId,
|
|
3970
|
-
status: "FAILED",
|
|
3971
|
-
error: detail
|
|
3972
|
-
});
|
|
3973
|
-
} catch (finalizeErr) {
|
|
3974
|
-
console.warn(
|
|
3975
|
-
"[apm] finalize sql execution failed:",
|
|
3976
|
-
finalizeErr instanceof Error ? finalizeErr.message : finalizeErr
|
|
3977
|
-
);
|
|
3978
|
-
}
|
|
3979
|
-
}
|
|
3980
|
-
await setError(cfg, messageId, detail);
|
|
3981
|
-
syncLocalTaskStatus(workdir, taskId, msg, "FAILED");
|
|
3496
|
+
await setError(cfg2, messageId, detail);
|
|
3982
3497
|
return;
|
|
3983
3498
|
}
|
|
3984
3499
|
if (outcome.status === "cancelled" || signal.aborted) {
|
|
@@ -3988,381 +3503,914 @@ async function handleWebIdeInboundMessage(cfg, msg, signal) {
|
|
|
3988
3503
|
"\u5DF2\u53D6\u6D88",
|
|
3989
3504
|
tokenUsage
|
|
3990
3505
|
);
|
|
3991
|
-
|
|
3992
|
-
try {
|
|
3993
|
-
await api.cli.webideFinalizeSqlExecution({
|
|
3994
|
-
executionId: sqlExecutionId,
|
|
3995
|
-
status: "CANCELLED",
|
|
3996
|
-
error: "\u5DF2\u53D6\u6D88"
|
|
3997
|
-
});
|
|
3998
|
-
} catch (finalizeErr) {
|
|
3999
|
-
console.warn(
|
|
4000
|
-
"[apm] finalize sql execution cancelled:",
|
|
4001
|
-
finalizeErr instanceof Error ? finalizeErr.message : finalizeErr
|
|
4002
|
-
);
|
|
4003
|
-
}
|
|
4004
|
-
}
|
|
4005
|
-
await updateStatus(cfg, messageId, "CANCELLED");
|
|
4006
|
-
syncLocalTaskStatus(workdir, taskId, msg, "CANCELLED");
|
|
3506
|
+
await updateStatus(cfg2, messageId, "CANCELLED");
|
|
4007
3507
|
return;
|
|
4008
3508
|
}
|
|
4009
3509
|
const fallback = resolveMessageReplyFallback({
|
|
4010
3510
|
assistantText: outcome.assistantText,
|
|
4011
|
-
result: outcome.result
|
|
4012
|
-
createPlan: outcome.createPlan
|
|
3511
|
+
result: outcome.result
|
|
4013
3512
|
});
|
|
4014
3513
|
await api.cli.webideEnsureMessageContent({
|
|
4015
3514
|
id: messageId,
|
|
4016
3515
|
content: fallback
|
|
4017
3516
|
});
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
}
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
3517
|
+
await logSyncRef.current?.markRun(
|
|
3518
|
+
outcome.runId,
|
|
3519
|
+
"finished",
|
|
3520
|
+
null,
|
|
3521
|
+
tokenUsage
|
|
3522
|
+
);
|
|
3523
|
+
await updateStatus(cfg2, messageId, "SUCCESS");
|
|
3524
|
+
console.log(
|
|
3525
|
+
`[apm] webide-start-project \u5B8C\u6210 target=${msg2.target} messageId=${messageId} agentId=${outcome.agentId}`
|
|
3526
|
+
);
|
|
3527
|
+
} catch (err) {
|
|
3528
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
3529
|
+
console.error(`[apm] webide-start-project \u5931\u8D25: ${detail}`);
|
|
3530
|
+
try {
|
|
3531
|
+
await setError(cfg2, messageId, detail);
|
|
3532
|
+
} catch (statusErr) {
|
|
3533
|
+
console.error(
|
|
3534
|
+
"[apm] \u5199\u5165\u542F\u52A8\u9879\u76EE FAILED \u5931\u8D25:",
|
|
3535
|
+
statusErr instanceof Error ? statusErr.message : statusErr
|
|
3536
|
+
);
|
|
3537
|
+
}
|
|
3538
|
+
}
|
|
3539
|
+
}
|
|
3540
|
+
|
|
3541
|
+
// src/commands/connect/webide-agent-registry.ts
|
|
3542
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
|
|
3543
|
+
import { dirname as dirname5, resolve as resolve6 } from "node:path";
|
|
3544
|
+
function registryPath(workdir, taskId, fileName = "cursor-agent.json") {
|
|
3545
|
+
return resolve6(workdir, ".apm", "webide", taskId, fileName);
|
|
3546
|
+
}
|
|
3547
|
+
var DESIGN_AGENT_FILE = "design-agent.json";
|
|
3548
|
+
function readRegistry(path3) {
|
|
3549
|
+
if (!existsSync7(path3)) {
|
|
3550
|
+
return {};
|
|
3551
|
+
}
|
|
3552
|
+
try {
|
|
3553
|
+
const parsed = JSON.parse(readFileSync7(path3, "utf8"));
|
|
3554
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
3555
|
+
const raw = parsed;
|
|
3556
|
+
const state = {};
|
|
3557
|
+
if (typeof raw.agentId === "string" && raw.agentId.trim()) {
|
|
3558
|
+
state.agentId = raw.agentId.trim();
|
|
4053
3559
|
}
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
3560
|
+
if (typeof raw.taskId === "string" && raw.taskId.trim()) {
|
|
3561
|
+
state.taskId = raw.taskId.trim();
|
|
3562
|
+
}
|
|
3563
|
+
if (typeof raw.messageId === "string" && raw.messageId.trim()) {
|
|
3564
|
+
state.messageId = raw.messageId.trim();
|
|
3565
|
+
}
|
|
3566
|
+
if (typeof raw.action === "string" && raw.action.trim()) {
|
|
3567
|
+
state.action = raw.action.trim();
|
|
3568
|
+
}
|
|
3569
|
+
if (raw.status === "TYPING" || raw.status === "SUCCESS" || raw.status === "FAILED" || raw.status === "CANCELLED") {
|
|
3570
|
+
state.status = raw.status;
|
|
3571
|
+
}
|
|
3572
|
+
if (typeof raw.updatedAt === "string" && raw.updatedAt.trim()) {
|
|
3573
|
+
state.updatedAt = raw.updatedAt.trim();
|
|
3574
|
+
}
|
|
3575
|
+
return state;
|
|
3576
|
+
}
|
|
3577
|
+
} catch {
|
|
3578
|
+
}
|
|
3579
|
+
return {};
|
|
3580
|
+
}
|
|
3581
|
+
function writeRegistry(path3, registry) {
|
|
3582
|
+
mkdirSync4(dirname5(path3), { recursive: true });
|
|
3583
|
+
writeFileSync5(path3, `${JSON.stringify(registry, null, 2)}
|
|
3584
|
+
`, "utf8");
|
|
3585
|
+
}
|
|
3586
|
+
function syncWebIdeTaskState(workdir, taskId, patch) {
|
|
3587
|
+
const trimmedTaskId = taskId.trim();
|
|
3588
|
+
if (!trimmedTaskId) return;
|
|
3589
|
+
const path3 = registryPath(workdir, trimmedTaskId);
|
|
3590
|
+
const current = readRegistry(path3);
|
|
3591
|
+
const next = {
|
|
3592
|
+
...current,
|
|
3593
|
+
taskId: trimmedTaskId,
|
|
3594
|
+
updatedAt: patch.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
3595
|
+
};
|
|
3596
|
+
if (patch.agentId !== void 0) {
|
|
3597
|
+
const agentId = patch.agentId.trim();
|
|
3598
|
+
if (agentId) next.agentId = agentId;
|
|
3599
|
+
else delete next.agentId;
|
|
3600
|
+
}
|
|
3601
|
+
if (patch.messageId !== void 0) {
|
|
3602
|
+
const messageId = patch.messageId.trim();
|
|
3603
|
+
if (messageId) next.messageId = messageId;
|
|
3604
|
+
else delete next.messageId;
|
|
3605
|
+
}
|
|
3606
|
+
if (patch.action !== void 0) {
|
|
3607
|
+
const action = patch.action.trim();
|
|
3608
|
+
if (action) next.action = action;
|
|
3609
|
+
else delete next.action;
|
|
3610
|
+
}
|
|
3611
|
+
if (patch.status !== void 0) {
|
|
3612
|
+
next.status = patch.status;
|
|
3613
|
+
}
|
|
3614
|
+
writeRegistry(path3, next);
|
|
3615
|
+
}
|
|
3616
|
+
function loadWebIdeAgentId(workdir, taskId) {
|
|
3617
|
+
return readRegistry(registryPath(workdir, taskId)).agentId;
|
|
3618
|
+
}
|
|
3619
|
+
function saveWebIdeAgentId(workdir, taskId, agentId) {
|
|
3620
|
+
syncWebIdeTaskState(workdir, taskId, { agentId });
|
|
3621
|
+
}
|
|
3622
|
+
function clearWebIdeAgentId(workdir, taskId) {
|
|
3623
|
+
const path3 = registryPath(workdir, taskId);
|
|
3624
|
+
if (!existsSync7(path3)) return;
|
|
3625
|
+
syncWebIdeTaskState(workdir, taskId, { agentId: "" });
|
|
3626
|
+
}
|
|
3627
|
+
function loadWebIdeDesignAgentId(workdir, taskId) {
|
|
3628
|
+
return readRegistry(registryPath(workdir, taskId, DESIGN_AGENT_FILE)).agentId;
|
|
3629
|
+
}
|
|
3630
|
+
function saveWebIdeDesignAgentId(workdir, taskId, agentId) {
|
|
3631
|
+
const trimmedTaskId = taskId.trim();
|
|
3632
|
+
if (!trimmedTaskId) return;
|
|
3633
|
+
const path3 = registryPath(workdir, trimmedTaskId, DESIGN_AGENT_FILE);
|
|
3634
|
+
const current = readRegistry(path3);
|
|
3635
|
+
writeRegistry(path3, {
|
|
3636
|
+
...current,
|
|
3637
|
+
taskId: trimmedTaskId,
|
|
3638
|
+
agentId: agentId.trim() || void 0,
|
|
3639
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3640
|
+
});
|
|
3641
|
+
}
|
|
3642
|
+
function clearWebIdeDesignAgentId(workdir, taskId) {
|
|
3643
|
+
const path3 = registryPath(workdir, taskId, DESIGN_AGENT_FILE);
|
|
3644
|
+
if (!existsSync7(path3)) return;
|
|
3645
|
+
const current = readRegistry(path3);
|
|
3646
|
+
const next = { ...current };
|
|
3647
|
+
delete next.agentId;
|
|
3648
|
+
writeRegistry(path3, {
|
|
3649
|
+
...next,
|
|
3650
|
+
taskId: taskId.trim(),
|
|
3651
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3652
|
+
});
|
|
3653
|
+
}
|
|
3654
|
+
|
|
3655
|
+
// src/commands/clean-webide-cache.ts
|
|
3656
|
+
import { existsSync as existsSync8, rmSync as rmSync2 } from "node:fs";
|
|
3657
|
+
import { resolve as resolve7 } from "node:path";
|
|
3658
|
+
import { getDefaultSdkStateRoot } from "@cursor/sdk";
|
|
3659
|
+
async function purgeCursorAgentStoreForAgent(workdir, agentId) {
|
|
3660
|
+
const trimmedAgentId = agentId.trim();
|
|
3661
|
+
const trimmedWorkdir = workdir.trim();
|
|
3662
|
+
if (!trimmedAgentId || !trimmedWorkdir) return false;
|
|
3663
|
+
const stateRoot = getDefaultSdkStateRoot(trimmedWorkdir);
|
|
3664
|
+
if (!existsSync8(stateRoot)) return false;
|
|
3665
|
+
const { SqliteLocalAgentStore } = await import(
|
|
3666
|
+
/* @vite-ignore */
|
|
3667
|
+
"@cursor/sdk/sqlite"
|
|
3668
|
+
);
|
|
3669
|
+
const store = await SqliteLocalAgentStore.open({
|
|
3670
|
+
workspaceRef: trimmedWorkdir
|
|
3671
|
+
});
|
|
3672
|
+
try {
|
|
3673
|
+
const runIds = [];
|
|
3674
|
+
let cursor;
|
|
3675
|
+
do {
|
|
3676
|
+
const page = await store.runs.list({
|
|
3677
|
+
filter: {
|
|
3678
|
+
agentIds: [trimmedAgentId],
|
|
3679
|
+
...cursor ? { cursor } : {},
|
|
3680
|
+
limit: 200
|
|
4058
3681
|
}
|
|
3682
|
+
});
|
|
3683
|
+
for (const run of page.items) {
|
|
3684
|
+
runIds.push(run.runId);
|
|
4059
3685
|
}
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
3686
|
+
cursor = page.nextCursor;
|
|
3687
|
+
} while (cursor);
|
|
3688
|
+
if (runIds.length > 0) {
|
|
3689
|
+
await store.runEvents.delete({ filter: { runIds } });
|
|
3690
|
+
}
|
|
3691
|
+
await store.runs.delete({ filter: { agentIds: [trimmedAgentId] } });
|
|
3692
|
+
await store.checkpoints.delete({ filter: { agentIds: [trimmedAgentId] } });
|
|
3693
|
+
await store.agents.delete({ filter: { agentIds: [trimmedAgentId] } });
|
|
3694
|
+
} finally {
|
|
3695
|
+
await store.dispose();
|
|
3696
|
+
}
|
|
3697
|
+
return true;
|
|
3698
|
+
}
|
|
3699
|
+
async function cleanWebIdeWorkspaceCache(taskId, workdir) {
|
|
3700
|
+
const trimmedTaskId = taskId.trim();
|
|
3701
|
+
const trimmedWorkdir = workdir.trim();
|
|
3702
|
+
if (!trimmedTaskId || !trimmedWorkdir) {
|
|
3703
|
+
return;
|
|
3704
|
+
}
|
|
3705
|
+
const agentId = loadWebIdeAgentId(trimmedWorkdir, trimmedTaskId);
|
|
3706
|
+
if (agentId) {
|
|
3707
|
+
try {
|
|
3708
|
+
const purged = await purgeCursorAgentStoreForAgent(
|
|
3709
|
+
trimmedWorkdir,
|
|
3710
|
+
agentId
|
|
3711
|
+
);
|
|
3712
|
+
if (purged) {
|
|
3713
|
+
console.log(
|
|
3714
|
+
`[apm] \u5DF2\u6E05\u7406 Cursor agent store agentId=${agentId} taskId=${trimmedTaskId}`
|
|
4070
3715
|
);
|
|
4071
3716
|
}
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
3717
|
+
} catch (err) {
|
|
3718
|
+
console.warn(
|
|
3719
|
+
`[apm] \u6E05\u7406 Cursor agent store \u5931\u8D25 agentId=${agentId}:`,
|
|
3720
|
+
err instanceof Error ? err.message : err
|
|
3721
|
+
);
|
|
3722
|
+
}
|
|
3723
|
+
} else {
|
|
3724
|
+
console.log(
|
|
3725
|
+
`[apm] \u65E0 WebIDE agentId\uFF0C\u8DF3\u8FC7 agent store \u6E05\u7406 taskId=${trimmedTaskId}`
|
|
3726
|
+
);
|
|
3727
|
+
}
|
|
3728
|
+
const dir = resolve7(trimmedWorkdir, ".apm", "webide", trimmedTaskId);
|
|
3729
|
+
if (existsSync8(dir)) {
|
|
3730
|
+
rmSync2(dir, { recursive: true, force: true });
|
|
3731
|
+
console.log(`[apm] \u5DF2\u6E05\u7406 WebIDE \u7F13\u5B58 ${dir}`);
|
|
3732
|
+
} else {
|
|
3733
|
+
console.log(`[apm] WebIDE \u7F13\u5B58\u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7 ${dir}`);
|
|
3734
|
+
}
|
|
3735
|
+
}
|
|
3736
|
+
|
|
3737
|
+
// src/commands/connect/webide-run-action.ts
|
|
3738
|
+
function startActionForInbound(action) {
|
|
3739
|
+
switch (action) {
|
|
3740
|
+
case "request-assessment":
|
|
3741
|
+
return "start-assessment";
|
|
3742
|
+
case "request-design":
|
|
3743
|
+
case "request-revise-design":
|
|
3744
|
+
return "start-design";
|
|
3745
|
+
case "request-write-plan":
|
|
3746
|
+
case "request-revise-plan":
|
|
3747
|
+
return "start-write-plan";
|
|
3748
|
+
case "request-develop":
|
|
3749
|
+
case "skip-plan":
|
|
3750
|
+
return "start-develop";
|
|
3751
|
+
case "request-start-project":
|
|
3752
|
+
return "start-project";
|
|
3753
|
+
case "request-execute-sql":
|
|
3754
|
+
return "start-execute-sql";
|
|
3755
|
+
case "request-bugfix":
|
|
3756
|
+
return "start-bugfix";
|
|
3757
|
+
case "request-organize-code":
|
|
3758
|
+
case "request-patch":
|
|
3759
|
+
return "start-organize-code";
|
|
3760
|
+
case "request-accept":
|
|
3761
|
+
return "start-accept";
|
|
3762
|
+
default:
|
|
3763
|
+
return null;
|
|
3764
|
+
}
|
|
3765
|
+
}
|
|
3766
|
+
function completeActionForInbound(action) {
|
|
3767
|
+
switch (action) {
|
|
3768
|
+
case "request-assessment":
|
|
3769
|
+
case "confirm-assumptions":
|
|
3770
|
+
return "complete-assessment";
|
|
3771
|
+
case "request-design":
|
|
3772
|
+
case "request-revise-design":
|
|
3773
|
+
return "complete-design";
|
|
3774
|
+
case "request-write-plan":
|
|
3775
|
+
case "request-revise-plan":
|
|
3776
|
+
return "complete-write-plan";
|
|
3777
|
+
case "request-develop":
|
|
3778
|
+
case "skip-plan":
|
|
3779
|
+
return "complete-develop";
|
|
3780
|
+
case "request-start-project":
|
|
3781
|
+
return "complete-project";
|
|
3782
|
+
case "request-execute-sql":
|
|
3783
|
+
return "complete-execute-sql";
|
|
3784
|
+
case "request-bugfix":
|
|
3785
|
+
return "complete-bugfix";
|
|
3786
|
+
case "request-organize-code":
|
|
3787
|
+
case "request-patch":
|
|
3788
|
+
return "complete-organize-code";
|
|
3789
|
+
case "request-accept":
|
|
3790
|
+
return "complete-accept";
|
|
3791
|
+
default:
|
|
3792
|
+
return null;
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
3795
|
+
async function webIdeRunAction(cfg2, taskId, action, params) {
|
|
3796
|
+
const api = createApmApiClient(cfg2);
|
|
3797
|
+
await api.cli.webideRunAction({ taskId, action, params });
|
|
3798
|
+
console.log(`[apm] webide runAction ok action=${action} taskId=${taskId}`);
|
|
3799
|
+
}
|
|
3800
|
+
|
|
3801
|
+
// src/commands/connect/webide-ask-question.ts
|
|
3802
|
+
function asString5(value) {
|
|
3803
|
+
return typeof value === "string" ? value.trim() : "";
|
|
3804
|
+
}
|
|
3805
|
+
function parseQuestions(args) {
|
|
3806
|
+
const title = asString5(args.title) || void 0;
|
|
3807
|
+
const raw = args.questions;
|
|
3808
|
+
if (!Array.isArray(raw) || raw.length === 0) {
|
|
3809
|
+
throw new Error("AskQuestion \u7F3A\u5C11 questions");
|
|
3810
|
+
}
|
|
3811
|
+
const questions = [];
|
|
3812
|
+
for (const item of raw) {
|
|
3813
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) continue;
|
|
3814
|
+
const row = item;
|
|
3815
|
+
const id = asString5(row.id);
|
|
3816
|
+
const prompt = asString5(row.prompt);
|
|
3817
|
+
const optionsRaw = row.options;
|
|
3818
|
+
if (!id || !prompt || !Array.isArray(optionsRaw)) continue;
|
|
3819
|
+
const options = [];
|
|
3820
|
+
for (const opt of optionsRaw) {
|
|
3821
|
+
if (!opt || typeof opt !== "object" || Array.isArray(opt)) continue;
|
|
3822
|
+
const o = opt;
|
|
3823
|
+
const oid = asString5(o.id);
|
|
3824
|
+
const label = asString5(o.label);
|
|
3825
|
+
if (oid && label) options.push({ id: oid, label });
|
|
3826
|
+
}
|
|
3827
|
+
if (options.length < 2) {
|
|
3828
|
+
throw new Error(`AskQuestion \u95EE\u9898 ${id} \u81F3\u5C11\u9700\u8981 2 \u4E2A\u9009\u9879`);
|
|
3829
|
+
}
|
|
3830
|
+
questions.push({ id, prompt, options });
|
|
3831
|
+
}
|
|
3832
|
+
if (questions.length === 0) {
|
|
3833
|
+
throw new Error("AskQuestion \u65E0\u6709\u6548\u95EE\u9898");
|
|
3834
|
+
}
|
|
3835
|
+
return { title, questions };
|
|
3836
|
+
}
|
|
3837
|
+
async function fetchAssumptionAnswersAndContinueAssessment(options) {
|
|
3838
|
+
const { cfg: cfg2, taskId, questionIds, title } = options;
|
|
3839
|
+
const api = createApmApiClient(cfg2);
|
|
3840
|
+
const list = await api.cli.webideListAssumptions({ taskId });
|
|
3841
|
+
const byId = new Map(list.assumptions.map((a) => [a.questionId, a]));
|
|
3842
|
+
const ids = questionIds && questionIds.length > 0 ? questionIds : list.assumptions.filter((a) => a.status === "RESOLVED").map((a) => a.questionId);
|
|
3843
|
+
if (ids.length === 0) {
|
|
3844
|
+
throw new Error("\u6CA1\u6709\u5DF2\u786E\u8BA4\u7684\u5047\u8BBE");
|
|
3845
|
+
}
|
|
3846
|
+
const answers = [];
|
|
3847
|
+
for (const id of ids) {
|
|
3848
|
+
const row = byId.get(id);
|
|
3849
|
+
if (!row || row.status !== "RESOLVED") {
|
|
3850
|
+
throw new Error(`\u5047\u8BBE\u5C1A\u672A\u5168\u90E8\u786E\u8BA4: ${id}`);
|
|
3851
|
+
}
|
|
3852
|
+
answers.push({
|
|
3853
|
+
questionId: id,
|
|
3854
|
+
prompt: row.prompt,
|
|
3855
|
+
selectedOptionId: row.selectedOptionId,
|
|
3856
|
+
customAnswer: row.customAnswer,
|
|
3857
|
+
resolution: row.resolution
|
|
3858
|
+
});
|
|
3859
|
+
}
|
|
3860
|
+
await webIdeRunAction(cfg2, taskId, "continue-assessment");
|
|
3861
|
+
console.log(
|
|
3862
|
+
`[apm] \u5047\u8BBE\u7ED3\u679C\u5DF2\u62C9\u53D6\u5E76\u7EE7\u7EED\u8BC4\u4F30 taskId=${taskId} count=${answers.length}`
|
|
3863
|
+
);
|
|
3864
|
+
return JSON.stringify({ title, answers }, null, 2);
|
|
3865
|
+
}
|
|
3866
|
+
function createWebIdeAskQuestionExecute(options) {
|
|
3867
|
+
const { cfg: cfg2, taskId, onAwaitingAssumptions } = options;
|
|
3868
|
+
return async (args) => {
|
|
3869
|
+
const parsed = parseQuestions(args);
|
|
3870
|
+
const api = createApmApiClient(cfg2);
|
|
3871
|
+
console.log(
|
|
3872
|
+
`[apm] AskQuestion \u521B\u5EFA\u5F85\u786E\u8BA4\u9879 taskId=${taskId} count=${parsed.questions.length}`
|
|
3873
|
+
);
|
|
3874
|
+
await api.cli.webideReplaceAssumptions({
|
|
3875
|
+
taskId,
|
|
3876
|
+
title: parsed.title,
|
|
3877
|
+
questions: parsed.questions
|
|
3878
|
+
});
|
|
3879
|
+
await api.cli.webideRunAction({
|
|
3880
|
+
taskId,
|
|
3881
|
+
action: "await-assumptions"
|
|
3882
|
+
});
|
|
3883
|
+
onAwaitingAssumptions?.();
|
|
3884
|
+
return [
|
|
3885
|
+
"\u5047\u8BBE\u5DF2\u63D0\u4EA4\uFF0C\u5DE5\u4F5C\u6D41\u8FDB\u5165\u300C\u5F85\u786E\u8BA4\u5047\u8BBE\u300D\u3002",
|
|
3886
|
+
"\u672C\u8F6E\u5FC5\u987B\u7ACB\u5373\u7ED3\u675F\uFF1A\u4E0D\u8981\u7EE7\u7EED\u8BC4\u4F30\uFF0C\u4E0D\u8981\u8C03\u7528 RecommendPlan\uFF0C\u4E0D\u8981\u518D\u6B21\u8C03\u7528 AskQuestion\u3002",
|
|
3887
|
+
"\u7528\u6237\u786E\u8BA4\u540E\uFF0C\u4E0B\u4E00\u8F6E\u4F1A\u5E26\u4E0A\u7B54\u6848\u5E76\u7EE7\u7EED\u8BC4\u4F30\u3002"
|
|
3888
|
+
].join("");
|
|
3889
|
+
};
|
|
3890
|
+
}
|
|
3891
|
+
|
|
3892
|
+
// src/commands/connect/pre-step-cache.ts
|
|
3893
|
+
function taskWorkdirKey(taskId, workdir) {
|
|
3894
|
+
return `${taskId}\0${workdir}`;
|
|
3895
|
+
}
|
|
3896
|
+
var lastBranchKey = null;
|
|
3897
|
+
function shouldRunBranch(taskId, workdir) {
|
|
3898
|
+
return lastBranchKey !== taskWorkdirKey(taskId, workdir);
|
|
3899
|
+
}
|
|
3900
|
+
function markBranchDone(taskId, workdir) {
|
|
3901
|
+
lastBranchKey = taskWorkdirKey(taskId, workdir);
|
|
3902
|
+
}
|
|
3903
|
+
|
|
3904
|
+
// src/commands/connect/webide-pull-requests.ts
|
|
3905
|
+
var PRE_AGENT_PR_ACTIONS = /* @__PURE__ */ new Set(["request-accept"]);
|
|
3906
|
+
var POST_AGENT_PR_ACTIONS = /* @__PURE__ */ new Set(["request-organize-code"]);
|
|
3907
|
+
function shouldEnsureWebIdePullRequests(action) {
|
|
3908
|
+
return PRE_AGENT_PR_ACTIONS.has(action);
|
|
3909
|
+
}
|
|
3910
|
+
function shouldEnsureWebIdePullRequestsAfterAgent(action) {
|
|
3911
|
+
return POST_AGENT_PR_ACTIONS.has(action);
|
|
3912
|
+
}
|
|
3913
|
+
function formatWebIdePullRequestSummary(result) {
|
|
3914
|
+
const lines = [];
|
|
3915
|
+
lines.push("## PR \u521B\u5EFA\u7ED3\u679C");
|
|
3916
|
+
lines.push(
|
|
3917
|
+
`\u5408\u8BA1\uFF1A\u6210\u529F ${result.ensured} \u6761\uFF0C\u65E0\u53D8\u66F4\u8DF3\u8FC7 ${result.skippedNoChanges} \u6761\uFF0C\u5176\u4ED6\u8DF3\u8FC7 ${result.skipped} \u6761\uFF0C\u5931\u8D25 ${result.failed} \u6761\u3002`
|
|
3918
|
+
);
|
|
3919
|
+
if (result.items.length === 0) {
|
|
3920
|
+
lines.push("\u672A\u5904\u7406\u4EFB\u4F55\u4ED3\u5E93\u3002");
|
|
3921
|
+
return lines.join("\n");
|
|
3922
|
+
}
|
|
3923
|
+
lines.push("");
|
|
3924
|
+
for (const item of result.items) {
|
|
3925
|
+
if (item.outcome === "created") {
|
|
3926
|
+
const num = item.number != null && item.number > 0 ? `#${item.number}` : "\uFF08\u65E0\u7F16\u53F7\uFF09";
|
|
3927
|
+
const branch = item.headBranch && item.baseBranch ? `${item.headBranch} \u2192 ${item.baseBranch}` : item.baseBranch ? `\u76EE\u6807\u5206\u652F ${item.baseBranch}` : "";
|
|
3928
|
+
const state = item.state ? `\u72B6\u6001 ${item.state}` : "";
|
|
3929
|
+
const url = item.url?.trim() ? item.url.trim() : "";
|
|
3930
|
+
lines.push(
|
|
3931
|
+
`- **${item.label}**\uFF1A\u5DF2\u521B\u5EFA ${num}${branch ? `\uFF0C${branch}` : ""}${state ? `\uFF0C${state}` : ""}${url ? `\uFF0C${url}` : ""}`
|
|
3932
|
+
);
|
|
3933
|
+
continue;
|
|
3934
|
+
}
|
|
3935
|
+
if (item.outcome === "skipped_no_changes") {
|
|
3936
|
+
lines.push(
|
|
3937
|
+
`- **${item.label}**\uFF1A\u76F8\u5BF9\u57FA\u7EBF ${item.baseBranch || "\uFF08\u672A\u77E5\uFF09"} \u65E0\u4EE3\u7801\u53D8\u66F4\uFF0C\u672A\u521B\u5EFA PR`
|
|
3938
|
+
);
|
|
3939
|
+
continue;
|
|
3940
|
+
}
|
|
3941
|
+
if (item.outcome === "skipped") {
|
|
3942
|
+
lines.push(`- **${item.label}**\uFF1A\u5DF2\u8DF3\u8FC7 \u2014 ${item.detail}`);
|
|
3943
|
+
continue;
|
|
3944
|
+
}
|
|
3945
|
+
lines.push(`- **${item.label}**\uFF1A\u521B\u5EFA\u5931\u8D25 \u2014 ${item.detail}`);
|
|
3946
|
+
}
|
|
3947
|
+
return lines.join("\n");
|
|
3948
|
+
}
|
|
3949
|
+
async function ensureWebIdePullRequests(cfg2, taskId, workdir) {
|
|
3950
|
+
const api = createApmApiClient(cfg2);
|
|
3951
|
+
const empty = () => ({
|
|
3952
|
+
ensured: 0,
|
|
3953
|
+
skipped: 0,
|
|
3954
|
+
skippedNoChanges: 0,
|
|
3955
|
+
failed: 0,
|
|
3956
|
+
items: [],
|
|
3957
|
+
tips: []
|
|
3958
|
+
});
|
|
3959
|
+
let repoRoots;
|
|
3960
|
+
try {
|
|
3961
|
+
const manifest = loadWorkspaceReposCache(workdir);
|
|
3962
|
+
repoRoots = resolveWorkspaceRepoAbsolutePaths(manifest);
|
|
3963
|
+
} catch (err) {
|
|
3964
|
+
const tip = `\u8BFB\u53D6\u4ED3\u5E93\u6E05\u5355\u5931\u8D25: ${err instanceof Error ? err.message : err}`;
|
|
3965
|
+
console.warn(`[apm] WebIDE PR\uFF1A${tip}`);
|
|
3966
|
+
return {
|
|
3967
|
+
...empty(),
|
|
3968
|
+
failed: 1,
|
|
3969
|
+
tips: [tip],
|
|
3970
|
+
items: [
|
|
3971
|
+
{
|
|
3972
|
+
label: "\u5DE5\u4F5C\u533A",
|
|
3973
|
+
outcome: "failed",
|
|
3974
|
+
detail: tip
|
|
3975
|
+
}
|
|
3976
|
+
]
|
|
3977
|
+
};
|
|
3978
|
+
}
|
|
3979
|
+
let projectBaseBranch = "";
|
|
3980
|
+
try {
|
|
3981
|
+
const baseline = await api.cli.projectBaseBranch({ taskId });
|
|
3982
|
+
projectBaseBranch = baseline.baseBranch?.trim() ?? "";
|
|
3983
|
+
} catch (err) {
|
|
3984
|
+
const tip = `\u8BFB\u53D6\u9879\u76EE\u57FA\u7EBF\u5206\u652F\u5931\u8D25: ${err instanceof Error ? err.message : err}`;
|
|
3985
|
+
console.warn(`[apm] WebIDE PR\uFF1A${tip}`);
|
|
3986
|
+
return {
|
|
3987
|
+
...empty(),
|
|
3988
|
+
failed: 1,
|
|
3989
|
+
tips: [tip],
|
|
3990
|
+
items: [{ label: "\u5DE5\u4F5C\u533A", outcome: "failed", detail: tip }]
|
|
3991
|
+
};
|
|
3992
|
+
}
|
|
3993
|
+
if (!projectBaseBranch) {
|
|
3994
|
+
const tip = "\u9879\u76EE\u672A\u914D\u7F6E\u57FA\u7840\u5206\u652F\uFF0C\u65E0\u6CD5\u521B\u5EFA PR";
|
|
3995
|
+
console.warn(`[apm] WebIDE PR\uFF1A${tip}`);
|
|
3996
|
+
return {
|
|
3997
|
+
...empty(),
|
|
3998
|
+
failed: 1,
|
|
3999
|
+
tips: [tip],
|
|
4000
|
+
items: [{ label: "\u5DE5\u4F5C\u533A", outcome: "failed", detail: tip }]
|
|
4001
|
+
};
|
|
4002
|
+
}
|
|
4003
|
+
const expectedHead = `feat/task-${taskId}`;
|
|
4004
|
+
console.log(
|
|
4005
|
+
`[apm] WebIDE PR\uFF1A\u51C6\u5907\u68C0\u67E5 ${repoRoots.length} \u4E2A\u4ED3\u5E93\uFF08\u671F\u671B\u7279\u6027\u5206\u652F ${expectedHead}\uFF0C\u9879\u76EE\u57FA\u7EBF ${projectBaseBranch}\uFF09`
|
|
4006
|
+
);
|
|
4007
|
+
let ensured = 0;
|
|
4008
|
+
let skipped = 0;
|
|
4009
|
+
let skippedNoChanges = 0;
|
|
4010
|
+
let failed = 0;
|
|
4011
|
+
const tips = [];
|
|
4012
|
+
const items = [];
|
|
4013
|
+
for (const gitRoot of repoRoots) {
|
|
4014
|
+
const label = formatRepoLabel(workdir, gitRoot);
|
|
4015
|
+
try {
|
|
4016
|
+
const current = await getCurrentBranch(gitRoot);
|
|
4017
|
+
if (!isFeatureBranchName(current)) {
|
|
4018
|
+
const tip = `${label}\uFF1A\u5F53\u524D\u5206\u652F ${current} \u4E0D\u662F feat/task-*\uFF0C\u4E0D\u7B26\u5408\u521B\u5EFA PR \u6761\u4EF6`;
|
|
4019
|
+
console.warn(`[apm] WebIDE PR\uFF1A${tip}`);
|
|
4020
|
+
tips.push(tip);
|
|
4021
|
+
skipped += 1;
|
|
4022
|
+
items.push({
|
|
4023
|
+
label,
|
|
4024
|
+
outcome: "skipped",
|
|
4025
|
+
headBranch: current,
|
|
4026
|
+
detail: tip
|
|
4027
|
+
});
|
|
4028
|
+
continue;
|
|
4029
|
+
}
|
|
4030
|
+
if (current !== expectedHead) {
|
|
4031
|
+
const tip = `${label}\uFF1A\u5F53\u524D\u4E3A ${current}\uFF0C\u671F\u671B ${expectedHead}\uFF0C\u5DF2\u8DF3\u8FC7`;
|
|
4032
|
+
console.warn(`[apm] WebIDE PR\uFF1A${tip}`);
|
|
4033
|
+
tips.push(tip);
|
|
4034
|
+
skipped += 1;
|
|
4035
|
+
items.push({
|
|
4036
|
+
label,
|
|
4037
|
+
outcome: "skipped",
|
|
4038
|
+
headBranch: current,
|
|
4039
|
+
detail: tip
|
|
4040
|
+
});
|
|
4041
|
+
continue;
|
|
4087
4042
|
}
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4043
|
+
const headBranch = current;
|
|
4044
|
+
const originUrl = await tryReadHttpsGitOriginUrl(gitRoot) ?? await tryReadGitOriginUrl(gitRoot);
|
|
4045
|
+
if (!originUrl) {
|
|
4046
|
+
const tip = `${label}\uFF1A\u65E0 remote.origin.url`;
|
|
4047
|
+
console.warn(`[apm] WebIDE PR\uFF1A\u8DF3\u8FC7 ${tip}`);
|
|
4048
|
+
tips.push(tip);
|
|
4049
|
+
skipped += 1;
|
|
4050
|
+
items.push({
|
|
4051
|
+
label,
|
|
4052
|
+
outcome: "skipped",
|
|
4053
|
+
headBranch,
|
|
4054
|
+
detail: tip
|
|
4055
|
+
});
|
|
4056
|
+
continue;
|
|
4097
4057
|
}
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
);
|
|
4058
|
+
const baseBranch = projectBaseBranch;
|
|
4059
|
+
const hasChanges = await branchHasChangesAgainstBase(
|
|
4060
|
+
gitRoot,
|
|
4061
|
+
baseBranch,
|
|
4062
|
+
headBranch
|
|
4063
|
+
);
|
|
4064
|
+
if (!hasChanges) {
|
|
4065
|
+
const tip = `${label}\uFF1A\u76F8\u5BF9\u57FA\u7EBF ${baseBranch} \u65E0\u4EE3\u7801\u53D8\u66F4\uFF0C\u65E0\u9700\u521B\u5EFA PR`;
|
|
4066
|
+
console.log(`[apm] WebIDE PR\uFF1A${tip}`);
|
|
4067
|
+
tips.push(tip);
|
|
4068
|
+
skippedNoChanges += 1;
|
|
4069
|
+
items.push({
|
|
4070
|
+
label,
|
|
4071
|
+
outcome: "skipped_no_changes",
|
|
4072
|
+
headBranch,
|
|
4073
|
+
baseBranch,
|
|
4074
|
+
detail: tip
|
|
4075
|
+
});
|
|
4076
|
+
continue;
|
|
4118
4077
|
}
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4078
|
+
const pr = await api.cli.createWebIdeDraftPullRequest({
|
|
4079
|
+
taskId,
|
|
4080
|
+
originUrl,
|
|
4081
|
+
headBranch,
|
|
4082
|
+
baseBranch
|
|
4083
|
+
});
|
|
4084
|
+
ensured += 1;
|
|
4085
|
+
items.push({
|
|
4086
|
+
label,
|
|
4087
|
+
outcome: "created",
|
|
4088
|
+
headBranch,
|
|
4089
|
+
baseBranch,
|
|
4090
|
+
number: pr.number,
|
|
4091
|
+
state: pr.state,
|
|
4092
|
+
url: pr.url,
|
|
4093
|
+
detail: `\u5DF2\u521B\u5EFA #${pr.number} ${headBranch} \u2192 ${baseBranch}`
|
|
4094
|
+
});
|
|
4095
|
+
console.log(
|
|
4096
|
+
`[apm] WebIDE PR\uFF1A${label} \u2192 #${pr.number} ${pr.state} ${headBranch} \u2192 ${baseBranch} ${pr.url || ""}`
|
|
4132
4097
|
);
|
|
4098
|
+
} catch (err) {
|
|
4099
|
+
failed += 1;
|
|
4100
|
+
const tip = `${label}\uFF1A${err instanceof Error ? err.message : err}`;
|
|
4101
|
+
console.warn(`[apm] WebIDE PR\uFF1A\u5931\u8D25 ${tip}`);
|
|
4102
|
+
tips.push(tip);
|
|
4103
|
+
items.push({
|
|
4104
|
+
label,
|
|
4105
|
+
outcome: "failed",
|
|
4106
|
+
detail: tip
|
|
4107
|
+
});
|
|
4133
4108
|
}
|
|
4134
|
-
syncLocalTaskStatus(workdir, taskId, msg, "FAILED");
|
|
4135
4109
|
}
|
|
4110
|
+
return { ensured, skipped, skippedNoChanges, failed, items, tips };
|
|
4136
4111
|
}
|
|
4137
4112
|
|
|
4138
|
-
// src/commands/
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
const
|
|
4145
|
-
|
|
4113
|
+
// src/commands/sync-webide-attachments.ts
|
|
4114
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
|
|
4115
|
+
import { join as join7 } from "path";
|
|
4116
|
+
var MANIFEST_FILE2 = ".sync-manifest.json";
|
|
4117
|
+
async function downloadAttachment(cfg2, attachmentId) {
|
|
4118
|
+
const base = cfg2.baseUrl.trim().replace(/\/+$/, "");
|
|
4119
|
+
const url = `${base}/api/v1/tasks/attachments/file?${new URLSearchParams({
|
|
4120
|
+
attachmentId
|
|
4121
|
+
})}`;
|
|
4122
|
+
const res = await fetch(url);
|
|
4123
|
+
if (!res.ok) {
|
|
4124
|
+
throw new Error(
|
|
4125
|
+
`[apm] \u4E0B\u8F7D\u9644\u4EF6\u5931\u8D25 (${res.status}): attachmentId=${attachmentId}`
|
|
4126
|
+
);
|
|
4127
|
+
}
|
|
4128
|
+
return Buffer.from(await res.arrayBuffer());
|
|
4146
4129
|
}
|
|
4147
|
-
|
|
4148
|
-
const
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
`[apm] webide-start-project target=${msg.target} restart=${msg.restart} taskId=${taskId} messageId=${messageId}`
|
|
4153
|
-
);
|
|
4154
|
-
await updateStatus2(cfg, messageId, "TYPING");
|
|
4155
|
-
const eventSession = new EventSession(msg.content);
|
|
4156
|
-
const logSyncRef = {
|
|
4157
|
-
current: createThrottledWebIdeMessageLogSync(
|
|
4158
|
-
cfg,
|
|
4159
|
-
{ taskId, messageId, agentId: "webide-start-project" },
|
|
4160
|
-
(err) => {
|
|
4161
|
-
console.warn(
|
|
4162
|
-
"[apm] WebIDE \u542F\u52A8\u9879\u76EE\u65E5\u5FD7\u540C\u6B65\u5931\u8D25:",
|
|
4163
|
-
err instanceof Error ? err.message : err
|
|
4164
|
-
);
|
|
4165
|
-
}
|
|
4166
|
-
)
|
|
4167
|
-
};
|
|
4130
|
+
function loadManifest2(dir) {
|
|
4131
|
+
const path3 = join7(dir, MANIFEST_FILE2);
|
|
4132
|
+
if (!existsSync9(path3)) {
|
|
4133
|
+
return { version: 1, attachments: {} };
|
|
4134
|
+
}
|
|
4168
4135
|
try {
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
const api = createApmApiClient(cfg);
|
|
4172
|
-
const outcome = await runCursorAgent(
|
|
4173
|
-
cfg,
|
|
4174
|
-
{
|
|
4175
|
-
messageId,
|
|
4176
|
-
prompt: msg.content,
|
|
4177
|
-
model: msg.model,
|
|
4178
|
-
apiKey: msg.apiKey,
|
|
4179
|
-
workdir,
|
|
4180
|
-
user: msg.user || "webide"
|
|
4181
|
-
},
|
|
4182
|
-
{
|
|
4183
|
-
signal,
|
|
4184
|
-
forceSend: true,
|
|
4185
|
-
eventSession,
|
|
4186
|
-
enableAppendMessage: false,
|
|
4187
|
-
enableAskQuestion: false,
|
|
4188
|
-
enableWebIdePlanTools: false,
|
|
4189
|
-
enablePtySessionMcp: true,
|
|
4190
|
-
enableSandbox: false,
|
|
4191
|
-
taskId,
|
|
4192
|
-
createRemoteLogSync: (agentId) => {
|
|
4193
|
-
logSyncRef.current = createThrottledWebIdeMessageLogSync(
|
|
4194
|
-
cfg,
|
|
4195
|
-
{ taskId, messageId, agentId },
|
|
4196
|
-
(err) => {
|
|
4197
|
-
console.warn(
|
|
4198
|
-
"[apm] WebIDE \u542F\u52A8\u9879\u76EE\u65E5\u5FD7\u540C\u6B65\u5931\u8D25:",
|
|
4199
|
-
err instanceof Error ? err.message : err
|
|
4200
|
-
);
|
|
4201
|
-
}
|
|
4202
|
-
);
|
|
4203
|
-
return logSyncRef.current;
|
|
4204
|
-
},
|
|
4205
|
-
onRunStarted: async ({ agentId, runId }) => {
|
|
4206
|
-
eventSession.addCliStep(
|
|
4207
|
-
"\u542F\u52A8\u72EC\u7ACB Agent",
|
|
4208
|
-
`agentId=${agentId} runId=${runId}`,
|
|
4209
|
-
"ok"
|
|
4210
|
-
);
|
|
4211
|
-
logSyncRef.current?.schedule(eventSession);
|
|
4212
|
-
await logSyncRef.current?.markRun(runId, "running");
|
|
4213
|
-
await logSyncRef.current?.flush(eventSession);
|
|
4214
|
-
}
|
|
4215
|
-
}
|
|
4216
|
-
);
|
|
4217
|
-
const tokenUsage = outcome.usage != null ? {
|
|
4218
|
-
modelId: outcome.modelId ?? (msg.model?.trim() || void 0),
|
|
4219
|
-
inputTokens: outcome.usage.inputTokens,
|
|
4220
|
-
outputTokens: outcome.usage.outputTokens,
|
|
4221
|
-
cacheReadTokens: outcome.usage.cacheReadTokens,
|
|
4222
|
-
cacheWriteTokens: outcome.usage.cacheWriteTokens,
|
|
4223
|
-
totalTokens: outcome.usage.totalTokens,
|
|
4224
|
-
...outcome.usage.reasoningTokens != null ? { reasoningTokens: outcome.usage.reasoningTokens } : {}
|
|
4225
|
-
} : void 0;
|
|
4226
|
-
if (outcome.status === "error") {
|
|
4227
|
-
const detail = formatCursorRunFailure(outcome.runId, {
|
|
4228
|
-
resultText: outcome.result
|
|
4229
|
-
});
|
|
4230
|
-
await logSyncRef.current?.markRun(
|
|
4231
|
-
outcome.runId,
|
|
4232
|
-
"error",
|
|
4233
|
-
detail,
|
|
4234
|
-
tokenUsage
|
|
4235
|
-
);
|
|
4236
|
-
await setError2(cfg, messageId, detail);
|
|
4237
|
-
return;
|
|
4238
|
-
}
|
|
4239
|
-
if (outcome.status === "cancelled" || signal.aborted) {
|
|
4240
|
-
await logSyncRef.current?.markRun(
|
|
4241
|
-
outcome.runId,
|
|
4242
|
-
"cancelled",
|
|
4243
|
-
"\u5DF2\u53D6\u6D88",
|
|
4244
|
-
tokenUsage
|
|
4245
|
-
);
|
|
4246
|
-
await updateStatus2(cfg, messageId, "CANCELLED");
|
|
4247
|
-
return;
|
|
4248
|
-
}
|
|
4249
|
-
const fallback = resolveMessageReplyFallback({
|
|
4250
|
-
assistantText: outcome.assistantText,
|
|
4251
|
-
result: outcome.result
|
|
4252
|
-
});
|
|
4253
|
-
await api.cli.webideEnsureMessageContent({
|
|
4254
|
-
id: messageId,
|
|
4255
|
-
content: fallback
|
|
4256
|
-
});
|
|
4257
|
-
await logSyncRef.current?.markRun(
|
|
4258
|
-
outcome.runId,
|
|
4259
|
-
"finished",
|
|
4260
|
-
null,
|
|
4261
|
-
tokenUsage
|
|
4262
|
-
);
|
|
4263
|
-
await updateStatus2(cfg, messageId, "SUCCESS");
|
|
4264
|
-
console.log(
|
|
4265
|
-
`[apm] webide-start-project \u5B8C\u6210 target=${msg.target} messageId=${messageId} agentId=${outcome.agentId}`
|
|
4136
|
+
const parsed = JSON.parse(
|
|
4137
|
+
readFileSync8(path3, "utf8")
|
|
4266
4138
|
);
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
console.error(`[apm] webide-start-project \u5931\u8D25: ${detail}`);
|
|
4270
|
-
try {
|
|
4271
|
-
await setError2(cfg, messageId, detail);
|
|
4272
|
-
} catch (statusErr) {
|
|
4273
|
-
console.error(
|
|
4274
|
-
"[apm] \u5199\u5165\u542F\u52A8\u9879\u76EE FAILED \u5931\u8D25:",
|
|
4275
|
-
statusErr instanceof Error ? statusErr.message : statusErr
|
|
4276
|
-
);
|
|
4139
|
+
if (parsed?.version === 1 && parsed.attachments && typeof parsed.attachments === "object") {
|
|
4140
|
+
return parsed;
|
|
4277
4141
|
}
|
|
4142
|
+
} catch {
|
|
4278
4143
|
}
|
|
4144
|
+
return { version: 1, attachments: {} };
|
|
4279
4145
|
}
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4146
|
+
function saveManifest2(dir, manifest) {
|
|
4147
|
+
writeFileSync6(
|
|
4148
|
+
join7(dir, MANIFEST_FILE2),
|
|
4149
|
+
`${JSON.stringify(manifest, null, 2)}
|
|
4150
|
+
`,
|
|
4151
|
+
"utf8"
|
|
4152
|
+
);
|
|
4286
4153
|
}
|
|
4287
|
-
function
|
|
4288
|
-
|
|
4289
|
-
|
|
4154
|
+
function isAttachmentUpToDate(entry, item, dest) {
|
|
4155
|
+
if (!entry || !existsSync9(dest)) return false;
|
|
4156
|
+
if (entry.name !== item.name) return false;
|
|
4157
|
+
const createdAt = item.createdAt ?? "";
|
|
4158
|
+
return entry.createdAt === createdAt;
|
|
4290
4159
|
}
|
|
4291
|
-
function
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4160
|
+
async function syncAttachmentsToDirectory(cfg2, attachments, dir, logLabel) {
|
|
4161
|
+
await ensureDirExists(dir);
|
|
4162
|
+
if (attachments.length === 0) {
|
|
4163
|
+
saveManifest2(dir, { version: 1, attachments: {} });
|
|
4164
|
+
return [];
|
|
4165
|
+
}
|
|
4166
|
+
const manifest = loadManifest2(dir);
|
|
4167
|
+
const nextManifest = { version: 1, attachments: {} };
|
|
4168
|
+
const names = [];
|
|
4169
|
+
for (const item of attachments) {
|
|
4170
|
+
const dest = join7(dir, item.name);
|
|
4171
|
+
const entry = manifest.attachments[item.id];
|
|
4172
|
+
const createdAt = item.createdAt ?? "";
|
|
4173
|
+
names.push(item.name);
|
|
4174
|
+
if (isAttachmentUpToDate(entry, item, dest)) {
|
|
4175
|
+
nextManifest.attachments[item.id] = entry;
|
|
4176
|
+
console.log(`[apm] \u9644\u4EF6\u65E0\u53D8\u5316\uFF0C\u5DF2\u8DF3\u8FC7: ${logLabel}/${item.name}`);
|
|
4177
|
+
continue;
|
|
4178
|
+
}
|
|
4179
|
+
const buffer = await downloadAttachment(cfg2, item.id);
|
|
4180
|
+
writeFileSync6(dest, buffer);
|
|
4181
|
+
nextManifest.attachments[item.id] = {
|
|
4182
|
+
name: item.name,
|
|
4183
|
+
createdAt
|
|
4184
|
+
};
|
|
4185
|
+
console.log(`[apm] \u5DF2\u4E0B\u8F7D\u9644\u4EF6: ${logLabel}/${item.name}`);
|
|
4186
|
+
}
|
|
4187
|
+
saveManifest2(dir, nextManifest);
|
|
4188
|
+
return names;
|
|
4295
4189
|
}
|
|
4296
|
-
function
|
|
4297
|
-
const dir =
|
|
4190
|
+
async function syncWebIdeAttachments(cfg2, taskId, workdir, attachments) {
|
|
4191
|
+
const dir = webideAttachmentsDir(taskId, workdir);
|
|
4192
|
+
return syncAttachmentsToDirectory(
|
|
4193
|
+
cfg2,
|
|
4194
|
+
attachments,
|
|
4195
|
+
dir,
|
|
4196
|
+
`.apm/webide/${taskId}/attachments`
|
|
4197
|
+
);
|
|
4198
|
+
}
|
|
4199
|
+
|
|
4200
|
+
// src/commands/sync-webide-design.ts
|
|
4201
|
+
import { createHash as createHash3 } from "crypto";
|
|
4202
|
+
import { existsSync as existsSync10, readdirSync as readdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
|
|
4203
|
+
import { join as join8 } from "path";
|
|
4204
|
+
var MANIFEST_FILE3 = ".sync-manifest.json";
|
|
4205
|
+
function hashBuffer(buf) {
|
|
4206
|
+
return createHash3("sha256").update(buf).digest("hex");
|
|
4207
|
+
}
|
|
4208
|
+
function loadManifest3(dir) {
|
|
4209
|
+
const path3 = join8(dir, MANIFEST_FILE3);
|
|
4210
|
+
if (!existsSync10(path3)) {
|
|
4211
|
+
return { version: 1, files: {} };
|
|
4212
|
+
}
|
|
4298
4213
|
try {
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4214
|
+
const parsed = JSON.parse(readFileSync9(path3, "utf8"));
|
|
4215
|
+
if (parsed?.version === 1 && parsed.files && typeof parsed.files === "object") {
|
|
4216
|
+
return parsed;
|
|
4217
|
+
}
|
|
4218
|
+
} catch {
|
|
4219
|
+
}
|
|
4220
|
+
return { version: 1, files: {} };
|
|
4221
|
+
}
|
|
4222
|
+
function saveManifest3(dir, manifest) {
|
|
4223
|
+
writeFileSync7(
|
|
4224
|
+
join8(dir, MANIFEST_FILE3),
|
|
4225
|
+
`${JSON.stringify(manifest, null, 2)}
|
|
4226
|
+
`,
|
|
4227
|
+
"utf8"
|
|
4228
|
+
);
|
|
4229
|
+
}
|
|
4230
|
+
async function downloadDesignFile(cfg2, taskId, name) {
|
|
4231
|
+
const base = cfg2.baseUrl.trim().replace(/\/+$/, "");
|
|
4232
|
+
const apiKey = resolveApiKey(cfg2);
|
|
4233
|
+
const params = new URLSearchParams({ taskId, name });
|
|
4234
|
+
if (apiKey) params.set("apiKey", apiKey);
|
|
4235
|
+
const url = `${base}/api/v1/cli/webide/design-artifacts/file?${params}`;
|
|
4236
|
+
const res = await fetch(url);
|
|
4237
|
+
if (!res.ok) {
|
|
4238
|
+
throw new Error(
|
|
4239
|
+
`[apm] \u4E0B\u8F7D\u8BBE\u8BA1\u6587\u4EF6\u5931\u8D25 (${res.status}): taskId=${taskId} name=${name}`
|
|
4304
4240
|
);
|
|
4305
4241
|
}
|
|
4242
|
+
return Buffer.from(await res.arrayBuffer());
|
|
4306
4243
|
}
|
|
4307
|
-
async function
|
|
4308
|
-
const
|
|
4309
|
-
|
|
4310
|
-
|
|
4244
|
+
async function syncWebIdeDesign(cfg2, taskId, workdir, artifacts) {
|
|
4245
|
+
const dir = webideDesignDir(taskId, workdir);
|
|
4246
|
+
await ensureDirExists(dir);
|
|
4247
|
+
const manifest = loadManifest3(dir);
|
|
4248
|
+
const synced = [];
|
|
4249
|
+
for (const art of artifacts) {
|
|
4250
|
+
const name = art.name?.trim();
|
|
4251
|
+
if (!name || !name.toLowerCase().endsWith(".html")) continue;
|
|
4252
|
+
const localPath = join8(dir, name);
|
|
4253
|
+
const entry = manifest.files[name];
|
|
4254
|
+
if (entry?.contentHash === art.contentHash && existsSync10(localPath)) {
|
|
4255
|
+
continue;
|
|
4256
|
+
}
|
|
4257
|
+
const buf = await downloadDesignFile(cfg2, taskId, name);
|
|
4258
|
+
writeFileSync7(localPath, buf);
|
|
4259
|
+
manifest.files[name] = { contentHash: hashBuffer(buf) };
|
|
4260
|
+
synced.push(name);
|
|
4311
4261
|
}
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4262
|
+
saveManifest3(dir, manifest);
|
|
4263
|
+
return synced;
|
|
4264
|
+
}
|
|
4265
|
+
function listLocalDesignHtmlFiles(taskId, workdir) {
|
|
4266
|
+
const dir = webideDesignDir(taskId, workdir);
|
|
4267
|
+
if (!existsSync10(dir)) return [];
|
|
4268
|
+
const out = [];
|
|
4269
|
+
for (const name of readdirSync4(dir)) {
|
|
4270
|
+
if (!name.toLowerCase().endsWith(".html")) continue;
|
|
4271
|
+
const absPath = join8(dir, name);
|
|
4272
|
+
const buf = readFileSync9(absPath);
|
|
4273
|
+
if (buf.length === 0) continue;
|
|
4274
|
+
out.push({ name, absPath, contentHash: hashBuffer(buf) });
|
|
4315
4275
|
}
|
|
4316
|
-
|
|
4317
|
-
|
|
4276
|
+
return out;
|
|
4277
|
+
}
|
|
4278
|
+
|
|
4279
|
+
// src/opc-shared-types.ts
|
|
4280
|
+
function webIdeDesignObjectKey(taskId, fileName) {
|
|
4281
|
+
const safe = fileName.replace(/[/\\:*?"<>|]/g, "_").trim() || "index.html";
|
|
4282
|
+
return `tasks/${taskId.trim()}/design/${safe}`;
|
|
4283
|
+
}
|
|
4284
|
+
|
|
4285
|
+
// src/commands/connect/upload-webide-design.ts
|
|
4286
|
+
async function uploadWebIdeDesignArtifacts(cfg2, taskId, workdir) {
|
|
4287
|
+
const locals = listLocalDesignHtmlFiles(taskId, workdir);
|
|
4288
|
+
if (locals.length === 0) {
|
|
4289
|
+
throw new Error(
|
|
4290
|
+
`\u8BBE\u8BA1\u76EE\u5F55\u65E0 HTML \u4EA7\u7269\uFF1A.apm/webide/${taskId}/design/\uFF08\u8BF7\u5199\u5165\u81F3\u5C11\u4E00\u4EFD .html\uFF09`
|
|
4291
|
+
);
|
|
4292
|
+
}
|
|
4293
|
+
const api = createApmApiClient(cfg2);
|
|
4294
|
+
const remote = await api.cli.webideListDesignArtifacts({ taskId }) ?? [];
|
|
4295
|
+
const remoteByName = new Map(
|
|
4296
|
+
remote.map((r) => [r.name, r])
|
|
4297
|
+
);
|
|
4298
|
+
const { client, bucket } = await createApmLogMinioClient(cfg2);
|
|
4318
4299
|
await client.ensureBucket(bucket);
|
|
4319
|
-
|
|
4320
|
-
|
|
4300
|
+
let uploaded = 0;
|
|
4301
|
+
let skipped = 0;
|
|
4302
|
+
const artifacts = [];
|
|
4303
|
+
for (const file of locals) {
|
|
4304
|
+
const objectKey = webIdeDesignObjectKey(taskId, file.name);
|
|
4305
|
+
const prev = remoteByName.get(file.name);
|
|
4306
|
+
if (prev?.contentHash === file.contentHash) {
|
|
4307
|
+
skipped += 1;
|
|
4308
|
+
artifacts.push({
|
|
4309
|
+
name: file.name,
|
|
4310
|
+
objectKey: prev.objectKey || objectKey,
|
|
4311
|
+
contentHash: file.contentHash
|
|
4312
|
+
});
|
|
4313
|
+
continue;
|
|
4314
|
+
}
|
|
4315
|
+
await client.fPutObject(bucket, objectKey, file.absPath, {
|
|
4316
|
+
"Content-Type": "text/html; charset=utf-8"
|
|
4317
|
+
});
|
|
4318
|
+
uploaded += 1;
|
|
4319
|
+
artifacts.push({
|
|
4320
|
+
name: file.name,
|
|
4321
|
+
objectKey,
|
|
4322
|
+
contentHash: file.contentHash
|
|
4323
|
+
});
|
|
4324
|
+
}
|
|
4325
|
+
await api.cli.webideRegisterDesignArtifacts({
|
|
4326
|
+
taskId,
|
|
4327
|
+
artifacts
|
|
4321
4328
|
});
|
|
4322
|
-
|
|
4323
|
-
const base = publicEndpoint?.replace(/\/$/, "") ?? "";
|
|
4324
|
-
const artifactUrl = base ? `${base}/${bucket}/${objectKey}` : null;
|
|
4325
|
-
return { artifactPath: objectKey, artifactUrl };
|
|
4329
|
+
return { uploaded, skipped, artifacts };
|
|
4326
4330
|
}
|
|
4327
4331
|
|
|
4328
|
-
// src/commands/connect/
|
|
4329
|
-
|
|
4330
|
-
const
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4332
|
+
// src/commands/connect/parse-webide-prompt-payload.ts
|
|
4333
|
+
function parseWebIdePromptPayload(content) {
|
|
4334
|
+
const marker = "\n\npayload=";
|
|
4335
|
+
const index = content.lastIndexOf(marker);
|
|
4336
|
+
if (index < 0) return null;
|
|
4337
|
+
const jsonText = content.slice(index + marker.length).trim();
|
|
4338
|
+
if (!jsonText) return null;
|
|
4339
|
+
try {
|
|
4340
|
+
const parsed = JSON.parse(jsonText);
|
|
4341
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
4342
|
+
return parsed;
|
|
4343
|
+
}
|
|
4344
|
+
} catch {
|
|
4345
|
+
return null;
|
|
4346
|
+
}
|
|
4347
|
+
return null;
|
|
4348
|
+
}
|
|
4349
|
+
|
|
4350
|
+
// src/commands/connect/core/worker/handlers/webide-message.ts
|
|
4351
|
+
var WEBIDE_CODE_CHANGE_ACTIONS = /* @__PURE__ */ new Set([
|
|
4352
|
+
"skip-plan",
|
|
4353
|
+
"request-develop",
|
|
4354
|
+
"request-bugfix",
|
|
4355
|
+
"request-organize-code",
|
|
4356
|
+
"request-patch"
|
|
4357
|
+
]);
|
|
4358
|
+
function shouldCommitAfterWebIdeMessage(action) {
|
|
4359
|
+
return WEBIDE_CODE_CHANGE_ACTIONS.has(action);
|
|
4360
|
+
}
|
|
4361
|
+
function isStartLocalServicesAction(_action) {
|
|
4362
|
+
return false;
|
|
4363
|
+
}
|
|
4364
|
+
function isDesignAction(action) {
|
|
4365
|
+
return action === "request-design" || action === "request-revise-design";
|
|
4366
|
+
}
|
|
4367
|
+
function isExecuteSqlAction(action) {
|
|
4368
|
+
return action === "request-execute-sql";
|
|
4369
|
+
}
|
|
4370
|
+
function syncLocalTaskStatus(workdir, taskId, msg2, status) {
|
|
4371
|
+
syncWebIdeTaskState(workdir, taskId, {
|
|
4372
|
+
messageId: msg2.messageId,
|
|
4373
|
+
action: msg2.action,
|
|
4374
|
+
status
|
|
4338
4375
|
});
|
|
4339
4376
|
}
|
|
4340
|
-
async function
|
|
4341
|
-
const
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4377
|
+
async function updateStatus2(cfg2, messageId, status) {
|
|
4378
|
+
const api = createApmApiClient(cfg2);
|
|
4379
|
+
await api.cli.webideUpdateMessageStatus({ id: messageId, status });
|
|
4380
|
+
}
|
|
4381
|
+
async function setError2(cfg2, messageId, error) {
|
|
4382
|
+
const api = createApmApiClient(cfg2);
|
|
4383
|
+
await api.cli.webideSetMessageError({ id: messageId, error });
|
|
4384
|
+
}
|
|
4385
|
+
async function appendContent(cfg2, messageId, content) {
|
|
4386
|
+
const api = createApmApiClient(cfg2);
|
|
4387
|
+
await api.cli.webideAppendMessageContent({ id: messageId, content });
|
|
4388
|
+
}
|
|
4389
|
+
async function handleWebIdeInboundMessage(cfg2, msg2, signal) {
|
|
4390
|
+
const workdir = requireRemoteWorkdir(msg2.workdir);
|
|
4391
|
+
const messageId = msg2.messageId;
|
|
4392
|
+
const taskId = msg2.taskId;
|
|
4393
|
+
const branchCacheKey = taskId;
|
|
4345
4394
|
console.log(
|
|
4346
|
-
`[apm] webide-
|
|
4395
|
+
`[apm] webide-message action=${msg2.action} taskId=${taskId} messageId=${messageId}`
|
|
4347
4396
|
);
|
|
4348
|
-
await
|
|
4349
|
-
|
|
4397
|
+
await updateStatus2(cfg2, messageId, "TYPING");
|
|
4398
|
+
syncLocalTaskStatus(workdir, taskId, msg2, "TYPING");
|
|
4399
|
+
const startAction = startActionForInbound(msg2.action);
|
|
4400
|
+
if (startAction) {
|
|
4401
|
+
await webIdeRunAction(cfg2, taskId, startAction);
|
|
4402
|
+
}
|
|
4403
|
+
const eventSession = new EventSession(msg2.content);
|
|
4404
|
+
const prepAgentId = loadWebIdeAgentId(workdir, taskId) ?? "webide-cli-prep";
|
|
4350
4405
|
const logSyncRef = {
|
|
4351
4406
|
current: createThrottledWebIdeMessageLogSync(
|
|
4352
|
-
|
|
4353
|
-
{
|
|
4354
|
-
taskId: `package:${packageId}`,
|
|
4355
|
-
messageId,
|
|
4356
|
-
agentId: "webide-package"
|
|
4357
|
-
},
|
|
4407
|
+
cfg2,
|
|
4408
|
+
{ taskId, messageId, agentId: prepAgentId },
|
|
4358
4409
|
(err) => {
|
|
4359
4410
|
console.warn(
|
|
4360
|
-
"[apm] WebIDE \
|
|
4411
|
+
"[apm] WebIDE \u65E5\u5FD7\u540C\u6B65\u5931\u8D25:",
|
|
4361
4412
|
err instanceof Error ? err.message : err
|
|
4362
4413
|
);
|
|
4363
|
-
},
|
|
4364
|
-
{
|
|
4365
|
-
objectPrefixOverride: `events/webide-package/${packageId}/`
|
|
4366
4414
|
}
|
|
4367
4415
|
)
|
|
4368
4416
|
};
|
|
@@ -4389,6 +4437,13 @@ async function handleWebIdePackage(cfg, msg, signal) {
|
|
|
4389
4437
|
};
|
|
4390
4438
|
try {
|
|
4391
4439
|
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
4440
|
+
let assumptionAnswersText;
|
|
4441
|
+
if (msg2.action === "confirm-assumptions") {
|
|
4442
|
+
assumptionAnswersText = await fetchAssumptionAnswersAndContinueAssessment(
|
|
4443
|
+
{ cfg: cfg2, taskId }
|
|
4444
|
+
);
|
|
4445
|
+
}
|
|
4446
|
+
let pausedForAssumptions = false;
|
|
4392
4447
|
await runPrepStep(
|
|
4393
4448
|
"\u521D\u59CB\u5316\u5DE5\u4F5C\u533A",
|
|
4394
4449
|
async () => {
|
|
@@ -4422,91 +4477,209 @@ async function handleWebIdePackage(cfg, msg, signal) {
|
|
|
4422
4477
|
async () => {
|
|
4423
4478
|
const workspaceRepos = resolveWorkspaceRepos(workdir);
|
|
4424
4479
|
try {
|
|
4425
|
-
await enrichWorkspaceReposRemoteUrls(workspaceRepos);
|
|
4480
|
+
await enrichWorkspaceReposRemoteUrls(workspaceRepos);
|
|
4481
|
+
} catch (err) {
|
|
4482
|
+
console.warn(
|
|
4483
|
+
"[apm] \u8865\u9F50 workspace-repos remoteUrl \u5931\u8D25:",
|
|
4484
|
+
err instanceof Error ? err.message : err
|
|
4485
|
+
);
|
|
4486
|
+
}
|
|
4487
|
+
return workspaceRepos;
|
|
4488
|
+
},
|
|
4489
|
+
(repos) => `kind=${repos.kind} repos=${repos.repos.length}`
|
|
4490
|
+
);
|
|
4491
|
+
if (shouldRunBranch(branchCacheKey, workdir)) {
|
|
4492
|
+
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
4493
|
+
await runPrepStep(
|
|
4494
|
+
"\u51C6\u5907\u7279\u6027\u5206\u652F",
|
|
4495
|
+
async () => runTaskBranch(taskId, { cwd: workdir }),
|
|
4496
|
+
(branchResult) => {
|
|
4497
|
+
markBranchDone(branchCacheKey, workdir);
|
|
4498
|
+
return `kind=${branchResult.kind} branch=${branchResult.branch} baseline=${branchResult.baseline} repos=${branchResult.repos.length}`;
|
|
4499
|
+
}
|
|
4500
|
+
);
|
|
4501
|
+
} else {
|
|
4502
|
+
eventSession.addCliStep("\u51C6\u5907\u7279\u6027\u5206\u652F", "\u5DF2\u7F13\u5B58\uFF0C\u8DF3\u8FC7", "skip");
|
|
4503
|
+
await syncPrepLog();
|
|
4504
|
+
}
|
|
4505
|
+
if (shouldEnsureWebIdePullRequests(msg2.action)) {
|
|
4506
|
+
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
4507
|
+
await runPrepStep(
|
|
4508
|
+
"\u6D4B\u8BD5\u524D\u63A8\u9001",
|
|
4509
|
+
async () => pushWorkspaceRepos(workdir),
|
|
4510
|
+
(pushed) => `\u5DF2 push ${pushed} \u4E2A\u4ED3\u5E93`
|
|
4511
|
+
);
|
|
4512
|
+
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
4513
|
+
await runPrepStep(
|
|
4514
|
+
"\u786E\u4FDD PR",
|
|
4515
|
+
async () => {
|
|
4516
|
+
await ensureWebIdePullRequests(cfg2, taskId, workdir);
|
|
4517
|
+
return true;
|
|
4518
|
+
},
|
|
4519
|
+
() => "PR \u68C0\u67E5\u5B8C\u6210"
|
|
4520
|
+
);
|
|
4521
|
+
}
|
|
4522
|
+
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
4523
|
+
const api = createApmApiClient(cfg2);
|
|
4524
|
+
let projectId = null;
|
|
4525
|
+
await runPrepStep(
|
|
4526
|
+
"\u540C\u6B65\u9879\u76EE\u6587\u6863",
|
|
4527
|
+
async () => {
|
|
4528
|
+
try {
|
|
4529
|
+
const baseline = await api.cli.projectBaseBranch({ taskId });
|
|
4530
|
+
projectId = baseline.projectId?.trim() || null;
|
|
4531
|
+
if (!projectId) {
|
|
4532
|
+
return { synced: false, reason: "\u65E0 projectId" };
|
|
4533
|
+
}
|
|
4534
|
+
const docs = await syncProjectDocumentsPull(workdir, void 0, {
|
|
4535
|
+
projectId
|
|
4536
|
+
});
|
|
4537
|
+
return {
|
|
4538
|
+
synced: docs.synced,
|
|
4539
|
+
downloaded: docs.downloaded,
|
|
4540
|
+
deleted: docs.deleted
|
|
4541
|
+
};
|
|
4542
|
+
} catch (err) {
|
|
4543
|
+
console.warn(
|
|
4544
|
+
"[apm] WebIDE \u9879\u76EE\u6587\u6863\u540C\u6B65\u5931\u8D25:",
|
|
4545
|
+
err instanceof Error ? err.message : err
|
|
4546
|
+
);
|
|
4547
|
+
return {
|
|
4548
|
+
synced: false,
|
|
4549
|
+
reason: err instanceof Error ? err.message : String(err)
|
|
4550
|
+
};
|
|
4551
|
+
}
|
|
4552
|
+
},
|
|
4553
|
+
(result) => {
|
|
4554
|
+
if ("reason" in result && result.reason) {
|
|
4555
|
+
return `\u8DF3\u8FC7\uFF1A${result.reason}`;
|
|
4556
|
+
}
|
|
4557
|
+
if (!result.synced) return "\u65E0\u9700\u540C\u6B65";
|
|
4558
|
+
return `downloaded=${result.downloaded ?? 0} deleted=${result.deleted ?? 0}`;
|
|
4559
|
+
}
|
|
4560
|
+
);
|
|
4561
|
+
await runPrepStep(
|
|
4562
|
+
"\u540C\u6B65\u4EFB\u52A1\u9644\u4EF6",
|
|
4563
|
+
async () => {
|
|
4564
|
+
try {
|
|
4565
|
+
const attachments = await api.cli.webideListAttachments({ taskId });
|
|
4566
|
+
const names = await syncWebIdeAttachments(
|
|
4567
|
+
cfg2,
|
|
4568
|
+
taskId,
|
|
4569
|
+
workdir,
|
|
4570
|
+
attachments ?? []
|
|
4571
|
+
);
|
|
4572
|
+
return names;
|
|
4426
4573
|
} catch (err) {
|
|
4427
4574
|
console.warn(
|
|
4428
|
-
"[apm] \
|
|
4575
|
+
"[apm] WebIDE \u9644\u4EF6\u540C\u6B65\u5931\u8D25:",
|
|
4429
4576
|
err instanceof Error ? err.message : err
|
|
4430
4577
|
);
|
|
4578
|
+
return [];
|
|
4431
4579
|
}
|
|
4432
|
-
return workspaceRepos;
|
|
4433
4580
|
},
|
|
4434
|
-
(
|
|
4435
|
-
);
|
|
4436
|
-
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
4437
|
-
await runPrepStep(
|
|
4438
|
-
"\u5207\u6362\u57FA\u7EBF\u5E76\u62C9\u53D6\u6700\u65B0",
|
|
4439
|
-
async () => checkoutBaselineBranches(msg.baseBranch, { cwd: workdir }),
|
|
4440
|
-
(result) => `baseline=${result.baseline} kind=${result.kind} repos=${result.repos.length}`
|
|
4581
|
+
(names) => names.length > 0 ? `\u5DF2\u540C\u6B65 ${names.length} \u4E2A\u9644\u4EF6` : "\u65E0\u9644\u4EF6\u6216\u540C\u6B65\u8DF3\u8FC7"
|
|
4441
4582
|
);
|
|
4442
|
-
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
4443
4583
|
await runPrepStep(
|
|
4444
|
-
"\u540C\u6B65\
|
|
4584
|
+
"\u540C\u6B65\u8BBE\u8BA1\u4EA7\u7269",
|
|
4445
4585
|
async () => {
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4586
|
+
try {
|
|
4587
|
+
const artifacts = await api.cli.webideListDesignArtifacts({ taskId });
|
|
4588
|
+
const names = await syncWebIdeDesign(
|
|
4589
|
+
cfg2,
|
|
4590
|
+
taskId,
|
|
4591
|
+
workdir,
|
|
4592
|
+
artifacts ?? []
|
|
4593
|
+
);
|
|
4594
|
+
return names;
|
|
4595
|
+
} catch (err) {
|
|
4596
|
+
console.warn(
|
|
4597
|
+
"[apm] WebIDE \u8BBE\u8BA1\u4EA7\u7269\u540C\u6B65\u5931\u8D25:",
|
|
4598
|
+
err instanceof Error ? err.message : err
|
|
4599
|
+
);
|
|
4600
|
+
return [];
|
|
4454
4601
|
}
|
|
4455
|
-
|
|
4456
|
-
}
|
|
4602
|
+
},
|
|
4603
|
+
(names) => names.length > 0 ? `\u5DF2\u540C\u6B65 ${names.length} \u4E2A\u8BBE\u8BA1\u6587\u4EF6` : "\u65E0\u8BBE\u8BA1\u6587\u4EF6\u6216\u540C\u6B65\u8DF3\u8FC7"
|
|
4604
|
+
);
|
|
4605
|
+
eventSession.addCliStep(
|
|
4606
|
+
"\u542F\u52A8 Cursor Agent",
|
|
4607
|
+
"\u51C6\u5907 resume/create\u2026",
|
|
4608
|
+
"running"
|
|
4457
4609
|
);
|
|
4458
|
-
eventSession.addCliStep("\u542F\u52A8\u6253\u5305 Agent", "\u51C6\u5907 create\u2026", "running");
|
|
4459
4610
|
await syncPrepLog();
|
|
4460
|
-
|
|
4611
|
+
const startLocalServices = isStartLocalServicesAction(msg2.action);
|
|
4612
|
+
const designAction = isDesignAction(msg2.action);
|
|
4613
|
+
const executeSql = isExecuteSqlAction(msg2.action);
|
|
4614
|
+
const savedAgentId = startLocalServices ? void 0 : designAction ? loadWebIdeDesignAgentId(workdir, taskId) : loadWebIdeAgentId(workdir, taskId);
|
|
4615
|
+
const promptPayload = executeSql ? parseWebIdePromptPayload(msg2.content) : null;
|
|
4616
|
+
const sqlExecutionId = executeSql && typeof promptPayload?.executionId === "string" ? promptPayload.executionId.trim() : void 0;
|
|
4617
|
+
const persistAgentId = (agentId) => {
|
|
4618
|
+
if (startLocalServices) return;
|
|
4619
|
+
if (designAction) {
|
|
4620
|
+
saveWebIdeDesignAgentId(workdir, taskId, agentId);
|
|
4621
|
+
} else {
|
|
4622
|
+
saveWebIdeAgentId(workdir, taskId, agentId);
|
|
4623
|
+
}
|
|
4624
|
+
};
|
|
4461
4625
|
const outcome = await runCursorAgent(
|
|
4462
|
-
|
|
4626
|
+
cfg2,
|
|
4463
4627
|
{
|
|
4464
4628
|
messageId,
|
|
4465
|
-
prompt:
|
|
4466
|
-
|
|
4467
|
-
|
|
4629
|
+
prompt: assumptionAnswersText ? `${msg2.content}
|
|
4630
|
+
|
|
4631
|
+
## \u5DF2\u786E\u8BA4\u5047\u8BBE
|
|
4632
|
+
${assumptionAnswersText}` : msg2.content,
|
|
4633
|
+
model: msg2.model,
|
|
4634
|
+
apiKey: msg2.apiKey,
|
|
4468
4635
|
workdir,
|
|
4469
|
-
user:
|
|
4636
|
+
user: msg2.user || "webide",
|
|
4637
|
+
resumeAgentId: savedAgentId
|
|
4470
4638
|
},
|
|
4471
4639
|
{
|
|
4472
4640
|
signal,
|
|
4473
4641
|
forceSend: true,
|
|
4474
4642
|
eventSession,
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4643
|
+
appendMessageContent: (content) => appendContent(cfg2, messageId, content),
|
|
4644
|
+
askQuestionExecute: startLocalServices ? void 0 : createWebIdeAskQuestionExecute({
|
|
4645
|
+
cfg: cfg2,
|
|
4646
|
+
taskId,
|
|
4647
|
+
onAwaitingAssumptions: () => {
|
|
4648
|
+
pausedForAssumptions = true;
|
|
4649
|
+
}
|
|
4650
|
+
}),
|
|
4651
|
+
enableAskQuestion: !startLocalServices && !executeSql,
|
|
4652
|
+
enableWebIdePlanTools: !startLocalServices && !executeSql && !designAction,
|
|
4653
|
+
enablePtySessionMcp: startLocalServices,
|
|
4654
|
+
enableMysqlTools: executeSql,
|
|
4655
|
+
sqlExecutionId,
|
|
4479
4656
|
enableSandbox: false,
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4657
|
+
onInvalidatePersistedAgentId: startLocalServices ? void 0 : () => {
|
|
4658
|
+
if (designAction) {
|
|
4659
|
+
clearWebIdeDesignAgentId(workdir, taskId);
|
|
4660
|
+
} else {
|
|
4661
|
+
clearWebIdeAgentId(workdir, taskId);
|
|
4662
|
+
}
|
|
4485
4663
|
},
|
|
4486
|
-
taskId
|
|
4664
|
+
taskId,
|
|
4487
4665
|
createRemoteLogSync: (agentId) => {
|
|
4666
|
+
persistAgentId(agentId);
|
|
4488
4667
|
logSyncRef.current = createThrottledWebIdeMessageLogSync(
|
|
4489
|
-
|
|
4490
|
-
{
|
|
4491
|
-
taskId: `package:${packageId}`,
|
|
4492
|
-
messageId,
|
|
4493
|
-
agentId
|
|
4494
|
-
},
|
|
4668
|
+
cfg2,
|
|
4669
|
+
{ taskId, messageId, agentId },
|
|
4495
4670
|
(err) => {
|
|
4496
4671
|
console.warn(
|
|
4497
|
-
"[apm] WebIDE \
|
|
4672
|
+
"[apm] WebIDE \u65E5\u5FD7\u540C\u6B65\u5931\u8D25:",
|
|
4498
4673
|
err instanceof Error ? err.message : err
|
|
4499
4674
|
);
|
|
4500
|
-
},
|
|
4501
|
-
{
|
|
4502
|
-
objectPrefixOverride: `events/webide-package/${packageId}/`
|
|
4503
4675
|
}
|
|
4504
4676
|
);
|
|
4505
4677
|
return logSyncRef.current;
|
|
4506
4678
|
},
|
|
4507
4679
|
onRunStarted: async ({ agentId, runId }) => {
|
|
4680
|
+
persistAgentId(agentId);
|
|
4508
4681
|
eventSession.addCliStep(
|
|
4509
|
-
"\u542F\u52A8
|
|
4682
|
+
"\u542F\u52A8 Cursor Agent",
|
|
4510
4683
|
`agentId=${agentId} runId=${runId}`,
|
|
4511
4684
|
"ok"
|
|
4512
4685
|
);
|
|
@@ -4516,8 +4689,11 @@ async function handleWebIdePackage(cfg, msg, signal) {
|
|
|
4516
4689
|
}
|
|
4517
4690
|
}
|
|
4518
4691
|
);
|
|
4692
|
+
if (!startLocalServices) {
|
|
4693
|
+
persistAgentId(outcome.agentId);
|
|
4694
|
+
}
|
|
4519
4695
|
const tokenUsage = outcome.usage != null ? {
|
|
4520
|
-
modelId: outcome.modelId ?? (
|
|
4696
|
+
modelId: outcome.modelId ?? (msg2.model?.trim() || void 0),
|
|
4521
4697
|
inputTokens: outcome.usage.inputTokens,
|
|
4522
4698
|
outputTokens: outcome.usage.outputTokens,
|
|
4523
4699
|
cacheReadTokens: outcome.usage.cacheReadTokens,
|
|
@@ -4535,9 +4711,22 @@ async function handleWebIdePackage(cfg, msg, signal) {
|
|
|
4535
4711
|
detail,
|
|
4536
4712
|
tokenUsage
|
|
4537
4713
|
);
|
|
4538
|
-
if (
|
|
4539
|
-
|
|
4714
|
+
if (executeSql && sqlExecutionId) {
|
|
4715
|
+
try {
|
|
4716
|
+
await api.cli.webideFinalizeSqlExecution({
|
|
4717
|
+
executionId: sqlExecutionId,
|
|
4718
|
+
status: "FAILED",
|
|
4719
|
+
error: detail
|
|
4720
|
+
});
|
|
4721
|
+
} catch (finalizeErr) {
|
|
4722
|
+
console.warn(
|
|
4723
|
+
"[apm] finalize sql execution failed:",
|
|
4724
|
+
finalizeErr instanceof Error ? finalizeErr.message : finalizeErr
|
|
4725
|
+
);
|
|
4726
|
+
}
|
|
4540
4727
|
}
|
|
4728
|
+
await setError2(cfg2, messageId, detail);
|
|
4729
|
+
syncLocalTaskStatus(workdir, taskId, msg2, "FAILED");
|
|
4541
4730
|
return;
|
|
4542
4731
|
}
|
|
4543
4732
|
if (outcome.status === "cancelled" || signal.aborted) {
|
|
@@ -4547,92 +4736,226 @@ async function handleWebIdePackage(cfg, msg, signal) {
|
|
|
4547
4736
|
"\u5DF2\u53D6\u6D88",
|
|
4548
4737
|
tokenUsage
|
|
4549
4738
|
);
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4739
|
+
if (executeSql && sqlExecutionId) {
|
|
4740
|
+
try {
|
|
4741
|
+
await api.cli.webideFinalizeSqlExecution({
|
|
4742
|
+
executionId: sqlExecutionId,
|
|
4743
|
+
status: "CANCELLED",
|
|
4744
|
+
error: "\u5DF2\u53D6\u6D88"
|
|
4745
|
+
});
|
|
4746
|
+
} catch (finalizeErr) {
|
|
4747
|
+
console.warn(
|
|
4748
|
+
"[apm] finalize sql execution cancelled:",
|
|
4749
|
+
finalizeErr instanceof Error ? finalizeErr.message : finalizeErr
|
|
4750
|
+
);
|
|
4751
|
+
}
|
|
4752
|
+
}
|
|
4753
|
+
await updateStatus2(cfg2, messageId, "CANCELLED");
|
|
4754
|
+
syncLocalTaskStatus(workdir, taskId, msg2, "CANCELLED");
|
|
4553
4755
|
return;
|
|
4554
4756
|
}
|
|
4757
|
+
const fallback = resolveMessageReplyFallback({
|
|
4758
|
+
assistantText: outcome.assistantText,
|
|
4759
|
+
result: outcome.result,
|
|
4760
|
+
createPlan: outcome.createPlan
|
|
4761
|
+
});
|
|
4762
|
+
await api.cli.webideEnsureMessageContent({
|
|
4763
|
+
id: messageId,
|
|
4764
|
+
content: fallback
|
|
4765
|
+
});
|
|
4766
|
+
if ((msg2.action === "write-plan" || msg2.action === "revise-plan") && outcome.createPlan && outcome.createPlan.trim()) {
|
|
4767
|
+
try {
|
|
4768
|
+
await api.cli.webideUpsertPlan({
|
|
4769
|
+
taskId,
|
|
4770
|
+
content: outcome.createPlan.trim()
|
|
4771
|
+
});
|
|
4772
|
+
} catch (err) {
|
|
4773
|
+
console.warn(
|
|
4774
|
+
`[apm] ${msg2.action} \u8865\u5199\u8BA1\u5212\u5931\u8D25:`,
|
|
4775
|
+
err instanceof Error ? err.message : err
|
|
4776
|
+
);
|
|
4777
|
+
}
|
|
4778
|
+
}
|
|
4779
|
+
if (isDesignAction(msg2.action)) {
|
|
4780
|
+
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
4781
|
+
try {
|
|
4782
|
+
const uploaded = await uploadWebIdeDesignArtifacts(
|
|
4783
|
+
cfg2,
|
|
4784
|
+
taskId,
|
|
4785
|
+
workdir
|
|
4786
|
+
);
|
|
4787
|
+
console.log(
|
|
4788
|
+
`[apm] webide \u8BBE\u8BA1\u4EA7\u7269\u4E0A\u4F20 action=${msg2.action} uploaded=${uploaded.uploaded} skipped=${uploaded.skipped} files=${uploaded.artifacts.map((a) => a.name).join(",")}`
|
|
4789
|
+
);
|
|
4790
|
+
} catch (err) {
|
|
4791
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
4792
|
+
await logSyncRef.current?.markRun(
|
|
4793
|
+
outcome.runId,
|
|
4794
|
+
"error",
|
|
4795
|
+
detail,
|
|
4796
|
+
tokenUsage
|
|
4797
|
+
);
|
|
4798
|
+
await setError2(cfg2, messageId, detail);
|
|
4799
|
+
syncLocalTaskStatus(workdir, taskId, msg2, "FAILED");
|
|
4800
|
+
return;
|
|
4801
|
+
}
|
|
4802
|
+
}
|
|
4803
|
+
if (shouldCommitAfterWebIdeMessage(msg2.action)) {
|
|
4804
|
+
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
4805
|
+
const committed = await commitWorkspaceReposIfDirty(
|
|
4806
|
+
workdir,
|
|
4807
|
+
`chore(webide): sync after ${msg2.action} (${taskId})`
|
|
4808
|
+
);
|
|
4809
|
+
console.log(
|
|
4810
|
+
`[apm] webide \u515C\u5E95 commit action=${msg2.action} repos=${committed}`
|
|
4811
|
+
);
|
|
4812
|
+
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
4813
|
+
const pushed = await pushWorkspaceRepos(workdir);
|
|
4814
|
+
console.log(
|
|
4815
|
+
`[apm] webide \u81EA\u52A8 push action=${msg2.action} repos=${pushed}`
|
|
4816
|
+
);
|
|
4817
|
+
}
|
|
4818
|
+
if (shouldEnsureWebIdePullRequestsAfterAgent(msg2.action)) {
|
|
4819
|
+
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
4820
|
+
if (!shouldCommitAfterWebIdeMessage(msg2.action)) {
|
|
4821
|
+
const pushed = await pushWorkspaceRepos(workdir);
|
|
4822
|
+
console.log(
|
|
4823
|
+
`[apm] webide create-pr \u524D push action=${msg2.action} repos=${pushed}`
|
|
4824
|
+
);
|
|
4825
|
+
}
|
|
4826
|
+
const prResult = await ensureWebIdePullRequests(cfg2, taskId, workdir);
|
|
4827
|
+
if (prResult.tips.length > 0) {
|
|
4828
|
+
for (const tip of prResult.tips) {
|
|
4829
|
+
console.log(`[apm] webide create-pr \u63D0\u793A: ${tip}`);
|
|
4830
|
+
}
|
|
4831
|
+
}
|
|
4832
|
+
try {
|
|
4833
|
+
await appendContent(
|
|
4834
|
+
cfg2,
|
|
4835
|
+
messageId,
|
|
4836
|
+
formatWebIdePullRequestSummary(prResult)
|
|
4837
|
+
);
|
|
4838
|
+
} catch (appendErr) {
|
|
4839
|
+
console.warn(
|
|
4840
|
+
"[apm] webide create-pr \u8FFD\u52A0 PR \u7ED3\u679C\u5931\u8D25:",
|
|
4841
|
+
appendErr instanceof Error ? appendErr.message : appendErr
|
|
4842
|
+
);
|
|
4843
|
+
}
|
|
4844
|
+
if (prResult.ensured === 0) {
|
|
4845
|
+
if (prResult.failed > 0) {
|
|
4846
|
+
throw new Error(
|
|
4847
|
+
`\u521B\u5EFA PR \u5931\u8D25\uFF08${prResult.failed} \u4E2A\u4ED3\u5E93\u51FA\u9519\uFF09${prResult.tips[0] ? `\uFF1A${prResult.tips[0]}` : ""}`
|
|
4848
|
+
);
|
|
4849
|
+
}
|
|
4850
|
+
if (prResult.skippedNoChanges > 0 && prResult.skipped === 0) {
|
|
4851
|
+
console.log(
|
|
4852
|
+
"[apm] webide create-pr\uFF1A\u5404\u4ED3\u5E93\u76F8\u5BF9\u63A8\u65AD\u57FA\u7EBF\u5747\u65E0\u53D8\u66F4\uFF0C\u8DF3\u8FC7\u521B\u5EFA PR"
|
|
4853
|
+
);
|
|
4854
|
+
} else {
|
|
4855
|
+
throw new Error(
|
|
4856
|
+
prResult.tips[0] || "\u672A\u80FD\u521B\u5EFA PR\uFF1A\u8BF7\u786E\u8BA4\u5404\u4ED3\u5F53\u524D\u4F4D\u4E8E feat/task-* \u5206\u652F\uFF0C\u4E14 remote \u5DF2\u5728\u5E73\u53F0\u767B\u8BB0"
|
|
4857
|
+
);
|
|
4858
|
+
}
|
|
4859
|
+
}
|
|
4860
|
+
}
|
|
4861
|
+
if (projectId) {
|
|
4862
|
+
try {
|
|
4863
|
+
await syncProjectDocumentsPush(cfg2, workdir, void 0, { projectId });
|
|
4864
|
+
} catch (err) {
|
|
4865
|
+
console.warn(
|
|
4866
|
+
"[apm] WebIDE \u9879\u76EE\u6587\u6863\u63A8\u9001\u5931\u8D25:",
|
|
4867
|
+
err instanceof Error ? err.message : err
|
|
4868
|
+
);
|
|
4869
|
+
}
|
|
4870
|
+
}
|
|
4555
4871
|
await logSyncRef.current?.markRun(
|
|
4556
4872
|
outcome.runId,
|
|
4557
4873
|
"finished",
|
|
4558
4874
|
null,
|
|
4559
4875
|
tokenUsage
|
|
4560
4876
|
);
|
|
4561
|
-
|
|
4877
|
+
const completeAction = completeActionForInbound(msg2.action);
|
|
4878
|
+
if (completeAction && !pausedForAssumptions) {
|
|
4879
|
+
await webIdeRunAction(cfg2, taskId, completeAction);
|
|
4880
|
+
} else if (pausedForAssumptions) {
|
|
4562
4881
|
console.log(
|
|
4563
|
-
`[apm]
|
|
4882
|
+
`[apm] \u672C\u8F6E\u505C\u5728\u5F85\u786E\u8BA4\u5047\u8BBE\uFF0C\u8DF3\u8FC7 complete action=${msg2.action} taskId=${taskId}`
|
|
4564
4883
|
);
|
|
4565
|
-
return;
|
|
4566
4884
|
}
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
() => uploadPackageArtifact(cfg, msg.target, packageId, projectId),
|
|
4570
|
-
(result) => `objectKey=${result.artifactPath}${result.artifactUrl ? ` url=${result.artifactUrl}` : ""}`
|
|
4571
|
-
);
|
|
4572
|
-
await updatePackageStatus(cfg, packageId, "SUCCESS", {
|
|
4573
|
-
artifactPath: uploaded.artifactPath,
|
|
4574
|
-
...uploaded.artifactUrl ? { artifactUrl: uploaded.artifactUrl } : {}
|
|
4575
|
-
});
|
|
4885
|
+
await updateStatus2(cfg2, messageId, "SUCCESS");
|
|
4886
|
+
syncLocalTaskStatus(workdir, taskId, msg2, "SUCCESS");
|
|
4576
4887
|
console.log(
|
|
4577
|
-
`[apm] webide-
|
|
4888
|
+
`[apm] webide-message \u5B8C\u6210 action=${msg2.action} messageId=${messageId} agentId=${outcome.agentId}`
|
|
4578
4889
|
);
|
|
4890
|
+
if (msg2.action === "request-accept") {
|
|
4891
|
+
try {
|
|
4892
|
+
await cleanWebIdeWorkspaceCache(taskId, workdir);
|
|
4893
|
+
} catch (err) {
|
|
4894
|
+
console.warn(
|
|
4895
|
+
"[apm] \u5408\u5E76 PR \u540E\u6E05\u7406 WebIDE agent \u7F13\u5B58\u5931\u8D25:",
|
|
4896
|
+
err instanceof Error ? err.message : err
|
|
4897
|
+
);
|
|
4898
|
+
}
|
|
4899
|
+
}
|
|
4579
4900
|
} catch (err) {
|
|
4580
4901
|
const detail = err instanceof Error ? err.message : String(err);
|
|
4581
|
-
console.error(`[apm] webide-
|
|
4902
|
+
console.error(`[apm] webide-message \u5931\u8D25: ${detail}`);
|
|
4903
|
+
if (detail.includes("\u590D\u7528 Agent") || detail.includes("resume")) {
|
|
4904
|
+
clearWebIdeAgentId(workdir, taskId);
|
|
4905
|
+
}
|
|
4582
4906
|
try {
|
|
4583
|
-
|
|
4584
|
-
await updatePackageStatus(cfg, packageId, "CANCELLED", {
|
|
4585
|
-
error: detail
|
|
4586
|
-
});
|
|
4587
|
-
} else {
|
|
4588
|
-
await updatePackageStatus(cfg, packageId, "FAILED", { error: detail });
|
|
4589
|
-
}
|
|
4907
|
+
await setError2(cfg2, messageId, detail);
|
|
4590
4908
|
} catch (statusErr) {
|
|
4591
4909
|
console.error(
|
|
4592
|
-
"[apm] \u5199\u5165
|
|
4910
|
+
"[apm] \u5199\u5165 WebIDE FAILED \u5931\u8D25:",
|
|
4593
4911
|
statusErr instanceof Error ? statusErr.message : statusErr
|
|
4594
4912
|
);
|
|
4595
4913
|
}
|
|
4914
|
+
syncLocalTaskStatus(workdir, taskId, msg2, "FAILED");
|
|
4596
4915
|
}
|
|
4597
4916
|
}
|
|
4598
4917
|
|
|
4599
|
-
// src/commands/connect/
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
await handleStartProject(
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4918
|
+
// src/commands/connect/core/worker/handlers/index.ts
|
|
4919
|
+
async function dispatchWorkerJob(cfg2, msg2, signal) {
|
|
4920
|
+
switch (msg2.type) {
|
|
4921
|
+
case "webide-message":
|
|
4922
|
+
await handleWebIdeInboundMessage(cfg2, msg2, signal);
|
|
4923
|
+
return;
|
|
4924
|
+
case "webide-start-project":
|
|
4925
|
+
await handleStartProject(cfg2, msg2, signal);
|
|
4926
|
+
return;
|
|
4927
|
+
case "webide-package":
|
|
4928
|
+
await handleWebIdePackage(cfg2, msg2, signal);
|
|
4929
|
+
return;
|
|
4930
|
+
default: {
|
|
4931
|
+
const unknown = msg2;
|
|
4932
|
+
throw new Error(
|
|
4933
|
+
`\u672A\u77E5 WebIDE worker \u4EFB\u52A1: ${unknown.type ?? "unknown"}`
|
|
4934
|
+
);
|
|
4611
4935
|
}
|
|
4612
|
-
parentPort?.postMessage({
|
|
4613
|
-
type: "done",
|
|
4614
|
-
messageId: msg.messageId
|
|
4615
|
-
});
|
|
4616
|
-
} catch (err) {
|
|
4617
|
-
parentPort?.postMessage({
|
|
4618
|
-
type: "error",
|
|
4619
|
-
messageId: msg.messageId,
|
|
4620
|
-
error: err instanceof Error ? err.message : String(err)
|
|
4621
|
-
});
|
|
4622
|
-
} finally {
|
|
4623
|
-
controllers.delete(msg.messageId);
|
|
4624
4936
|
}
|
|
4625
4937
|
}
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4938
|
+
|
|
4939
|
+
// src/commands/connect/core/worker/worker-script.ts
|
|
4940
|
+
var port = parentPort;
|
|
4941
|
+
if (!port) throw new Error("worker-script \u5FC5\u987B\u5728 Worker \u7EBF\u7A0B\u4E2D\u8FD0\u884C");
|
|
4942
|
+
var cfg = tryReadApmConfig();
|
|
4943
|
+
var { msg } = workerData;
|
|
4944
|
+
if (!cfg || !msg) throw new Error("worker-script \u7F3A\u5C11 workerData");
|
|
4945
|
+
var controller = new AbortController();
|
|
4946
|
+
port.on("message", (raw) => {
|
|
4947
|
+
if (raw?.type === "cancel" && raw.messageId === msg.messageId) {
|
|
4948
|
+
controller.abort();
|
|
4633
4949
|
}
|
|
4634
4950
|
});
|
|
4635
|
-
var
|
|
4636
|
-
|
|
4637
|
-
|
|
4951
|
+
var reply = (body) => port.postMessage(body);
|
|
4952
|
+
try {
|
|
4953
|
+
await dispatchWorkerJob(cfg, msg, controller.signal);
|
|
4954
|
+
reply({ type: "done", messageId: msg.messageId });
|
|
4955
|
+
} catch (err) {
|
|
4956
|
+
reply({
|
|
4957
|
+
type: "error",
|
|
4958
|
+
messageId: msg.messageId,
|
|
4959
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4960
|
+
});
|
|
4638
4961
|
}
|