ai-project-manage-cli 7.1.15 → 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 +86 -40
- package/dist/webide-message-worker.js +284 -29
- package/package.json +1 -1
- package/template/AGENTS.md +8 -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
|
@@ -184,6 +184,10 @@ var init_request_config = __esm({
|
|
|
184
184
|
method: "GET",
|
|
185
185
|
path: "/cli/webide/assumptions"
|
|
186
186
|
}),
|
|
187
|
+
webideListAttachments: defineEndpoint({
|
|
188
|
+
method: "GET",
|
|
189
|
+
path: "/cli/webide/attachments"
|
|
190
|
+
}),
|
|
187
191
|
webideRecommendPlan: defineEndpoint({
|
|
188
192
|
method: "PUT",
|
|
189
193
|
path: "/cli/webide/plan-recommendation"
|
|
@@ -220,10 +224,12 @@ var init_request_config = __esm({
|
|
|
220
224
|
method: "GET",
|
|
221
225
|
path: "/cli/tasks/branch-baseline"
|
|
222
226
|
}),
|
|
223
|
-
projectBaseBranch: defineEndpoint(
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
+
projectBaseBranch: defineEndpoint(
|
|
228
|
+
{
|
|
229
|
+
method: "GET",
|
|
230
|
+
path: "/cli/tasks/project-base-branch"
|
|
231
|
+
}
|
|
232
|
+
),
|
|
227
233
|
listSessionsForBranchCleanup: defineEndpoint({
|
|
228
234
|
method: "GET",
|
|
229
235
|
path: "/cli/sessions/branch-cleanup"
|
|
@@ -1665,12 +1671,26 @@ import {
|
|
|
1665
1671
|
import { createHash } from "crypto";
|
|
1666
1672
|
import { dirname as dirname2, join as join4, relative, sep } from "path";
|
|
1667
1673
|
var MANIFEST_FILE = "manifest.json";
|
|
1668
|
-
function
|
|
1669
|
-
|
|
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);
|
|
1670
1687
|
}
|
|
1671
|
-
function projectDocumentLocalPath(apmRoot, documentPath) {
|
|
1688
|
+
function projectDocumentLocalPath(apmRoot, projectId, documentPath) {
|
|
1672
1689
|
const normalized = normalizeLocalDocumentPath(documentPath);
|
|
1673
|
-
return join4(
|
|
1690
|
+
return join4(
|
|
1691
|
+
projectDocumentsDir(apmRoot, projectId),
|
|
1692
|
+
...normalized.split("/")
|
|
1693
|
+
);
|
|
1674
1694
|
}
|
|
1675
1695
|
function normalizeLocalDocumentPath(path19) {
|
|
1676
1696
|
const trimmed = path19.trim().replace(/\\/g, "/");
|
|
@@ -1686,8 +1706,11 @@ function normalizeLocalDocumentPath(path19) {
|
|
|
1686
1706
|
function hashLocalFileContent(content) {
|
|
1687
1707
|
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
1688
1708
|
}
|
|
1689
|
-
function readLocalManifest(apmRoot) {
|
|
1690
|
-
const manifestPath2 = join4(
|
|
1709
|
+
function readLocalManifest(apmRoot, projectId) {
|
|
1710
|
+
const manifestPath2 = join4(
|
|
1711
|
+
projectDocumentsDir(apmRoot, projectId),
|
|
1712
|
+
MANIFEST_FILE
|
|
1713
|
+
);
|
|
1691
1714
|
if (!existsSync2(manifestPath2)) {
|
|
1692
1715
|
return null;
|
|
1693
1716
|
}
|
|
@@ -1699,8 +1722,8 @@ function readLocalManifest(apmRoot) {
|
|
|
1699
1722
|
return null;
|
|
1700
1723
|
}
|
|
1701
1724
|
}
|
|
1702
|
-
function listLocalDocumentPaths(apmRoot) {
|
|
1703
|
-
const root = projectDocumentsDir(apmRoot);
|
|
1725
|
+
function listLocalDocumentPaths(apmRoot, projectId) {
|
|
1726
|
+
const root = projectDocumentsDir(apmRoot, projectId);
|
|
1704
1727
|
if (!existsSync2(root)) {
|
|
1705
1728
|
return [];
|
|
1706
1729
|
}
|
|
@@ -1743,7 +1766,11 @@ function diffManifestPaths(remote, local) {
|
|
|
1743
1766
|
}
|
|
1744
1767
|
return { download, deleteLocal };
|
|
1745
1768
|
}
|
|
1746
|
-
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
|
+
}
|
|
1747
1774
|
try {
|
|
1748
1775
|
const baseline = await api.cli.workspaceBaseline({ workdirPath });
|
|
1749
1776
|
const projectId = baseline.projectId?.trim() || null;
|
|
@@ -1771,7 +1798,7 @@ async function resolveProjectIdForSync(api, workdirPath) {
|
|
|
1771
1798
|
};
|
|
1772
1799
|
}
|
|
1773
1800
|
}
|
|
1774
|
-
async function syncProjectDocumentsPull(workdirPath, apmDir) {
|
|
1801
|
+
async function syncProjectDocumentsPull(workdirPath, apmDir, options) {
|
|
1775
1802
|
const empty = {
|
|
1776
1803
|
synced: false,
|
|
1777
1804
|
projectId: null,
|
|
@@ -1788,7 +1815,8 @@ async function syncProjectDocumentsPull(workdirPath, apmDir) {
|
|
|
1788
1815
|
const api = createApmApiClient(cfg);
|
|
1789
1816
|
const { projectId, diagnostic } = await resolveProjectIdForSync(
|
|
1790
1817
|
api,
|
|
1791
|
-
workdirPath
|
|
1818
|
+
workdirPath,
|
|
1819
|
+
options
|
|
1792
1820
|
);
|
|
1793
1821
|
if (!projectId) {
|
|
1794
1822
|
console.log(`[apm] \u672A\u80FD\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002
|
|
@@ -1796,14 +1824,16 @@ ${diagnostic ?? ""}`);
|
|
|
1796
1824
|
return empty;
|
|
1797
1825
|
}
|
|
1798
1826
|
const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
|
|
1799
|
-
const docsDir = projectDocumentsDir(targetApmDir);
|
|
1827
|
+
const docsDir = projectDocumentsDir(targetApmDir, projectId);
|
|
1800
1828
|
await ensureDirExists(docsDir);
|
|
1801
|
-
const { manifest: remoteManifest } = await api.cli.getProjectDocumentManifest(
|
|
1829
|
+
const { manifest: remoteManifest } = await api.cli.getProjectDocumentManifest(
|
|
1830
|
+
{ projectId }
|
|
1831
|
+
);
|
|
1802
1832
|
if (!remoteManifest) {
|
|
1803
1833
|
console.log(`[apm] \u9879\u76EE ${projectId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`);
|
|
1804
1834
|
return { ...empty, projectId };
|
|
1805
1835
|
}
|
|
1806
|
-
const localManifest = readLocalManifest(targetApmDir);
|
|
1836
|
+
const localManifest = readLocalManifest(targetApmDir, projectId);
|
|
1807
1837
|
const { download, deleteLocal } = diffManifestPaths(
|
|
1808
1838
|
remoteManifest,
|
|
1809
1839
|
localManifest
|
|
@@ -1816,7 +1846,7 @@ ${diagnostic ?? ""}`);
|
|
|
1816
1846
|
});
|
|
1817
1847
|
for (const doc of list) {
|
|
1818
1848
|
const absPath = toFsPath(
|
|
1819
|
-
projectDocumentLocalPath(targetApmDir, doc.path)
|
|
1849
|
+
projectDocumentLocalPath(targetApmDir, projectId, doc.path)
|
|
1820
1850
|
);
|
|
1821
1851
|
await ensureDirExists(dirname2(absPath));
|
|
1822
1852
|
writeFileSync4(absPath, doc.content, "utf8");
|
|
@@ -1825,7 +1855,9 @@ ${diagnostic ?? ""}`);
|
|
|
1825
1855
|
}
|
|
1826
1856
|
let deleted = 0;
|
|
1827
1857
|
for (const path19 of deleteLocal) {
|
|
1828
|
-
const absPath = toFsPath(
|
|
1858
|
+
const absPath = toFsPath(
|
|
1859
|
+
projectDocumentLocalPath(targetApmDir, projectId, path19)
|
|
1860
|
+
);
|
|
1829
1861
|
if (existsSync2(absPath)) {
|
|
1830
1862
|
rmSync(absPath, { force: true });
|
|
1831
1863
|
deleted += 1;
|
|
@@ -1838,7 +1870,7 @@ ${diagnostic ?? ""}`);
|
|
|
1838
1870
|
"utf8"
|
|
1839
1871
|
);
|
|
1840
1872
|
console.log(
|
|
1841
|
-
`[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}/`
|
|
1842
1874
|
);
|
|
1843
1875
|
return {
|
|
1844
1876
|
synced: true,
|
|
@@ -1847,14 +1879,18 @@ ${diagnostic ?? ""}`);
|
|
|
1847
1879
|
deleted
|
|
1848
1880
|
};
|
|
1849
1881
|
}
|
|
1850
|
-
async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot) {
|
|
1882
|
+
async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot, options) {
|
|
1851
1883
|
const api = createApmApiClient(cfg);
|
|
1852
|
-
const { projectId } = await resolveProjectIdForSync(
|
|
1884
|
+
const { projectId } = await resolveProjectIdForSync(
|
|
1885
|
+
api,
|
|
1886
|
+
workdirPath,
|
|
1887
|
+
options
|
|
1888
|
+
);
|
|
1853
1889
|
if (!projectId) {
|
|
1854
1890
|
return 0;
|
|
1855
1891
|
}
|
|
1856
1892
|
const targetApmDir = apmRoot ?? workspaceApmDir(workdirPath);
|
|
1857
|
-
const localPaths = listLocalDocumentPaths(targetApmDir);
|
|
1893
|
+
const localPaths = listLocalDocumentPaths(targetApmDir, projectId);
|
|
1858
1894
|
if (localPaths.length === 0) {
|
|
1859
1895
|
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u672C\u5730\u6587\u4EF6\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
1860
1896
|
return 0;
|
|
@@ -1864,14 +1900,13 @@ async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot) {
|
|
|
1864
1900
|
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
1865
1901
|
);
|
|
1866
1902
|
const remoteDescriptionByPath = new Map(
|
|
1867
|
-
(remoteManifest?.documents ?? []).map((doc) => [
|
|
1868
|
-
doc.path,
|
|
1869
|
-
doc.description
|
|
1870
|
-
])
|
|
1903
|
+
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.description])
|
|
1871
1904
|
);
|
|
1872
1905
|
let synced = 0;
|
|
1873
1906
|
for (const path19 of localPaths) {
|
|
1874
|
-
const absPath = toFsPath(
|
|
1907
|
+
const absPath = toFsPath(
|
|
1908
|
+
projectDocumentLocalPath(targetApmDir, projectId, path19)
|
|
1909
|
+
);
|
|
1875
1910
|
const content = readFileSync3(absPath, "utf8");
|
|
1876
1911
|
const contentHash = hashLocalFileContent(content);
|
|
1877
1912
|
if (remoteHashByPath.get(path19) === contentHash) {
|
|
@@ -2498,7 +2533,9 @@ import { join as join6 } from "path";
|
|
|
2498
2533
|
var MANIFEST_FILE2 = ".sync-manifest.json";
|
|
2499
2534
|
async function downloadAttachment(cfg, attachmentId) {
|
|
2500
2535
|
const base = cfg.baseUrl.trim().replace(/\/+$/, "");
|
|
2501
|
-
const url = `${base}/api/v1/tasks/attachments/file?${new URLSearchParams({
|
|
2536
|
+
const url = `${base}/api/v1/tasks/attachments/file?${new URLSearchParams({
|
|
2537
|
+
attachmentId
|
|
2538
|
+
})}`;
|
|
2502
2539
|
const res = await fetch(url);
|
|
2503
2540
|
if (!res.ok) {
|
|
2504
2541
|
throw new Error(
|
|
@@ -2537,24 +2574,23 @@ function isAttachmentUpToDate(entry, item, dest) {
|
|
|
2537
2574
|
const createdAt = item.createdAt ?? "";
|
|
2538
2575
|
return entry.createdAt === createdAt;
|
|
2539
2576
|
}
|
|
2540
|
-
async function
|
|
2541
|
-
const dir = join6(sessionDir(sessionId, apmRoot), SESSION_ATTACHMENTS_SUBDIR);
|
|
2577
|
+
async function syncAttachmentsToDirectory(cfg, attachments, dir, logLabel) {
|
|
2542
2578
|
await ensureDirExists(dir);
|
|
2543
2579
|
if (attachments.length === 0) {
|
|
2544
2580
|
saveManifest(dir, { version: 1, attachments: {} });
|
|
2545
|
-
return;
|
|
2581
|
+
return [];
|
|
2546
2582
|
}
|
|
2547
2583
|
const manifest = loadManifest(dir);
|
|
2548
2584
|
const nextManifest = { version: 1, attachments: {} };
|
|
2585
|
+
const names = [];
|
|
2549
2586
|
for (const item of attachments) {
|
|
2550
2587
|
const dest = join6(dir, item.name);
|
|
2551
2588
|
const entry = manifest.attachments[item.id];
|
|
2552
2589
|
const createdAt = item.createdAt ?? "";
|
|
2590
|
+
names.push(item.name);
|
|
2553
2591
|
if (isAttachmentUpToDate(entry, item, dest)) {
|
|
2554
2592
|
nextManifest.attachments[item.id] = entry;
|
|
2555
|
-
console.log(
|
|
2556
|
-
`[apm] \u9644\u4EF6\u65E0\u53D8\u5316\uFF0C\u5DF2\u8DF3\u8FC7: ${SESSION_ATTACHMENTS_SUBDIR}/${item.name}`
|
|
2557
|
-
);
|
|
2593
|
+
console.log(`[apm] \u9644\u4EF6\u65E0\u53D8\u5316\uFF0C\u5DF2\u8DF3\u8FC7: ${logLabel}/${item.name}`);
|
|
2558
2594
|
continue;
|
|
2559
2595
|
}
|
|
2560
2596
|
const buffer = await downloadAttachment(cfg, item.id);
|
|
@@ -2563,9 +2599,19 @@ async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
|
|
|
2563
2599
|
name: item.name,
|
|
2564
2600
|
createdAt
|
|
2565
2601
|
};
|
|
2566
|
-
console.log(`[apm] \u5DF2\u4E0B\u8F7D\u9644\u4EF6: ${
|
|
2602
|
+
console.log(`[apm] \u5DF2\u4E0B\u8F7D\u9644\u4EF6: ${logLabel}/${item.name}`);
|
|
2567
2603
|
}
|
|
2568
2604
|
saveManifest(dir, nextManifest);
|
|
2605
|
+
return names;
|
|
2606
|
+
}
|
|
2607
|
+
async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
|
|
2608
|
+
const dir = join6(sessionDir(sessionId, apmRoot), SESSION_ATTACHMENTS_SUBDIR);
|
|
2609
|
+
return syncAttachmentsToDirectory(
|
|
2610
|
+
cfg,
|
|
2611
|
+
attachments,
|
|
2612
|
+
dir,
|
|
2613
|
+
SESSION_ATTACHMENTS_SUBDIR
|
|
2614
|
+
);
|
|
2569
2615
|
}
|
|
2570
2616
|
|
|
2571
2617
|
// src/rules-sync.ts
|
|
@@ -10031,12 +10077,12 @@ function buildProgram() {
|
|
|
10031
10077
|
await runSyncDeployConfig();
|
|
10032
10078
|
});
|
|
10033
10079
|
program.command("sync-project-documents").description(
|
|
10034
|
-
"\u540C\u6B65\u9879\u76EE\u6587\u6863\uFF1A\u9ED8\u8BA4 pull \u5230 .apm/
|
|
10035
|
-
).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) => {
|
|
10036
10082
|
await runSyncProjectDocuments(opts);
|
|
10037
10083
|
});
|
|
10038
10084
|
program.command("pull").description(
|
|
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/
|
|
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>/"
|
|
10040
10086
|
).argument("<sessionId>", "\u6C9F\u901A\u7FA4 ID").action(async (sessionId) => {
|
|
10041
10087
|
await runPull(sessionId);
|
|
10042
10088
|
});
|
|
@@ -88,6 +88,10 @@ var requestConfig = {
|
|
|
88
88
|
method: "GET",
|
|
89
89
|
path: "/cli/webide/assumptions"
|
|
90
90
|
}),
|
|
91
|
+
webideListAttachments: defineEndpoint({
|
|
92
|
+
method: "GET",
|
|
93
|
+
path: "/cli/webide/attachments"
|
|
94
|
+
}),
|
|
91
95
|
webideRecommendPlan: defineEndpoint({
|
|
92
96
|
method: "PUT",
|
|
93
97
|
path: "/cli/webide/plan-recommendation"
|
|
@@ -124,10 +128,12 @@ var requestConfig = {
|
|
|
124
128
|
method: "GET",
|
|
125
129
|
path: "/cli/tasks/branch-baseline"
|
|
126
130
|
}),
|
|
127
|
-
projectBaseBranch: defineEndpoint(
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
+
projectBaseBranch: defineEndpoint(
|
|
132
|
+
{
|
|
133
|
+
method: "GET",
|
|
134
|
+
path: "/cli/tasks/project-base-branch"
|
|
135
|
+
}
|
|
136
|
+
),
|
|
131
137
|
listSessionsForBranchCleanup: defineEndpoint({
|
|
132
138
|
method: "GET",
|
|
133
139
|
path: "/cli/sessions/branch-cleanup"
|
|
@@ -591,6 +597,14 @@ function assertApmGitignoredInRepo(workdir) {
|
|
|
591
597
|
);
|
|
592
598
|
}
|
|
593
599
|
}
|
|
600
|
+
var SESSION_ATTACHMENTS_SUBDIR = "attachments";
|
|
601
|
+
var WEBIDE_SUBDIR = "webide";
|
|
602
|
+
function webideTaskDir(taskId, workdir) {
|
|
603
|
+
return join2(workdir, ".apm", WEBIDE_SUBDIR, taskId);
|
|
604
|
+
}
|
|
605
|
+
function webideAttachmentsDir(taskId, workdir) {
|
|
606
|
+
return join2(webideTaskDir(taskId, workdir), SESSION_ATTACHMENTS_SUBDIR);
|
|
607
|
+
}
|
|
594
608
|
async function ensureLoggedConfig() {
|
|
595
609
|
const cfg = await ensureApmConfig();
|
|
596
610
|
if (!resolveApiKey(cfg)) {
|
|
@@ -2667,14 +2681,29 @@ import {
|
|
|
2667
2681
|
rmSync,
|
|
2668
2682
|
writeFileSync as writeFileSync7
|
|
2669
2683
|
} from "fs";
|
|
2684
|
+
import { createHash } from "crypto";
|
|
2670
2685
|
import { dirname as dirname4, join as join6, relative as relative2, sep } from "path";
|
|
2671
2686
|
var MANIFEST_FILE = "manifest.json";
|
|
2672
|
-
function
|
|
2673
|
-
|
|
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);
|
|
2674
2700
|
}
|
|
2675
|
-
function projectDocumentLocalPath(apmRoot, documentPath) {
|
|
2701
|
+
function projectDocumentLocalPath(apmRoot, projectId, documentPath) {
|
|
2676
2702
|
const normalized = normalizeLocalDocumentPath(documentPath);
|
|
2677
|
-
return join6(
|
|
2703
|
+
return join6(
|
|
2704
|
+
projectDocumentsDir(apmRoot, projectId),
|
|
2705
|
+
...normalized.split("/")
|
|
2706
|
+
);
|
|
2678
2707
|
}
|
|
2679
2708
|
function normalizeLocalDocumentPath(path) {
|
|
2680
2709
|
const trimmed = path.trim().replace(/\\/g, "/");
|
|
@@ -2687,8 +2716,14 @@ function normalizeLocalDocumentPath(path) {
|
|
|
2687
2716
|
}
|
|
2688
2717
|
return segments.join("/");
|
|
2689
2718
|
}
|
|
2690
|
-
function
|
|
2691
|
-
|
|
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
|
+
);
|
|
2692
2727
|
if (!existsSync5(manifestPath3)) {
|
|
2693
2728
|
return null;
|
|
2694
2729
|
}
|
|
@@ -2700,6 +2735,29 @@ function readLocalManifest(apmRoot) {
|
|
|
2700
2735
|
return null;
|
|
2701
2736
|
}
|
|
2702
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
|
+
}
|
|
2703
2761
|
function diffManifestPaths(remote, local) {
|
|
2704
2762
|
const remoteMap = new Map(
|
|
2705
2763
|
(remote?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
@@ -2721,7 +2779,11 @@ function diffManifestPaths(remote, local) {
|
|
|
2721
2779
|
}
|
|
2722
2780
|
return { download, deleteLocal };
|
|
2723
2781
|
}
|
|
2724
|
-
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
|
+
}
|
|
2725
2787
|
try {
|
|
2726
2788
|
const baseline = await api.cli.workspaceBaseline({ workdirPath });
|
|
2727
2789
|
const projectId = baseline.projectId?.trim() || null;
|
|
@@ -2749,7 +2811,7 @@ async function resolveProjectIdForSync(api, workdirPath) {
|
|
|
2749
2811
|
};
|
|
2750
2812
|
}
|
|
2751
2813
|
}
|
|
2752
|
-
async function syncProjectDocumentsPull(workdirPath, apmDir) {
|
|
2814
|
+
async function syncProjectDocumentsPull(workdirPath, apmDir, options) {
|
|
2753
2815
|
const empty = {
|
|
2754
2816
|
synced: false,
|
|
2755
2817
|
projectId: null,
|
|
@@ -2766,7 +2828,8 @@ async function syncProjectDocumentsPull(workdirPath, apmDir) {
|
|
|
2766
2828
|
const api = createApmApiClient(cfg);
|
|
2767
2829
|
const { projectId, diagnostic } = await resolveProjectIdForSync(
|
|
2768
2830
|
api,
|
|
2769
|
-
workdirPath
|
|
2831
|
+
workdirPath,
|
|
2832
|
+
options
|
|
2770
2833
|
);
|
|
2771
2834
|
if (!projectId) {
|
|
2772
2835
|
console.log(`[apm] \u672A\u80FD\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002
|
|
@@ -2774,14 +2837,16 @@ ${diagnostic ?? ""}`);
|
|
|
2774
2837
|
return empty;
|
|
2775
2838
|
}
|
|
2776
2839
|
const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
|
|
2777
|
-
const docsDir = projectDocumentsDir(targetApmDir);
|
|
2840
|
+
const docsDir = projectDocumentsDir(targetApmDir, projectId);
|
|
2778
2841
|
await ensureDirExists(docsDir);
|
|
2779
|
-
const { manifest: remoteManifest } = await api.cli.getProjectDocumentManifest(
|
|
2842
|
+
const { manifest: remoteManifest } = await api.cli.getProjectDocumentManifest(
|
|
2843
|
+
{ projectId }
|
|
2844
|
+
);
|
|
2780
2845
|
if (!remoteManifest) {
|
|
2781
2846
|
console.log(`[apm] \u9879\u76EE ${projectId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`);
|
|
2782
2847
|
return { ...empty, projectId };
|
|
2783
2848
|
}
|
|
2784
|
-
const localManifest = readLocalManifest(targetApmDir);
|
|
2849
|
+
const localManifest = readLocalManifest(targetApmDir, projectId);
|
|
2785
2850
|
const { download, deleteLocal } = diffManifestPaths(
|
|
2786
2851
|
remoteManifest,
|
|
2787
2852
|
localManifest
|
|
@@ -2794,7 +2859,7 @@ ${diagnostic ?? ""}`);
|
|
|
2794
2859
|
});
|
|
2795
2860
|
for (const doc of list) {
|
|
2796
2861
|
const absPath = toFsPath(
|
|
2797
|
-
projectDocumentLocalPath(targetApmDir, doc.path)
|
|
2862
|
+
projectDocumentLocalPath(targetApmDir, projectId, doc.path)
|
|
2798
2863
|
);
|
|
2799
2864
|
await ensureDirExists(dirname4(absPath));
|
|
2800
2865
|
writeFileSync7(absPath, doc.content, "utf8");
|
|
@@ -2803,7 +2868,9 @@ ${diagnostic ?? ""}`);
|
|
|
2803
2868
|
}
|
|
2804
2869
|
let deleted = 0;
|
|
2805
2870
|
for (const path of deleteLocal) {
|
|
2806
|
-
const absPath = toFsPath(
|
|
2871
|
+
const absPath = toFsPath(
|
|
2872
|
+
projectDocumentLocalPath(targetApmDir, projectId, path)
|
|
2873
|
+
);
|
|
2807
2874
|
if (existsSync5(absPath)) {
|
|
2808
2875
|
rmSync(absPath, { force: true });
|
|
2809
2876
|
deleted += 1;
|
|
@@ -2816,7 +2883,7 @@ ${diagnostic ?? ""}`);
|
|
|
2816
2883
|
"utf8"
|
|
2817
2884
|
);
|
|
2818
2885
|
console.log(
|
|
2819
|
-
`[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}/`
|
|
2820
2887
|
);
|
|
2821
2888
|
return {
|
|
2822
2889
|
synced: true,
|
|
@@ -2825,6 +2892,53 @@ ${diagnostic ?? ""}`);
|
|
|
2825
2892
|
deleted
|
|
2826
2893
|
};
|
|
2827
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
|
+
}
|
|
2828
2942
|
|
|
2829
2943
|
// src/commands/init.ts
|
|
2830
2944
|
async function ensureWorkspaceInitialized(workdir, options) {
|
|
@@ -3087,21 +3201,108 @@ async function syncWorkspaceSkills(cfg, workdir) {
|
|
|
3087
3201
|
);
|
|
3088
3202
|
}
|
|
3089
3203
|
|
|
3090
|
-
// src/commands/
|
|
3204
|
+
// src/commands/sync-session-attachments.ts
|
|
3091
3205
|
import { existsSync as existsSync8, readFileSync as readFileSync9, writeFileSync as writeFileSync10 } from "fs";
|
|
3092
3206
|
import { join as join11 } from "path";
|
|
3207
|
+
var MANIFEST_FILE2 = ".sync-manifest.json";
|
|
3208
|
+
async function downloadAttachment(cfg, attachmentId) {
|
|
3209
|
+
const base = cfg.baseUrl.trim().replace(/\/+$/, "");
|
|
3210
|
+
const url = `${base}/api/v1/tasks/attachments/file?${new URLSearchParams({
|
|
3211
|
+
attachmentId
|
|
3212
|
+
})}`;
|
|
3213
|
+
const res = await fetch(url);
|
|
3214
|
+
if (!res.ok) {
|
|
3215
|
+
throw new Error(
|
|
3216
|
+
`[apm] \u4E0B\u8F7D\u9644\u4EF6\u5931\u8D25 (${res.status}): attachmentId=${attachmentId}`
|
|
3217
|
+
);
|
|
3218
|
+
}
|
|
3219
|
+
return Buffer.from(await res.arrayBuffer());
|
|
3220
|
+
}
|
|
3221
|
+
function loadManifest(dir) {
|
|
3222
|
+
const path = join11(dir, MANIFEST_FILE2);
|
|
3223
|
+
if (!existsSync8(path)) {
|
|
3224
|
+
return { version: 1, attachments: {} };
|
|
3225
|
+
}
|
|
3226
|
+
try {
|
|
3227
|
+
const parsed = JSON.parse(
|
|
3228
|
+
readFileSync9(path, "utf8")
|
|
3229
|
+
);
|
|
3230
|
+
if (parsed?.version === 1 && parsed.attachments && typeof parsed.attachments === "object") {
|
|
3231
|
+
return parsed;
|
|
3232
|
+
}
|
|
3233
|
+
} catch {
|
|
3234
|
+
}
|
|
3235
|
+
return { version: 1, attachments: {} };
|
|
3236
|
+
}
|
|
3237
|
+
function saveManifest(dir, manifest) {
|
|
3238
|
+
writeFileSync10(
|
|
3239
|
+
join11(dir, MANIFEST_FILE2),
|
|
3240
|
+
`${JSON.stringify(manifest, null, 2)}
|
|
3241
|
+
`,
|
|
3242
|
+
"utf8"
|
|
3243
|
+
);
|
|
3244
|
+
}
|
|
3245
|
+
function isAttachmentUpToDate(entry, item, dest) {
|
|
3246
|
+
if (!entry || !existsSync8(dest)) return false;
|
|
3247
|
+
if (entry.name !== item.name) return false;
|
|
3248
|
+
const createdAt = item.createdAt ?? "";
|
|
3249
|
+
return entry.createdAt === createdAt;
|
|
3250
|
+
}
|
|
3251
|
+
async function syncAttachmentsToDirectory(cfg, attachments, dir, logLabel) {
|
|
3252
|
+
await ensureDirExists(dir);
|
|
3253
|
+
if (attachments.length === 0) {
|
|
3254
|
+
saveManifest(dir, { version: 1, attachments: {} });
|
|
3255
|
+
return [];
|
|
3256
|
+
}
|
|
3257
|
+
const manifest = loadManifest(dir);
|
|
3258
|
+
const nextManifest = { version: 1, attachments: {} };
|
|
3259
|
+
const names = [];
|
|
3260
|
+
for (const item of attachments) {
|
|
3261
|
+
const dest = join11(dir, item.name);
|
|
3262
|
+
const entry = manifest.attachments[item.id];
|
|
3263
|
+
const createdAt = item.createdAt ?? "";
|
|
3264
|
+
names.push(item.name);
|
|
3265
|
+
if (isAttachmentUpToDate(entry, item, dest)) {
|
|
3266
|
+
nextManifest.attachments[item.id] = entry;
|
|
3267
|
+
console.log(`[apm] \u9644\u4EF6\u65E0\u53D8\u5316\uFF0C\u5DF2\u8DF3\u8FC7: ${logLabel}/${item.name}`);
|
|
3268
|
+
continue;
|
|
3269
|
+
}
|
|
3270
|
+
const buffer = await downloadAttachment(cfg, item.id);
|
|
3271
|
+
writeFileSync10(dest, buffer);
|
|
3272
|
+
nextManifest.attachments[item.id] = {
|
|
3273
|
+
name: item.name,
|
|
3274
|
+
createdAt
|
|
3275
|
+
};
|
|
3276
|
+
console.log(`[apm] \u5DF2\u4E0B\u8F7D\u9644\u4EF6: ${logLabel}/${item.name}`);
|
|
3277
|
+
}
|
|
3278
|
+
saveManifest(dir, nextManifest);
|
|
3279
|
+
return names;
|
|
3280
|
+
}
|
|
3281
|
+
async function syncWebIdeAttachments(cfg, taskId, workdir, attachments) {
|
|
3282
|
+
const dir = webideAttachmentsDir(taskId, workdir);
|
|
3283
|
+
return syncAttachmentsToDirectory(
|
|
3284
|
+
cfg,
|
|
3285
|
+
attachments,
|
|
3286
|
+
dir,
|
|
3287
|
+
`.apm/webide/${taskId}/attachments`
|
|
3288
|
+
);
|
|
3289
|
+
}
|
|
3290
|
+
|
|
3291
|
+
// src/commands/connect/cli-version-sync.ts
|
|
3292
|
+
import { existsSync as existsSync9, readFileSync as readFileSync10, writeFileSync as writeFileSync11 } from "fs";
|
|
3293
|
+
import { join as join12 } from "path";
|
|
3093
3294
|
var CLI_VERSION_FILE = ".cli-version.json";
|
|
3094
3295
|
function manifestPath2(apmDir) {
|
|
3095
|
-
return
|
|
3296
|
+
return join12(apmDir, CLI_VERSION_FILE);
|
|
3096
3297
|
}
|
|
3097
|
-
function
|
|
3298
|
+
function loadManifest2(apmDir) {
|
|
3098
3299
|
const path = toFsPath(manifestPath2(apmDir));
|
|
3099
|
-
if (!
|
|
3300
|
+
if (!existsSync9(path)) {
|
|
3100
3301
|
return null;
|
|
3101
3302
|
}
|
|
3102
3303
|
try {
|
|
3103
3304
|
const parsed = JSON.parse(
|
|
3104
|
-
|
|
3305
|
+
readFileSync10(path, "utf8")
|
|
3105
3306
|
);
|
|
3106
3307
|
if (parsed?.version === 1 && typeof parsed.cliVersion === "string" && parsed.cliVersion.trim()) {
|
|
3107
3308
|
return parsed;
|
|
@@ -3110,9 +3311,9 @@ function loadManifest(apmDir) {
|
|
|
3110
3311
|
}
|
|
3111
3312
|
return null;
|
|
3112
3313
|
}
|
|
3113
|
-
function
|
|
3314
|
+
function saveManifest2(apmDir, cliVersion) {
|
|
3114
3315
|
const manifest = { version: 1, cliVersion };
|
|
3115
|
-
|
|
3316
|
+
writeFileSync11(
|
|
3116
3317
|
toFsPath(manifestPath2(apmDir)),
|
|
3117
3318
|
`${JSON.stringify(manifest, null, 2)}
|
|
3118
3319
|
`,
|
|
@@ -3125,7 +3326,7 @@ function shouldSyncSkillsForCliVersion(workdir, currentVersion) {
|
|
|
3125
3326
|
if (cached === currentVersion) {
|
|
3126
3327
|
return false;
|
|
3127
3328
|
}
|
|
3128
|
-
const stored =
|
|
3329
|
+
const stored = loadManifest2(workspaceApmDir(workdir));
|
|
3129
3330
|
if (stored?.cliVersion === currentVersion) {
|
|
3130
3331
|
syncedInSession.set(workdir, currentVersion);
|
|
3131
3332
|
return false;
|
|
@@ -3133,7 +3334,7 @@ function shouldSyncSkillsForCliVersion(workdir, currentVersion) {
|
|
|
3133
3334
|
return true;
|
|
3134
3335
|
}
|
|
3135
3336
|
function markSkillsSyncedForCliVersion(workdir, cliVersion) {
|
|
3136
|
-
|
|
3337
|
+
saveManifest2(workspaceApmDir(workdir), cliVersion);
|
|
3137
3338
|
syncedInSession.set(workdir, cliVersion);
|
|
3138
3339
|
}
|
|
3139
3340
|
|
|
@@ -3206,6 +3407,51 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
|
|
|
3206
3407
|
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
3207
3408
|
await ensureWebIdePullRequests(cfg, taskId, workdir);
|
|
3208
3409
|
}
|
|
3410
|
+
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
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
|
+
}
|
|
3436
|
+
try {
|
|
3437
|
+
const attachments = await api.cli.webideListAttachments({ taskId });
|
|
3438
|
+
const names = await syncWebIdeAttachments(
|
|
3439
|
+
cfg,
|
|
3440
|
+
taskId,
|
|
3441
|
+
workdir,
|
|
3442
|
+
attachments ?? []
|
|
3443
|
+
);
|
|
3444
|
+
if (names.length > 0) {
|
|
3445
|
+
console.log(
|
|
3446
|
+
`[apm] webide \u9644\u4EF6\u5DF2\u5C31\u7EEA count=${names.length} dir=.apm/webide/${taskId}/attachments`
|
|
3447
|
+
);
|
|
3448
|
+
}
|
|
3449
|
+
} catch (err) {
|
|
3450
|
+
console.warn(
|
|
3451
|
+
"[apm] WebIDE \u9644\u4EF6\u540C\u6B65\u5931\u8D25:",
|
|
3452
|
+
err instanceof Error ? err.message : err
|
|
3453
|
+
);
|
|
3454
|
+
}
|
|
3209
3455
|
const savedAgentId = loadWebIdeAgentId(workdir, taskId);
|
|
3210
3456
|
const logSyncRef = { current: null };
|
|
3211
3457
|
const outcome = await runCursorAgent(
|
|
@@ -3272,7 +3518,6 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
|
|
|
3272
3518
|
result: outcome.result,
|
|
3273
3519
|
createPlan: outcome.createPlan
|
|
3274
3520
|
});
|
|
3275
|
-
const api = createApmApiClient(cfg);
|
|
3276
3521
|
await api.cli.webideEnsureMessageContent({
|
|
3277
3522
|
id: messageId,
|
|
3278
3523
|
content: fallback
|
|
@@ -3305,6 +3550,16 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
|
|
|
3305
3550
|
`[apm] webide \u81EA\u52A8 push action=${msg.action} repos=${pushed}`
|
|
3306
3551
|
);
|
|
3307
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
|
+
}
|
|
3308
3563
|
await logSyncRef.current?.markRun(outcome.runId, "finished");
|
|
3309
3564
|
await updateStatus(cfg, messageId, "SUCCESS");
|
|
3310
3565
|
console.log(
|
package/package.json
CHANGED
package/template/AGENTS.md
CHANGED
|
@@ -33,13 +33,15 @@
|
|
|
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>/\*):
|
|
44
|
+
|
|
43
45
|
- 本轮会话状态: `session.yaml`,从这里可以看到每个成员的信息,找到可以协助你一起解决问题的人,可以结合 `RULE.md`一起看
|
|
44
46
|
- 群文档: `docs/xxxx.md`,当需要相关上下文可以在这里查找,按需阅读
|
|
45
47
|
- 附件列表: `attachments/*`,当有文档中提及附件时,从这里查找
|
|
@@ -47,3 +49,6 @@
|
|
|
47
49
|
- 协作 TODO: `TODO.md`,跨轮次任务清单(待办与已完成项),按需阅读
|
|
48
50
|
- 历史消息记录: `messages.xml`,历史消息列表,数据量很大,按需阅读
|
|
49
51
|
- 初始目标: `TASK.md`,最初的目标,不一定具体,团队成员会有人负责让这个任务变得具体,仅供参考。如果你是相关的角色,则你需要先读取这个目标,然后规划接下来的动作。
|
|
52
|
+
|
|
53
|
+
- WebIDE 任务附件(`.apm/webide/<taskId>/attachments/`):
|
|
54
|
+
- `apm connect` 处理 webide-message 时会自动同步;prompt 中若出现「任务附件」指引,请按路径查看相关文件
|
|
@@ -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
|
|