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.
@@ -1,4 +1,4 @@
1
- // src/commands/connect/webide-message-worker.ts
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(cfg) {
134
- return (cfg.clientMachineId ?? cfg.userId ?? "").trim();
138
+ function resolveClientMachineId(cfg2) {
139
+ return (cfg2.clientMachineId ?? cfg2.userId ?? "").trim();
135
140
  }
136
- function resolveApiKey(cfg) {
137
- return (cfg.apiKey ?? cfg.token ?? "").trim();
141
+ function resolveApiKey(cfg2) {
142
+ return (cfg2.apiKey ?? cfg2.token ?? "").trim();
138
143
  }
139
- async function tryReadApmConfig() {
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 cfg = v;
153
- const clientMachineId = resolveClientMachineId(cfg);
157
+ const cfg2 = v;
158
+ const clientMachineId = resolveClientMachineId(cfg2);
154
159
  return {
155
- baseUrl: cfg.baseUrl.trim().replace(/\/+$/, ""),
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(cfg) {
178
- const clientMachineId = resolveClientMachineId(cfg);
179
- const apiKey = resolveApiKey(cfg);
182
+ async function writeApmConfig(cfg2) {
183
+ const clientMachineId = resolveClientMachineId(cfg2);
184
+ const apiKey = resolveApiKey(cfg2);
180
185
  const normalized = {
181
- baseUrl: cfg.baseUrl.trim().replace(/\/+$/, ""),
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(cfg) {
193
- const baseURL = `${cfg.baseUrl.trim().replace(/\/+$/, "")}/api/v1`;
197
+ function createApmApiClient(cfg2) {
198
+ const baseURL = `${cfg2.baseUrl.trim().replace(/\/+$/, "")}/api/v1`;
194
199
  return createApiClient(requestConfig, {
195
200
  baseURL,
196
- getToken: () => resolveApiKey(cfg) || void 0,
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 cfg = await ensureApmConfig();
286
- if (!resolveApiKey(cfg)) {
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 cfg;
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 cfg = await ensureLoggedConfig();
939
- const api = createApmApiClient(cfg);
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 port = asNumber(args.port) ?? 3306;
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(cfg) {
1941
- const api = createApmApiClient(cfg);
1945
+ async function fetchApmLogStorage(cfg2) {
1946
+ const api = createApmApiClient(cfg2);
1942
1947
  return api.cli.getApmLogStorage(void 0);
1943
1948
  }
1944
- async function createApmLogMinioClient(cfg) {
1945
- const storage = await fetchApmLogStorage(cfg);
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(cfg, options) {
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(cfg);
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(cfg);
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 port = env.LOCAL_TERMINAL_PORT?.trim() || DEFAULT_PTY_SESSION_PORT;
2342
- return `http://127.0.0.1:${port}`;
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(cfg) {
2345
- const token = resolveApiKey(cfg);
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(cfg, ctx, options) {
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(cfg, {
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(cfg) : void 0;
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(cfg, ctx, patch, objectPrefix) {
2884
- const api = createApmApiClient(cfg);
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(cfg, ctx, onError, options) {
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(cfg);
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
- cfg,
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
- cfg,
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/commands/connect/pre-step-cache.ts
3030
- function taskWorkdirKey(taskId, workdir) {
3031
- return `${taskId}\0${workdir}`;
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
- var lastBranchKey = null;
3034
- function shouldRunBranch(taskId, workdir) {
3035
- return lastBranchKey !== taskWorkdirKey(taskId, workdir);
2752
+ function projectDocumentsDir(apmRoot, projectId) {
2753
+ const id = normalizeProjectIdForPath(projectId);
2754
+ return join4(apmRoot ?? workspaceApmDir(), "project", id);
3036
2755
  }
3037
- function markBranchDone(taskId, workdir) {
3038
- lastBranchKey = taskWorkdirKey(taskId, workdir);
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
- // src/commands/connect/webide-pull-requests.ts
3042
- var PRE_AGENT_PR_ACTIONS = /* @__PURE__ */ new Set([
3043
- "generate-cases",
3044
- "run-auto-test",
3045
- // 验收合并前兜底确保 PR 存在
3046
- "accept"
3047
- ]);
3048
- var POST_AGENT_PR_ACTIONS = /* @__PURE__ */ new Set(["create-pr", "finish-manual-test"]);
3049
- function shouldEnsureWebIdePullRequests(action) {
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 shouldEnsureWebIdePullRequestsAfterAgent(action) {
3053
- return POST_AGENT_PR_ACTIONS.has(action);
2774
+ function hashLocalFileContent(content) {
2775
+ return createHash2("sha256").update(content, "utf8").digest("hex");
3054
2776
  }
3055
- function formatWebIdePullRequestSummary(result) {
3056
- const lines = [];
3057
- lines.push("## PR \u521B\u5EFA\u7ED3\u679C");
3058
- lines.push(
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 (result.items.length === 0) {
3062
- lines.push("\u672A\u5904\u7406\u4EFB\u4F55\u4ED3\u5E93\u3002");
3063
- return lines.join("\n");
2782
+ if (!existsSync5(manifestPath3)) {
2783
+ return null;
3064
2784
  }
3065
- lines.push("");
3066
- for (const item of result.items) {
3067
- if (item.outcome === "created") {
3068
- const num = item.number != null && item.number > 0 ? `#${item.number}` : "\uFF08\u65E0\u7F16\u53F7\uFF09";
3069
- const branch = item.headBranch && item.baseBranch ? `${item.headBranch} \u2192 ${item.baseBranch}` : item.baseBranch ? `\u76EE\u6807\u5206\u652F ${item.baseBranch}` : "";
3070
- const state = item.state ? `\u72B6\u6001 ${item.state}` : "";
3071
- const url = item.url?.trim() ? item.url.trim() : "";
3072
- lines.push(
3073
- `- **${item.label}**\uFF1A\u5DF2\u521B\u5EFA ${num}${branch ? `\uFF0C${branch}` : ""}${state ? `\uFF0C${state}` : ""}${url ? `\uFF0C${url}` : ""}`
3074
- );
3075
- continue;
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
- if (item.outcome === "skipped_no_changes") {
3078
- lines.push(
3079
- `- **${item.label}**\uFF1A\u76F8\u5BF9\u57FA\u7EBF ${item.baseBranch || "\uFF08\u672A\u77E5\uFF09"} \u65E0\u4EE3\u7801\u53D8\u66F4\uFF0C\u672A\u521B\u5EFA PR`
3080
- );
3081
- continue;
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
- if (item.outcome === "skipped") {
3084
- lines.push(`- **${item.label}**\uFF1A\u5DF2\u8DF3\u8FC7 \u2014 ${item.detail}`);
3085
- continue;
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 lines.join("\n");
2835
+ return { download, deleteLocal };
3090
2836
  }
3091
- async function ensureWebIdePullRequests(cfg, taskId, workdir) {
3092
- const api = createApmApiClient(cfg);
3093
- const empty = () => ({
3094
- ensured: 0,
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
- projectBaseBranch = baseline.baseBranch?.trim() ?? "";
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 tip = `\u8BFB\u53D6\u9879\u76EE\u57FA\u7EBF\u5206\u652F\u5931\u8D25: ${err instanceof Error ? err.message : err}`;
3127
- console.warn(`[apm] WebIDE PR\uFF1A${tip}`);
2863
+ const detail = err instanceof Error ? err.message : String(err);
3128
2864
  return {
3129
- ...empty(),
3130
- failed: 1,
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
- if (!projectBaseBranch) {
3136
- const tip = "\u9879\u76EE\u672A\u914D\u7F6E\u57FA\u7840\u5206\u652F\uFF0C\u65E0\u6CD5\u521B\u5EFA PR";
3137
- console.warn(`[apm] WebIDE PR\uFF1A${tip}`);
3138
- return {
3139
- ...empty(),
3140
- failed: 1,
3141
- tips: [tip],
3142
- items: [{ label: "\u5DE5\u4F5C\u533A", outcome: "failed", detail: tip }]
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 expectedHead = `feat/task-${taskId}`;
3146
- console.log(
3147
- `[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`
2884
+ const api = createApmApiClient(cfg2);
2885
+ const { projectId, diagnostic } = await resolveProjectIdForSync(
2886
+ api,
2887
+ workdirPath,
2888
+ options
3148
2889
  );
3149
- let ensured = 0;
3150
- let skipped = 0;
3151
- let skippedNoChanges = 0;
3152
- let failed = 0;
3153
- const tips = [];
3154
- const items = [];
3155
- for (const gitRoot of repoRoots) {
3156
- const label = formatRepoLabel(workdir, gitRoot);
3157
- try {
3158
- const current = await getCurrentBranch(gitRoot);
3159
- if (!isFeatureBranchName(current)) {
3160
- const tip = `${label}\uFF1A\u5F53\u524D\u5206\u652F ${current} \u4E0D\u662F feat/task-*\uFF0C\u4E0D\u7B26\u5408\u521B\u5EFA PR \u6761\u4EF6`;
3161
- console.warn(`[apm] WebIDE PR\uFF1A${tip}`);
3162
- tips.push(tip);
3163
- skipped += 1;
3164
- items.push({
3165
- label,
3166
- outcome: "skipped",
3167
- headBranch: current,
3168
- detail: tip
3169
- });
3170
- continue;
3171
- }
3172
- if (current !== expectedHead) {
3173
- const tip = `${label}\uFF1A\u5F53\u524D\u4E3A ${current}\uFF0C\u671F\u671B ${expectedHead}\uFF0C\u5DF2\u8DF3\u8FC7`;
3174
- console.warn(`[apm] WebIDE PR\uFF1A${tip}`);
3175
- tips.push(tip);
3176
- skipped += 1;
3177
- items.push({
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
- } catch (err) {
3241
- failed += 1;
3242
- const tip = `${label}\uFF1A${err instanceof Error ? err.message : err}`;
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
- return { ensured, skipped, skippedNoChanges, failed, items, tips };
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 join4 } from "path";
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 = join4(dir, "..", "package.json");
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/sync-webide-attachments.ts
3271
- import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
3272
- import { join as join5 } from "path";
3273
- var MANIFEST_FILE = ".sync-manifest.json";
3274
- async function downloadAttachment(cfg, attachmentId) {
3275
- const base = cfg.baseUrl.trim().replace(/\/+$/, "");
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(dir) {
3288
- const path3 = join5(dir, MANIFEST_FILE);
3289
- if (!existsSync7(path3)) {
3290
- return { version: 1, attachments: {} };
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 && parsed.attachments && typeof parsed.attachments === "object") {
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 { version: 1, attachments: {} };
3035
+ return null;
3302
3036
  }
3303
- function saveManifest(dir, manifest) {
3037
+ function saveManifest(apmDir, cliVersion) {
3038
+ const manifest = { version: 1, cliVersion };
3304
3039
  writeFileSync4(
3305
- join5(dir, MANIFEST_FILE),
3040
+ toFsPath(manifestPath2(apmDir)),
3306
3041
  `${JSON.stringify(manifest, null, 2)}
3307
3042
  `,
3308
3043
  "utf8"
3309
3044
  );
3310
3045
  }
3311
- function isAttachmentUpToDate(entry, item, dest) {
3312
- if (!entry || !existsSync7(dest)) return false;
3313
- if (entry.name !== item.name) return false;
3314
- const createdAt = item.createdAt ?? "";
3315
- return entry.createdAt === createdAt;
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 manifest = loadManifest(dir);
3324
- const nextManifest = { version: 1, attachments: {} };
3325
- const names = [];
3326
- for (const item of attachments) {
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
- saveManifest(dir, nextManifest);
3345
- return names;
3057
+ return true;
3346
3058
  }
3347
- async function syncWebIdeAttachments(cfg, taskId, workdir, attachments) {
3348
- const dir = webideAttachmentsDir(taskId, workdir);
3349
- return syncAttachmentsToDirectory(
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/sync-webide-design.ts
3358
- import { createHash as createHash2 } from "crypto";
3359
- import { existsSync as existsSync8, readdirSync as readdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
3360
- import { join as join6 } from "path";
3361
- var MANIFEST_FILE2 = ".sync-manifest.json";
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 loadManifest2(dir) {
3366
- const path3 = join6(dir, MANIFEST_FILE2);
3367
- if (!existsSync8(path3)) {
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 saveManifest2(dir, manifest) {
3380
- writeFileSync5(
3381
- join6(dir, MANIFEST_FILE2),
3382
- `${JSON.stringify(manifest, null, 2)}
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
- async function downloadDesignFile(cfg, taskId, name) {
3388
- const base = cfg.baseUrl.trim().replace(/\/+$/, "");
3389
- const apiKey = resolveApiKey(cfg);
3390
- const params = new URLSearchParams({ taskId, name });
3391
- if (apiKey) params.set("apiKey", apiKey);
3392
- const url = `${base}/api/v1/cli/webide/design-artifacts/file?${params}`;
3393
- const res = await fetch(url);
3394
- if (!res.ok) {
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 listLocalDesignHtmlFiles(taskId, workdir) {
3423
- const dir = webideDesignDir(taskId, workdir);
3424
- if (!existsSync8(dir)) return [];
3425
- const out = [];
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
- return out;
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 api = createApmApiClient(cfg);
3451
- const remote = await api.cli.webideListDesignArtifacts({ taskId }) ?? [];
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
- let uploaded = 0;
3458
- let skipped = 0;
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
- return { uploaded, skipped, artifacts };
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/utils/project-documents.ts
3490
- import {
3491
- existsSync as existsSync9,
3492
- readdirSync as readdirSync4,
3493
- readFileSync as readFileSync8,
3494
- rmSync as rmSync2,
3495
- writeFileSync as writeFileSync6
3496
- } from "fs";
3497
- import { createHash as createHash3 } from "crypto";
3498
- import { dirname as dirname5, join as join7, relative as relative4, sep } from "path";
3499
- var MANIFEST_FILE3 = "manifest.json";
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 readLocalManifest(apmRoot, projectId) {
3536
- const manifestPath3 = join7(
3537
- projectDocumentsDir(apmRoot, projectId),
3538
- MANIFEST_FILE3
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
- if (!existsSync9(manifestPath3)) {
3541
- return null;
3542
- }
3543
- try {
3544
- return JSON.parse(
3545
- readFileSync8(manifestPath3, "utf8")
3546
- );
3547
- } catch {
3548
- return null;
3549
- }
3550
- }
3551
- function listLocalDocumentPaths(apmRoot, projectId) {
3552
- const root = projectDocumentsDir(apmRoot, projectId);
3553
- if (!existsSync9(root)) {
3554
- return [];
3555
- }
3556
- const paths = [];
3557
- const walk = (dir) => {
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
- const rel = relative4(root, abs).split(sep).join("/");
3568
- paths.push(rel);
3569
- }
3150
+ )
3570
3151
  };
3571
- walk(root);
3572
- return paths.sort();
3573
- }
3574
- function diffManifestPaths(remote, local) {
3575
- const remoteMap = new Map(
3576
- (remote?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
3577
- );
3578
- const localMap = new Map(
3579
- (local?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
3580
- );
3581
- const download = [];
3582
- for (const [path3, hash] of remoteMap) {
3583
- if (localMap.get(path3) !== hash) {
3584
- download.push(path3);
3585
- }
3586
- }
3587
- const deleteLocal = [];
3588
- for (const path3 of localMap.keys()) {
3589
- if (!remoteMap.has(path3)) {
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
- const api = createApmApiClient(cfg);
3989
- let projectId = null;
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
- try {
3994
- const baseline = await api.cli.projectBaseBranch({ taskId });
3995
- projectId = baseline.projectId?.trim() || null;
3996
- if (!projectId) {
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 ("reason" in result && result.reason) {
4020
- return `\u8DF3\u8FC7\uFF1A${result.reason}`;
3235
+ if (!result.synced) {
3236
+ return result.projectId ? "\u65E0\u9700\u540C\u6B65\u6216\u8DF3\u8FC7" : "\u8DF3\u8FC7\uFF1A\u65E0 projectId";
4021
3237
  }
4022
- if (!result.synced) return "\u65E0\u9700\u540C\u6B65";
4023
- return `downloaded=${result.downloaded ?? 0} deleted=${result.deleted ?? 0}`;
3238
+ return `downloaded=${result.downloaded} deleted=${result.deleted}`;
4024
3239
  }
4025
3240
  );
4026
- await runPrepStep(
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
- const startLocalServices = isStartLocalServicesAction(msg.action);
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
- cfg,
3245
+ cfg2,
4092
3246
  {
4093
3247
  messageId,
4094
- prompt: msg.content,
4095
- model: msg.model,
4096
- apiKey: msg.apiKey,
3248
+ prompt: msg2.content,
3249
+ model: msg2.model,
3250
+ apiKey: msg2.apiKey,
4097
3251
  workdir,
4098
- user: msg.user || "webide",
4099
- resumeAgentId: savedAgentId
3252
+ user: msg2.user || "webide"
4100
3253
  },
4101
3254
  {
4102
3255
  signal,
4103
3256
  forceSend: true,
4104
3257
  eventSession,
4105
- appendMessageContent: (content) => appendContent(cfg, messageId, content),
4106
- askQuestionExecute: startLocalServices ? void 0 : createWebIdeAskQuestionExecute({
4107
- cfg,
4108
- taskId,
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
- onInvalidatePersistedAgentId: startLocalServices ? void 0 : () => {
4118
- if (designAction) {
4119
- clearWebIdeDesignAgentId(workdir, taskId);
4120
- } else {
4121
- clearWebIdeAgentId(workdir, taskId);
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
- cfg,
4129
- { taskId, messageId, agentId },
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 Cursor Agent",
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 ?? (msg.model?.trim() || void 0),
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 (executeSql && sqlExecutionId) {
4175
- try {
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
- if (executeSql && sqlExecutionId) {
4200
- try {
4201
- await api.cli.webideFinalizeSqlExecution({
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
- const fallback = resolveMessageReplyFallback({
4218
- assistantText: outcome.assistantText,
4219
- result: outcome.result,
4220
- createPlan: outcome.createPlan
4221
- });
4222
- await api.cli.webideEnsureMessageContent({
4223
- id: messageId,
4224
- content: fallback
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
- if ((msg.action === "write-plan" || msg.action === "revise-plan") && outcome.createPlan && outcome.createPlan.trim()) {
4227
- try {
4228
- await api.cli.webideUpsertPlan({
4229
- taskId,
4230
- content: outcome.createPlan.trim()
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
- } catch (err) {
4233
- console.warn(
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
- if (isDesignAction(msg.action)) {
4240
- if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
4241
- try {
4242
- const uploaded = await uploadWebIdeDesignArtifacts(
4243
- cfg,
4244
- taskId,
4245
- workdir
4246
- );
4247
- console.log(
4248
- `[apm] webide \u8BBE\u8BA1\u4EA7\u7269\u4E0A\u4F20 action=${msg.action} uploaded=${uploaded.uploaded} skipped=${uploaded.skipped} files=${uploaded.artifacts.map((a) => a.name).join(",")}`
4249
- );
4250
- } catch (err) {
4251
- const detail = err instanceof Error ? err.message : String(err);
4252
- await logSyncRef.current?.markRun(
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/handle-start-project.ts
4371
- async function updateStatus2(cfg, messageId, status) {
4372
- const api = createApmApiClient(cfg);
3398
+ // src/commands/connect/core/worker/handlers/start-project.ts
3399
+ async function updateStatus(cfg2, messageId, status) {
3400
+ const api = createApmApiClient(cfg2);
4373
3401
  await api.cli.webideUpdateMessageStatus({ id: messageId, status });
4374
3402
  }
4375
- async function setError2(cfg, messageId, error) {
4376
- const api = createApmApiClient(cfg);
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(cfg, msg, signal) {
4380
- const workdir = requireRemoteWorkdir(msg.workdir);
4381
- const messageId = msg.messageId;
4382
- const taskId = msg.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=${msg.target} restart=${msg.restart} taskId=${taskId} messageId=${messageId}`
3412
+ `[apm] webide-start-project target=${msg2.target} restart=${msg2.restart} taskId=${taskId} messageId=${messageId}`
4385
3413
  );
4386
- await updateStatus2(cfg, messageId, "TYPING");
4387
- const eventSession = new EventSession(msg.content);
3414
+ await updateStatus(cfg2, messageId, "TYPING");
3415
+ const eventSession = new EventSession(msg2.content);
4388
3416
  const logSyncRef = {
4389
3417
  current: createThrottledWebIdeMessageLogSync(
4390
- cfg,
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(cfg);
3431
+ const api = createApmApiClient(cfg2);
4404
3432
  const outcome = await runCursorAgent(
4405
- cfg,
3433
+ cfg2,
4406
3434
  {
4407
3435
  messageId,
4408
- prompt: msg.content,
4409
- model: msg.model,
4410
- apiKey: msg.apiKey,
3436
+ prompt: msg2.content,
3437
+ model: msg2.model,
3438
+ apiKey: msg2.apiKey,
4411
3439
  workdir,
4412
- user: msg.user || "webide"
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
- cfg,
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 ?? (msg.model?.trim() || void 0),
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 setError2(cfg, messageId, detail);
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 updateStatus2(cfg, messageId, "CANCELLED");
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
- const fallback = resolveMessageReplyFallback({
4482
- assistantText: outcome.assistantText,
4483
- result: outcome.result
4315
+ await client.fPutObject(bucket, objectKey, file.absPath, {
4316
+ "Content-Type": "text/html; charset=utf-8"
4484
4317
  });
4485
- await api.cli.webideEnsureMessageContent({
4486
- id: messageId,
4487
- content: fallback
4318
+ uploaded += 1;
4319
+ artifacts.push({
4320
+ name: file.name,
4321
+ objectKey,
4322
+ contentHash: file.contentHash
4488
4323
  });
4489
- await logSyncRef.current?.markRun(
4490
- outcome.runId,
4491
- "finished",
4492
- null,
4493
- tokenUsage
4494
- );
4495
- await updateStatus2(cfg, messageId, "SUCCESS");
4496
- console.log(
4497
- `[apm] webide-start-project \u5B8C\u6210 target=${msg.target} messageId=${messageId} agentId=${outcome.agentId}`
4498
- );
4499
- } catch (err) {
4500
- const detail = err instanceof Error ? err.message : String(err);
4501
- console.error(`[apm] webide-start-project \u5931\u8D25: ${detail}`);
4502
- try {
4503
- await setError2(cfg, messageId, detail);
4504
- } catch (statusErr) {
4505
- console.error(
4506
- "[apm] \u5199\u5165\u542F\u52A8\u9879\u76EE FAILED \u5931\u8D25:",
4507
- statusErr instanceof Error ? statusErr.message : statusErr
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/upload-package-artifact.ts
4514
- import fs2 from "node:fs";
4515
- import path2 from "node:path";
4516
- function packageTmpDir(packageId) {
4517
- return `/data/package-tmp/${packageId}`;
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 expectedLocalArtifactPath(target, packageId) {
4520
- const fileName = target === "frontend" ? "dist.zip" : "jars.zip";
4521
- return path2.join(packageTmpDir(packageId), fileName);
4361
+ function isStartLocalServicesAction(_action) {
4362
+ return false;
4522
4363
  }
4523
- function packageArtifactObjectKey(target, projectId, timestampMs = Date.now()) {
4524
- const folder = target === "frontend" ? "vue" : "java";
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 removePackageTmpDir(packageId) {
4529
- const dir = packageTmpDir(packageId);
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
- async function uploadPackageArtifact(cfg, target, packageId, projectId) {
4540
- const localPath = expectedLocalArtifactPath(target, packageId);
4541
- if (!fs2.existsSync(localPath)) {
4542
- throw new Error(`\u6253\u5305\u4EA7\u7269\u4E0D\u5B58\u5728: ${localPath}`);
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
- // src/commands/connect/handle-package.ts
4561
- async function updatePackageStatus(cfg, packageId, status, extra) {
4562
- const api = createApmApiClient(cfg);
4563
- await api.cli.projectPackageUpdateStatus({
4564
- id: packageId,
4565
- status,
4566
- ...extra?.error != null ? { error: extra.error } : {},
4567
- ...extra?.content != null ? { content: extra.content } : {},
4568
- ...extra?.artifactPath != null ? { artifactPath: extra.artifactPath } : {},
4569
- ...extra?.artifactUrl != null ? { artifactUrl: extra.artifactUrl } : {}
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 handleWebIdePackage(cfg, msg, signal) {
4573
- const workdir = requireRemoteWorkdir(msg.workdir);
4574
- const messageId = msg.messageId;
4575
- const packageId = msg.packageId;
4576
- const projectId = msg.projectId;
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-package target=${msg.target} packageId=${packageId} projectId=${projectId} baseBranch=${msg.baseBranch}`
4395
+ `[apm] webide-message action=${msg2.action} taskId=${taskId} messageId=${messageId}`
4579
4396
  );
4580
- await updatePackageStatus(cfg, packageId, "RUNNING");
4581
- const eventSession = new EventSession(msg.content);
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
- cfg,
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 \u6253\u5305\u65E5\u5FD7\u540C\u6B65\u5931\u8D25:",
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
- await runPrepStep(
4670
- "\u5207\u6362\u57FA\u7EBF\u5E76\u62C9\u53D6\u6700\u65B0",
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
- const docs = await syncProjectDocumentsPull(workdir, void 0, {
4679
- projectId
4680
- });
4681
- return docs;
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 (!result.synced) {
4685
- return result.projectId ? "\u65E0\u9700\u540C\u6B65\u6216\u8DF3\u8FC7" : "\u8DF3\u8FC7\uFF1A\u65E0 projectId";
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
- return `downloaded=${result.downloaded} deleted=${result.deleted}`;
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
- let failedByTool = false;
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
- cfg,
4626
+ cfg2,
4695
4627
  {
4696
4628
  messageId,
4697
- prompt: msg.content,
4698
- model: msg.model,
4699
- apiKey: msg.apiKey,
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: msg.user || "webide"
4636
+ user: msg2.user || "webide",
4637
+ resumeAgentId: savedAgentId
4702
4638
  },
4703
4639
  {
4704
4640
  signal,
4705
4641
  forceSend: true,
4706
4642
  eventSession,
4707
- enableAppendMessage: false,
4708
- enableAskQuestion: false,
4709
- enableWebIdePlanTools: false,
4710
- enablePtySessionMcp: false,
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
- enablePackageStatusTools: true,
4713
- packageId,
4714
- projectId,
4715
- onPackageFailed: async () => {
4716
- failedByTool = true;
4657
+ onInvalidatePersistedAgentId: startLocalServices ? void 0 : () => {
4658
+ if (designAction) {
4659
+ clearWebIdeDesignAgentId(workdir, taskId);
4660
+ } else {
4661
+ clearWebIdeAgentId(workdir, taskId);
4662
+ }
4717
4663
  },
4718
- taskId: `package:${packageId}`,
4664
+ taskId,
4719
4665
  createRemoteLogSync: (agentId) => {
4666
+ persistAgentId(agentId);
4720
4667
  logSyncRef.current = createThrottledWebIdeMessageLogSync(
4721
- cfg,
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 \u6253\u5305\u65E5\u5FD7\u540C\u6B65\u5931\u8D25:",
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\u6253\u5305 Agent",
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 ?? (msg.model?.trim() || void 0),
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 (!failedByTool) {
4771
- await updatePackageStatus(cfg, packageId, "FAILED", { error: detail });
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
- await updatePackageStatus(cfg, packageId, "CANCELLED", {
4783
- error: "\u5DF2\u53D6\u6D88"
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
- if (failedByTool) {
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] webide-package Agent \u5DF2\u6807 FAILED packageId=${packageId}`
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
- const uploaded = await runPrepStep(
4800
- "\u4E0A\u4F20\u6253\u5305\u4EA7\u7269\u5230 MinIO",
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-package \u5B8C\u6210 target=${msg.target} packageId=${packageId} agentId=${outcome.agentId} artifact=${uploaded.artifactPath}`
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-package \u5931\u8D25: ${detail}`);
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
- if (signal.aborted) {
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\u6253\u5305 FAILED \u5931\u8D25:",
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/webide-message-worker.ts
4832
- var controllers = /* @__PURE__ */ new Map();
4833
- async function runJob(cfg, msg) {
4834
- const controller = new AbortController();
4835
- controllers.set(msg.messageId, controller);
4836
- try {
4837
- if (msg.type === "webide-start-project") {
4838
- await handleStartProject(cfg, msg, controller.signal);
4839
- } else if (msg.type === "webide-package") {
4840
- await handleWebIdePackage(cfg, msg, controller.signal);
4841
- } else {
4842
- await handleWebIdeInboundMessage(cfg, msg, controller.signal);
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
- parentPort?.on("message", (raw) => {
4859
- if (raw.type === "cancel") {
4860
- controllers.get(raw.messageId)?.abort();
4861
- return;
4862
- }
4863
- if (raw.type === "run") {
4864
- void runJob(raw.cfg, raw.msg);
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 boot = workerData;
4868
- if (boot?.cfg && boot?.msg) {
4869
- void runJob(boot.cfg, boot.msg);
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
  }