ai-project-manage-cli 8.0.5 → 8.0.8
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
CHANGED
|
@@ -154,6 +154,14 @@ var init_request_config = __esm({
|
|
|
154
154
|
method: "PUT",
|
|
155
155
|
path: "/cli/webide/test-cases"
|
|
156
156
|
}),
|
|
157
|
+
webideUpsertSqlExecution: defineEndpoint({
|
|
158
|
+
method: "PUT",
|
|
159
|
+
path: "/cli/webide/sql-executions"
|
|
160
|
+
}),
|
|
161
|
+
webideFinalizeSqlExecution: defineEndpoint({
|
|
162
|
+
method: "PUT",
|
|
163
|
+
path: "/cli/webide/sql-executions/finalize"
|
|
164
|
+
}),
|
|
157
165
|
projectBaseBranch: defineEndpoint(
|
|
158
166
|
{
|
|
159
167
|
method: "GET",
|
|
@@ -1858,7 +1866,7 @@ function buildProgram() {
|
|
|
1858
1866
|
).version(readCliVersion(), "-V, --version", "\u663E\u793A\u7248\u672C\u53F7").helpOption("-h, --help", "\u663E\u793A\u5E2E\u52A9").showHelpAfterError(true);
|
|
1859
1867
|
program.command("login").description(
|
|
1860
1868
|
"\u9A8C\u8BC1 API Key \u5E76\u5199\u5165 ~/.config/apm/config.json\uFF08GET /api/v1/cli/me\uFF09"
|
|
1861
|
-
).option("--api-key <key>", "\
|
|
1869
|
+
).option("--api-key <key>", "\u8FDC\u7A0B\u4E3B\u673A API Key\uFF08cm_ \u5F00\u5934\uFF09").option("--server <url>", "API \u6839\u5730\u5740\uFF0C\u4F8B\u5982 http://127.0.0.1:3000").action(async (opts) => {
|
|
1862
1870
|
await runLogin(opts);
|
|
1863
1871
|
});
|
|
1864
1872
|
program.command("init").description("\u5728\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u6309 WebIDE \u5DE5\u4F5C\u533A\u521D\u59CB\u5316\uFF1A\u5199\u5165\u6307\u5357\u4E0E webide \u89C4\u5219").action(async () => {
|
|
@@ -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",
|
|
@@ -1515,6 +1523,286 @@ function createUpsertWebIdeTestCasesTool(options) {
|
|
|
1515
1523
|
};
|
|
1516
1524
|
}
|
|
1517
1525
|
|
|
1526
|
+
// src/commands/connect/tools/mysql-tools.ts
|
|
1527
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, statSync as statSync3 } from "node:fs";
|
|
1528
|
+
import { isAbsolute, relative as relative2, resolve as resolve4 } from "node:path";
|
|
1529
|
+
import mysql from "mysql2/promise";
|
|
1530
|
+
var MAX_RESULT_ROWS = 100;
|
|
1531
|
+
var EXEC_TIMEOUT_MS = 6e4;
|
|
1532
|
+
var MAX_SQL_FILE_BYTES = 2 * 1024 * 1024;
|
|
1533
|
+
function asString3(value) {
|
|
1534
|
+
return typeof value === "string" ? value.trim() : "";
|
|
1535
|
+
}
|
|
1536
|
+
function asNumber(value) {
|
|
1537
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
1538
|
+
if (typeof value === "string" && value.trim()) {
|
|
1539
|
+
const parsed = Number(value);
|
|
1540
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
1541
|
+
}
|
|
1542
|
+
return void 0;
|
|
1543
|
+
}
|
|
1544
|
+
function isResultSetHeader(value) {
|
|
1545
|
+
return typeof value === "object" && value !== null && "affectedRows" in value && !Array.isArray(value);
|
|
1546
|
+
}
|
|
1547
|
+
function formatOkPacket(packet) {
|
|
1548
|
+
return {
|
|
1549
|
+
kind: "update",
|
|
1550
|
+
affectedRows: packet.affectedRows ?? 0,
|
|
1551
|
+
insertId: packet.insertId ?? 0,
|
|
1552
|
+
changedRows: packet.changedRows,
|
|
1553
|
+
warnings: packet.warningStatus
|
|
1554
|
+
};
|
|
1555
|
+
}
|
|
1556
|
+
function formatSelectRows(rows) {
|
|
1557
|
+
const truncated = rows.slice(0, MAX_RESULT_ROWS);
|
|
1558
|
+
return {
|
|
1559
|
+
kind: "select",
|
|
1560
|
+
rowCount: rows.length,
|
|
1561
|
+
truncated: rows.length > MAX_RESULT_ROWS,
|
|
1562
|
+
rows: truncated
|
|
1563
|
+
};
|
|
1564
|
+
}
|
|
1565
|
+
function formatStatementResult(result) {
|
|
1566
|
+
if (Array.isArray(result)) {
|
|
1567
|
+
return formatSelectRows(result);
|
|
1568
|
+
}
|
|
1569
|
+
if (isResultSetHeader(result)) {
|
|
1570
|
+
return formatOkPacket(result);
|
|
1571
|
+
}
|
|
1572
|
+
return { kind: "unknown", value: result };
|
|
1573
|
+
}
|
|
1574
|
+
function normalizeMultiStatementResults(raw) {
|
|
1575
|
+
if (!Array.isArray(raw)) {
|
|
1576
|
+
return [formatStatementResult(raw)];
|
|
1577
|
+
}
|
|
1578
|
+
if (raw.length === 0) {
|
|
1579
|
+
return [];
|
|
1580
|
+
}
|
|
1581
|
+
const first = raw[0];
|
|
1582
|
+
if (Array.isArray(first)) {
|
|
1583
|
+
return raw.map((item) => {
|
|
1584
|
+
if (Array.isArray(item)) {
|
|
1585
|
+
return formatStatementResult(item[0]);
|
|
1586
|
+
}
|
|
1587
|
+
return formatStatementResult(item);
|
|
1588
|
+
});
|
|
1589
|
+
}
|
|
1590
|
+
if (isResultSetHeader(first)) {
|
|
1591
|
+
return raw.map((item) => formatStatementResult(item));
|
|
1592
|
+
}
|
|
1593
|
+
return [formatStatementResult(raw)];
|
|
1594
|
+
}
|
|
1595
|
+
function resolveSqlArtifactPath(workdir, absolutePath) {
|
|
1596
|
+
if (!isAbsolute(absolutePath)) {
|
|
1597
|
+
return { ok: false, error: "path \u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84" };
|
|
1598
|
+
}
|
|
1599
|
+
const workdirAbs = resolve4(toFsPath(workdir));
|
|
1600
|
+
const targetAbs = resolve4(toFsPath(absolutePath));
|
|
1601
|
+
const rel = relative2(workdirAbs, targetAbs);
|
|
1602
|
+
if (!rel || rel.startsWith("..") || isAbsolute(rel)) {
|
|
1603
|
+
return { ok: false, error: "path \u5FC5\u987B\u4F4D\u4E8E\u5F53\u524D\u5DE5\u4F5C\u533A\u5185" };
|
|
1604
|
+
}
|
|
1605
|
+
const fsPath = toFsPath(targetAbs);
|
|
1606
|
+
if (!existsSync3(fsPath)) {
|
|
1607
|
+
return { ok: false, error: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${absolutePath}` };
|
|
1608
|
+
}
|
|
1609
|
+
let st;
|
|
1610
|
+
try {
|
|
1611
|
+
st = statSync3(fsPath);
|
|
1612
|
+
} catch {
|
|
1613
|
+
return { ok: false, error: `\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6: ${absolutePath}` };
|
|
1614
|
+
}
|
|
1615
|
+
if (!st.isFile()) {
|
|
1616
|
+
return { ok: false, error: "path \u5FC5\u987B\u6307\u5411\u666E\u901A\u6587\u4EF6" };
|
|
1617
|
+
}
|
|
1618
|
+
if (st.size > MAX_SQL_FILE_BYTES) {
|
|
1619
|
+
return {
|
|
1620
|
+
ok: false,
|
|
1621
|
+
error: `SQL \u6587\u4EF6\u8FC7\u5927\uFF08>${MAX_SQL_FILE_BYTES} bytes\uFF09`
|
|
1622
|
+
};
|
|
1623
|
+
}
|
|
1624
|
+
const displayPath = normalizeWorkdirPath(rel) || normalizeWorkdirPath(absolutePath);
|
|
1625
|
+
return { ok: true, absPath: targetAbs, displayPath };
|
|
1626
|
+
}
|
|
1627
|
+
function readSqlFile(absPath) {
|
|
1628
|
+
return readFileSync3(toFsPath(absPath), "utf8");
|
|
1629
|
+
}
|
|
1630
|
+
function createMysqlExecuteTool(options) {
|
|
1631
|
+
const { taskId, executionId, workdir, upsertSqlExecution } = options;
|
|
1632
|
+
return {
|
|
1633
|
+
MysqlExecute: {
|
|
1634
|
+
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.",
|
|
1635
|
+
inputSchema: {
|
|
1636
|
+
type: "object",
|
|
1637
|
+
properties: {
|
|
1638
|
+
host: { type: "string", description: "MySQL host" },
|
|
1639
|
+
port: { type: "number", description: "MySQL port, default 3306" },
|
|
1640
|
+
user: { type: "string", description: "MySQL username" },
|
|
1641
|
+
password: { type: "string", description: "MySQL password" },
|
|
1642
|
+
database: { type: "string", description: "Database name" },
|
|
1643
|
+
path: {
|
|
1644
|
+
type: "string",
|
|
1645
|
+
description: "Absolute filesystem path to the SQL artifact file under the current workspace (CLI reads the file; do not pass SQL text)"
|
|
1646
|
+
}
|
|
1647
|
+
},
|
|
1648
|
+
required: ["host", "user", "password", "database", "path"]
|
|
1649
|
+
},
|
|
1650
|
+
execute: async (args) => {
|
|
1651
|
+
const host = asString3(args.host);
|
|
1652
|
+
const user = asString3(args.user);
|
|
1653
|
+
const password = asString3(args.password);
|
|
1654
|
+
const database = asString3(args.database);
|
|
1655
|
+
const pathArg = asString3(args.path);
|
|
1656
|
+
const port = asNumber(args.port) ?? 3306;
|
|
1657
|
+
if (!host || !user || !database || !pathArg) {
|
|
1658
|
+
return {
|
|
1659
|
+
content: [
|
|
1660
|
+
{
|
|
1661
|
+
type: "text",
|
|
1662
|
+
text: "host / user / database / path \u5747\u4E0D\u80FD\u4E3A\u7A7A"
|
|
1663
|
+
}
|
|
1664
|
+
],
|
|
1665
|
+
isError: true
|
|
1666
|
+
};
|
|
1667
|
+
}
|
|
1668
|
+
const resolved = resolveSqlArtifactPath(workdir, pathArg);
|
|
1669
|
+
if (!resolved.ok) {
|
|
1670
|
+
try {
|
|
1671
|
+
await upsertSqlExecution({
|
|
1672
|
+
executionId,
|
|
1673
|
+
taskId,
|
|
1674
|
+
status: "FAILED",
|
|
1675
|
+
artifactPath: pathArg,
|
|
1676
|
+
error: resolved.error,
|
|
1677
|
+
host,
|
|
1678
|
+
port,
|
|
1679
|
+
database,
|
|
1680
|
+
dbUser: user
|
|
1681
|
+
});
|
|
1682
|
+
} catch {
|
|
1683
|
+
}
|
|
1684
|
+
return {
|
|
1685
|
+
content: [{ type: "text", text: resolved.error }],
|
|
1686
|
+
isError: true
|
|
1687
|
+
};
|
|
1688
|
+
}
|
|
1689
|
+
let sql;
|
|
1690
|
+
try {
|
|
1691
|
+
sql = readSqlFile(resolved.absPath).trim();
|
|
1692
|
+
} catch (err) {
|
|
1693
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1694
|
+
try {
|
|
1695
|
+
await upsertSqlExecution({
|
|
1696
|
+
executionId,
|
|
1697
|
+
taskId,
|
|
1698
|
+
status: "FAILED",
|
|
1699
|
+
artifactPath: resolved.displayPath,
|
|
1700
|
+
error: `\u8BFB\u53D6 SQL \u6587\u4EF6\u5931\u8D25: ${detail}`,
|
|
1701
|
+
host,
|
|
1702
|
+
port,
|
|
1703
|
+
database,
|
|
1704
|
+
dbUser: user
|
|
1705
|
+
});
|
|
1706
|
+
} catch {
|
|
1707
|
+
}
|
|
1708
|
+
return {
|
|
1709
|
+
content: [
|
|
1710
|
+
{
|
|
1711
|
+
type: "text",
|
|
1712
|
+
text: `\u8BFB\u53D6 SQL \u6587\u4EF6\u5931\u8D25: ${detail}`
|
|
1713
|
+
}
|
|
1714
|
+
],
|
|
1715
|
+
isError: true
|
|
1716
|
+
};
|
|
1717
|
+
}
|
|
1718
|
+
if (!sql) {
|
|
1719
|
+
try {
|
|
1720
|
+
await upsertSqlExecution({
|
|
1721
|
+
executionId,
|
|
1722
|
+
taskId,
|
|
1723
|
+
status: "FAILED",
|
|
1724
|
+
artifactPath: resolved.displayPath,
|
|
1725
|
+
sql: "",
|
|
1726
|
+
error: "SQL \u6587\u4EF6\u4E3A\u7A7A",
|
|
1727
|
+
host,
|
|
1728
|
+
port,
|
|
1729
|
+
database,
|
|
1730
|
+
dbUser: user
|
|
1731
|
+
});
|
|
1732
|
+
} catch {
|
|
1733
|
+
}
|
|
1734
|
+
return {
|
|
1735
|
+
content: [{ type: "text", text: "SQL \u6587\u4EF6\u4E3A\u7A7A" }],
|
|
1736
|
+
isError: true
|
|
1737
|
+
};
|
|
1738
|
+
}
|
|
1739
|
+
let connection;
|
|
1740
|
+
try {
|
|
1741
|
+
connection = await mysql.createConnection({
|
|
1742
|
+
host,
|
|
1743
|
+
port,
|
|
1744
|
+
user,
|
|
1745
|
+
password,
|
|
1746
|
+
database,
|
|
1747
|
+
multipleStatements: true,
|
|
1748
|
+
connectTimeout: 15e3
|
|
1749
|
+
});
|
|
1750
|
+
const [rawResults] = await connection.query({
|
|
1751
|
+
sql,
|
|
1752
|
+
timeout: EXEC_TIMEOUT_MS
|
|
1753
|
+
});
|
|
1754
|
+
const statements = normalizeMultiStatementResults(rawResults);
|
|
1755
|
+
const resultPayload = {
|
|
1756
|
+
ok: true,
|
|
1757
|
+
path: resolved.displayPath,
|
|
1758
|
+
statements,
|
|
1759
|
+
host,
|
|
1760
|
+
port,
|
|
1761
|
+
database,
|
|
1762
|
+
dbUser: user
|
|
1763
|
+
};
|
|
1764
|
+
await upsertSqlExecution({
|
|
1765
|
+
executionId,
|
|
1766
|
+
taskId,
|
|
1767
|
+
status: "SUCCESS",
|
|
1768
|
+
artifactPath: resolved.displayPath,
|
|
1769
|
+
sql,
|
|
1770
|
+
result: resultPayload,
|
|
1771
|
+
host,
|
|
1772
|
+
port,
|
|
1773
|
+
database,
|
|
1774
|
+
dbUser: user
|
|
1775
|
+
});
|
|
1776
|
+
return JSON.stringify(resultPayload, null, 2);
|
|
1777
|
+
} catch (err) {
|
|
1778
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1779
|
+
try {
|
|
1780
|
+
await upsertSqlExecution({
|
|
1781
|
+
executionId,
|
|
1782
|
+
taskId,
|
|
1783
|
+
status: "FAILED",
|
|
1784
|
+
artifactPath: resolved.displayPath,
|
|
1785
|
+
sql,
|
|
1786
|
+
error: detail,
|
|
1787
|
+
host: host || void 0,
|
|
1788
|
+
port,
|
|
1789
|
+
database: database || void 0,
|
|
1790
|
+
dbUser: user || void 0
|
|
1791
|
+
});
|
|
1792
|
+
} catch {
|
|
1793
|
+
}
|
|
1794
|
+
return {
|
|
1795
|
+
content: [{ type: "text", text: `MySQL \u6267\u884C\u5931\u8D25: ${detail}` }],
|
|
1796
|
+
isError: true
|
|
1797
|
+
};
|
|
1798
|
+
} finally {
|
|
1799
|
+
await connection?.end().catch(() => void 0);
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
};
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1518
1806
|
// src/commands/connect/tools/index.ts
|
|
1519
1807
|
var PLAN_MODE_ASK_QUESTION_HINT = `[SDK \u73AF\u5883\u8BF4\u660E]
|
|
1520
1808
|
AskQuestion \u5DF2\u901A\u8FC7 MCP \u670D\u52A1\u5668 custom-user-tools \u6CE8\u518C\uFF0C\u5DE5\u5177\u540D\u4E3A AskQuestion\u3002
|
|
@@ -1553,6 +1841,17 @@ function createCursorCustomTools(cfg, options) {
|
|
|
1553
1841
|
})
|
|
1554
1842
|
);
|
|
1555
1843
|
}
|
|
1844
|
+
if (options.enableMysqlTools && options.sqlExecutionId && options.workdir) {
|
|
1845
|
+
Object.assign(
|
|
1846
|
+
tools,
|
|
1847
|
+
createMysqlExecuteTool({
|
|
1848
|
+
taskId,
|
|
1849
|
+
executionId: options.sqlExecutionId,
|
|
1850
|
+
workdir: options.workdir,
|
|
1851
|
+
upsertSqlExecution: (args) => cli.webideUpsertSqlExecution(args)
|
|
1852
|
+
})
|
|
1853
|
+
);
|
|
1854
|
+
}
|
|
1556
1855
|
}
|
|
1557
1856
|
return tools;
|
|
1558
1857
|
}
|
|
@@ -1671,6 +1970,8 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
1671
1970
|
enableAskQuestion: options.enableAskQuestion,
|
|
1672
1971
|
askQuestionExecute: options.askQuestionExecute,
|
|
1673
1972
|
enableWebIdePlanTools: options.enableWebIdePlanTools,
|
|
1973
|
+
enableMysqlTools: options.enableMysqlTools,
|
|
1974
|
+
sqlExecutionId: options.sqlExecutionId,
|
|
1674
1975
|
taskId: options.taskId,
|
|
1675
1976
|
workdir
|
|
1676
1977
|
});
|
|
@@ -1805,17 +2106,17 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
1805
2106
|
}
|
|
1806
2107
|
|
|
1807
2108
|
// src/commands/connect/webide-agent-registry.ts
|
|
1808
|
-
import { existsSync as
|
|
1809
|
-
import { dirname as dirname3, resolve as
|
|
2109
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
2110
|
+
import { dirname as dirname3, resolve as resolve5 } from "node:path";
|
|
1810
2111
|
function registryPath(workdir, taskId) {
|
|
1811
|
-
return
|
|
2112
|
+
return resolve5(workdir, ".apm", "webide", taskId, "cursor-agent.json");
|
|
1812
2113
|
}
|
|
1813
2114
|
function readRegistry(path) {
|
|
1814
|
-
if (!
|
|
2115
|
+
if (!existsSync4(path)) {
|
|
1815
2116
|
return {};
|
|
1816
2117
|
}
|
|
1817
2118
|
try {
|
|
1818
|
-
const parsed = JSON.parse(
|
|
2119
|
+
const parsed = JSON.parse(readFileSync4(path, "utf8"));
|
|
1819
2120
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
1820
2121
|
const raw = parsed;
|
|
1821
2122
|
const state = {};
|
|
@@ -1886,20 +2187,20 @@ function saveWebIdeAgentId(workdir, taskId, agentId) {
|
|
|
1886
2187
|
}
|
|
1887
2188
|
function clearWebIdeAgentId(workdir, taskId) {
|
|
1888
2189
|
const path = registryPath(workdir, taskId);
|
|
1889
|
-
if (!
|
|
2190
|
+
if (!existsSync4(path)) return;
|
|
1890
2191
|
syncWebIdeTaskState(workdir, taskId, { agentId: "" });
|
|
1891
2192
|
}
|
|
1892
2193
|
|
|
1893
2194
|
// src/commands/clean-webide-cache.ts
|
|
1894
|
-
import { existsSync as
|
|
1895
|
-
import { resolve as
|
|
2195
|
+
import { existsSync as existsSync5, rmSync } from "node:fs";
|
|
2196
|
+
import { resolve as resolve6 } from "node:path";
|
|
1896
2197
|
import { getDefaultSdkStateRoot } from "@cursor/sdk";
|
|
1897
2198
|
async function purgeCursorAgentStoreForAgent(workdir, agentId) {
|
|
1898
2199
|
const trimmedAgentId = agentId.trim();
|
|
1899
2200
|
const trimmedWorkdir = workdir.trim();
|
|
1900
2201
|
if (!trimmedAgentId || !trimmedWorkdir) return false;
|
|
1901
2202
|
const stateRoot = getDefaultSdkStateRoot(trimmedWorkdir);
|
|
1902
|
-
if (!
|
|
2203
|
+
if (!existsSync5(stateRoot)) return false;
|
|
1903
2204
|
const { SqliteLocalAgentStore } = await import(
|
|
1904
2205
|
/* @vite-ignore */
|
|
1905
2206
|
"@cursor/sdk/sqlite"
|
|
@@ -1963,8 +2264,8 @@ async function cleanWebIdeWorkspaceCache(taskId, workdir) {
|
|
|
1963
2264
|
`[apm] \u65E0 WebIDE agentId\uFF0C\u8DF3\u8FC7 agent store \u6E05\u7406 taskId=${trimmedTaskId}`
|
|
1964
2265
|
);
|
|
1965
2266
|
}
|
|
1966
|
-
const dir =
|
|
1967
|
-
if (
|
|
2267
|
+
const dir = resolve6(trimmedWorkdir, ".apm", "webide", trimmedTaskId);
|
|
2268
|
+
if (existsSync5(dir)) {
|
|
1968
2269
|
rmSync(dir, { recursive: true, force: true });
|
|
1969
2270
|
console.log(`[apm] \u5DF2\u6E05\u7406 WebIDE \u7F13\u5B58 ${dir}`);
|
|
1970
2271
|
} else {
|
|
@@ -1975,11 +2276,11 @@ async function cleanWebIdeWorkspaceCache(taskId, workdir) {
|
|
|
1975
2276
|
// src/commands/connect/webide-ask-question.ts
|
|
1976
2277
|
import { setTimeout as delay } from "node:timers/promises";
|
|
1977
2278
|
var POLL_INTERVAL_MS = 2e3;
|
|
1978
|
-
function
|
|
2279
|
+
function asString4(value) {
|
|
1979
2280
|
return typeof value === "string" ? value.trim() : "";
|
|
1980
2281
|
}
|
|
1981
2282
|
function parseQuestions(args) {
|
|
1982
|
-
const title =
|
|
2283
|
+
const title = asString4(args.title) || void 0;
|
|
1983
2284
|
const raw = args.questions;
|
|
1984
2285
|
if (!Array.isArray(raw) || raw.length === 0) {
|
|
1985
2286
|
throw new Error("AskQuestion \u7F3A\u5C11 questions");
|
|
@@ -1988,16 +2289,16 @@ function parseQuestions(args) {
|
|
|
1988
2289
|
for (const item of raw) {
|
|
1989
2290
|
if (!item || typeof item !== "object" || Array.isArray(item)) continue;
|
|
1990
2291
|
const row = item;
|
|
1991
|
-
const id =
|
|
1992
|
-
const prompt =
|
|
2292
|
+
const id = asString4(row.id);
|
|
2293
|
+
const prompt = asString4(row.prompt);
|
|
1993
2294
|
const optionsRaw = row.options;
|
|
1994
2295
|
if (!id || !prompt || !Array.isArray(optionsRaw)) continue;
|
|
1995
2296
|
const options = [];
|
|
1996
2297
|
for (const opt of optionsRaw) {
|
|
1997
2298
|
if (!opt || typeof opt !== "object" || Array.isArray(opt)) continue;
|
|
1998
2299
|
const o = opt;
|
|
1999
|
-
const oid =
|
|
2000
|
-
const label =
|
|
2300
|
+
const oid = asString4(o.id);
|
|
2301
|
+
const label = asString4(o.label);
|
|
2001
2302
|
if (oid && label) options.push({ id: oid, label });
|
|
2002
2303
|
}
|
|
2003
2304
|
if (options.length < 2) {
|
|
@@ -2490,14 +2791,14 @@ async function ensureWebIdePullRequests(cfg, taskId, workdir) {
|
|
|
2490
2791
|
}
|
|
2491
2792
|
|
|
2492
2793
|
// src/version.ts
|
|
2493
|
-
import { readFileSync as
|
|
2794
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
2494
2795
|
import { dirname as dirname4, join as join4 } from "path";
|
|
2495
2796
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2496
2797
|
function readCliVersion() {
|
|
2497
2798
|
try {
|
|
2498
2799
|
const dir = dirname4(fileURLToPath2(import.meta.url));
|
|
2499
2800
|
const pkgPath = join4(dir, "..", "package.json");
|
|
2500
|
-
const pkg = JSON.parse(
|
|
2801
|
+
const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
|
|
2501
2802
|
return pkg.version ?? "0.0.0";
|
|
2502
2803
|
} catch {
|
|
2503
2804
|
return "0.0.0";
|
|
@@ -2505,7 +2806,7 @@ function readCliVersion() {
|
|
|
2505
2806
|
}
|
|
2506
2807
|
|
|
2507
2808
|
// src/commands/sync-webide-attachments.ts
|
|
2508
|
-
import { existsSync as
|
|
2809
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
2509
2810
|
import { join as join5 } from "path";
|
|
2510
2811
|
var MANIFEST_FILE = ".sync-manifest.json";
|
|
2511
2812
|
async function downloadAttachment(cfg, attachmentId) {
|
|
@@ -2523,12 +2824,12 @@ async function downloadAttachment(cfg, attachmentId) {
|
|
|
2523
2824
|
}
|
|
2524
2825
|
function loadManifest(dir) {
|
|
2525
2826
|
const path = join5(dir, MANIFEST_FILE);
|
|
2526
|
-
if (!
|
|
2827
|
+
if (!existsSync6(path)) {
|
|
2527
2828
|
return { version: 1, attachments: {} };
|
|
2528
2829
|
}
|
|
2529
2830
|
try {
|
|
2530
2831
|
const parsed = JSON.parse(
|
|
2531
|
-
|
|
2832
|
+
readFileSync6(path, "utf8")
|
|
2532
2833
|
);
|
|
2533
2834
|
if (parsed?.version === 1 && parsed.attachments && typeof parsed.attachments === "object") {
|
|
2534
2835
|
return parsed;
|
|
@@ -2546,7 +2847,7 @@ function saveManifest(dir, manifest) {
|
|
|
2546
2847
|
);
|
|
2547
2848
|
}
|
|
2548
2849
|
function isAttachmentUpToDate(entry, item, dest) {
|
|
2549
|
-
if (!entry || !
|
|
2850
|
+
if (!entry || !existsSync6(dest)) return false;
|
|
2550
2851
|
if (entry.name !== item.name) return false;
|
|
2551
2852
|
const createdAt = item.createdAt ?? "";
|
|
2552
2853
|
return entry.createdAt === createdAt;
|
|
@@ -2593,14 +2894,14 @@ async function syncWebIdeAttachments(cfg, taskId, workdir, attachments) {
|
|
|
2593
2894
|
|
|
2594
2895
|
// src/utils/project-documents.ts
|
|
2595
2896
|
import {
|
|
2596
|
-
existsSync as
|
|
2897
|
+
existsSync as existsSync7,
|
|
2597
2898
|
readdirSync as readdirSync3,
|
|
2598
|
-
readFileSync as
|
|
2899
|
+
readFileSync as readFileSync7,
|
|
2599
2900
|
rmSync as rmSync2,
|
|
2600
2901
|
writeFileSync as writeFileSync5
|
|
2601
2902
|
} from "fs";
|
|
2602
2903
|
import { createHash } from "crypto";
|
|
2603
|
-
import { dirname as dirname5, join as join6, relative as
|
|
2904
|
+
import { dirname as dirname5, join as join6, relative as relative3, sep } from "path";
|
|
2604
2905
|
var MANIFEST_FILE2 = "manifest.json";
|
|
2605
2906
|
function normalizeProjectIdForPath(projectId) {
|
|
2606
2907
|
const id = projectId.trim();
|
|
@@ -2642,12 +2943,12 @@ function readLocalManifest(apmRoot, projectId) {
|
|
|
2642
2943
|
projectDocumentsDir(apmRoot, projectId),
|
|
2643
2944
|
MANIFEST_FILE2
|
|
2644
2945
|
);
|
|
2645
|
-
if (!
|
|
2946
|
+
if (!existsSync7(manifestPath3)) {
|
|
2646
2947
|
return null;
|
|
2647
2948
|
}
|
|
2648
2949
|
try {
|
|
2649
2950
|
return JSON.parse(
|
|
2650
|
-
|
|
2951
|
+
readFileSync7(manifestPath3, "utf8")
|
|
2651
2952
|
);
|
|
2652
2953
|
} catch {
|
|
2653
2954
|
return null;
|
|
@@ -2655,7 +2956,7 @@ function readLocalManifest(apmRoot, projectId) {
|
|
|
2655
2956
|
}
|
|
2656
2957
|
function listLocalDocumentPaths(apmRoot, projectId) {
|
|
2657
2958
|
const root = projectDocumentsDir(apmRoot, projectId);
|
|
2658
|
-
if (!
|
|
2959
|
+
if (!existsSync7(root)) {
|
|
2659
2960
|
return [];
|
|
2660
2961
|
}
|
|
2661
2962
|
const paths = [];
|
|
@@ -2669,7 +2970,7 @@ function listLocalDocumentPaths(apmRoot, projectId) {
|
|
|
2669
2970
|
if (entry.isFile() && entry.name === MANIFEST_FILE2) {
|
|
2670
2971
|
continue;
|
|
2671
2972
|
}
|
|
2672
|
-
const rel =
|
|
2973
|
+
const rel = relative3(root, abs).split(sep).join("/");
|
|
2673
2974
|
paths.push(rel);
|
|
2674
2975
|
}
|
|
2675
2976
|
};
|
|
@@ -2790,7 +3091,7 @@ ${diagnostic ?? ""}`);
|
|
|
2790
3091
|
const absPath = toFsPath(
|
|
2791
3092
|
projectDocumentLocalPath(targetApmDir, projectId, path)
|
|
2792
3093
|
);
|
|
2793
|
-
if (
|
|
3094
|
+
if (existsSync7(absPath)) {
|
|
2794
3095
|
rmSync2(absPath, { force: true });
|
|
2795
3096
|
deleted += 1;
|
|
2796
3097
|
}
|
|
@@ -2839,7 +3140,7 @@ async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot, options) {
|
|
|
2839
3140
|
const absPath = toFsPath(
|
|
2840
3141
|
projectDocumentLocalPath(targetApmDir, projectId, path)
|
|
2841
3142
|
);
|
|
2842
|
-
const content =
|
|
3143
|
+
const content = readFileSync7(absPath, "utf8");
|
|
2843
3144
|
const contentHash = hashLocalFileContent(content);
|
|
2844
3145
|
if (remoteHashByPath.get(path) === contentHash) {
|
|
2845
3146
|
continue;
|
|
@@ -2860,7 +3161,7 @@ async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot, options) {
|
|
|
2860
3161
|
}
|
|
2861
3162
|
|
|
2862
3163
|
// src/commands/connect/cli-version-sync.ts
|
|
2863
|
-
import { existsSync as
|
|
3164
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
|
|
2864
3165
|
import { join as join7 } from "path";
|
|
2865
3166
|
var CLI_VERSION_FILE = ".cli-version.json";
|
|
2866
3167
|
function manifestPath2(apmDir) {
|
|
@@ -2868,12 +3169,12 @@ function manifestPath2(apmDir) {
|
|
|
2868
3169
|
}
|
|
2869
3170
|
function loadManifest2(apmDir) {
|
|
2870
3171
|
const path = toFsPath(manifestPath2(apmDir));
|
|
2871
|
-
if (!
|
|
3172
|
+
if (!existsSync8(path)) {
|
|
2872
3173
|
return null;
|
|
2873
3174
|
}
|
|
2874
3175
|
try {
|
|
2875
3176
|
const parsed = JSON.parse(
|
|
2876
|
-
|
|
3177
|
+
readFileSync8(path, "utf8")
|
|
2877
3178
|
);
|
|
2878
3179
|
if (parsed?.version === 1 && typeof parsed.cliVersion === "string" && parsed.cliVersion.trim()) {
|
|
2879
3180
|
return parsed;
|
|
@@ -2909,6 +3210,24 @@ function markSkillsSyncedForCliVersion(workdir, cliVersion) {
|
|
|
2909
3210
|
syncedInSession.set(workdir, cliVersion);
|
|
2910
3211
|
}
|
|
2911
3212
|
|
|
3213
|
+
// src/commands/connect/parse-webide-prompt-payload.ts
|
|
3214
|
+
function parseWebIdePromptPayload(content) {
|
|
3215
|
+
const marker = "\n\npayload=";
|
|
3216
|
+
const index = content.lastIndexOf(marker);
|
|
3217
|
+
if (index < 0) return null;
|
|
3218
|
+
const jsonText = content.slice(index + marker.length).trim();
|
|
3219
|
+
if (!jsonText) return null;
|
|
3220
|
+
try {
|
|
3221
|
+
const parsed = JSON.parse(jsonText);
|
|
3222
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
3223
|
+
return parsed;
|
|
3224
|
+
}
|
|
3225
|
+
} catch {
|
|
3226
|
+
return null;
|
|
3227
|
+
}
|
|
3228
|
+
return null;
|
|
3229
|
+
}
|
|
3230
|
+
|
|
2912
3231
|
// src/commands/connect/handle-webide-message.ts
|
|
2913
3232
|
var WEBIDE_CODE_CHANGE_ACTIONS = /* @__PURE__ */ new Set([
|
|
2914
3233
|
"skip-plan",
|
|
@@ -2921,6 +3240,12 @@ var WEBIDE_CODE_CHANGE_ACTIONS = /* @__PURE__ */ new Set([
|
|
|
2921
3240
|
function shouldCommitAfterWebIdeMessage(action) {
|
|
2922
3241
|
return WEBIDE_CODE_CHANGE_ACTIONS.has(action);
|
|
2923
3242
|
}
|
|
3243
|
+
function isStartLocalServicesAction(action) {
|
|
3244
|
+
return action === "enter-manual-test" || action === "skip-test";
|
|
3245
|
+
}
|
|
3246
|
+
function isExecuteSqlAction(action) {
|
|
3247
|
+
return action === "execute-sql";
|
|
3248
|
+
}
|
|
2924
3249
|
function syncLocalTaskStatus(workdir, taskId, msg, status) {
|
|
2925
3250
|
syncWebIdeTaskState(workdir, taskId, {
|
|
2926
3251
|
messageId: msg.messageId,
|
|
@@ -3129,7 +3454,11 @@ async function handleWebIdeInboundMessage(cfg, msg, signal) {
|
|
|
3129
3454
|
"running"
|
|
3130
3455
|
);
|
|
3131
3456
|
await syncPrepLog();
|
|
3132
|
-
const
|
|
3457
|
+
const startLocalServices = isStartLocalServicesAction(msg.action);
|
|
3458
|
+
const executeSql = isExecuteSqlAction(msg.action);
|
|
3459
|
+
const savedAgentId = startLocalServices ? void 0 : loadWebIdeAgentId(workdir, taskId);
|
|
3460
|
+
const promptPayload = executeSql ? parseWebIdePromptPayload(msg.content) : null;
|
|
3461
|
+
const sqlExecutionId = executeSql && typeof promptPayload?.executionId === "string" ? promptPayload.executionId.trim() : void 0;
|
|
3133
3462
|
const outcome = await runCursorAgent(
|
|
3134
3463
|
cfg,
|
|
3135
3464
|
{
|
|
@@ -3146,17 +3475,23 @@ async function handleWebIdeInboundMessage(cfg, msg, signal) {
|
|
|
3146
3475
|
forceSend: true,
|
|
3147
3476
|
eventSession,
|
|
3148
3477
|
appendMessageContent: (content) => appendContent(cfg, messageId, content),
|
|
3149
|
-
askQuestionExecute: createWebIdeAskQuestionExecute({
|
|
3478
|
+
askQuestionExecute: startLocalServices ? void 0 : createWebIdeAskQuestionExecute({
|
|
3150
3479
|
cfg,
|
|
3151
3480
|
taskId,
|
|
3152
3481
|
signal
|
|
3153
3482
|
}),
|
|
3154
|
-
|
|
3483
|
+
enableAskQuestion: !startLocalServices && !executeSql,
|
|
3484
|
+
enableWebIdePlanTools: !startLocalServices && !executeSql,
|
|
3485
|
+
enablePtySessionMcp: startLocalServices,
|
|
3486
|
+
enableMysqlTools: executeSql,
|
|
3487
|
+
sqlExecutionId,
|
|
3155
3488
|
enableSandbox: false,
|
|
3156
|
-
onInvalidatePersistedAgentId: () => clearWebIdeAgentId(workdir, taskId),
|
|
3489
|
+
onInvalidatePersistedAgentId: startLocalServices ? void 0 : () => clearWebIdeAgentId(workdir, taskId),
|
|
3157
3490
|
taskId,
|
|
3158
3491
|
createRemoteLogSync: (agentId) => {
|
|
3159
|
-
|
|
3492
|
+
if (!startLocalServices) {
|
|
3493
|
+
saveWebIdeAgentId(workdir, taskId, agentId);
|
|
3494
|
+
}
|
|
3160
3495
|
logSyncRef.current = createThrottledWebIdeMessageLogSync(
|
|
3161
3496
|
cfg,
|
|
3162
3497
|
{ taskId, messageId, agentId },
|
|
@@ -3170,7 +3505,9 @@ async function handleWebIdeInboundMessage(cfg, msg, signal) {
|
|
|
3170
3505
|
return logSyncRef.current;
|
|
3171
3506
|
},
|
|
3172
3507
|
onRunStarted: async ({ agentId, runId }) => {
|
|
3173
|
-
|
|
3508
|
+
if (!startLocalServices) {
|
|
3509
|
+
saveWebIdeAgentId(workdir, taskId, agentId);
|
|
3510
|
+
}
|
|
3174
3511
|
eventSession.addCliStep(
|
|
3175
3512
|
"\u542F\u52A8 Cursor Agent",
|
|
3176
3513
|
`agentId=${agentId} runId=${runId}`,
|
|
@@ -3182,7 +3519,9 @@ async function handleWebIdeInboundMessage(cfg, msg, signal) {
|
|
|
3182
3519
|
}
|
|
3183
3520
|
}
|
|
3184
3521
|
);
|
|
3185
|
-
|
|
3522
|
+
if (!startLocalServices) {
|
|
3523
|
+
saveWebIdeAgentId(workdir, taskId, outcome.agentId);
|
|
3524
|
+
}
|
|
3186
3525
|
const tokenUsage = outcome.usage != null ? {
|
|
3187
3526
|
modelId: outcome.modelId ?? (msg.model?.trim() || void 0),
|
|
3188
3527
|
inputTokens: outcome.usage.inputTokens,
|
|
@@ -3202,6 +3541,20 @@ async function handleWebIdeInboundMessage(cfg, msg, signal) {
|
|
|
3202
3541
|
detail,
|
|
3203
3542
|
tokenUsage
|
|
3204
3543
|
);
|
|
3544
|
+
if (executeSql && sqlExecutionId) {
|
|
3545
|
+
try {
|
|
3546
|
+
await api.cli.webideFinalizeSqlExecution({
|
|
3547
|
+
executionId: sqlExecutionId,
|
|
3548
|
+
status: "FAILED",
|
|
3549
|
+
error: detail
|
|
3550
|
+
});
|
|
3551
|
+
} catch (finalizeErr) {
|
|
3552
|
+
console.warn(
|
|
3553
|
+
"[apm] finalize sql execution failed:",
|
|
3554
|
+
finalizeErr instanceof Error ? finalizeErr.message : finalizeErr
|
|
3555
|
+
);
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3205
3558
|
await setError(cfg, messageId, detail);
|
|
3206
3559
|
syncLocalTaskStatus(workdir, taskId, msg, "FAILED");
|
|
3207
3560
|
return;
|
|
@@ -3213,6 +3566,20 @@ async function handleWebIdeInboundMessage(cfg, msg, signal) {
|
|
|
3213
3566
|
"\u5DF2\u53D6\u6D88",
|
|
3214
3567
|
tokenUsage
|
|
3215
3568
|
);
|
|
3569
|
+
if (executeSql && sqlExecutionId) {
|
|
3570
|
+
try {
|
|
3571
|
+
await api.cli.webideFinalizeSqlExecution({
|
|
3572
|
+
executionId: sqlExecutionId,
|
|
3573
|
+
status: "CANCELLED",
|
|
3574
|
+
error: "\u5DF2\u53D6\u6D88"
|
|
3575
|
+
});
|
|
3576
|
+
} catch (finalizeErr) {
|
|
3577
|
+
console.warn(
|
|
3578
|
+
"[apm] finalize sql execution cancelled:",
|
|
3579
|
+
finalizeErr instanceof Error ? finalizeErr.message : finalizeErr
|
|
3580
|
+
);
|
|
3581
|
+
}
|
|
3582
|
+
}
|
|
3216
3583
|
await updateStatus(cfg, messageId, "CANCELLED");
|
|
3217
3584
|
syncLocalTaskStatus(workdir, taskId, msg, "CANCELLED");
|
|
3218
3585
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-project-manage-cli",
|
|
3
|
-
"version": "8.0.
|
|
3
|
+
"version": "8.0.8",
|
|
4
4
|
"description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
"prepublishOnly": "npm run build"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@types/ws": "~8.5.14",
|
|
22
21
|
"@types/node": "^22.0.0",
|
|
23
|
-
"
|
|
22
|
+
"@types/ws": "~8.5.14",
|
|
24
23
|
"esbuild": "~0.28.0",
|
|
24
|
+
"typescript": "~5.6.0",
|
|
25
25
|
"vitest": "~4.1.5"
|
|
26
26
|
},
|
|
27
27
|
"engines": {
|
|
@@ -32,9 +32,10 @@
|
|
|
32
32
|
"@connectrpc/connect": "~1.7.0",
|
|
33
33
|
"@connectrpc/connect-node": "~1.7.0",
|
|
34
34
|
"@cursor/sdk": "^1.0.22",
|
|
35
|
-
"ws": "~8.18.0",
|
|
36
|
-
"listpage-http": "~0.0.318",
|
|
37
35
|
"commander": "~14.0.3",
|
|
38
|
-
"
|
|
36
|
+
"listpage-http": "~0.0.318",
|
|
37
|
+
"minio": "~8.0.7",
|
|
38
|
+
"mysql2": "~3.14.5",
|
|
39
|
+
"ws": "~8.18.0"
|
|
39
40
|
}
|
|
40
41
|
}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
- `webide_reply.md`:输出通道(每轮必守)
|
|
15
15
|
- `webide_git_commit.md`:开发 / 修码时的 Git 提交与 push
|
|
16
16
|
- `webide_sql_change.md`:有表结构或 SQL 变更时,须在 `AppendMessage` 中醒目提示
|
|
17
|
+
- `webide_sql_execute.md`:execute-sql 轮次连库执行 SQL 时 Read
|
|
17
18
|
- `webide_terminal.md`:长驻进程禁止用 Shell;本地服务由顶栏「启动项目」触发,开发任务不要自行起服务
|
|
18
19
|
- `webide_testcase.md`:生成测试用例并调用 `UpsertWebIdeTestCases`
|
|
19
20
|
- `webide_merge.md`:验收通过后调用 `MergeWebIdePullRequests`
|
|
@@ -15,9 +15,11 @@
|
|
|
15
15
|
在回复中增加小节(标题可用「数据表 / SQL 变更」),至少包含:
|
|
16
16
|
|
|
17
17
|
1. **涉及表**:表名列表与变更类型(如新增列、建表)
|
|
18
|
-
2. **SQL
|
|
19
|
-
3. **待执行 SQL
|
|
20
|
-
4.
|
|
18
|
+
2. **SQL 产物路径**:仓库内相关文件路径(**必填**;完整、可 Read;多仓写明仓库)
|
|
19
|
+
3. **待执行 SQL**:完整、可复制的语句预览,放在 ` ```sql ` 代码块中,按执行顺序列出;禁止用 `...` 省略表名/字段名(预览仅供人看,执行时以产物文件为准)
|
|
20
|
+
4. **执行提醒**:请在目标环境数据库执行;可点 SQL 块上的「执行」由助手连库执行
|
|
21
|
+
|
|
22
|
+
**禁止**写 Flyway、「若启动已自动迁移可跳过」或同类表述。
|
|
21
23
|
|
|
22
24
|
多仓时注明 SQL 属于哪个仓库(如后端仓)。
|
|
23
25
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
## WebIDE 执行 SQL(execute-sql 轮次)
|
|
2
|
+
|
|
3
|
+
适用场景:用户在手工测试阶段点击消息里 SQL 块的「执行」。
|
|
4
|
+
|
|
5
|
+
### 本轮目标
|
|
6
|
+
|
|
7
|
+
1. 根据你**此前开发轮次**在 AppendMessage 里写明的 **SQL 产物路径**,定位工作区内的文件(可用 Read / Glob 确认)。
|
|
8
|
+
2. 从 deploy 文档、`application.yml` / `.env` 等推断 MySQL 连接信息(**禁止瞎猜密码**)。
|
|
9
|
+
3. 调用 **MysqlExecute**,传入连接参数,以及产物文件的 **绝对路径**(`path`)。**不要**把 SQL 全文作为工具参数。
|
|
10
|
+
4. 用 **AppendMessage** 汇总执行结果(成功/失败、影响行、必要时的校验说明)。
|
|
11
|
+
|
|
12
|
+
### MysqlExecute 参数
|
|
13
|
+
|
|
14
|
+
- `host` / `port` / `user` / `password` / `database`
|
|
15
|
+
- `path`:SQL 产物在磁盘上的 **绝对路径**(须在当前工作区内)。CLI 自行读文件并执行多语句脚本。
|
|
16
|
+
|
|
17
|
+
### 硬性约束
|
|
18
|
+
|
|
19
|
+
- **禁止**把 SQL 正文塞进工具参数;由 CLI 读 `path` 指向的文件。
|
|
20
|
+
- **禁止**使用对话里 SQL 预览正文代替文件内容。
|
|
21
|
+
- **禁止**改代码、Git 提交、启动 dev 服务。
|
|
22
|
+
- 密码不得写入 AppendMessage。
|
|
23
|
+
- 找不到产物文件或路径在工作区外 → AppendMessage 说明原因并以错误结束。
|
|
24
|
+
|
|
25
|
+
### 连接信息来源
|
|
26
|
+
|
|
27
|
+
1. Read `.apm/project/<项目ID>/` 下文件名或路径包含 `deploy` 的文档。
|
|
28
|
+
2. 若无,Glob 工作区内 `application*.yml` / `.env*` 等配置文件。
|
|
29
|
+
3. 仍无法确定 → 不要猜测,AppendMessage 说明缺少连接配置。
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
会话与 WebIDE「终端」面板共用同一套 `pty-session-mcp serve`。工具名:`create` / `write` / `read` / `resize` / `kill` / `list`。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
本地服务由用户在顶栏「启动项目」触发,或在进入手工测试时由启动指令触发(独立 Agent)。**开发/修码任务不要自行起或重启长驻进程**。
|
|
8
8
|
|
|
9
9
|
## 工具
|
|
10
10
|
|
|
@@ -26,19 +26,20 @@
|
|
|
26
26
|
3. 按文档中的 cwd / command / 服务划分创建会话。
|
|
27
27
|
4. 文档不存在或未写明启动方式 → 结束任务,**禁止猜测命令**。
|
|
28
28
|
|
|
29
|
-
## 时机(顶栏启动 /
|
|
29
|
+
## 时机(顶栏启动 / 重启 / 进入手工测试)
|
|
30
30
|
|
|
31
31
|
固定顺序:
|
|
32
32
|
|
|
33
33
|
1. **`list`**
|
|
34
|
-
2.
|
|
35
|
-
3.
|
|
36
|
-
4. `
|
|
34
|
+
2. 顶栏启动:无对应 `name` 的活会话 → 按部署文档 `create` + `write`;已有同名活会话则结束,不重起
|
|
35
|
+
3. 顶栏重启:先 `kill` 目标 `name`,再按部署文档 `create` + `write`(不要杀掉其它端)
|
|
36
|
+
4. 进入手工测试:文档写明的每一端,无对应 `name` 则启动;**已有同名活会话则重启**(只 kill 该 name,再 `create` + `write`)
|
|
37
|
+
5. `read` 等待就绪日志 / 端口即可
|
|
37
38
|
|
|
38
|
-
|
|
39
|
+
用户在「终端」面板看日志,在「浏览器」面板看远程主机外网地址。
|
|
39
40
|
|
|
40
41
|
## 规范
|
|
41
42
|
|
|
42
43
|
1. 禁止用 Shell / 后台进程方式起 `dev` / `serve` 等长驻命令。
|
|
43
|
-
2. 观察日志请用户看 WebIDE
|
|
44
|
+
2. 观察日志请用户看 WebIDE「终端」面板;页面请用户看「浏览器」面板(远程主机外网地址)。
|
|
44
45
|
3. 不再需要时可 kill;不要留下孤儿进程。
|