ai-project-manage-cli 7.1.37 → 7.1.39
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 +461 -389
- package/dist/webide-message-worker.js +456 -656
- package/package.json +1 -1
- package/template/AGENTS.webide.md +27 -0
|
@@ -601,57 +601,6 @@ async function pushCurrentBranch(cwd) {
|
|
|
601
601
|
return true;
|
|
602
602
|
}
|
|
603
603
|
|
|
604
|
-
// src/baseline-resolve.ts
|
|
605
|
-
function formatBaselineDiagnostic(workdirPath, baselineWorkdirPath, diagnostic) {
|
|
606
|
-
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
|
|
607
|
-
\u8BF7\u5148\u5728\u5E73\u53F0\u767B\u8BB0\u8BE5\u8DEF\u5F84\u5BF9\u5E94\u7684\u5DE5\u4F5C\u76EE\u5F55\u4E0E\u4ED3\u5E93\u3002`;
|
|
608
|
-
}
|
|
609
|
-
async function matchRepositoryByGitRemote(api, workdirPath) {
|
|
610
|
-
const gitRoot = await resolveGitRepoRoot(workdirPath);
|
|
611
|
-
const gitUrl = await tryReadGitOriginUrl(gitRoot);
|
|
612
|
-
if (!gitUrl) {
|
|
613
|
-
return null;
|
|
614
|
-
}
|
|
615
|
-
const matched = await api.cli.matchRepository({ url: gitUrl });
|
|
616
|
-
const repositoryId = matched.repositoryId?.trim();
|
|
617
|
-
const defaultBranch = matched.defaultBranch?.trim();
|
|
618
|
-
if (!repositoryId || !defaultBranch) {
|
|
619
|
-
return null;
|
|
620
|
-
}
|
|
621
|
-
console.log(
|
|
622
|
-
`[apm] \u5DE5\u4F5C\u7A7A\u95F4\u8DEF\u5F84\u672A\u5339\u914D\uFF0C\u5DF2\u901A\u8FC7 git remote \u5173\u8054\u4ED3\u5E93: ${gitUrl}`
|
|
623
|
-
);
|
|
624
|
-
return { repositoryId, defaultBranch };
|
|
625
|
-
}
|
|
626
|
-
async function resolveWorkspaceBaseline(api, workdirPath) {
|
|
627
|
-
const baseline = await api.cli.workspaceBaseline({ workdirPath });
|
|
628
|
-
const repositoryId = baseline.repositoryId?.trim();
|
|
629
|
-
const defaultBranch = baseline.defaultBranch?.trim();
|
|
630
|
-
if (repositoryId && defaultBranch) {
|
|
631
|
-
return {
|
|
632
|
-
repositoryId,
|
|
633
|
-
defaultBranch,
|
|
634
|
-
workdirPath: baseline.workdirPath,
|
|
635
|
-
matchedViaGitRemote: false
|
|
636
|
-
};
|
|
637
|
-
}
|
|
638
|
-
const viaGit = await matchRepositoryByGitRemote(api, workdirPath);
|
|
639
|
-
if (viaGit) {
|
|
640
|
-
return {
|
|
641
|
-
...viaGit,
|
|
642
|
-
workdirPath: baseline.workdirPath,
|
|
643
|
-
matchedViaGitRemote: true
|
|
644
|
-
};
|
|
645
|
-
}
|
|
646
|
-
throw new Error(
|
|
647
|
-
`[apm] ${formatBaselineDiagnostic(
|
|
648
|
-
workdirPath,
|
|
649
|
-
baseline.workdirPath,
|
|
650
|
-
baseline.diagnostic
|
|
651
|
-
)}`
|
|
652
|
-
);
|
|
653
|
-
}
|
|
654
|
-
|
|
655
604
|
// src/command-utils.ts
|
|
656
605
|
import { copyFileSync, existsSync, mkdirSync as mkdirSync2, readdirSync, statSync } from "fs";
|
|
657
606
|
import { basename, dirname, extname, join as join2, resolve as resolve2 } from "path";
|
|
@@ -710,8 +659,12 @@ var WORKSPACE_TEMPLATE_SUBDIRS = [
|
|
|
710
659
|
"rules",
|
|
711
660
|
"deploy"
|
|
712
661
|
];
|
|
662
|
+
var WEBIDE_AGENTS_TEMPLATE_NAME = "AGENTS.webide.md";
|
|
713
663
|
function shouldSkipTemplateEntry(name) {
|
|
714
|
-
return name === ".DS_Store" || name === "Thumbs.db";
|
|
664
|
+
return name === ".DS_Store" || name === "Thumbs.db" || name === WEBIDE_AGENTS_TEMPLATE_NAME;
|
|
665
|
+
}
|
|
666
|
+
function isWebIdeRuleFileName(name) {
|
|
667
|
+
return name.startsWith("webide");
|
|
715
668
|
}
|
|
716
669
|
function copyTemplateEntry(src, dest) {
|
|
717
670
|
const fsSrc = toFsPath(src);
|
|
@@ -777,6 +730,68 @@ async function copyTemplateFiles(targetDir, workdir = resolveWorkdirPath()) {
|
|
|
777
730
|
}
|
|
778
731
|
assertTemplateCopiedToApm(resolvedTarget, resolve2(workdir));
|
|
779
732
|
}
|
|
733
|
+
function listWebIdeRuleFileNames(rulesDir) {
|
|
734
|
+
const fsRulesDir = toFsPath(rulesDir);
|
|
735
|
+
if (!existsSync(fsRulesDir) || !statSync(fsRulesDir).isDirectory()) {
|
|
736
|
+
return [];
|
|
737
|
+
}
|
|
738
|
+
return readdirSync(fsRulesDir).filter((name) => {
|
|
739
|
+
if (shouldSkipTemplateEntry(name) || !isWebIdeRuleFileName(name)) {
|
|
740
|
+
return false;
|
|
741
|
+
}
|
|
742
|
+
return statSync(toFsPath(join2(rulesDir, name))).isFile();
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
function assertWebIdeTemplateCopiedToApm(apmDir, workdir) {
|
|
746
|
+
const required = ["AGENTS.md", "apm.config.json", "rules"];
|
|
747
|
+
for (const item of required) {
|
|
748
|
+
const path = join2(apmDir, item);
|
|
749
|
+
if (!existsSync(toFsPath(path))) {
|
|
750
|
+
throw new Error(`[apm] WebIDE \u521D\u59CB\u5316\u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5C11: ${path}`);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
const rulesDir = join2(apmDir, "rules");
|
|
754
|
+
const ruleNames = listWebIdeRuleFileNames(rulesDir);
|
|
755
|
+
if (ruleNames.length === 0) {
|
|
756
|
+
throw new Error(`[apm] WebIDE \u521D\u59CB\u5316\u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5C11 webide \u89C4\u5219: ${rulesDir}`);
|
|
757
|
+
}
|
|
758
|
+
const leakedRules = join2(workdir, "rules");
|
|
759
|
+
const apmRules = join2(apmDir, "rules");
|
|
760
|
+
if (existsSync(toFsPath(leakedRules)) && !existsSync(toFsPath(join2(apmRules, "webide_reply.md")))) {
|
|
761
|
+
throw new Error(
|
|
762
|
+
`[apm] \u6A21\u677F\u88AB\u590D\u5236\u5230\u9519\u8BEF\u4F4D\u7F6E: ${leakedRules}\uFF08\u5E94\u5728 ${apmRules}\uFF09`
|
|
763
|
+
);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
async function copyWebIdeTemplateFiles(targetDir, workdir = resolveWorkdirPath()) {
|
|
767
|
+
const resolvedTarget = resolve2(targetDir);
|
|
768
|
+
const templateDir = resolve2(CLI_TEMPLATE_DIR);
|
|
769
|
+
const fsTemplateDir = toFsPath(templateDir);
|
|
770
|
+
if (!existsSync(fsTemplateDir) || !statSync(fsTemplateDir).isDirectory()) {
|
|
771
|
+
throw new Error(`[apm] \u672A\u627E\u5230 CLI \u6A21\u677F\u76EE\u5F55: ${templateDir}`);
|
|
772
|
+
}
|
|
773
|
+
const agentsSrc = join2(templateDir, WEBIDE_AGENTS_TEMPLATE_NAME);
|
|
774
|
+
if (!existsSync(toFsPath(agentsSrc))) {
|
|
775
|
+
throw new Error(`[apm] \u672A\u627E\u5230 WebIDE \u6307\u5357: ${agentsSrc}`);
|
|
776
|
+
}
|
|
777
|
+
const rulesSrc = join2(templateDir, "rules");
|
|
778
|
+
const ruleNames = listWebIdeRuleFileNames(rulesSrc);
|
|
779
|
+
if (ruleNames.length === 0) {
|
|
780
|
+
throw new Error(`[apm] CLI \u6A21\u677F\u4E2D\u6CA1\u6709 webide \u89C4\u5219: ${rulesSrc}`);
|
|
781
|
+
}
|
|
782
|
+
mkdirSync2(toFsPath(resolvedTarget), { recursive: true });
|
|
783
|
+
copyFileSync(toFsPath(agentsSrc), toFsPath(join2(resolvedTarget, "AGENTS.md")));
|
|
784
|
+
copyTemplateEntry(
|
|
785
|
+
join2(templateDir, "apm.config.json"),
|
|
786
|
+
join2(resolvedTarget, "apm.config.json")
|
|
787
|
+
);
|
|
788
|
+
const rulesDest = join2(resolvedTarget, "rules");
|
|
789
|
+
mkdirSync2(toFsPath(rulesDest), { recursive: true });
|
|
790
|
+
for (const name of ruleNames) {
|
|
791
|
+
copyTemplateEntry(join2(rulesSrc, name), join2(rulesDest, name));
|
|
792
|
+
}
|
|
793
|
+
assertWebIdeTemplateCopiedToApm(resolvedTarget, resolve2(workdir));
|
|
794
|
+
}
|
|
780
795
|
|
|
781
796
|
// src/workspace-repos.ts
|
|
782
797
|
import {
|
|
@@ -2923,462 +2938,119 @@ function resolveMessageReplyFallback(fallback) {
|
|
|
2923
2938
|
}
|
|
2924
2939
|
|
|
2925
2940
|
// src/commands/init.ts
|
|
2926
|
-
import { join as join6 } from "path";
|
|
2927
|
-
import { readFileSync as readFileSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
2928
|
-
|
|
2929
|
-
// src/deployment-config-sync.ts
|
|
2930
2941
|
import { join as join4 } from "path";
|
|
2931
|
-
import { writeFileSync as writeFileSync5 } from "fs";
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
try {
|
|
2936
|
-
const baseline = await resolveWorkspaceBaseline(api, workdirPath);
|
|
2937
|
-
return { repositoryId: baseline.repositoryId, diagnostic: null };
|
|
2938
|
-
} catch (err) {
|
|
2939
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
2940
|
-
return {
|
|
2941
|
-
repositoryId: null,
|
|
2942
|
-
diagnostic: detail.replace(/^\[apm\]\s*/, "")
|
|
2943
|
-
};
|
|
2944
|
-
}
|
|
2945
|
-
}
|
|
2946
|
-
async function writeDeploymentConfigContent(apmDir, content, configName) {
|
|
2947
|
-
let parsed;
|
|
2948
|
-
try {
|
|
2949
|
-
parsed = JSON.parse(content);
|
|
2950
|
-
} catch {
|
|
2951
|
-
console.warn(
|
|
2952
|
-
`[apm] \u8FDC\u7A0B\u90E8\u7F72\u914D\u7F6E\u300C${configName}\u300DJSON \u65E0\u6548\uFF08${TEMPLATE_HINT}\uFF09`
|
|
2953
|
-
);
|
|
2954
|
-
return false;
|
|
2955
|
-
}
|
|
2942
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
2943
|
+
function applyWorkspaceName(apmDir, name) {
|
|
2944
|
+
const trimmedName = name?.trim();
|
|
2945
|
+
if (!trimmedName) return;
|
|
2956
2946
|
const apmConfigPath = toFsPath(join4(apmDir, "apm.config.json"));
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2947
|
+
const config = readFileSync5(apmConfigPath, "utf8");
|
|
2948
|
+
const configJson = JSON.parse(config);
|
|
2949
|
+
configJson.name = trimmedName;
|
|
2950
|
+
writeFileSync5(
|
|
2951
|
+
apmConfigPath,
|
|
2952
|
+
`${JSON.stringify(configJson, null, 2)}
|
|
2953
|
+
`,
|
|
2954
|
+
"utf8"
|
|
2955
|
+
);
|
|
2962
2956
|
}
|
|
2963
|
-
async function
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
);
|
|
2970
|
-
return { synced: false, repositoryId: null };
|
|
2971
|
-
}
|
|
2972
|
-
const api = createApmApiClient(cfg);
|
|
2973
|
-
const preferredId = options?.repositoryId?.trim() || "";
|
|
2974
|
-
let repositoryId = preferredId || null;
|
|
2975
|
-
let diagnostic = null;
|
|
2976
|
-
if (!repositoryId) {
|
|
2977
|
-
const resolved = await resolveRepositoryIdForSync(api, workdirPath);
|
|
2978
|
-
repositoryId = resolved.repositoryId;
|
|
2979
|
-
diagnostic = resolved.diagnostic;
|
|
2980
|
-
}
|
|
2981
|
-
if (!repositoryId) {
|
|
2982
|
-
console.log(
|
|
2983
|
-
`[apm] \u672A\u80FD\u540C\u6B65\u5E73\u53F0\u90E8\u7F72\u914D\u7F6E\uFF08${TEMPLATE_HINT}\uFF09\u3002
|
|
2984
|
-
${diagnostic ?? ""}
|
|
2985
|
-
[apm] ${SYNC_HINT}`
|
|
2986
|
-
);
|
|
2987
|
-
return { synced: false, repositoryId: null };
|
|
2988
|
-
}
|
|
2989
|
-
const { config } = await api.cli.getDeploymentConfiguration({ repositoryId });
|
|
2990
|
-
if (!config) {
|
|
2991
|
-
console.log(
|
|
2992
|
-
`[apm] \u672A\u627E\u5230\u5173\u8054\u4ED3\u5E93\u7684\u90E8\u7F72\u914D\u7F6E\uFF08${TEMPLATE_HINT}\uFF0CrepositoryId\uFF1A${repositoryId}\uFF09\u3002
|
|
2993
|
-
[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`
|
|
2994
|
-
);
|
|
2995
|
-
return { synced: false, repositoryId };
|
|
2957
|
+
async function initializeWorkspace(workdir, options) {
|
|
2958
|
+
await ensureWorkspaceApmDirForInit(workdir);
|
|
2959
|
+
const apmDir = workspaceApmDir(workdir);
|
|
2960
|
+
if (options?.webide) {
|
|
2961
|
+
await copyWebIdeTemplateFiles(apmDir, workdir);
|
|
2962
|
+
} else {
|
|
2963
|
+
await copyTemplateFiles(apmDir, workdir);
|
|
2996
2964
|
}
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
config.content,
|
|
3001
|
-
config.name
|
|
2965
|
+
applyWorkspaceName(apmDir, options?.name);
|
|
2966
|
+
console.log(
|
|
2967
|
+
options?.webide ? `[apm] \u5DF2\u6309 WebIDE \u521D\u59CB\u5316\u5DE5\u4F5C\u533A\uFF1A${apmDir}` : `[apm] \u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A\uFF1A${apmDir}`
|
|
3002
2968
|
);
|
|
3003
|
-
|
|
3004
|
-
|
|
2969
|
+
return { didInit: true };
|
|
2970
|
+
}
|
|
2971
|
+
async function ensureWorkspaceInitialized(workdir, options) {
|
|
2972
|
+
if (isWorkspaceApmInitialized(workdir)) {
|
|
2973
|
+
return { didInit: false };
|
|
3005
2974
|
}
|
|
3006
|
-
|
|
2975
|
+
console.log(
|
|
2976
|
+
options?.webide ? `[apm] \u5DE5\u4F5C\u76EE\u5F55 ${workdir} \u672A\u68C0\u6D4B\u5230\u5DF2\u521D\u59CB\u5316\u7684 .apm\uFF0C\u6B63\u5728\u6309 WebIDE \u81EA\u52A8\u521D\u59CB\u5316\u2026` : `[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`
|
|
2977
|
+
);
|
|
2978
|
+
return initializeWorkspace(workdir, options);
|
|
3007
2979
|
}
|
|
3008
2980
|
|
|
3009
|
-
// src/
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
readdirSync as readdirSync3,
|
|
3013
|
-
readFileSync as readFileSync5,
|
|
3014
|
-
rmSync as rmSync2,
|
|
3015
|
-
writeFileSync as writeFileSync6
|
|
3016
|
-
} from "fs";
|
|
3017
|
-
import { createHash } from "crypto";
|
|
3018
|
-
import { dirname as dirname5, join as join5, relative as relative2, sep } from "path";
|
|
3019
|
-
var MANIFEST_FILE = "manifest.json";
|
|
3020
|
-
function normalizeProjectIdForPath(projectId) {
|
|
3021
|
-
const id = projectId.trim();
|
|
3022
|
-
if (!id) {
|
|
3023
|
-
throw new Error("projectId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
3024
|
-
}
|
|
3025
|
-
if (id.includes("..") || id.includes("/") || id.includes("\\") || id.includes("\0")) {
|
|
3026
|
-
throw new Error(`\u975E\u6CD5 projectId: ${projectId}`);
|
|
3027
|
-
}
|
|
3028
|
-
return id;
|
|
2981
|
+
// src/commands/connect/pre-step-cache.ts
|
|
2982
|
+
function sessionWorkdirKey(sessionId, workdir) {
|
|
2983
|
+
return `${sessionId}\0${workdir}`;
|
|
3029
2984
|
}
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
return
|
|
2985
|
+
var lastBranchKey = null;
|
|
2986
|
+
function shouldRunBranch(sessionId, workdir) {
|
|
2987
|
+
return lastBranchKey !== sessionWorkdirKey(sessionId, workdir);
|
|
3033
2988
|
}
|
|
3034
|
-
function
|
|
3035
|
-
|
|
3036
|
-
return join5(
|
|
3037
|
-
projectDocumentsDir(apmRoot, projectId),
|
|
3038
|
-
...normalized.split("/")
|
|
3039
|
-
);
|
|
2989
|
+
function markBranchDone(sessionId, workdir) {
|
|
2990
|
+
lastBranchKey = sessionWorkdirKey(sessionId, workdir);
|
|
3040
2991
|
}
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
2992
|
+
|
|
2993
|
+
// src/commands/connect/webide-pull-requests.ts
|
|
2994
|
+
var PRE_AGENT_PR_ACTIONS = /* @__PURE__ */ new Set([
|
|
2995
|
+
"generate-cases",
|
|
2996
|
+
"run-auto-test",
|
|
2997
|
+
// 验收合并前兜底确保 PR 存在
|
|
2998
|
+
"accept"
|
|
2999
|
+
]);
|
|
3000
|
+
var POST_AGENT_PR_ACTIONS = /* @__PURE__ */ new Set(["create-pr", "finish-manual-test"]);
|
|
3001
|
+
function shouldEnsureWebIdePullRequests(action) {
|
|
3002
|
+
return PRE_AGENT_PR_ACTIONS.has(action);
|
|
3051
3003
|
}
|
|
3052
|
-
function
|
|
3053
|
-
return
|
|
3004
|
+
function shouldEnsureWebIdePullRequestsAfterAgent(action) {
|
|
3005
|
+
return POST_AGENT_PR_ACTIONS.has(action);
|
|
3054
3006
|
}
|
|
3055
|
-
function
|
|
3056
|
-
const
|
|
3057
|
-
|
|
3058
|
-
|
|
3007
|
+
function formatWebIdePullRequestSummary(result) {
|
|
3008
|
+
const lines = [];
|
|
3009
|
+
lines.push("## PR \u521B\u5EFA\u7ED3\u679C");
|
|
3010
|
+
lines.push(
|
|
3011
|
+
`\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`
|
|
3059
3012
|
);
|
|
3060
|
-
if (
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
try {
|
|
3064
|
-
return JSON.parse(
|
|
3065
|
-
readFileSync5(manifestPath3, "utf8")
|
|
3066
|
-
);
|
|
3067
|
-
} catch {
|
|
3068
|
-
return null;
|
|
3069
|
-
}
|
|
3070
|
-
}
|
|
3071
|
-
function listLocalDocumentPaths(apmRoot, projectId) {
|
|
3072
|
-
const root = projectDocumentsDir(apmRoot, projectId);
|
|
3073
|
-
if (!existsSync6(root)) {
|
|
3074
|
-
return [];
|
|
3013
|
+
if (result.items.length === 0) {
|
|
3014
|
+
lines.push("\u672A\u5904\u7406\u4EFB\u4F55\u4ED3\u5E93\u3002");
|
|
3015
|
+
return lines.join("\n");
|
|
3075
3016
|
}
|
|
3076
|
-
|
|
3077
|
-
const
|
|
3078
|
-
|
|
3079
|
-
const
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
const rel = relative2(root, abs).split(sep).join("/");
|
|
3088
|
-
paths.push(rel);
|
|
3017
|
+
lines.push("");
|
|
3018
|
+
for (const item of result.items) {
|
|
3019
|
+
if (item.outcome === "created") {
|
|
3020
|
+
const num = item.number != null && item.number > 0 ? `#${item.number}` : "\uFF08\u65E0\u7F16\u53F7\uFF09";
|
|
3021
|
+
const branch = item.headBranch && item.baseBranch ? `${item.headBranch} \u2192 ${item.baseBranch}` : item.baseBranch ? `\u76EE\u6807\u5206\u652F ${item.baseBranch}` : "";
|
|
3022
|
+
const state = item.state ? `\u72B6\u6001 ${item.state}` : "";
|
|
3023
|
+
const url = item.url?.trim() ? item.url.trim() : "";
|
|
3024
|
+
lines.push(
|
|
3025
|
+
`- **${item.label}**\uFF1A\u5DF2\u521B\u5EFA ${num}${branch ? `\uFF0C${branch}` : ""}${state ? `\uFF0C${state}` : ""}${url ? `\uFF0C${url}` : ""}`
|
|
3026
|
+
);
|
|
3027
|
+
continue;
|
|
3089
3028
|
}
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
const remoteMap = new Map(
|
|
3096
|
-
(remote?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
3097
|
-
);
|
|
3098
|
-
const localMap = new Map(
|
|
3099
|
-
(local?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
3100
|
-
);
|
|
3101
|
-
const download = [];
|
|
3102
|
-
for (const [path, hash] of remoteMap) {
|
|
3103
|
-
if (localMap.get(path) !== hash) {
|
|
3104
|
-
download.push(path);
|
|
3029
|
+
if (item.outcome === "skipped_no_changes") {
|
|
3030
|
+
lines.push(
|
|
3031
|
+
`- **${item.label}**\uFF1A\u76F8\u5BF9\u57FA\u7EBF ${item.baseBranch || "\uFF08\u672A\u77E5\uFF09"} \u65E0\u4EE3\u7801\u53D8\u66F4\uFF0C\u672A\u521B\u5EFA PR`
|
|
3032
|
+
);
|
|
3033
|
+
continue;
|
|
3105
3034
|
}
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
if (!remoteMap.has(path)) {
|
|
3110
|
-
deleteLocal.push(path);
|
|
3035
|
+
if (item.outcome === "skipped") {
|
|
3036
|
+
lines.push(`- **${item.label}**\uFF1A\u5DF2\u8DF3\u8FC7 \u2014 ${item.detail}`);
|
|
3037
|
+
continue;
|
|
3111
3038
|
}
|
|
3039
|
+
lines.push(`- **${item.label}**\uFF1A\u521B\u5EFA\u5931\u8D25 \u2014 ${item.detail}`);
|
|
3112
3040
|
}
|
|
3113
|
-
return
|
|
3041
|
+
return lines.join("\n");
|
|
3114
3042
|
}
|
|
3115
|
-
async function
|
|
3116
|
-
const
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
const projectIds = baseline.projectIds ?? [];
|
|
3127
|
-
if (projectIds.length === 0) {
|
|
3128
|
-
return {
|
|
3129
|
-
projectId: null,
|
|
3130
|
-
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
|
|
3131
|
-
\u8BF7\u5728\u5E73\u53F0\u5C06\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u5230\u552F\u4E00\u9879\u76EE\u3002`
|
|
3132
|
-
};
|
|
3133
|
-
}
|
|
3134
|
-
return {
|
|
3135
|
-
projectId: null,
|
|
3136
|
-
diagnostic: `\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u4E86 ${projectIds.length} \u4E2A\u9879\u76EE\uFF0C\u65E0\u6CD5\u786E\u5B9A\u540C\u6B65\u76EE\u6807\u3002
|
|
3137
|
-
\u8BF7\u786E\u4FDD\u8BE5\u5DE5\u4F5C\u7A7A\u95F4\u53EA\u5173\u8054\u4E00\u4E2A\u9879\u76EE\u540E\u518D\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002`
|
|
3138
|
-
};
|
|
3139
|
-
} catch (err) {
|
|
3140
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
3141
|
-
return {
|
|
3142
|
-
projectId: null,
|
|
3143
|
-
diagnostic: detail.replace(/^\[apm\]\s*/, "")
|
|
3144
|
-
};
|
|
3145
|
-
}
|
|
3146
|
-
}
|
|
3147
|
-
async function syncProjectDocumentsPull(workdirPath, apmDir, options) {
|
|
3148
|
-
const empty = {
|
|
3149
|
-
synced: false,
|
|
3150
|
-
projectId: null,
|
|
3151
|
-
downloaded: 0,
|
|
3152
|
-
deleted: 0
|
|
3153
|
-
};
|
|
3154
|
-
const cfg = await tryReadApmConfig();
|
|
3155
|
-
if (!cfg || !resolveApiKey(cfg)) {
|
|
3156
|
-
console.log(
|
|
3157
|
-
"[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"
|
|
3158
|
-
);
|
|
3159
|
-
return empty;
|
|
3160
|
-
}
|
|
3161
|
-
const api = createApmApiClient(cfg);
|
|
3162
|
-
const { projectId, diagnostic } = await resolveProjectIdForSync(
|
|
3163
|
-
api,
|
|
3164
|
-
workdirPath,
|
|
3165
|
-
options
|
|
3166
|
-
);
|
|
3167
|
-
if (!projectId) {
|
|
3168
|
-
console.log(`[apm] \u672A\u80FD\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002
|
|
3169
|
-
${diagnostic ?? ""}`);
|
|
3170
|
-
return empty;
|
|
3171
|
-
}
|
|
3172
|
-
const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
|
|
3173
|
-
const docsDir = projectDocumentsDir(targetApmDir, projectId);
|
|
3174
|
-
await ensureDirExists(docsDir);
|
|
3175
|
-
const { manifest: remoteManifest } = await api.cli.getProjectDocumentManifest(
|
|
3176
|
-
{ projectId }
|
|
3177
|
-
);
|
|
3178
|
-
if (!remoteManifest) {
|
|
3179
|
-
console.log(`[apm] \u9879\u76EE ${projectId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`);
|
|
3180
|
-
return { ...empty, projectId };
|
|
3181
|
-
}
|
|
3182
|
-
const localManifest = readLocalManifest(targetApmDir, projectId);
|
|
3183
|
-
const { download, deleteLocal } = diffManifestPaths(
|
|
3184
|
-
remoteManifest,
|
|
3185
|
-
localManifest
|
|
3186
|
-
);
|
|
3187
|
-
let downloaded = 0;
|
|
3188
|
-
if (download.length > 0) {
|
|
3189
|
-
const { list } = await api.cli.listProjectDocuments({
|
|
3190
|
-
projectId,
|
|
3191
|
-
paths: download.join(",")
|
|
3192
|
-
});
|
|
3193
|
-
for (const doc of list) {
|
|
3194
|
-
const absPath = toFsPath(
|
|
3195
|
-
projectDocumentLocalPath(targetApmDir, projectId, doc.path)
|
|
3196
|
-
);
|
|
3197
|
-
await ensureDirExists(dirname5(absPath));
|
|
3198
|
-
writeFileSync6(absPath, doc.content, "utf8");
|
|
3199
|
-
downloaded += 1;
|
|
3200
|
-
}
|
|
3201
|
-
}
|
|
3202
|
-
let deleted = 0;
|
|
3203
|
-
for (const path of deleteLocal) {
|
|
3204
|
-
const absPath = toFsPath(
|
|
3205
|
-
projectDocumentLocalPath(targetApmDir, projectId, path)
|
|
3206
|
-
);
|
|
3207
|
-
if (existsSync6(absPath)) {
|
|
3208
|
-
rmSync2(absPath, { force: true });
|
|
3209
|
-
deleted += 1;
|
|
3210
|
-
}
|
|
3211
|
-
}
|
|
3212
|
-
writeFileSync6(
|
|
3213
|
-
toFsPath(join5(docsDir, MANIFEST_FILE)),
|
|
3214
|
-
`${JSON.stringify(remoteManifest, null, 2)}
|
|
3215
|
-
`,
|
|
3216
|
-
"utf8"
|
|
3217
|
-
);
|
|
3218
|
-
console.log(
|
|
3219
|
-
`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: projectId=${projectId} \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted} \u2192 .apm/project/${projectId}/`
|
|
3220
|
-
);
|
|
3221
|
-
return {
|
|
3222
|
-
synced: true,
|
|
3223
|
-
projectId,
|
|
3224
|
-
downloaded,
|
|
3225
|
-
deleted
|
|
3226
|
-
};
|
|
3227
|
-
}
|
|
3228
|
-
async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot, options) {
|
|
3229
|
-
const api = createApmApiClient(cfg);
|
|
3230
|
-
const { projectId } = await resolveProjectIdForSync(
|
|
3231
|
-
api,
|
|
3232
|
-
workdirPath,
|
|
3233
|
-
options
|
|
3234
|
-
);
|
|
3235
|
-
if (!projectId) {
|
|
3236
|
-
return 0;
|
|
3237
|
-
}
|
|
3238
|
-
const targetApmDir = apmRoot ?? workspaceApmDir(workdirPath);
|
|
3239
|
-
const localPaths = listLocalDocumentPaths(targetApmDir, projectId);
|
|
3240
|
-
if (localPaths.length === 0) {
|
|
3241
|
-
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u672C\u5730\u6587\u4EF6\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
3242
|
-
return 0;
|
|
3243
|
-
}
|
|
3244
|
-
const remoteManifest = (await api.cli.getProjectDocumentManifest({ projectId })).manifest ?? null;
|
|
3245
|
-
const remoteHashByPath = new Map(
|
|
3246
|
-
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
3247
|
-
);
|
|
3248
|
-
const remoteDescriptionByPath = new Map(
|
|
3249
|
-
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.description])
|
|
3250
|
-
);
|
|
3251
|
-
let synced = 0;
|
|
3252
|
-
for (const path of localPaths) {
|
|
3253
|
-
const absPath = toFsPath(
|
|
3254
|
-
projectDocumentLocalPath(targetApmDir, projectId, path)
|
|
3255
|
-
);
|
|
3256
|
-
const content = readFileSync5(absPath, "utf8");
|
|
3257
|
-
const contentHash = hashLocalFileContent(content);
|
|
3258
|
-
if (remoteHashByPath.get(path) === contentHash) {
|
|
3259
|
-
continue;
|
|
3260
|
-
}
|
|
3261
|
-
await api.cli.upsertProjectDocument({
|
|
3262
|
-
projectId,
|
|
3263
|
-
path,
|
|
3264
|
-
content,
|
|
3265
|
-
description: remoteDescriptionByPath.get(path) ?? void 0
|
|
3266
|
-
});
|
|
3267
|
-
synced += 1;
|
|
3268
|
-
console.log(`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: ${path}`);
|
|
3269
|
-
}
|
|
3270
|
-
if (synced === 0) {
|
|
3271
|
-
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
3272
|
-
}
|
|
3273
|
-
return synced;
|
|
3274
|
-
}
|
|
3275
|
-
|
|
3276
|
-
// src/commands/init.ts
|
|
3277
|
-
async function initializeWorkspace(workdir, options) {
|
|
3278
|
-
await ensureWorkspaceApmDirForInit(workdir);
|
|
3279
|
-
const apmDir = workspaceApmDir(workdir);
|
|
3280
|
-
await copyTemplateFiles(apmDir, workdir);
|
|
3281
|
-
const syncResult = await syncRemoteDeploymentConfig(workdir, apmDir);
|
|
3282
|
-
await syncProjectDocumentsPull(workdir, apmDir);
|
|
3283
|
-
const trimmedName = options?.name?.trim();
|
|
3284
|
-
if (trimmedName) {
|
|
3285
|
-
const apmConfigPath = toFsPath(join6(apmDir, "apm.config.json"));
|
|
3286
|
-
const config = readFileSync6(apmConfigPath, "utf8");
|
|
3287
|
-
const configJson = JSON.parse(config);
|
|
3288
|
-
configJson.name = trimmedName;
|
|
3289
|
-
writeFileSync7(
|
|
3290
|
-
apmConfigPath,
|
|
3291
|
-
`${JSON.stringify(configJson, null, 2)}
|
|
3292
|
-
`,
|
|
3293
|
-
"utf8"
|
|
3294
|
-
);
|
|
3295
|
-
}
|
|
3296
|
-
console.log(`[apm] \u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A\uFF1A${apmDir}`);
|
|
3297
|
-
return { didInit: true, syncResult };
|
|
3298
|
-
}
|
|
3299
|
-
async function ensureWorkspaceInitialized(workdir, options) {
|
|
3300
|
-
if (isWorkspaceApmInitialized(workdir)) {
|
|
3301
|
-
return { didInit: false };
|
|
3302
|
-
}
|
|
3303
|
-
console.log(
|
|
3304
|
-
`[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`
|
|
3305
|
-
);
|
|
3306
|
-
return initializeWorkspace(workdir, options);
|
|
3307
|
-
}
|
|
3308
|
-
|
|
3309
|
-
// src/commands/connect/pre-step-cache.ts
|
|
3310
|
-
function sessionWorkdirKey(sessionId, workdir) {
|
|
3311
|
-
return `${sessionId}\0${workdir}`;
|
|
3312
|
-
}
|
|
3313
|
-
var lastBranchKey = null;
|
|
3314
|
-
function shouldRunBranch(sessionId, workdir) {
|
|
3315
|
-
return lastBranchKey !== sessionWorkdirKey(sessionId, workdir);
|
|
3316
|
-
}
|
|
3317
|
-
function markBranchDone(sessionId, workdir) {
|
|
3318
|
-
lastBranchKey = sessionWorkdirKey(sessionId, workdir);
|
|
3319
|
-
}
|
|
3320
|
-
|
|
3321
|
-
// src/commands/connect/webide-pull-requests.ts
|
|
3322
|
-
var PRE_AGENT_PR_ACTIONS = /* @__PURE__ */ new Set([
|
|
3323
|
-
"generate-cases",
|
|
3324
|
-
"run-auto-test",
|
|
3325
|
-
// 验收合并前兜底确保 PR 存在
|
|
3326
|
-
"accept"
|
|
3327
|
-
]);
|
|
3328
|
-
var POST_AGENT_PR_ACTIONS = /* @__PURE__ */ new Set(["create-pr", "finish-manual-test"]);
|
|
3329
|
-
function shouldEnsureWebIdePullRequests(action) {
|
|
3330
|
-
return PRE_AGENT_PR_ACTIONS.has(action);
|
|
3331
|
-
}
|
|
3332
|
-
function shouldEnsureWebIdePullRequestsAfterAgent(action) {
|
|
3333
|
-
return POST_AGENT_PR_ACTIONS.has(action);
|
|
3334
|
-
}
|
|
3335
|
-
function formatWebIdePullRequestSummary(result) {
|
|
3336
|
-
const lines = [];
|
|
3337
|
-
lines.push("## PR \u521B\u5EFA\u7ED3\u679C");
|
|
3338
|
-
lines.push(
|
|
3339
|
-
`\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`
|
|
3340
|
-
);
|
|
3341
|
-
if (result.items.length === 0) {
|
|
3342
|
-
lines.push("\u672A\u5904\u7406\u4EFB\u4F55\u4ED3\u5E93\u3002");
|
|
3343
|
-
return lines.join("\n");
|
|
3344
|
-
}
|
|
3345
|
-
lines.push("");
|
|
3346
|
-
for (const item of result.items) {
|
|
3347
|
-
if (item.outcome === "created") {
|
|
3348
|
-
const num = item.number != null && item.number > 0 ? `#${item.number}` : "\uFF08\u65E0\u7F16\u53F7\uFF09";
|
|
3349
|
-
const branch = item.headBranch && item.baseBranch ? `${item.headBranch} \u2192 ${item.baseBranch}` : item.baseBranch ? `\u76EE\u6807\u5206\u652F ${item.baseBranch}` : "";
|
|
3350
|
-
const state = item.state ? `\u72B6\u6001 ${item.state}` : "";
|
|
3351
|
-
const url = item.url?.trim() ? item.url.trim() : "";
|
|
3352
|
-
lines.push(
|
|
3353
|
-
`- **${item.label}**\uFF1A\u5DF2\u521B\u5EFA ${num}${branch ? `\uFF0C${branch}` : ""}${state ? `\uFF0C${state}` : ""}${url ? `\uFF0C${url}` : ""}`
|
|
3354
|
-
);
|
|
3355
|
-
continue;
|
|
3356
|
-
}
|
|
3357
|
-
if (item.outcome === "skipped_no_changes") {
|
|
3358
|
-
lines.push(
|
|
3359
|
-
`- **${item.label}**\uFF1A\u76F8\u5BF9\u57FA\u7EBF ${item.baseBranch || "\uFF08\u672A\u77E5\uFF09"} \u65E0\u4EE3\u7801\u53D8\u66F4\uFF0C\u672A\u521B\u5EFA PR`
|
|
3360
|
-
);
|
|
3361
|
-
continue;
|
|
3362
|
-
}
|
|
3363
|
-
if (item.outcome === "skipped") {
|
|
3364
|
-
lines.push(`- **${item.label}**\uFF1A\u5DF2\u8DF3\u8FC7 \u2014 ${item.detail}`);
|
|
3365
|
-
continue;
|
|
3366
|
-
}
|
|
3367
|
-
lines.push(`- **${item.label}**\uFF1A\u521B\u5EFA\u5931\u8D25 \u2014 ${item.detail}`);
|
|
3368
|
-
}
|
|
3369
|
-
return lines.join("\n");
|
|
3370
|
-
}
|
|
3371
|
-
async function ensureWebIdePullRequests(cfg, taskId, workdir) {
|
|
3372
|
-
const api = createApmApiClient(cfg);
|
|
3373
|
-
const empty = () => ({
|
|
3374
|
-
ensured: 0,
|
|
3375
|
-
skipped: 0,
|
|
3376
|
-
skippedNoChanges: 0,
|
|
3377
|
-
failed: 0,
|
|
3378
|
-
items: [],
|
|
3379
|
-
tips: []
|
|
3380
|
-
});
|
|
3381
|
-
let repoRoots;
|
|
3043
|
+
async function ensureWebIdePullRequests(cfg, taskId, workdir) {
|
|
3044
|
+
const api = createApmApiClient(cfg);
|
|
3045
|
+
const empty = () => ({
|
|
3046
|
+
ensured: 0,
|
|
3047
|
+
skipped: 0,
|
|
3048
|
+
skippedNoChanges: 0,
|
|
3049
|
+
failed: 0,
|
|
3050
|
+
items: [],
|
|
3051
|
+
tips: []
|
|
3052
|
+
});
|
|
3053
|
+
let repoRoots;
|
|
3382
3054
|
try {
|
|
3383
3055
|
const manifest = loadWorkspaceReposCache(workdir);
|
|
3384
3056
|
repoRoots = resolveWorkspaceRepoAbsolutePaths(manifest);
|
|
@@ -3555,165 +3227,24 @@ async function ensureWebIdePullRequests(cfg, taskId, workdir) {
|
|
|
3555
3227
|
}
|
|
3556
3228
|
|
|
3557
3229
|
// src/version.ts
|
|
3558
|
-
import { readFileSync as
|
|
3559
|
-
import { dirname as
|
|
3230
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
3231
|
+
import { dirname as dirname5, join as join5 } from "path";
|
|
3560
3232
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3561
3233
|
function readCliVersion() {
|
|
3562
3234
|
try {
|
|
3563
|
-
const dir =
|
|
3564
|
-
const pkgPath =
|
|
3565
|
-
const pkg = JSON.parse(
|
|
3235
|
+
const dir = dirname5(fileURLToPath2(import.meta.url));
|
|
3236
|
+
const pkgPath = join5(dir, "..", "package.json");
|
|
3237
|
+
const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
|
|
3566
3238
|
return pkg.version ?? "0.0.0";
|
|
3567
|
-
} catch {
|
|
3568
|
-
return "0.0.0";
|
|
3569
|
-
}
|
|
3570
|
-
}
|
|
3571
|
-
|
|
3572
|
-
// src/commands/update-skills.ts
|
|
3573
|
-
import { existsSync as existsSync8, mkdirSync as mkdirSync7, statSync as statSync4 } from "fs";
|
|
3574
|
-
import { join as join9 } from "path";
|
|
3575
|
-
|
|
3576
|
-
// src/skills-sync.ts
|
|
3577
|
-
import {
|
|
3578
|
-
copyFileSync as copyFileSync2,
|
|
3579
|
-
cpSync,
|
|
3580
|
-
existsSync as existsSync7,
|
|
3581
|
-
mkdirSync as mkdirSync6,
|
|
3582
|
-
readdirSync as readdirSync4,
|
|
3583
|
-
rmSync as rmSync3,
|
|
3584
|
-
statSync as statSync3,
|
|
3585
|
-
writeFileSync as writeFileSync8
|
|
3586
|
-
} from "fs";
|
|
3587
|
-
import { join as join8 } from "path";
|
|
3588
|
-
var AGENTS_TEMPLATE_PATH = join8(CLI_TEMPLATE_DIR, "AGENTS.md");
|
|
3589
|
-
var BASE_SKILLS_TEMPLATE_DIR = join8(CLI_TEMPLATE_DIR, "skills");
|
|
3590
|
-
var BASE_RULES_TEMPLATE_DIR = join8(CLI_TEMPLATE_DIR, "rules");
|
|
3591
|
-
function sanitizeSkillDirName(name) {
|
|
3592
|
-
const trimmed = name.trim();
|
|
3593
|
-
if (!trimmed) return "skill";
|
|
3594
|
-
return trimmed.replace(/[/\\:*?"<>|]/g, "_");
|
|
3595
|
-
}
|
|
3596
|
-
function listBaseSkillDirNames() {
|
|
3597
|
-
if (!existsSync7(BASE_SKILLS_TEMPLATE_DIR)) return [];
|
|
3598
|
-
return readdirSync4(BASE_SKILLS_TEMPLATE_DIR).filter((name) => {
|
|
3599
|
-
const path = join8(BASE_SKILLS_TEMPLATE_DIR, name);
|
|
3600
|
-
return statSync3(path).isDirectory();
|
|
3601
|
-
});
|
|
3602
|
-
}
|
|
3603
|
-
function syncAgentsGuide(apmDir) {
|
|
3604
|
-
if (!existsSync7(AGENTS_TEMPLATE_PATH)) return false;
|
|
3605
|
-
mkdirSync6(apmDir, { recursive: true });
|
|
3606
|
-
copyFileSync2(AGENTS_TEMPLATE_PATH, join8(apmDir, "AGENTS.md"));
|
|
3607
|
-
return true;
|
|
3608
|
-
}
|
|
3609
|
-
function listBaseRuleFileNames() {
|
|
3610
|
-
if (!existsSync7(BASE_RULES_TEMPLATE_DIR)) return [];
|
|
3611
|
-
return readdirSync4(BASE_RULES_TEMPLATE_DIR).filter((name) => {
|
|
3612
|
-
const path = join8(BASE_RULES_TEMPLATE_DIR, name);
|
|
3613
|
-
return statSync3(path).isFile();
|
|
3614
|
-
});
|
|
3615
|
-
}
|
|
3616
|
-
function syncBaseRules(rulesDir) {
|
|
3617
|
-
mkdirSync6(rulesDir, { recursive: true });
|
|
3618
|
-
const names = listBaseRuleFileNames();
|
|
3619
|
-
for (const name of names) {
|
|
3620
|
-
const src = join8(BASE_RULES_TEMPLATE_DIR, name);
|
|
3621
|
-
const dest = join8(rulesDir, name);
|
|
3622
|
-
copyFileSync2(src, dest);
|
|
3623
|
-
}
|
|
3624
|
-
return names;
|
|
3625
|
-
}
|
|
3626
|
-
function syncBaseSkills(skillsDir) {
|
|
3627
|
-
mkdirSync6(skillsDir, { recursive: true });
|
|
3628
|
-
const names = listBaseSkillDirNames();
|
|
3629
|
-
for (const name of names) {
|
|
3630
|
-
const src = join8(BASE_SKILLS_TEMPLATE_DIR, name);
|
|
3631
|
-
const dest = join8(skillsDir, name);
|
|
3632
|
-
cpSync(src, dest, { recursive: true, force: true });
|
|
3633
|
-
}
|
|
3634
|
-
return names;
|
|
3635
|
-
}
|
|
3636
|
-
function syncSupplementarySkills(skillsDir, list) {
|
|
3637
|
-
const baseNames = new Set(listBaseSkillDirNames());
|
|
3638
|
-
const apiDirNames = /* @__PURE__ */ new Set();
|
|
3639
|
-
const written = [];
|
|
3640
|
-
const skipped = [];
|
|
3641
|
-
for (const skill of list) {
|
|
3642
|
-
const dirName = sanitizeSkillDirName(skill.name);
|
|
3643
|
-
apiDirNames.add(dirName);
|
|
3644
|
-
if (baseNames.has(dirName)) {
|
|
3645
|
-
skipped.push(dirName);
|
|
3646
|
-
continue;
|
|
3647
|
-
}
|
|
3648
|
-
const skillDir = join8(skillsDir, dirName);
|
|
3649
|
-
mkdirSync6(skillDir, { recursive: true });
|
|
3650
|
-
writeFileSync8(join8(skillDir, "SKILL.md"), skill.content ?? "", "utf8");
|
|
3651
|
-
written.push(dirName);
|
|
3652
|
-
}
|
|
3653
|
-
const removed = [];
|
|
3654
|
-
if (!existsSync7(skillsDir)) return { written, skipped, removed };
|
|
3655
|
-
for (const entry of readdirSync4(skillsDir)) {
|
|
3656
|
-
const full = join8(skillsDir, entry);
|
|
3657
|
-
if (!statSync3(full).isDirectory()) continue;
|
|
3658
|
-
if (baseNames.has(entry)) continue;
|
|
3659
|
-
if (apiDirNames.has(entry)) continue;
|
|
3660
|
-
rmSync3(full, { recursive: true, force: true });
|
|
3661
|
-
removed.push(entry);
|
|
3662
|
-
}
|
|
3663
|
-
return { written, skipped, removed };
|
|
3664
|
-
}
|
|
3665
|
-
|
|
3666
|
-
// src/commands/update-skills.ts
|
|
3667
|
-
async function syncWorkspaceSkills(cfg, workdir) {
|
|
3668
|
-
const apmDir = workspaceApmDir(workdir);
|
|
3669
|
-
const fsApmDir = toFsPath(apmDir);
|
|
3670
|
-
if (!existsSync8(fsApmDir)) {
|
|
3671
|
-
throw new Error("[apm] \u672A\u627E\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5148\u6267\u884C apm init");
|
|
3672
|
-
}
|
|
3673
|
-
const apmStat = statSync4(fsApmDir);
|
|
3674
|
-
if (!apmStat.isDirectory()) {
|
|
3675
|
-
throw new Error(`[apm] \u8DEF\u5F84\u5DF2\u5B58\u5728\u4F46\u4E0D\u662F\u76EE\u5F55: ${apmDir}`);
|
|
3676
|
-
}
|
|
3677
|
-
const api = createApmApiClient(cfg);
|
|
3678
|
-
const { list } = await api.cli.listSkills({});
|
|
3679
|
-
if (syncAgentsGuide(apmDir)) {
|
|
3680
|
-
console.log("[apm] \u5DF2\u540C\u6B65 APM \u6307\u5357: .apm/AGENTS.md");
|
|
3681
|
-
}
|
|
3682
|
-
const rulesDir = join9(apmDir, "rules");
|
|
3683
|
-
const ruleNames = syncBaseRules(rulesDir);
|
|
3684
|
-
for (const name of ruleNames) {
|
|
3685
|
-
console.log(`[apm] \u5DF2\u540C\u6B65\u57FA\u7840\u89C4\u5219: rules/${name}`);
|
|
3686
|
-
}
|
|
3687
|
-
const skillsDir = join9(apmDir, "skills");
|
|
3688
|
-
mkdirSync7(toFsPath(skillsDir), { recursive: true });
|
|
3689
|
-
const baseNames = syncBaseSkills(skillsDir);
|
|
3690
|
-
for (const name of baseNames) {
|
|
3691
|
-
console.log(`[apm] \u5DF2\u540C\u6B65\u57FA\u7840\u6280\u80FD: skills/${name}/`);
|
|
3692
|
-
}
|
|
3693
|
-
const { written, skipped, removed } = syncSupplementarySkills(
|
|
3694
|
-
skillsDir,
|
|
3695
|
-
list
|
|
3696
|
-
);
|
|
3697
|
-
for (const name of written) {
|
|
3698
|
-
console.log(`[apm] \u5DF2\u5199\u5165\u8865\u5145\u6280\u80FD: skills/${name}/SKILL.md`);
|
|
3699
|
-
}
|
|
3700
|
-
for (const name of skipped) {
|
|
3701
|
-
console.log(
|
|
3702
|
-
`[apm] \u5DF2\u8DF3\u8FC7\u4E0E\u57FA\u7840\u6280\u80FD\u540C\u540D\u7684\u8865\u5145\u6280\u80FD: ${name}\uFF08\u4FDD\u7559\u6A21\u677F\u7248\u672C\uFF09`
|
|
3703
|
-
);
|
|
3704
|
-
}
|
|
3705
|
-
for (const name of removed) {
|
|
3706
|
-
console.log(`[apm] \u5DF2\u79FB\u9664\u5DF2\u4E0B\u7EBF\u7684\u8865\u5145\u6280\u80FD: skills/${name}/`);
|
|
3239
|
+
} catch {
|
|
3240
|
+
return "0.0.0";
|
|
3707
3241
|
}
|
|
3708
|
-
console.log(
|
|
3709
|
-
`[apm] \u540C\u6B65\u5B8C\u6210\uFF1A${ruleNames.length} \u4E2A\u57FA\u7840\u89C4\u5219\uFF0C${baseNames.length} \u4E2A\u57FA\u7840\u6280\u80FD\uFF0C${written.length} \u4E2A\u8865\u5145\u6280\u80FD`
|
|
3710
|
-
);
|
|
3711
3242
|
}
|
|
3712
3243
|
|
|
3713
3244
|
// src/commands/sync-session-attachments.ts
|
|
3714
|
-
import { existsSync as
|
|
3715
|
-
import { join as
|
|
3716
|
-
var
|
|
3245
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
3246
|
+
import { join as join6 } from "path";
|
|
3247
|
+
var MANIFEST_FILE = ".sync-manifest.json";
|
|
3717
3248
|
async function downloadAttachment(cfg, attachmentId) {
|
|
3718
3249
|
const base = cfg.baseUrl.trim().replace(/\/+$/, "");
|
|
3719
3250
|
const url = `${base}/api/v1/tasks/attachments/file?${new URLSearchParams({
|
|
@@ -3728,13 +3259,13 @@ async function downloadAttachment(cfg, attachmentId) {
|
|
|
3728
3259
|
return Buffer.from(await res.arrayBuffer());
|
|
3729
3260
|
}
|
|
3730
3261
|
function loadManifest(dir) {
|
|
3731
|
-
const path =
|
|
3732
|
-
if (!
|
|
3262
|
+
const path = join6(dir, MANIFEST_FILE);
|
|
3263
|
+
if (!existsSync6(path)) {
|
|
3733
3264
|
return { version: 1, attachments: {} };
|
|
3734
3265
|
}
|
|
3735
3266
|
try {
|
|
3736
3267
|
const parsed = JSON.parse(
|
|
3737
|
-
|
|
3268
|
+
readFileSync7(path, "utf8")
|
|
3738
3269
|
);
|
|
3739
3270
|
if (parsed?.version === 1 && parsed.attachments && typeof parsed.attachments === "object") {
|
|
3740
3271
|
return parsed;
|
|
@@ -3744,15 +3275,15 @@ function loadManifest(dir) {
|
|
|
3744
3275
|
return { version: 1, attachments: {} };
|
|
3745
3276
|
}
|
|
3746
3277
|
function saveManifest(dir, manifest) {
|
|
3747
|
-
|
|
3748
|
-
|
|
3278
|
+
writeFileSync6(
|
|
3279
|
+
join6(dir, MANIFEST_FILE),
|
|
3749
3280
|
`${JSON.stringify(manifest, null, 2)}
|
|
3750
3281
|
`,
|
|
3751
3282
|
"utf8"
|
|
3752
3283
|
);
|
|
3753
3284
|
}
|
|
3754
3285
|
function isAttachmentUpToDate(entry, item, dest) {
|
|
3755
|
-
if (!entry || !
|
|
3286
|
+
if (!entry || !existsSync6(dest)) return false;
|
|
3756
3287
|
if (entry.name !== item.name) return false;
|
|
3757
3288
|
const createdAt = item.createdAt ?? "";
|
|
3758
3289
|
return entry.createdAt === createdAt;
|
|
@@ -3767,7 +3298,7 @@ async function syncAttachmentsToDirectory(cfg, attachments, dir, logLabel) {
|
|
|
3767
3298
|
const nextManifest = { version: 1, attachments: {} };
|
|
3768
3299
|
const names = [];
|
|
3769
3300
|
for (const item of attachments) {
|
|
3770
|
-
const dest =
|
|
3301
|
+
const dest = join6(dir, item.name);
|
|
3771
3302
|
const entry = manifest.attachments[item.id];
|
|
3772
3303
|
const createdAt = item.createdAt ?? "";
|
|
3773
3304
|
names.push(item.name);
|
|
@@ -3777,7 +3308,7 @@ async function syncAttachmentsToDirectory(cfg, attachments, dir, logLabel) {
|
|
|
3777
3308
|
continue;
|
|
3778
3309
|
}
|
|
3779
3310
|
const buffer = await downloadAttachment(cfg, item.id);
|
|
3780
|
-
|
|
3311
|
+
writeFileSync6(dest, buffer);
|
|
3781
3312
|
nextManifest.attachments[item.id] = {
|
|
3782
3313
|
name: item.name,
|
|
3783
3314
|
createdAt
|
|
@@ -3797,16 +3328,283 @@ async function syncWebIdeAttachments(cfg, taskId, workdir, attachments) {
|
|
|
3797
3328
|
);
|
|
3798
3329
|
}
|
|
3799
3330
|
|
|
3331
|
+
// src/project-documents-sync.ts
|
|
3332
|
+
import {
|
|
3333
|
+
existsSync as existsSync7,
|
|
3334
|
+
readdirSync as readdirSync3,
|
|
3335
|
+
readFileSync as readFileSync8,
|
|
3336
|
+
rmSync as rmSync2,
|
|
3337
|
+
writeFileSync as writeFileSync7
|
|
3338
|
+
} from "fs";
|
|
3339
|
+
import { createHash } from "crypto";
|
|
3340
|
+
import { dirname as dirname6, join as join7, relative as relative2, sep } from "path";
|
|
3341
|
+
var MANIFEST_FILE2 = "manifest.json";
|
|
3342
|
+
function normalizeProjectIdForPath(projectId) {
|
|
3343
|
+
const id = projectId.trim();
|
|
3344
|
+
if (!id) {
|
|
3345
|
+
throw new Error("projectId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
3346
|
+
}
|
|
3347
|
+
if (id.includes("..") || id.includes("/") || id.includes("\\") || id.includes("\0")) {
|
|
3348
|
+
throw new Error(`\u975E\u6CD5 projectId: ${projectId}`);
|
|
3349
|
+
}
|
|
3350
|
+
return id;
|
|
3351
|
+
}
|
|
3352
|
+
function projectDocumentsDir(apmRoot, projectId) {
|
|
3353
|
+
const id = normalizeProjectIdForPath(projectId);
|
|
3354
|
+
return join7(apmRoot ?? workspaceApmDir(), "project", id);
|
|
3355
|
+
}
|
|
3356
|
+
function projectDocumentLocalPath(apmRoot, projectId, documentPath) {
|
|
3357
|
+
const normalized = normalizeLocalDocumentPath(documentPath);
|
|
3358
|
+
return join7(
|
|
3359
|
+
projectDocumentsDir(apmRoot, projectId),
|
|
3360
|
+
...normalized.split("/")
|
|
3361
|
+
);
|
|
3362
|
+
}
|
|
3363
|
+
function normalizeLocalDocumentPath(path) {
|
|
3364
|
+
const trimmed = path.trim().replace(/\\/g, "/");
|
|
3365
|
+
if (!trimmed || trimmed.startsWith("/") || /^[a-zA-Z]:/.test(trimmed)) {
|
|
3366
|
+
throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path}`);
|
|
3367
|
+
}
|
|
3368
|
+
const segments = trimmed.split("/").filter(Boolean);
|
|
3369
|
+
if (segments.some((segment) => segment === ".." || segment === ".")) {
|
|
3370
|
+
throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path}`);
|
|
3371
|
+
}
|
|
3372
|
+
return segments.join("/");
|
|
3373
|
+
}
|
|
3374
|
+
function hashLocalFileContent(content) {
|
|
3375
|
+
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
3376
|
+
}
|
|
3377
|
+
function readLocalManifest(apmRoot, projectId) {
|
|
3378
|
+
const manifestPath3 = join7(
|
|
3379
|
+
projectDocumentsDir(apmRoot, projectId),
|
|
3380
|
+
MANIFEST_FILE2
|
|
3381
|
+
);
|
|
3382
|
+
if (!existsSync7(manifestPath3)) {
|
|
3383
|
+
return null;
|
|
3384
|
+
}
|
|
3385
|
+
try {
|
|
3386
|
+
return JSON.parse(
|
|
3387
|
+
readFileSync8(manifestPath3, "utf8")
|
|
3388
|
+
);
|
|
3389
|
+
} catch {
|
|
3390
|
+
return null;
|
|
3391
|
+
}
|
|
3392
|
+
}
|
|
3393
|
+
function listLocalDocumentPaths(apmRoot, projectId) {
|
|
3394
|
+
const root = projectDocumentsDir(apmRoot, projectId);
|
|
3395
|
+
if (!existsSync7(root)) {
|
|
3396
|
+
return [];
|
|
3397
|
+
}
|
|
3398
|
+
const paths = [];
|
|
3399
|
+
const walk = (dir) => {
|
|
3400
|
+
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
3401
|
+
const abs = join7(dir, entry.name);
|
|
3402
|
+
if (entry.isDirectory()) {
|
|
3403
|
+
walk(abs);
|
|
3404
|
+
continue;
|
|
3405
|
+
}
|
|
3406
|
+
if (entry.isFile() && entry.name === MANIFEST_FILE2) {
|
|
3407
|
+
continue;
|
|
3408
|
+
}
|
|
3409
|
+
const rel = relative2(root, abs).split(sep).join("/");
|
|
3410
|
+
paths.push(rel);
|
|
3411
|
+
}
|
|
3412
|
+
};
|
|
3413
|
+
walk(root);
|
|
3414
|
+
return paths.sort();
|
|
3415
|
+
}
|
|
3416
|
+
function diffManifestPaths(remote, local) {
|
|
3417
|
+
const remoteMap = new Map(
|
|
3418
|
+
(remote?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
3419
|
+
);
|
|
3420
|
+
const localMap = new Map(
|
|
3421
|
+
(local?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
3422
|
+
);
|
|
3423
|
+
const download = [];
|
|
3424
|
+
for (const [path, hash] of remoteMap) {
|
|
3425
|
+
if (localMap.get(path) !== hash) {
|
|
3426
|
+
download.push(path);
|
|
3427
|
+
}
|
|
3428
|
+
}
|
|
3429
|
+
const deleteLocal = [];
|
|
3430
|
+
for (const path of localMap.keys()) {
|
|
3431
|
+
if (!remoteMap.has(path)) {
|
|
3432
|
+
deleteLocal.push(path);
|
|
3433
|
+
}
|
|
3434
|
+
}
|
|
3435
|
+
return { download, deleteLocal };
|
|
3436
|
+
}
|
|
3437
|
+
async function resolveProjectIdForSync(api, workdirPath, options) {
|
|
3438
|
+
const explicit = options?.projectId?.trim();
|
|
3439
|
+
if (explicit) {
|
|
3440
|
+
return { projectId: explicit, diagnostic: null };
|
|
3441
|
+
}
|
|
3442
|
+
try {
|
|
3443
|
+
const baseline = await api.cli.workspaceBaseline({ workdirPath });
|
|
3444
|
+
const projectId = baseline.projectId?.trim() || null;
|
|
3445
|
+
if (projectId) {
|
|
3446
|
+
return { projectId, diagnostic: null };
|
|
3447
|
+
}
|
|
3448
|
+
const projectIds = baseline.projectIds ?? [];
|
|
3449
|
+
if (projectIds.length === 0) {
|
|
3450
|
+
return {
|
|
3451
|
+
projectId: null,
|
|
3452
|
+
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
|
|
3453
|
+
\u8BF7\u5728\u5E73\u53F0\u5C06\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u5230\u552F\u4E00\u9879\u76EE\u3002`
|
|
3454
|
+
};
|
|
3455
|
+
}
|
|
3456
|
+
return {
|
|
3457
|
+
projectId: null,
|
|
3458
|
+
diagnostic: `\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u4E86 ${projectIds.length} \u4E2A\u9879\u76EE\uFF0C\u65E0\u6CD5\u786E\u5B9A\u540C\u6B65\u76EE\u6807\u3002
|
|
3459
|
+
\u8BF7\u786E\u4FDD\u8BE5\u5DE5\u4F5C\u7A7A\u95F4\u53EA\u5173\u8054\u4E00\u4E2A\u9879\u76EE\u540E\u518D\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002`
|
|
3460
|
+
};
|
|
3461
|
+
} catch (err) {
|
|
3462
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
3463
|
+
return {
|
|
3464
|
+
projectId: null,
|
|
3465
|
+
diagnostic: detail.replace(/^\[apm\]\s*/, "")
|
|
3466
|
+
};
|
|
3467
|
+
}
|
|
3468
|
+
}
|
|
3469
|
+
async function syncProjectDocumentsPull(workdirPath, apmDir, options) {
|
|
3470
|
+
const empty = {
|
|
3471
|
+
synced: false,
|
|
3472
|
+
projectId: null,
|
|
3473
|
+
downloaded: 0,
|
|
3474
|
+
deleted: 0
|
|
3475
|
+
};
|
|
3476
|
+
const cfg = await tryReadApmConfig();
|
|
3477
|
+
if (!cfg || !resolveApiKey(cfg)) {
|
|
3478
|
+
console.log(
|
|
3479
|
+
"[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"
|
|
3480
|
+
);
|
|
3481
|
+
return empty;
|
|
3482
|
+
}
|
|
3483
|
+
const api = createApmApiClient(cfg);
|
|
3484
|
+
const { projectId, diagnostic } = await resolveProjectIdForSync(
|
|
3485
|
+
api,
|
|
3486
|
+
workdirPath,
|
|
3487
|
+
options
|
|
3488
|
+
);
|
|
3489
|
+
if (!projectId) {
|
|
3490
|
+
console.log(`[apm] \u672A\u80FD\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002
|
|
3491
|
+
${diagnostic ?? ""}`);
|
|
3492
|
+
return empty;
|
|
3493
|
+
}
|
|
3494
|
+
const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
|
|
3495
|
+
const docsDir = projectDocumentsDir(targetApmDir, projectId);
|
|
3496
|
+
await ensureDirExists(docsDir);
|
|
3497
|
+
const { manifest: remoteManifest } = await api.cli.getProjectDocumentManifest(
|
|
3498
|
+
{ projectId }
|
|
3499
|
+
);
|
|
3500
|
+
if (!remoteManifest) {
|
|
3501
|
+
console.log(`[apm] \u9879\u76EE ${projectId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`);
|
|
3502
|
+
return { ...empty, projectId };
|
|
3503
|
+
}
|
|
3504
|
+
const localManifest = readLocalManifest(targetApmDir, projectId);
|
|
3505
|
+
const { download, deleteLocal } = diffManifestPaths(
|
|
3506
|
+
remoteManifest,
|
|
3507
|
+
localManifest
|
|
3508
|
+
);
|
|
3509
|
+
let downloaded = 0;
|
|
3510
|
+
if (download.length > 0) {
|
|
3511
|
+
const { list } = await api.cli.listProjectDocuments({
|
|
3512
|
+
projectId,
|
|
3513
|
+
paths: download.join(",")
|
|
3514
|
+
});
|
|
3515
|
+
for (const doc of list) {
|
|
3516
|
+
const absPath = toFsPath(
|
|
3517
|
+
projectDocumentLocalPath(targetApmDir, projectId, doc.path)
|
|
3518
|
+
);
|
|
3519
|
+
await ensureDirExists(dirname6(absPath));
|
|
3520
|
+
writeFileSync7(absPath, doc.content, "utf8");
|
|
3521
|
+
downloaded += 1;
|
|
3522
|
+
}
|
|
3523
|
+
}
|
|
3524
|
+
let deleted = 0;
|
|
3525
|
+
for (const path of deleteLocal) {
|
|
3526
|
+
const absPath = toFsPath(
|
|
3527
|
+
projectDocumentLocalPath(targetApmDir, projectId, path)
|
|
3528
|
+
);
|
|
3529
|
+
if (existsSync7(absPath)) {
|
|
3530
|
+
rmSync2(absPath, { force: true });
|
|
3531
|
+
deleted += 1;
|
|
3532
|
+
}
|
|
3533
|
+
}
|
|
3534
|
+
writeFileSync7(
|
|
3535
|
+
toFsPath(join7(docsDir, MANIFEST_FILE2)),
|
|
3536
|
+
`${JSON.stringify(remoteManifest, null, 2)}
|
|
3537
|
+
`,
|
|
3538
|
+
"utf8"
|
|
3539
|
+
);
|
|
3540
|
+
console.log(
|
|
3541
|
+
`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: projectId=${projectId} \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted} \u2192 .apm/project/${projectId}/`
|
|
3542
|
+
);
|
|
3543
|
+
return {
|
|
3544
|
+
synced: true,
|
|
3545
|
+
projectId,
|
|
3546
|
+
downloaded,
|
|
3547
|
+
deleted
|
|
3548
|
+
};
|
|
3549
|
+
}
|
|
3550
|
+
async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot, options) {
|
|
3551
|
+
const api = createApmApiClient(cfg);
|
|
3552
|
+
const { projectId } = await resolveProjectIdForSync(
|
|
3553
|
+
api,
|
|
3554
|
+
workdirPath,
|
|
3555
|
+
options
|
|
3556
|
+
);
|
|
3557
|
+
if (!projectId) {
|
|
3558
|
+
return 0;
|
|
3559
|
+
}
|
|
3560
|
+
const targetApmDir = apmRoot ?? workspaceApmDir(workdirPath);
|
|
3561
|
+
const localPaths = listLocalDocumentPaths(targetApmDir, projectId);
|
|
3562
|
+
if (localPaths.length === 0) {
|
|
3563
|
+
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u672C\u5730\u6587\u4EF6\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
3564
|
+
return 0;
|
|
3565
|
+
}
|
|
3566
|
+
const remoteManifest = (await api.cli.getProjectDocumentManifest({ projectId })).manifest ?? null;
|
|
3567
|
+
const remoteHashByPath = new Map(
|
|
3568
|
+
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
3569
|
+
);
|
|
3570
|
+
const remoteDescriptionByPath = new Map(
|
|
3571
|
+
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.description])
|
|
3572
|
+
);
|
|
3573
|
+
let synced = 0;
|
|
3574
|
+
for (const path of localPaths) {
|
|
3575
|
+
const absPath = toFsPath(
|
|
3576
|
+
projectDocumentLocalPath(targetApmDir, projectId, path)
|
|
3577
|
+
);
|
|
3578
|
+
const content = readFileSync8(absPath, "utf8");
|
|
3579
|
+
const contentHash = hashLocalFileContent(content);
|
|
3580
|
+
if (remoteHashByPath.get(path) === contentHash) {
|
|
3581
|
+
continue;
|
|
3582
|
+
}
|
|
3583
|
+
await api.cli.upsertProjectDocument({
|
|
3584
|
+
projectId,
|
|
3585
|
+
path,
|
|
3586
|
+
content,
|
|
3587
|
+
description: remoteDescriptionByPath.get(path) ?? void 0
|
|
3588
|
+
});
|
|
3589
|
+
synced += 1;
|
|
3590
|
+
console.log(`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: ${path}`);
|
|
3591
|
+
}
|
|
3592
|
+
if (synced === 0) {
|
|
3593
|
+
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
3594
|
+
}
|
|
3595
|
+
return synced;
|
|
3596
|
+
}
|
|
3597
|
+
|
|
3800
3598
|
// src/commands/connect/cli-version-sync.ts
|
|
3801
|
-
import { existsSync as
|
|
3802
|
-
import { join as
|
|
3599
|
+
import { existsSync as existsSync8, readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "fs";
|
|
3600
|
+
import { join as join8 } from "path";
|
|
3803
3601
|
var CLI_VERSION_FILE = ".cli-version.json";
|
|
3804
3602
|
function manifestPath2(apmDir) {
|
|
3805
|
-
return
|
|
3603
|
+
return join8(apmDir, CLI_VERSION_FILE);
|
|
3806
3604
|
}
|
|
3807
3605
|
function loadManifest2(apmDir) {
|
|
3808
3606
|
const path = toFsPath(manifestPath2(apmDir));
|
|
3809
|
-
if (!
|
|
3607
|
+
if (!existsSync8(path)) {
|
|
3810
3608
|
return null;
|
|
3811
3609
|
}
|
|
3812
3610
|
try {
|
|
@@ -3822,7 +3620,7 @@ function loadManifest2(apmDir) {
|
|
|
3822
3620
|
}
|
|
3823
3621
|
function saveManifest2(apmDir, cliVersion) {
|
|
3824
3622
|
const manifest = { version: 1, cliVersion };
|
|
3825
|
-
|
|
3623
|
+
writeFileSync8(
|
|
3826
3624
|
toFsPath(manifestPath2(apmDir)),
|
|
3827
3625
|
`${JSON.stringify(manifest, null, 2)}
|
|
3828
3626
|
`,
|
|
@@ -3928,7 +3726,9 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
|
|
|
3928
3726
|
await runPrepStep(
|
|
3929
3727
|
"\u521D\u59CB\u5316\u5DE5\u4F5C\u533A",
|
|
3930
3728
|
async () => {
|
|
3931
|
-
const { didInit } = await ensureWorkspaceInitialized(workdir
|
|
3729
|
+
const { didInit } = await ensureWorkspaceInitialized(workdir, {
|
|
3730
|
+
webide: true
|
|
3731
|
+
});
|
|
3932
3732
|
return didInit;
|
|
3933
3733
|
},
|
|
3934
3734
|
(didInit) => didInit ? "\u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A" : "\u5DE5\u4F5C\u533A\u5DF2\u5C31\u7EEA"
|
|
@@ -3937,9 +3737,9 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
|
|
|
3937
3737
|
if (shouldSyncSkillsForCliVersion(workdir, cliVersion)) {
|
|
3938
3738
|
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
3939
3739
|
await runPrepStep(
|
|
3940
|
-
"\u540C\u6B65
|
|
3740
|
+
"\u540C\u6B65 WebIDE \u89C4\u5219",
|
|
3941
3741
|
async () => {
|
|
3942
|
-
await
|
|
3742
|
+
await copyWebIdeTemplateFiles(workspaceApmDir(workdir), workdir);
|
|
3943
3743
|
markSkillsSyncedForCliVersion(workdir, cliVersion);
|
|
3944
3744
|
return cliVersion;
|
|
3945
3745
|
},
|
|
@@ -3947,7 +3747,7 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
|
|
|
3947
3747
|
);
|
|
3948
3748
|
} else {
|
|
3949
3749
|
eventSession.addCliStep(
|
|
3950
|
-
"\u540C\u6B65
|
|
3750
|
+
"\u540C\u6B65 WebIDE \u89C4\u5219",
|
|
3951
3751
|
`\u7248\u672C\u4E00\u81F4\uFF08${cliVersion}\uFF09\uFF0C\u8DF3\u8FC7`,
|
|
3952
3752
|
"skip"
|
|
3953
3753
|
);
|