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.
- package/dist/index.js +132 -2
- package/dist/webide-message-worker.js +809 -50
- package/package.json +7 -6
- package/template/AGENTS.webide.md +1 -0
- package/template/rules/webide_package.md +75 -0
- package/template/rules/webide_sql_change.md +5 -3
- package/template/rules/webide_sql_execute.md +29 -0
- package/template/rules/webide_terminal.md +2 -2
|
@@ -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
|
|
1809
|
-
import { dirname as dirname3, resolve as
|
|
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
|
|
2233
|
+
return resolve5(workdir, ".apm", "webide", taskId, "cursor-agent.json");
|
|
1812
2234
|
}
|
|
1813
2235
|
function readRegistry(path) {
|
|
1814
|
-
if (!
|
|
2236
|
+
if (!existsSync4(path)) {
|
|
1815
2237
|
return {};
|
|
1816
2238
|
}
|
|
1817
2239
|
try {
|
|
1818
|
-
const parsed = JSON.parse(
|
|
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 (!
|
|
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
|
|
1895
|
-
import { resolve as
|
|
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 (!
|
|
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 =
|
|
1967
|
-
if (
|
|
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
|
|
2400
|
+
function asString4(value) {
|
|
1979
2401
|
return typeof value === "string" ? value.trim() : "";
|
|
1980
2402
|
}
|
|
1981
2403
|
function parseQuestions(args) {
|
|
1982
|
-
const title =
|
|
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 =
|
|
1992
|
-
const 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 =
|
|
2000
|
-
const 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
|
|
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 =
|
|
2594
|
+
const prefix = resolvePrefix();
|
|
2172
2595
|
await putDirtyEvents(minio, bucket, prefix, events);
|
|
2173
|
-
await upsertLogHeader(
|
|
2174
|
-
|
|
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(
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
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
|
|
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(
|
|
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
|
|
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 (!
|
|
2959
|
+
if (!existsSync6(path)) {
|
|
2527
2960
|
return { version: 1, attachments: {} };
|
|
2528
2961
|
}
|
|
2529
2962
|
try {
|
|
2530
2963
|
const parsed = JSON.parse(
|
|
2531
|
-
|
|
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 || !
|
|
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
|
|
3029
|
+
existsSync as existsSync7,
|
|
2597
3030
|
readdirSync as readdirSync3,
|
|
2598
|
-
readFileSync as
|
|
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
|
|
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 (!
|
|
3078
|
+
if (!existsSync7(manifestPath3)) {
|
|
2646
3079
|
return null;
|
|
2647
3080
|
}
|
|
2648
3081
|
try {
|
|
2649
3082
|
return JSON.parse(
|
|
2650
|
-
|
|
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 (!
|
|
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 =
|
|
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 (
|
|
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 =
|
|
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
|
|
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 (!
|
|
3304
|
+
if (!existsSync8(path)) {
|
|
2872
3305
|
return null;
|
|
2873
3306
|
}
|
|
2874
3307
|
try {
|
|
2875
3308
|
const parsed = JSON.parse(
|
|
2876
|
-
|
|
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
|
}
|