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