ai-project-manage-cli 7.1.36 → 7.1.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +625 -762
- package/dist/webide-message-worker.js +417 -646
- package/package.json +1 -1
|
@@ -600,84 +600,9 @@ async function pushCurrentBranch(cwd) {
|
|
|
600
600
|
}
|
|
601
601
|
return true;
|
|
602
602
|
}
|
|
603
|
-
var GITIGNORE_COMMIT_MESSAGE = "chore(apm): ignore .apm directory";
|
|
604
|
-
async function commitAndPushGitignore(workdir) {
|
|
605
|
-
if (!await isGitRepo(workdir)) {
|
|
606
|
-
console.log("[apm] \u5F53\u524D\u76EE\u5F55\u4E0D\u662F git \u4ED3\u5E93\uFF0C\u8BF7\u624B\u52A8\u63D0\u4EA4 .gitignore");
|
|
607
|
-
return;
|
|
608
|
-
}
|
|
609
|
-
await execGit(workdir, ["add", "--", ".gitignore"]);
|
|
610
|
-
await execGit(workdir, ["commit", "-m", GITIGNORE_COMMIT_MESSAGE]);
|
|
611
|
-
console.log(`[apm] \u5DF2\u63D0\u4EA4 .gitignore: ${GITIGNORE_COMMIT_MESSAGE}`);
|
|
612
|
-
const pushed = await pushCurrentBranch(workdir);
|
|
613
|
-
if (pushed) {
|
|
614
|
-
console.log("[apm] \u5DF2\u63A8\u9001 .gitignore");
|
|
615
|
-
} else {
|
|
616
|
-
console.log("[apm] \u672A\u914D\u7F6E remote.origin\uFF0C\u8BF7\u7A0D\u540E\u624B\u52A8 push .gitignore");
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
// src/baseline-resolve.ts
|
|
621
|
-
function formatBaselineDiagnostic(workdirPath, baselineWorkdirPath, diagnostic) {
|
|
622
|
-
return diagnostic?.message ?? `\u672A\u5728\u5E73\u53F0\u627E\u5230\u4E0E\u5F53\u524D\u76EE\u5F55\u5339\u914D\u7684\u5DE5\u4F5C\u76EE\u5F55\u767B\u8BB0\uFF1A${workdirPath}\uFF08\u89C4\u8303\u5316\uFF1A${baselineWorkdirPath}\uFF09
|
|
623
|
-
\u8BF7\u5148\u5728\u5E73\u53F0\u767B\u8BB0\u8BE5\u8DEF\u5F84\u5BF9\u5E94\u7684\u5DE5\u4F5C\u76EE\u5F55\u4E0E\u4ED3\u5E93\u3002`;
|
|
624
|
-
}
|
|
625
|
-
async function matchRepositoryByGitRemote(api, workdirPath) {
|
|
626
|
-
const gitRoot = await resolveGitRepoRoot(workdirPath);
|
|
627
|
-
const gitUrl = await tryReadGitOriginUrl(gitRoot);
|
|
628
|
-
if (!gitUrl) {
|
|
629
|
-
return null;
|
|
630
|
-
}
|
|
631
|
-
const matched = await api.cli.matchRepository({ url: gitUrl });
|
|
632
|
-
const repositoryId = matched.repositoryId?.trim();
|
|
633
|
-
const defaultBranch = matched.defaultBranch?.trim();
|
|
634
|
-
if (!repositoryId || !defaultBranch) {
|
|
635
|
-
return null;
|
|
636
|
-
}
|
|
637
|
-
console.log(
|
|
638
|
-
`[apm] \u5DE5\u4F5C\u7A7A\u95F4\u8DEF\u5F84\u672A\u5339\u914D\uFF0C\u5DF2\u901A\u8FC7 git remote \u5173\u8054\u4ED3\u5E93: ${gitUrl}`
|
|
639
|
-
);
|
|
640
|
-
return { repositoryId, defaultBranch };
|
|
641
|
-
}
|
|
642
|
-
async function resolveWorkspaceBaseline(api, workdirPath) {
|
|
643
|
-
const baseline = await api.cli.workspaceBaseline({ workdirPath });
|
|
644
|
-
const repositoryId = baseline.repositoryId?.trim();
|
|
645
|
-
const defaultBranch = baseline.defaultBranch?.trim();
|
|
646
|
-
if (repositoryId && defaultBranch) {
|
|
647
|
-
return {
|
|
648
|
-
repositoryId,
|
|
649
|
-
defaultBranch,
|
|
650
|
-
workdirPath: baseline.workdirPath,
|
|
651
|
-
matchedViaGitRemote: false
|
|
652
|
-
};
|
|
653
|
-
}
|
|
654
|
-
const viaGit = await matchRepositoryByGitRemote(api, workdirPath);
|
|
655
|
-
if (viaGit) {
|
|
656
|
-
return {
|
|
657
|
-
...viaGit,
|
|
658
|
-
workdirPath: baseline.workdirPath,
|
|
659
|
-
matchedViaGitRemote: true
|
|
660
|
-
};
|
|
661
|
-
}
|
|
662
|
-
throw new Error(
|
|
663
|
-
`[apm] ${formatBaselineDiagnostic(
|
|
664
|
-
workdirPath,
|
|
665
|
-
baseline.workdirPath,
|
|
666
|
-
baseline.diagnostic
|
|
667
|
-
)}`
|
|
668
|
-
);
|
|
669
|
-
}
|
|
670
603
|
|
|
671
604
|
// src/command-utils.ts
|
|
672
|
-
import {
|
|
673
|
-
copyFileSync,
|
|
674
|
-
existsSync,
|
|
675
|
-
mkdirSync as mkdirSync2,
|
|
676
|
-
readFileSync as readFileSync2,
|
|
677
|
-
readdirSync,
|
|
678
|
-
statSync,
|
|
679
|
-
writeFileSync as writeFileSync2
|
|
680
|
-
} from "fs";
|
|
605
|
+
import { copyFileSync, existsSync, mkdirSync as mkdirSync2, readdirSync, statSync } from "fs";
|
|
681
606
|
import { basename, dirname, extname, join as join2, resolve as resolve2 } from "path";
|
|
682
607
|
import { fileURLToPath } from "url";
|
|
683
608
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -699,63 +624,6 @@ function isWorkspaceApmInitialized(workdir) {
|
|
|
699
624
|
}
|
|
700
625
|
return readdirSync(fsApmDir).length > 0;
|
|
701
626
|
}
|
|
702
|
-
var APM_GITIGNORE_PATTERNS = [
|
|
703
|
-
/^\.apm\/?$/,
|
|
704
|
-
/^\.apm\/\*\*$/,
|
|
705
|
-
/^\*\*\/\.apm\/?$/,
|
|
706
|
-
/^\*\*\/\.apm\/\*\*$/,
|
|
707
|
-
/^\/\.apm\/?$/,
|
|
708
|
-
/^\/\.apm\/\*\*$/
|
|
709
|
-
];
|
|
710
|
-
function normalizeGitignorePattern(line) {
|
|
711
|
-
const trimmed = line.trim();
|
|
712
|
-
if (!trimmed || trimmed.startsWith("#")) return "";
|
|
713
|
-
if (trimmed.startsWith("!")) return "";
|
|
714
|
-
const hashIndex = trimmed.indexOf("#");
|
|
715
|
-
return (hashIndex >= 0 ? trimmed.slice(0, hashIndex) : trimmed).trim();
|
|
716
|
-
}
|
|
717
|
-
function gitignoreIgnoresApm(line) {
|
|
718
|
-
const pattern = normalizeGitignorePattern(line);
|
|
719
|
-
if (!pattern) return false;
|
|
720
|
-
return APM_GITIGNORE_PATTERNS.some((re) => re.test(pattern));
|
|
721
|
-
}
|
|
722
|
-
var APM_GITIGNORE_LINE = "**/.apm/**";
|
|
723
|
-
function ensureApmGitignoredInRepo(workdir) {
|
|
724
|
-
const gitignorePath = join2(workdir, ".gitignore");
|
|
725
|
-
const fsGitignorePath = toFsPath(gitignorePath);
|
|
726
|
-
if (!existsSync(fsGitignorePath)) {
|
|
727
|
-
writeFileSync2(fsGitignorePath, `${APM_GITIGNORE_LINE}
|
|
728
|
-
`, "utf8");
|
|
729
|
-
return true;
|
|
730
|
-
}
|
|
731
|
-
const content = readFileSync2(fsGitignorePath, "utf8");
|
|
732
|
-
if (content.split(/\r?\n/).some(gitignoreIgnoresApm)) {
|
|
733
|
-
return false;
|
|
734
|
-
}
|
|
735
|
-
const suffix = content.endsWith("\n") || content.length === 0 ? "" : "\n";
|
|
736
|
-
writeFileSync2(
|
|
737
|
-
fsGitignorePath,
|
|
738
|
-
`${content}${suffix}${APM_GITIGNORE_LINE}
|
|
739
|
-
`,
|
|
740
|
-
"utf8"
|
|
741
|
-
);
|
|
742
|
-
return true;
|
|
743
|
-
}
|
|
744
|
-
function assertApmGitignoredInRepo(workdir) {
|
|
745
|
-
const gitignorePath = join2(workdir, ".gitignore");
|
|
746
|
-
const fsGitignorePath = toFsPath(gitignorePath);
|
|
747
|
-
if (!existsSync(fsGitignorePath)) {
|
|
748
|
-
throw new Error(
|
|
749
|
-
`\u5DE5\u4F5C\u76EE\u5F55 ${workdir} \u7F3A\u5C11 .gitignore \u6587\u4EF6\uFF0C\u8BF7\u6DFB\u52A0\u5BF9 .apm \u7684\u5FFD\u7565\u89C4\u5219\uFF08\u4F8B\u5982 **/.apm/**\uFF09\uFF0C\u907F\u514D\u672C\u5730\u4F1A\u8BDD\u4E0E\u90E8\u7F72\u51ED\u636E\u88AB\u63D0\u4EA4\u3002`
|
|
750
|
-
);
|
|
751
|
-
}
|
|
752
|
-
const content = readFileSync2(fsGitignorePath, "utf8");
|
|
753
|
-
if (!content.split(/\r?\n/).some(gitignoreIgnoresApm)) {
|
|
754
|
-
throw new Error(
|
|
755
|
-
`\u5DE5\u4F5C\u76EE\u5F55 ${workdir} \u7684 .gitignore \u672A\u5FFD\u7565 .apm\uFF0C\u8BF7\u6DFB\u52A0 **/.apm/** \u6216 .apm/\uFF0C\u907F\u514D\u672C\u5730\u4F1A\u8BDD\u4E0E\u90E8\u7F72\u51ED\u636E\u88AB\u63D0\u4EA4\u3002`
|
|
756
|
-
);
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
627
|
var SESSION_ATTACHMENTS_SUBDIR = "attachments";
|
|
760
628
|
var WEBIDE_SUBDIR = "webide";
|
|
761
629
|
function webideTaskDir(taskId, workdir) {
|
|
@@ -780,19 +648,10 @@ async function ensureDirExists(dir) {
|
|
|
780
648
|
async function ensureWorkspaceApmDirForInit(cwd = resolveWorkdirPath()) {
|
|
781
649
|
const dir = workspaceApmDir(cwd);
|
|
782
650
|
const fsDir = toFsPath(dir);
|
|
783
|
-
if (
|
|
784
|
-
mkdirSync2(fsDir, { recursive: true });
|
|
785
|
-
return;
|
|
786
|
-
}
|
|
787
|
-
const st = statSync(fsDir);
|
|
788
|
-
if (!st.isDirectory()) {
|
|
651
|
+
if (existsSync(fsDir) && !statSync(fsDir).isDirectory()) {
|
|
789
652
|
throw new Error(`[apm] \u8DEF\u5F84\u5DF2\u5B58\u5728\u4F46\u4E0D\u662F\u76EE\u5F55: ${dir}`);
|
|
790
653
|
}
|
|
791
|
-
|
|
792
|
-
throw new Error(
|
|
793
|
-
"[apm] .apm \u76EE\u5F55\u5DF2\u5B58\u5728\u4E14\u975E\u7A7A\uFF0C\u8BF7\u5148\u5907\u4EFD\u3001\u6E05\u7A7A\u6216\u5220\u9664\u540E\u518D\u6267\u884C init"
|
|
794
|
-
);
|
|
795
|
-
}
|
|
654
|
+
mkdirSync2(fsDir, { recursive: true });
|
|
796
655
|
}
|
|
797
656
|
var WORKSPACE_TEMPLATE_SUBDIRS = [
|
|
798
657
|
"sessions",
|
|
@@ -872,10 +731,10 @@ async function copyTemplateFiles(targetDir, workdir = resolveWorkdirPath()) {
|
|
|
872
731
|
import {
|
|
873
732
|
existsSync as existsSync2,
|
|
874
733
|
mkdirSync as mkdirSync3,
|
|
875
|
-
readFileSync as
|
|
734
|
+
readFileSync as readFileSync2,
|
|
876
735
|
readdirSync as readdirSync2,
|
|
877
736
|
statSync as statSync2,
|
|
878
|
-
writeFileSync as
|
|
737
|
+
writeFileSync as writeFileSync2
|
|
879
738
|
} from "fs";
|
|
880
739
|
import { basename as basename2, dirname as dirname2, join as join3, relative, resolve as resolve3 } from "path";
|
|
881
740
|
var WORKSPACE_REPOS_MANIFEST = "workspace-repos.json";
|
|
@@ -923,7 +782,7 @@ function readManifest(workdir) {
|
|
|
923
782
|
}
|
|
924
783
|
try {
|
|
925
784
|
const raw = JSON.parse(
|
|
926
|
-
|
|
785
|
+
readFileSync2(path, "utf8")
|
|
927
786
|
);
|
|
928
787
|
if (raw?.version !== WORKSPACE_REPOS_VERSION) {
|
|
929
788
|
return null;
|
|
@@ -956,7 +815,7 @@ function writeManifest(workdir, manifest) {
|
|
|
956
815
|
const apmDir = toFsPath(workspaceApmDir(workdir));
|
|
957
816
|
mkdirSync3(apmDir, { recursive: true });
|
|
958
817
|
const path = toFsPath(manifestPath(workdir));
|
|
959
|
-
|
|
818
|
+
writeFileSync2(path, `${JSON.stringify(manifest, null, 2)}
|
|
960
819
|
`, "utf8");
|
|
961
820
|
}
|
|
962
821
|
function manifestStillValid(workdir, manifest) {
|
|
@@ -1576,7 +1435,7 @@ ${JSON.stringify(event, null, 2)}
|
|
|
1576
1435
|
}
|
|
1577
1436
|
|
|
1578
1437
|
// src/commands/connect/agent-session-registry.ts
|
|
1579
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as
|
|
1438
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
1580
1439
|
import { dirname as dirname3, resolve as resolve4 } from "node:path";
|
|
1581
1440
|
function registryPath(workdir, sessionId) {
|
|
1582
1441
|
return resolve4(workdir, ".apm", "sessions", sessionId, "cursor-agents.json");
|
|
@@ -1586,7 +1445,7 @@ function readRegistry(path) {
|
|
|
1586
1445
|
return {};
|
|
1587
1446
|
}
|
|
1588
1447
|
try {
|
|
1589
|
-
const parsed = JSON.parse(
|
|
1448
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
1590
1449
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
1591
1450
|
const result = {};
|
|
1592
1451
|
for (const [key, value] of Object.entries(
|
|
@@ -1604,7 +1463,7 @@ function readRegistry(path) {
|
|
|
1604
1463
|
}
|
|
1605
1464
|
function writeRegistry(path, registry) {
|
|
1606
1465
|
mkdirSync4(dirname3(path), { recursive: true });
|
|
1607
|
-
|
|
1466
|
+
writeFileSync3(path, `${JSON.stringify(registry, null, 2)}
|
|
1608
1467
|
`, "utf8");
|
|
1609
1468
|
}
|
|
1610
1469
|
function loadSessionAgentId(workdir, sessionId, user) {
|
|
@@ -2536,7 +2395,7 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
2536
2395
|
}
|
|
2537
2396
|
|
|
2538
2397
|
// src/commands/connect/webide-agent-registry.ts
|
|
2539
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync5, readFileSync as
|
|
2398
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
2540
2399
|
import { dirname as dirname4, resolve as resolve5 } from "node:path";
|
|
2541
2400
|
function registryPath2(workdir, taskId) {
|
|
2542
2401
|
return resolve5(workdir, ".apm", "webide", taskId, "cursor-agent.json");
|
|
@@ -2546,7 +2405,7 @@ function readRegistry2(path) {
|
|
|
2546
2405
|
return {};
|
|
2547
2406
|
}
|
|
2548
2407
|
try {
|
|
2549
|
-
const parsed = JSON.parse(
|
|
2408
|
+
const parsed = JSON.parse(readFileSync4(path, "utf8"));
|
|
2550
2409
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
2551
2410
|
const raw = parsed;
|
|
2552
2411
|
const state = {};
|
|
@@ -2576,7 +2435,7 @@ function readRegistry2(path) {
|
|
|
2576
2435
|
}
|
|
2577
2436
|
function writeRegistry2(path, registry) {
|
|
2578
2437
|
mkdirSync5(dirname4(path), { recursive: true });
|
|
2579
|
-
|
|
2438
|
+
writeFileSync4(path, `${JSON.stringify(registry, null, 2)}
|
|
2580
2439
|
`, "utf8");
|
|
2581
2440
|
}
|
|
2582
2441
|
function syncWebIdeTaskState(workdir, taskId, patch) {
|
|
@@ -3013,463 +2872,111 @@ function resolveMessageReplyFallback(fallback) {
|
|
|
3013
2872
|
}
|
|
3014
2873
|
|
|
3015
2874
|
// src/commands/init.ts
|
|
3016
|
-
import { join as join6 } from "path";
|
|
3017
|
-
import { readFileSync as readFileSync7, writeFileSync as writeFileSync8 } from "fs";
|
|
3018
|
-
|
|
3019
|
-
// src/deployment-config-sync.ts
|
|
3020
2875
|
import { join as join4 } from "path";
|
|
3021
|
-
import { writeFileSync as
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
const
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
let parsed;
|
|
3038
|
-
try {
|
|
3039
|
-
parsed = JSON.parse(content);
|
|
3040
|
-
} catch {
|
|
3041
|
-
console.warn(
|
|
3042
|
-
`[apm] \u8FDC\u7A0B\u90E8\u7F72\u914D\u7F6E\u300C${configName}\u300DJSON \u65E0\u6548\uFF08${TEMPLATE_HINT}\uFF09`
|
|
2876
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
2877
|
+
async function initializeWorkspace(workdir, options) {
|
|
2878
|
+
await ensureWorkspaceApmDirForInit(workdir);
|
|
2879
|
+
const apmDir = workspaceApmDir(workdir);
|
|
2880
|
+
await copyTemplateFiles(apmDir, workdir);
|
|
2881
|
+
const trimmedName = options?.name?.trim();
|
|
2882
|
+
if (trimmedName) {
|
|
2883
|
+
const apmConfigPath = toFsPath(join4(apmDir, "apm.config.json"));
|
|
2884
|
+
const config = readFileSync5(apmConfigPath, "utf8");
|
|
2885
|
+
const configJson = JSON.parse(config);
|
|
2886
|
+
configJson.name = trimmedName;
|
|
2887
|
+
writeFileSync5(
|
|
2888
|
+
apmConfigPath,
|
|
2889
|
+
`${JSON.stringify(configJson, null, 2)}
|
|
2890
|
+
`,
|
|
2891
|
+
"utf8"
|
|
3043
2892
|
);
|
|
3044
|
-
return false;
|
|
3045
2893
|
}
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
`, "utf8");
|
|
3049
|
-
console.log(`[apm] \u5DF2\u540C\u6B65\u5E73\u53F0\u90E8\u7F72\u914D\u7F6E: ${configName}`);
|
|
3050
|
-
console.log("[apm] \u5DF2\u5199\u5165 .apm/apm.config.json");
|
|
3051
|
-
return true;
|
|
2894
|
+
console.log(`[apm] \u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A\uFF1A${apmDir}`);
|
|
2895
|
+
return { didInit: true };
|
|
3052
2896
|
}
|
|
3053
|
-
async function
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
console.log(
|
|
3057
|
-
`[apm] \u672A\u68C0\u6D4B\u5230\u767B\u5F55\u4FE1\u606F\uFF0C\u8DF3\u8FC7\u5E73\u53F0\u90E8\u7F72\u914D\u7F6E\u540C\u6B65\uFF08${TEMPLATE_HINT}\uFF09\u3002
|
|
3058
|
-
[apm] \u8BF7\u5148\u6267\u884C apm login\uFF0C\u518D\u6267\u884C apm sync-deploy-config \u62C9\u53D6\u6700\u65B0\u914D\u7F6E\u3002`
|
|
3059
|
-
);
|
|
3060
|
-
return { synced: false, repositoryId: null };
|
|
3061
|
-
}
|
|
3062
|
-
const api = createApmApiClient(cfg);
|
|
3063
|
-
const preferredId = options?.repositoryId?.trim() || "";
|
|
3064
|
-
let repositoryId = preferredId || null;
|
|
3065
|
-
let diagnostic = null;
|
|
3066
|
-
if (!repositoryId) {
|
|
3067
|
-
const resolved = await resolveRepositoryIdForSync(api, workdirPath);
|
|
3068
|
-
repositoryId = resolved.repositoryId;
|
|
3069
|
-
diagnostic = resolved.diagnostic;
|
|
3070
|
-
}
|
|
3071
|
-
if (!repositoryId) {
|
|
3072
|
-
console.log(
|
|
3073
|
-
`[apm] \u672A\u80FD\u540C\u6B65\u5E73\u53F0\u90E8\u7F72\u914D\u7F6E\uFF08${TEMPLATE_HINT}\uFF09\u3002
|
|
3074
|
-
${diagnostic ?? ""}
|
|
3075
|
-
[apm] ${SYNC_HINT}`
|
|
3076
|
-
);
|
|
3077
|
-
return { synced: false, repositoryId: null };
|
|
3078
|
-
}
|
|
3079
|
-
const { config } = await api.cli.getDeploymentConfiguration({ repositoryId });
|
|
3080
|
-
if (!config) {
|
|
3081
|
-
console.log(
|
|
3082
|
-
`[apm] \u672A\u627E\u5230\u5173\u8054\u4ED3\u5E93\u7684\u90E8\u7F72\u914D\u7F6E\uFF08${TEMPLATE_HINT}\uFF0CrepositoryId\uFF1A${repositoryId}\uFF09\u3002
|
|
3083
|
-
[apm] \u8BF7\u5728\u5E73\u53F0\u300C\u90E8\u7F72\u914D\u7F6E\u300D\u4E2D\u521B\u5EFA\u914D\u7F6E\u5E76\u5173\u8054\u8BE5\u4ED3\u5E93\uFF0C\u7136\u540E\u6267\u884C: apm sync-deploy-config`
|
|
3084
|
-
);
|
|
3085
|
-
return { synced: false, repositoryId };
|
|
2897
|
+
async function ensureWorkspaceInitialized(workdir, options) {
|
|
2898
|
+
if (isWorkspaceApmInitialized(workdir)) {
|
|
2899
|
+
return { didInit: false };
|
|
3086
2900
|
}
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
targetApmDir,
|
|
3090
|
-
config.content,
|
|
3091
|
-
config.name
|
|
2901
|
+
console.log(
|
|
2902
|
+
`[apm] \u5DE5\u4F5C\u76EE\u5F55 ${workdir} \u672A\u68C0\u6D4B\u5230\u5DF2\u521D\u59CB\u5316\u7684 .apm\uFF0C\u6B63\u5728\u81EA\u52A8\u521D\u59CB\u5316\u2026`
|
|
3092
2903
|
);
|
|
3093
|
-
|
|
3094
|
-
return { synced: false, repositoryId };
|
|
3095
|
-
}
|
|
3096
|
-
return { synced: true, repositoryId, configName: config.name };
|
|
2904
|
+
return initializeWorkspace(workdir, options);
|
|
3097
2905
|
}
|
|
3098
2906
|
|
|
3099
|
-
// src/
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
readdirSync as readdirSync3,
|
|
3103
|
-
readFileSync as readFileSync6,
|
|
3104
|
-
rmSync as rmSync2,
|
|
3105
|
-
writeFileSync as writeFileSync7
|
|
3106
|
-
} from "fs";
|
|
3107
|
-
import { createHash } from "crypto";
|
|
3108
|
-
import { dirname as dirname5, join as join5, relative as relative2, sep } from "path";
|
|
3109
|
-
var MANIFEST_FILE = "manifest.json";
|
|
3110
|
-
function normalizeProjectIdForPath(projectId) {
|
|
3111
|
-
const id = projectId.trim();
|
|
3112
|
-
if (!id) {
|
|
3113
|
-
throw new Error("projectId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
3114
|
-
}
|
|
3115
|
-
if (id.includes("..") || id.includes("/") || id.includes("\\") || id.includes("\0")) {
|
|
3116
|
-
throw new Error(`\u975E\u6CD5 projectId: ${projectId}`);
|
|
3117
|
-
}
|
|
3118
|
-
return id;
|
|
2907
|
+
// src/commands/connect/pre-step-cache.ts
|
|
2908
|
+
function sessionWorkdirKey(sessionId, workdir) {
|
|
2909
|
+
return `${sessionId}\0${workdir}`;
|
|
3119
2910
|
}
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
return
|
|
2911
|
+
var lastBranchKey = null;
|
|
2912
|
+
function shouldRunBranch(sessionId, workdir) {
|
|
2913
|
+
return lastBranchKey !== sessionWorkdirKey(sessionId, workdir);
|
|
3123
2914
|
}
|
|
3124
|
-
function
|
|
3125
|
-
|
|
3126
|
-
return join5(
|
|
3127
|
-
projectDocumentsDir(apmRoot, projectId),
|
|
3128
|
-
...normalized.split("/")
|
|
3129
|
-
);
|
|
2915
|
+
function markBranchDone(sessionId, workdir) {
|
|
2916
|
+
lastBranchKey = sessionWorkdirKey(sessionId, workdir);
|
|
3130
2917
|
}
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
2918
|
+
|
|
2919
|
+
// src/commands/connect/webide-pull-requests.ts
|
|
2920
|
+
var PRE_AGENT_PR_ACTIONS = /* @__PURE__ */ new Set([
|
|
2921
|
+
"generate-cases",
|
|
2922
|
+
"run-auto-test",
|
|
2923
|
+
// 验收合并前兜底确保 PR 存在
|
|
2924
|
+
"accept"
|
|
2925
|
+
]);
|
|
2926
|
+
var POST_AGENT_PR_ACTIONS = /* @__PURE__ */ new Set(["create-pr", "finish-manual-test"]);
|
|
2927
|
+
function shouldEnsureWebIdePullRequests(action) {
|
|
2928
|
+
return PRE_AGENT_PR_ACTIONS.has(action);
|
|
3141
2929
|
}
|
|
3142
|
-
function
|
|
3143
|
-
return
|
|
2930
|
+
function shouldEnsureWebIdePullRequestsAfterAgent(action) {
|
|
2931
|
+
return POST_AGENT_PR_ACTIONS.has(action);
|
|
3144
2932
|
}
|
|
3145
|
-
function
|
|
3146
|
-
const
|
|
3147
|
-
|
|
3148
|
-
|
|
2933
|
+
function formatWebIdePullRequestSummary(result) {
|
|
2934
|
+
const lines = [];
|
|
2935
|
+
lines.push("## PR \u521B\u5EFA\u7ED3\u679C");
|
|
2936
|
+
lines.push(
|
|
2937
|
+
`\u5408\u8BA1\uFF1A\u6210\u529F ${result.ensured} \u6761\uFF0C\u65E0\u53D8\u66F4\u8DF3\u8FC7 ${result.skippedNoChanges} \u6761\uFF0C\u5176\u4ED6\u8DF3\u8FC7 ${result.skipped} \u6761\uFF0C\u5931\u8D25 ${result.failed} \u6761\u3002`
|
|
3149
2938
|
);
|
|
3150
|
-
if (
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
try {
|
|
3154
|
-
return JSON.parse(
|
|
3155
|
-
readFileSync6(manifestPath3, "utf8")
|
|
3156
|
-
);
|
|
3157
|
-
} catch {
|
|
3158
|
-
return null;
|
|
3159
|
-
}
|
|
3160
|
-
}
|
|
3161
|
-
function listLocalDocumentPaths(apmRoot, projectId) {
|
|
3162
|
-
const root = projectDocumentsDir(apmRoot, projectId);
|
|
3163
|
-
if (!existsSync6(root)) {
|
|
3164
|
-
return [];
|
|
2939
|
+
if (result.items.length === 0) {
|
|
2940
|
+
lines.push("\u672A\u5904\u7406\u4EFB\u4F55\u4ED3\u5E93\u3002");
|
|
2941
|
+
return lines.join("\n");
|
|
3165
2942
|
}
|
|
3166
|
-
|
|
3167
|
-
const
|
|
3168
|
-
|
|
3169
|
-
const
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
const rel = relative2(root, abs).split(sep).join("/");
|
|
3178
|
-
paths.push(rel);
|
|
2943
|
+
lines.push("");
|
|
2944
|
+
for (const item of result.items) {
|
|
2945
|
+
if (item.outcome === "created") {
|
|
2946
|
+
const num = item.number != null && item.number > 0 ? `#${item.number}` : "\uFF08\u65E0\u7F16\u53F7\uFF09";
|
|
2947
|
+
const branch = item.headBranch && item.baseBranch ? `${item.headBranch} \u2192 ${item.baseBranch}` : item.baseBranch ? `\u76EE\u6807\u5206\u652F ${item.baseBranch}` : "";
|
|
2948
|
+
const state = item.state ? `\u72B6\u6001 ${item.state}` : "";
|
|
2949
|
+
const url = item.url?.trim() ? item.url.trim() : "";
|
|
2950
|
+
lines.push(
|
|
2951
|
+
`- **${item.label}**\uFF1A\u5DF2\u521B\u5EFA ${num}${branch ? `\uFF0C${branch}` : ""}${state ? `\uFF0C${state}` : ""}${url ? `\uFF0C${url}` : ""}`
|
|
2952
|
+
);
|
|
2953
|
+
continue;
|
|
3179
2954
|
}
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
const remoteMap = new Map(
|
|
3186
|
-
(remote?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
3187
|
-
);
|
|
3188
|
-
const localMap = new Map(
|
|
3189
|
-
(local?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
3190
|
-
);
|
|
3191
|
-
const download = [];
|
|
3192
|
-
for (const [path, hash] of remoteMap) {
|
|
3193
|
-
if (localMap.get(path) !== hash) {
|
|
3194
|
-
download.push(path);
|
|
2955
|
+
if (item.outcome === "skipped_no_changes") {
|
|
2956
|
+
lines.push(
|
|
2957
|
+
`- **${item.label}**\uFF1A\u76F8\u5BF9\u57FA\u7EBF ${item.baseBranch || "\uFF08\u672A\u77E5\uFF09"} \u65E0\u4EE3\u7801\u53D8\u66F4\uFF0C\u672A\u521B\u5EFA PR`
|
|
2958
|
+
);
|
|
2959
|
+
continue;
|
|
3195
2960
|
}
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
if (!remoteMap.has(path)) {
|
|
3200
|
-
deleteLocal.push(path);
|
|
2961
|
+
if (item.outcome === "skipped") {
|
|
2962
|
+
lines.push(`- **${item.label}**\uFF1A\u5DF2\u8DF3\u8FC7 \u2014 ${item.detail}`);
|
|
2963
|
+
continue;
|
|
3201
2964
|
}
|
|
2965
|
+
lines.push(`- **${item.label}**\uFF1A\u521B\u5EFA\u5931\u8D25 \u2014 ${item.detail}`);
|
|
3202
2966
|
}
|
|
3203
|
-
return
|
|
2967
|
+
return lines.join("\n");
|
|
3204
2968
|
}
|
|
3205
|
-
async function
|
|
3206
|
-
const
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
const projectIds = baseline.projectIds ?? [];
|
|
3217
|
-
if (projectIds.length === 0) {
|
|
3218
|
-
return {
|
|
3219
|
-
projectId: null,
|
|
3220
|
-
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
|
|
3221
|
-
\u8BF7\u5728\u5E73\u53F0\u5C06\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u5230\u552F\u4E00\u9879\u76EE\u3002`
|
|
3222
|
-
};
|
|
3223
|
-
}
|
|
3224
|
-
return {
|
|
3225
|
-
projectId: null,
|
|
3226
|
-
diagnostic: `\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u4E86 ${projectIds.length} \u4E2A\u9879\u76EE\uFF0C\u65E0\u6CD5\u786E\u5B9A\u540C\u6B65\u76EE\u6807\u3002
|
|
3227
|
-
\u8BF7\u786E\u4FDD\u8BE5\u5DE5\u4F5C\u7A7A\u95F4\u53EA\u5173\u8054\u4E00\u4E2A\u9879\u76EE\u540E\u518D\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002`
|
|
3228
|
-
};
|
|
3229
|
-
} catch (err) {
|
|
3230
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
3231
|
-
return {
|
|
3232
|
-
projectId: null,
|
|
3233
|
-
diagnostic: detail.replace(/^\[apm\]\s*/, "")
|
|
3234
|
-
};
|
|
3235
|
-
}
|
|
3236
|
-
}
|
|
3237
|
-
async function syncProjectDocumentsPull(workdirPath, apmDir, options) {
|
|
3238
|
-
const empty = {
|
|
3239
|
-
synced: false,
|
|
3240
|
-
projectId: null,
|
|
3241
|
-
downloaded: 0,
|
|
3242
|
-
deleted: 0
|
|
3243
|
-
};
|
|
3244
|
-
const cfg = await tryReadApmConfig();
|
|
3245
|
-
if (!cfg || !resolveApiKey(cfg)) {
|
|
3246
|
-
console.log(
|
|
3247
|
-
"[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"
|
|
3248
|
-
);
|
|
3249
|
-
return empty;
|
|
3250
|
-
}
|
|
3251
|
-
const api = createApmApiClient(cfg);
|
|
3252
|
-
const { projectId, diagnostic } = await resolveProjectIdForSync(
|
|
3253
|
-
api,
|
|
3254
|
-
workdirPath,
|
|
3255
|
-
options
|
|
3256
|
-
);
|
|
3257
|
-
if (!projectId) {
|
|
3258
|
-
console.log(`[apm] \u672A\u80FD\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002
|
|
3259
|
-
${diagnostic ?? ""}`);
|
|
3260
|
-
return empty;
|
|
3261
|
-
}
|
|
3262
|
-
const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
|
|
3263
|
-
const docsDir = projectDocumentsDir(targetApmDir, projectId);
|
|
3264
|
-
await ensureDirExists(docsDir);
|
|
3265
|
-
const { manifest: remoteManifest } = await api.cli.getProjectDocumentManifest(
|
|
3266
|
-
{ projectId }
|
|
3267
|
-
);
|
|
3268
|
-
if (!remoteManifest) {
|
|
3269
|
-
console.log(`[apm] \u9879\u76EE ${projectId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`);
|
|
3270
|
-
return { ...empty, projectId };
|
|
3271
|
-
}
|
|
3272
|
-
const localManifest = readLocalManifest(targetApmDir, projectId);
|
|
3273
|
-
const { download, deleteLocal } = diffManifestPaths(
|
|
3274
|
-
remoteManifest,
|
|
3275
|
-
localManifest
|
|
3276
|
-
);
|
|
3277
|
-
let downloaded = 0;
|
|
3278
|
-
if (download.length > 0) {
|
|
3279
|
-
const { list } = await api.cli.listProjectDocuments({
|
|
3280
|
-
projectId,
|
|
3281
|
-
paths: download.join(",")
|
|
3282
|
-
});
|
|
3283
|
-
for (const doc of list) {
|
|
3284
|
-
const absPath = toFsPath(
|
|
3285
|
-
projectDocumentLocalPath(targetApmDir, projectId, doc.path)
|
|
3286
|
-
);
|
|
3287
|
-
await ensureDirExists(dirname5(absPath));
|
|
3288
|
-
writeFileSync7(absPath, doc.content, "utf8");
|
|
3289
|
-
downloaded += 1;
|
|
3290
|
-
}
|
|
3291
|
-
}
|
|
3292
|
-
let deleted = 0;
|
|
3293
|
-
for (const path of deleteLocal) {
|
|
3294
|
-
const absPath = toFsPath(
|
|
3295
|
-
projectDocumentLocalPath(targetApmDir, projectId, path)
|
|
3296
|
-
);
|
|
3297
|
-
if (existsSync6(absPath)) {
|
|
3298
|
-
rmSync2(absPath, { force: true });
|
|
3299
|
-
deleted += 1;
|
|
3300
|
-
}
|
|
3301
|
-
}
|
|
3302
|
-
writeFileSync7(
|
|
3303
|
-
toFsPath(join5(docsDir, MANIFEST_FILE)),
|
|
3304
|
-
`${JSON.stringify(remoteManifest, null, 2)}
|
|
3305
|
-
`,
|
|
3306
|
-
"utf8"
|
|
3307
|
-
);
|
|
3308
|
-
console.log(
|
|
3309
|
-
`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: projectId=${projectId} \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted} \u2192 .apm/project/${projectId}/`
|
|
3310
|
-
);
|
|
3311
|
-
return {
|
|
3312
|
-
synced: true,
|
|
3313
|
-
projectId,
|
|
3314
|
-
downloaded,
|
|
3315
|
-
deleted
|
|
3316
|
-
};
|
|
3317
|
-
}
|
|
3318
|
-
async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot, options) {
|
|
3319
|
-
const api = createApmApiClient(cfg);
|
|
3320
|
-
const { projectId } = await resolveProjectIdForSync(
|
|
3321
|
-
api,
|
|
3322
|
-
workdirPath,
|
|
3323
|
-
options
|
|
3324
|
-
);
|
|
3325
|
-
if (!projectId) {
|
|
3326
|
-
return 0;
|
|
3327
|
-
}
|
|
3328
|
-
const targetApmDir = apmRoot ?? workspaceApmDir(workdirPath);
|
|
3329
|
-
const localPaths = listLocalDocumentPaths(targetApmDir, projectId);
|
|
3330
|
-
if (localPaths.length === 0) {
|
|
3331
|
-
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u672C\u5730\u6587\u4EF6\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
3332
|
-
return 0;
|
|
3333
|
-
}
|
|
3334
|
-
const remoteManifest = (await api.cli.getProjectDocumentManifest({ projectId })).manifest ?? null;
|
|
3335
|
-
const remoteHashByPath = new Map(
|
|
3336
|
-
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
3337
|
-
);
|
|
3338
|
-
const remoteDescriptionByPath = new Map(
|
|
3339
|
-
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.description])
|
|
3340
|
-
);
|
|
3341
|
-
let synced = 0;
|
|
3342
|
-
for (const path of localPaths) {
|
|
3343
|
-
const absPath = toFsPath(
|
|
3344
|
-
projectDocumentLocalPath(targetApmDir, projectId, path)
|
|
3345
|
-
);
|
|
3346
|
-
const content = readFileSync6(absPath, "utf8");
|
|
3347
|
-
const contentHash = hashLocalFileContent(content);
|
|
3348
|
-
if (remoteHashByPath.get(path) === contentHash) {
|
|
3349
|
-
continue;
|
|
3350
|
-
}
|
|
3351
|
-
await api.cli.upsertProjectDocument({
|
|
3352
|
-
projectId,
|
|
3353
|
-
path,
|
|
3354
|
-
content,
|
|
3355
|
-
description: remoteDescriptionByPath.get(path) ?? void 0
|
|
3356
|
-
});
|
|
3357
|
-
synced += 1;
|
|
3358
|
-
console.log(`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: ${path}`);
|
|
3359
|
-
}
|
|
3360
|
-
if (synced === 0) {
|
|
3361
|
-
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
3362
|
-
}
|
|
3363
|
-
return synced;
|
|
3364
|
-
}
|
|
3365
|
-
|
|
3366
|
-
// src/commands/init.ts
|
|
3367
|
-
async function ensureWorkspaceInitialized(workdir, options) {
|
|
3368
|
-
if (isWorkspaceApmInitialized(workdir)) {
|
|
3369
|
-
return { didInit: false };
|
|
3370
|
-
}
|
|
3371
|
-
console.log(
|
|
3372
|
-
`[apm] \u5DE5\u4F5C\u76EE\u5F55 ${workdir} \u672A\u68C0\u6D4B\u5230\u5DF2\u521D\u59CB\u5316\u7684 .apm\uFF0C\u6B63\u5728\u81EA\u52A8\u521D\u59CB\u5316\u2026`
|
|
3373
|
-
);
|
|
3374
|
-
await ensureWorkspaceApmDirForInit(workdir);
|
|
3375
|
-
if (ensureApmGitignoredInRepo(workdir)) {
|
|
3376
|
-
console.log("[apm] \u5DF2\u5728 .gitignore \u4E2D\u6DFB\u52A0 **/.apm/**");
|
|
3377
|
-
await commitAndPushGitignore(workdir);
|
|
3378
|
-
}
|
|
3379
|
-
const apmDir = workspaceApmDir(workdir);
|
|
3380
|
-
await copyTemplateFiles(apmDir, workdir);
|
|
3381
|
-
const syncResult = await syncRemoteDeploymentConfig(workdir, apmDir);
|
|
3382
|
-
await syncProjectDocumentsPull(workdir, apmDir);
|
|
3383
|
-
const trimmedName = options?.name?.trim();
|
|
3384
|
-
if (trimmedName) {
|
|
3385
|
-
const apmConfigPath = toFsPath(join6(apmDir, "apm.config.json"));
|
|
3386
|
-
const config = readFileSync7(apmConfigPath, "utf8");
|
|
3387
|
-
const configJson = JSON.parse(config);
|
|
3388
|
-
configJson.name = trimmedName;
|
|
3389
|
-
writeFileSync8(
|
|
3390
|
-
apmConfigPath,
|
|
3391
|
-
`${JSON.stringify(configJson, null, 2)}
|
|
3392
|
-
`,
|
|
3393
|
-
"utf8"
|
|
3394
|
-
);
|
|
3395
|
-
}
|
|
3396
|
-
console.log(`[apm] \u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A\uFF1A${apmDir}`);
|
|
3397
|
-
return { didInit: true, syncResult };
|
|
3398
|
-
}
|
|
3399
|
-
|
|
3400
|
-
// src/commands/connect/pre-step-cache.ts
|
|
3401
|
-
function sessionWorkdirKey(sessionId, workdir) {
|
|
3402
|
-
return `${sessionId}\0${workdir}`;
|
|
3403
|
-
}
|
|
3404
|
-
var lastBranchKey = null;
|
|
3405
|
-
function shouldRunBranch(sessionId, workdir) {
|
|
3406
|
-
return lastBranchKey !== sessionWorkdirKey(sessionId, workdir);
|
|
3407
|
-
}
|
|
3408
|
-
function markBranchDone(sessionId, workdir) {
|
|
3409
|
-
lastBranchKey = sessionWorkdirKey(sessionId, workdir);
|
|
3410
|
-
}
|
|
3411
|
-
|
|
3412
|
-
// src/commands/connect/webide-pull-requests.ts
|
|
3413
|
-
var PRE_AGENT_PR_ACTIONS = /* @__PURE__ */ new Set([
|
|
3414
|
-
"generate-cases",
|
|
3415
|
-
"run-auto-test",
|
|
3416
|
-
// 验收合并前兜底确保 PR 存在
|
|
3417
|
-
"accept"
|
|
3418
|
-
]);
|
|
3419
|
-
var POST_AGENT_PR_ACTIONS = /* @__PURE__ */ new Set(["create-pr", "finish-manual-test"]);
|
|
3420
|
-
function shouldEnsureWebIdePullRequests(action) {
|
|
3421
|
-
return PRE_AGENT_PR_ACTIONS.has(action);
|
|
3422
|
-
}
|
|
3423
|
-
function shouldEnsureWebIdePullRequestsAfterAgent(action) {
|
|
3424
|
-
return POST_AGENT_PR_ACTIONS.has(action);
|
|
3425
|
-
}
|
|
3426
|
-
function formatWebIdePullRequestSummary(result) {
|
|
3427
|
-
const lines = [];
|
|
3428
|
-
lines.push("## PR \u521B\u5EFA\u7ED3\u679C");
|
|
3429
|
-
lines.push(
|
|
3430
|
-
`\u5408\u8BA1\uFF1A\u6210\u529F ${result.ensured} \u6761\uFF0C\u65E0\u53D8\u66F4\u8DF3\u8FC7 ${result.skippedNoChanges} \u6761\uFF0C\u5176\u4ED6\u8DF3\u8FC7 ${result.skipped} \u6761\uFF0C\u5931\u8D25 ${result.failed} \u6761\u3002`
|
|
3431
|
-
);
|
|
3432
|
-
if (result.items.length === 0) {
|
|
3433
|
-
lines.push("\u672A\u5904\u7406\u4EFB\u4F55\u4ED3\u5E93\u3002");
|
|
3434
|
-
return lines.join("\n");
|
|
3435
|
-
}
|
|
3436
|
-
lines.push("");
|
|
3437
|
-
for (const item of result.items) {
|
|
3438
|
-
if (item.outcome === "created") {
|
|
3439
|
-
const num = item.number != null && item.number > 0 ? `#${item.number}` : "\uFF08\u65E0\u7F16\u53F7\uFF09";
|
|
3440
|
-
const branch = item.headBranch && item.baseBranch ? `${item.headBranch} \u2192 ${item.baseBranch}` : item.baseBranch ? `\u76EE\u6807\u5206\u652F ${item.baseBranch}` : "";
|
|
3441
|
-
const state = item.state ? `\u72B6\u6001 ${item.state}` : "";
|
|
3442
|
-
const url = item.url?.trim() ? item.url.trim() : "";
|
|
3443
|
-
lines.push(
|
|
3444
|
-
`- **${item.label}**\uFF1A\u5DF2\u521B\u5EFA ${num}${branch ? `\uFF0C${branch}` : ""}${state ? `\uFF0C${state}` : ""}${url ? `\uFF0C${url}` : ""}`
|
|
3445
|
-
);
|
|
3446
|
-
continue;
|
|
3447
|
-
}
|
|
3448
|
-
if (item.outcome === "skipped_no_changes") {
|
|
3449
|
-
lines.push(
|
|
3450
|
-
`- **${item.label}**\uFF1A\u76F8\u5BF9\u57FA\u7EBF ${item.baseBranch || "\uFF08\u672A\u77E5\uFF09"} \u65E0\u4EE3\u7801\u53D8\u66F4\uFF0C\u672A\u521B\u5EFA PR`
|
|
3451
|
-
);
|
|
3452
|
-
continue;
|
|
3453
|
-
}
|
|
3454
|
-
if (item.outcome === "skipped") {
|
|
3455
|
-
lines.push(`- **${item.label}**\uFF1A\u5DF2\u8DF3\u8FC7 \u2014 ${item.detail}`);
|
|
3456
|
-
continue;
|
|
3457
|
-
}
|
|
3458
|
-
lines.push(`- **${item.label}**\uFF1A\u521B\u5EFA\u5931\u8D25 \u2014 ${item.detail}`);
|
|
3459
|
-
}
|
|
3460
|
-
return lines.join("\n");
|
|
3461
|
-
}
|
|
3462
|
-
async function ensureWebIdePullRequests(cfg, taskId, workdir) {
|
|
3463
|
-
const api = createApmApiClient(cfg);
|
|
3464
|
-
const empty = () => ({
|
|
3465
|
-
ensured: 0,
|
|
3466
|
-
skipped: 0,
|
|
3467
|
-
skippedNoChanges: 0,
|
|
3468
|
-
failed: 0,
|
|
3469
|
-
items: [],
|
|
3470
|
-
tips: []
|
|
3471
|
-
});
|
|
3472
|
-
let repoRoots;
|
|
2969
|
+
async function ensureWebIdePullRequests(cfg, taskId, workdir) {
|
|
2970
|
+
const api = createApmApiClient(cfg);
|
|
2971
|
+
const empty = () => ({
|
|
2972
|
+
ensured: 0,
|
|
2973
|
+
skipped: 0,
|
|
2974
|
+
skippedNoChanges: 0,
|
|
2975
|
+
failed: 0,
|
|
2976
|
+
items: [],
|
|
2977
|
+
tips: []
|
|
2978
|
+
});
|
|
2979
|
+
let repoRoots;
|
|
3473
2980
|
try {
|
|
3474
2981
|
const manifest = loadWorkspaceReposCache(workdir);
|
|
3475
2982
|
repoRoots = resolveWorkspaceRepoAbsolutePaths(manifest);
|
|
@@ -3646,14 +3153,14 @@ async function ensureWebIdePullRequests(cfg, taskId, workdir) {
|
|
|
3646
3153
|
}
|
|
3647
3154
|
|
|
3648
3155
|
// src/version.ts
|
|
3649
|
-
import { readFileSync as
|
|
3650
|
-
import { dirname as
|
|
3156
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
3157
|
+
import { dirname as dirname5, join as join5 } from "path";
|
|
3651
3158
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3652
3159
|
function readCliVersion() {
|
|
3653
3160
|
try {
|
|
3654
|
-
const dir =
|
|
3655
|
-
const pkgPath =
|
|
3656
|
-
const pkg = JSON.parse(
|
|
3161
|
+
const dir = dirname5(fileURLToPath2(import.meta.url));
|
|
3162
|
+
const pkgPath = join5(dir, "..", "package.json");
|
|
3163
|
+
const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
|
|
3657
3164
|
return pkg.version ?? "0.0.0";
|
|
3658
3165
|
} catch {
|
|
3659
3166
|
return "0.0.0";
|
|
@@ -3661,46 +3168,46 @@ function readCliVersion() {
|
|
|
3661
3168
|
}
|
|
3662
3169
|
|
|
3663
3170
|
// src/commands/update-skills.ts
|
|
3664
|
-
import { existsSync as
|
|
3665
|
-
import { join as
|
|
3171
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync7, statSync as statSync4 } from "fs";
|
|
3172
|
+
import { join as join7 } from "path";
|
|
3666
3173
|
|
|
3667
3174
|
// src/skills-sync.ts
|
|
3668
3175
|
import {
|
|
3669
3176
|
copyFileSync as copyFileSync2,
|
|
3670
3177
|
cpSync,
|
|
3671
|
-
existsSync as
|
|
3178
|
+
existsSync as existsSync6,
|
|
3672
3179
|
mkdirSync as mkdirSync6,
|
|
3673
|
-
readdirSync as
|
|
3674
|
-
rmSync as
|
|
3180
|
+
readdirSync as readdirSync3,
|
|
3181
|
+
rmSync as rmSync2,
|
|
3675
3182
|
statSync as statSync3,
|
|
3676
|
-
writeFileSync as
|
|
3183
|
+
writeFileSync as writeFileSync6
|
|
3677
3184
|
} from "fs";
|
|
3678
|
-
import { join as
|
|
3679
|
-
var AGENTS_TEMPLATE_PATH =
|
|
3680
|
-
var BASE_SKILLS_TEMPLATE_DIR =
|
|
3681
|
-
var BASE_RULES_TEMPLATE_DIR =
|
|
3185
|
+
import { join as join6 } from "path";
|
|
3186
|
+
var AGENTS_TEMPLATE_PATH = join6(CLI_TEMPLATE_DIR, "AGENTS.md");
|
|
3187
|
+
var BASE_SKILLS_TEMPLATE_DIR = join6(CLI_TEMPLATE_DIR, "skills");
|
|
3188
|
+
var BASE_RULES_TEMPLATE_DIR = join6(CLI_TEMPLATE_DIR, "rules");
|
|
3682
3189
|
function sanitizeSkillDirName(name) {
|
|
3683
3190
|
const trimmed = name.trim();
|
|
3684
3191
|
if (!trimmed) return "skill";
|
|
3685
3192
|
return trimmed.replace(/[/\\:*?"<>|]/g, "_");
|
|
3686
3193
|
}
|
|
3687
3194
|
function listBaseSkillDirNames() {
|
|
3688
|
-
if (!
|
|
3689
|
-
return
|
|
3690
|
-
const path =
|
|
3195
|
+
if (!existsSync6(BASE_SKILLS_TEMPLATE_DIR)) return [];
|
|
3196
|
+
return readdirSync3(BASE_SKILLS_TEMPLATE_DIR).filter((name) => {
|
|
3197
|
+
const path = join6(BASE_SKILLS_TEMPLATE_DIR, name);
|
|
3691
3198
|
return statSync3(path).isDirectory();
|
|
3692
3199
|
});
|
|
3693
3200
|
}
|
|
3694
3201
|
function syncAgentsGuide(apmDir) {
|
|
3695
|
-
if (!
|
|
3202
|
+
if (!existsSync6(AGENTS_TEMPLATE_PATH)) return false;
|
|
3696
3203
|
mkdirSync6(apmDir, { recursive: true });
|
|
3697
|
-
copyFileSync2(AGENTS_TEMPLATE_PATH,
|
|
3204
|
+
copyFileSync2(AGENTS_TEMPLATE_PATH, join6(apmDir, "AGENTS.md"));
|
|
3698
3205
|
return true;
|
|
3699
3206
|
}
|
|
3700
3207
|
function listBaseRuleFileNames() {
|
|
3701
|
-
if (!
|
|
3702
|
-
return
|
|
3703
|
-
const path =
|
|
3208
|
+
if (!existsSync6(BASE_RULES_TEMPLATE_DIR)) return [];
|
|
3209
|
+
return readdirSync3(BASE_RULES_TEMPLATE_DIR).filter((name) => {
|
|
3210
|
+
const path = join6(BASE_RULES_TEMPLATE_DIR, name);
|
|
3704
3211
|
return statSync3(path).isFile();
|
|
3705
3212
|
});
|
|
3706
3213
|
}
|
|
@@ -3708,8 +3215,8 @@ function syncBaseRules(rulesDir) {
|
|
|
3708
3215
|
mkdirSync6(rulesDir, { recursive: true });
|
|
3709
3216
|
const names = listBaseRuleFileNames();
|
|
3710
3217
|
for (const name of names) {
|
|
3711
|
-
const src =
|
|
3712
|
-
const dest =
|
|
3218
|
+
const src = join6(BASE_RULES_TEMPLATE_DIR, name);
|
|
3219
|
+
const dest = join6(rulesDir, name);
|
|
3713
3220
|
copyFileSync2(src, dest);
|
|
3714
3221
|
}
|
|
3715
3222
|
return names;
|
|
@@ -3718,8 +3225,8 @@ function syncBaseSkills(skillsDir) {
|
|
|
3718
3225
|
mkdirSync6(skillsDir, { recursive: true });
|
|
3719
3226
|
const names = listBaseSkillDirNames();
|
|
3720
3227
|
for (const name of names) {
|
|
3721
|
-
const src =
|
|
3722
|
-
const dest =
|
|
3228
|
+
const src = join6(BASE_SKILLS_TEMPLATE_DIR, name);
|
|
3229
|
+
const dest = join6(skillsDir, name);
|
|
3723
3230
|
cpSync(src, dest, { recursive: true, force: true });
|
|
3724
3231
|
}
|
|
3725
3232
|
return names;
|
|
@@ -3736,19 +3243,19 @@ function syncSupplementarySkills(skillsDir, list) {
|
|
|
3736
3243
|
skipped.push(dirName);
|
|
3737
3244
|
continue;
|
|
3738
3245
|
}
|
|
3739
|
-
const skillDir =
|
|
3246
|
+
const skillDir = join6(skillsDir, dirName);
|
|
3740
3247
|
mkdirSync6(skillDir, { recursive: true });
|
|
3741
|
-
|
|
3248
|
+
writeFileSync6(join6(skillDir, "SKILL.md"), skill.content ?? "", "utf8");
|
|
3742
3249
|
written.push(dirName);
|
|
3743
3250
|
}
|
|
3744
3251
|
const removed = [];
|
|
3745
|
-
if (!
|
|
3746
|
-
for (const entry of
|
|
3747
|
-
const full =
|
|
3252
|
+
if (!existsSync6(skillsDir)) return { written, skipped, removed };
|
|
3253
|
+
for (const entry of readdirSync3(skillsDir)) {
|
|
3254
|
+
const full = join6(skillsDir, entry);
|
|
3748
3255
|
if (!statSync3(full).isDirectory()) continue;
|
|
3749
3256
|
if (baseNames.has(entry)) continue;
|
|
3750
3257
|
if (apiDirNames.has(entry)) continue;
|
|
3751
|
-
|
|
3258
|
+
rmSync2(full, { recursive: true, force: true });
|
|
3752
3259
|
removed.push(entry);
|
|
3753
3260
|
}
|
|
3754
3261
|
return { written, skipped, removed };
|
|
@@ -3758,7 +3265,7 @@ function syncSupplementarySkills(skillsDir, list) {
|
|
|
3758
3265
|
async function syncWorkspaceSkills(cfg, workdir) {
|
|
3759
3266
|
const apmDir = workspaceApmDir(workdir);
|
|
3760
3267
|
const fsApmDir = toFsPath(apmDir);
|
|
3761
|
-
if (!
|
|
3268
|
+
if (!existsSync7(fsApmDir)) {
|
|
3762
3269
|
throw new Error("[apm] \u672A\u627E\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5148\u6267\u884C apm init");
|
|
3763
3270
|
}
|
|
3764
3271
|
const apmStat = statSync4(fsApmDir);
|
|
@@ -3770,12 +3277,12 @@ async function syncWorkspaceSkills(cfg, workdir) {
|
|
|
3770
3277
|
if (syncAgentsGuide(apmDir)) {
|
|
3771
3278
|
console.log("[apm] \u5DF2\u540C\u6B65 APM \u6307\u5357: .apm/AGENTS.md");
|
|
3772
3279
|
}
|
|
3773
|
-
const rulesDir =
|
|
3280
|
+
const rulesDir = join7(apmDir, "rules");
|
|
3774
3281
|
const ruleNames = syncBaseRules(rulesDir);
|
|
3775
3282
|
for (const name of ruleNames) {
|
|
3776
3283
|
console.log(`[apm] \u5DF2\u540C\u6B65\u57FA\u7840\u89C4\u5219: rules/${name}`);
|
|
3777
3284
|
}
|
|
3778
|
-
const skillsDir =
|
|
3285
|
+
const skillsDir = join7(apmDir, "skills");
|
|
3779
3286
|
mkdirSync7(toFsPath(skillsDir), { recursive: true });
|
|
3780
3287
|
const baseNames = syncBaseSkills(skillsDir);
|
|
3781
3288
|
for (const name of baseNames) {
|
|
@@ -3802,9 +3309,9 @@ async function syncWorkspaceSkills(cfg, workdir) {
|
|
|
3802
3309
|
}
|
|
3803
3310
|
|
|
3804
3311
|
// src/commands/sync-session-attachments.ts
|
|
3805
|
-
import { existsSync as
|
|
3806
|
-
import { join as
|
|
3807
|
-
var
|
|
3312
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync7 } from "fs";
|
|
3313
|
+
import { join as join8 } from "path";
|
|
3314
|
+
var MANIFEST_FILE = ".sync-manifest.json";
|
|
3808
3315
|
async function downloadAttachment(cfg, attachmentId) {
|
|
3809
3316
|
const base = cfg.baseUrl.trim().replace(/\/+$/, "");
|
|
3810
3317
|
const url = `${base}/api/v1/tasks/attachments/file?${new URLSearchParams({
|
|
@@ -3819,13 +3326,13 @@ async function downloadAttachment(cfg, attachmentId) {
|
|
|
3819
3326
|
return Buffer.from(await res.arrayBuffer());
|
|
3820
3327
|
}
|
|
3821
3328
|
function loadManifest(dir) {
|
|
3822
|
-
const path =
|
|
3823
|
-
if (!
|
|
3329
|
+
const path = join8(dir, MANIFEST_FILE);
|
|
3330
|
+
if (!existsSync8(path)) {
|
|
3824
3331
|
return { version: 1, attachments: {} };
|
|
3825
3332
|
}
|
|
3826
3333
|
try {
|
|
3827
3334
|
const parsed = JSON.parse(
|
|
3828
|
-
|
|
3335
|
+
readFileSync7(path, "utf8")
|
|
3829
3336
|
);
|
|
3830
3337
|
if (parsed?.version === 1 && parsed.attachments && typeof parsed.attachments === "object") {
|
|
3831
3338
|
return parsed;
|
|
@@ -3835,15 +3342,15 @@ function loadManifest(dir) {
|
|
|
3835
3342
|
return { version: 1, attachments: {} };
|
|
3836
3343
|
}
|
|
3837
3344
|
function saveManifest(dir, manifest) {
|
|
3838
|
-
|
|
3839
|
-
|
|
3345
|
+
writeFileSync7(
|
|
3346
|
+
join8(dir, MANIFEST_FILE),
|
|
3840
3347
|
`${JSON.stringify(manifest, null, 2)}
|
|
3841
3348
|
`,
|
|
3842
3349
|
"utf8"
|
|
3843
3350
|
);
|
|
3844
3351
|
}
|
|
3845
3352
|
function isAttachmentUpToDate(entry, item, dest) {
|
|
3846
|
-
if (!entry || !
|
|
3353
|
+
if (!entry || !existsSync8(dest)) return false;
|
|
3847
3354
|
if (entry.name !== item.name) return false;
|
|
3848
3355
|
const createdAt = item.createdAt ?? "";
|
|
3849
3356
|
return entry.createdAt === createdAt;
|
|
@@ -3858,7 +3365,7 @@ async function syncAttachmentsToDirectory(cfg, attachments, dir, logLabel) {
|
|
|
3858
3365
|
const nextManifest = { version: 1, attachments: {} };
|
|
3859
3366
|
const names = [];
|
|
3860
3367
|
for (const item of attachments) {
|
|
3861
|
-
const dest =
|
|
3368
|
+
const dest = join8(dir, item.name);
|
|
3862
3369
|
const entry = manifest.attachments[item.id];
|
|
3863
3370
|
const createdAt = item.createdAt ?? "";
|
|
3864
3371
|
names.push(item.name);
|
|
@@ -3868,7 +3375,7 @@ async function syncAttachmentsToDirectory(cfg, attachments, dir, logLabel) {
|
|
|
3868
3375
|
continue;
|
|
3869
3376
|
}
|
|
3870
3377
|
const buffer = await downloadAttachment(cfg, item.id);
|
|
3871
|
-
|
|
3378
|
+
writeFileSync7(dest, buffer);
|
|
3872
3379
|
nextManifest.attachments[item.id] = {
|
|
3873
3380
|
name: item.name,
|
|
3874
3381
|
createdAt
|
|
@@ -3888,12 +3395,279 @@ async function syncWebIdeAttachments(cfg, taskId, workdir, attachments) {
|
|
|
3888
3395
|
);
|
|
3889
3396
|
}
|
|
3890
3397
|
|
|
3398
|
+
// src/project-documents-sync.ts
|
|
3399
|
+
import {
|
|
3400
|
+
existsSync as existsSync9,
|
|
3401
|
+
readdirSync as readdirSync4,
|
|
3402
|
+
readFileSync as readFileSync8,
|
|
3403
|
+
rmSync as rmSync3,
|
|
3404
|
+
writeFileSync as writeFileSync8
|
|
3405
|
+
} from "fs";
|
|
3406
|
+
import { createHash } from "crypto";
|
|
3407
|
+
import { dirname as dirname6, join as join9, relative as relative2, sep } from "path";
|
|
3408
|
+
var MANIFEST_FILE2 = "manifest.json";
|
|
3409
|
+
function normalizeProjectIdForPath(projectId) {
|
|
3410
|
+
const id = projectId.trim();
|
|
3411
|
+
if (!id) {
|
|
3412
|
+
throw new Error("projectId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
3413
|
+
}
|
|
3414
|
+
if (id.includes("..") || id.includes("/") || id.includes("\\") || id.includes("\0")) {
|
|
3415
|
+
throw new Error(`\u975E\u6CD5 projectId: ${projectId}`);
|
|
3416
|
+
}
|
|
3417
|
+
return id;
|
|
3418
|
+
}
|
|
3419
|
+
function projectDocumentsDir(apmRoot, projectId) {
|
|
3420
|
+
const id = normalizeProjectIdForPath(projectId);
|
|
3421
|
+
return join9(apmRoot ?? workspaceApmDir(), "project", id);
|
|
3422
|
+
}
|
|
3423
|
+
function projectDocumentLocalPath(apmRoot, projectId, documentPath) {
|
|
3424
|
+
const normalized = normalizeLocalDocumentPath(documentPath);
|
|
3425
|
+
return join9(
|
|
3426
|
+
projectDocumentsDir(apmRoot, projectId),
|
|
3427
|
+
...normalized.split("/")
|
|
3428
|
+
);
|
|
3429
|
+
}
|
|
3430
|
+
function normalizeLocalDocumentPath(path) {
|
|
3431
|
+
const trimmed = path.trim().replace(/\\/g, "/");
|
|
3432
|
+
if (!trimmed || trimmed.startsWith("/") || /^[a-zA-Z]:/.test(trimmed)) {
|
|
3433
|
+
throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path}`);
|
|
3434
|
+
}
|
|
3435
|
+
const segments = trimmed.split("/").filter(Boolean);
|
|
3436
|
+
if (segments.some((segment) => segment === ".." || segment === ".")) {
|
|
3437
|
+
throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path}`);
|
|
3438
|
+
}
|
|
3439
|
+
return segments.join("/");
|
|
3440
|
+
}
|
|
3441
|
+
function hashLocalFileContent(content) {
|
|
3442
|
+
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
3443
|
+
}
|
|
3444
|
+
function readLocalManifest(apmRoot, projectId) {
|
|
3445
|
+
const manifestPath3 = join9(
|
|
3446
|
+
projectDocumentsDir(apmRoot, projectId),
|
|
3447
|
+
MANIFEST_FILE2
|
|
3448
|
+
);
|
|
3449
|
+
if (!existsSync9(manifestPath3)) {
|
|
3450
|
+
return null;
|
|
3451
|
+
}
|
|
3452
|
+
try {
|
|
3453
|
+
return JSON.parse(
|
|
3454
|
+
readFileSync8(manifestPath3, "utf8")
|
|
3455
|
+
);
|
|
3456
|
+
} catch {
|
|
3457
|
+
return null;
|
|
3458
|
+
}
|
|
3459
|
+
}
|
|
3460
|
+
function listLocalDocumentPaths(apmRoot, projectId) {
|
|
3461
|
+
const root = projectDocumentsDir(apmRoot, projectId);
|
|
3462
|
+
if (!existsSync9(root)) {
|
|
3463
|
+
return [];
|
|
3464
|
+
}
|
|
3465
|
+
const paths = [];
|
|
3466
|
+
const walk = (dir) => {
|
|
3467
|
+
for (const entry of readdirSync4(dir, { withFileTypes: true })) {
|
|
3468
|
+
const abs = join9(dir, entry.name);
|
|
3469
|
+
if (entry.isDirectory()) {
|
|
3470
|
+
walk(abs);
|
|
3471
|
+
continue;
|
|
3472
|
+
}
|
|
3473
|
+
if (entry.isFile() && entry.name === MANIFEST_FILE2) {
|
|
3474
|
+
continue;
|
|
3475
|
+
}
|
|
3476
|
+
const rel = relative2(root, abs).split(sep).join("/");
|
|
3477
|
+
paths.push(rel);
|
|
3478
|
+
}
|
|
3479
|
+
};
|
|
3480
|
+
walk(root);
|
|
3481
|
+
return paths.sort();
|
|
3482
|
+
}
|
|
3483
|
+
function diffManifestPaths(remote, local) {
|
|
3484
|
+
const remoteMap = new Map(
|
|
3485
|
+
(remote?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
3486
|
+
);
|
|
3487
|
+
const localMap = new Map(
|
|
3488
|
+
(local?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
3489
|
+
);
|
|
3490
|
+
const download = [];
|
|
3491
|
+
for (const [path, hash] of remoteMap) {
|
|
3492
|
+
if (localMap.get(path) !== hash) {
|
|
3493
|
+
download.push(path);
|
|
3494
|
+
}
|
|
3495
|
+
}
|
|
3496
|
+
const deleteLocal = [];
|
|
3497
|
+
for (const path of localMap.keys()) {
|
|
3498
|
+
if (!remoteMap.has(path)) {
|
|
3499
|
+
deleteLocal.push(path);
|
|
3500
|
+
}
|
|
3501
|
+
}
|
|
3502
|
+
return { download, deleteLocal };
|
|
3503
|
+
}
|
|
3504
|
+
async function resolveProjectIdForSync(api, workdirPath, options) {
|
|
3505
|
+
const explicit = options?.projectId?.trim();
|
|
3506
|
+
if (explicit) {
|
|
3507
|
+
return { projectId: explicit, diagnostic: null };
|
|
3508
|
+
}
|
|
3509
|
+
try {
|
|
3510
|
+
const baseline = await api.cli.workspaceBaseline({ workdirPath });
|
|
3511
|
+
const projectId = baseline.projectId?.trim() || null;
|
|
3512
|
+
if (projectId) {
|
|
3513
|
+
return { projectId, diagnostic: null };
|
|
3514
|
+
}
|
|
3515
|
+
const projectIds = baseline.projectIds ?? [];
|
|
3516
|
+
if (projectIds.length === 0) {
|
|
3517
|
+
return {
|
|
3518
|
+
projectId: null,
|
|
3519
|
+
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
|
|
3520
|
+
\u8BF7\u5728\u5E73\u53F0\u5C06\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u5230\u552F\u4E00\u9879\u76EE\u3002`
|
|
3521
|
+
};
|
|
3522
|
+
}
|
|
3523
|
+
return {
|
|
3524
|
+
projectId: null,
|
|
3525
|
+
diagnostic: `\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u4E86 ${projectIds.length} \u4E2A\u9879\u76EE\uFF0C\u65E0\u6CD5\u786E\u5B9A\u540C\u6B65\u76EE\u6807\u3002
|
|
3526
|
+
\u8BF7\u786E\u4FDD\u8BE5\u5DE5\u4F5C\u7A7A\u95F4\u53EA\u5173\u8054\u4E00\u4E2A\u9879\u76EE\u540E\u518D\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002`
|
|
3527
|
+
};
|
|
3528
|
+
} catch (err) {
|
|
3529
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
3530
|
+
return {
|
|
3531
|
+
projectId: null,
|
|
3532
|
+
diagnostic: detail.replace(/^\[apm\]\s*/, "")
|
|
3533
|
+
};
|
|
3534
|
+
}
|
|
3535
|
+
}
|
|
3536
|
+
async function syncProjectDocumentsPull(workdirPath, apmDir, options) {
|
|
3537
|
+
const empty = {
|
|
3538
|
+
synced: false,
|
|
3539
|
+
projectId: null,
|
|
3540
|
+
downloaded: 0,
|
|
3541
|
+
deleted: 0
|
|
3542
|
+
};
|
|
3543
|
+
const cfg = await tryReadApmConfig();
|
|
3544
|
+
if (!cfg || !resolveApiKey(cfg)) {
|
|
3545
|
+
console.log(
|
|
3546
|
+
"[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"
|
|
3547
|
+
);
|
|
3548
|
+
return empty;
|
|
3549
|
+
}
|
|
3550
|
+
const api = createApmApiClient(cfg);
|
|
3551
|
+
const { projectId, diagnostic } = await resolveProjectIdForSync(
|
|
3552
|
+
api,
|
|
3553
|
+
workdirPath,
|
|
3554
|
+
options
|
|
3555
|
+
);
|
|
3556
|
+
if (!projectId) {
|
|
3557
|
+
console.log(`[apm] \u672A\u80FD\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002
|
|
3558
|
+
${diagnostic ?? ""}`);
|
|
3559
|
+
return empty;
|
|
3560
|
+
}
|
|
3561
|
+
const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
|
|
3562
|
+
const docsDir = projectDocumentsDir(targetApmDir, projectId);
|
|
3563
|
+
await ensureDirExists(docsDir);
|
|
3564
|
+
const { manifest: remoteManifest } = await api.cli.getProjectDocumentManifest(
|
|
3565
|
+
{ projectId }
|
|
3566
|
+
);
|
|
3567
|
+
if (!remoteManifest) {
|
|
3568
|
+
console.log(`[apm] \u9879\u76EE ${projectId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`);
|
|
3569
|
+
return { ...empty, projectId };
|
|
3570
|
+
}
|
|
3571
|
+
const localManifest = readLocalManifest(targetApmDir, projectId);
|
|
3572
|
+
const { download, deleteLocal } = diffManifestPaths(
|
|
3573
|
+
remoteManifest,
|
|
3574
|
+
localManifest
|
|
3575
|
+
);
|
|
3576
|
+
let downloaded = 0;
|
|
3577
|
+
if (download.length > 0) {
|
|
3578
|
+
const { list } = await api.cli.listProjectDocuments({
|
|
3579
|
+
projectId,
|
|
3580
|
+
paths: download.join(",")
|
|
3581
|
+
});
|
|
3582
|
+
for (const doc of list) {
|
|
3583
|
+
const absPath = toFsPath(
|
|
3584
|
+
projectDocumentLocalPath(targetApmDir, projectId, doc.path)
|
|
3585
|
+
);
|
|
3586
|
+
await ensureDirExists(dirname6(absPath));
|
|
3587
|
+
writeFileSync8(absPath, doc.content, "utf8");
|
|
3588
|
+
downloaded += 1;
|
|
3589
|
+
}
|
|
3590
|
+
}
|
|
3591
|
+
let deleted = 0;
|
|
3592
|
+
for (const path of deleteLocal) {
|
|
3593
|
+
const absPath = toFsPath(
|
|
3594
|
+
projectDocumentLocalPath(targetApmDir, projectId, path)
|
|
3595
|
+
);
|
|
3596
|
+
if (existsSync9(absPath)) {
|
|
3597
|
+
rmSync3(absPath, { force: true });
|
|
3598
|
+
deleted += 1;
|
|
3599
|
+
}
|
|
3600
|
+
}
|
|
3601
|
+
writeFileSync8(
|
|
3602
|
+
toFsPath(join9(docsDir, MANIFEST_FILE2)),
|
|
3603
|
+
`${JSON.stringify(remoteManifest, null, 2)}
|
|
3604
|
+
`,
|
|
3605
|
+
"utf8"
|
|
3606
|
+
);
|
|
3607
|
+
console.log(
|
|
3608
|
+
`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: projectId=${projectId} \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted} \u2192 .apm/project/${projectId}/`
|
|
3609
|
+
);
|
|
3610
|
+
return {
|
|
3611
|
+
synced: true,
|
|
3612
|
+
projectId,
|
|
3613
|
+
downloaded,
|
|
3614
|
+
deleted
|
|
3615
|
+
};
|
|
3616
|
+
}
|
|
3617
|
+
async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot, options) {
|
|
3618
|
+
const api = createApmApiClient(cfg);
|
|
3619
|
+
const { projectId } = await resolveProjectIdForSync(
|
|
3620
|
+
api,
|
|
3621
|
+
workdirPath,
|
|
3622
|
+
options
|
|
3623
|
+
);
|
|
3624
|
+
if (!projectId) {
|
|
3625
|
+
return 0;
|
|
3626
|
+
}
|
|
3627
|
+
const targetApmDir = apmRoot ?? workspaceApmDir(workdirPath);
|
|
3628
|
+
const localPaths = listLocalDocumentPaths(targetApmDir, projectId);
|
|
3629
|
+
if (localPaths.length === 0) {
|
|
3630
|
+
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u672C\u5730\u6587\u4EF6\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
3631
|
+
return 0;
|
|
3632
|
+
}
|
|
3633
|
+
const remoteManifest = (await api.cli.getProjectDocumentManifest({ projectId })).manifest ?? null;
|
|
3634
|
+
const remoteHashByPath = new Map(
|
|
3635
|
+
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
3636
|
+
);
|
|
3637
|
+
const remoteDescriptionByPath = new Map(
|
|
3638
|
+
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.description])
|
|
3639
|
+
);
|
|
3640
|
+
let synced = 0;
|
|
3641
|
+
for (const path of localPaths) {
|
|
3642
|
+
const absPath = toFsPath(
|
|
3643
|
+
projectDocumentLocalPath(targetApmDir, projectId, path)
|
|
3644
|
+
);
|
|
3645
|
+
const content = readFileSync8(absPath, "utf8");
|
|
3646
|
+
const contentHash = hashLocalFileContent(content);
|
|
3647
|
+
if (remoteHashByPath.get(path) === contentHash) {
|
|
3648
|
+
continue;
|
|
3649
|
+
}
|
|
3650
|
+
await api.cli.upsertProjectDocument({
|
|
3651
|
+
projectId,
|
|
3652
|
+
path,
|
|
3653
|
+
content,
|
|
3654
|
+
description: remoteDescriptionByPath.get(path) ?? void 0
|
|
3655
|
+
});
|
|
3656
|
+
synced += 1;
|
|
3657
|
+
console.log(`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: ${path}`);
|
|
3658
|
+
}
|
|
3659
|
+
if (synced === 0) {
|
|
3660
|
+
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
3661
|
+
}
|
|
3662
|
+
return synced;
|
|
3663
|
+
}
|
|
3664
|
+
|
|
3891
3665
|
// src/commands/connect/cli-version-sync.ts
|
|
3892
|
-
import { existsSync as existsSync10, readFileSync as
|
|
3893
|
-
import { join as
|
|
3666
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
3667
|
+
import { join as join10 } from "path";
|
|
3894
3668
|
var CLI_VERSION_FILE = ".cli-version.json";
|
|
3895
3669
|
function manifestPath2(apmDir) {
|
|
3896
|
-
return
|
|
3670
|
+
return join10(apmDir, CLI_VERSION_FILE);
|
|
3897
3671
|
}
|
|
3898
3672
|
function loadManifest2(apmDir) {
|
|
3899
3673
|
const path = toFsPath(manifestPath2(apmDir));
|
|
@@ -3902,7 +3676,7 @@ function loadManifest2(apmDir) {
|
|
|
3902
3676
|
}
|
|
3903
3677
|
try {
|
|
3904
3678
|
const parsed = JSON.parse(
|
|
3905
|
-
|
|
3679
|
+
readFileSync9(path, "utf8")
|
|
3906
3680
|
);
|
|
3907
3681
|
if (parsed?.version === 1 && typeof parsed.cliVersion === "string" && parsed.cliVersion.trim()) {
|
|
3908
3682
|
return parsed;
|
|
@@ -3913,7 +3687,7 @@ function loadManifest2(apmDir) {
|
|
|
3913
3687
|
}
|
|
3914
3688
|
function saveManifest2(apmDir, cliVersion) {
|
|
3915
3689
|
const manifest = { version: 1, cliVersion };
|
|
3916
|
-
|
|
3690
|
+
writeFileSync9(
|
|
3917
3691
|
toFsPath(manifestPath2(apmDir)),
|
|
3918
3692
|
`${JSON.stringify(manifest, null, 2)}
|
|
3919
3693
|
`,
|
|
@@ -4020,9 +3794,6 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
|
|
|
4020
3794
|
"\u521D\u59CB\u5316\u5DE5\u4F5C\u533A",
|
|
4021
3795
|
async () => {
|
|
4022
3796
|
const { didInit } = await ensureWorkspaceInitialized(workdir);
|
|
4023
|
-
if (!didInit) {
|
|
4024
|
-
assertApmGitignoredInRepo(workdir);
|
|
4025
|
-
}
|
|
4026
3797
|
return didInit;
|
|
4027
3798
|
},
|
|
4028
3799
|
(didInit) => didInit ? "\u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A" : "\u5DE5\u4F5C\u533A\u5DF2\u5C31\u7EEA"
|