ai-project-manage-cli 7.1.16 → 7.1.17
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 +57 -28
- package/dist/webide-message-worker.js +149 -15
- package/package.json +1 -1
- package/template/AGENTS.md +4 -3
- package/template/rules/webide_terminal.md +3 -3
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` 会自动将平台登记的**项目文档**同步到 `.apm/
|
|
60
|
+
`apm pull` 会自动将平台登记的**项目文档**同步到 `.apm/project/<项目ID>/`(含 `manifest.json`)。Agent 修改该目录下文件后,`apm connect` 处理完消息会自动推回平台。
|
|
61
61
|
|
|
62
62
|
详见仓库根目录 [docs/CLI.md](../../docs/CLI.md)。
|
package/dist/index.js
CHANGED
|
@@ -1671,12 +1671,26 @@ import {
|
|
|
1671
1671
|
import { createHash } from "crypto";
|
|
1672
1672
|
import { dirname as dirname2, join as join4, relative, sep } from "path";
|
|
1673
1673
|
var MANIFEST_FILE = "manifest.json";
|
|
1674
|
-
function
|
|
1675
|
-
|
|
1674
|
+
function normalizeProjectIdForPath(projectId) {
|
|
1675
|
+
const id = projectId.trim();
|
|
1676
|
+
if (!id) {
|
|
1677
|
+
throw new Error("projectId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
1678
|
+
}
|
|
1679
|
+
if (id.includes("..") || id.includes("/") || id.includes("\\") || id.includes("\0")) {
|
|
1680
|
+
throw new Error(`\u975E\u6CD5 projectId: ${projectId}`);
|
|
1681
|
+
}
|
|
1682
|
+
return id;
|
|
1683
|
+
}
|
|
1684
|
+
function projectDocumentsDir(apmRoot, projectId) {
|
|
1685
|
+
const id = normalizeProjectIdForPath(projectId);
|
|
1686
|
+
return join4(apmRoot ?? workspaceApmDir(), "project", id);
|
|
1676
1687
|
}
|
|
1677
|
-
function projectDocumentLocalPath(apmRoot, documentPath) {
|
|
1688
|
+
function projectDocumentLocalPath(apmRoot, projectId, documentPath) {
|
|
1678
1689
|
const normalized = normalizeLocalDocumentPath(documentPath);
|
|
1679
|
-
return join4(
|
|
1690
|
+
return join4(
|
|
1691
|
+
projectDocumentsDir(apmRoot, projectId),
|
|
1692
|
+
...normalized.split("/")
|
|
1693
|
+
);
|
|
1680
1694
|
}
|
|
1681
1695
|
function normalizeLocalDocumentPath(path19) {
|
|
1682
1696
|
const trimmed = path19.trim().replace(/\\/g, "/");
|
|
@@ -1692,8 +1706,11 @@ function normalizeLocalDocumentPath(path19) {
|
|
|
1692
1706
|
function hashLocalFileContent(content) {
|
|
1693
1707
|
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
1694
1708
|
}
|
|
1695
|
-
function readLocalManifest(apmRoot) {
|
|
1696
|
-
const manifestPath2 = join4(
|
|
1709
|
+
function readLocalManifest(apmRoot, projectId) {
|
|
1710
|
+
const manifestPath2 = join4(
|
|
1711
|
+
projectDocumentsDir(apmRoot, projectId),
|
|
1712
|
+
MANIFEST_FILE
|
|
1713
|
+
);
|
|
1697
1714
|
if (!existsSync2(manifestPath2)) {
|
|
1698
1715
|
return null;
|
|
1699
1716
|
}
|
|
@@ -1705,8 +1722,8 @@ function readLocalManifest(apmRoot) {
|
|
|
1705
1722
|
return null;
|
|
1706
1723
|
}
|
|
1707
1724
|
}
|
|
1708
|
-
function listLocalDocumentPaths(apmRoot) {
|
|
1709
|
-
const root = projectDocumentsDir(apmRoot);
|
|
1725
|
+
function listLocalDocumentPaths(apmRoot, projectId) {
|
|
1726
|
+
const root = projectDocumentsDir(apmRoot, projectId);
|
|
1710
1727
|
if (!existsSync2(root)) {
|
|
1711
1728
|
return [];
|
|
1712
1729
|
}
|
|
@@ -1749,7 +1766,11 @@ function diffManifestPaths(remote, local) {
|
|
|
1749
1766
|
}
|
|
1750
1767
|
return { download, deleteLocal };
|
|
1751
1768
|
}
|
|
1752
|
-
async function resolveProjectIdForSync(api, workdirPath) {
|
|
1769
|
+
async function resolveProjectIdForSync(api, workdirPath, options) {
|
|
1770
|
+
const explicit = options?.projectId?.trim();
|
|
1771
|
+
if (explicit) {
|
|
1772
|
+
return { projectId: explicit, diagnostic: null };
|
|
1773
|
+
}
|
|
1753
1774
|
try {
|
|
1754
1775
|
const baseline = await api.cli.workspaceBaseline({ workdirPath });
|
|
1755
1776
|
const projectId = baseline.projectId?.trim() || null;
|
|
@@ -1777,7 +1798,7 @@ async function resolveProjectIdForSync(api, workdirPath) {
|
|
|
1777
1798
|
};
|
|
1778
1799
|
}
|
|
1779
1800
|
}
|
|
1780
|
-
async function syncProjectDocumentsPull(workdirPath, apmDir) {
|
|
1801
|
+
async function syncProjectDocumentsPull(workdirPath, apmDir, options) {
|
|
1781
1802
|
const empty = {
|
|
1782
1803
|
synced: false,
|
|
1783
1804
|
projectId: null,
|
|
@@ -1794,7 +1815,8 @@ async function syncProjectDocumentsPull(workdirPath, apmDir) {
|
|
|
1794
1815
|
const api = createApmApiClient(cfg);
|
|
1795
1816
|
const { projectId, diagnostic } = await resolveProjectIdForSync(
|
|
1796
1817
|
api,
|
|
1797
|
-
workdirPath
|
|
1818
|
+
workdirPath,
|
|
1819
|
+
options
|
|
1798
1820
|
);
|
|
1799
1821
|
if (!projectId) {
|
|
1800
1822
|
console.log(`[apm] \u672A\u80FD\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002
|
|
@@ -1802,14 +1824,16 @@ ${diagnostic ?? ""}`);
|
|
|
1802
1824
|
return empty;
|
|
1803
1825
|
}
|
|
1804
1826
|
const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
|
|
1805
|
-
const docsDir = projectDocumentsDir(targetApmDir);
|
|
1827
|
+
const docsDir = projectDocumentsDir(targetApmDir, projectId);
|
|
1806
1828
|
await ensureDirExists(docsDir);
|
|
1807
|
-
const { manifest: remoteManifest } = await api.cli.getProjectDocumentManifest(
|
|
1829
|
+
const { manifest: remoteManifest } = await api.cli.getProjectDocumentManifest(
|
|
1830
|
+
{ projectId }
|
|
1831
|
+
);
|
|
1808
1832
|
if (!remoteManifest) {
|
|
1809
1833
|
console.log(`[apm] \u9879\u76EE ${projectId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`);
|
|
1810
1834
|
return { ...empty, projectId };
|
|
1811
1835
|
}
|
|
1812
|
-
const localManifest = readLocalManifest(targetApmDir);
|
|
1836
|
+
const localManifest = readLocalManifest(targetApmDir, projectId);
|
|
1813
1837
|
const { download, deleteLocal } = diffManifestPaths(
|
|
1814
1838
|
remoteManifest,
|
|
1815
1839
|
localManifest
|
|
@@ -1822,7 +1846,7 @@ ${diagnostic ?? ""}`);
|
|
|
1822
1846
|
});
|
|
1823
1847
|
for (const doc of list) {
|
|
1824
1848
|
const absPath = toFsPath(
|
|
1825
|
-
projectDocumentLocalPath(targetApmDir, doc.path)
|
|
1849
|
+
projectDocumentLocalPath(targetApmDir, projectId, doc.path)
|
|
1826
1850
|
);
|
|
1827
1851
|
await ensureDirExists(dirname2(absPath));
|
|
1828
1852
|
writeFileSync4(absPath, doc.content, "utf8");
|
|
@@ -1831,7 +1855,9 @@ ${diagnostic ?? ""}`);
|
|
|
1831
1855
|
}
|
|
1832
1856
|
let deleted = 0;
|
|
1833
1857
|
for (const path19 of deleteLocal) {
|
|
1834
|
-
const absPath = toFsPath(
|
|
1858
|
+
const absPath = toFsPath(
|
|
1859
|
+
projectDocumentLocalPath(targetApmDir, projectId, path19)
|
|
1860
|
+
);
|
|
1835
1861
|
if (existsSync2(absPath)) {
|
|
1836
1862
|
rmSync(absPath, { force: true });
|
|
1837
1863
|
deleted += 1;
|
|
@@ -1844,7 +1870,7 @@ ${diagnostic ?? ""}`);
|
|
|
1844
1870
|
"utf8"
|
|
1845
1871
|
);
|
|
1846
1872
|
console.log(
|
|
1847
|
-
`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted}
|
|
1873
|
+
`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: projectId=${projectId} \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted} \u2192 .apm/project/${projectId}/`
|
|
1848
1874
|
);
|
|
1849
1875
|
return {
|
|
1850
1876
|
synced: true,
|
|
@@ -1853,14 +1879,18 @@ ${diagnostic ?? ""}`);
|
|
|
1853
1879
|
deleted
|
|
1854
1880
|
};
|
|
1855
1881
|
}
|
|
1856
|
-
async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot) {
|
|
1882
|
+
async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot, options) {
|
|
1857
1883
|
const api = createApmApiClient(cfg);
|
|
1858
|
-
const { projectId } = await resolveProjectIdForSync(
|
|
1884
|
+
const { projectId } = await resolveProjectIdForSync(
|
|
1885
|
+
api,
|
|
1886
|
+
workdirPath,
|
|
1887
|
+
options
|
|
1888
|
+
);
|
|
1859
1889
|
if (!projectId) {
|
|
1860
1890
|
return 0;
|
|
1861
1891
|
}
|
|
1862
1892
|
const targetApmDir = apmRoot ?? workspaceApmDir(workdirPath);
|
|
1863
|
-
const localPaths = listLocalDocumentPaths(targetApmDir);
|
|
1893
|
+
const localPaths = listLocalDocumentPaths(targetApmDir, projectId);
|
|
1864
1894
|
if (localPaths.length === 0) {
|
|
1865
1895
|
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u672C\u5730\u6587\u4EF6\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
1866
1896
|
return 0;
|
|
@@ -1870,14 +1900,13 @@ async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot) {
|
|
|
1870
1900
|
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
1871
1901
|
);
|
|
1872
1902
|
const remoteDescriptionByPath = new Map(
|
|
1873
|
-
(remoteManifest?.documents ?? []).map((doc) => [
|
|
1874
|
-
doc.path,
|
|
1875
|
-
doc.description
|
|
1876
|
-
])
|
|
1903
|
+
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.description])
|
|
1877
1904
|
);
|
|
1878
1905
|
let synced = 0;
|
|
1879
1906
|
for (const path19 of localPaths) {
|
|
1880
|
-
const absPath = toFsPath(
|
|
1907
|
+
const absPath = toFsPath(
|
|
1908
|
+
projectDocumentLocalPath(targetApmDir, projectId, path19)
|
|
1909
|
+
);
|
|
1881
1910
|
const content = readFileSync3(absPath, "utf8");
|
|
1882
1911
|
const contentHash = hashLocalFileContent(content);
|
|
1883
1912
|
if (remoteHashByPath.get(path19) === contentHash) {
|
|
@@ -10048,12 +10077,12 @@ function buildProgram() {
|
|
|
10048
10077
|
await runSyncDeployConfig();
|
|
10049
10078
|
});
|
|
10050
10079
|
program.command("sync-project-documents").description(
|
|
10051
|
-
"\u540C\u6B65\u9879\u76EE\u6587\u6863\uFF1A\u9ED8\u8BA4 pull \u5230 .apm/
|
|
10052
|
-
).option("--push", "\u63A8\u9001\u672C\u5730 .apm/
|
|
10080
|
+
"\u540C\u6B65\u9879\u76EE\u6587\u6863\uFF1A\u9ED8\u8BA4 pull \u5230 .apm/project/<\u9879\u76EEID>/\uFF1B\u52A0 --push \u5C06\u672C\u5730\u53D8\u66F4\u63A8\u9001\u5230\u5E73\u53F0"
|
|
10081
|
+
).option("--push", "\u63A8\u9001\u672C\u5730 .apm/project/<\u9879\u76EEID>/ \u5230\u5E73\u53F0").option("--pull", "\u4ECE\u5E73\u53F0\u62C9\u53D6\u5230 .apm/project/<\u9879\u76EEID>/").action(async (opts) => {
|
|
10053
10082
|
await runSyncProjectDocuments(opts);
|
|
10054
10083
|
});
|
|
10055
10084
|
program.command("pull").description(
|
|
10056
|
-
"\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/
|
|
10085
|
+
"\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/project/<\u9879\u76EEID>/"
|
|
10057
10086
|
).argument("<sessionId>", "\u6C9F\u901A\u7FA4 ID").action(async (sessionId) => {
|
|
10058
10087
|
await runPull(sessionId);
|
|
10059
10088
|
});
|
|
@@ -2681,14 +2681,29 @@ import {
|
|
|
2681
2681
|
rmSync,
|
|
2682
2682
|
writeFileSync as writeFileSync7
|
|
2683
2683
|
} from "fs";
|
|
2684
|
+
import { createHash } from "crypto";
|
|
2684
2685
|
import { dirname as dirname4, join as join6, relative as relative2, sep } from "path";
|
|
2685
2686
|
var MANIFEST_FILE = "manifest.json";
|
|
2686
|
-
function
|
|
2687
|
-
|
|
2687
|
+
function normalizeProjectIdForPath(projectId) {
|
|
2688
|
+
const id = projectId.trim();
|
|
2689
|
+
if (!id) {
|
|
2690
|
+
throw new Error("projectId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
2691
|
+
}
|
|
2692
|
+
if (id.includes("..") || id.includes("/") || id.includes("\\") || id.includes("\0")) {
|
|
2693
|
+
throw new Error(`\u975E\u6CD5 projectId: ${projectId}`);
|
|
2694
|
+
}
|
|
2695
|
+
return id;
|
|
2696
|
+
}
|
|
2697
|
+
function projectDocumentsDir(apmRoot, projectId) {
|
|
2698
|
+
const id = normalizeProjectIdForPath(projectId);
|
|
2699
|
+
return join6(apmRoot ?? workspaceApmDir(), "project", id);
|
|
2688
2700
|
}
|
|
2689
|
-
function projectDocumentLocalPath(apmRoot, documentPath) {
|
|
2701
|
+
function projectDocumentLocalPath(apmRoot, projectId, documentPath) {
|
|
2690
2702
|
const normalized = normalizeLocalDocumentPath(documentPath);
|
|
2691
|
-
return join6(
|
|
2703
|
+
return join6(
|
|
2704
|
+
projectDocumentsDir(apmRoot, projectId),
|
|
2705
|
+
...normalized.split("/")
|
|
2706
|
+
);
|
|
2692
2707
|
}
|
|
2693
2708
|
function normalizeLocalDocumentPath(path) {
|
|
2694
2709
|
const trimmed = path.trim().replace(/\\/g, "/");
|
|
@@ -2701,8 +2716,14 @@ function normalizeLocalDocumentPath(path) {
|
|
|
2701
2716
|
}
|
|
2702
2717
|
return segments.join("/");
|
|
2703
2718
|
}
|
|
2704
|
-
function
|
|
2705
|
-
|
|
2719
|
+
function hashLocalFileContent(content) {
|
|
2720
|
+
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
2721
|
+
}
|
|
2722
|
+
function readLocalManifest(apmRoot, projectId) {
|
|
2723
|
+
const manifestPath3 = join6(
|
|
2724
|
+
projectDocumentsDir(apmRoot, projectId),
|
|
2725
|
+
MANIFEST_FILE
|
|
2726
|
+
);
|
|
2706
2727
|
if (!existsSync5(manifestPath3)) {
|
|
2707
2728
|
return null;
|
|
2708
2729
|
}
|
|
@@ -2714,6 +2735,29 @@ function readLocalManifest(apmRoot) {
|
|
|
2714
2735
|
return null;
|
|
2715
2736
|
}
|
|
2716
2737
|
}
|
|
2738
|
+
function listLocalDocumentPaths(apmRoot, projectId) {
|
|
2739
|
+
const root = projectDocumentsDir(apmRoot, projectId);
|
|
2740
|
+
if (!existsSync5(root)) {
|
|
2741
|
+
return [];
|
|
2742
|
+
}
|
|
2743
|
+
const paths = [];
|
|
2744
|
+
const walk = (dir) => {
|
|
2745
|
+
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
2746
|
+
const abs = join6(dir, entry.name);
|
|
2747
|
+
if (entry.isDirectory()) {
|
|
2748
|
+
walk(abs);
|
|
2749
|
+
continue;
|
|
2750
|
+
}
|
|
2751
|
+
if (entry.isFile() && entry.name === MANIFEST_FILE) {
|
|
2752
|
+
continue;
|
|
2753
|
+
}
|
|
2754
|
+
const rel = relative2(root, abs).split(sep).join("/");
|
|
2755
|
+
paths.push(rel);
|
|
2756
|
+
}
|
|
2757
|
+
};
|
|
2758
|
+
walk(root);
|
|
2759
|
+
return paths.sort();
|
|
2760
|
+
}
|
|
2717
2761
|
function diffManifestPaths(remote, local) {
|
|
2718
2762
|
const remoteMap = new Map(
|
|
2719
2763
|
(remote?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
@@ -2735,7 +2779,11 @@ function diffManifestPaths(remote, local) {
|
|
|
2735
2779
|
}
|
|
2736
2780
|
return { download, deleteLocal };
|
|
2737
2781
|
}
|
|
2738
|
-
async function resolveProjectIdForSync(api, workdirPath) {
|
|
2782
|
+
async function resolveProjectIdForSync(api, workdirPath, options) {
|
|
2783
|
+
const explicit = options?.projectId?.trim();
|
|
2784
|
+
if (explicit) {
|
|
2785
|
+
return { projectId: explicit, diagnostic: null };
|
|
2786
|
+
}
|
|
2739
2787
|
try {
|
|
2740
2788
|
const baseline = await api.cli.workspaceBaseline({ workdirPath });
|
|
2741
2789
|
const projectId = baseline.projectId?.trim() || null;
|
|
@@ -2763,7 +2811,7 @@ async function resolveProjectIdForSync(api, workdirPath) {
|
|
|
2763
2811
|
};
|
|
2764
2812
|
}
|
|
2765
2813
|
}
|
|
2766
|
-
async function syncProjectDocumentsPull(workdirPath, apmDir) {
|
|
2814
|
+
async function syncProjectDocumentsPull(workdirPath, apmDir, options) {
|
|
2767
2815
|
const empty = {
|
|
2768
2816
|
synced: false,
|
|
2769
2817
|
projectId: null,
|
|
@@ -2780,7 +2828,8 @@ async function syncProjectDocumentsPull(workdirPath, apmDir) {
|
|
|
2780
2828
|
const api = createApmApiClient(cfg);
|
|
2781
2829
|
const { projectId, diagnostic } = await resolveProjectIdForSync(
|
|
2782
2830
|
api,
|
|
2783
|
-
workdirPath
|
|
2831
|
+
workdirPath,
|
|
2832
|
+
options
|
|
2784
2833
|
);
|
|
2785
2834
|
if (!projectId) {
|
|
2786
2835
|
console.log(`[apm] \u672A\u80FD\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002
|
|
@@ -2788,14 +2837,16 @@ ${diagnostic ?? ""}`);
|
|
|
2788
2837
|
return empty;
|
|
2789
2838
|
}
|
|
2790
2839
|
const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
|
|
2791
|
-
const docsDir = projectDocumentsDir(targetApmDir);
|
|
2840
|
+
const docsDir = projectDocumentsDir(targetApmDir, projectId);
|
|
2792
2841
|
await ensureDirExists(docsDir);
|
|
2793
|
-
const { manifest: remoteManifest } = await api.cli.getProjectDocumentManifest(
|
|
2842
|
+
const { manifest: remoteManifest } = await api.cli.getProjectDocumentManifest(
|
|
2843
|
+
{ projectId }
|
|
2844
|
+
);
|
|
2794
2845
|
if (!remoteManifest) {
|
|
2795
2846
|
console.log(`[apm] \u9879\u76EE ${projectId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`);
|
|
2796
2847
|
return { ...empty, projectId };
|
|
2797
2848
|
}
|
|
2798
|
-
const localManifest = readLocalManifest(targetApmDir);
|
|
2849
|
+
const localManifest = readLocalManifest(targetApmDir, projectId);
|
|
2799
2850
|
const { download, deleteLocal } = diffManifestPaths(
|
|
2800
2851
|
remoteManifest,
|
|
2801
2852
|
localManifest
|
|
@@ -2808,7 +2859,7 @@ ${diagnostic ?? ""}`);
|
|
|
2808
2859
|
});
|
|
2809
2860
|
for (const doc of list) {
|
|
2810
2861
|
const absPath = toFsPath(
|
|
2811
|
-
projectDocumentLocalPath(targetApmDir, doc.path)
|
|
2862
|
+
projectDocumentLocalPath(targetApmDir, projectId, doc.path)
|
|
2812
2863
|
);
|
|
2813
2864
|
await ensureDirExists(dirname4(absPath));
|
|
2814
2865
|
writeFileSync7(absPath, doc.content, "utf8");
|
|
@@ -2817,7 +2868,9 @@ ${diagnostic ?? ""}`);
|
|
|
2817
2868
|
}
|
|
2818
2869
|
let deleted = 0;
|
|
2819
2870
|
for (const path of deleteLocal) {
|
|
2820
|
-
const absPath = toFsPath(
|
|
2871
|
+
const absPath = toFsPath(
|
|
2872
|
+
projectDocumentLocalPath(targetApmDir, projectId, path)
|
|
2873
|
+
);
|
|
2821
2874
|
if (existsSync5(absPath)) {
|
|
2822
2875
|
rmSync(absPath, { force: true });
|
|
2823
2876
|
deleted += 1;
|
|
@@ -2830,7 +2883,7 @@ ${diagnostic ?? ""}`);
|
|
|
2830
2883
|
"utf8"
|
|
2831
2884
|
);
|
|
2832
2885
|
console.log(
|
|
2833
|
-
`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted}
|
|
2886
|
+
`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: projectId=${projectId} \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted} \u2192 .apm/project/${projectId}/`
|
|
2834
2887
|
);
|
|
2835
2888
|
return {
|
|
2836
2889
|
synced: true,
|
|
@@ -2839,6 +2892,53 @@ ${diagnostic ?? ""}`);
|
|
|
2839
2892
|
deleted
|
|
2840
2893
|
};
|
|
2841
2894
|
}
|
|
2895
|
+
async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot, options) {
|
|
2896
|
+
const api = createApmApiClient(cfg);
|
|
2897
|
+
const { projectId } = await resolveProjectIdForSync(
|
|
2898
|
+
api,
|
|
2899
|
+
workdirPath,
|
|
2900
|
+
options
|
|
2901
|
+
);
|
|
2902
|
+
if (!projectId) {
|
|
2903
|
+
return 0;
|
|
2904
|
+
}
|
|
2905
|
+
const targetApmDir = apmRoot ?? workspaceApmDir(workdirPath);
|
|
2906
|
+
const localPaths = listLocalDocumentPaths(targetApmDir, projectId);
|
|
2907
|
+
if (localPaths.length === 0) {
|
|
2908
|
+
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u672C\u5730\u6587\u4EF6\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
2909
|
+
return 0;
|
|
2910
|
+
}
|
|
2911
|
+
const remoteManifest = (await api.cli.getProjectDocumentManifest({ projectId })).manifest ?? null;
|
|
2912
|
+
const remoteHashByPath = new Map(
|
|
2913
|
+
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
2914
|
+
);
|
|
2915
|
+
const remoteDescriptionByPath = new Map(
|
|
2916
|
+
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.description])
|
|
2917
|
+
);
|
|
2918
|
+
let synced = 0;
|
|
2919
|
+
for (const path of localPaths) {
|
|
2920
|
+
const absPath = toFsPath(
|
|
2921
|
+
projectDocumentLocalPath(targetApmDir, projectId, path)
|
|
2922
|
+
);
|
|
2923
|
+
const content = readFileSync6(absPath, "utf8");
|
|
2924
|
+
const contentHash = hashLocalFileContent(content);
|
|
2925
|
+
if (remoteHashByPath.get(path) === contentHash) {
|
|
2926
|
+
continue;
|
|
2927
|
+
}
|
|
2928
|
+
await api.cli.upsertProjectDocument({
|
|
2929
|
+
projectId,
|
|
2930
|
+
path,
|
|
2931
|
+
content,
|
|
2932
|
+
description: remoteDescriptionByPath.get(path) ?? void 0
|
|
2933
|
+
});
|
|
2934
|
+
synced += 1;
|
|
2935
|
+
console.log(`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: ${path}`);
|
|
2936
|
+
}
|
|
2937
|
+
if (synced === 0) {
|
|
2938
|
+
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
2939
|
+
}
|
|
2940
|
+
return synced;
|
|
2941
|
+
}
|
|
2842
2942
|
|
|
2843
2943
|
// src/commands/init.ts
|
|
2844
2944
|
async function ensureWorkspaceInitialized(workdir, options) {
|
|
@@ -3309,6 +3409,30 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
|
|
|
3309
3409
|
}
|
|
3310
3410
|
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
3311
3411
|
const api = createApmApiClient(cfg);
|
|
3412
|
+
let projectId = null;
|
|
3413
|
+
try {
|
|
3414
|
+
const baseline = await api.cli.projectBaseBranch({ taskId });
|
|
3415
|
+
projectId = baseline.projectId?.trim() || null;
|
|
3416
|
+
if (projectId) {
|
|
3417
|
+
const docs = await syncProjectDocumentsPull(workdir, void 0, {
|
|
3418
|
+
projectId
|
|
3419
|
+
});
|
|
3420
|
+
if (docs.synced) {
|
|
3421
|
+
console.log(
|
|
3422
|
+
`[apm] webide \u9879\u76EE\u6587\u6863\u5DF2\u5C31\u7EEA projectId=${projectId} downloaded=${docs.downloaded} deleted=${docs.deleted}`
|
|
3423
|
+
);
|
|
3424
|
+
}
|
|
3425
|
+
} else {
|
|
3426
|
+
console.warn(
|
|
3427
|
+
`[apm] WebIDE \u65E0\u6CD5\u89E3\u6790\u4EFB\u52A1\u6240\u5C5E\u9879\u76EE\uFF0C\u8DF3\u8FC7\u9879\u76EE\u6587\u6863\u540C\u6B65 taskId=${taskId}`
|
|
3428
|
+
);
|
|
3429
|
+
}
|
|
3430
|
+
} catch (err) {
|
|
3431
|
+
console.warn(
|
|
3432
|
+
"[apm] WebIDE \u9879\u76EE\u6587\u6863\u540C\u6B65\u5931\u8D25:",
|
|
3433
|
+
err instanceof Error ? err.message : err
|
|
3434
|
+
);
|
|
3435
|
+
}
|
|
3312
3436
|
try {
|
|
3313
3437
|
const attachments = await api.cli.webideListAttachments({ taskId });
|
|
3314
3438
|
const names = await syncWebIdeAttachments(
|
|
@@ -3426,6 +3550,16 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
|
|
|
3426
3550
|
`[apm] webide \u81EA\u52A8 push action=${msg.action} repos=${pushed}`
|
|
3427
3551
|
);
|
|
3428
3552
|
}
|
|
3553
|
+
if (projectId) {
|
|
3554
|
+
try {
|
|
3555
|
+
await syncProjectDocumentsPush(cfg, workdir, void 0, { projectId });
|
|
3556
|
+
} catch (err) {
|
|
3557
|
+
console.warn(
|
|
3558
|
+
"[apm] WebIDE \u9879\u76EE\u6587\u6863\u63A8\u9001\u5931\u8D25:",
|
|
3559
|
+
err instanceof Error ? err.message : err
|
|
3560
|
+
);
|
|
3561
|
+
}
|
|
3562
|
+
}
|
|
3429
3563
|
await logSyncRef.current?.markRun(outcome.runId, "finished");
|
|
3430
3564
|
await updateStatus(cfg, messageId, "SUCCESS");
|
|
3431
3565
|
console.log(
|
package/package.json
CHANGED
package/template/AGENTS.md
CHANGED
|
@@ -33,10 +33,11 @@
|
|
|
33
33
|
- webide_testcase.md:WebIDE 生成测试用例时读取,按规范编写并提交用例
|
|
34
34
|
- webide_merge.md:WebIDE 验收通过后读取;直接调用 MergeWebIdePullRequests 合并代码
|
|
35
35
|
|
|
36
|
-
- 项目文档(`.apm/
|
|
36
|
+
- 项目文档(`.apm/project/<项目ID>/`):
|
|
37
37
|
|
|
38
|
-
- 索引: `.apm/
|
|
39
|
-
- 文档文件: `.apm/
|
|
38
|
+
- 索引: `.apm/project/<项目ID>/manifest.json` — 该项目在平台登记的文档列表
|
|
39
|
+
- 文档文件: `.apm/project/<项目ID>/{path}` — 与 manifest 中 path 对应;`apm pull` / Session `connect` 会同步;WebIDE 处理 `webide-message` 时按**任务所属项目**拉取到对应目录,结束后推回平台
|
|
40
|
+
- 部署/本地 dev:Read `.apm/project/<项目ID>/deploy.md`(完整路径以 prompt 为准)
|
|
40
41
|
- 任务涉及菜单名、路由、业务术语等时,**先 Read manifest 与相关文档**,禁止猜测路径
|
|
41
42
|
|
|
42
43
|
- 本轮任务需要关注的文档指引(.apm/sessions/<会话 ID>/\*):
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
## 启动命令来源
|
|
15
15
|
|
|
16
|
-
必须 Read `.apm/
|
|
16
|
+
必须 Read `.apm/project/<项目ID>/deploy.md`(完整路径以本轮 prompt 中给出的为准,勿省略项目 ID),按文档中的 cwd / command / 服务划分调用 `StartWebIdeTerminal`。
|
|
17
17
|
|
|
18
18
|
- 文档不存在或未写明启动方式 → 用 AppendMessage 说明无法启动本地环境,**禁止猜测命令**。
|
|
19
19
|
|
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
固定顺序:
|
|
23
23
|
|
|
24
24
|
1. **`ListWebIdeTerminals`**
|
|
25
|
-
2. 若无活终端 → Read
|
|
25
|
+
2. 若无活终端 → Read prompt 中的 `.apm/project/<项目ID>/deploy.md` 完整路径 → 按前后端分别 `StartWebIdeTerminal`;已有活终端则复用
|
|
26
26
|
3. 等待 READY / 端口即可
|
|
27
27
|
4. AppendMessage:可在「终端」面板观察本地日志;「浏览器」面板由平台打开本客户机外网地址(勿粘贴整段 stdout;无需 Agent 打开浏览器)
|
|
28
28
|
|
|
29
29
|
后续改码(热更新):有活终端则**复用,不重起**。
|
|
30
30
|
|
|
31
|
-
显式重跑:`StopAllWebIdeTerminals` → 再 Read
|
|
31
|
+
显式重跑:`StopAllWebIdeTerminals` → 再 Read `.apm/project/<项目ID>/deploy.md` → 重新 Start。
|
|
32
32
|
|
|
33
33
|
## 规范
|
|
34
34
|
|