ai-project-manage-cli 6.0.58 → 6.0.60
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 +312 -283
- package/package.json +1 -1
- package/template/project/.gitkeep +0 -0
package/dist/index.js
CHANGED
|
@@ -80,8 +80,8 @@ function buildAgentWsUrl(httpBase, apiKey) {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
// src/commands/init.ts
|
|
83
|
-
import { join as
|
|
84
|
-
import { readFileSync as
|
|
83
|
+
import { join as join5 } from "path";
|
|
84
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
85
85
|
|
|
86
86
|
// src/command-utils.ts
|
|
87
87
|
import {
|
|
@@ -149,13 +149,11 @@ var CLI_TEMPLATE_DIR = resolve2(__dirname, "../template");
|
|
|
149
149
|
function workspaceApmDir(cwd = resolveWorkdirPath()) {
|
|
150
150
|
return resolve2(resolve2(cwd), ".apm");
|
|
151
151
|
}
|
|
152
|
-
function
|
|
152
|
+
function isWorkspaceApmInitialized(workdir) {
|
|
153
153
|
const apmDir = workspaceApmDir(workdir);
|
|
154
154
|
const fsApmDir = toFsPath(apmDir);
|
|
155
155
|
if (!existsSync(fsApmDir)) {
|
|
156
|
-
|
|
157
|
-
`\u5DE5\u4F5C\u76EE\u5F55 ${workdir} \u4E0B\u672A\u68C0\u6D4B\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5728\u8BE5\u4ED3\u5E93\u6839\u76EE\u5F55\u6267\u884C apm init \u5B8C\u6210\u63A5\u5165\u3002`
|
|
158
|
-
);
|
|
156
|
+
return false;
|
|
159
157
|
}
|
|
160
158
|
const st = statSync(fsApmDir);
|
|
161
159
|
if (!st.isDirectory()) {
|
|
@@ -163,6 +161,7 @@ function assertWorkspaceApmDirExists(workdir) {
|
|
|
163
161
|
`\u5DE5\u4F5C\u76EE\u5F55 ${workdir} \u4E0B\u7684 .apm \u4E0D\u662F\u76EE\u5F55\uFF0C\u8BF7\u68C0\u67E5\u672C\u5730\u63A5\u5165\u72B6\u6001\u3002`
|
|
164
162
|
);
|
|
165
163
|
}
|
|
164
|
+
return readdirSync(fsApmDir).length > 0;
|
|
166
165
|
}
|
|
167
166
|
var APM_GITIGNORE_PATTERNS = [
|
|
168
167
|
/^\.apm\/?$/,
|
|
@@ -590,6 +589,218 @@ ${diagnostic ?? ""}
|
|
|
590
589
|
return { synced: true, repositoryId, configName: config.name };
|
|
591
590
|
}
|
|
592
591
|
|
|
592
|
+
// src/repository-project-documents-sync.ts
|
|
593
|
+
import {
|
|
594
|
+
existsSync as existsSync2,
|
|
595
|
+
readdirSync as readdirSync2,
|
|
596
|
+
readFileSync as readFileSync3,
|
|
597
|
+
rmSync,
|
|
598
|
+
writeFileSync as writeFileSync4
|
|
599
|
+
} from "fs";
|
|
600
|
+
import { createHash } from "crypto";
|
|
601
|
+
import { dirname as dirname2, join as join4, relative, sep } from "path";
|
|
602
|
+
var MANIFEST_FILE = "manifest.json";
|
|
603
|
+
function projectDocumentsDir(apmRoot) {
|
|
604
|
+
return join4(apmRoot ?? workspaceApmDir(), "project");
|
|
605
|
+
}
|
|
606
|
+
function projectDocumentLocalPath(apmRoot, documentPath) {
|
|
607
|
+
const normalized = normalizeLocalDocumentPath(documentPath);
|
|
608
|
+
return join4(projectDocumentsDir(apmRoot), ...normalized.split("/"));
|
|
609
|
+
}
|
|
610
|
+
function normalizeLocalDocumentPath(path10) {
|
|
611
|
+
const trimmed = path10.trim().replace(/\\/g, "/");
|
|
612
|
+
if (!trimmed || trimmed.startsWith("/") || /^[a-zA-Z]:/.test(trimmed)) {
|
|
613
|
+
throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path10}`);
|
|
614
|
+
}
|
|
615
|
+
const segments = trimmed.split("/").filter(Boolean);
|
|
616
|
+
if (segments.some((segment) => segment === ".." || segment === ".")) {
|
|
617
|
+
throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path10}`);
|
|
618
|
+
}
|
|
619
|
+
return segments.join("/");
|
|
620
|
+
}
|
|
621
|
+
function hashLocalFileContent(content) {
|
|
622
|
+
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
623
|
+
}
|
|
624
|
+
function readLocalManifest(apmRoot) {
|
|
625
|
+
const manifestPath2 = join4(projectDocumentsDir(apmRoot), MANIFEST_FILE);
|
|
626
|
+
if (!existsSync2(manifestPath2)) {
|
|
627
|
+
return null;
|
|
628
|
+
}
|
|
629
|
+
try {
|
|
630
|
+
return JSON.parse(
|
|
631
|
+
readFileSync3(manifestPath2, "utf8")
|
|
632
|
+
);
|
|
633
|
+
} catch {
|
|
634
|
+
return null;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
function listLocalDocumentPaths(apmRoot) {
|
|
638
|
+
const root = projectDocumentsDir(apmRoot);
|
|
639
|
+
if (!existsSync2(root)) {
|
|
640
|
+
return [];
|
|
641
|
+
}
|
|
642
|
+
const paths = [];
|
|
643
|
+
const walk = (dir) => {
|
|
644
|
+
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
645
|
+
const abs = join4(dir, entry.name);
|
|
646
|
+
if (entry.isDirectory()) {
|
|
647
|
+
walk(abs);
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
650
|
+
if (entry.isFile() && entry.name === MANIFEST_FILE) {
|
|
651
|
+
continue;
|
|
652
|
+
}
|
|
653
|
+
const rel = relative(root, abs).split(sep).join("/");
|
|
654
|
+
paths.push(rel);
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
walk(root);
|
|
658
|
+
return paths.sort();
|
|
659
|
+
}
|
|
660
|
+
function diffManifestPaths(remote, local) {
|
|
661
|
+
const remoteMap = new Map(
|
|
662
|
+
(remote?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
663
|
+
);
|
|
664
|
+
const localMap = new Map(
|
|
665
|
+
(local?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
666
|
+
);
|
|
667
|
+
const download = [];
|
|
668
|
+
for (const [path10, hash] of remoteMap) {
|
|
669
|
+
if (localMap.get(path10) !== hash) {
|
|
670
|
+
download.push(path10);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
const deleteLocal = [];
|
|
674
|
+
for (const path10 of localMap.keys()) {
|
|
675
|
+
if (!remoteMap.has(path10)) {
|
|
676
|
+
deleteLocal.push(path10);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
return { download, deleteLocal };
|
|
680
|
+
}
|
|
681
|
+
async function syncRepositoryProjectDocumentsPull(workdirPath, apmDir) {
|
|
682
|
+
const empty = {
|
|
683
|
+
synced: false,
|
|
684
|
+
repositoryId: null,
|
|
685
|
+
downloaded: 0,
|
|
686
|
+
deleted: 0
|
|
687
|
+
};
|
|
688
|
+
const cfg = await tryReadApmConfig();
|
|
689
|
+
if (!cfg || !resolveApiKey(cfg)) {
|
|
690
|
+
console.log(
|
|
691
|
+
"[apm] \u672A\u68C0\u6D4B\u5230\u767B\u5F55\u4FE1\u606F\uFF0C\u8DF3\u8FC7\u4ED3\u5E93\u9879\u76EE\u6587\u6863\u540C\u6B65\u3002\n[apm] \u8BF7\u5148\u6267\u884C apm login\u3002"
|
|
692
|
+
);
|
|
693
|
+
return empty;
|
|
694
|
+
}
|
|
695
|
+
const api = createApmApiClient(cfg);
|
|
696
|
+
const { repositoryId, diagnostic } = await resolveRepositoryIdForSync(
|
|
697
|
+
api,
|
|
698
|
+
workdirPath
|
|
699
|
+
);
|
|
700
|
+
if (!repositoryId) {
|
|
701
|
+
console.log(
|
|
702
|
+
`[apm] \u672A\u80FD\u540C\u6B65\u4ED3\u5E93\u9879\u76EE\u6587\u6863\u3002
|
|
703
|
+
${diagnostic ?? ""}`
|
|
704
|
+
);
|
|
705
|
+
return empty;
|
|
706
|
+
}
|
|
707
|
+
const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
|
|
708
|
+
const projectDir = projectDocumentsDir(targetApmDir);
|
|
709
|
+
await ensureDirExists(projectDir);
|
|
710
|
+
const { manifest: remoteManifest } = await api.cli.getRepositoryProjectDocumentManifest({ repositoryId });
|
|
711
|
+
if (!remoteManifest) {
|
|
712
|
+
console.log(
|
|
713
|
+
`[apm] \u4ED3\u5E93 ${repositoryId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`
|
|
714
|
+
);
|
|
715
|
+
return { ...empty, repositoryId };
|
|
716
|
+
}
|
|
717
|
+
const localManifest = readLocalManifest(targetApmDir);
|
|
718
|
+
const { download, deleteLocal } = diffManifestPaths(
|
|
719
|
+
remoteManifest,
|
|
720
|
+
localManifest
|
|
721
|
+
);
|
|
722
|
+
let downloaded = 0;
|
|
723
|
+
if (download.length > 0) {
|
|
724
|
+
const { list } = await api.cli.listRepositoryProjectDocuments({
|
|
725
|
+
repositoryId,
|
|
726
|
+
paths: download.join(",")
|
|
727
|
+
});
|
|
728
|
+
for (const doc of list) {
|
|
729
|
+
const absPath = toFsPath(projectDocumentLocalPath(targetApmDir, doc.path));
|
|
730
|
+
await ensureDirExists(dirname2(absPath));
|
|
731
|
+
writeFileSync4(absPath, doc.content, "utf8");
|
|
732
|
+
downloaded += 1;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
let deleted = 0;
|
|
736
|
+
for (const path10 of deleteLocal) {
|
|
737
|
+
const absPath = toFsPath(projectDocumentLocalPath(targetApmDir, path10));
|
|
738
|
+
if (existsSync2(absPath)) {
|
|
739
|
+
rmSync(absPath, { force: true });
|
|
740
|
+
deleted += 1;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
writeFileSync4(
|
|
744
|
+
toFsPath(join4(projectDir, MANIFEST_FILE)),
|
|
745
|
+
`${JSON.stringify(remoteManifest, null, 2)}
|
|
746
|
+
`,
|
|
747
|
+
"utf8"
|
|
748
|
+
);
|
|
749
|
+
console.log(
|
|
750
|
+
`[apm] \u5DF2\u540C\u6B65\u4ED3\u5E93\u9879\u76EE\u6587\u6863: \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted}`
|
|
751
|
+
);
|
|
752
|
+
return {
|
|
753
|
+
synced: true,
|
|
754
|
+
repositoryId,
|
|
755
|
+
downloaded,
|
|
756
|
+
deleted
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
async function syncRepositoryProjectDocumentsPush(cfg, workdirPath, apmRoot) {
|
|
760
|
+
const api = createApmApiClient(cfg);
|
|
761
|
+
const { repositoryId } = await resolveRepositoryIdForSync(api, workdirPath);
|
|
762
|
+
if (!repositoryId) {
|
|
763
|
+
return 0;
|
|
764
|
+
}
|
|
765
|
+
const targetApmDir = apmRoot ?? workspaceApmDir(workdirPath);
|
|
766
|
+
const localPaths = listLocalDocumentPaths(targetApmDir);
|
|
767
|
+
if (localPaths.length === 0) {
|
|
768
|
+
console.log("[apm] \u4ED3\u5E93\u9879\u76EE\u6587\u6863\u65E0\u672C\u5730\u6587\u4EF6\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
769
|
+
return 0;
|
|
770
|
+
}
|
|
771
|
+
const remoteManifest = (await api.cli.getRepositoryProjectDocumentManifest({ repositoryId })).manifest ?? null;
|
|
772
|
+
const remoteHashByPath = new Map(
|
|
773
|
+
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
774
|
+
);
|
|
775
|
+
const remoteDescriptionByPath = new Map(
|
|
776
|
+
(remoteManifest?.documents ?? []).map((doc) => [
|
|
777
|
+
doc.path,
|
|
778
|
+
doc.description
|
|
779
|
+
])
|
|
780
|
+
);
|
|
781
|
+
let synced = 0;
|
|
782
|
+
for (const path10 of localPaths) {
|
|
783
|
+
const absPath = toFsPath(projectDocumentLocalPath(targetApmDir, path10));
|
|
784
|
+
const content = readFileSync3(absPath, "utf8");
|
|
785
|
+
const contentHash = hashLocalFileContent(content);
|
|
786
|
+
if (remoteHashByPath.get(path10) === contentHash) {
|
|
787
|
+
continue;
|
|
788
|
+
}
|
|
789
|
+
await api.cli.upsertRepositoryProjectDocument({
|
|
790
|
+
repositoryId,
|
|
791
|
+
path: path10,
|
|
792
|
+
content,
|
|
793
|
+
description: remoteDescriptionByPath.get(path10) ?? void 0
|
|
794
|
+
});
|
|
795
|
+
synced += 1;
|
|
796
|
+
console.log(`[apm] \u5DF2\u540C\u6B65\u4ED3\u5E93\u9879\u76EE\u6587\u6863: ${path10}`);
|
|
797
|
+
}
|
|
798
|
+
if (synced === 0) {
|
|
799
|
+
console.log("[apm] \u4ED3\u5E93\u9879\u76EE\u6587\u6863\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
800
|
+
}
|
|
801
|
+
return synced;
|
|
802
|
+
}
|
|
803
|
+
|
|
593
804
|
// src/git-utils.ts
|
|
594
805
|
import { execFile as execFile2 } from "child_process";
|
|
595
806
|
import { promisify as promisify2 } from "util";
|
|
@@ -652,8 +863,13 @@ async function commitAndPushGitignore(workdir) {
|
|
|
652
863
|
}
|
|
653
864
|
|
|
654
865
|
// src/commands/init.ts
|
|
655
|
-
async function
|
|
656
|
-
|
|
866
|
+
async function ensureWorkspaceInitialized(workdir, options) {
|
|
867
|
+
if (isWorkspaceApmInitialized(workdir)) {
|
|
868
|
+
return { didInit: false };
|
|
869
|
+
}
|
|
870
|
+
console.log(
|
|
871
|
+
`[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`
|
|
872
|
+
);
|
|
657
873
|
await ensureWorkspaceApmDirForInit(workdir);
|
|
658
874
|
if (ensureApmGitignoredInRepo(workdir)) {
|
|
659
875
|
console.log("[apm] \u5DF2\u5728 .gitignore \u4E2D\u6DFB\u52A0 **/.apm/**");
|
|
@@ -662,13 +878,14 @@ async function runInit(name) {
|
|
|
662
878
|
const apmDir = workspaceApmDir(workdir);
|
|
663
879
|
await copyTemplateFiles(apmDir, workdir);
|
|
664
880
|
const syncResult = await syncRemoteDeploymentConfig(workdir, apmDir);
|
|
665
|
-
|
|
881
|
+
await syncRepositoryProjectDocumentsPull(workdir, apmDir);
|
|
882
|
+
const trimmedName = options?.name?.trim();
|
|
666
883
|
if (trimmedName) {
|
|
667
|
-
const apmConfigPath = toFsPath(
|
|
668
|
-
const config =
|
|
884
|
+
const apmConfigPath = toFsPath(join5(apmDir, "apm.config.json"));
|
|
885
|
+
const config = readFileSync4(apmConfigPath, "utf8");
|
|
669
886
|
const configJson = JSON.parse(config);
|
|
670
887
|
configJson.name = trimmedName;
|
|
671
|
-
|
|
888
|
+
writeFileSync5(
|
|
672
889
|
apmConfigPath,
|
|
673
890
|
`${JSON.stringify(configJson, null, 2)}
|
|
674
891
|
`,
|
|
@@ -676,8 +893,21 @@ async function runInit(name) {
|
|
|
676
893
|
);
|
|
677
894
|
}
|
|
678
895
|
console.log(`[apm] \u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A\uFF1A${apmDir}`);
|
|
896
|
+
return { didInit: true, syncResult };
|
|
897
|
+
}
|
|
898
|
+
async function runInit(name) {
|
|
899
|
+
const workdir = resolveWorkdirPath();
|
|
900
|
+
await ensureWorkspaceApmDirForInit(workdir);
|
|
901
|
+
const { didInit, syncResult } = await ensureWorkspaceInitialized(workdir, {
|
|
902
|
+
name
|
|
903
|
+
});
|
|
904
|
+
if (!didInit) {
|
|
905
|
+
throw new Error(
|
|
906
|
+
"[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"
|
|
907
|
+
);
|
|
908
|
+
}
|
|
679
909
|
console.log(`[apm] \u5DE5\u4F5C\u76EE\u5F55\u8DEF\u5F84\uFF1A${workdir}`);
|
|
680
|
-
if (!syncResult.synced) {
|
|
910
|
+
if (syncResult && !syncResult.synced) {
|
|
681
911
|
console.log(
|
|
682
912
|
"[apm] \u5F53\u524D .apm/apm.config.json \u4E0E .apm/deploy/README.md \u4E3A\u6A21\u677F\u9ED8\u8BA4\u503C\uFF1B\u5B8C\u6210\u5E73\u53F0\u767B\u8BB0\u540E\u6267\u884C apm sync-deploy-config"
|
|
683
913
|
);
|
|
@@ -686,7 +916,7 @@ async function runInit(name) {
|
|
|
686
916
|
}
|
|
687
917
|
|
|
688
918
|
// src/commands/login.ts
|
|
689
|
-
import { existsSync as
|
|
919
|
+
import { existsSync as existsSync3 } from "fs";
|
|
690
920
|
import { ApiError } from "listpage-http";
|
|
691
921
|
async function runLogin(opts) {
|
|
692
922
|
const baseUrl = (opts.server?.trim() || process.env.AI_PM_SERVER?.trim() || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
@@ -741,7 +971,7 @@ async function runLogin(opts) {
|
|
|
741
971
|
);
|
|
742
972
|
const workdir = resolveWorkdirPath();
|
|
743
973
|
const apmDir = workspaceApmDir(workdir);
|
|
744
|
-
if (
|
|
974
|
+
if (existsSync3(apmDir)) {
|
|
745
975
|
await syncRemoteDeploymentConfig(workdir, apmDir);
|
|
746
976
|
}
|
|
747
977
|
}
|
|
@@ -1144,9 +1374,9 @@ function formatSessionMessagesXml(sessionId, messages) {
|
|
|
1144
1374
|
}
|
|
1145
1375
|
|
|
1146
1376
|
// src/commands/sync-session-attachments.ts
|
|
1147
|
-
import { existsSync as
|
|
1148
|
-
import { join as
|
|
1149
|
-
var
|
|
1377
|
+
import { existsSync as existsSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync6 } from "fs";
|
|
1378
|
+
import { join as join6 } from "path";
|
|
1379
|
+
var MANIFEST_FILE2 = ".sync-manifest.json";
|
|
1150
1380
|
async function downloadAttachment(cfg, attachmentId) {
|
|
1151
1381
|
const base = cfg.baseUrl.trim().replace(/\/+$/, "");
|
|
1152
1382
|
const url = `${base}/api/v1/tasks/attachments/file?${new URLSearchParams({ attachmentId })}`;
|
|
@@ -1159,13 +1389,13 @@ async function downloadAttachment(cfg, attachmentId) {
|
|
|
1159
1389
|
return Buffer.from(await res.arrayBuffer());
|
|
1160
1390
|
}
|
|
1161
1391
|
function loadManifest(dir) {
|
|
1162
|
-
const path10 =
|
|
1163
|
-
if (!
|
|
1392
|
+
const path10 = join6(dir, MANIFEST_FILE2);
|
|
1393
|
+
if (!existsSync4(path10)) {
|
|
1164
1394
|
return { version: 1, attachments: {} };
|
|
1165
1395
|
}
|
|
1166
1396
|
try {
|
|
1167
1397
|
const parsed = JSON.parse(
|
|
1168
|
-
|
|
1398
|
+
readFileSync5(path10, "utf8")
|
|
1169
1399
|
);
|
|
1170
1400
|
if (parsed?.version === 1 && parsed.attachments && typeof parsed.attachments === "object") {
|
|
1171
1401
|
return parsed;
|
|
@@ -1175,21 +1405,21 @@ function loadManifest(dir) {
|
|
|
1175
1405
|
return { version: 1, attachments: {} };
|
|
1176
1406
|
}
|
|
1177
1407
|
function saveManifest(dir, manifest) {
|
|
1178
|
-
|
|
1179
|
-
|
|
1408
|
+
writeFileSync6(
|
|
1409
|
+
join6(dir, MANIFEST_FILE2),
|
|
1180
1410
|
`${JSON.stringify(manifest, null, 2)}
|
|
1181
1411
|
`,
|
|
1182
1412
|
"utf8"
|
|
1183
1413
|
);
|
|
1184
1414
|
}
|
|
1185
1415
|
function isAttachmentUpToDate(entry, item, dest) {
|
|
1186
|
-
if (!entry || !
|
|
1416
|
+
if (!entry || !existsSync4(dest)) return false;
|
|
1187
1417
|
if (entry.name !== item.name) return false;
|
|
1188
1418
|
const createdAt = item.createdAt ?? "";
|
|
1189
1419
|
return entry.createdAt === createdAt;
|
|
1190
1420
|
}
|
|
1191
1421
|
async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
|
|
1192
|
-
const dir =
|
|
1422
|
+
const dir = join6(sessionDir(sessionId, apmRoot), SESSION_ATTACHMENTS_SUBDIR);
|
|
1193
1423
|
await ensureDirExists(dir);
|
|
1194
1424
|
if (attachments.length === 0) {
|
|
1195
1425
|
saveManifest(dir, { version: 1, attachments: {} });
|
|
@@ -1198,7 +1428,7 @@ async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
|
|
|
1198
1428
|
const manifest = loadManifest(dir);
|
|
1199
1429
|
const nextManifest = { version: 1, attachments: {} };
|
|
1200
1430
|
for (const item of attachments) {
|
|
1201
|
-
const dest =
|
|
1431
|
+
const dest = join6(dir, item.name);
|
|
1202
1432
|
const entry = manifest.attachments[item.id];
|
|
1203
1433
|
const createdAt = item.createdAt ?? "";
|
|
1204
1434
|
if (isAttachmentUpToDate(entry, item, dest)) {
|
|
@@ -1209,7 +1439,7 @@ async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
|
|
|
1209
1439
|
continue;
|
|
1210
1440
|
}
|
|
1211
1441
|
const buffer = await downloadAttachment(cfg, item.id);
|
|
1212
|
-
|
|
1442
|
+
writeFileSync6(dest, buffer);
|
|
1213
1443
|
nextManifest.attachments[item.id] = {
|
|
1214
1444
|
name: item.name,
|
|
1215
1445
|
createdAt
|
|
@@ -1220,46 +1450,46 @@ async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
|
|
|
1220
1450
|
}
|
|
1221
1451
|
|
|
1222
1452
|
// src/rules-sync.ts
|
|
1223
|
-
import { basename as basename2, extname as extname2, join as
|
|
1224
|
-
import { existsSync as
|
|
1453
|
+
import { basename as basename2, extname as extname2, join as join8 } from "path";
|
|
1454
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "fs";
|
|
1225
1455
|
|
|
1226
1456
|
// src/skills-sync.ts
|
|
1227
1457
|
import {
|
|
1228
1458
|
copyFileSync as copyFileSync2,
|
|
1229
1459
|
cpSync,
|
|
1230
|
-
existsSync as
|
|
1460
|
+
existsSync as existsSync5,
|
|
1231
1461
|
mkdirSync as mkdirSync3,
|
|
1232
|
-
readdirSync as
|
|
1233
|
-
rmSync,
|
|
1462
|
+
readdirSync as readdirSync3,
|
|
1463
|
+
rmSync as rmSync2,
|
|
1234
1464
|
statSync as statSync2,
|
|
1235
|
-
writeFileSync as
|
|
1465
|
+
writeFileSync as writeFileSync7
|
|
1236
1466
|
} from "fs";
|
|
1237
|
-
import { join as
|
|
1238
|
-
var AGENTS_TEMPLATE_PATH =
|
|
1239
|
-
var BASE_SKILLS_TEMPLATE_DIR =
|
|
1240
|
-
var BASE_RULES_TEMPLATE_DIR =
|
|
1467
|
+
import { join as join7 } from "path";
|
|
1468
|
+
var AGENTS_TEMPLATE_PATH = join7(CLI_TEMPLATE_DIR, "AGENTS.md");
|
|
1469
|
+
var BASE_SKILLS_TEMPLATE_DIR = join7(CLI_TEMPLATE_DIR, "skills");
|
|
1470
|
+
var BASE_RULES_TEMPLATE_DIR = join7(CLI_TEMPLATE_DIR, "rules");
|
|
1241
1471
|
function sanitizeSkillDirName(name) {
|
|
1242
1472
|
const trimmed = name.trim();
|
|
1243
1473
|
if (!trimmed) return "skill";
|
|
1244
1474
|
return trimmed.replace(/[/\\:*?"<>|]/g, "_");
|
|
1245
1475
|
}
|
|
1246
1476
|
function listBaseSkillDirNames() {
|
|
1247
|
-
if (!
|
|
1248
|
-
return
|
|
1249
|
-
const path10 =
|
|
1477
|
+
if (!existsSync5(BASE_SKILLS_TEMPLATE_DIR)) return [];
|
|
1478
|
+
return readdirSync3(BASE_SKILLS_TEMPLATE_DIR).filter((name) => {
|
|
1479
|
+
const path10 = join7(BASE_SKILLS_TEMPLATE_DIR, name);
|
|
1250
1480
|
return statSync2(path10).isDirectory();
|
|
1251
1481
|
});
|
|
1252
1482
|
}
|
|
1253
1483
|
function syncAgentsGuide(apmDir) {
|
|
1254
|
-
if (!
|
|
1484
|
+
if (!existsSync5(AGENTS_TEMPLATE_PATH)) return false;
|
|
1255
1485
|
mkdirSync3(apmDir, { recursive: true });
|
|
1256
|
-
copyFileSync2(AGENTS_TEMPLATE_PATH,
|
|
1486
|
+
copyFileSync2(AGENTS_TEMPLATE_PATH, join7(apmDir, "AGENTS.md"));
|
|
1257
1487
|
return true;
|
|
1258
1488
|
}
|
|
1259
1489
|
function listBaseRuleFileNames() {
|
|
1260
|
-
if (!
|
|
1261
|
-
return
|
|
1262
|
-
const path10 =
|
|
1490
|
+
if (!existsSync5(BASE_RULES_TEMPLATE_DIR)) return [];
|
|
1491
|
+
return readdirSync3(BASE_RULES_TEMPLATE_DIR).filter((name) => {
|
|
1492
|
+
const path10 = join7(BASE_RULES_TEMPLATE_DIR, name);
|
|
1263
1493
|
return statSync2(path10).isFile();
|
|
1264
1494
|
});
|
|
1265
1495
|
}
|
|
@@ -1267,8 +1497,8 @@ function syncBaseRules(rulesDir) {
|
|
|
1267
1497
|
mkdirSync3(rulesDir, { recursive: true });
|
|
1268
1498
|
const names = listBaseRuleFileNames();
|
|
1269
1499
|
for (const name of names) {
|
|
1270
|
-
const src =
|
|
1271
|
-
const dest =
|
|
1500
|
+
const src = join7(BASE_RULES_TEMPLATE_DIR, name);
|
|
1501
|
+
const dest = join7(rulesDir, name);
|
|
1272
1502
|
copyFileSync2(src, dest);
|
|
1273
1503
|
}
|
|
1274
1504
|
return names;
|
|
@@ -1277,8 +1507,8 @@ function syncBaseSkills(skillsDir) {
|
|
|
1277
1507
|
mkdirSync3(skillsDir, { recursive: true });
|
|
1278
1508
|
const names = listBaseSkillDirNames();
|
|
1279
1509
|
for (const name of names) {
|
|
1280
|
-
const src =
|
|
1281
|
-
const dest =
|
|
1510
|
+
const src = join7(BASE_SKILLS_TEMPLATE_DIR, name);
|
|
1511
|
+
const dest = join7(skillsDir, name);
|
|
1282
1512
|
cpSync(src, dest, { recursive: true, force: true });
|
|
1283
1513
|
}
|
|
1284
1514
|
return names;
|
|
@@ -1295,26 +1525,26 @@ function syncSupplementarySkills(skillsDir, list) {
|
|
|
1295
1525
|
skipped.push(dirName);
|
|
1296
1526
|
continue;
|
|
1297
1527
|
}
|
|
1298
|
-
const skillDir =
|
|
1528
|
+
const skillDir = join7(skillsDir, dirName);
|
|
1299
1529
|
mkdirSync3(skillDir, { recursive: true });
|
|
1300
|
-
|
|
1530
|
+
writeFileSync7(join7(skillDir, "SKILL.md"), skill.content ?? "", "utf8");
|
|
1301
1531
|
written.push(dirName);
|
|
1302
1532
|
}
|
|
1303
1533
|
const removed = [];
|
|
1304
|
-
if (!
|
|
1305
|
-
for (const entry of
|
|
1306
|
-
const full =
|
|
1534
|
+
if (!existsSync5(skillsDir)) return { written, skipped, removed };
|
|
1535
|
+
for (const entry of readdirSync3(skillsDir)) {
|
|
1536
|
+
const full = join7(skillsDir, entry);
|
|
1307
1537
|
if (!statSync2(full).isDirectory()) continue;
|
|
1308
1538
|
if (baseNames.has(entry)) continue;
|
|
1309
1539
|
if (apiDirNames.has(entry)) continue;
|
|
1310
|
-
|
|
1540
|
+
rmSync2(full, { recursive: true, force: true });
|
|
1311
1541
|
removed.push(entry);
|
|
1312
1542
|
}
|
|
1313
1543
|
return { written, skipped, removed };
|
|
1314
1544
|
}
|
|
1315
1545
|
|
|
1316
1546
|
// src/rules-sync.ts
|
|
1317
|
-
var
|
|
1547
|
+
var MANIFEST_FILE3 = ".rules-sync-manifest.json";
|
|
1318
1548
|
function ruleLocalFileName(ruleName) {
|
|
1319
1549
|
const trimmed = ruleName.trim();
|
|
1320
1550
|
if (!trimmed) return "rule.md";
|
|
@@ -1323,13 +1553,13 @@ function ruleLocalFileName(ruleName) {
|
|
|
1323
1553
|
return `${sanitized}.md`;
|
|
1324
1554
|
}
|
|
1325
1555
|
function loadManifest2(rulesDir) {
|
|
1326
|
-
const path10 =
|
|
1327
|
-
if (!
|
|
1556
|
+
const path10 = join8(rulesDir, MANIFEST_FILE3);
|
|
1557
|
+
if (!existsSync6(toFsPath(path10))) {
|
|
1328
1558
|
return { version: 1, rules: {} };
|
|
1329
1559
|
}
|
|
1330
1560
|
try {
|
|
1331
1561
|
const parsed = JSON.parse(
|
|
1332
|
-
|
|
1562
|
+
readFileSync6(toFsPath(path10), "utf8")
|
|
1333
1563
|
);
|
|
1334
1564
|
if (parsed?.version === 1 && parsed.rules && typeof parsed.rules === "object") {
|
|
1335
1565
|
return parsed;
|
|
@@ -1339,8 +1569,8 @@ function loadManifest2(rulesDir) {
|
|
|
1339
1569
|
return { version: 1, rules: {} };
|
|
1340
1570
|
}
|
|
1341
1571
|
function saveManifest2(rulesDir, manifest) {
|
|
1342
|
-
|
|
1343
|
-
toFsPath(
|
|
1572
|
+
writeFileSync8(
|
|
1573
|
+
toFsPath(join8(rulesDir, MANIFEST_FILE3)),
|
|
1344
1574
|
`${JSON.stringify(manifest, null, 2)}
|
|
1345
1575
|
`,
|
|
1346
1576
|
"utf8"
|
|
@@ -1350,18 +1580,18 @@ function isBaseRuleFileName(fileName) {
|
|
|
1350
1580
|
return listBaseRuleFileNames().includes(basename2(fileName));
|
|
1351
1581
|
}
|
|
1352
1582
|
function isRuleUpToDate(entry, rule, dest) {
|
|
1353
|
-
if (!entry || !
|
|
1583
|
+
if (!entry || !existsSync6(toFsPath(dest))) return false;
|
|
1354
1584
|
if (entry.fileName !== ruleLocalFileName(rule.name)) return false;
|
|
1355
1585
|
const updatedAt = rule.updatedAt ?? "";
|
|
1356
1586
|
if (entry.updatedAt !== updatedAt) return false;
|
|
1357
|
-
const localContent =
|
|
1587
|
+
const localContent = readFileSync6(toFsPath(dest), "utf8");
|
|
1358
1588
|
return localContent === (rule.content ?? "");
|
|
1359
1589
|
}
|
|
1360
1590
|
async function syncPlatformRules(cfg, sessionId, workdirPath, apmRoot) {
|
|
1361
1591
|
const api = createApmApiClient(cfg);
|
|
1362
1592
|
const baseline = await api.cli.branchBaseline({ sessionId, workdirPath });
|
|
1363
1593
|
const repositoryId = baseline.repositoryId;
|
|
1364
|
-
const rulesDir =
|
|
1594
|
+
const rulesDir = join8(apmRoot ?? workspaceApmDir(workdirPath), "rules");
|
|
1365
1595
|
await ensureDirExists(rulesDir);
|
|
1366
1596
|
if (!repositoryId) {
|
|
1367
1597
|
console.log(
|
|
@@ -1378,7 +1608,7 @@ async function syncPlatformRules(cfg, sessionId, workdirPath, apmRoot) {
|
|
|
1378
1608
|
for (const rule of list) {
|
|
1379
1609
|
remoteIds.add(rule.id);
|
|
1380
1610
|
const fileName = ruleLocalFileName(rule.name);
|
|
1381
|
-
const dest =
|
|
1611
|
+
const dest = join8(rulesDir, fileName);
|
|
1382
1612
|
const entry = manifest.rules[rule.id];
|
|
1383
1613
|
const updatedAt = rule.updatedAt ?? "";
|
|
1384
1614
|
if (isRuleUpToDate(entry, rule, dest)) {
|
|
@@ -1387,7 +1617,7 @@ async function syncPlatformRules(cfg, sessionId, workdirPath, apmRoot) {
|
|
|
1387
1617
|
console.log(`[apm] \u89C4\u5219\u65E0\u53D8\u5316\uFF0C\u5DF2\u8DF3\u8FC7: rules/${fileName}`);
|
|
1388
1618
|
continue;
|
|
1389
1619
|
}
|
|
1390
|
-
|
|
1620
|
+
writeFileSync8(toFsPath(dest), rule.content ?? "", "utf8");
|
|
1391
1621
|
nextManifest.rules[rule.id] = { fileName, updatedAt };
|
|
1392
1622
|
written.push(fileName);
|
|
1393
1623
|
console.log(`[apm] \u5DF2\u540C\u6B65\u5E73\u53F0\u89C4\u5219: rules/${fileName}`);
|
|
@@ -1396,9 +1626,9 @@ async function syncPlatformRules(cfg, sessionId, workdirPath, apmRoot) {
|
|
|
1396
1626
|
for (const [ruleId, entry] of Object.entries(manifest.rules)) {
|
|
1397
1627
|
if (remoteIds.has(ruleId)) continue;
|
|
1398
1628
|
if (isBaseRuleFileName(entry.fileName)) continue;
|
|
1399
|
-
const dest =
|
|
1400
|
-
if (
|
|
1401
|
-
|
|
1629
|
+
const dest = join8(rulesDir, entry.fileName);
|
|
1630
|
+
if (existsSync6(toFsPath(dest))) {
|
|
1631
|
+
rmSync3(toFsPath(dest), { force: true });
|
|
1402
1632
|
}
|
|
1403
1633
|
removed.push(entry.fileName);
|
|
1404
1634
|
console.log(`[apm] \u5DF2\u79FB\u9664\u5DF2\u4E0B\u7EBF\u7684\u5E73\u53F0\u89C4\u5219: rules/${entry.fileName}`);
|
|
@@ -1407,218 +1637,6 @@ async function syncPlatformRules(cfg, sessionId, workdirPath, apmRoot) {
|
|
|
1407
1637
|
return { written, skipped, removed, repositoryId };
|
|
1408
1638
|
}
|
|
1409
1639
|
|
|
1410
|
-
// src/repository-project-documents-sync.ts
|
|
1411
|
-
import {
|
|
1412
|
-
existsSync as existsSync6,
|
|
1413
|
-
readdirSync as readdirSync3,
|
|
1414
|
-
readFileSync as readFileSync6,
|
|
1415
|
-
rmSync as rmSync3,
|
|
1416
|
-
writeFileSync as writeFileSync8
|
|
1417
|
-
} from "fs";
|
|
1418
|
-
import { createHash } from "crypto";
|
|
1419
|
-
import { dirname as dirname2, join as join8, relative, sep } from "path";
|
|
1420
|
-
var MANIFEST_FILE3 = "manifest.json";
|
|
1421
|
-
function projectDocumentsDir(apmRoot) {
|
|
1422
|
-
return join8(apmRoot ?? workspaceApmDir(), "project");
|
|
1423
|
-
}
|
|
1424
|
-
function projectDocumentLocalPath(apmRoot, documentPath) {
|
|
1425
|
-
const normalized = normalizeLocalDocumentPath(documentPath);
|
|
1426
|
-
return join8(projectDocumentsDir(apmRoot), ...normalized.split("/"));
|
|
1427
|
-
}
|
|
1428
|
-
function normalizeLocalDocumentPath(path10) {
|
|
1429
|
-
const trimmed = path10.trim().replace(/\\/g, "/");
|
|
1430
|
-
if (!trimmed || trimmed.startsWith("/") || /^[a-zA-Z]:/.test(trimmed)) {
|
|
1431
|
-
throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path10}`);
|
|
1432
|
-
}
|
|
1433
|
-
const segments = trimmed.split("/").filter(Boolean);
|
|
1434
|
-
if (segments.some((segment) => segment === ".." || segment === ".")) {
|
|
1435
|
-
throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path10}`);
|
|
1436
|
-
}
|
|
1437
|
-
return segments.join("/");
|
|
1438
|
-
}
|
|
1439
|
-
function hashLocalFileContent(content) {
|
|
1440
|
-
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
1441
|
-
}
|
|
1442
|
-
function readLocalManifest(apmRoot) {
|
|
1443
|
-
const manifestPath2 = join8(projectDocumentsDir(apmRoot), MANIFEST_FILE3);
|
|
1444
|
-
if (!existsSync6(manifestPath2)) {
|
|
1445
|
-
return null;
|
|
1446
|
-
}
|
|
1447
|
-
try {
|
|
1448
|
-
return JSON.parse(
|
|
1449
|
-
readFileSync6(manifestPath2, "utf8")
|
|
1450
|
-
);
|
|
1451
|
-
} catch {
|
|
1452
|
-
return null;
|
|
1453
|
-
}
|
|
1454
|
-
}
|
|
1455
|
-
function listLocalDocumentPaths(apmRoot) {
|
|
1456
|
-
const root = projectDocumentsDir(apmRoot);
|
|
1457
|
-
if (!existsSync6(root)) {
|
|
1458
|
-
return [];
|
|
1459
|
-
}
|
|
1460
|
-
const paths = [];
|
|
1461
|
-
const walk = (dir) => {
|
|
1462
|
-
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
1463
|
-
const abs = join8(dir, entry.name);
|
|
1464
|
-
if (entry.isDirectory()) {
|
|
1465
|
-
walk(abs);
|
|
1466
|
-
continue;
|
|
1467
|
-
}
|
|
1468
|
-
if (entry.isFile() && entry.name === MANIFEST_FILE3) {
|
|
1469
|
-
continue;
|
|
1470
|
-
}
|
|
1471
|
-
const rel = relative(root, abs).split(sep).join("/");
|
|
1472
|
-
paths.push(rel);
|
|
1473
|
-
}
|
|
1474
|
-
};
|
|
1475
|
-
walk(root);
|
|
1476
|
-
return paths.sort();
|
|
1477
|
-
}
|
|
1478
|
-
function diffManifestPaths(remote, local) {
|
|
1479
|
-
const remoteMap = new Map(
|
|
1480
|
-
(remote?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
1481
|
-
);
|
|
1482
|
-
const localMap = new Map(
|
|
1483
|
-
(local?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
1484
|
-
);
|
|
1485
|
-
const download = [];
|
|
1486
|
-
for (const [path10, hash] of remoteMap) {
|
|
1487
|
-
if (localMap.get(path10) !== hash) {
|
|
1488
|
-
download.push(path10);
|
|
1489
|
-
}
|
|
1490
|
-
}
|
|
1491
|
-
const deleteLocal = [];
|
|
1492
|
-
for (const path10 of localMap.keys()) {
|
|
1493
|
-
if (!remoteMap.has(path10)) {
|
|
1494
|
-
deleteLocal.push(path10);
|
|
1495
|
-
}
|
|
1496
|
-
}
|
|
1497
|
-
return { download, deleteLocal };
|
|
1498
|
-
}
|
|
1499
|
-
async function syncRepositoryProjectDocumentsPull(workdirPath, apmDir) {
|
|
1500
|
-
const empty = {
|
|
1501
|
-
synced: false,
|
|
1502
|
-
repositoryId: null,
|
|
1503
|
-
downloaded: 0,
|
|
1504
|
-
deleted: 0
|
|
1505
|
-
};
|
|
1506
|
-
const cfg = await tryReadApmConfig();
|
|
1507
|
-
if (!cfg || !resolveApiKey(cfg)) {
|
|
1508
|
-
console.log(
|
|
1509
|
-
"[apm] \u672A\u68C0\u6D4B\u5230\u767B\u5F55\u4FE1\u606F\uFF0C\u8DF3\u8FC7\u4ED3\u5E93\u9879\u76EE\u6587\u6863\u540C\u6B65\u3002\n[apm] \u8BF7\u5148\u6267\u884C apm login\u3002"
|
|
1510
|
-
);
|
|
1511
|
-
return empty;
|
|
1512
|
-
}
|
|
1513
|
-
const api = createApmApiClient(cfg);
|
|
1514
|
-
const { repositoryId, diagnostic } = await resolveRepositoryIdForSync(
|
|
1515
|
-
api,
|
|
1516
|
-
workdirPath
|
|
1517
|
-
);
|
|
1518
|
-
if (!repositoryId) {
|
|
1519
|
-
console.log(
|
|
1520
|
-
`[apm] \u672A\u80FD\u540C\u6B65\u4ED3\u5E93\u9879\u76EE\u6587\u6863\u3002
|
|
1521
|
-
${diagnostic ?? ""}`
|
|
1522
|
-
);
|
|
1523
|
-
return empty;
|
|
1524
|
-
}
|
|
1525
|
-
const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
|
|
1526
|
-
const projectDir = projectDocumentsDir(targetApmDir);
|
|
1527
|
-
await ensureDirExists(projectDir);
|
|
1528
|
-
const { manifest: remoteManifest } = await api.cli.getRepositoryProjectDocumentManifest({ repositoryId });
|
|
1529
|
-
if (!remoteManifest) {
|
|
1530
|
-
console.log(
|
|
1531
|
-
`[apm] \u4ED3\u5E93 ${repositoryId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`
|
|
1532
|
-
);
|
|
1533
|
-
return { ...empty, repositoryId };
|
|
1534
|
-
}
|
|
1535
|
-
const localManifest = readLocalManifest(targetApmDir);
|
|
1536
|
-
const { download, deleteLocal } = diffManifestPaths(
|
|
1537
|
-
remoteManifest,
|
|
1538
|
-
localManifest
|
|
1539
|
-
);
|
|
1540
|
-
let downloaded = 0;
|
|
1541
|
-
if (download.length > 0) {
|
|
1542
|
-
const { list } = await api.cli.listRepositoryProjectDocuments({
|
|
1543
|
-
repositoryId,
|
|
1544
|
-
paths: download.join(",")
|
|
1545
|
-
});
|
|
1546
|
-
for (const doc of list) {
|
|
1547
|
-
const absPath = toFsPath(projectDocumentLocalPath(targetApmDir, doc.path));
|
|
1548
|
-
await ensureDirExists(dirname2(absPath));
|
|
1549
|
-
writeFileSync8(absPath, doc.content, "utf8");
|
|
1550
|
-
downloaded += 1;
|
|
1551
|
-
}
|
|
1552
|
-
}
|
|
1553
|
-
let deleted = 0;
|
|
1554
|
-
for (const path10 of deleteLocal) {
|
|
1555
|
-
const absPath = toFsPath(projectDocumentLocalPath(targetApmDir, path10));
|
|
1556
|
-
if (existsSync6(absPath)) {
|
|
1557
|
-
rmSync3(absPath, { force: true });
|
|
1558
|
-
deleted += 1;
|
|
1559
|
-
}
|
|
1560
|
-
}
|
|
1561
|
-
writeFileSync8(
|
|
1562
|
-
toFsPath(join8(projectDir, MANIFEST_FILE3)),
|
|
1563
|
-
`${JSON.stringify(remoteManifest, null, 2)}
|
|
1564
|
-
`,
|
|
1565
|
-
"utf8"
|
|
1566
|
-
);
|
|
1567
|
-
console.log(
|
|
1568
|
-
`[apm] \u5DF2\u540C\u6B65\u4ED3\u5E93\u9879\u76EE\u6587\u6863: \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted}`
|
|
1569
|
-
);
|
|
1570
|
-
return {
|
|
1571
|
-
synced: true,
|
|
1572
|
-
repositoryId,
|
|
1573
|
-
downloaded,
|
|
1574
|
-
deleted
|
|
1575
|
-
};
|
|
1576
|
-
}
|
|
1577
|
-
async function syncRepositoryProjectDocumentsPush(cfg, workdirPath, apmRoot) {
|
|
1578
|
-
const api = createApmApiClient(cfg);
|
|
1579
|
-
const { repositoryId } = await resolveRepositoryIdForSync(api, workdirPath);
|
|
1580
|
-
if (!repositoryId) {
|
|
1581
|
-
return 0;
|
|
1582
|
-
}
|
|
1583
|
-
const targetApmDir = apmRoot ?? workspaceApmDir(workdirPath);
|
|
1584
|
-
const localPaths = listLocalDocumentPaths(targetApmDir);
|
|
1585
|
-
if (localPaths.length === 0) {
|
|
1586
|
-
console.log("[apm] \u4ED3\u5E93\u9879\u76EE\u6587\u6863\u65E0\u672C\u5730\u6587\u4EF6\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
1587
|
-
return 0;
|
|
1588
|
-
}
|
|
1589
|
-
const remoteManifest = (await api.cli.getRepositoryProjectDocumentManifest({ repositoryId })).manifest ?? null;
|
|
1590
|
-
const remoteHashByPath = new Map(
|
|
1591
|
-
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
1592
|
-
);
|
|
1593
|
-
const remoteDescriptionByPath = new Map(
|
|
1594
|
-
(remoteManifest?.documents ?? []).map((doc) => [
|
|
1595
|
-
doc.path,
|
|
1596
|
-
doc.description
|
|
1597
|
-
])
|
|
1598
|
-
);
|
|
1599
|
-
let synced = 0;
|
|
1600
|
-
for (const path10 of localPaths) {
|
|
1601
|
-
const absPath = toFsPath(projectDocumentLocalPath(targetApmDir, path10));
|
|
1602
|
-
const content = readFileSync6(absPath, "utf8");
|
|
1603
|
-
const contentHash = hashLocalFileContent(content);
|
|
1604
|
-
if (remoteHashByPath.get(path10) === contentHash) {
|
|
1605
|
-
continue;
|
|
1606
|
-
}
|
|
1607
|
-
await api.cli.upsertRepositoryProjectDocument({
|
|
1608
|
-
repositoryId,
|
|
1609
|
-
path: path10,
|
|
1610
|
-
content,
|
|
1611
|
-
description: remoteDescriptionByPath.get(path10) ?? void 0
|
|
1612
|
-
});
|
|
1613
|
-
synced += 1;
|
|
1614
|
-
console.log(`[apm] \u5DF2\u540C\u6B65\u4ED3\u5E93\u9879\u76EE\u6587\u6863: ${path10}`);
|
|
1615
|
-
}
|
|
1616
|
-
if (synced === 0) {
|
|
1617
|
-
console.log("[apm] \u4ED3\u5E93\u9879\u76EE\u6587\u6863\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
1618
|
-
}
|
|
1619
|
-
return synced;
|
|
1620
|
-
}
|
|
1621
|
-
|
|
1622
1640
|
// src/commands/pull.ts
|
|
1623
1641
|
async function runPull(sessionId, remoteWorkdir) {
|
|
1624
1642
|
const trimmedId = sessionId.trim();
|
|
@@ -2961,8 +2979,13 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
|
|
|
2961
2979
|
};
|
|
2962
2980
|
try {
|
|
2963
2981
|
if (signal.aborted) return;
|
|
2964
|
-
|
|
2965
|
-
|
|
2982
|
+
const { didInit } = await runStep(
|
|
2983
|
+
"workspace-init",
|
|
2984
|
+
() => ensureWorkspaceInitialized(workdir)
|
|
2985
|
+
);
|
|
2986
|
+
if (!didInit) {
|
|
2987
|
+
assertApmGitignoredInRepo(workdir);
|
|
2988
|
+
}
|
|
2966
2989
|
await runStep(
|
|
2967
2990
|
"status-typing",
|
|
2968
2991
|
() => updateMessageStatus(cfg, messageId, "TYPING")
|
|
@@ -3006,6 +3029,12 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
|
|
|
3006
3029
|
console.log(`[apm] step=update-skills skipped workdir=${workdir}`);
|
|
3007
3030
|
}
|
|
3008
3031
|
if (signal.aborted) return;
|
|
3032
|
+
if (!pullRan) {
|
|
3033
|
+
await runStep(
|
|
3034
|
+
"sync-project-documents-pull",
|
|
3035
|
+
() => syncRepositoryProjectDocumentsPull(workdir, apmRoot)
|
|
3036
|
+
);
|
|
3037
|
+
}
|
|
3009
3038
|
await runStep(
|
|
3010
3039
|
"cursor-agent",
|
|
3011
3040
|
() => runCursorAgent(
|
package/package.json
CHANGED
|
File without changes
|