ai-project-manage-cli 8.0.7 → 8.0.9

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.
@@ -58,6 +58,14 @@ var requestConfig = {
58
58
  method: "PUT",
59
59
  path: "/cli/webide/test-cases"
60
60
  }),
61
+ webideUpsertSqlExecution: defineEndpoint({
62
+ method: "PUT",
63
+ path: "/cli/webide/sql-executions"
64
+ }),
65
+ webideFinalizeSqlExecution: defineEndpoint({
66
+ method: "PUT",
67
+ path: "/cli/webide/sql-executions/finalize"
68
+ }),
61
69
  projectBaseBranch: defineEndpoint(
62
70
  {
63
71
  method: "GET",
@@ -91,6 +99,18 @@ var requestConfig = {
91
99
  getApmLogStorage: defineEndpoint({
92
100
  method: "GET",
93
101
  path: "/cli/apm-log-storage"
102
+ }),
103
+ projectPackageUpdateStatus: defineEndpoint({
104
+ method: "PUT",
105
+ path: "/cli/project-packages/status"
106
+ }),
107
+ projectPackageAppendContent: defineEndpoint({
108
+ method: "PUT",
109
+ path: "/cli/project-packages/content"
110
+ }),
111
+ projectPackageEnsureContent: defineEndpoint({
112
+ method: "PUT",
113
+ path: "/cli/project-packages/ensure-content"
94
114
  })
95
115
  }
96
116
  };
@@ -934,6 +954,55 @@ async function runTaskBranch(taskId, options = {}) {
934
954
  repos: repoRoots
935
955
  };
936
956
  }
957
+ async function checkoutBaselineBranches(baselineBranch, options = {}) {
958
+ const baseline = baselineBranch.trim().replace(/^origin\//, "");
959
+ if (!baseline) {
960
+ throw new Error("[apm] \u57FA\u7EBF\u5206\u652F\u4E0D\u80FD\u4E3A\u7A7A");
961
+ }
962
+ const cwd = options.cwd ?? process.cwd();
963
+ const workdirPath = resolveWorkdirPath(cwd);
964
+ const manifest = loadWorkspaceReposCache(workdirPath);
965
+ const repoRoots = resolveWorkspaceRepoAbsolutePaths(manifest);
966
+ for (const gitRoot of repoRoots) {
967
+ const label = formatRepoLabel(workdirPath, gitRoot);
968
+ console.log(`[apm] \u5207\u6362\u57FA\u7EBF\u5206\u652F ${baseline} @ ${label}`);
969
+ await checkoutBaselineBranch(baseline, { ...options, cwd: gitRoot });
970
+ }
971
+ return {
972
+ baseline,
973
+ kind: manifest.kind,
974
+ repos: repoRoots
975
+ };
976
+ }
977
+ async function checkoutBaselineBranch(baselineBranch, options = {}) {
978
+ const baseline = baselineBranch.trim().replace(/^origin\//, "");
979
+ if (!baseline) {
980
+ throw new Error("[apm] \u57FA\u7EBF\u5206\u652F\u4E0D\u80FD\u4E3A\u7A7A");
981
+ }
982
+ const cwd = options.cwd ?? process.cwd();
983
+ const gitRoot = await resolveGitRepoRoot(cwd);
984
+ await ensureGitRepo(gitRoot);
985
+ if (await isWorkingTreeDirty(gitRoot)) {
986
+ throw new Error(
987
+ `[apm] \u5DE5\u4F5C\u533A\u6709\u672A\u63D0\u4EA4\u53D8\u66F4\uFF0C\u65E0\u6CD5\u5207\u6362\u5230\u57FA\u7EBF\u5206\u652F ${baseline}`
988
+ );
989
+ }
990
+ await ensureRemoteBaselineBranch(gitRoot, baseline);
991
+ const current = await getCurrentBranch(gitRoot);
992
+ if (current === baseline) {
993
+ await execGit(gitRoot, ["reset", "--hard", `origin/${baseline}`]);
994
+ console.log(`[apm] \u5DF2\u5728\u57FA\u7EBF ${baseline}\uFF0C\u5DF2\u5BF9\u9F50 origin/${baseline}`);
995
+ return baseline;
996
+ }
997
+ await execGit(gitRoot, [
998
+ "checkout",
999
+ "-B",
1000
+ baseline,
1001
+ `origin/${baseline}`
1002
+ ]);
1003
+ console.log(`[apm] \u5DF2\u5207\u6362\u5230\u57FA\u7EBF\u5206\u652F ${baseline}`);
1004
+ return baseline;
1005
+ }
937
1006
 
938
1007
  // src/commands/connect/cursor-agent.ts
939
1008
  import {
@@ -1515,6 +1584,325 @@ function createUpsertWebIdeTestCasesTool(options) {
1515
1584
  };
1516
1585
  }
1517
1586
 
1587
+ // src/commands/connect/tools/mysql-tools.ts
1588
+ import { existsSync as existsSync3, readFileSync as readFileSync3, statSync as statSync3 } from "node:fs";
1589
+ import { isAbsolute, relative as relative2, resolve as resolve4 } from "node:path";
1590
+ import mysql from "mysql2/promise";
1591
+ var MAX_RESULT_ROWS = 100;
1592
+ var EXEC_TIMEOUT_MS = 6e4;
1593
+ var MAX_SQL_FILE_BYTES = 2 * 1024 * 1024;
1594
+ function asString3(value) {
1595
+ return typeof value === "string" ? value.trim() : "";
1596
+ }
1597
+ function asNumber(value) {
1598
+ if (typeof value === "number" && Number.isFinite(value)) return value;
1599
+ if (typeof value === "string" && value.trim()) {
1600
+ const parsed = Number(value);
1601
+ if (Number.isFinite(parsed)) return parsed;
1602
+ }
1603
+ return void 0;
1604
+ }
1605
+ function isResultSetHeader(value) {
1606
+ return typeof value === "object" && value !== null && "affectedRows" in value && !Array.isArray(value);
1607
+ }
1608
+ function formatOkPacket(packet) {
1609
+ return {
1610
+ kind: "update",
1611
+ affectedRows: packet.affectedRows ?? 0,
1612
+ insertId: packet.insertId ?? 0,
1613
+ changedRows: packet.changedRows,
1614
+ warnings: packet.warningStatus
1615
+ };
1616
+ }
1617
+ function formatSelectRows(rows) {
1618
+ const truncated = rows.slice(0, MAX_RESULT_ROWS);
1619
+ return {
1620
+ kind: "select",
1621
+ rowCount: rows.length,
1622
+ truncated: rows.length > MAX_RESULT_ROWS,
1623
+ rows: truncated
1624
+ };
1625
+ }
1626
+ function formatStatementResult(result) {
1627
+ if (Array.isArray(result)) {
1628
+ return formatSelectRows(result);
1629
+ }
1630
+ if (isResultSetHeader(result)) {
1631
+ return formatOkPacket(result);
1632
+ }
1633
+ return { kind: "unknown", value: result };
1634
+ }
1635
+ function normalizeMultiStatementResults(raw) {
1636
+ if (!Array.isArray(raw)) {
1637
+ return [formatStatementResult(raw)];
1638
+ }
1639
+ if (raw.length === 0) {
1640
+ return [];
1641
+ }
1642
+ const first = raw[0];
1643
+ if (Array.isArray(first)) {
1644
+ return raw.map((item) => {
1645
+ if (Array.isArray(item)) {
1646
+ return formatStatementResult(item[0]);
1647
+ }
1648
+ return formatStatementResult(item);
1649
+ });
1650
+ }
1651
+ if (isResultSetHeader(first)) {
1652
+ return raw.map((item) => formatStatementResult(item));
1653
+ }
1654
+ return [formatStatementResult(raw)];
1655
+ }
1656
+ function resolveSqlArtifactPath(workdir, absolutePath) {
1657
+ if (!isAbsolute(absolutePath)) {
1658
+ return { ok: false, error: "path \u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84" };
1659
+ }
1660
+ const workdirAbs = resolve4(toFsPath(workdir));
1661
+ const targetAbs = resolve4(toFsPath(absolutePath));
1662
+ const rel = relative2(workdirAbs, targetAbs);
1663
+ if (!rel || rel.startsWith("..") || isAbsolute(rel)) {
1664
+ return { ok: false, error: "path \u5FC5\u987B\u4F4D\u4E8E\u5F53\u524D\u5DE5\u4F5C\u533A\u5185" };
1665
+ }
1666
+ const fsPath = toFsPath(targetAbs);
1667
+ if (!existsSync3(fsPath)) {
1668
+ return { ok: false, error: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${absolutePath}` };
1669
+ }
1670
+ let st;
1671
+ try {
1672
+ st = statSync3(fsPath);
1673
+ } catch {
1674
+ return { ok: false, error: `\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6: ${absolutePath}` };
1675
+ }
1676
+ if (!st.isFile()) {
1677
+ return { ok: false, error: "path \u5FC5\u987B\u6307\u5411\u666E\u901A\u6587\u4EF6" };
1678
+ }
1679
+ if (st.size > MAX_SQL_FILE_BYTES) {
1680
+ return {
1681
+ ok: false,
1682
+ error: `SQL \u6587\u4EF6\u8FC7\u5927\uFF08>${MAX_SQL_FILE_BYTES} bytes\uFF09`
1683
+ };
1684
+ }
1685
+ const displayPath = normalizeWorkdirPath(rel) || normalizeWorkdirPath(absolutePath);
1686
+ return { ok: true, absPath: targetAbs, displayPath };
1687
+ }
1688
+ function readSqlFile(absPath) {
1689
+ return readFileSync3(toFsPath(absPath), "utf8");
1690
+ }
1691
+ function createMysqlExecuteTool(options) {
1692
+ const { taskId, executionId, workdir, upsertSqlExecution } = options;
1693
+ return {
1694
+ MysqlExecute: {
1695
+ description: "Execute SQL from a workspace artifact file against MySQL. Pass the absolute filesystem path of the SQL file (not the SQL text). The CLI reads the file itself to avoid escaping issues with multi-statement scripts.",
1696
+ inputSchema: {
1697
+ type: "object",
1698
+ properties: {
1699
+ host: { type: "string", description: "MySQL host" },
1700
+ port: { type: "number", description: "MySQL port, default 3306" },
1701
+ user: { type: "string", description: "MySQL username" },
1702
+ password: { type: "string", description: "MySQL password" },
1703
+ database: { type: "string", description: "Database name" },
1704
+ path: {
1705
+ type: "string",
1706
+ description: "Absolute filesystem path to the SQL artifact file under the current workspace (CLI reads the file; do not pass SQL text)"
1707
+ }
1708
+ },
1709
+ required: ["host", "user", "password", "database", "path"]
1710
+ },
1711
+ execute: async (args) => {
1712
+ const host = asString3(args.host);
1713
+ const user = asString3(args.user);
1714
+ const password = asString3(args.password);
1715
+ const database = asString3(args.database);
1716
+ const pathArg = asString3(args.path);
1717
+ const port = asNumber(args.port) ?? 3306;
1718
+ if (!host || !user || !database || !pathArg) {
1719
+ return {
1720
+ content: [
1721
+ {
1722
+ type: "text",
1723
+ text: "host / user / database / path \u5747\u4E0D\u80FD\u4E3A\u7A7A"
1724
+ }
1725
+ ],
1726
+ isError: true
1727
+ };
1728
+ }
1729
+ const resolved = resolveSqlArtifactPath(workdir, pathArg);
1730
+ if (!resolved.ok) {
1731
+ try {
1732
+ await upsertSqlExecution({
1733
+ executionId,
1734
+ taskId,
1735
+ status: "FAILED",
1736
+ artifactPath: pathArg,
1737
+ error: resolved.error,
1738
+ host,
1739
+ port,
1740
+ database,
1741
+ dbUser: user
1742
+ });
1743
+ } catch {
1744
+ }
1745
+ return {
1746
+ content: [{ type: "text", text: resolved.error }],
1747
+ isError: true
1748
+ };
1749
+ }
1750
+ let sql;
1751
+ try {
1752
+ sql = readSqlFile(resolved.absPath).trim();
1753
+ } catch (err) {
1754
+ const detail = err instanceof Error ? err.message : String(err);
1755
+ try {
1756
+ await upsertSqlExecution({
1757
+ executionId,
1758
+ taskId,
1759
+ status: "FAILED",
1760
+ artifactPath: resolved.displayPath,
1761
+ error: `\u8BFB\u53D6 SQL \u6587\u4EF6\u5931\u8D25: ${detail}`,
1762
+ host,
1763
+ port,
1764
+ database,
1765
+ dbUser: user
1766
+ });
1767
+ } catch {
1768
+ }
1769
+ return {
1770
+ content: [
1771
+ {
1772
+ type: "text",
1773
+ text: `\u8BFB\u53D6 SQL \u6587\u4EF6\u5931\u8D25: ${detail}`
1774
+ }
1775
+ ],
1776
+ isError: true
1777
+ };
1778
+ }
1779
+ if (!sql) {
1780
+ try {
1781
+ await upsertSqlExecution({
1782
+ executionId,
1783
+ taskId,
1784
+ status: "FAILED",
1785
+ artifactPath: resolved.displayPath,
1786
+ sql: "",
1787
+ error: "SQL \u6587\u4EF6\u4E3A\u7A7A",
1788
+ host,
1789
+ port,
1790
+ database,
1791
+ dbUser: user
1792
+ });
1793
+ } catch {
1794
+ }
1795
+ return {
1796
+ content: [{ type: "text", text: "SQL \u6587\u4EF6\u4E3A\u7A7A" }],
1797
+ isError: true
1798
+ };
1799
+ }
1800
+ let connection;
1801
+ try {
1802
+ connection = await mysql.createConnection({
1803
+ host,
1804
+ port,
1805
+ user,
1806
+ password,
1807
+ database,
1808
+ multipleStatements: true,
1809
+ connectTimeout: 15e3
1810
+ });
1811
+ const [rawResults] = await connection.query({
1812
+ sql,
1813
+ timeout: EXEC_TIMEOUT_MS
1814
+ });
1815
+ const statements = normalizeMultiStatementResults(rawResults);
1816
+ const resultPayload = {
1817
+ ok: true,
1818
+ path: resolved.displayPath,
1819
+ statements,
1820
+ host,
1821
+ port,
1822
+ database,
1823
+ dbUser: user
1824
+ };
1825
+ await upsertSqlExecution({
1826
+ executionId,
1827
+ taskId,
1828
+ status: "SUCCESS",
1829
+ artifactPath: resolved.displayPath,
1830
+ sql,
1831
+ result: resultPayload,
1832
+ host,
1833
+ port,
1834
+ database,
1835
+ dbUser: user
1836
+ });
1837
+ return JSON.stringify(resultPayload, null, 2);
1838
+ } catch (err) {
1839
+ const detail = err instanceof Error ? err.message : String(err);
1840
+ try {
1841
+ await upsertSqlExecution({
1842
+ executionId,
1843
+ taskId,
1844
+ status: "FAILED",
1845
+ artifactPath: resolved.displayPath,
1846
+ sql,
1847
+ error: detail,
1848
+ host: host || void 0,
1849
+ port,
1850
+ database: database || void 0,
1851
+ dbUser: user || void 0
1852
+ });
1853
+ } catch {
1854
+ }
1855
+ return {
1856
+ content: [{ type: "text", text: `MySQL \u6267\u884C\u5931\u8D25: ${detail}` }],
1857
+ isError: true
1858
+ };
1859
+ } finally {
1860
+ await connection?.end().catch(() => void 0);
1861
+ }
1862
+ }
1863
+ }
1864
+ };
1865
+ }
1866
+
1867
+ // src/commands/connect/tools/package-status-tool.ts
1868
+ function createSetPackageFailedTool(options) {
1869
+ const { packageId, setFailed } = options;
1870
+ return {
1871
+ SetPackageFailed: {
1872
+ description: "\u6253\u5305\u5931\u8D25\u65F6\u8C03\u7528\uFF1A\u5C06\u5F53\u524D\u6253\u5305\u8BB0\u5F55\u6807\u4E3A FAILED\uFF0C\u5E76\u5199\u5165\u5931\u8D25\u539F\u56E0\u3002\u6784\u5EFA\u5931\u8D25\u3001\u7F3A\u4EA7\u7269\u3001\u6587\u6863\u7F3A\u5931\u7B49\u5747\u5E94\u8C03\u7528\uFF1B\u6210\u529F\u65F6\u4E0D\u8981\u8C03\u7528\u3002",
1873
+ inputSchema: {
1874
+ type: "object",
1875
+ properties: {
1876
+ error: {
1877
+ type: "string",
1878
+ description: "\u5931\u8D25\u539F\u56E0\u6458\u8981\uFF08\u4F1A\u5199\u5165\u6253\u5305\u8BB0\u5F55\uFF0C\u4F9B\u5217\u8868/\u65E5\u5FD7\u5C55\u793A\uFF09"
1879
+ }
1880
+ },
1881
+ required: ["error"]
1882
+ },
1883
+ execute: async (args) => {
1884
+ const error = typeof args.error === "string" ? args.error.trim() : "";
1885
+ if (!error) {
1886
+ return {
1887
+ content: [{ type: "text", text: "error \u4E0D\u80FD\u4E3A\u7A7A" }],
1888
+ isError: true
1889
+ };
1890
+ }
1891
+ try {
1892
+ await setFailed(error);
1893
+ return `\u5DF2\u5C06\u6253\u5305\u8BB0\u5F55 ${packageId} \u6807\u4E3A FAILED`;
1894
+ } catch (err) {
1895
+ const detail = err instanceof Error ? err.message : String(err);
1896
+ return {
1897
+ content: [{ type: "text", text: `\u5199\u5165\u5931\u8D25\u72B6\u6001\u5931\u8D25: ${detail}` }],
1898
+ isError: true
1899
+ };
1900
+ }
1901
+ }
1902
+ }
1903
+ };
1904
+ }
1905
+
1518
1906
  // src/commands/connect/tools/index.ts
1519
1907
  var PLAN_MODE_ASK_QUESTION_HINT = `[SDK \u73AF\u5883\u8BF4\u660E]
1520
1908
  AskQuestion \u5DF2\u901A\u8FC7 MCP \u670D\u52A1\u5668 custom-user-tools \u6CE8\u518C\uFF0C\u5DE5\u5177\u540D\u4E3A AskQuestion\u3002
@@ -1553,6 +1941,35 @@ function createCursorCustomTools(cfg, options) {
1553
1941
  })
1554
1942
  );
1555
1943
  }
1944
+ if (options.enableMysqlTools && options.sqlExecutionId && options.workdir) {
1945
+ Object.assign(
1946
+ tools,
1947
+ createMysqlExecuteTool({
1948
+ taskId,
1949
+ executionId: options.sqlExecutionId,
1950
+ workdir: options.workdir,
1951
+ upsertSqlExecution: (args) => cli.webideUpsertSqlExecution(args)
1952
+ })
1953
+ );
1954
+ }
1955
+ }
1956
+ if (options.enablePackageStatusTools && options.packageId) {
1957
+ const packageId = options.packageId;
1958
+ const { cli } = createApmApiClient(cfg);
1959
+ Object.assign(
1960
+ tools,
1961
+ createSetPackageFailedTool({
1962
+ packageId,
1963
+ setFailed: async (error) => {
1964
+ await cli.projectPackageUpdateStatus({
1965
+ id: packageId,
1966
+ status: "FAILED",
1967
+ error
1968
+ });
1969
+ await options.onPackageFailed?.(error);
1970
+ }
1971
+ })
1972
+ );
1556
1973
  }
1557
1974
  return tools;
1558
1975
  }
@@ -1671,8 +2088,13 @@ async function runCursorAgent(cfg, ctx, options) {
1671
2088
  enableAskQuestion: options.enableAskQuestion,
1672
2089
  askQuestionExecute: options.askQuestionExecute,
1673
2090
  enableWebIdePlanTools: options.enableWebIdePlanTools,
2091
+ enableMysqlTools: options.enableMysqlTools,
2092
+ sqlExecutionId: options.sqlExecutionId,
1674
2093
  taskId: options.taskId,
1675
- workdir
2094
+ workdir,
2095
+ enablePackageStatusTools: options.enablePackageStatusTools,
2096
+ packageId: options.packageId,
2097
+ onPackageFailed: options.onPackageFailed
1676
2098
  });
1677
2099
  const enableSandbox = Boolean(options.enableSandbox);
1678
2100
  const mcpServers = options.enablePtySessionMcp ? createPtySessionMcpServers(cfg) : void 0;
@@ -1805,17 +2227,17 @@ async function runCursorAgent(cfg, ctx, options) {
1805
2227
  }
1806
2228
 
1807
2229
  // src/commands/connect/webide-agent-registry.ts
1808
- import { existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
1809
- import { dirname as dirname3, resolve as resolve4 } from "node:path";
2230
+ import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
2231
+ import { dirname as dirname3, resolve as resolve5 } from "node:path";
1810
2232
  function registryPath(workdir, taskId) {
1811
- return resolve4(workdir, ".apm", "webide", taskId, "cursor-agent.json");
2233
+ return resolve5(workdir, ".apm", "webide", taskId, "cursor-agent.json");
1812
2234
  }
1813
2235
  function readRegistry(path) {
1814
- if (!existsSync3(path)) {
2236
+ if (!existsSync4(path)) {
1815
2237
  return {};
1816
2238
  }
1817
2239
  try {
1818
- const parsed = JSON.parse(readFileSync3(path, "utf8"));
2240
+ const parsed = JSON.parse(readFileSync4(path, "utf8"));
1819
2241
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
1820
2242
  const raw = parsed;
1821
2243
  const state = {};
@@ -1886,20 +2308,20 @@ function saveWebIdeAgentId(workdir, taskId, agentId) {
1886
2308
  }
1887
2309
  function clearWebIdeAgentId(workdir, taskId) {
1888
2310
  const path = registryPath(workdir, taskId);
1889
- if (!existsSync3(path)) return;
2311
+ if (!existsSync4(path)) return;
1890
2312
  syncWebIdeTaskState(workdir, taskId, { agentId: "" });
1891
2313
  }
1892
2314
 
1893
2315
  // src/commands/clean-webide-cache.ts
1894
- import { existsSync as existsSync4, rmSync } from "node:fs";
1895
- import { resolve as resolve5 } from "node:path";
2316
+ import { existsSync as existsSync5, rmSync } from "node:fs";
2317
+ import { resolve as resolve6 } from "node:path";
1896
2318
  import { getDefaultSdkStateRoot } from "@cursor/sdk";
1897
2319
  async function purgeCursorAgentStoreForAgent(workdir, agentId) {
1898
2320
  const trimmedAgentId = agentId.trim();
1899
2321
  const trimmedWorkdir = workdir.trim();
1900
2322
  if (!trimmedAgentId || !trimmedWorkdir) return false;
1901
2323
  const stateRoot = getDefaultSdkStateRoot(trimmedWorkdir);
1902
- if (!existsSync4(stateRoot)) return false;
2324
+ if (!existsSync5(stateRoot)) return false;
1903
2325
  const { SqliteLocalAgentStore } = await import(
1904
2326
  /* @vite-ignore */
1905
2327
  "@cursor/sdk/sqlite"
@@ -1963,8 +2385,8 @@ async function cleanWebIdeWorkspaceCache(taskId, workdir) {
1963
2385
  `[apm] \u65E0 WebIDE agentId\uFF0C\u8DF3\u8FC7 agent store \u6E05\u7406 taskId=${trimmedTaskId}`
1964
2386
  );
1965
2387
  }
1966
- const dir = resolve5(trimmedWorkdir, ".apm", "webide", trimmedTaskId);
1967
- if (existsSync4(dir)) {
2388
+ const dir = resolve6(trimmedWorkdir, ".apm", "webide", trimmedTaskId);
2389
+ if (existsSync5(dir)) {
1968
2390
  rmSync(dir, { recursive: true, force: true });
1969
2391
  console.log(`[apm] \u5DF2\u6E05\u7406 WebIDE \u7F13\u5B58 ${dir}`);
1970
2392
  } else {
@@ -1975,11 +2397,11 @@ async function cleanWebIdeWorkspaceCache(taskId, workdir) {
1975
2397
  // src/commands/connect/webide-ask-question.ts
1976
2398
  import { setTimeout as delay } from "node:timers/promises";
1977
2399
  var POLL_INTERVAL_MS = 2e3;
1978
- function asString3(value) {
2400
+ function asString4(value) {
1979
2401
  return typeof value === "string" ? value.trim() : "";
1980
2402
  }
1981
2403
  function parseQuestions(args) {
1982
- const title = asString3(args.title) || void 0;
2404
+ const title = asString4(args.title) || void 0;
1983
2405
  const raw = args.questions;
1984
2406
  if (!Array.isArray(raw) || raw.length === 0) {
1985
2407
  throw new Error("AskQuestion \u7F3A\u5C11 questions");
@@ -1988,16 +2410,16 @@ function parseQuestions(args) {
1988
2410
  for (const item of raw) {
1989
2411
  if (!item || typeof item !== "object" || Array.isArray(item)) continue;
1990
2412
  const row = item;
1991
- const id = asString3(row.id);
1992
- const prompt = asString3(row.prompt);
2413
+ const id = asString4(row.id);
2414
+ const prompt = asString4(row.prompt);
1993
2415
  const optionsRaw = row.options;
1994
2416
  if (!id || !prompt || !Array.isArray(optionsRaw)) continue;
1995
2417
  const options = [];
1996
2418
  for (const opt of optionsRaw) {
1997
2419
  if (!opt || typeof opt !== "object" || Array.isArray(opt)) continue;
1998
2420
  const o = opt;
1999
- const oid = asString3(o.id);
2000
- const label = asString3(o.label);
2421
+ const oid = asString4(o.id);
2422
+ const label = asString4(o.label);
2001
2423
  if (oid && label) options.push({ id: oid, label });
2002
2424
  }
2003
2425
  if (options.length < 2) {
@@ -2128,12 +2550,12 @@ async function putDirtyEvents(minio, bucket, prefix, events) {
2128
2550
  });
2129
2551
  }
2130
2552
  }
2131
- async function upsertLogHeader(cfg, ctx, patch) {
2553
+ async function upsertLogHeader(cfg, ctx, patch, objectPrefix) {
2132
2554
  const api = createApmApiClient(cfg);
2133
2555
  await api.cli.webideUpsertMessageLog({
2134
2556
  messageId: ctx.messageId,
2135
2557
  agentId: ctx.agentId,
2136
- objectPrefix: webIdeEventsObjectPrefix(ctx.taskId, ctx.messageId),
2558
+ objectPrefix,
2137
2559
  ...patch
2138
2560
  });
2139
2561
  }
@@ -2145,6 +2567,7 @@ function createThrottledWebIdeMessageLogSync(cfg, ctx, onError, options) {
2145
2567
  let minio;
2146
2568
  let bucket = "";
2147
2569
  let firstLogNotified = false;
2570
+ const resolvePrefix = () => options?.objectPrefixOverride?.trim() || webIdeEventsObjectPrefix(ctx.taskId, ctx.messageId);
2148
2571
  const ensureMinio = async () => {
2149
2572
  if (minio) return;
2150
2573
  const created = await createApmLogMinioClient(cfg);
@@ -2168,11 +2591,16 @@ function createThrottledWebIdeMessageLogSync(cfg, ctx, onError, options) {
2168
2591
  lastRunAt = Date.now();
2169
2592
  try {
2170
2593
  await ensureMinio();
2171
- const prefix = webIdeEventsObjectPrefix(ctx.taskId, ctx.messageId);
2594
+ const prefix = resolvePrefix();
2172
2595
  await putDirtyEvents(minio, bucket, prefix, events);
2173
- await upsertLogHeader(cfg, ctx, {
2174
- eventCount: session.getEventCount()
2175
- });
2596
+ await upsertLogHeader(
2597
+ cfg,
2598
+ ctx,
2599
+ {
2600
+ eventCount: session.getEventCount()
2601
+ },
2602
+ prefix
2603
+ );
2176
2604
  session.clearDirtyIfUnchanged(events);
2177
2605
  await notifyFirstLog();
2178
2606
  } catch (err) {
@@ -2190,12 +2618,17 @@ function createThrottledWebIdeMessageLogSync(cfg, ctx, onError, options) {
2190
2618
  return {
2191
2619
  async markRun(runId, runStatus, lastError, tokenUsage) {
2192
2620
  try {
2193
- await upsertLogHeader(cfg, ctx, {
2194
- runId,
2195
- runStatus,
2196
- lastError: lastError ?? null,
2197
- ...tokenUsage ? { tokenUsage } : {}
2198
- });
2621
+ await upsertLogHeader(
2622
+ cfg,
2623
+ ctx,
2624
+ {
2625
+ runId,
2626
+ runStatus,
2627
+ lastError: lastError ?? null,
2628
+ ...tokenUsage ? { tokenUsage } : {}
2629
+ },
2630
+ resolvePrefix()
2631
+ );
2199
2632
  } catch (err) {
2200
2633
  onError(err);
2201
2634
  }
@@ -2490,14 +2923,14 @@ async function ensureWebIdePullRequests(cfg, taskId, workdir) {
2490
2923
  }
2491
2924
 
2492
2925
  // src/version.ts
2493
- import { readFileSync as readFileSync4 } from "fs";
2926
+ import { readFileSync as readFileSync5 } from "fs";
2494
2927
  import { dirname as dirname4, join as join4 } from "path";
2495
2928
  import { fileURLToPath as fileURLToPath2 } from "url";
2496
2929
  function readCliVersion() {
2497
2930
  try {
2498
2931
  const dir = dirname4(fileURLToPath2(import.meta.url));
2499
2932
  const pkgPath = join4(dir, "..", "package.json");
2500
- const pkg = JSON.parse(readFileSync4(pkgPath, "utf8"));
2933
+ const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
2501
2934
  return pkg.version ?? "0.0.0";
2502
2935
  } catch {
2503
2936
  return "0.0.0";
@@ -2505,7 +2938,7 @@ function readCliVersion() {
2505
2938
  }
2506
2939
 
2507
2940
  // src/commands/sync-webide-attachments.ts
2508
- import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
2941
+ import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
2509
2942
  import { join as join5 } from "path";
2510
2943
  var MANIFEST_FILE = ".sync-manifest.json";
2511
2944
  async function downloadAttachment(cfg, attachmentId) {
@@ -2523,12 +2956,12 @@ async function downloadAttachment(cfg, attachmentId) {
2523
2956
  }
2524
2957
  function loadManifest(dir) {
2525
2958
  const path = join5(dir, MANIFEST_FILE);
2526
- if (!existsSync5(path)) {
2959
+ if (!existsSync6(path)) {
2527
2960
  return { version: 1, attachments: {} };
2528
2961
  }
2529
2962
  try {
2530
2963
  const parsed = JSON.parse(
2531
- readFileSync5(path, "utf8")
2964
+ readFileSync6(path, "utf8")
2532
2965
  );
2533
2966
  if (parsed?.version === 1 && parsed.attachments && typeof parsed.attachments === "object") {
2534
2967
  return parsed;
@@ -2546,7 +2979,7 @@ function saveManifest(dir, manifest) {
2546
2979
  );
2547
2980
  }
2548
2981
  function isAttachmentUpToDate(entry, item, dest) {
2549
- if (!entry || !existsSync5(dest)) return false;
2982
+ if (!entry || !existsSync6(dest)) return false;
2550
2983
  if (entry.name !== item.name) return false;
2551
2984
  const createdAt = item.createdAt ?? "";
2552
2985
  return entry.createdAt === createdAt;
@@ -2593,14 +3026,14 @@ async function syncWebIdeAttachments(cfg, taskId, workdir, attachments) {
2593
3026
 
2594
3027
  // src/utils/project-documents.ts
2595
3028
  import {
2596
- existsSync as existsSync6,
3029
+ existsSync as existsSync7,
2597
3030
  readdirSync as readdirSync3,
2598
- readFileSync as readFileSync6,
3031
+ readFileSync as readFileSync7,
2599
3032
  rmSync as rmSync2,
2600
3033
  writeFileSync as writeFileSync5
2601
3034
  } from "fs";
2602
3035
  import { createHash } from "crypto";
2603
- import { dirname as dirname5, join as join6, relative as relative2, sep } from "path";
3036
+ import { dirname as dirname5, join as join6, relative as relative3, sep } from "path";
2604
3037
  var MANIFEST_FILE2 = "manifest.json";
2605
3038
  function normalizeProjectIdForPath(projectId) {
2606
3039
  const id = projectId.trim();
@@ -2642,12 +3075,12 @@ function readLocalManifest(apmRoot, projectId) {
2642
3075
  projectDocumentsDir(apmRoot, projectId),
2643
3076
  MANIFEST_FILE2
2644
3077
  );
2645
- if (!existsSync6(manifestPath3)) {
3078
+ if (!existsSync7(manifestPath3)) {
2646
3079
  return null;
2647
3080
  }
2648
3081
  try {
2649
3082
  return JSON.parse(
2650
- readFileSync6(manifestPath3, "utf8")
3083
+ readFileSync7(manifestPath3, "utf8")
2651
3084
  );
2652
3085
  } catch {
2653
3086
  return null;
@@ -2655,7 +3088,7 @@ function readLocalManifest(apmRoot, projectId) {
2655
3088
  }
2656
3089
  function listLocalDocumentPaths(apmRoot, projectId) {
2657
3090
  const root = projectDocumentsDir(apmRoot, projectId);
2658
- if (!existsSync6(root)) {
3091
+ if (!existsSync7(root)) {
2659
3092
  return [];
2660
3093
  }
2661
3094
  const paths = [];
@@ -2669,7 +3102,7 @@ function listLocalDocumentPaths(apmRoot, projectId) {
2669
3102
  if (entry.isFile() && entry.name === MANIFEST_FILE2) {
2670
3103
  continue;
2671
3104
  }
2672
- const rel = relative2(root, abs).split(sep).join("/");
3105
+ const rel = relative3(root, abs).split(sep).join("/");
2673
3106
  paths.push(rel);
2674
3107
  }
2675
3108
  };
@@ -2790,7 +3223,7 @@ ${diagnostic ?? ""}`);
2790
3223
  const absPath = toFsPath(
2791
3224
  projectDocumentLocalPath(targetApmDir, projectId, path)
2792
3225
  );
2793
- if (existsSync6(absPath)) {
3226
+ if (existsSync7(absPath)) {
2794
3227
  rmSync2(absPath, { force: true });
2795
3228
  deleted += 1;
2796
3229
  }
@@ -2839,7 +3272,7 @@ async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot, options) {
2839
3272
  const absPath = toFsPath(
2840
3273
  projectDocumentLocalPath(targetApmDir, projectId, path)
2841
3274
  );
2842
- const content = readFileSync6(absPath, "utf8");
3275
+ const content = readFileSync7(absPath, "utf8");
2843
3276
  const contentHash = hashLocalFileContent(content);
2844
3277
  if (remoteHashByPath.get(path) === contentHash) {
2845
3278
  continue;
@@ -2860,7 +3293,7 @@ async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot, options) {
2860
3293
  }
2861
3294
 
2862
3295
  // src/commands/connect/cli-version-sync.ts
2863
- import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
3296
+ import { existsSync as existsSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
2864
3297
  import { join as join7 } from "path";
2865
3298
  var CLI_VERSION_FILE = ".cli-version.json";
2866
3299
  function manifestPath2(apmDir) {
@@ -2868,12 +3301,12 @@ function manifestPath2(apmDir) {
2868
3301
  }
2869
3302
  function loadManifest2(apmDir) {
2870
3303
  const path = toFsPath(manifestPath2(apmDir));
2871
- if (!existsSync7(path)) {
3304
+ if (!existsSync8(path)) {
2872
3305
  return null;
2873
3306
  }
2874
3307
  try {
2875
3308
  const parsed = JSON.parse(
2876
- readFileSync7(path, "utf8")
3309
+ readFileSync8(path, "utf8")
2877
3310
  );
2878
3311
  if (parsed?.version === 1 && typeof parsed.cliVersion === "string" && parsed.cliVersion.trim()) {
2879
3312
  return parsed;
@@ -2909,6 +3342,24 @@ function markSkillsSyncedForCliVersion(workdir, cliVersion) {
2909
3342
  syncedInSession.set(workdir, cliVersion);
2910
3343
  }
2911
3344
 
3345
+ // src/commands/connect/parse-webide-prompt-payload.ts
3346
+ function parseWebIdePromptPayload(content) {
3347
+ const marker = "\n\npayload=";
3348
+ const index = content.lastIndexOf(marker);
3349
+ if (index < 0) return null;
3350
+ const jsonText = content.slice(index + marker.length).trim();
3351
+ if (!jsonText) return null;
3352
+ try {
3353
+ const parsed = JSON.parse(jsonText);
3354
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
3355
+ return parsed;
3356
+ }
3357
+ } catch {
3358
+ return null;
3359
+ }
3360
+ return null;
3361
+ }
3362
+
2912
3363
  // src/commands/connect/handle-webide-message.ts
2913
3364
  var WEBIDE_CODE_CHANGE_ACTIONS = /* @__PURE__ */ new Set([
2914
3365
  "skip-plan",
@@ -2924,6 +3375,9 @@ function shouldCommitAfterWebIdeMessage(action) {
2924
3375
  function isStartLocalServicesAction(action) {
2925
3376
  return action === "enter-manual-test" || action === "skip-test";
2926
3377
  }
3378
+ function isExecuteSqlAction(action) {
3379
+ return action === "execute-sql";
3380
+ }
2927
3381
  function syncLocalTaskStatus(workdir, taskId, msg, status) {
2928
3382
  syncWebIdeTaskState(workdir, taskId, {
2929
3383
  messageId: msg.messageId,
@@ -3133,7 +3587,10 @@ async function handleWebIdeInboundMessage(cfg, msg, signal) {
3133
3587
  );
3134
3588
  await syncPrepLog();
3135
3589
  const startLocalServices = isStartLocalServicesAction(msg.action);
3590
+ const executeSql = isExecuteSqlAction(msg.action);
3136
3591
  const savedAgentId = startLocalServices ? void 0 : loadWebIdeAgentId(workdir, taskId);
3592
+ const promptPayload = executeSql ? parseWebIdePromptPayload(msg.content) : null;
3593
+ const sqlExecutionId = executeSql && typeof promptPayload?.executionId === "string" ? promptPayload.executionId.trim() : void 0;
3137
3594
  const outcome = await runCursorAgent(
3138
3595
  cfg,
3139
3596
  {
@@ -3155,9 +3612,11 @@ async function handleWebIdeInboundMessage(cfg, msg, signal) {
3155
3612
  taskId,
3156
3613
  signal
3157
3614
  }),
3158
- enableAskQuestion: !startLocalServices,
3159
- enableWebIdePlanTools: !startLocalServices,
3615
+ enableAskQuestion: !startLocalServices && !executeSql,
3616
+ enableWebIdePlanTools: !startLocalServices && !executeSql,
3160
3617
  enablePtySessionMcp: startLocalServices,
3618
+ enableMysqlTools: executeSql,
3619
+ sqlExecutionId,
3161
3620
  enableSandbox: false,
3162
3621
  onInvalidatePersistedAgentId: startLocalServices ? void 0 : () => clearWebIdeAgentId(workdir, taskId),
3163
3622
  taskId,
@@ -3214,6 +3673,20 @@ async function handleWebIdeInboundMessage(cfg, msg, signal) {
3214
3673
  detail,
3215
3674
  tokenUsage
3216
3675
  );
3676
+ if (executeSql && sqlExecutionId) {
3677
+ try {
3678
+ await api.cli.webideFinalizeSqlExecution({
3679
+ executionId: sqlExecutionId,
3680
+ status: "FAILED",
3681
+ error: detail
3682
+ });
3683
+ } catch (finalizeErr) {
3684
+ console.warn(
3685
+ "[apm] finalize sql execution failed:",
3686
+ finalizeErr instanceof Error ? finalizeErr.message : finalizeErr
3687
+ );
3688
+ }
3689
+ }
3217
3690
  await setError(cfg, messageId, detail);
3218
3691
  syncLocalTaskStatus(workdir, taskId, msg, "FAILED");
3219
3692
  return;
@@ -3225,6 +3698,20 @@ async function handleWebIdeInboundMessage(cfg, msg, signal) {
3225
3698
  "\u5DF2\u53D6\u6D88",
3226
3699
  tokenUsage
3227
3700
  );
3701
+ if (executeSql && sqlExecutionId) {
3702
+ try {
3703
+ await api.cli.webideFinalizeSqlExecution({
3704
+ executionId: sqlExecutionId,
3705
+ status: "CANCELLED",
3706
+ error: "\u5DF2\u53D6\u6D88"
3707
+ });
3708
+ } catch (finalizeErr) {
3709
+ console.warn(
3710
+ "[apm] finalize sql execution cancelled:",
3711
+ finalizeErr instanceof Error ? finalizeErr.message : finalizeErr
3712
+ );
3713
+ }
3714
+ }
3228
3715
  await updateStatus(cfg, messageId, "CANCELLED");
3229
3716
  syncLocalTaskStatus(workdir, taskId, msg, "CANCELLED");
3230
3717
  return;
@@ -3501,6 +3988,276 @@ async function handleStartProject(cfg, msg, signal) {
3501
3988
  }
3502
3989
  }
3503
3990
 
3991
+ // src/commands/connect/handle-package.ts
3992
+ async function updatePackageStatus(cfg, packageId, status, extra) {
3993
+ const api = createApmApiClient(cfg);
3994
+ await api.cli.projectPackageUpdateStatus({
3995
+ id: packageId,
3996
+ status,
3997
+ ...extra?.error != null ? { error: extra.error } : {},
3998
+ ...extra?.content != null ? { content: extra.content } : {},
3999
+ ...extra?.artifactPath != null ? { artifactPath: extra.artifactPath } : {}
4000
+ });
4001
+ }
4002
+ function expectedArtifactPath(target, packageId) {
4003
+ if (target === "frontend") {
4004
+ return `/data/artifacts/vue/${packageId}/dist.zip`;
4005
+ }
4006
+ return `/data/artifacts/springboot/${packageId}/manifest.json`;
4007
+ }
4008
+ async function handleWebIdePackage(cfg, msg, signal) {
4009
+ const workdir = requireRemoteWorkdir(msg.workdir);
4010
+ const messageId = msg.messageId;
4011
+ const packageId = msg.packageId;
4012
+ const projectId = msg.projectId;
4013
+ console.log(
4014
+ `[apm] webide-package target=${msg.target} packageId=${packageId} projectId=${projectId} baseBranch=${msg.baseBranch}`
4015
+ );
4016
+ await updatePackageStatus(cfg, packageId, "RUNNING");
4017
+ const eventSession = new EventSession(msg.content);
4018
+ const logSyncRef = {
4019
+ current: createThrottledWebIdeMessageLogSync(
4020
+ cfg,
4021
+ {
4022
+ taskId: `package:${packageId}`,
4023
+ messageId,
4024
+ agentId: "webide-package"
4025
+ },
4026
+ (err) => {
4027
+ console.warn(
4028
+ "[apm] WebIDE \u6253\u5305\u65E5\u5FD7\u540C\u6B65\u5931\u8D25:",
4029
+ err instanceof Error ? err.message : err
4030
+ );
4031
+ },
4032
+ {
4033
+ objectPrefixOverride: `events/webide-package/${packageId}/`
4034
+ }
4035
+ )
4036
+ };
4037
+ const syncPrepLog = async () => {
4038
+ const sync = logSyncRef.current;
4039
+ if (!sync) return;
4040
+ sync.schedule(eventSession);
4041
+ await sync.flush(eventSession);
4042
+ };
4043
+ const runPrepStep = async (step, work, formatOk) => {
4044
+ eventSession.addCliStep(step, "\u8FDB\u884C\u4E2D\u2026", "running");
4045
+ await syncPrepLog();
4046
+ try {
4047
+ const result = await work();
4048
+ eventSession.addCliStep(step, formatOk(result), "ok");
4049
+ await syncPrepLog();
4050
+ return result;
4051
+ } catch (err) {
4052
+ const detail = err instanceof Error ? err.message : String(err);
4053
+ eventSession.addCliStep(step, detail, "error");
4054
+ await syncPrepLog();
4055
+ throw err;
4056
+ }
4057
+ };
4058
+ try {
4059
+ if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
4060
+ await runPrepStep(
4061
+ "\u521D\u59CB\u5316\u5DE5\u4F5C\u533A",
4062
+ async () => {
4063
+ const { didInit } = await ensureWorkspaceInitialized(workdir);
4064
+ return didInit;
4065
+ },
4066
+ (didInit) => didInit ? "\u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A" : "\u5DE5\u4F5C\u533A\u5DF2\u5C31\u7EEA"
4067
+ );
4068
+ const cliVersion = readCliVersion();
4069
+ if (shouldSyncSkillsForCliVersion(workdir, cliVersion)) {
4070
+ if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
4071
+ await runPrepStep(
4072
+ "\u540C\u6B65 WebIDE \u89C4\u5219",
4073
+ async () => {
4074
+ await copyWebIdeTemplateFiles(workspaceApmDir(workdir), workdir);
4075
+ markSkillsSyncedForCliVersion(workdir, cliVersion);
4076
+ return cliVersion;
4077
+ },
4078
+ (version) => `\u5DF2\u540C\u6B65\u81F3 CLI ${version}`
4079
+ );
4080
+ } else {
4081
+ eventSession.addCliStep(
4082
+ "\u540C\u6B65 WebIDE \u89C4\u5219",
4083
+ `\u7248\u672C\u4E00\u81F4\uFF08${cliVersion}\uFF09\uFF0C\u8DF3\u8FC7`,
4084
+ "skip"
4085
+ );
4086
+ await syncPrepLog();
4087
+ }
4088
+ await runPrepStep(
4089
+ "\u5DE5\u4F5C\u533A\u4ED3\u5E93\u6E05\u5355",
4090
+ async () => {
4091
+ const workspaceRepos = resolveWorkspaceRepos(workdir);
4092
+ try {
4093
+ await enrichWorkspaceReposRemoteUrls(workspaceRepos);
4094
+ } catch (err) {
4095
+ console.warn(
4096
+ "[apm] \u8865\u9F50 workspace-repos remoteUrl \u5931\u8D25:",
4097
+ err instanceof Error ? err.message : err
4098
+ );
4099
+ }
4100
+ return workspaceRepos;
4101
+ },
4102
+ (repos) => `kind=${repos.kind} repos=${repos.repos.length}`
4103
+ );
4104
+ if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
4105
+ await runPrepStep(
4106
+ "\u5207\u6362\u57FA\u7EBF\u5206\u652F",
4107
+ async () => checkoutBaselineBranches(msg.baseBranch, { cwd: workdir }),
4108
+ (result) => `baseline=${result.baseline} kind=${result.kind} repos=${result.repos.length}`
4109
+ );
4110
+ if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
4111
+ await runPrepStep(
4112
+ "\u540C\u6B65\u9879\u76EE\u6587\u6863",
4113
+ async () => {
4114
+ const docs = await syncProjectDocumentsPull(workdir, void 0, {
4115
+ projectId
4116
+ });
4117
+ return docs;
4118
+ },
4119
+ (result) => {
4120
+ if (!result.synced) {
4121
+ return result.projectId ? "\u65E0\u9700\u540C\u6B65\u6216\u8DF3\u8FC7" : "\u8DF3\u8FC7\uFF1A\u65E0 projectId";
4122
+ }
4123
+ return `downloaded=${result.downloaded} deleted=${result.deleted}`;
4124
+ }
4125
+ );
4126
+ eventSession.addCliStep("\u542F\u52A8\u6253\u5305 Agent", "\u51C6\u5907 create\u2026", "running");
4127
+ await syncPrepLog();
4128
+ const artifactHint = expectedArtifactPath(msg.target, packageId);
4129
+ let failedByTool = false;
4130
+ const outcome = await runCursorAgent(
4131
+ cfg,
4132
+ {
4133
+ messageId,
4134
+ prompt: msg.content,
4135
+ model: msg.model,
4136
+ apiKey: msg.apiKey,
4137
+ workdir,
4138
+ user: msg.user || "webide"
4139
+ },
4140
+ {
4141
+ signal,
4142
+ forceSend: true,
4143
+ eventSession,
4144
+ enableAppendMessage: false,
4145
+ enableAskQuestion: false,
4146
+ enableWebIdePlanTools: false,
4147
+ enablePtySessionMcp: false,
4148
+ enableSandbox: false,
4149
+ enablePackageStatusTools: true,
4150
+ packageId,
4151
+ onPackageFailed: async () => {
4152
+ failedByTool = true;
4153
+ },
4154
+ taskId: `package:${packageId}`,
4155
+ createRemoteLogSync: (agentId) => {
4156
+ logSyncRef.current = createThrottledWebIdeMessageLogSync(
4157
+ cfg,
4158
+ {
4159
+ taskId: `package:${packageId}`,
4160
+ messageId,
4161
+ agentId
4162
+ },
4163
+ (err) => {
4164
+ console.warn(
4165
+ "[apm] WebIDE \u6253\u5305\u65E5\u5FD7\u540C\u6B65\u5931\u8D25:",
4166
+ err instanceof Error ? err.message : err
4167
+ );
4168
+ },
4169
+ {
4170
+ objectPrefixOverride: `events/webide-package/${packageId}/`
4171
+ }
4172
+ );
4173
+ return logSyncRef.current;
4174
+ },
4175
+ onRunStarted: async ({ agentId, runId }) => {
4176
+ eventSession.addCliStep(
4177
+ "\u542F\u52A8\u6253\u5305 Agent",
4178
+ `agentId=${agentId} runId=${runId}`,
4179
+ "ok"
4180
+ );
4181
+ logSyncRef.current?.schedule(eventSession);
4182
+ await logSyncRef.current?.markRun(runId, "running");
4183
+ await logSyncRef.current?.flush(eventSession);
4184
+ }
4185
+ }
4186
+ );
4187
+ const tokenUsage = outcome.usage != null ? {
4188
+ modelId: outcome.modelId ?? (msg.model?.trim() || void 0),
4189
+ inputTokens: outcome.usage.inputTokens,
4190
+ outputTokens: outcome.usage.outputTokens,
4191
+ cacheReadTokens: outcome.usage.cacheReadTokens,
4192
+ cacheWriteTokens: outcome.usage.cacheWriteTokens,
4193
+ totalTokens: outcome.usage.totalTokens,
4194
+ ...outcome.usage.reasoningTokens != null ? { reasoningTokens: outcome.usage.reasoningTokens } : {}
4195
+ } : void 0;
4196
+ if (outcome.status === "error") {
4197
+ const detail = formatCursorRunFailure(outcome.runId, {
4198
+ resultText: outcome.result
4199
+ });
4200
+ await logSyncRef.current?.markRun(
4201
+ outcome.runId,
4202
+ "error",
4203
+ detail,
4204
+ tokenUsage
4205
+ );
4206
+ if (!failedByTool) {
4207
+ await updatePackageStatus(cfg, packageId, "FAILED", { error: detail });
4208
+ }
4209
+ return;
4210
+ }
4211
+ if (outcome.status === "cancelled" || signal.aborted) {
4212
+ await logSyncRef.current?.markRun(
4213
+ outcome.runId,
4214
+ "cancelled",
4215
+ "\u5DF2\u53D6\u6D88",
4216
+ tokenUsage
4217
+ );
4218
+ await updatePackageStatus(cfg, packageId, "CANCELLED", {
4219
+ error: "\u5DF2\u53D6\u6D88"
4220
+ });
4221
+ return;
4222
+ }
4223
+ await logSyncRef.current?.markRun(
4224
+ outcome.runId,
4225
+ "finished",
4226
+ null,
4227
+ tokenUsage
4228
+ );
4229
+ if (failedByTool) {
4230
+ console.log(
4231
+ `[apm] webide-package Agent \u5DF2\u6807 FAILED packageId=${packageId}`
4232
+ );
4233
+ return;
4234
+ }
4235
+ await updatePackageStatus(cfg, packageId, "SUCCESS", {
4236
+ artifactPath: artifactHint
4237
+ });
4238
+ console.log(
4239
+ `[apm] webide-package \u5B8C\u6210 target=${msg.target} packageId=${packageId} agentId=${outcome.agentId}`
4240
+ );
4241
+ } catch (err) {
4242
+ const detail = err instanceof Error ? err.message : String(err);
4243
+ console.error(`[apm] webide-package \u5931\u8D25: ${detail}`);
4244
+ try {
4245
+ if (signal.aborted) {
4246
+ await updatePackageStatus(cfg, packageId, "CANCELLED", {
4247
+ error: detail
4248
+ });
4249
+ } else {
4250
+ await updatePackageStatus(cfg, packageId, "FAILED", { error: detail });
4251
+ }
4252
+ } catch (statusErr) {
4253
+ console.error(
4254
+ "[apm] \u5199\u5165\u6253\u5305 FAILED \u5931\u8D25:",
4255
+ statusErr instanceof Error ? statusErr.message : statusErr
4256
+ );
4257
+ }
4258
+ }
4259
+ }
4260
+
3504
4261
  // src/commands/connect/webide-message-worker.ts
3505
4262
  var controllers = /* @__PURE__ */ new Map();
3506
4263
  async function runJob(cfg, msg) {
@@ -3509,6 +4266,8 @@ async function runJob(cfg, msg) {
3509
4266
  try {
3510
4267
  if (msg.type === "webide-start-project") {
3511
4268
  await handleStartProject(cfg, msg, controller.signal);
4269
+ } else if (msg.type === "webide-package") {
4270
+ await handleWebIdePackage(cfg, msg, controller.signal);
3512
4271
  } else {
3513
4272
  await handleWebIdeInboundMessage(cfg, msg, controller.signal);
3514
4273
  }