ai-project-manage-cli 7.1.16 → 7.1.18
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 +625 -295
- package/dist/webide-message-worker.js +261 -26
- package/package.json +1 -1
- package/template/AGENTS.md +4 -3
- package/template/rules/webide_terminal.md +3 -3
|
@@ -342,6 +342,36 @@ function requireRemoteWorkdir(workdir) {
|
|
|
342
342
|
import { execFile } from "child_process";
|
|
343
343
|
import { promisify } from "util";
|
|
344
344
|
var execFileAsync = promisify(execFile);
|
|
345
|
+
function toHttpsGitRemoteUrl(raw) {
|
|
346
|
+
let s = raw.trim();
|
|
347
|
+
if (!s) return null;
|
|
348
|
+
const sshScp = /^git@([^:]+):(.+)$/.exec(s);
|
|
349
|
+
if (sshScp) {
|
|
350
|
+
s = `https://${sshScp[1]}/${sshScp[2]}`;
|
|
351
|
+
} else {
|
|
352
|
+
const sshUri = /^ssh:\/\/(?:git@)?([^/]+)\/(.+)$/i.exec(s);
|
|
353
|
+
if (sshUri) {
|
|
354
|
+
s = `https://${sshUri[1]}/${sshUri[2]}`;
|
|
355
|
+
} else if (/^http:\/\//i.test(s)) {
|
|
356
|
+
s = `https://${s.slice("http://".length)}`;
|
|
357
|
+
} else if (!/^https:\/\//i.test(s)) {
|
|
358
|
+
const scpLike = /^([^/:]+):(.+)$/.exec(s);
|
|
359
|
+
if (scpLike && !scpLike[1].includes(".")) {
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
if (scpLike) {
|
|
363
|
+
s = `https://${scpLike[1]}/${scpLike[2]}`;
|
|
364
|
+
} else if (s.includes("/")) {
|
|
365
|
+
s = `https://${s}`;
|
|
366
|
+
} else {
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
s = s.replace(/\/+$/, "");
|
|
372
|
+
if (!/^https:\/\//i.test(s)) return null;
|
|
373
|
+
return s;
|
|
374
|
+
}
|
|
345
375
|
async function tryReadGitOriginUrl(cwd) {
|
|
346
376
|
try {
|
|
347
377
|
const { stdout } = await execFileAsync(
|
|
@@ -355,6 +385,11 @@ async function tryReadGitOriginUrl(cwd) {
|
|
|
355
385
|
return null;
|
|
356
386
|
}
|
|
357
387
|
}
|
|
388
|
+
async function tryReadHttpsGitOriginUrl(cwd) {
|
|
389
|
+
const raw = await tryReadGitOriginUrl(cwd);
|
|
390
|
+
if (!raw) return null;
|
|
391
|
+
return toHttpsGitRemoteUrl(raw);
|
|
392
|
+
}
|
|
358
393
|
|
|
359
394
|
// src/git-utils.ts
|
|
360
395
|
import { execFile as execFile2 } from "child_process";
|
|
@@ -718,7 +753,7 @@ import {
|
|
|
718
753
|
statSync as statSync2,
|
|
719
754
|
writeFileSync as writeFileSync3
|
|
720
755
|
} from "fs";
|
|
721
|
-
import { basename as basename2, join as join3, relative, resolve as resolve3 } from "path";
|
|
756
|
+
import { basename as basename2, dirname as dirname2, join as join3, relative, resolve as resolve3 } from "path";
|
|
722
757
|
var WORKSPACE_REPOS_MANIFEST = "workspace-repos.json";
|
|
723
758
|
var WORKSPACE_REPOS_VERSION = 1;
|
|
724
759
|
var SKIP_DIR_NAMES = /* @__PURE__ */ new Set([".apm", "node_modules"]);
|
|
@@ -743,6 +778,20 @@ function toRelativeRepoPath(workdir, repoAbs) {
|
|
|
743
778
|
const rel = relative(workdir, repoAbs).replace(/\\/g, "/");
|
|
744
779
|
return rel === "" ? "." : rel;
|
|
745
780
|
}
|
|
781
|
+
function normalizeRepoEntry(raw) {
|
|
782
|
+
if (!raw || typeof raw !== "object") return null;
|
|
783
|
+
const o = raw;
|
|
784
|
+
if (typeof o.path !== "string" || !o.path.trim()) return null;
|
|
785
|
+
const entry = {
|
|
786
|
+
path: o.path.trim().replace(/\\/g, "/")
|
|
787
|
+
};
|
|
788
|
+
if (typeof o.remoteUrl === "string" && o.remoteUrl.trim()) {
|
|
789
|
+
entry.remoteUrl = o.remoteUrl.trim();
|
|
790
|
+
} else if (o.remoteUrl === null) {
|
|
791
|
+
entry.remoteUrl = null;
|
|
792
|
+
}
|
|
793
|
+
return entry;
|
|
794
|
+
}
|
|
746
795
|
function readManifest(workdir) {
|
|
747
796
|
const path = toFsPath(manifestPath(workdir));
|
|
748
797
|
if (!existsSync2(path)) {
|
|
@@ -764,7 +813,17 @@ function readManifest(workdir) {
|
|
|
764
813
|
if (typeof raw.workdir !== "string" || !raw.workdir.trim()) {
|
|
765
814
|
return null;
|
|
766
815
|
}
|
|
767
|
-
|
|
816
|
+
const repos = raw.repos.map((item) => normalizeRepoEntry(item)).filter((item) => item != null);
|
|
817
|
+
if (repos.length === 0) {
|
|
818
|
+
return null;
|
|
819
|
+
}
|
|
820
|
+
return {
|
|
821
|
+
version: WORKSPACE_REPOS_VERSION,
|
|
822
|
+
kind: raw.kind,
|
|
823
|
+
workdir: raw.workdir,
|
|
824
|
+
repos,
|
|
825
|
+
scannedAt: typeof raw.scannedAt === "string" && raw.scannedAt.trim() ? raw.scannedAt : (/* @__PURE__ */ new Date()).toISOString()
|
|
826
|
+
};
|
|
768
827
|
} catch {
|
|
769
828
|
return null;
|
|
770
829
|
}
|
|
@@ -859,6 +918,31 @@ function loadWorkspaceReposCache(workdirInput) {
|
|
|
859
918
|
workdir
|
|
860
919
|
};
|
|
861
920
|
}
|
|
921
|
+
async function enrichWorkspaceReposRemoteUrls(manifest) {
|
|
922
|
+
let changed = false;
|
|
923
|
+
const repos = [];
|
|
924
|
+
for (const entry of manifest.repos) {
|
|
925
|
+
const abs = absoluteRepoPath(manifest.workdir, entry);
|
|
926
|
+
const remoteUrl = await tryReadHttpsGitOriginUrl(abs);
|
|
927
|
+
const next = {
|
|
928
|
+
path: entry.path,
|
|
929
|
+
remoteUrl: remoteUrl || null
|
|
930
|
+
};
|
|
931
|
+
if ((entry.remoteUrl ?? null) !== next.remoteUrl) {
|
|
932
|
+
changed = true;
|
|
933
|
+
}
|
|
934
|
+
repos.push(next);
|
|
935
|
+
}
|
|
936
|
+
const nextManifest = {
|
|
937
|
+
...manifest,
|
|
938
|
+
repos,
|
|
939
|
+
scannedAt: changed ? (/* @__PURE__ */ new Date()).toISOString() : manifest.scannedAt
|
|
940
|
+
};
|
|
941
|
+
if (changed) {
|
|
942
|
+
writeManifest(manifest.workdir, nextManifest);
|
|
943
|
+
}
|
|
944
|
+
return nextManifest;
|
|
945
|
+
}
|
|
862
946
|
function resolveWorkspaceRepos(workdirInput, options) {
|
|
863
947
|
const workdir = resolveWorkdirPath(workdirInput);
|
|
864
948
|
if (!options?.forceRescan) {
|
|
@@ -871,6 +955,15 @@ function resolveWorkspaceRepos(workdirInput, options) {
|
|
|
871
955
|
}
|
|
872
956
|
}
|
|
873
957
|
const scanned = scanWorkspaceRepos(workdir);
|
|
958
|
+
const previous = readManifest(workdir);
|
|
959
|
+
if (previous) {
|
|
960
|
+
const prevByPath = new Map(previous.repos.map((r) => [r.path, r]));
|
|
961
|
+
scanned.repos = scanned.repos.map((entry) => {
|
|
962
|
+
const old = prevByPath.get(entry.path);
|
|
963
|
+
if (!old?.remoteUrl) return entry;
|
|
964
|
+
return { ...entry, remoteUrl: old.remoteUrl };
|
|
965
|
+
});
|
|
966
|
+
}
|
|
874
967
|
writeManifest(workdir, scanned);
|
|
875
968
|
console.log(
|
|
876
969
|
`[apm] \u5DF2\u626B\u63CF\u5DE5\u4F5C\u533A\u4ED3\u5E93 kind=${scanned.kind} repos=${scanned.repos.map((r) => r.path).join(", ")} \u2192 .apm/${WORKSPACE_REPOS_MANIFEST}`
|
|
@@ -1329,7 +1422,7 @@ ${JSON.stringify(event, null, 2)}
|
|
|
1329
1422
|
|
|
1330
1423
|
// src/commands/connect/agent-session-registry.ts
|
|
1331
1424
|
import { existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
1332
|
-
import { dirname as
|
|
1425
|
+
import { dirname as dirname3, resolve as resolve4 } from "node:path";
|
|
1333
1426
|
function registryPath(workdir, sessionId) {
|
|
1334
1427
|
return resolve4(workdir, ".apm", "sessions", sessionId, "cursor-agents.json");
|
|
1335
1428
|
}
|
|
@@ -1355,7 +1448,7 @@ function readRegistry(path) {
|
|
|
1355
1448
|
return {};
|
|
1356
1449
|
}
|
|
1357
1450
|
function writeRegistry(path, registry) {
|
|
1358
|
-
mkdirSync4(
|
|
1451
|
+
mkdirSync4(dirname3(path), { recursive: true });
|
|
1359
1452
|
writeFileSync4(path, `${JSON.stringify(registry, null, 2)}
|
|
1360
1453
|
`, "utf8");
|
|
1361
1454
|
}
|
|
@@ -2279,7 +2372,7 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
2279
2372
|
|
|
2280
2373
|
// src/commands/connect/webide-agent-registry.ts
|
|
2281
2374
|
import { existsSync as existsSync4, mkdirSync as mkdirSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "node:fs";
|
|
2282
|
-
import { dirname as
|
|
2375
|
+
import { dirname as dirname4, resolve as resolve5 } from "node:path";
|
|
2283
2376
|
function registryPath2(workdir, taskId) {
|
|
2284
2377
|
return resolve5(workdir, ".apm", "webide", taskId, "cursor-agent.json");
|
|
2285
2378
|
}
|
|
@@ -2300,7 +2393,7 @@ function readRegistry2(path) {
|
|
|
2300
2393
|
return {};
|
|
2301
2394
|
}
|
|
2302
2395
|
function writeRegistry2(path, registry) {
|
|
2303
|
-
mkdirSync6(
|
|
2396
|
+
mkdirSync6(dirname4(path), { recursive: true });
|
|
2304
2397
|
writeFileSync5(path, `${JSON.stringify(registry, null, 2)}
|
|
2305
2398
|
`, "utf8");
|
|
2306
2399
|
}
|
|
@@ -2681,14 +2774,29 @@ import {
|
|
|
2681
2774
|
rmSync,
|
|
2682
2775
|
writeFileSync as writeFileSync7
|
|
2683
2776
|
} from "fs";
|
|
2684
|
-
import {
|
|
2777
|
+
import { createHash } from "crypto";
|
|
2778
|
+
import { dirname as dirname5, join as join6, relative as relative2, sep } from "path";
|
|
2685
2779
|
var MANIFEST_FILE = "manifest.json";
|
|
2686
|
-
function
|
|
2687
|
-
|
|
2780
|
+
function normalizeProjectIdForPath(projectId) {
|
|
2781
|
+
const id = projectId.trim();
|
|
2782
|
+
if (!id) {
|
|
2783
|
+
throw new Error("projectId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
2784
|
+
}
|
|
2785
|
+
if (id.includes("..") || id.includes("/") || id.includes("\\") || id.includes("\0")) {
|
|
2786
|
+
throw new Error(`\u975E\u6CD5 projectId: ${projectId}`);
|
|
2787
|
+
}
|
|
2788
|
+
return id;
|
|
2789
|
+
}
|
|
2790
|
+
function projectDocumentsDir(apmRoot, projectId) {
|
|
2791
|
+
const id = normalizeProjectIdForPath(projectId);
|
|
2792
|
+
return join6(apmRoot ?? workspaceApmDir(), "project", id);
|
|
2688
2793
|
}
|
|
2689
|
-
function projectDocumentLocalPath(apmRoot, documentPath) {
|
|
2794
|
+
function projectDocumentLocalPath(apmRoot, projectId, documentPath) {
|
|
2690
2795
|
const normalized = normalizeLocalDocumentPath(documentPath);
|
|
2691
|
-
return join6(
|
|
2796
|
+
return join6(
|
|
2797
|
+
projectDocumentsDir(apmRoot, projectId),
|
|
2798
|
+
...normalized.split("/")
|
|
2799
|
+
);
|
|
2692
2800
|
}
|
|
2693
2801
|
function normalizeLocalDocumentPath(path) {
|
|
2694
2802
|
const trimmed = path.trim().replace(/\\/g, "/");
|
|
@@ -2701,8 +2809,14 @@ function normalizeLocalDocumentPath(path) {
|
|
|
2701
2809
|
}
|
|
2702
2810
|
return segments.join("/");
|
|
2703
2811
|
}
|
|
2704
|
-
function
|
|
2705
|
-
|
|
2812
|
+
function hashLocalFileContent(content) {
|
|
2813
|
+
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
2814
|
+
}
|
|
2815
|
+
function readLocalManifest(apmRoot, projectId) {
|
|
2816
|
+
const manifestPath3 = join6(
|
|
2817
|
+
projectDocumentsDir(apmRoot, projectId),
|
|
2818
|
+
MANIFEST_FILE
|
|
2819
|
+
);
|
|
2706
2820
|
if (!existsSync5(manifestPath3)) {
|
|
2707
2821
|
return null;
|
|
2708
2822
|
}
|
|
@@ -2714,6 +2828,29 @@ function readLocalManifest(apmRoot) {
|
|
|
2714
2828
|
return null;
|
|
2715
2829
|
}
|
|
2716
2830
|
}
|
|
2831
|
+
function listLocalDocumentPaths(apmRoot, projectId) {
|
|
2832
|
+
const root = projectDocumentsDir(apmRoot, projectId);
|
|
2833
|
+
if (!existsSync5(root)) {
|
|
2834
|
+
return [];
|
|
2835
|
+
}
|
|
2836
|
+
const paths = [];
|
|
2837
|
+
const walk = (dir) => {
|
|
2838
|
+
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
2839
|
+
const abs = join6(dir, entry.name);
|
|
2840
|
+
if (entry.isDirectory()) {
|
|
2841
|
+
walk(abs);
|
|
2842
|
+
continue;
|
|
2843
|
+
}
|
|
2844
|
+
if (entry.isFile() && entry.name === MANIFEST_FILE) {
|
|
2845
|
+
continue;
|
|
2846
|
+
}
|
|
2847
|
+
const rel = relative2(root, abs).split(sep).join("/");
|
|
2848
|
+
paths.push(rel);
|
|
2849
|
+
}
|
|
2850
|
+
};
|
|
2851
|
+
walk(root);
|
|
2852
|
+
return paths.sort();
|
|
2853
|
+
}
|
|
2717
2854
|
function diffManifestPaths(remote, local) {
|
|
2718
2855
|
const remoteMap = new Map(
|
|
2719
2856
|
(remote?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
@@ -2735,7 +2872,11 @@ function diffManifestPaths(remote, local) {
|
|
|
2735
2872
|
}
|
|
2736
2873
|
return { download, deleteLocal };
|
|
2737
2874
|
}
|
|
2738
|
-
async function resolveProjectIdForSync(api, workdirPath) {
|
|
2875
|
+
async function resolveProjectIdForSync(api, workdirPath, options) {
|
|
2876
|
+
const explicit = options?.projectId?.trim();
|
|
2877
|
+
if (explicit) {
|
|
2878
|
+
return { projectId: explicit, diagnostic: null };
|
|
2879
|
+
}
|
|
2739
2880
|
try {
|
|
2740
2881
|
const baseline = await api.cli.workspaceBaseline({ workdirPath });
|
|
2741
2882
|
const projectId = baseline.projectId?.trim() || null;
|
|
@@ -2763,7 +2904,7 @@ async function resolveProjectIdForSync(api, workdirPath) {
|
|
|
2763
2904
|
};
|
|
2764
2905
|
}
|
|
2765
2906
|
}
|
|
2766
|
-
async function syncProjectDocumentsPull(workdirPath, apmDir) {
|
|
2907
|
+
async function syncProjectDocumentsPull(workdirPath, apmDir, options) {
|
|
2767
2908
|
const empty = {
|
|
2768
2909
|
synced: false,
|
|
2769
2910
|
projectId: null,
|
|
@@ -2780,7 +2921,8 @@ async function syncProjectDocumentsPull(workdirPath, apmDir) {
|
|
|
2780
2921
|
const api = createApmApiClient(cfg);
|
|
2781
2922
|
const { projectId, diagnostic } = await resolveProjectIdForSync(
|
|
2782
2923
|
api,
|
|
2783
|
-
workdirPath
|
|
2924
|
+
workdirPath,
|
|
2925
|
+
options
|
|
2784
2926
|
);
|
|
2785
2927
|
if (!projectId) {
|
|
2786
2928
|
console.log(`[apm] \u672A\u80FD\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002
|
|
@@ -2788,14 +2930,16 @@ ${diagnostic ?? ""}`);
|
|
|
2788
2930
|
return empty;
|
|
2789
2931
|
}
|
|
2790
2932
|
const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
|
|
2791
|
-
const docsDir = projectDocumentsDir(targetApmDir);
|
|
2933
|
+
const docsDir = projectDocumentsDir(targetApmDir, projectId);
|
|
2792
2934
|
await ensureDirExists(docsDir);
|
|
2793
|
-
const { manifest: remoteManifest } = await api.cli.getProjectDocumentManifest(
|
|
2935
|
+
const { manifest: remoteManifest } = await api.cli.getProjectDocumentManifest(
|
|
2936
|
+
{ projectId }
|
|
2937
|
+
);
|
|
2794
2938
|
if (!remoteManifest) {
|
|
2795
2939
|
console.log(`[apm] \u9879\u76EE ${projectId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`);
|
|
2796
2940
|
return { ...empty, projectId };
|
|
2797
2941
|
}
|
|
2798
|
-
const localManifest = readLocalManifest(targetApmDir);
|
|
2942
|
+
const localManifest = readLocalManifest(targetApmDir, projectId);
|
|
2799
2943
|
const { download, deleteLocal } = diffManifestPaths(
|
|
2800
2944
|
remoteManifest,
|
|
2801
2945
|
localManifest
|
|
@@ -2808,16 +2952,18 @@ ${diagnostic ?? ""}`);
|
|
|
2808
2952
|
});
|
|
2809
2953
|
for (const doc of list) {
|
|
2810
2954
|
const absPath = toFsPath(
|
|
2811
|
-
projectDocumentLocalPath(targetApmDir, doc.path)
|
|
2955
|
+
projectDocumentLocalPath(targetApmDir, projectId, doc.path)
|
|
2812
2956
|
);
|
|
2813
|
-
await ensureDirExists(
|
|
2957
|
+
await ensureDirExists(dirname5(absPath));
|
|
2814
2958
|
writeFileSync7(absPath, doc.content, "utf8");
|
|
2815
2959
|
downloaded += 1;
|
|
2816
2960
|
}
|
|
2817
2961
|
}
|
|
2818
2962
|
let deleted = 0;
|
|
2819
2963
|
for (const path of deleteLocal) {
|
|
2820
|
-
const absPath = toFsPath(
|
|
2964
|
+
const absPath = toFsPath(
|
|
2965
|
+
projectDocumentLocalPath(targetApmDir, projectId, path)
|
|
2966
|
+
);
|
|
2821
2967
|
if (existsSync5(absPath)) {
|
|
2822
2968
|
rmSync(absPath, { force: true });
|
|
2823
2969
|
deleted += 1;
|
|
@@ -2830,7 +2976,7 @@ ${diagnostic ?? ""}`);
|
|
|
2830
2976
|
"utf8"
|
|
2831
2977
|
);
|
|
2832
2978
|
console.log(
|
|
2833
|
-
`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted}
|
|
2979
|
+
`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: projectId=${projectId} \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted} \u2192 .apm/project/${projectId}/`
|
|
2834
2980
|
);
|
|
2835
2981
|
return {
|
|
2836
2982
|
synced: true,
|
|
@@ -2839,6 +2985,53 @@ ${diagnostic ?? ""}`);
|
|
|
2839
2985
|
deleted
|
|
2840
2986
|
};
|
|
2841
2987
|
}
|
|
2988
|
+
async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot, options) {
|
|
2989
|
+
const api = createApmApiClient(cfg);
|
|
2990
|
+
const { projectId } = await resolveProjectIdForSync(
|
|
2991
|
+
api,
|
|
2992
|
+
workdirPath,
|
|
2993
|
+
options
|
|
2994
|
+
);
|
|
2995
|
+
if (!projectId) {
|
|
2996
|
+
return 0;
|
|
2997
|
+
}
|
|
2998
|
+
const targetApmDir = apmRoot ?? workspaceApmDir(workdirPath);
|
|
2999
|
+
const localPaths = listLocalDocumentPaths(targetApmDir, projectId);
|
|
3000
|
+
if (localPaths.length === 0) {
|
|
3001
|
+
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u672C\u5730\u6587\u4EF6\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
3002
|
+
return 0;
|
|
3003
|
+
}
|
|
3004
|
+
const remoteManifest = (await api.cli.getProjectDocumentManifest({ projectId })).manifest ?? null;
|
|
3005
|
+
const remoteHashByPath = new Map(
|
|
3006
|
+
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
3007
|
+
);
|
|
3008
|
+
const remoteDescriptionByPath = new Map(
|
|
3009
|
+
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.description])
|
|
3010
|
+
);
|
|
3011
|
+
let synced = 0;
|
|
3012
|
+
for (const path of localPaths) {
|
|
3013
|
+
const absPath = toFsPath(
|
|
3014
|
+
projectDocumentLocalPath(targetApmDir, projectId, path)
|
|
3015
|
+
);
|
|
3016
|
+
const content = readFileSync6(absPath, "utf8");
|
|
3017
|
+
const contentHash = hashLocalFileContent(content);
|
|
3018
|
+
if (remoteHashByPath.get(path) === contentHash) {
|
|
3019
|
+
continue;
|
|
3020
|
+
}
|
|
3021
|
+
await api.cli.upsertProjectDocument({
|
|
3022
|
+
projectId,
|
|
3023
|
+
path,
|
|
3024
|
+
content,
|
|
3025
|
+
description: remoteDescriptionByPath.get(path) ?? void 0
|
|
3026
|
+
});
|
|
3027
|
+
synced += 1;
|
|
3028
|
+
console.log(`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: ${path}`);
|
|
3029
|
+
}
|
|
3030
|
+
if (synced === 0) {
|
|
3031
|
+
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
3032
|
+
}
|
|
3033
|
+
return synced;
|
|
3034
|
+
}
|
|
2842
3035
|
|
|
2843
3036
|
// src/commands/init.ts
|
|
2844
3037
|
async function ensureWorkspaceInitialized(workdir, options) {
|
|
@@ -2947,11 +3140,11 @@ async function ensureWebIdePullRequests(cfg, taskId, workdir) {
|
|
|
2947
3140
|
|
|
2948
3141
|
// src/version.ts
|
|
2949
3142
|
import { readFileSync as readFileSync8 } from "fs";
|
|
2950
|
-
import { dirname as
|
|
3143
|
+
import { dirname as dirname6, join as join8 } from "path";
|
|
2951
3144
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2952
3145
|
function readCliVersion() {
|
|
2953
3146
|
try {
|
|
2954
|
-
const dir =
|
|
3147
|
+
const dir = dirname6(fileURLToPath2(import.meta.url));
|
|
2955
3148
|
const pkgPath = join8(dir, "..", "package.json");
|
|
2956
3149
|
const pkg = JSON.parse(readFileSync8(pkgPath, "utf8"));
|
|
2957
3150
|
return pkg.version ?? "0.0.0";
|
|
@@ -3285,7 +3478,15 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
|
|
|
3285
3478
|
await syncWorkspaceSkills(cfg, workdir);
|
|
3286
3479
|
markSkillsSyncedForCliVersion(workdir, cliVersion);
|
|
3287
3480
|
}
|
|
3288
|
-
resolveWorkspaceRepos(workdir);
|
|
3481
|
+
const workspaceRepos = resolveWorkspaceRepos(workdir);
|
|
3482
|
+
try {
|
|
3483
|
+
await enrichWorkspaceReposRemoteUrls(workspaceRepos);
|
|
3484
|
+
} catch (err) {
|
|
3485
|
+
console.warn(
|
|
3486
|
+
"[apm] \u8865\u9F50 workspace-repos remoteUrl \u5931\u8D25:",
|
|
3487
|
+
err instanceof Error ? err.message : err
|
|
3488
|
+
);
|
|
3489
|
+
}
|
|
3289
3490
|
if (shouldRunBranch(branchCacheKey, workdir)) {
|
|
3290
3491
|
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
3291
3492
|
const branchResult = await runTaskBranch(taskId, { cwd: workdir });
|
|
@@ -3309,6 +3510,30 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
|
|
|
3309
3510
|
}
|
|
3310
3511
|
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
3311
3512
|
const api = createApmApiClient(cfg);
|
|
3513
|
+
let projectId = null;
|
|
3514
|
+
try {
|
|
3515
|
+
const baseline = await api.cli.projectBaseBranch({ taskId });
|
|
3516
|
+
projectId = baseline.projectId?.trim() || null;
|
|
3517
|
+
if (projectId) {
|
|
3518
|
+
const docs = await syncProjectDocumentsPull(workdir, void 0, {
|
|
3519
|
+
projectId
|
|
3520
|
+
});
|
|
3521
|
+
if (docs.synced) {
|
|
3522
|
+
console.log(
|
|
3523
|
+
`[apm] webide \u9879\u76EE\u6587\u6863\u5DF2\u5C31\u7EEA projectId=${projectId} downloaded=${docs.downloaded} deleted=${docs.deleted}`
|
|
3524
|
+
);
|
|
3525
|
+
}
|
|
3526
|
+
} else {
|
|
3527
|
+
console.warn(
|
|
3528
|
+
`[apm] WebIDE \u65E0\u6CD5\u89E3\u6790\u4EFB\u52A1\u6240\u5C5E\u9879\u76EE\uFF0C\u8DF3\u8FC7\u9879\u76EE\u6587\u6863\u540C\u6B65 taskId=${taskId}`
|
|
3529
|
+
);
|
|
3530
|
+
}
|
|
3531
|
+
} catch (err) {
|
|
3532
|
+
console.warn(
|
|
3533
|
+
"[apm] WebIDE \u9879\u76EE\u6587\u6863\u540C\u6B65\u5931\u8D25:",
|
|
3534
|
+
err instanceof Error ? err.message : err
|
|
3535
|
+
);
|
|
3536
|
+
}
|
|
3312
3537
|
try {
|
|
3313
3538
|
const attachments = await api.cli.webideListAttachments({ taskId });
|
|
3314
3539
|
const names = await syncWebIdeAttachments(
|
|
@@ -3426,6 +3651,16 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
|
|
|
3426
3651
|
`[apm] webide \u81EA\u52A8 push action=${msg.action} repos=${pushed}`
|
|
3427
3652
|
);
|
|
3428
3653
|
}
|
|
3654
|
+
if (projectId) {
|
|
3655
|
+
try {
|
|
3656
|
+
await syncProjectDocumentsPush(cfg, workdir, void 0, { projectId });
|
|
3657
|
+
} catch (err) {
|
|
3658
|
+
console.warn(
|
|
3659
|
+
"[apm] WebIDE \u9879\u76EE\u6587\u6863\u63A8\u9001\u5931\u8D25:",
|
|
3660
|
+
err instanceof Error ? err.message : err
|
|
3661
|
+
);
|
|
3662
|
+
}
|
|
3663
|
+
}
|
|
3429
3664
|
await logSyncRef.current?.markRun(outcome.runId, "finished");
|
|
3430
3665
|
await updateStatus(cfg, messageId, "SUCCESS");
|
|
3431
3666
|
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
|
|