ai-project-manage-cli 7.1.13 → 7.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.js +127 -75
- package/dist/webide-message-worker.js +122 -59
- package/package.json +1 -1
- package/template/AGENTS.md +3 -3
- package/template/rules/webide_terminal.md +13 -11
package/README.md
CHANGED
|
@@ -57,6 +57,6 @@ apm daemon start -f
|
|
|
57
57
|
|
|
58
58
|
使用全局 PM2(未安装时会自动执行 `npm install -g pm2`);同一时刻仅允许一个 connect。启动时会自动检测 CLI 更新并刷新守护配置。
|
|
59
59
|
|
|
60
|
-
`apm pull`
|
|
60
|
+
`apm pull` 会自动将平台登记的**项目文档**同步到 `.apm/docs/`(含 `manifest.json`)。Agent 修改 `.apm/docs/` 下文件后,`apm connect` 处理完消息会自动推回平台。
|
|
61
61
|
|
|
62
62
|
详见仓库根目录 [docs/CLI.md](../../docs/CLI.md)。
|
package/dist/index.js
CHANGED
|
@@ -212,10 +212,18 @@ var init_request_config = __esm({
|
|
|
212
212
|
method: "PUT",
|
|
213
213
|
path: "/cli/webide/terminals/meta"
|
|
214
214
|
}),
|
|
215
|
+
acquireSteelBrowser: defineEndpoint({
|
|
216
|
+
method: "POST",
|
|
217
|
+
path: "/cli/steel-browser/acquire"
|
|
218
|
+
}),
|
|
215
219
|
branchBaseline: defineEndpoint({
|
|
216
220
|
method: "GET",
|
|
217
221
|
path: "/cli/tasks/branch-baseline"
|
|
218
222
|
}),
|
|
223
|
+
projectBaseBranch: defineEndpoint({
|
|
224
|
+
method: "GET",
|
|
225
|
+
path: "/cli/tasks/project-base-branch"
|
|
226
|
+
}),
|
|
219
227
|
listSessionsForBranchCleanup: defineEndpoint({
|
|
220
228
|
method: "GET",
|
|
221
229
|
path: "/cli/sessions/branch-cleanup"
|
|
@@ -252,21 +260,21 @@ var init_request_config = __esm({
|
|
|
252
260
|
method: "POST",
|
|
253
261
|
path: "/cli/webide/pull-requests/merge"
|
|
254
262
|
}),
|
|
255
|
-
|
|
263
|
+
getProjectDocumentManifest: defineEndpoint({
|
|
256
264
|
method: "GET",
|
|
257
|
-
path: "/cli/
|
|
265
|
+
path: "/cli/project-documents/manifest"
|
|
258
266
|
}),
|
|
259
|
-
|
|
267
|
+
listProjectDocuments: defineEndpoint({
|
|
260
268
|
method: "GET",
|
|
261
|
-
path: "/cli/
|
|
269
|
+
path: "/cli/project-documents"
|
|
262
270
|
}),
|
|
263
|
-
|
|
271
|
+
upsertProjectDocument: defineEndpoint({
|
|
264
272
|
method: "PUT",
|
|
265
|
-
path: "/cli/
|
|
273
|
+
path: "/cli/project-documents/upsert"
|
|
266
274
|
}),
|
|
267
|
-
|
|
275
|
+
removeProjectDocument: defineEndpoint({
|
|
268
276
|
method: "DELETE",
|
|
269
|
-
path: "/cli/
|
|
277
|
+
path: "/cli/project-documents"
|
|
270
278
|
}),
|
|
271
279
|
createTaskDeployment: defineEndpoint({
|
|
272
280
|
method: "POST",
|
|
@@ -648,11 +656,33 @@ function forceKillProcessTree(pid) {
|
|
|
648
656
|
}
|
|
649
657
|
}
|
|
650
658
|
}
|
|
659
|
+
async function listProcessTreePids(rootPid) {
|
|
660
|
+
const pids = [rootPid];
|
|
661
|
+
const queue = [rootPid];
|
|
662
|
+
while (queue.length > 0) {
|
|
663
|
+
const parent = queue.shift();
|
|
664
|
+
try {
|
|
665
|
+
const { stdout } = await execFileAsync3("pgrep", ["-P", String(parent)], {
|
|
666
|
+
timeout: 2e3
|
|
667
|
+
});
|
|
668
|
+
for (const line of stdout.split("\n")) {
|
|
669
|
+
const child = Number(line.trim());
|
|
670
|
+
if (child > 0 && !pids.includes(child)) {
|
|
671
|
+
pids.push(child);
|
|
672
|
+
queue.push(child);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
} catch {
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
return pids;
|
|
679
|
+
}
|
|
651
680
|
async function probeListenPorts(pid) {
|
|
652
681
|
try {
|
|
682
|
+
const treePids = await listProcessTreePids(pid);
|
|
653
683
|
const { stdout } = await execFileAsync3(
|
|
654
684
|
"lsof",
|
|
655
|
-
["-nP", `-p${
|
|
685
|
+
["-nP", "-a", `-p${treePids.join(",")}`, "-iTCP", "-sTCP:LISTEN"],
|
|
656
686
|
{ timeout: 3e3, maxBuffer: 1024 * 1024 }
|
|
657
687
|
);
|
|
658
688
|
const ports = /* @__PURE__ */ new Set();
|
|
@@ -944,23 +974,16 @@ var init_webide_terminal_registry = __esm({
|
|
|
944
974
|
if (!session.pid) return;
|
|
945
975
|
const ports = await probeListenPorts(session.pid);
|
|
946
976
|
if (ports.length === 0) return;
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
}
|
|
977
|
+
const same = ports.length === session.ports.length && ports.every((p, i) => p === session.ports[i]);
|
|
978
|
+
if (same) return;
|
|
979
|
+
session.ports = ports;
|
|
980
|
+
if (!session.primaryUrl && session.ports[0]) {
|
|
981
|
+
session.primaryUrl = `http://127.0.0.1:${session.ports[0]}`;
|
|
953
982
|
}
|
|
954
|
-
if (
|
|
955
|
-
session.
|
|
956
|
-
if (!session.primaryUrl && session.ports[0]) {
|
|
957
|
-
session.primaryUrl = `http://127.0.0.1:${session.ports[0]}`;
|
|
958
|
-
}
|
|
959
|
-
if (session.status === "RUNNING" || session.status === "STARTING") {
|
|
960
|
-
session.status = "READY";
|
|
961
|
-
}
|
|
962
|
-
void this.syncStatus(session);
|
|
983
|
+
if (session.status === "RUNNING" || session.status === "STARTING") {
|
|
984
|
+
session.status = "READY";
|
|
963
985
|
}
|
|
986
|
+
void this.syncStatus(session);
|
|
964
987
|
}
|
|
965
988
|
async syncStatus(session) {
|
|
966
989
|
const api = createApmApiClient(this.cfg);
|
|
@@ -1629,7 +1652,7 @@ ${diagnostic ?? ""}
|
|
|
1629
1652
|
return { synced: true, repositoryId, configName: config.name };
|
|
1630
1653
|
}
|
|
1631
1654
|
|
|
1632
|
-
// src/
|
|
1655
|
+
// src/project-documents-sync.ts
|
|
1633
1656
|
init_client();
|
|
1634
1657
|
init_config();
|
|
1635
1658
|
import {
|
|
@@ -1643,7 +1666,7 @@ import { createHash } from "crypto";
|
|
|
1643
1666
|
import { dirname as dirname2, join as join4, relative, sep } from "path";
|
|
1644
1667
|
var MANIFEST_FILE = "manifest.json";
|
|
1645
1668
|
function projectDocumentsDir(apmRoot) {
|
|
1646
|
-
return join4(apmRoot ?? workspaceApmDir(), "
|
|
1669
|
+
return join4(apmRoot ?? workspaceApmDir(), "docs");
|
|
1647
1670
|
}
|
|
1648
1671
|
function projectDocumentLocalPath(apmRoot, documentPath) {
|
|
1649
1672
|
const normalized = normalizeLocalDocumentPath(documentPath);
|
|
@@ -1720,41 +1743,65 @@ function diffManifestPaths(remote, local) {
|
|
|
1720
1743
|
}
|
|
1721
1744
|
return { download, deleteLocal };
|
|
1722
1745
|
}
|
|
1723
|
-
async function
|
|
1746
|
+
async function resolveProjectIdForSync(api, workdirPath) {
|
|
1747
|
+
try {
|
|
1748
|
+
const baseline = await api.cli.workspaceBaseline({ workdirPath });
|
|
1749
|
+
const projectId = baseline.projectId?.trim() || null;
|
|
1750
|
+
if (projectId) {
|
|
1751
|
+
return { projectId, diagnostic: null };
|
|
1752
|
+
}
|
|
1753
|
+
const projectIds = baseline.projectIds ?? [];
|
|
1754
|
+
if (projectIds.length === 0) {
|
|
1755
|
+
return {
|
|
1756
|
+
projectId: null,
|
|
1757
|
+
diagnostic: baseline.diagnostic?.message ?? `\u5DE5\u4F5C\u7A7A\u95F4\u672A\u5173\u8054\u9879\u76EE\uFF0C\u65E0\u6CD5\u540C\u6B65\u9879\u76EE\u6587\u6863\uFF08\u76EE\u5F55\uFF1A${workdirPath}\uFF09\u3002
|
|
1758
|
+
\u8BF7\u5728\u5E73\u53F0\u5C06\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u5230\u552F\u4E00\u9879\u76EE\u3002`
|
|
1759
|
+
};
|
|
1760
|
+
}
|
|
1761
|
+
return {
|
|
1762
|
+
projectId: null,
|
|
1763
|
+
diagnostic: `\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u4E86 ${projectIds.length} \u4E2A\u9879\u76EE\uFF0C\u65E0\u6CD5\u786E\u5B9A\u540C\u6B65\u76EE\u6807\u3002
|
|
1764
|
+
\u8BF7\u786E\u4FDD\u8BE5\u5DE5\u4F5C\u7A7A\u95F4\u53EA\u5173\u8054\u4E00\u4E2A\u9879\u76EE\u540E\u518D\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002`
|
|
1765
|
+
};
|
|
1766
|
+
} catch (err) {
|
|
1767
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1768
|
+
return {
|
|
1769
|
+
projectId: null,
|
|
1770
|
+
diagnostic: detail.replace(/^\[apm\]\s*/, "")
|
|
1771
|
+
};
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
async function syncProjectDocumentsPull(workdirPath, apmDir) {
|
|
1724
1775
|
const empty = {
|
|
1725
1776
|
synced: false,
|
|
1726
|
-
|
|
1777
|
+
projectId: null,
|
|
1727
1778
|
downloaded: 0,
|
|
1728
1779
|
deleted: 0
|
|
1729
1780
|
};
|
|
1730
1781
|
const cfg = await tryReadApmConfig();
|
|
1731
1782
|
if (!cfg || !resolveApiKey(cfg)) {
|
|
1732
1783
|
console.log(
|
|
1733
|
-
"[apm] \u672A\u68C0\u6D4B\u5230\u767B\u5F55\u4FE1\u606F\uFF0C\u8DF3\u8FC7\
|
|
1784
|
+
"[apm] \u672A\u68C0\u6D4B\u5230\u767B\u5F55\u4FE1\u606F\uFF0C\u8DF3\u8FC7\u9879\u76EE\u6587\u6863\u540C\u6B65\u3002\n[apm] \u8BF7\u5148\u6267\u884C apm login\u3002"
|
|
1734
1785
|
);
|
|
1735
1786
|
return empty;
|
|
1736
1787
|
}
|
|
1737
1788
|
const api = createApmApiClient(cfg);
|
|
1738
|
-
const {
|
|
1789
|
+
const { projectId, diagnostic } = await resolveProjectIdForSync(
|
|
1739
1790
|
api,
|
|
1740
1791
|
workdirPath
|
|
1741
1792
|
);
|
|
1742
|
-
if (!
|
|
1743
|
-
console.log(
|
|
1744
|
-
|
|
1745
|
-
${diagnostic ?? ""}`
|
|
1746
|
-
);
|
|
1793
|
+
if (!projectId) {
|
|
1794
|
+
console.log(`[apm] \u672A\u80FD\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002
|
|
1795
|
+
${diagnostic ?? ""}`);
|
|
1747
1796
|
return empty;
|
|
1748
1797
|
}
|
|
1749
1798
|
const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
|
|
1750
|
-
const
|
|
1751
|
-
await ensureDirExists(
|
|
1752
|
-
const { manifest: remoteManifest } = await api.cli.
|
|
1799
|
+
const docsDir = projectDocumentsDir(targetApmDir);
|
|
1800
|
+
await ensureDirExists(docsDir);
|
|
1801
|
+
const { manifest: remoteManifest } = await api.cli.getProjectDocumentManifest({ projectId });
|
|
1753
1802
|
if (!remoteManifest) {
|
|
1754
|
-
console.log(
|
|
1755
|
-
|
|
1756
|
-
);
|
|
1757
|
-
return { ...empty, repositoryId };
|
|
1803
|
+
console.log(`[apm] \u9879\u76EE ${projectId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`);
|
|
1804
|
+
return { ...empty, projectId };
|
|
1758
1805
|
}
|
|
1759
1806
|
const localManifest = readLocalManifest(targetApmDir);
|
|
1760
1807
|
const { download, deleteLocal } = diffManifestPaths(
|
|
@@ -1763,12 +1810,14 @@ ${diagnostic ?? ""}`
|
|
|
1763
1810
|
);
|
|
1764
1811
|
let downloaded = 0;
|
|
1765
1812
|
if (download.length > 0) {
|
|
1766
|
-
const { list } = await api.cli.
|
|
1767
|
-
|
|
1813
|
+
const { list } = await api.cli.listProjectDocuments({
|
|
1814
|
+
projectId,
|
|
1768
1815
|
paths: download.join(",")
|
|
1769
1816
|
});
|
|
1770
1817
|
for (const doc of list) {
|
|
1771
|
-
const absPath = toFsPath(
|
|
1818
|
+
const absPath = toFsPath(
|
|
1819
|
+
projectDocumentLocalPath(targetApmDir, doc.path)
|
|
1820
|
+
);
|
|
1772
1821
|
await ensureDirExists(dirname2(absPath));
|
|
1773
1822
|
writeFileSync4(absPath, doc.content, "utf8");
|
|
1774
1823
|
downloaded += 1;
|
|
@@ -1783,34 +1832,34 @@ ${diagnostic ?? ""}`
|
|
|
1783
1832
|
}
|
|
1784
1833
|
}
|
|
1785
1834
|
writeFileSync4(
|
|
1786
|
-
toFsPath(join4(
|
|
1835
|
+
toFsPath(join4(docsDir, MANIFEST_FILE)),
|
|
1787
1836
|
`${JSON.stringify(remoteManifest, null, 2)}
|
|
1788
1837
|
`,
|
|
1789
1838
|
"utf8"
|
|
1790
1839
|
);
|
|
1791
1840
|
console.log(
|
|
1792
|
-
`[apm] \u5DF2\u540C\u6B65\
|
|
1841
|
+
`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted}`
|
|
1793
1842
|
);
|
|
1794
1843
|
return {
|
|
1795
1844
|
synced: true,
|
|
1796
|
-
|
|
1845
|
+
projectId,
|
|
1797
1846
|
downloaded,
|
|
1798
1847
|
deleted
|
|
1799
1848
|
};
|
|
1800
1849
|
}
|
|
1801
|
-
async function
|
|
1850
|
+
async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot) {
|
|
1802
1851
|
const api = createApmApiClient(cfg);
|
|
1803
|
-
const {
|
|
1804
|
-
if (!
|
|
1852
|
+
const { projectId } = await resolveProjectIdForSync(api, workdirPath);
|
|
1853
|
+
if (!projectId) {
|
|
1805
1854
|
return 0;
|
|
1806
1855
|
}
|
|
1807
1856
|
const targetApmDir = apmRoot ?? workspaceApmDir(workdirPath);
|
|
1808
1857
|
const localPaths = listLocalDocumentPaths(targetApmDir);
|
|
1809
1858
|
if (localPaths.length === 0) {
|
|
1810
|
-
console.log("[apm] \
|
|
1859
|
+
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u672C\u5730\u6587\u4EF6\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
1811
1860
|
return 0;
|
|
1812
1861
|
}
|
|
1813
|
-
const remoteManifest = (await api.cli.
|
|
1862
|
+
const remoteManifest = (await api.cli.getProjectDocumentManifest({ projectId })).manifest ?? null;
|
|
1814
1863
|
const remoteHashByPath = new Map(
|
|
1815
1864
|
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
1816
1865
|
);
|
|
@@ -1828,17 +1877,17 @@ async function syncRepositoryProjectDocumentsPush(cfg, workdirPath, apmRoot) {
|
|
|
1828
1877
|
if (remoteHashByPath.get(path19) === contentHash) {
|
|
1829
1878
|
continue;
|
|
1830
1879
|
}
|
|
1831
|
-
await api.cli.
|
|
1832
|
-
|
|
1880
|
+
await api.cli.upsertProjectDocument({
|
|
1881
|
+
projectId,
|
|
1833
1882
|
path: path19,
|
|
1834
1883
|
content,
|
|
1835
1884
|
description: remoteDescriptionByPath.get(path19) ?? void 0
|
|
1836
1885
|
});
|
|
1837
1886
|
synced += 1;
|
|
1838
|
-
console.log(`[apm] \u5DF2\u540C\u6B65\
|
|
1887
|
+
console.log(`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: ${path19}`);
|
|
1839
1888
|
}
|
|
1840
1889
|
if (synced === 0) {
|
|
1841
|
-
console.log("[apm] \
|
|
1890
|
+
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
1842
1891
|
}
|
|
1843
1892
|
return synced;
|
|
1844
1893
|
}
|
|
@@ -1859,7 +1908,7 @@ async function ensureWorkspaceInitialized(workdir, options) {
|
|
|
1859
1908
|
const apmDir = workspaceApmDir(workdir);
|
|
1860
1909
|
await copyTemplateFiles(apmDir, workdir);
|
|
1861
1910
|
const syncResult = await syncRemoteDeploymentConfig(workdir, apmDir);
|
|
1862
|
-
await
|
|
1911
|
+
await syncProjectDocumentsPull(workdir, apmDir);
|
|
1863
1912
|
const trimmedName = options?.name?.trim();
|
|
1864
1913
|
if (trimmedName) {
|
|
1865
1914
|
const apmConfigPath = toFsPath(join5(apmDir, "apm.config.json"));
|
|
@@ -2773,7 +2822,7 @@ async function runPull(sessionId, remoteWorkdir) {
|
|
|
2773
2822
|
await syncSessionAttachments(cfg, trimmedId, attachments, apmRoot);
|
|
2774
2823
|
await syncPlatformRules(cfg, trimmedId, workdir, apmRoot);
|
|
2775
2824
|
await syncRemoteDeploymentConfig(workdir, apmRoot);
|
|
2776
|
-
await
|
|
2825
|
+
await syncProjectDocumentsPull(workdir, apmRoot);
|
|
2777
2826
|
console.log(`[apm] \u5DF2\u540C\u6B65\u4F1A\u8BDD\u5DE5\u4F5C\u533A: ${dir}`);
|
|
2778
2827
|
return dir;
|
|
2779
2828
|
}
|
|
@@ -3026,17 +3075,21 @@ async function runSyncDeployConfig() {
|
|
|
3026
3075
|
}
|
|
3027
3076
|
|
|
3028
3077
|
// src/commands/sync-project-documents.ts
|
|
3078
|
+
init_config();
|
|
3029
3079
|
async function runSyncProjectDocuments(options) {
|
|
3030
|
-
const pull = options?.pull ?? !options?.push;
|
|
3031
|
-
const push = options?.push ?? false;
|
|
3032
3080
|
const workdir = resolveWorkdirPath();
|
|
3033
3081
|
const apmRoot = workspaceApmDir(workdir);
|
|
3034
|
-
|
|
3035
|
-
|
|
3082
|
+
const doPush = Boolean(options.push);
|
|
3083
|
+
const doPull = Boolean(options.pull) || !doPush;
|
|
3084
|
+
if (doPull) {
|
|
3085
|
+
await syncProjectDocumentsPull(workdir, apmRoot);
|
|
3036
3086
|
}
|
|
3037
|
-
if (
|
|
3038
|
-
const cfg = await
|
|
3039
|
-
|
|
3087
|
+
if (doPush) {
|
|
3088
|
+
const cfg = await tryReadApmConfig();
|
|
3089
|
+
if (!cfg) {
|
|
3090
|
+
throw new Error("[apm] \u672A\u68C0\u6D4B\u5230\u767B\u5F55\u4FE1\u606F\uFF0C\u8BF7\u5148\u6267\u884C apm login");
|
|
3091
|
+
}
|
|
3092
|
+
await syncProjectDocumentsPush(cfg, workdir, apmRoot);
|
|
3040
3093
|
}
|
|
3041
3094
|
}
|
|
3042
3095
|
|
|
@@ -7491,7 +7544,7 @@ function createRecommendPlanTool(options) {
|
|
|
7491
7544
|
function createUpsertWebIdePlanTool(options) {
|
|
7492
7545
|
const { cfg, taskId } = options;
|
|
7493
7546
|
return {
|
|
7494
|
-
description: "Persist the implementation plan markdown for this WebIDE task and mark
|
|
7547
|
+
description: "Persist or replace the implementation plan markdown for this WebIDE task and mark it ready for human confirmation. Call with the full plan document (not a diff). Use after generating or revising the plan.",
|
|
7495
7548
|
inputSchema: {
|
|
7496
7549
|
type: "object",
|
|
7497
7550
|
properties: {
|
|
@@ -7878,8 +7931,8 @@ async function obtainAgent(ctx) {
|
|
|
7878
7931
|
settingSources: ["project"]
|
|
7879
7932
|
} : {}
|
|
7880
7933
|
},
|
|
7881
|
-
...ctx.mode ? { mode: ctx.mode } : {}
|
|
7882
|
-
|
|
7934
|
+
...ctx.mode ? { mode: ctx.mode } : {},
|
|
7935
|
+
...ctx.mcpServers ? { mcpServers: ctx.mcpServers } : {}
|
|
7883
7936
|
};
|
|
7884
7937
|
const explicitAgentId = ctx.resumeAgentId?.trim();
|
|
7885
7938
|
const savedAgentId = explicitAgentId || (ctx.user ? loadSessionAgentId(ctx.workdir, ctx.sessionId, ctx.user) : void 0);
|
|
@@ -7976,7 +8029,6 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
7976
8029
|
try {
|
|
7977
8030
|
const run = await agent.send(prompt, {
|
|
7978
8031
|
...ctx.mode ? { mode: ctx.mode } : {},
|
|
7979
|
-
// mcpServers: createPlaywrightMcpServers(),
|
|
7980
8032
|
local: {
|
|
7981
8033
|
...options?.forceSend ? { force: true } : {},
|
|
7982
8034
|
customTools
|
|
@@ -8409,7 +8461,7 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
|
|
|
8409
8461
|
if (!pullRan) {
|
|
8410
8462
|
await runStep(
|
|
8411
8463
|
"sync-project-documents-pull",
|
|
8412
|
-
() =>
|
|
8464
|
+
() => syncProjectDocumentsPull(workdir, apmRoot)
|
|
8413
8465
|
);
|
|
8414
8466
|
}
|
|
8415
8467
|
const agentResult = await runStep(
|
|
@@ -8442,7 +8494,7 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
|
|
|
8442
8494
|
);
|
|
8443
8495
|
await runStep(
|
|
8444
8496
|
"sync-project-documents",
|
|
8445
|
-
() =>
|
|
8497
|
+
() => syncProjectDocumentsPush(cfg, workdir, apmRoot)
|
|
8446
8498
|
);
|
|
8447
8499
|
await runStep(
|
|
8448
8500
|
"commit-files",
|
|
@@ -9979,12 +10031,12 @@ function buildProgram() {
|
|
|
9979
10031
|
await runSyncDeployConfig();
|
|
9980
10032
|
});
|
|
9981
10033
|
program.command("sync-project-documents").description(
|
|
9982
|
-
"\u540C\u6B65\
|
|
9983
|
-
).option("--push", "\u63A8\u9001\u672C\u5730 .apm/
|
|
10034
|
+
"\u540C\u6B65\u9879\u76EE\u6587\u6863\uFF1A\u9ED8\u8BA4 pull \u5230 .apm/docs/\uFF1B\u52A0 --push \u5C06\u672C\u5730\u53D8\u66F4\u63A8\u9001\u5230\u5E73\u53F0"
|
|
10035
|
+
).option("--push", "\u63A8\u9001\u672C\u5730 .apm/docs/ \u5230\u5E73\u53F0").option("--pull", "\u4ECE\u5E73\u53F0\u62C9\u53D6\u5230 .apm/docs/").action(async (opts) => {
|
|
9984
10036
|
await runSyncProjectDocuments(opts);
|
|
9985
10037
|
});
|
|
9986
10038
|
program.command("pull").description(
|
|
9987
|
-
"\u62C9\u53D6\u6C9F\u901A\u7FA4\u6570\u636E\u5230 .apm/sessions/<sessionId>/\uFF0C\u5E76\u540C\u6B65\u90E8\u7F72\u914D\u7F6E\u4E0E\
|
|
10039
|
+
"\u62C9\u53D6\u6C9F\u901A\u7FA4\u6570\u636E\u5230 .apm/sessions/<sessionId>/\uFF0C\u5E76\u540C\u6B65\u90E8\u7F72\u914D\u7F6E\u4E0E\u9879\u76EE\u6587\u6863\u5230 .apm/docs/"
|
|
9988
10040
|
).argument("<sessionId>", "\u6C9F\u901A\u7FA4 ID").action(async (sessionId) => {
|
|
9989
10041
|
await runPull(sessionId);
|
|
9990
10042
|
});
|
|
@@ -116,10 +116,18 @@ var requestConfig = {
|
|
|
116
116
|
method: "PUT",
|
|
117
117
|
path: "/cli/webide/terminals/meta"
|
|
118
118
|
}),
|
|
119
|
+
acquireSteelBrowser: defineEndpoint({
|
|
120
|
+
method: "POST",
|
|
121
|
+
path: "/cli/steel-browser/acquire"
|
|
122
|
+
}),
|
|
119
123
|
branchBaseline: defineEndpoint({
|
|
120
124
|
method: "GET",
|
|
121
125
|
path: "/cli/tasks/branch-baseline"
|
|
122
126
|
}),
|
|
127
|
+
projectBaseBranch: defineEndpoint({
|
|
128
|
+
method: "GET",
|
|
129
|
+
path: "/cli/tasks/project-base-branch"
|
|
130
|
+
}),
|
|
123
131
|
listSessionsForBranchCleanup: defineEndpoint({
|
|
124
132
|
method: "GET",
|
|
125
133
|
path: "/cli/sessions/branch-cleanup"
|
|
@@ -156,21 +164,21 @@ var requestConfig = {
|
|
|
156
164
|
method: "POST",
|
|
157
165
|
path: "/cli/webide/pull-requests/merge"
|
|
158
166
|
}),
|
|
159
|
-
|
|
167
|
+
getProjectDocumentManifest: defineEndpoint({
|
|
160
168
|
method: "GET",
|
|
161
|
-
path: "/cli/
|
|
169
|
+
path: "/cli/project-documents/manifest"
|
|
162
170
|
}),
|
|
163
|
-
|
|
171
|
+
listProjectDocuments: defineEndpoint({
|
|
164
172
|
method: "GET",
|
|
165
|
-
path: "/cli/
|
|
173
|
+
path: "/cli/project-documents"
|
|
166
174
|
}),
|
|
167
|
-
|
|
175
|
+
upsertProjectDocument: defineEndpoint({
|
|
168
176
|
method: "PUT",
|
|
169
|
-
path: "/cli/
|
|
177
|
+
path: "/cli/project-documents/upsert"
|
|
170
178
|
}),
|
|
171
|
-
|
|
179
|
+
removeProjectDocument: defineEndpoint({
|
|
172
180
|
method: "DELETE",
|
|
173
|
-
path: "/cli/
|
|
181
|
+
path: "/cli/project-documents"
|
|
174
182
|
}),
|
|
175
183
|
createTaskDeployment: defineEndpoint({
|
|
176
184
|
method: "POST",
|
|
@@ -207,6 +215,7 @@ import { homedir } from "os";
|
|
|
207
215
|
import { join } from "path";
|
|
208
216
|
var APM_CONFIG_DIR = join(homedir(), ".config", "apm");
|
|
209
217
|
var APM_CONFIG_PATH = join(APM_CONFIG_DIR, "config.json");
|
|
218
|
+
var DEFAULT_BASE_URL = "http://127.0.0.1:3000";
|
|
210
219
|
function resolveClientMachineId(cfg) {
|
|
211
220
|
return (cfg.clientMachineId ?? cfg.userId ?? "").trim();
|
|
212
221
|
}
|
|
@@ -237,6 +246,33 @@ async function tryReadApmConfig() {
|
|
|
237
246
|
return null;
|
|
238
247
|
}
|
|
239
248
|
}
|
|
249
|
+
function defaultApmConfig() {
|
|
250
|
+
return {
|
|
251
|
+
baseUrl: DEFAULT_BASE_URL,
|
|
252
|
+
clientMachineId: "",
|
|
253
|
+
apiKey: ""
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
async function ensureApmConfig() {
|
|
257
|
+
const existing = await tryReadApmConfig();
|
|
258
|
+
if (existing) return existing;
|
|
259
|
+
const defaults = defaultApmConfig();
|
|
260
|
+
await writeApmConfig(defaults);
|
|
261
|
+
return defaults;
|
|
262
|
+
}
|
|
263
|
+
async function writeApmConfig(cfg) {
|
|
264
|
+
const clientMachineId = resolveClientMachineId(cfg);
|
|
265
|
+
const apiKey = resolveApiKey(cfg);
|
|
266
|
+
const normalized = {
|
|
267
|
+
baseUrl: cfg.baseUrl.trim().replace(/\/+$/, ""),
|
|
268
|
+
apiKey,
|
|
269
|
+
...clientMachineId ? { clientMachineId } : {}
|
|
270
|
+
};
|
|
271
|
+
mkdirSync(APM_CONFIG_DIR, { recursive: true });
|
|
272
|
+
writeFileSync(APM_CONFIG_PATH, JSON.stringify(normalized, null, 2) + "\n", {
|
|
273
|
+
mode: 384
|
|
274
|
+
});
|
|
275
|
+
}
|
|
240
276
|
|
|
241
277
|
// src/api/client.ts
|
|
242
278
|
function createApmApiClient(cfg) {
|
|
@@ -375,24 +411,6 @@ async function ensureRemoteBaselineBranch(cwd, baselineBranch) {
|
|
|
375
411
|
`[apm] \u8FDC\u7A0B\u4E0D\u5B58\u5728\u57FA\u7EBF\u5206\u652F origin/${baselineBranch}\uFF0C\u8BF7\u786E\u8BA4\u4ED3\u5E93\u9ED8\u8BA4\u5206\u652F\u5DF2\u63A8\u9001\u5230 origin`
|
|
376
412
|
);
|
|
377
413
|
}
|
|
378
|
-
async function resolveDefaultRemoteBranch(cwd) {
|
|
379
|
-
try {
|
|
380
|
-
const ref = (await execGit(cwd, ["symbolic-ref", "refs/remotes/origin/HEAD"], true)).trim();
|
|
381
|
-
const match = ref.match(/^refs\/remotes\/origin\/(.+)$/);
|
|
382
|
-
if (match?.[1]) {
|
|
383
|
-
return match[1];
|
|
384
|
-
}
|
|
385
|
-
} catch {
|
|
386
|
-
}
|
|
387
|
-
for (const candidate of ["main", "master"]) {
|
|
388
|
-
if (await remoteBranchExists(cwd, candidate)) {
|
|
389
|
-
return candidate;
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
throw new Error(
|
|
393
|
-
"[apm] \u65E0\u6CD5\u786E\u5B9A\u8FDC\u7A0B\u9ED8\u8BA4\u5206\u652F\uFF08origin/HEAD\u3001main\u3001master \u5747\u4E0D\u53EF\u7528\uFF09"
|
|
394
|
-
);
|
|
395
|
-
}
|
|
396
414
|
async function hasUpstream(cwd) {
|
|
397
415
|
try {
|
|
398
416
|
await execGit(cwd, ["rev-parse", "--abbrev-ref", "@{upstream}"], true);
|
|
@@ -573,6 +591,16 @@ function assertApmGitignoredInRepo(workdir) {
|
|
|
573
591
|
);
|
|
574
592
|
}
|
|
575
593
|
}
|
|
594
|
+
async function ensureLoggedConfig() {
|
|
595
|
+
const cfg = await ensureApmConfig();
|
|
596
|
+
if (!resolveApiKey(cfg)) {
|
|
597
|
+
console.error(
|
|
598
|
+
"[apm] \u672A\u68C0\u6D4B\u5230 API Key\uFF0C\u8BF7\u5148\u6267\u884C: apm login --api-key cm_xxx"
|
|
599
|
+
);
|
|
600
|
+
process.exit(1);
|
|
601
|
+
}
|
|
602
|
+
return cfg;
|
|
603
|
+
}
|
|
576
604
|
async function ensureDirExists(dir) {
|
|
577
605
|
mkdirSync2(dir, { recursive: true });
|
|
578
606
|
}
|
|
@@ -977,6 +1005,17 @@ async function runTaskBranch(taskId, options = {}) {
|
|
|
977
1005
|
if (!trimmedTaskId) {
|
|
978
1006
|
throw new Error("[apm] taskId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
979
1007
|
}
|
|
1008
|
+
const cfg = await ensureLoggedConfig();
|
|
1009
|
+
const api = createApmApiClient(cfg);
|
|
1010
|
+
const projectBaseline = await api.cli.projectBaseBranch({
|
|
1011
|
+
taskId: trimmedTaskId
|
|
1012
|
+
});
|
|
1013
|
+
const baselineBranch = projectBaseline.baseBranch?.trim() ?? "";
|
|
1014
|
+
if (!baselineBranch) {
|
|
1015
|
+
throw new Error(
|
|
1016
|
+
"[apm] \u9879\u76EE\u672A\u914D\u7F6E\u57FA\u7840\u5206\u652F\uFF0C\u65E0\u6CD5\u521B\u5EFA WebIDE \u7279\u6027\u5206\u652F"
|
|
1017
|
+
);
|
|
1018
|
+
}
|
|
980
1019
|
const cwd = options.cwd ?? process.cwd();
|
|
981
1020
|
const workdirPath = resolveWorkdirPath(cwd);
|
|
982
1021
|
const manifest = loadWorkspaceReposCache(workdirPath);
|
|
@@ -984,9 +1023,8 @@ async function runTaskBranch(taskId, options = {}) {
|
|
|
984
1023
|
const branch = branchNameForTask(trimmedTaskId);
|
|
985
1024
|
for (const gitRoot of repoRoots) {
|
|
986
1025
|
const label = formatRepoLabel(workdirPath, gitRoot);
|
|
987
|
-
const baselineBranch = await resolveDefaultRemoteBranch(gitRoot);
|
|
988
1026
|
console.log(
|
|
989
|
-
`[apm] \u4EFB\u52A1\u5206\u652F ${branch} @ ${label} (baseline=${baselineBranch})`
|
|
1027
|
+
`[apm] \u4EFB\u52A1\u5206\u652F ${branch} @ ${label} (project baseline=${baselineBranch})`
|
|
990
1028
|
);
|
|
991
1029
|
await ensureFeatureBranch(branch, baselineBranch, {
|
|
992
1030
|
...options,
|
|
@@ -1643,7 +1681,7 @@ function createRecommendPlanTool(options) {
|
|
|
1643
1681
|
function createUpsertWebIdePlanTool(options) {
|
|
1644
1682
|
const { cfg, taskId } = options;
|
|
1645
1683
|
return {
|
|
1646
|
-
description: "Persist the implementation plan markdown for this WebIDE task and mark
|
|
1684
|
+
description: "Persist or replace the implementation plan markdown for this WebIDE task and mark it ready for human confirmation. Call with the full plan document (not a diff). Use after generating or revising the plan.",
|
|
1647
1685
|
inputSchema: {
|
|
1648
1686
|
type: "object",
|
|
1649
1687
|
properties: {
|
|
@@ -2028,8 +2066,8 @@ async function obtainAgent(ctx) {
|
|
|
2028
2066
|
settingSources: ["project"]
|
|
2029
2067
|
} : {}
|
|
2030
2068
|
},
|
|
2031
|
-
...ctx.mode ? { mode: ctx.mode } : {}
|
|
2032
|
-
|
|
2069
|
+
...ctx.mode ? { mode: ctx.mode } : {},
|
|
2070
|
+
...ctx.mcpServers ? { mcpServers: ctx.mcpServers } : {}
|
|
2033
2071
|
};
|
|
2034
2072
|
const explicitAgentId = ctx.resumeAgentId?.trim();
|
|
2035
2073
|
const savedAgentId = explicitAgentId || (ctx.user ? loadSessionAgentId(ctx.workdir, ctx.sessionId, ctx.user) : void 0);
|
|
@@ -2126,7 +2164,6 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
2126
2164
|
try {
|
|
2127
2165
|
const run = await agent.send(prompt, {
|
|
2128
2166
|
...ctx.mode ? { mode: ctx.mode } : {},
|
|
2129
|
-
// mcpServers: createPlaywrightMcpServers(),
|
|
2130
2167
|
local: {
|
|
2131
2168
|
...options?.forceSend ? { force: true } : {},
|
|
2132
2169
|
customTools
|
|
@@ -2622,7 +2659,7 @@ ${diagnostic ?? ""}
|
|
|
2622
2659
|
return { synced: true, repositoryId, configName: config.name };
|
|
2623
2660
|
}
|
|
2624
2661
|
|
|
2625
|
-
// src/
|
|
2662
|
+
// src/project-documents-sync.ts
|
|
2626
2663
|
import {
|
|
2627
2664
|
existsSync as existsSync5,
|
|
2628
2665
|
readdirSync as readdirSync3,
|
|
@@ -2633,7 +2670,7 @@ import {
|
|
|
2633
2670
|
import { dirname as dirname4, join as join6, relative as relative2, sep } from "path";
|
|
2634
2671
|
var MANIFEST_FILE = "manifest.json";
|
|
2635
2672
|
function projectDocumentsDir(apmRoot) {
|
|
2636
|
-
return join6(apmRoot ?? workspaceApmDir(), "
|
|
2673
|
+
return join6(apmRoot ?? workspaceApmDir(), "docs");
|
|
2637
2674
|
}
|
|
2638
2675
|
function projectDocumentLocalPath(apmRoot, documentPath) {
|
|
2639
2676
|
const normalized = normalizeLocalDocumentPath(documentPath);
|
|
@@ -2684,41 +2721,65 @@ function diffManifestPaths(remote, local) {
|
|
|
2684
2721
|
}
|
|
2685
2722
|
return { download, deleteLocal };
|
|
2686
2723
|
}
|
|
2687
|
-
async function
|
|
2724
|
+
async function resolveProjectIdForSync(api, workdirPath) {
|
|
2725
|
+
try {
|
|
2726
|
+
const baseline = await api.cli.workspaceBaseline({ workdirPath });
|
|
2727
|
+
const projectId = baseline.projectId?.trim() || null;
|
|
2728
|
+
if (projectId) {
|
|
2729
|
+
return { projectId, diagnostic: null };
|
|
2730
|
+
}
|
|
2731
|
+
const projectIds = baseline.projectIds ?? [];
|
|
2732
|
+
if (projectIds.length === 0) {
|
|
2733
|
+
return {
|
|
2734
|
+
projectId: null,
|
|
2735
|
+
diagnostic: baseline.diagnostic?.message ?? `\u5DE5\u4F5C\u7A7A\u95F4\u672A\u5173\u8054\u9879\u76EE\uFF0C\u65E0\u6CD5\u540C\u6B65\u9879\u76EE\u6587\u6863\uFF08\u76EE\u5F55\uFF1A${workdirPath}\uFF09\u3002
|
|
2736
|
+
\u8BF7\u5728\u5E73\u53F0\u5C06\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u5230\u552F\u4E00\u9879\u76EE\u3002`
|
|
2737
|
+
};
|
|
2738
|
+
}
|
|
2739
|
+
return {
|
|
2740
|
+
projectId: null,
|
|
2741
|
+
diagnostic: `\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u4E86 ${projectIds.length} \u4E2A\u9879\u76EE\uFF0C\u65E0\u6CD5\u786E\u5B9A\u540C\u6B65\u76EE\u6807\u3002
|
|
2742
|
+
\u8BF7\u786E\u4FDD\u8BE5\u5DE5\u4F5C\u7A7A\u95F4\u53EA\u5173\u8054\u4E00\u4E2A\u9879\u76EE\u540E\u518D\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002`
|
|
2743
|
+
};
|
|
2744
|
+
} catch (err) {
|
|
2745
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
2746
|
+
return {
|
|
2747
|
+
projectId: null,
|
|
2748
|
+
diagnostic: detail.replace(/^\[apm\]\s*/, "")
|
|
2749
|
+
};
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2752
|
+
async function syncProjectDocumentsPull(workdirPath, apmDir) {
|
|
2688
2753
|
const empty = {
|
|
2689
2754
|
synced: false,
|
|
2690
|
-
|
|
2755
|
+
projectId: null,
|
|
2691
2756
|
downloaded: 0,
|
|
2692
2757
|
deleted: 0
|
|
2693
2758
|
};
|
|
2694
2759
|
const cfg = await tryReadApmConfig();
|
|
2695
2760
|
if (!cfg || !resolveApiKey(cfg)) {
|
|
2696
2761
|
console.log(
|
|
2697
|
-
"[apm] \u672A\u68C0\u6D4B\u5230\u767B\u5F55\u4FE1\u606F\uFF0C\u8DF3\u8FC7\
|
|
2762
|
+
"[apm] \u672A\u68C0\u6D4B\u5230\u767B\u5F55\u4FE1\u606F\uFF0C\u8DF3\u8FC7\u9879\u76EE\u6587\u6863\u540C\u6B65\u3002\n[apm] \u8BF7\u5148\u6267\u884C apm login\u3002"
|
|
2698
2763
|
);
|
|
2699
2764
|
return empty;
|
|
2700
2765
|
}
|
|
2701
2766
|
const api = createApmApiClient(cfg);
|
|
2702
|
-
const {
|
|
2767
|
+
const { projectId, diagnostic } = await resolveProjectIdForSync(
|
|
2703
2768
|
api,
|
|
2704
2769
|
workdirPath
|
|
2705
2770
|
);
|
|
2706
|
-
if (!
|
|
2707
|
-
console.log(
|
|
2708
|
-
|
|
2709
|
-
${diagnostic ?? ""}`
|
|
2710
|
-
);
|
|
2771
|
+
if (!projectId) {
|
|
2772
|
+
console.log(`[apm] \u672A\u80FD\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002
|
|
2773
|
+
${diagnostic ?? ""}`);
|
|
2711
2774
|
return empty;
|
|
2712
2775
|
}
|
|
2713
2776
|
const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
|
|
2714
|
-
const
|
|
2715
|
-
await ensureDirExists(
|
|
2716
|
-
const { manifest: remoteManifest } = await api.cli.
|
|
2777
|
+
const docsDir = projectDocumentsDir(targetApmDir);
|
|
2778
|
+
await ensureDirExists(docsDir);
|
|
2779
|
+
const { manifest: remoteManifest } = await api.cli.getProjectDocumentManifest({ projectId });
|
|
2717
2780
|
if (!remoteManifest) {
|
|
2718
|
-
console.log(
|
|
2719
|
-
|
|
2720
|
-
);
|
|
2721
|
-
return { ...empty, repositoryId };
|
|
2781
|
+
console.log(`[apm] \u9879\u76EE ${projectId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`);
|
|
2782
|
+
return { ...empty, projectId };
|
|
2722
2783
|
}
|
|
2723
2784
|
const localManifest = readLocalManifest(targetApmDir);
|
|
2724
2785
|
const { download, deleteLocal } = diffManifestPaths(
|
|
@@ -2727,12 +2788,14 @@ ${diagnostic ?? ""}`
|
|
|
2727
2788
|
);
|
|
2728
2789
|
let downloaded = 0;
|
|
2729
2790
|
if (download.length > 0) {
|
|
2730
|
-
const { list } = await api.cli.
|
|
2731
|
-
|
|
2791
|
+
const { list } = await api.cli.listProjectDocuments({
|
|
2792
|
+
projectId,
|
|
2732
2793
|
paths: download.join(",")
|
|
2733
2794
|
});
|
|
2734
2795
|
for (const doc of list) {
|
|
2735
|
-
const absPath = toFsPath(
|
|
2796
|
+
const absPath = toFsPath(
|
|
2797
|
+
projectDocumentLocalPath(targetApmDir, doc.path)
|
|
2798
|
+
);
|
|
2736
2799
|
await ensureDirExists(dirname4(absPath));
|
|
2737
2800
|
writeFileSync7(absPath, doc.content, "utf8");
|
|
2738
2801
|
downloaded += 1;
|
|
@@ -2747,17 +2810,17 @@ ${diagnostic ?? ""}`
|
|
|
2747
2810
|
}
|
|
2748
2811
|
}
|
|
2749
2812
|
writeFileSync7(
|
|
2750
|
-
toFsPath(join6(
|
|
2813
|
+
toFsPath(join6(docsDir, MANIFEST_FILE)),
|
|
2751
2814
|
`${JSON.stringify(remoteManifest, null, 2)}
|
|
2752
2815
|
`,
|
|
2753
2816
|
"utf8"
|
|
2754
2817
|
);
|
|
2755
2818
|
console.log(
|
|
2756
|
-
`[apm] \u5DF2\u540C\u6B65\
|
|
2819
|
+
`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted}`
|
|
2757
2820
|
);
|
|
2758
2821
|
return {
|
|
2759
2822
|
synced: true,
|
|
2760
|
-
|
|
2823
|
+
projectId,
|
|
2761
2824
|
downloaded,
|
|
2762
2825
|
deleted
|
|
2763
2826
|
};
|
|
@@ -2779,7 +2842,7 @@ async function ensureWorkspaceInitialized(workdir, options) {
|
|
|
2779
2842
|
const apmDir = workspaceApmDir(workdir);
|
|
2780
2843
|
await copyTemplateFiles(apmDir, workdir);
|
|
2781
2844
|
const syncResult = await syncRemoteDeploymentConfig(workdir, apmDir);
|
|
2782
|
-
await
|
|
2845
|
+
await syncProjectDocumentsPull(workdir, apmDir);
|
|
2783
2846
|
const trimmedName = options?.name?.trim();
|
|
2784
2847
|
if (trimmedName) {
|
|
2785
2848
|
const apmConfigPath = toFsPath(join7(apmDir, "apm.config.json"));
|
|
@@ -3214,7 +3277,7 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
|
|
|
3214
3277
|
id: messageId,
|
|
3215
3278
|
content: fallback
|
|
3216
3279
|
});
|
|
3217
|
-
if (msg.action === "write-plan" && outcome.createPlan && outcome.createPlan.trim()) {
|
|
3280
|
+
if ((msg.action === "write-plan" || msg.action === "revise-plan") && outcome.createPlan && outcome.createPlan.trim()) {
|
|
3218
3281
|
try {
|
|
3219
3282
|
await api.cli.webideUpsertPlan({
|
|
3220
3283
|
taskId,
|
|
@@ -3222,7 +3285,7 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
|
|
|
3222
3285
|
});
|
|
3223
3286
|
} catch (err) {
|
|
3224
3287
|
console.warn(
|
|
3225
|
-
|
|
3288
|
+
`[apm] ${msg.action} \u8865\u5199\u8BA1\u5212\u5931\u8D25:`,
|
|
3226
3289
|
err instanceof Error ? err.message : err
|
|
3227
3290
|
);
|
|
3228
3291
|
}
|
package/package.json
CHANGED
package/template/AGENTS.md
CHANGED
|
@@ -33,10 +33,10 @@
|
|
|
33
33
|
- webide_testcase.md:WebIDE 生成测试用例时读取,按规范编写并提交用例
|
|
34
34
|
- webide_merge.md:WebIDE 验收通过后读取;直接调用 MergeWebIdePullRequests 合并代码
|
|
35
35
|
|
|
36
|
-
-
|
|
36
|
+
- 项目文档(`.apm/docs/`):
|
|
37
37
|
|
|
38
|
-
- 索引: `.apm/
|
|
39
|
-
- 文档文件: `.apm/
|
|
38
|
+
- 索引: `.apm/docs/manifest.json` — 本项目在平台登记的文档列表
|
|
39
|
+
- 文档文件: `.apm/docs/{path}` — 与 manifest 中 path 对应;`apm pull` 自动同步,`apm connect` 消息结束后自动推回平台
|
|
40
40
|
- 任务涉及菜单名、路由、业务术语等时,**先 Read manifest 与相关文档**,禁止猜测路径
|
|
41
41
|
|
|
42
42
|
- 本轮任务需要关注的文档指引(.apm/sessions/<会话 ID>/\*):
|
|
@@ -13,23 +13,25 @@
|
|
|
13
13
|
|
|
14
14
|
## 启动命令来源
|
|
15
15
|
|
|
16
|
-
必须 Read `.apm/
|
|
16
|
+
必须 Read `.apm/docs/deploy.md`,按文档中的 cwd / command / 服务划分调用 `StartWebIdeTerminal`。
|
|
17
17
|
|
|
18
18
|
- 文档不存在或未写明启动方式 → 用 AppendMessage 说明无法启动本地环境,**禁止猜测命令**。
|
|
19
19
|
|
|
20
|
-
##
|
|
20
|
+
## 时机(打开本地 dev)
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
固定顺序:
|
|
23
|
+
|
|
24
|
+
1. **`ListWebIdeTerminals`**
|
|
25
|
+
2. 若无活终端 → Read `deploy.md` → 按前后端分别 `StartWebIdeTerminal`;已有活终端则复用
|
|
26
|
+
3. 等待 READY / 端口即可
|
|
27
|
+
4. AppendMessage:可在「终端」面板观察本地日志;「浏览器」面板由平台打开本客户机外网地址(勿粘贴整段 stdout;无需 Agent 打开浏览器)
|
|
28
|
+
|
|
29
|
+
后续改码(热更新):有活终端则**复用,不重起**。
|
|
30
|
+
|
|
31
|
+
显式重跑:`StopAllWebIdeTerminals` → 再 Read `deploy.md` → 重新 Start。
|
|
30
32
|
|
|
31
33
|
## 规范
|
|
32
34
|
|
|
33
35
|
1. 禁止用 Shell / 后台进程方式起 `dev` / `serve` 等长驻命令。
|
|
34
|
-
2. 观察日志请用户看 WebIDE
|
|
36
|
+
2. 观察日志请用户看 WebIDE「终端」面板;页面请用户看「浏览器」面板(客户机外网地址)。
|
|
35
37
|
3. 任务结束或不再需要时可 Stop;不要留下孤儿进程。
|