ai-project-manage-cli 7.0.2 → 7.0.3
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 +1194 -339
- package/package.json +1 -1
- package/template/AGENTS.md +21 -12
- package/template/apm.config.json +3 -3
- package/template/deploy/README.md +2 -9
- package/template/rules/reply.md +8 -8
- package/template/rules/write_doc.md +2 -14
- package/template/skills/apm-diff-review/SKILL.md +6 -6
- package/template/deploy/deploy.py +0 -564
- package/template/project/.gitkeep +0 -0
- package/template/skills/apm-apply-change/SKILL.md +0 -114
- package/template/skills/apm-deploy/SKILL.md +0 -10
- package/template/skills/apm-dev/SKILL.md +0 -93
- package/template/skills/apm-propose/SKILL.md +0 -52
- package/template/skills/apm-propose/design.md +0 -37
- package/template/skills/apm-propose/proposal.md +0 -31
- package/template/skills/apm-propose/specs.md +0 -39
- package/template/skills/apm-propose/tasks.md +0 -16
- package/template/skills/apm-recap/SKILL.md +0 -70
- package/template/skills/apm-recap/recap-template.md +0 -121
- package/template/skills/apm-review/SKILL.md +0 -26
- package/template/skills/apm-write-frontend-plan/SKILL.md +0 -27
- package/template/skills/apm-write-frontend-plan/plan-template.md +0 -40
- package/template/skills/apm-write-plan/SKILL.md +0 -79
- package/template/skills/apm-write-plan/api-template.md +0 -35
- package/template/skills/apm-write-plan/plan-template.md +0 -95
- package/template/skills/apm-write-prd/SKILL.md +0 -14
- package/template/skills/apm-write-prd/template.md +0 -134
package/dist/index.js
CHANGED
|
@@ -83,8 +83,8 @@ function buildAgentWsUrl(httpBase, apiKey) {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
// src/commands/init.ts
|
|
86
|
-
import { join as
|
|
87
|
-
import { readFileSync as
|
|
86
|
+
import { join as join7 } from "path";
|
|
87
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
88
88
|
|
|
89
89
|
// src/command-utils.ts
|
|
90
90
|
import {
|
|
@@ -113,8 +113,8 @@ function toFsPath(inputPath) {
|
|
|
113
113
|
}
|
|
114
114
|
return `\\\\?\\${normalized}`;
|
|
115
115
|
}
|
|
116
|
-
function normalizeWorkdirPath(
|
|
117
|
-
let normalized =
|
|
116
|
+
function normalizeWorkdirPath(path13) {
|
|
117
|
+
let normalized = path13.trim().replace(/\\/g, "/").normalize("NFC");
|
|
118
118
|
if (normalized.startsWith("//?/")) {
|
|
119
119
|
normalized = normalized.slice(4);
|
|
120
120
|
}
|
|
@@ -297,9 +297,9 @@ function assertTemplateCopiedToApm(apmDir, workdir) {
|
|
|
297
297
|
"project"
|
|
298
298
|
];
|
|
299
299
|
for (const item of required) {
|
|
300
|
-
const
|
|
301
|
-
if (!existsSync2(toFsPath(
|
|
302
|
-
throw new Error(`[apm] \u521D\u59CB\u5316\u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5C11: ${
|
|
300
|
+
const path13 = join2(apmDir, item);
|
|
301
|
+
if (!existsSync2(toFsPath(path13))) {
|
|
302
|
+
throw new Error(`[apm] \u521D\u59CB\u5316\u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5C11: ${path13}`);
|
|
303
303
|
}
|
|
304
304
|
}
|
|
305
305
|
const leakedRules = join2(workdir, "rules");
|
|
@@ -348,9 +348,6 @@ function taskDir(taskId, apmRoot, workdir) {
|
|
|
348
348
|
function taskDocsDir(taskId, apmRoot, workdir) {
|
|
349
349
|
return join2(taskDir(taskId, apmRoot, workdir), TASK_DOCS_SUBDIR);
|
|
350
350
|
}
|
|
351
|
-
function taskRulePath(taskId, apmRoot, workdir) {
|
|
352
|
-
return join2(taskDir(taskId, apmRoot, workdir), "RULE.md");
|
|
353
|
-
}
|
|
354
351
|
function taskTaskPath(taskId, apmRoot, workdir) {
|
|
355
352
|
return join2(taskDir(taskId, apmRoot, workdir), "TASK.md");
|
|
356
353
|
}
|
|
@@ -574,66 +571,170 @@ ${diagnostic ?? ""}
|
|
|
574
571
|
|
|
575
572
|
// src/repository-project-documents-sync.ts
|
|
576
573
|
import {
|
|
577
|
-
existsSync as
|
|
574
|
+
existsSync as existsSync4,
|
|
578
575
|
readdirSync as readdirSync2,
|
|
579
|
-
readFileSync as
|
|
576
|
+
readFileSync as readFileSync4,
|
|
580
577
|
rmSync,
|
|
581
|
-
writeFileSync as
|
|
578
|
+
writeFileSync as writeFileSync5
|
|
582
579
|
} from "fs";
|
|
583
|
-
import { dirname as dirname2, join as
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
580
|
+
import { dirname as dirname2, join as join5, relative, sep } from "path";
|
|
581
|
+
|
|
582
|
+
// src/apm-manifest.ts
|
|
583
|
+
import { createHash } from "crypto";
|
|
584
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
585
|
+
import { join as join4 } from "path";
|
|
586
|
+
var APM_MANIFEST_FILE = "manifest.json";
|
|
587
|
+
var LEGACY_PROJECT_MANIFEST = join4("project", APM_MANIFEST_FILE);
|
|
588
|
+
function apmManifestPath(apmRoot) {
|
|
589
|
+
return join4(apmRoot, APM_MANIFEST_FILE);
|
|
587
590
|
}
|
|
588
|
-
function
|
|
589
|
-
|
|
590
|
-
return join4(projectDocumentsDir(apmRoot), ...normalized.split("/"));
|
|
591
|
+
function hashApmFileContent(content) {
|
|
592
|
+
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
591
593
|
}
|
|
592
|
-
function
|
|
593
|
-
const trimmed =
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
const
|
|
598
|
-
if (
|
|
599
|
-
throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${
|
|
594
|
+
function toProjectManifestPath(localPath) {
|
|
595
|
+
const trimmed = localPath.trim().replace(/\\/g, "/");
|
|
596
|
+
return `project/${trimmed}`;
|
|
597
|
+
}
|
|
598
|
+
function fromProjectManifestPath(manifestPath) {
|
|
599
|
+
const prefix = "project/";
|
|
600
|
+
if (!manifestPath.startsWith(prefix)) {
|
|
601
|
+
throw new Error(`\u975E\u6CD5\u9879\u76EE\u6587\u6863 manifest \u8DEF\u5F84: ${manifestPath}`);
|
|
600
602
|
}
|
|
601
|
-
return
|
|
603
|
+
return manifestPath.slice(prefix.length);
|
|
604
|
+
}
|
|
605
|
+
function toRuleManifestPath(fileName) {
|
|
606
|
+
return `rules/${fileName}`;
|
|
607
|
+
}
|
|
608
|
+
function toSkillManifestPath(skillDirName) {
|
|
609
|
+
return `skills/${skillDirName}/SKILL.md`;
|
|
602
610
|
}
|
|
603
|
-
function
|
|
604
|
-
|
|
605
|
-
|
|
611
|
+
function manifestSectionPrefix(section) {
|
|
612
|
+
return `${section}/`;
|
|
613
|
+
}
|
|
614
|
+
function readApmManifest(apmRoot) {
|
|
615
|
+
const primary = apmManifestPath(apmRoot);
|
|
616
|
+
if (existsSync3(toFsPath(primary))) {
|
|
617
|
+
return parseApmManifest(readFileSync3(toFsPath(primary), "utf8"));
|
|
618
|
+
}
|
|
619
|
+
const legacy = join4(apmRoot, LEGACY_PROJECT_MANIFEST);
|
|
620
|
+
if (!existsSync3(toFsPath(legacy))) {
|
|
621
|
+
return null;
|
|
622
|
+
}
|
|
623
|
+
const legacyManifest = parseApmManifest(readFileSync3(toFsPath(legacy), "utf8"));
|
|
624
|
+
if (!legacyManifest) {
|
|
606
625
|
return null;
|
|
607
626
|
}
|
|
627
|
+
return {
|
|
628
|
+
...legacyManifest,
|
|
629
|
+
documents: legacyManifest.documents.map((entry) => ({
|
|
630
|
+
...entry,
|
|
631
|
+
path: entry.path.startsWith("project/") ? entry.path : toProjectManifestPath(entry.path)
|
|
632
|
+
}))
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
function parseApmManifest(raw) {
|
|
608
636
|
try {
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
637
|
+
const parsed = JSON.parse(raw);
|
|
638
|
+
if (parsed?.version === 1 && Array.isArray(parsed.documents) && (parsed.repositoryId === null || typeof parsed.repositoryId === "string")) {
|
|
639
|
+
return parsed;
|
|
640
|
+
}
|
|
612
641
|
} catch {
|
|
613
|
-
return null;
|
|
614
642
|
}
|
|
643
|
+
return null;
|
|
615
644
|
}
|
|
616
|
-
function
|
|
645
|
+
function writeApmManifest(apmRoot, manifest) {
|
|
646
|
+
writeFileSync4(
|
|
647
|
+
toFsPath(apmManifestPath(apmRoot)),
|
|
648
|
+
`${JSON.stringify(
|
|
649
|
+
{
|
|
650
|
+
...manifest,
|
|
651
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
652
|
+
documents: [...manifest.documents].sort(
|
|
653
|
+
(a, b) => a.path.localeCompare(b.path)
|
|
654
|
+
)
|
|
655
|
+
},
|
|
656
|
+
null,
|
|
657
|
+
2
|
|
658
|
+
)}
|
|
659
|
+
`,
|
|
660
|
+
"utf8"
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
function mergeManifestSection(manifest, section, entries, repositoryId) {
|
|
664
|
+
const prefix = manifestSectionPrefix(section);
|
|
665
|
+
const kept = (manifest?.documents ?? []).filter(
|
|
666
|
+
(entry) => !entry.path.startsWith(prefix)
|
|
667
|
+
);
|
|
668
|
+
return {
|
|
669
|
+
version: 1,
|
|
670
|
+
repositoryId: repositoryId !== void 0 ? repositoryId : manifest?.repositoryId ?? null,
|
|
671
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
672
|
+
documents: [...kept, ...entries]
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
function buildManifestEntryFromFile(input) {
|
|
676
|
+
return {
|
|
677
|
+
path: input.path,
|
|
678
|
+
description: input.description ?? null,
|
|
679
|
+
contentHash: hashApmFileContent(input.content),
|
|
680
|
+
updatedAt: input.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
681
|
+
size: Buffer.byteLength(input.content, "utf8"),
|
|
682
|
+
...input.remoteId ? { remoteId: input.remoteId } : {}
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
function diffManifestPaths(remote, local, section) {
|
|
686
|
+
const prefix = manifestSectionPrefix(section);
|
|
617
687
|
const remoteMap = new Map(
|
|
618
|
-
(remote?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
688
|
+
(remote?.documents ?? []).filter((doc) => doc.path.startsWith(prefix)).map((doc) => [doc.path, doc.contentHash])
|
|
619
689
|
);
|
|
620
690
|
const localMap = new Map(
|
|
621
|
-
(local?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
691
|
+
(local?.documents ?? []).filter((doc) => doc.path.startsWith(prefix)).map((doc) => [doc.path, doc.contentHash])
|
|
622
692
|
);
|
|
623
693
|
const download = [];
|
|
624
|
-
for (const [
|
|
625
|
-
if (localMap.get(
|
|
626
|
-
download.push(
|
|
694
|
+
for (const [path13, hash] of remoteMap) {
|
|
695
|
+
if (localMap.get(path13) !== hash) {
|
|
696
|
+
download.push(path13);
|
|
627
697
|
}
|
|
628
698
|
}
|
|
629
699
|
const deleteLocal = [];
|
|
630
|
-
for (const
|
|
631
|
-
if (!remoteMap.has(
|
|
632
|
-
deleteLocal.push(
|
|
700
|
+
for (const path13 of localMap.keys()) {
|
|
701
|
+
if (!remoteMap.has(path13)) {
|
|
702
|
+
deleteLocal.push(path13);
|
|
633
703
|
}
|
|
634
704
|
}
|
|
635
705
|
return { download, deleteLocal };
|
|
636
706
|
}
|
|
707
|
+
|
|
708
|
+
// src/repository-project-documents-sync.ts
|
|
709
|
+
function projectDocumentsDir(apmRoot) {
|
|
710
|
+
return join5(apmRoot ?? workspaceApmDir(), "project");
|
|
711
|
+
}
|
|
712
|
+
function projectDocumentLocalPath(apmRoot, documentPath) {
|
|
713
|
+
const normalized = normalizeLocalDocumentPath(documentPath);
|
|
714
|
+
return join5(projectDocumentsDir(apmRoot), ...normalized.split("/"));
|
|
715
|
+
}
|
|
716
|
+
function normalizeLocalDocumentPath(path13) {
|
|
717
|
+
const trimmed = path13.trim().replace(/\\/g, "/");
|
|
718
|
+
if (!trimmed || trimmed.startsWith("/") || /^[a-zA-Z]:/.test(trimmed)) {
|
|
719
|
+
throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path13}`);
|
|
720
|
+
}
|
|
721
|
+
const segments = trimmed.split("/").filter(Boolean);
|
|
722
|
+
if (segments.some((segment) => segment === ".." || segment === ".")) {
|
|
723
|
+
throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path13}`);
|
|
724
|
+
}
|
|
725
|
+
return segments.join("/");
|
|
726
|
+
}
|
|
727
|
+
function toRemoteProjectManifest(repositoryId, remoteManifest) {
|
|
728
|
+
return {
|
|
729
|
+
version: 1,
|
|
730
|
+
repositoryId,
|
|
731
|
+
generatedAt: remoteManifest.generatedAt,
|
|
732
|
+
documents: remoteManifest.documents.map((doc) => ({
|
|
733
|
+
...doc,
|
|
734
|
+
path: toProjectManifestPath(doc.path)
|
|
735
|
+
}))
|
|
736
|
+
};
|
|
737
|
+
}
|
|
637
738
|
async function syncRepositoryProjectDocumentsPull(workdirPath, apmDir) {
|
|
638
739
|
const empty = {
|
|
639
740
|
synced: false,
|
|
@@ -654,10 +755,8 @@ async function syncRepositoryProjectDocumentsPull(workdirPath, apmDir) {
|
|
|
654
755
|
workdirPath
|
|
655
756
|
);
|
|
656
757
|
if (!repositoryId) {
|
|
657
|
-
console.log(
|
|
658
|
-
|
|
659
|
-
${diagnostic ?? ""}`
|
|
660
|
-
);
|
|
758
|
+
console.log(`[apm] \u672A\u80FD\u540C\u6B65\u4ED3\u5E93\u9879\u76EE\u6587\u6863\u3002
|
|
759
|
+
${diagnostic ?? ""}`);
|
|
661
760
|
return empty;
|
|
662
761
|
}
|
|
663
762
|
const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
|
|
@@ -665,43 +764,54 @@ ${diagnostic ?? ""}`
|
|
|
665
764
|
await ensureDirExists(projectDir);
|
|
666
765
|
const { manifest: remoteManifest } = await api.cli.getRepositoryProjectDocumentManifest({ repositoryId });
|
|
667
766
|
if (!remoteManifest) {
|
|
668
|
-
console.log(
|
|
669
|
-
`[apm] \u4ED3\u5E93 ${repositoryId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`
|
|
670
|
-
);
|
|
767
|
+
console.log(`[apm] \u4ED3\u5E93 ${repositoryId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`);
|
|
671
768
|
return { ...empty, repositoryId };
|
|
672
769
|
}
|
|
673
|
-
const
|
|
770
|
+
const remoteApmManifest = toRemoteProjectManifest(
|
|
771
|
+
repositoryId,
|
|
772
|
+
remoteManifest
|
|
773
|
+
);
|
|
774
|
+
const localManifest = readApmManifest(targetApmDir);
|
|
674
775
|
const { download, deleteLocal } = diffManifestPaths(
|
|
675
|
-
|
|
676
|
-
localManifest
|
|
776
|
+
remoteApmManifest,
|
|
777
|
+
localManifest,
|
|
778
|
+
"project"
|
|
677
779
|
);
|
|
678
780
|
let downloaded = 0;
|
|
679
781
|
if (download.length > 0) {
|
|
680
782
|
const { list } = await api.cli.listRepositoryProjectDocuments({
|
|
681
783
|
repositoryId,
|
|
682
|
-
paths: download.join(",")
|
|
784
|
+
paths: download.map(fromProjectManifestPath).join(",")
|
|
683
785
|
});
|
|
684
786
|
for (const doc of list) {
|
|
685
|
-
const absPath = toFsPath(
|
|
787
|
+
const absPath = toFsPath(
|
|
788
|
+
projectDocumentLocalPath(targetApmDir, doc.path)
|
|
789
|
+
);
|
|
686
790
|
await ensureDirExists(dirname2(absPath));
|
|
687
|
-
|
|
791
|
+
writeFileSync5(absPath, doc.content, "utf8");
|
|
688
792
|
downloaded += 1;
|
|
689
793
|
}
|
|
690
794
|
}
|
|
691
795
|
let deleted = 0;
|
|
692
|
-
for (const
|
|
693
|
-
const absPath = toFsPath(
|
|
694
|
-
|
|
796
|
+
for (const manifestPath of deleteLocal) {
|
|
797
|
+
const absPath = toFsPath(
|
|
798
|
+
projectDocumentLocalPath(
|
|
799
|
+
targetApmDir,
|
|
800
|
+
fromProjectManifestPath(manifestPath)
|
|
801
|
+
)
|
|
802
|
+
);
|
|
803
|
+
if (existsSync4(absPath)) {
|
|
695
804
|
rmSync(absPath, { force: true });
|
|
696
805
|
deleted += 1;
|
|
697
806
|
}
|
|
698
807
|
}
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
808
|
+
const nextManifest = mergeManifestSection(
|
|
809
|
+
localManifest,
|
|
810
|
+
"project",
|
|
811
|
+
remoteApmManifest.documents,
|
|
812
|
+
repositoryId
|
|
704
813
|
);
|
|
814
|
+
writeApmManifest(targetApmDir, nextManifest);
|
|
705
815
|
console.log(
|
|
706
816
|
`[apm] \u5DF2\u540C\u6B65\u4ED3\u5E93\u9879\u76EE\u6587\u6863: \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted}`
|
|
707
817
|
);
|
|
@@ -713,6 +823,162 @@ ${diagnostic ?? ""}`
|
|
|
713
823
|
};
|
|
714
824
|
}
|
|
715
825
|
|
|
826
|
+
// src/skills-sync.ts
|
|
827
|
+
import {
|
|
828
|
+
copyFileSync as copyFileSync2,
|
|
829
|
+
cpSync,
|
|
830
|
+
existsSync as existsSync5,
|
|
831
|
+
mkdirSync as mkdirSync3,
|
|
832
|
+
readdirSync as readdirSync3,
|
|
833
|
+
readFileSync as readFileSync5,
|
|
834
|
+
rmSync as rmSync2,
|
|
835
|
+
statSync as statSync2,
|
|
836
|
+
writeFileSync as writeFileSync6
|
|
837
|
+
} from "fs";
|
|
838
|
+
import { join as join6 } from "path";
|
|
839
|
+
var AGENTS_TEMPLATE_PATH = join6(CLI_TEMPLATE_DIR, "AGENTS.md");
|
|
840
|
+
var BASE_SKILLS_TEMPLATE_DIR = join6(CLI_TEMPLATE_DIR, "skills");
|
|
841
|
+
var BASE_RULES_TEMPLATE_DIR = join6(CLI_TEMPLATE_DIR, "rules");
|
|
842
|
+
function sanitizeSkillDirName(name) {
|
|
843
|
+
const trimmed = name.trim();
|
|
844
|
+
if (!trimmed) return "skill";
|
|
845
|
+
return trimmed.replace(/[/\\:*?"<>|]/g, "_");
|
|
846
|
+
}
|
|
847
|
+
function listBaseSkillDirNames() {
|
|
848
|
+
if (!existsSync5(BASE_SKILLS_TEMPLATE_DIR)) return [];
|
|
849
|
+
return readdirSync3(BASE_SKILLS_TEMPLATE_DIR).filter((name) => {
|
|
850
|
+
const path13 = join6(BASE_SKILLS_TEMPLATE_DIR, name);
|
|
851
|
+
return statSync2(path13).isDirectory();
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
function syncAgentsGuide(apmDir) {
|
|
855
|
+
if (!existsSync5(AGENTS_TEMPLATE_PATH)) return false;
|
|
856
|
+
mkdirSync3(apmDir, { recursive: true });
|
|
857
|
+
copyFileSync2(AGENTS_TEMPLATE_PATH, join6(apmDir, "AGENTS.md"));
|
|
858
|
+
return true;
|
|
859
|
+
}
|
|
860
|
+
function listBaseRuleFileNames() {
|
|
861
|
+
if (!existsSync5(BASE_RULES_TEMPLATE_DIR)) return [];
|
|
862
|
+
return readdirSync3(BASE_RULES_TEMPLATE_DIR).filter((name) => {
|
|
863
|
+
const path13 = join6(BASE_RULES_TEMPLATE_DIR, name);
|
|
864
|
+
return statSync2(path13).isFile();
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
function syncBaseRules(rulesDir) {
|
|
868
|
+
mkdirSync3(rulesDir, { recursive: true });
|
|
869
|
+
const names = listBaseRuleFileNames();
|
|
870
|
+
for (const name of names) {
|
|
871
|
+
const src = join6(BASE_RULES_TEMPLATE_DIR, name);
|
|
872
|
+
const dest = join6(rulesDir, name);
|
|
873
|
+
copyFileSync2(src, dest);
|
|
874
|
+
}
|
|
875
|
+
return names;
|
|
876
|
+
}
|
|
877
|
+
function syncBaseSkills(skillsDir) {
|
|
878
|
+
mkdirSync3(skillsDir, { recursive: true });
|
|
879
|
+
const names = listBaseSkillDirNames();
|
|
880
|
+
for (const name of names) {
|
|
881
|
+
const src = join6(BASE_SKILLS_TEMPLATE_DIR, name);
|
|
882
|
+
const dest = join6(skillsDir, name);
|
|
883
|
+
cpSync(src, dest, { recursive: true, force: true });
|
|
884
|
+
}
|
|
885
|
+
return names;
|
|
886
|
+
}
|
|
887
|
+
function syncSupplementarySkills(skillsDir, list) {
|
|
888
|
+
const baseNames = new Set(listBaseSkillDirNames());
|
|
889
|
+
const apiDirNames = /* @__PURE__ */ new Set();
|
|
890
|
+
const written = [];
|
|
891
|
+
const skipped = [];
|
|
892
|
+
for (const skill of list) {
|
|
893
|
+
const dirName = sanitizeSkillDirName(skill.name);
|
|
894
|
+
apiDirNames.add(dirName);
|
|
895
|
+
if (baseNames.has(dirName)) {
|
|
896
|
+
skipped.push(dirName);
|
|
897
|
+
continue;
|
|
898
|
+
}
|
|
899
|
+
const skillDir = join6(skillsDir, dirName);
|
|
900
|
+
mkdirSync3(skillDir, { recursive: true });
|
|
901
|
+
writeFileSync6(join6(skillDir, "SKILL.md"), skill.content ?? "", "utf8");
|
|
902
|
+
written.push(dirName);
|
|
903
|
+
}
|
|
904
|
+
const removed = [];
|
|
905
|
+
if (!existsSync5(skillsDir)) return { written, skipped, removed };
|
|
906
|
+
for (const entry of readdirSync3(skillsDir)) {
|
|
907
|
+
const full = join6(skillsDir, entry);
|
|
908
|
+
if (!statSync2(full).isDirectory()) continue;
|
|
909
|
+
if (baseNames.has(entry)) continue;
|
|
910
|
+
if (apiDirNames.has(entry)) continue;
|
|
911
|
+
rmSync2(full, { recursive: true, force: true });
|
|
912
|
+
removed.push(entry);
|
|
913
|
+
}
|
|
914
|
+
return { written, skipped, removed };
|
|
915
|
+
}
|
|
916
|
+
function syncSkillsManifest(apmDir, supplementarySkills = []) {
|
|
917
|
+
const skillsDir = join6(apmDir, "skills");
|
|
918
|
+
if (!existsSync5(skillsDir)) {
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
const supplementaryByDir = new Map(
|
|
922
|
+
supplementarySkills.map((skill) => [
|
|
923
|
+
sanitizeSkillDirName(skill.name),
|
|
924
|
+
skill
|
|
925
|
+
])
|
|
926
|
+
);
|
|
927
|
+
const manifest = readApmManifest(apmDir);
|
|
928
|
+
const existingByPath = new Map(
|
|
929
|
+
(manifest?.documents ?? []).filter((entry) => entry.path.startsWith("skills/")).map((entry) => [entry.path, entry])
|
|
930
|
+
);
|
|
931
|
+
const entries = [];
|
|
932
|
+
for (const entry of readdirSync3(skillsDir)) {
|
|
933
|
+
const skillDir = join6(skillsDir, entry);
|
|
934
|
+
if (!statSync2(skillDir).isDirectory()) continue;
|
|
935
|
+
const skillFile = join6(skillDir, "SKILL.md");
|
|
936
|
+
if (!existsSync5(skillFile)) continue;
|
|
937
|
+
const content = readFileSync5(skillFile, "utf8");
|
|
938
|
+
const supplementary = supplementaryByDir.get(entry);
|
|
939
|
+
const manifestPath = toSkillManifestPath(entry);
|
|
940
|
+
const existing = existingByPath.get(manifestPath);
|
|
941
|
+
entries.push(
|
|
942
|
+
buildManifestEntryFromFile({
|
|
943
|
+
path: manifestPath,
|
|
944
|
+
content,
|
|
945
|
+
description: supplementary?.description ?? existing?.description ?? null,
|
|
946
|
+
remoteId: supplementary?.id ?? existing?.remoteId
|
|
947
|
+
})
|
|
948
|
+
);
|
|
949
|
+
}
|
|
950
|
+
writeApmManifest(apmDir, mergeManifestSection(manifest, "skills", entries));
|
|
951
|
+
}
|
|
952
|
+
function syncRulesManifest(apmDir) {
|
|
953
|
+
const rulesDir = join6(apmDir, "rules");
|
|
954
|
+
if (!existsSync5(rulesDir)) {
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
const manifest = readApmManifest(apmDir);
|
|
958
|
+
const existingByPath = new Map(
|
|
959
|
+
(manifest?.documents ?? []).filter((entry) => entry.path.startsWith("rules/")).map((entry) => [entry.path, entry])
|
|
960
|
+
);
|
|
961
|
+
const entries = [];
|
|
962
|
+
for (const fileName of readdirSync3(rulesDir)) {
|
|
963
|
+
const dest = join6(rulesDir, fileName);
|
|
964
|
+
if (!statSync2(dest).isFile()) continue;
|
|
965
|
+
if (fileName === ".rules-sync-manifest.json") continue;
|
|
966
|
+
const manifestPath = toRuleManifestPath(fileName);
|
|
967
|
+
const content = readFileSync5(dest, "utf8");
|
|
968
|
+
const existing = existingByPath.get(manifestPath);
|
|
969
|
+
entries.push(
|
|
970
|
+
buildManifestEntryFromFile({
|
|
971
|
+
path: manifestPath,
|
|
972
|
+
content,
|
|
973
|
+
description: existing?.description ?? null,
|
|
974
|
+
updatedAt: existing?.updatedAt,
|
|
975
|
+
remoteId: existing?.remoteId
|
|
976
|
+
})
|
|
977
|
+
);
|
|
978
|
+
}
|
|
979
|
+
writeApmManifest(apmDir, mergeManifestSection(manifest, "rules", entries));
|
|
980
|
+
}
|
|
981
|
+
|
|
716
982
|
// src/git-utils.ts
|
|
717
983
|
import { execFile as execFile2 } from "child_process";
|
|
718
984
|
import { promisify as promisify2 } from "util";
|
|
@@ -789,15 +1055,17 @@ async function ensureWorkspaceInitialized(workdir, options) {
|
|
|
789
1055
|
}
|
|
790
1056
|
const apmDir = workspaceApmDir(workdir);
|
|
791
1057
|
await copyTemplateFiles(apmDir, workdir);
|
|
1058
|
+
syncRulesManifest(apmDir);
|
|
1059
|
+
syncSkillsManifest(apmDir);
|
|
792
1060
|
const syncResult = await syncRemoteDeploymentConfig(workdir, apmDir);
|
|
793
1061
|
await syncRepositoryProjectDocumentsPull(workdir, apmDir);
|
|
794
1062
|
const trimmedName = options?.name?.trim();
|
|
795
1063
|
if (trimmedName) {
|
|
796
|
-
const apmConfigPath = toFsPath(
|
|
797
|
-
const config =
|
|
1064
|
+
const apmConfigPath = toFsPath(join7(apmDir, "apm.config.json"));
|
|
1065
|
+
const config = readFileSync6(apmConfigPath, "utf8");
|
|
798
1066
|
const configJson = JSON.parse(config);
|
|
799
1067
|
configJson.name = trimmedName;
|
|
800
|
-
|
|
1068
|
+
writeFileSync7(
|
|
801
1069
|
apmConfigPath,
|
|
802
1070
|
`${JSON.stringify(configJson, null, 2)}
|
|
803
1071
|
`,
|
|
@@ -886,15 +1154,15 @@ async function runLogin(opts) {
|
|
|
886
1154
|
import { spawnSync } from "child_process";
|
|
887
1155
|
|
|
888
1156
|
// src/version.ts
|
|
889
|
-
import { readFileSync as
|
|
890
|
-
import { dirname as dirname3, join as
|
|
1157
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
1158
|
+
import { dirname as dirname3, join as join8 } from "path";
|
|
891
1159
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
892
1160
|
var CLI_PACKAGE_NAME = "ai-project-manage-cli";
|
|
893
1161
|
function readCliVersion() {
|
|
894
1162
|
try {
|
|
895
1163
|
const dir = dirname3(fileURLToPath2(import.meta.url));
|
|
896
|
-
const pkgPath =
|
|
897
|
-
const pkg = JSON.parse(
|
|
1164
|
+
const pkgPath = join8(dir, "..", "package.json");
|
|
1165
|
+
const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
|
|
898
1166
|
return pkg.version ?? "0.0.0";
|
|
899
1167
|
} catch {
|
|
900
1168
|
return "0.0.0";
|
|
@@ -967,104 +1235,12 @@ async function runUpdate() {
|
|
|
967
1235
|
}
|
|
968
1236
|
|
|
969
1237
|
// src/commands/update-skills.ts
|
|
970
|
-
import { existsSync as
|
|
971
|
-
import { join as
|
|
972
|
-
|
|
973
|
-
// src/skills-sync.ts
|
|
974
|
-
import {
|
|
975
|
-
copyFileSync as copyFileSync2,
|
|
976
|
-
cpSync,
|
|
977
|
-
existsSync as existsSync4,
|
|
978
|
-
mkdirSync as mkdirSync3,
|
|
979
|
-
readdirSync as readdirSync3,
|
|
980
|
-
rmSync as rmSync2,
|
|
981
|
-
statSync as statSync2,
|
|
982
|
-
writeFileSync as writeFileSync6
|
|
983
|
-
} from "fs";
|
|
984
|
-
import { join as join7 } from "path";
|
|
985
|
-
var AGENTS_TEMPLATE_PATH = join7(CLI_TEMPLATE_DIR, "AGENTS.md");
|
|
986
|
-
var BASE_SKILLS_TEMPLATE_DIR = join7(CLI_TEMPLATE_DIR, "skills");
|
|
987
|
-
var BASE_RULES_TEMPLATE_DIR = join7(CLI_TEMPLATE_DIR, "rules");
|
|
988
|
-
function sanitizeSkillDirName(name) {
|
|
989
|
-
const trimmed = name.trim();
|
|
990
|
-
if (!trimmed) return "skill";
|
|
991
|
-
return trimmed.replace(/[/\\:*?"<>|]/g, "_");
|
|
992
|
-
}
|
|
993
|
-
function listBaseSkillDirNames() {
|
|
994
|
-
if (!existsSync4(BASE_SKILLS_TEMPLATE_DIR)) return [];
|
|
995
|
-
return readdirSync3(BASE_SKILLS_TEMPLATE_DIR).filter((name) => {
|
|
996
|
-
const path10 = join7(BASE_SKILLS_TEMPLATE_DIR, name);
|
|
997
|
-
return statSync2(path10).isDirectory();
|
|
998
|
-
});
|
|
999
|
-
}
|
|
1000
|
-
function syncAgentsGuide(apmDir) {
|
|
1001
|
-
if (!existsSync4(AGENTS_TEMPLATE_PATH)) return false;
|
|
1002
|
-
mkdirSync3(apmDir, { recursive: true });
|
|
1003
|
-
copyFileSync2(AGENTS_TEMPLATE_PATH, join7(apmDir, "AGENTS.md"));
|
|
1004
|
-
return true;
|
|
1005
|
-
}
|
|
1006
|
-
function listBaseRuleFileNames() {
|
|
1007
|
-
if (!existsSync4(BASE_RULES_TEMPLATE_DIR)) return [];
|
|
1008
|
-
return readdirSync3(BASE_RULES_TEMPLATE_DIR).filter((name) => {
|
|
1009
|
-
const path10 = join7(BASE_RULES_TEMPLATE_DIR, name);
|
|
1010
|
-
return statSync2(path10).isFile();
|
|
1011
|
-
});
|
|
1012
|
-
}
|
|
1013
|
-
function syncBaseRules(rulesDir) {
|
|
1014
|
-
mkdirSync3(rulesDir, { recursive: true });
|
|
1015
|
-
const names = listBaseRuleFileNames();
|
|
1016
|
-
for (const name of names) {
|
|
1017
|
-
const src = join7(BASE_RULES_TEMPLATE_DIR, name);
|
|
1018
|
-
const dest = join7(rulesDir, name);
|
|
1019
|
-
copyFileSync2(src, dest);
|
|
1020
|
-
}
|
|
1021
|
-
return names;
|
|
1022
|
-
}
|
|
1023
|
-
function syncBaseSkills(skillsDir) {
|
|
1024
|
-
mkdirSync3(skillsDir, { recursive: true });
|
|
1025
|
-
const names = listBaseSkillDirNames();
|
|
1026
|
-
for (const name of names) {
|
|
1027
|
-
const src = join7(BASE_SKILLS_TEMPLATE_DIR, name);
|
|
1028
|
-
const dest = join7(skillsDir, name);
|
|
1029
|
-
cpSync(src, dest, { recursive: true, force: true });
|
|
1030
|
-
}
|
|
1031
|
-
return names;
|
|
1032
|
-
}
|
|
1033
|
-
function syncSupplementarySkills(skillsDir, list) {
|
|
1034
|
-
const baseNames = new Set(listBaseSkillDirNames());
|
|
1035
|
-
const apiDirNames = /* @__PURE__ */ new Set();
|
|
1036
|
-
const written = [];
|
|
1037
|
-
const skipped = [];
|
|
1038
|
-
for (const skill of list) {
|
|
1039
|
-
const dirName = sanitizeSkillDirName(skill.name);
|
|
1040
|
-
apiDirNames.add(dirName);
|
|
1041
|
-
if (baseNames.has(dirName)) {
|
|
1042
|
-
skipped.push(dirName);
|
|
1043
|
-
continue;
|
|
1044
|
-
}
|
|
1045
|
-
const skillDir = join7(skillsDir, dirName);
|
|
1046
|
-
mkdirSync3(skillDir, { recursive: true });
|
|
1047
|
-
writeFileSync6(join7(skillDir, "SKILL.md"), skill.content ?? "", "utf8");
|
|
1048
|
-
written.push(dirName);
|
|
1049
|
-
}
|
|
1050
|
-
const removed = [];
|
|
1051
|
-
if (!existsSync4(skillsDir)) return { written, skipped, removed };
|
|
1052
|
-
for (const entry of readdirSync3(skillsDir)) {
|
|
1053
|
-
const full = join7(skillsDir, entry);
|
|
1054
|
-
if (!statSync2(full).isDirectory()) continue;
|
|
1055
|
-
if (baseNames.has(entry)) continue;
|
|
1056
|
-
if (apiDirNames.has(entry)) continue;
|
|
1057
|
-
rmSync2(full, { recursive: true, force: true });
|
|
1058
|
-
removed.push(entry);
|
|
1059
|
-
}
|
|
1060
|
-
return { written, skipped, removed };
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
|
-
// src/commands/update-skills.ts
|
|
1238
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4, statSync as statSync3 } from "fs";
|
|
1239
|
+
import { join as join9 } from "path";
|
|
1064
1240
|
async function syncWorkspaceSkills(cfg, workdir) {
|
|
1065
1241
|
const apmDir = workspaceApmDir(workdir);
|
|
1066
1242
|
const fsApmDir = toFsPath(apmDir);
|
|
1067
|
-
if (!
|
|
1243
|
+
if (!existsSync6(fsApmDir)) {
|
|
1068
1244
|
throw new Error("[apm] \u672A\u627E\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5148\u6267\u884C apm init");
|
|
1069
1245
|
}
|
|
1070
1246
|
const apmStat = statSync3(fsApmDir);
|
|
@@ -1076,12 +1252,13 @@ async function syncWorkspaceSkills(cfg, workdir) {
|
|
|
1076
1252
|
if (syncAgentsGuide(apmDir)) {
|
|
1077
1253
|
console.log("[apm] \u5DF2\u540C\u6B65 APM \u6307\u5357: .apm/AGENTS.md");
|
|
1078
1254
|
}
|
|
1079
|
-
const rulesDir =
|
|
1255
|
+
const rulesDir = join9(apmDir, "rules");
|
|
1080
1256
|
const ruleNames = syncBaseRules(rulesDir);
|
|
1081
1257
|
for (const name of ruleNames) {
|
|
1082
1258
|
console.log(`[apm] \u5DF2\u540C\u6B65\u57FA\u7840\u89C4\u5219: rules/${name}`);
|
|
1083
1259
|
}
|
|
1084
|
-
|
|
1260
|
+
syncRulesManifest(apmDir);
|
|
1261
|
+
const skillsDir = join9(apmDir, "skills");
|
|
1085
1262
|
mkdirSync4(toFsPath(skillsDir), { recursive: true });
|
|
1086
1263
|
const baseNames = syncBaseSkills(skillsDir);
|
|
1087
1264
|
for (const name of baseNames) {
|
|
@@ -1102,13 +1279,14 @@ async function syncWorkspaceSkills(cfg, workdir) {
|
|
|
1102
1279
|
for (const name of removed) {
|
|
1103
1280
|
console.log(`[apm] \u5DF2\u79FB\u9664\u5DF2\u4E0B\u7EBF\u7684\u8865\u5145\u6280\u80FD: skills/${name}/`);
|
|
1104
1281
|
}
|
|
1282
|
+
syncSkillsManifest(apmDir, list);
|
|
1105
1283
|
console.log(
|
|
1106
1284
|
`[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`
|
|
1107
1285
|
);
|
|
1108
1286
|
}
|
|
1109
1287
|
async function runUpdateSkills() {
|
|
1110
1288
|
const apmDir = workspaceApmDir();
|
|
1111
|
-
if (!
|
|
1289
|
+
if (!existsSync6(apmDir)) {
|
|
1112
1290
|
console.error("[apm] \u672A\u627E\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5148\u6267\u884C apm init");
|
|
1113
1291
|
process.exit(1);
|
|
1114
1292
|
}
|
|
@@ -1121,7 +1299,7 @@ async function runUpdateSkills() {
|
|
|
1121
1299
|
}
|
|
1122
1300
|
|
|
1123
1301
|
// src/commands/connect.ts
|
|
1124
|
-
import { spawnSync as
|
|
1302
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
1125
1303
|
import WebSocket from "ws";
|
|
1126
1304
|
|
|
1127
1305
|
// src/ws/protocol.ts
|
|
@@ -1212,15 +1390,104 @@ function validateAgentWsMessage(value, kind) {
|
|
|
1212
1390
|
return validateReceivedMailPush(o);
|
|
1213
1391
|
}
|
|
1214
1392
|
|
|
1215
|
-
// src/commands/
|
|
1216
|
-
import { spawn } from "node:child_process";
|
|
1217
|
-
import { readFileSync as readFileSync6 } from "node:fs";
|
|
1218
|
-
import { join as join9 } from "node:path";
|
|
1393
|
+
// src/commands/deploy/internal/deploy-log-syncer.ts
|
|
1219
1394
|
var DEPLOY_LOG_SYNC_INTERVAL_MS = 3e4;
|
|
1395
|
+
function createDeployLogSyncer(api, deploymentRunId) {
|
|
1396
|
+
let lastSyncedLog = "";
|
|
1397
|
+
let latestLog = "";
|
|
1398
|
+
const syncIfChanged = async () => {
|
|
1399
|
+
if (!latestLog || latestLog === lastSyncedLog) {
|
|
1400
|
+
return;
|
|
1401
|
+
}
|
|
1402
|
+
await api.cli.syncTaskDeploymentLog({
|
|
1403
|
+
id: deploymentRunId,
|
|
1404
|
+
log: latestLog
|
|
1405
|
+
});
|
|
1406
|
+
lastSyncedLog = latestLog;
|
|
1407
|
+
};
|
|
1408
|
+
const timer = setInterval(() => {
|
|
1409
|
+
void syncIfChanged().catch((error) => {
|
|
1410
|
+
console.error(
|
|
1411
|
+
"[apm] deploy log sync failed:",
|
|
1412
|
+
error instanceof Error ? error.message : String(error)
|
|
1413
|
+
);
|
|
1414
|
+
});
|
|
1415
|
+
}, DEPLOY_LOG_SYNC_INTERVAL_MS);
|
|
1416
|
+
return {
|
|
1417
|
+
updateLog(log2) {
|
|
1418
|
+
latestLog = log2;
|
|
1419
|
+
},
|
|
1420
|
+
async flush() {
|
|
1421
|
+
clearInterval(timer);
|
|
1422
|
+
await syncIfChanged();
|
|
1423
|
+
},
|
|
1424
|
+
dispose() {
|
|
1425
|
+
clearInterval(timer);
|
|
1426
|
+
}
|
|
1427
|
+
};
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
// src/commands/deploy/internal/deploy-shell.ts
|
|
1431
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
1432
|
+
import { readFileSync as readFileSync8 } from "node:fs";
|
|
1433
|
+
import { join as join10 } from "node:path";
|
|
1434
|
+
|
|
1435
|
+
// src/commands/deploy/internal/shell-exec.ts
|
|
1436
|
+
import {
|
|
1437
|
+
spawn,
|
|
1438
|
+
spawnSync as spawnSync2
|
|
1439
|
+
} from "node:child_process";
|
|
1440
|
+
import { platform as platform2 } from "node:os";
|
|
1441
|
+
function spawnShellOptions() {
|
|
1442
|
+
if (platform2() === "win32") {
|
|
1443
|
+
return {
|
|
1444
|
+
shell: process.env.ComSpec ?? "cmd.exe",
|
|
1445
|
+
windowsHide: true
|
|
1446
|
+
};
|
|
1447
|
+
}
|
|
1448
|
+
return { shell: true, windowsHide: true };
|
|
1449
|
+
}
|
|
1450
|
+
function terminateChildProcess(child) {
|
|
1451
|
+
try {
|
|
1452
|
+
if (platform2() === "win32") {
|
|
1453
|
+
child.kill();
|
|
1454
|
+
} else {
|
|
1455
|
+
child.kill("SIGTERM");
|
|
1456
|
+
}
|
|
1457
|
+
} catch {
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
function normalizeMavenLocalRepoPath(repoPath) {
|
|
1461
|
+
return repoPath.replace(/\\/g, "/");
|
|
1462
|
+
}
|
|
1463
|
+
function quoteShellArg(value) {
|
|
1464
|
+
if (!/\s/.test(value)) {
|
|
1465
|
+
return value;
|
|
1466
|
+
}
|
|
1467
|
+
if (platform2() === "win32") {
|
|
1468
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
1469
|
+
}
|
|
1470
|
+
return `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
1471
|
+
}
|
|
1472
|
+
function expandUserHomePath(pathStr) {
|
|
1473
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
1474
|
+
return pathStr.replace(/^~(?=$|[\\/])/, home);
|
|
1475
|
+
}
|
|
1476
|
+
function spawnSyncShellCommand(command, options = {}) {
|
|
1477
|
+
return spawnSync2(command, {
|
|
1478
|
+
...options,
|
|
1479
|
+
...spawnShellOptions()
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
// src/commands/deploy/internal/deploy-shell.ts
|
|
1484
|
+
function isDeployEnvironment(value) {
|
|
1485
|
+
return value === "test" || value === "online";
|
|
1486
|
+
}
|
|
1220
1487
|
function readDeployConfig(workdir) {
|
|
1221
|
-
const configPath =
|
|
1488
|
+
const configPath = join10(workspaceApmDir(workdir), "apm.config.json");
|
|
1222
1489
|
try {
|
|
1223
|
-
const raw =
|
|
1490
|
+
const raw = readFileSync8(configPath, "utf8");
|
|
1224
1491
|
const parsed = JSON.parse(raw);
|
|
1225
1492
|
return parsed.deploy;
|
|
1226
1493
|
} catch {
|
|
@@ -1240,88 +1507,91 @@ function missingDeployCommandMessage(environment) {
|
|
|
1240
1507
|
function buildDeployLog(stdout, stderr) {
|
|
1241
1508
|
return [stdout, stderr].filter(Boolean).join("\n");
|
|
1242
1509
|
}
|
|
1243
|
-
function
|
|
1510
|
+
async function runDeployShellCommand(command, cwd, options) {
|
|
1511
|
+
const signal = options?.signal ?? new AbortController().signal;
|
|
1512
|
+
const shellOpts = spawnShellOptions();
|
|
1513
|
+
if (options?.inheritStdio) {
|
|
1514
|
+
return new Promise((resolve5, reject) => {
|
|
1515
|
+
const child = spawn2(command, {
|
|
1516
|
+
cwd,
|
|
1517
|
+
env: process.env,
|
|
1518
|
+
stdio: "inherit",
|
|
1519
|
+
...shellOpts
|
|
1520
|
+
});
|
|
1521
|
+
const onAbort = () => {
|
|
1522
|
+
terminateChildProcess(child);
|
|
1523
|
+
};
|
|
1524
|
+
if (signal.aborted) {
|
|
1525
|
+
onAbort();
|
|
1526
|
+
} else {
|
|
1527
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1528
|
+
}
|
|
1529
|
+
child.on("error", (error) => {
|
|
1530
|
+
signal.removeEventListener("abort", onAbort);
|
|
1531
|
+
reject(error);
|
|
1532
|
+
});
|
|
1533
|
+
child.on("close", (code) => {
|
|
1534
|
+
signal.removeEventListener("abort", onAbort);
|
|
1535
|
+
if (code === 0) {
|
|
1536
|
+
resolve5({ log: "" });
|
|
1537
|
+
return;
|
|
1538
|
+
}
|
|
1539
|
+
reject(new Error(`\u90E8\u7F72\u547D\u4EE4\u9000\u51FA\u7801 ${code ?? "unknown"}: ${command}`));
|
|
1540
|
+
});
|
|
1541
|
+
});
|
|
1542
|
+
}
|
|
1244
1543
|
return new Promise((resolve5, reject) => {
|
|
1245
|
-
const child =
|
|
1544
|
+
const child = spawn2(command, {
|
|
1246
1545
|
cwd,
|
|
1247
|
-
shell: true,
|
|
1248
1546
|
env: process.env,
|
|
1249
|
-
|
|
1547
|
+
...shellOpts
|
|
1250
1548
|
});
|
|
1251
1549
|
let stdout = "";
|
|
1252
1550
|
let stderr = "";
|
|
1253
1551
|
const emitLog = () => {
|
|
1254
|
-
onOutput?.(buildDeployLog(stdout, stderr));
|
|
1552
|
+
options?.onOutput?.(buildDeployLog(stdout, stderr));
|
|
1255
1553
|
};
|
|
1256
1554
|
const onAbort = () => {
|
|
1257
|
-
child
|
|
1555
|
+
terminateChildProcess(child);
|
|
1258
1556
|
};
|
|
1259
1557
|
if (signal.aborted) {
|
|
1260
1558
|
onAbort();
|
|
1261
1559
|
} else {
|
|
1262
1560
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
1263
1561
|
}
|
|
1264
|
-
child.stdout
|
|
1265
|
-
stdout
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1562
|
+
if (child.stdout) {
|
|
1563
|
+
child.stdout.on("data", (chunk) => {
|
|
1564
|
+
stdout += String(chunk);
|
|
1565
|
+
emitLog();
|
|
1566
|
+
});
|
|
1567
|
+
}
|
|
1568
|
+
if (child.stderr) {
|
|
1569
|
+
child.stderr.on("data", (chunk) => {
|
|
1570
|
+
stderr += String(chunk);
|
|
1571
|
+
emitLog();
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1272
1574
|
child.on("error", (error) => {
|
|
1273
1575
|
signal.removeEventListener("abort", onAbort);
|
|
1274
1576
|
reject(error);
|
|
1275
1577
|
});
|
|
1276
1578
|
child.on("close", (code) => {
|
|
1277
1579
|
signal.removeEventListener("abort", onAbort);
|
|
1278
|
-
const
|
|
1580
|
+
const log2 = buildDeployLog(stdout, stderr);
|
|
1279
1581
|
if (code === 0) {
|
|
1280
|
-
resolve5({ log });
|
|
1582
|
+
resolve5({ log: log2 });
|
|
1281
1583
|
return;
|
|
1282
1584
|
}
|
|
1283
1585
|
const error = new Error(
|
|
1284
1586
|
`\u90E8\u7F72\u547D\u4EE4\u9000\u51FA\u7801 ${code ?? "unknown"}: ${command}`
|
|
1285
1587
|
);
|
|
1286
|
-
error.log =
|
|
1588
|
+
error.log = log2;
|
|
1287
1589
|
reject(error);
|
|
1288
1590
|
});
|
|
1289
1591
|
});
|
|
1290
1592
|
}
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
let latestLog = "";
|
|
1294
|
-
const syncIfChanged = async () => {
|
|
1295
|
-
if (!latestLog || latestLog === lastSyncedLog) {
|
|
1296
|
-
return;
|
|
1297
|
-
}
|
|
1298
|
-
await api.cli.syncTaskDeploymentLog({
|
|
1299
|
-
id: deploymentRunId,
|
|
1300
|
-
log: latestLog
|
|
1301
|
-
});
|
|
1302
|
-
lastSyncedLog = latestLog;
|
|
1303
|
-
};
|
|
1304
|
-
const timer = setInterval(() => {
|
|
1305
|
-
void syncIfChanged().catch((error) => {
|
|
1306
|
-
console.error(
|
|
1307
|
-
"[apm] deploy log sync failed:",
|
|
1308
|
-
error instanceof Error ? error.message : String(error)
|
|
1309
|
-
);
|
|
1310
|
-
});
|
|
1311
|
-
}, DEPLOY_LOG_SYNC_INTERVAL_MS);
|
|
1312
|
-
return {
|
|
1313
|
-
updateLog(log) {
|
|
1314
|
-
latestLog = log;
|
|
1315
|
-
},
|
|
1316
|
-
async flush() {
|
|
1317
|
-
clearInterval(timer);
|
|
1318
|
-
await syncIfChanged();
|
|
1319
|
-
},
|
|
1320
|
-
dispose() {
|
|
1321
|
-
clearInterval(timer);
|
|
1322
|
-
}
|
|
1323
|
-
};
|
|
1324
|
-
}
|
|
1593
|
+
|
|
1594
|
+
// src/commands/connect/deploy-run.ts
|
|
1325
1595
|
async function handleInboundDeploy(cfg, msg, signal) {
|
|
1326
1596
|
const api = createApmApiClient(cfg);
|
|
1327
1597
|
const deploymentRunId = msg.deploymentRunId;
|
|
@@ -1331,9 +1601,10 @@ async function handleInboundDeploy(cfg, msg, signal) {
|
|
|
1331
1601
|
status: "DEPLOYING"
|
|
1332
1602
|
});
|
|
1333
1603
|
const workdir = requireRemoteWorkdir(msg.workdir);
|
|
1334
|
-
const
|
|
1604
|
+
const environment = msg.environment;
|
|
1605
|
+
const command = resolveDeployCommand(workdir, environment);
|
|
1335
1606
|
if (!command) {
|
|
1336
|
-
const error = missingDeployCommandMessage(
|
|
1607
|
+
const error = missingDeployCommandMessage(environment);
|
|
1337
1608
|
console.error(`[apm] ${error}`);
|
|
1338
1609
|
await api.cli.completeTaskDeployment({
|
|
1339
1610
|
id: deploymentRunId,
|
|
@@ -1344,34 +1615,37 @@ async function handleInboundDeploy(cfg, msg, signal) {
|
|
|
1344
1615
|
return;
|
|
1345
1616
|
}
|
|
1346
1617
|
console.log(
|
|
1347
|
-
`[apm] deploy start id=${deploymentRunId} env=${
|
|
1618
|
+
`[apm] deploy start id=${deploymentRunId} env=${environment} cwd=${workdir}`
|
|
1348
1619
|
);
|
|
1349
1620
|
console.log(`[apm] deploy command: ${command}`);
|
|
1350
1621
|
const logSyncer = createDeployLogSyncer(api, deploymentRunId);
|
|
1351
1622
|
let latestLog = "";
|
|
1352
1623
|
try {
|
|
1353
|
-
const { log } = await
|
|
1354
|
-
|
|
1355
|
-
|
|
1624
|
+
const { log: log2 } = await runDeployShellCommand(command, workdir, {
|
|
1625
|
+
signal,
|
|
1626
|
+
onOutput: (log3) => {
|
|
1627
|
+
latestLog = log3;
|
|
1628
|
+
logSyncer.updateLog(log3);
|
|
1629
|
+
}
|
|
1356
1630
|
});
|
|
1357
|
-
latestLog =
|
|
1358
|
-
logSyncer.updateLog(
|
|
1631
|
+
latestLog = log2;
|
|
1632
|
+
logSyncer.updateLog(log2);
|
|
1359
1633
|
await logSyncer.flush();
|
|
1360
1634
|
await api.cli.completeTaskDeployment({
|
|
1361
1635
|
id: deploymentRunId,
|
|
1362
1636
|
status: "SUCCESS",
|
|
1363
|
-
log
|
|
1637
|
+
log: log2
|
|
1364
1638
|
});
|
|
1365
1639
|
console.log(`[apm] deploy success id=${deploymentRunId}`);
|
|
1366
1640
|
} catch (error) {
|
|
1367
1641
|
const detail = error instanceof Error ? error.message : String(error);
|
|
1368
|
-
const
|
|
1369
|
-
logSyncer.updateLog(
|
|
1642
|
+
const log2 = error && typeof error === "object" && "log" in error ? String(error.log ?? latestLog) : latestLog;
|
|
1643
|
+
logSyncer.updateLog(log2);
|
|
1370
1644
|
await logSyncer.flush();
|
|
1371
1645
|
await api.cli.completeTaskDeployment({
|
|
1372
1646
|
id: deploymentRunId,
|
|
1373
1647
|
status: "FAILED",
|
|
1374
|
-
log,
|
|
1648
|
+
log: log2,
|
|
1375
1649
|
error: detail
|
|
1376
1650
|
});
|
|
1377
1651
|
console.error(`[apm] deploy failed id=${deploymentRunId}: ${detail}`);
|
|
@@ -1381,10 +1655,10 @@ async function handleInboundDeploy(cfg, msg, signal) {
|
|
|
1381
1655
|
}
|
|
1382
1656
|
|
|
1383
1657
|
// src/commands/sync-task-documents.ts
|
|
1384
|
-
import { existsSync as
|
|
1385
|
-
import { join as
|
|
1658
|
+
import { existsSync as existsSync7, readdirSync as readdirSync4, readFileSync as readFileSync9 } from "fs";
|
|
1659
|
+
import { join as join11 } from "path";
|
|
1386
1660
|
function listLocalMarkdownFiles(docsDir) {
|
|
1387
|
-
if (!
|
|
1661
|
+
if (!existsSync7(docsDir)) {
|
|
1388
1662
|
return [];
|
|
1389
1663
|
}
|
|
1390
1664
|
return readdirSync4(docsDir).filter(
|
|
@@ -1399,8 +1673,8 @@ function remoteDocumentByLocalName(remoteDocuments, localFileName) {
|
|
|
1399
1673
|
});
|
|
1400
1674
|
}
|
|
1401
1675
|
async function upsertLocalDocumentFile(api, taskId, docsDir, fileName) {
|
|
1402
|
-
const content =
|
|
1403
|
-
const name = documentPlatformName(
|
|
1676
|
+
const content = readFileSync9(join11(docsDir, fileName), "utf8");
|
|
1677
|
+
const name = documentPlatformName(join11(docsDir, fileName));
|
|
1404
1678
|
return api.cli.upsertDocument({
|
|
1405
1679
|
taskId,
|
|
1406
1680
|
name,
|
|
@@ -1422,7 +1696,7 @@ async function syncTaskDocuments(cfg, taskId, workdir, options) {
|
|
|
1422
1696
|
const remoteDocuments = options?.remoteDocuments ?? await api.cli.listDocuments({ taskId: trimmedTaskId });
|
|
1423
1697
|
let synced = 0;
|
|
1424
1698
|
for (const fileName of localFiles) {
|
|
1425
|
-
const content =
|
|
1699
|
+
const content = readFileSync9(join11(docsDir, fileName), "utf8");
|
|
1426
1700
|
const remote = remoteDocumentByLocalName(remoteDocuments, fileName);
|
|
1427
1701
|
if (remote && remote.content === content) {
|
|
1428
1702
|
continue;
|
|
@@ -1450,8 +1724,8 @@ import {
|
|
|
1450
1724
|
import { setMaxListeners as setMaxListeners2 } from "node:events";
|
|
1451
1725
|
|
|
1452
1726
|
// src/commands/connect/plan-document.ts
|
|
1453
|
-
import { mkdirSync as mkdirSync5, writeFileSync as
|
|
1454
|
-
import { join as
|
|
1727
|
+
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync8 } from "node:fs";
|
|
1728
|
+
import { join as join12 } from "node:path";
|
|
1455
1729
|
function normalizePlanContent(plan) {
|
|
1456
1730
|
return plan.replace(/\\n/g, "\n").trim();
|
|
1457
1731
|
}
|
|
@@ -1521,10 +1795,10 @@ function saveMemberPlanDocument(input) {
|
|
|
1521
1795
|
input.workdir
|
|
1522
1796
|
);
|
|
1523
1797
|
mkdirSync5(toFsPath(docsDir), { recursive: true });
|
|
1524
|
-
const filePath =
|
|
1798
|
+
const filePath = join12(docsDir, fileName);
|
|
1525
1799
|
const normalized = trimmedContent.endsWith("\n") ? trimmedContent : `${trimmedContent}
|
|
1526
1800
|
`;
|
|
1527
|
-
|
|
1801
|
+
writeFileSync8(toFsPath(filePath), normalized, "utf8");
|
|
1528
1802
|
console.log(`[apm] \u5DF2\u4FDD\u5B58\u8BA1\u5212\u6587\u6863: ${fileName}`);
|
|
1529
1803
|
return fileName;
|
|
1530
1804
|
}
|
|
@@ -1682,17 +1956,17 @@ function logAbortSignalStats(signal, label) {
|
|
|
1682
1956
|
}
|
|
1683
1957
|
|
|
1684
1958
|
// src/commands/connect/agent-task-registry.ts
|
|
1685
|
-
import { existsSync as
|
|
1959
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync9 } from "node:fs";
|
|
1686
1960
|
import { dirname as dirname4, resolve as resolve3 } from "node:path";
|
|
1687
1961
|
function registryPath(workdir, taskId) {
|
|
1688
1962
|
return resolve3(workdir, ".apm", "tasks", taskId, "cursor-agents.json");
|
|
1689
1963
|
}
|
|
1690
|
-
function readRegistry(
|
|
1691
|
-
if (!
|
|
1964
|
+
function readRegistry(path13) {
|
|
1965
|
+
if (!existsSync8(path13)) {
|
|
1692
1966
|
return {};
|
|
1693
1967
|
}
|
|
1694
1968
|
try {
|
|
1695
|
-
const parsed = JSON.parse(
|
|
1969
|
+
const parsed = JSON.parse(readFileSync10(path13, "utf8"));
|
|
1696
1970
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
1697
1971
|
const result = {};
|
|
1698
1972
|
for (const [key, value] of Object.entries(
|
|
@@ -1708,28 +1982,28 @@ function readRegistry(path10) {
|
|
|
1708
1982
|
}
|
|
1709
1983
|
return {};
|
|
1710
1984
|
}
|
|
1711
|
-
function writeRegistry(
|
|
1712
|
-
mkdirSync6(dirname4(
|
|
1713
|
-
|
|
1985
|
+
function writeRegistry(path13, registry) {
|
|
1986
|
+
mkdirSync6(dirname4(path13), { recursive: true });
|
|
1987
|
+
writeFileSync9(path13, `${JSON.stringify(registry, null, 2)}
|
|
1714
1988
|
`, "utf8");
|
|
1715
1989
|
}
|
|
1716
1990
|
function loadTaskAgentId(workdir, taskId, user) {
|
|
1717
1991
|
return readRegistry(registryPath(workdir, taskId))[user];
|
|
1718
1992
|
}
|
|
1719
1993
|
function saveTaskAgentId(workdir, taskId, user, agentId) {
|
|
1720
|
-
const
|
|
1721
|
-
const registry = readRegistry(
|
|
1994
|
+
const path13 = registryPath(workdir, taskId);
|
|
1995
|
+
const registry = readRegistry(path13);
|
|
1722
1996
|
registry[user] = agentId;
|
|
1723
|
-
writeRegistry(
|
|
1997
|
+
writeRegistry(path13, registry);
|
|
1724
1998
|
}
|
|
1725
1999
|
function clearTaskAgentId(workdir, taskId, user) {
|
|
1726
|
-
const
|
|
1727
|
-
const registry = readRegistry(
|
|
2000
|
+
const path13 = registryPath(workdir, taskId);
|
|
2001
|
+
const registry = readRegistry(path13);
|
|
1728
2002
|
if (!(user in registry)) {
|
|
1729
2003
|
return;
|
|
1730
2004
|
}
|
|
1731
2005
|
delete registry[user];
|
|
1732
|
-
writeRegistry(
|
|
2006
|
+
writeRegistry(path13, registry);
|
|
1733
2007
|
}
|
|
1734
2008
|
|
|
1735
2009
|
// src/commands/connect/cursor-log.ts
|
|
@@ -2042,14 +2316,13 @@ function createMailReplyDraft() {
|
|
|
2042
2316
|
}
|
|
2043
2317
|
|
|
2044
2318
|
// src/commands/connect/task-pull.ts
|
|
2045
|
-
import { writeFileSync as
|
|
2046
|
-
import { join as
|
|
2319
|
+
import { writeFileSync as writeFileSync11 } from "fs";
|
|
2320
|
+
import { join as join14 } from "path";
|
|
2047
2321
|
import { stringify as yamlStringify } from "yaml";
|
|
2048
2322
|
|
|
2049
2323
|
// src/rules-sync.ts
|
|
2050
|
-
import { basename as basename2, extname, join as
|
|
2051
|
-
import { existsSync as
|
|
2052
|
-
var MANIFEST_FILE2 = ".rules-sync-manifest.json";
|
|
2324
|
+
import { basename as basename2, extname, join as join13 } from "path";
|
|
2325
|
+
import { existsSync as existsSync9, readdirSync as readdirSync5, readFileSync as readFileSync11, rmSync as rmSync3, statSync as statSync4, writeFileSync as writeFileSync10 } from "fs";
|
|
2053
2326
|
function ruleLocalFileName(ruleName) {
|
|
2054
2327
|
const trimmed = ruleName.trim();
|
|
2055
2328
|
if (!trimmed) return "rule.md";
|
|
@@ -2057,46 +2330,31 @@ function ruleLocalFileName(ruleName) {
|
|
|
2057
2330
|
if (extname(sanitized).toLowerCase() === ".md") return sanitized;
|
|
2058
2331
|
return `${sanitized}.md`;
|
|
2059
2332
|
}
|
|
2060
|
-
function loadManifest(rulesDir) {
|
|
2061
|
-
const path10 = join12(rulesDir, MANIFEST_FILE2);
|
|
2062
|
-
if (!existsSync8(toFsPath(path10))) {
|
|
2063
|
-
return { version: 1, rules: {} };
|
|
2064
|
-
}
|
|
2065
|
-
try {
|
|
2066
|
-
const parsed = JSON.parse(
|
|
2067
|
-
readFileSync9(toFsPath(path10), "utf8")
|
|
2068
|
-
);
|
|
2069
|
-
if (parsed?.version === 1 && parsed.rules && typeof parsed.rules === "object") {
|
|
2070
|
-
return parsed;
|
|
2071
|
-
}
|
|
2072
|
-
} catch {
|
|
2073
|
-
}
|
|
2074
|
-
return { version: 1, rules: {} };
|
|
2075
|
-
}
|
|
2076
|
-
function saveManifest(rulesDir, manifest) {
|
|
2077
|
-
writeFileSync9(
|
|
2078
|
-
toFsPath(join12(rulesDir, MANIFEST_FILE2)),
|
|
2079
|
-
`${JSON.stringify(manifest, null, 2)}
|
|
2080
|
-
`,
|
|
2081
|
-
"utf8"
|
|
2082
|
-
);
|
|
2083
|
-
}
|
|
2084
2333
|
function isBaseRuleFileName(fileName) {
|
|
2085
2334
|
return listBaseRuleFileNames().includes(basename2(fileName));
|
|
2086
2335
|
}
|
|
2336
|
+
function findRuleManifestEntry(manifest, rule, fileName) {
|
|
2337
|
+
const manifestPath = toRuleManifestPath(fileName);
|
|
2338
|
+
return (manifest?.documents ?? []).find(
|
|
2339
|
+
(entry) => entry.remoteId === rule.id || entry.path === manifestPath
|
|
2340
|
+
);
|
|
2341
|
+
}
|
|
2087
2342
|
function isRuleUpToDate(entry, rule, dest) {
|
|
2088
|
-
if (!entry || !
|
|
2089
|
-
if (entry.
|
|
2343
|
+
if (!entry || !existsSync9(toFsPath(dest))) return false;
|
|
2344
|
+
if (entry.path !== toRuleManifestPath(ruleLocalFileName(rule.name))) {
|
|
2345
|
+
return false;
|
|
2346
|
+
}
|
|
2090
2347
|
const updatedAt = rule.updatedAt ?? "";
|
|
2091
2348
|
if (entry.updatedAt !== updatedAt) return false;
|
|
2092
|
-
const localContent =
|
|
2349
|
+
const localContent = readFileSync11(toFsPath(dest), "utf8");
|
|
2093
2350
|
return localContent === (rule.content ?? "");
|
|
2094
2351
|
}
|
|
2095
2352
|
async function syncPlatformRules(cfg, workdirPath, apmRoot) {
|
|
2096
2353
|
const api = createApmApiClient(cfg);
|
|
2097
2354
|
const baseline = await api.cli.workspaceBaseline({ workdirPath });
|
|
2098
2355
|
const repositoryId = baseline.repositoryId;
|
|
2099
|
-
const
|
|
2356
|
+
const targetApmRoot = apmRoot ?? workspaceApmDir(workdirPath);
|
|
2357
|
+
const rulesDir = join13(targetApmRoot, "rules");
|
|
2100
2358
|
await ensureDirExists(rulesDir);
|
|
2101
2359
|
if (!repositoryId) {
|
|
2102
2360
|
console.log(
|
|
@@ -2105,40 +2363,65 @@ async function syncPlatformRules(cfg, workdirPath, apmRoot) {
|
|
|
2105
2363
|
return { written: [], skipped: [], removed: [], repositoryId: null };
|
|
2106
2364
|
}
|
|
2107
2365
|
const { list } = await api.cli.listRules({ repositoryId });
|
|
2108
|
-
const manifest =
|
|
2109
|
-
const nextManifest = { version: 1, rules: {} };
|
|
2366
|
+
const manifest = readApmManifest(targetApmRoot);
|
|
2110
2367
|
const remoteIds = /* @__PURE__ */ new Set();
|
|
2111
2368
|
const written = [];
|
|
2112
2369
|
const skipped = [];
|
|
2370
|
+
const ruleEntries = [];
|
|
2113
2371
|
for (const rule of list) {
|
|
2114
2372
|
remoteIds.add(rule.id);
|
|
2115
2373
|
const fileName = ruleLocalFileName(rule.name);
|
|
2116
|
-
const dest =
|
|
2117
|
-
const entry = manifest
|
|
2374
|
+
const dest = join13(rulesDir, fileName);
|
|
2375
|
+
const entry = findRuleManifestEntry(manifest, rule, fileName);
|
|
2118
2376
|
const updatedAt = rule.updatedAt ?? "";
|
|
2119
2377
|
if (isRuleUpToDate(entry, rule, dest)) {
|
|
2120
|
-
|
|
2378
|
+
ruleEntries.push(entry);
|
|
2121
2379
|
skipped.push(fileName);
|
|
2122
2380
|
console.log(`[apm] \u89C4\u5219\u65E0\u53D8\u5316\uFF0C\u5DF2\u8DF3\u8FC7: rules/${fileName}`);
|
|
2123
2381
|
continue;
|
|
2124
2382
|
}
|
|
2125
|
-
|
|
2126
|
-
|
|
2383
|
+
writeFileSync10(toFsPath(dest), rule.content ?? "", "utf8");
|
|
2384
|
+
ruleEntries.push(
|
|
2385
|
+
buildManifestEntryFromFile({
|
|
2386
|
+
path: toRuleManifestPath(fileName),
|
|
2387
|
+
content: rule.content ?? "",
|
|
2388
|
+
updatedAt,
|
|
2389
|
+
remoteId: rule.id
|
|
2390
|
+
})
|
|
2391
|
+
);
|
|
2127
2392
|
written.push(fileName);
|
|
2128
2393
|
console.log(`[apm] \u5DF2\u540C\u6B65\u5E73\u53F0\u89C4\u5219: rules/${fileName}`);
|
|
2129
2394
|
}
|
|
2130
2395
|
const removed = [];
|
|
2131
|
-
for (const
|
|
2132
|
-
if (
|
|
2133
|
-
if (
|
|
2134
|
-
const
|
|
2135
|
-
if (
|
|
2396
|
+
for (const entry of manifest?.documents ?? []) {
|
|
2397
|
+
if (!entry.path.startsWith("rules/")) continue;
|
|
2398
|
+
if (!entry.remoteId || remoteIds.has(entry.remoteId)) continue;
|
|
2399
|
+
const fileName = basename2(entry.path);
|
|
2400
|
+
if (isBaseRuleFileName(fileName)) continue;
|
|
2401
|
+
const dest = join13(rulesDir, fileName);
|
|
2402
|
+
if (existsSync9(toFsPath(dest))) {
|
|
2136
2403
|
rmSync3(toFsPath(dest), { force: true });
|
|
2137
2404
|
}
|
|
2138
|
-
removed.push(
|
|
2139
|
-
console.log(`[apm] \u5DF2\u79FB\u9664\u5DF2\u4E0B\u7EBF\u7684\u5E73\u53F0\u89C4\u5219: rules/${
|
|
2405
|
+
removed.push(fileName);
|
|
2406
|
+
console.log(`[apm] \u5DF2\u79FB\u9664\u5DF2\u4E0B\u7EBF\u7684\u5E73\u53F0\u89C4\u5219: rules/${fileName}`);
|
|
2407
|
+
}
|
|
2408
|
+
const coveredPaths = new Set(ruleEntries.map((entry) => entry.path));
|
|
2409
|
+
for (const fileName of readdirSync5(rulesDir)) {
|
|
2410
|
+
const dest = join13(rulesDir, fileName);
|
|
2411
|
+
if (!statSync4(dest).isFile()) continue;
|
|
2412
|
+
const manifestPath = toRuleManifestPath(fileName);
|
|
2413
|
+
if (coveredPaths.has(manifestPath)) continue;
|
|
2414
|
+
ruleEntries.push(
|
|
2415
|
+
buildManifestEntryFromFile({
|
|
2416
|
+
path: manifestPath,
|
|
2417
|
+
content: readFileSync11(toFsPath(dest), "utf8")
|
|
2418
|
+
})
|
|
2419
|
+
);
|
|
2140
2420
|
}
|
|
2141
|
-
|
|
2421
|
+
writeApmManifest(
|
|
2422
|
+
targetApmRoot,
|
|
2423
|
+
mergeManifestSection(manifest, "rules", ruleEntries, repositoryId)
|
|
2424
|
+
);
|
|
2142
2425
|
return { written, skipped, removed, repositoryId };
|
|
2143
2426
|
}
|
|
2144
2427
|
|
|
@@ -2150,15 +2433,14 @@ async function runTaskPull(cfg, detail) {
|
|
|
2150
2433
|
const dir = taskDir(taskId, apmRoot, workdir);
|
|
2151
2434
|
const docsDir = taskDocsDir(taskId, apmRoot, workdir);
|
|
2152
2435
|
await ensureDirExists(docsDir);
|
|
2153
|
-
|
|
2154
|
-
writeFileSync10(
|
|
2436
|
+
writeFileSync11(
|
|
2155
2437
|
taskTaskPath(taskId, apmRoot, workdir),
|
|
2156
2438
|
detail.task.description ?? "",
|
|
2157
2439
|
"utf8"
|
|
2158
2440
|
);
|
|
2159
2441
|
for (const doc of detail.documents) {
|
|
2160
2442
|
const fileName = documentLocalFileName(doc.name);
|
|
2161
|
-
|
|
2443
|
+
writeFileSync11(join14(docsDir, fileName), doc.content ?? "", "utf8");
|
|
2162
2444
|
}
|
|
2163
2445
|
const members = detail.studio?.members ?? [];
|
|
2164
2446
|
const taskYaml = yamlStringify(
|
|
@@ -2176,7 +2458,7 @@ async function runTaskPull(cfg, detail) {
|
|
|
2176
2458
|
},
|
|
2177
2459
|
{ lineWidth: 0 }
|
|
2178
2460
|
);
|
|
2179
|
-
|
|
2461
|
+
writeFileSync11(
|
|
2180
2462
|
taskYamlPath(taskId, apmRoot, workdir),
|
|
2181
2463
|
taskYaml.endsWith("\n") ? taskYaml : `${taskYaml}
|
|
2182
2464
|
`,
|
|
@@ -2420,7 +2702,7 @@ function reexecConnect(options) {
|
|
|
2420
2702
|
if (server) {
|
|
2421
2703
|
args.push("--server", server);
|
|
2422
2704
|
}
|
|
2423
|
-
const result =
|
|
2705
|
+
const result = spawnSync3(process.execPath, args, { stdio: "inherit" });
|
|
2424
2706
|
if (result.error) {
|
|
2425
2707
|
console.error("[apm] \u91CD\u542F connect \u5931\u8D25:", result.error.message);
|
|
2426
2708
|
process.exit(1);
|
|
@@ -2548,19 +2830,19 @@ async function runConnect(options) {
|
|
|
2548
2830
|
import path5 from "node:path";
|
|
2549
2831
|
|
|
2550
2832
|
// src/commands/deploy/internal/apm-config.ts
|
|
2551
|
-
import { existsSync as
|
|
2833
|
+
import { existsSync as existsSync10, readFileSync as readFileSync12 } from "node:fs";
|
|
2552
2834
|
import { resolve as resolve4 } from "node:path";
|
|
2553
2835
|
function loadApmConfig(options) {
|
|
2554
2836
|
const p = resolve4(
|
|
2555
2837
|
process.cwd(),
|
|
2556
2838
|
options?.configPath ?? resolve4(workspaceApmDir(), "apm.config.json")
|
|
2557
2839
|
);
|
|
2558
|
-
if (!
|
|
2840
|
+
if (!existsSync10(p)) {
|
|
2559
2841
|
console.error(`\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\uFF1A${p}`);
|
|
2560
2842
|
process.exit(1);
|
|
2561
2843
|
}
|
|
2562
2844
|
try {
|
|
2563
|
-
const raw =
|
|
2845
|
+
const raw = readFileSync12(p, "utf8");
|
|
2564
2846
|
return JSON.parse(raw);
|
|
2565
2847
|
} catch (e) {
|
|
2566
2848
|
console.error(`\u65E0\u6CD5\u89E3\u6790 apm.config.json\uFF1A${p}`, e);
|
|
@@ -2674,6 +2956,60 @@ function resolveWisdomDeployFromApmConfig(cfg) {
|
|
|
2674
2956
|
remotePath: reqWd(w.remotePath, "remotePath").trim()
|
|
2675
2957
|
};
|
|
2676
2958
|
}
|
|
2959
|
+
function reqHc(v, field) {
|
|
2960
|
+
if (v === void 0 || v === null || typeof v === "string" && !v.trim()) {
|
|
2961
|
+
console.error(`apm.config.json \u4E2D healthCheck.${field} \u4E0D\u80FD\u4E3A\u7A7A`);
|
|
2962
|
+
process.exit(1);
|
|
2963
|
+
}
|
|
2964
|
+
return v;
|
|
2965
|
+
}
|
|
2966
|
+
function reqHealthPositiveInt(v, field) {
|
|
2967
|
+
const n = Number(v);
|
|
2968
|
+
if (!Number.isFinite(n) || !Number.isInteger(n) || n < 1) {
|
|
2969
|
+
console.error(`apm.config.json \u4E2D healthCheck.${field} \u987B\u4E3A\u6B63\u6574\u6570`);
|
|
2970
|
+
process.exit(1);
|
|
2971
|
+
}
|
|
2972
|
+
return n;
|
|
2973
|
+
}
|
|
2974
|
+
function resolveHealthCheckFromApmConfig(cfg) {
|
|
2975
|
+
const h = cfg.healthCheck ?? {};
|
|
2976
|
+
return {
|
|
2977
|
+
port: reqHealthPositiveInt(h.port, "port"),
|
|
2978
|
+
context: reqHc(h.context, "context").trim(),
|
|
2979
|
+
timeout: reqHealthPositiveInt(h.timeout, "timeout")
|
|
2980
|
+
};
|
|
2981
|
+
}
|
|
2982
|
+
function resolveWisdomJarDeployFromApmConfig(cfg) {
|
|
2983
|
+
const base = resolveWisdomDeployFromApmConfig(cfg);
|
|
2984
|
+
const w = cfg.wisdomDeploy ?? {};
|
|
2985
|
+
const jarPath = reqWd(w.jarPath, "jarPath").trim().replace(/\\/g, "/");
|
|
2986
|
+
const remoteAppDir = posixDirname(jarPath);
|
|
2987
|
+
const projectName = reqTopLevelName(cfg);
|
|
2988
|
+
return {
|
|
2989
|
+
...base,
|
|
2990
|
+
projectName,
|
|
2991
|
+
jarPath,
|
|
2992
|
+
mavenLocalRepo: reqWd(w.mavenLocalRepo, "mavenLocalRepo").trim(),
|
|
2993
|
+
remoteAppDir,
|
|
2994
|
+
remoteLibDir: `${remoteAppDir}/lib`,
|
|
2995
|
+
startupJar: posixBasename(jarPath),
|
|
2996
|
+
packageName: `${projectName}.jar.zip`,
|
|
2997
|
+
healthCheck: resolveHealthCheckFromApmConfig(cfg)
|
|
2998
|
+
};
|
|
2999
|
+
}
|
|
3000
|
+
function posixDirname(p) {
|
|
3001
|
+
const normalized = p.replace(/\\/g, "/");
|
|
3002
|
+
const idx = normalized.lastIndexOf("/");
|
|
3003
|
+
if (idx <= 0) {
|
|
3004
|
+
return normalized.startsWith("/") ? "/" : ".";
|
|
3005
|
+
}
|
|
3006
|
+
return normalized.slice(0, idx);
|
|
3007
|
+
}
|
|
3008
|
+
function posixBasename(p) {
|
|
3009
|
+
const normalized = p.replace(/\\/g, "/");
|
|
3010
|
+
const idx = normalized.lastIndexOf("/");
|
|
3011
|
+
return idx >= 0 ? normalized.slice(idx + 1) : normalized;
|
|
3012
|
+
}
|
|
2677
3013
|
|
|
2678
3014
|
// src/commands/deploy/internal/backend-deploy/backend-deploy-workflow.ts
|
|
2679
3015
|
import path4 from "node:path";
|
|
@@ -2682,7 +3018,7 @@ import path4 from "node:path";
|
|
|
2682
3018
|
import Docker from "dockerode";
|
|
2683
3019
|
|
|
2684
3020
|
// src/commands/deploy/internal/backend-deploy/dockerode-client/connection-options.ts
|
|
2685
|
-
import { existsSync as
|
|
3021
|
+
import { existsSync as existsSync11, readFileSync as readFileSync13 } from "node:fs";
|
|
2686
3022
|
import path from "node:path";
|
|
2687
3023
|
function asOptionalTlsBuffer(value) {
|
|
2688
3024
|
if (typeof value !== "string") {
|
|
@@ -2694,8 +3030,8 @@ function asOptionalTlsBuffer(value) {
|
|
|
2694
3030
|
if (normalized === "") {
|
|
2695
3031
|
return void 0;
|
|
2696
3032
|
}
|
|
2697
|
-
if (
|
|
2698
|
-
return
|
|
3033
|
+
if (existsSync11(normalized)) {
|
|
3034
|
+
return readFileSync13(normalized);
|
|
2699
3035
|
}
|
|
2700
3036
|
const looksLikePath = /[\\/]/.test(normalized) || normalized.endsWith(".pem");
|
|
2701
3037
|
if (looksLikePath) {
|
|
@@ -2905,7 +3241,7 @@ var DockerodeClient = class {
|
|
|
2905
3241
|
var createDockerodeClient = (config) => new DockerodeClient(config);
|
|
2906
3242
|
|
|
2907
3243
|
// src/commands/deploy/internal/backend-deploy/dockerode-client/env.ts
|
|
2908
|
-
import { existsSync as
|
|
3244
|
+
import { existsSync as existsSync12, readFileSync as readFileSync14, statSync as statSync5 } from "node:fs";
|
|
2909
3245
|
import path2 from "node:path";
|
|
2910
3246
|
function stripSurroundingQuotes(value) {
|
|
2911
3247
|
const t = value.trim();
|
|
@@ -2922,10 +3258,10 @@ function loadEnvFromFile(envFilePath) {
|
|
|
2922
3258
|
return {};
|
|
2923
3259
|
}
|
|
2924
3260
|
const targetPath = path2.resolve(envFilePath);
|
|
2925
|
-
if (!
|
|
3261
|
+
if (!existsSync12(targetPath) || !statSync5(targetPath).isFile()) {
|
|
2926
3262
|
return {};
|
|
2927
3263
|
}
|
|
2928
|
-
const raw =
|
|
3264
|
+
const raw = readFileSync14(targetPath, "utf-8");
|
|
2929
3265
|
const result = {};
|
|
2930
3266
|
for (const line of raw.split(/\r?\n/)) {
|
|
2931
3267
|
const normalized = line.trim();
|
|
@@ -2990,7 +3326,7 @@ function assertDeployImageTag(tag) {
|
|
|
2990
3326
|
}
|
|
2991
3327
|
|
|
2992
3328
|
// src/commands/deploy/internal/backend-deploy/local-docker-build.ts
|
|
2993
|
-
import { platform as
|
|
3329
|
+
import { platform as platform3 } from "node:os";
|
|
2994
3330
|
|
|
2995
3331
|
// src/commands/deploy/internal/backend-deploy/command-runner.ts
|
|
2996
3332
|
import { execSync } from "child_process";
|
|
@@ -3050,7 +3386,7 @@ var CommandRunner = class {
|
|
|
3050
3386
|
|
|
3051
3387
|
// src/commands/deploy/internal/backend-deploy/local-docker-build.ts
|
|
3052
3388
|
function dockerBuildPlatformFlags() {
|
|
3053
|
-
return
|
|
3389
|
+
return platform3() === "darwin" ? ["--platform", "linux/amd64"] : [];
|
|
3054
3390
|
}
|
|
3055
3391
|
function buildDockerImageLocally(params, cwd) {
|
|
3056
3392
|
const platformFlags = dockerBuildPlatformFlags();
|
|
@@ -3096,12 +3432,12 @@ function dockerPushImage(params, cwd) {
|
|
|
3096
3432
|
}
|
|
3097
3433
|
|
|
3098
3434
|
// src/commands/deploy/internal/backend-deploy/resolve-dockerfile.ts
|
|
3099
|
-
import { existsSync as
|
|
3435
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
3100
3436
|
import path3 from "node:path";
|
|
3101
3437
|
function resolveDockerBuildPaths(cwd) {
|
|
3102
3438
|
const dockerfilePath = path3.join(cwd, "Dockerfile");
|
|
3103
3439
|
Logger.info(`\u67E5\u627EDockerfile\u6587\u4EF6\uFF0C\u8DEF\u5F84: ${dockerfilePath}`);
|
|
3104
|
-
if (!
|
|
3440
|
+
if (!existsSync13(dockerfilePath)) {
|
|
3105
3441
|
throw new Error(`Dockerfile \u4E0D\u5B58\u5728\uFF1A${dockerfilePath}`);
|
|
3106
3442
|
}
|
|
3107
3443
|
Logger.info("\u2713 Dockerfile \u5B58\u5728");
|
|
@@ -3230,14 +3566,14 @@ import { copyFile, readdir as readdir2, stat } from "node:fs/promises";
|
|
|
3230
3566
|
import path7 from "node:path";
|
|
3231
3567
|
|
|
3232
3568
|
// src/commands/deploy/internal/minio.ts
|
|
3233
|
-
import { statSync as
|
|
3569
|
+
import { statSync as statSync6 } from "node:fs";
|
|
3234
3570
|
import { readdir, readFile } from "node:fs/promises";
|
|
3235
3571
|
import path6 from "node:path";
|
|
3236
3572
|
import * as Minio from "minio";
|
|
3237
3573
|
var DEFAULT_MAX_FILE_SIZE_MB = 50;
|
|
3238
3574
|
async function isDirectoryPath(dir) {
|
|
3239
3575
|
try {
|
|
3240
|
-
const st =
|
|
3576
|
+
const st = statSync6(dir);
|
|
3241
3577
|
return st.isDirectory();
|
|
3242
3578
|
} catch {
|
|
3243
3579
|
return false;
|
|
@@ -3267,7 +3603,7 @@ async function collectFiles(root) {
|
|
|
3267
3603
|
if (e.isDirectory()) {
|
|
3268
3604
|
await walk(abs, rel);
|
|
3269
3605
|
} else if (e.isFile()) {
|
|
3270
|
-
const st =
|
|
3606
|
+
const st = statSync6(abs);
|
|
3271
3607
|
out.push({
|
|
3272
3608
|
absPath: abs,
|
|
3273
3609
|
relativePath: rel.replace(/\\/g, "/"),
|
|
@@ -3525,18 +3861,47 @@ function registerDeployFrontendCommands(program) {
|
|
|
3525
3861
|
);
|
|
3526
3862
|
}
|
|
3527
3863
|
|
|
3864
|
+
// src/commands/deploy/run.ts
|
|
3865
|
+
import path8 from "node:path";
|
|
3866
|
+
function registerDeployRunCommands(program) {
|
|
3867
|
+
program.command("deploy").description(
|
|
3868
|
+
"\u8BFB\u53D6 apm.config.json \u4E2D deploy.<env> \u5E76\u4EE5 shell \u6267\u884C\uFF08\u73AF\u5883\uFF1Atest | online\uFF09"
|
|
3869
|
+
).argument("<env>", "\u90E8\u7F72\u73AF\u5883\uFF1Atest \u6216 online").option("--cwd <path>", "\u5DE5\u4F5C\u76EE\u5F55\uFF08\u9ED8\u8BA4\u5F53\u524D\u76EE\u5F55\uFF09").action(async (env, opts) => {
|
|
3870
|
+
if (!isDeployEnvironment(env)) {
|
|
3871
|
+
console.error(`\u65E0\u6548\u90E8\u7F72\u73AF\u5883 "${env}"\uFF0C\u4EC5\u652F\u6301 test \u6216 online`);
|
|
3872
|
+
process.exit(1);
|
|
3873
|
+
}
|
|
3874
|
+
const workdir = path8.resolve(opts.cwd ?? process.cwd());
|
|
3875
|
+
const command = resolveDeployCommand(workdir, env);
|
|
3876
|
+
if (!command) {
|
|
3877
|
+
console.error(missingDeployCommandMessage(env));
|
|
3878
|
+
process.exit(1);
|
|
3879
|
+
}
|
|
3880
|
+
console.log(`[apm] deploy env=${env} cwd=${workdir}`);
|
|
3881
|
+
console.log(`[apm] deploy command: ${command}`);
|
|
3882
|
+
try {
|
|
3883
|
+
await runDeployShellCommand(command, workdir, { inheritStdio: true });
|
|
3884
|
+
console.log(`[apm] deploy success env=${env}`);
|
|
3885
|
+
} catch (error) {
|
|
3886
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
3887
|
+
console.error(`[apm] deploy failed env=${env}: ${detail}`);
|
|
3888
|
+
process.exit(1);
|
|
3889
|
+
}
|
|
3890
|
+
});
|
|
3891
|
+
}
|
|
3892
|
+
|
|
3528
3893
|
// src/commands/deploy/sftp.ts
|
|
3529
|
-
import
|
|
3894
|
+
import path10 from "node:path";
|
|
3530
3895
|
|
|
3531
3896
|
// src/commands/deploy/internal/wisdom-sftp.ts
|
|
3532
3897
|
import { readdir as readdir3, readFile as readFile2, unlink, writeFile } from "node:fs/promises";
|
|
3533
|
-
import
|
|
3898
|
+
import path9 from "node:path";
|
|
3534
3899
|
import JSZip from "jszip";
|
|
3535
3900
|
import SftpClient from "ssh2-sftp-client";
|
|
3536
3901
|
async function addDirToZip(dir, zipFolder) {
|
|
3537
3902
|
const entries = await readdir3(dir, { withFileTypes: true });
|
|
3538
3903
|
for (const entry of entries) {
|
|
3539
|
-
const fullPath =
|
|
3904
|
+
const fullPath = path9.join(dir, entry.name);
|
|
3540
3905
|
if (entry.isDirectory()) {
|
|
3541
3906
|
const folder = zipFolder.folder(entry.name);
|
|
3542
3907
|
if (folder) {
|
|
@@ -3651,8 +4016,8 @@ async function uploadAndMaybeExtract(settings, localZip, extract) {
|
|
|
3651
4016
|
}
|
|
3652
4017
|
}
|
|
3653
4018
|
async function runWisdomSftpDeploy(params) {
|
|
3654
|
-
const zipPath =
|
|
3655
|
-
const resolvedZipPath =
|
|
4019
|
+
const zipPath = path9.join(params.localDir, "..", ".deploy-sftp-dist.zip");
|
|
4020
|
+
const resolvedZipPath = path9.resolve(zipPath);
|
|
3656
4021
|
let zipSizeBytes = 0;
|
|
3657
4022
|
try {
|
|
3658
4023
|
zipSizeBytes = await zipDirectory(params.localDir, resolvedZipPath);
|
|
@@ -3693,7 +4058,7 @@ function registerDeploySftpCommands(program) {
|
|
|
3693
4058
|
async (opts) => {
|
|
3694
4059
|
const cfg = loadApmConfig({ configPath: opts.config });
|
|
3695
4060
|
const settings = resolveWisdomDeployFromApmConfig(cfg);
|
|
3696
|
-
const root =
|
|
4061
|
+
const root = path10.resolve(process.cwd(), opts.dir || "apps/web/dist");
|
|
3697
4062
|
if (!await isDirectoryPath(root)) {
|
|
3698
4063
|
console.error(`\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${root}`);
|
|
3699
4064
|
process.exit(1);
|
|
@@ -3714,11 +4079,501 @@ function registerDeploySftpCommands(program) {
|
|
|
3714
4079
|
);
|
|
3715
4080
|
}
|
|
3716
4081
|
|
|
4082
|
+
// src/commands/deploy/wisdom-jar.ts
|
|
4083
|
+
import path12 from "node:path";
|
|
4084
|
+
|
|
4085
|
+
// src/commands/deploy/internal/wisdom-jar-deploy.ts
|
|
4086
|
+
import {
|
|
4087
|
+
existsSync as existsSync14,
|
|
4088
|
+
mkdirSync as mkdirSync7,
|
|
4089
|
+
readFileSync as readFileSync15,
|
|
4090
|
+
readdirSync as readdirSync6,
|
|
4091
|
+
statSync as statSync7,
|
|
4092
|
+
writeFileSync as writeFileSync12
|
|
4093
|
+
} from "node:fs";
|
|
4094
|
+
import { readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
|
|
4095
|
+
import path11 from "node:path";
|
|
4096
|
+
import JSZip2 from "jszip";
|
|
4097
|
+
import SftpClient2 from "ssh2-sftp-client";
|
|
4098
|
+
var DEFAULT_MAVEN_MODULE = "jeecg-module-system/jeecg-system-start";
|
|
4099
|
+
var DEFAULT_MAVEN_PROFILE = "dev";
|
|
4100
|
+
var SPRINGBOOT_SCRIPT = "springboot.sh";
|
|
4101
|
+
function log(message) {
|
|
4102
|
+
const now = /* @__PURE__ */ new Date();
|
|
4103
|
+
const hh = String(now.getHours()).padStart(2, "0");
|
|
4104
|
+
const mm = String(now.getMinutes()).padStart(2, "0");
|
|
4105
|
+
const ss = String(now.getSeconds()).padStart(2, "0");
|
|
4106
|
+
console.log(`[${hh}:${mm}:${ss}] ${message}`);
|
|
4107
|
+
}
|
|
4108
|
+
function fail(message) {
|
|
4109
|
+
log(`ERROR: ${message}`);
|
|
4110
|
+
process.exit(1);
|
|
4111
|
+
}
|
|
4112
|
+
function shellSingleQuote2(value) {
|
|
4113
|
+
return `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
4114
|
+
}
|
|
4115
|
+
function expandPath(pathStr) {
|
|
4116
|
+
return path11.resolve(expandUserHomePath(pathStr));
|
|
4117
|
+
}
|
|
4118
|
+
function relativeKey(projectRoot, filePath) {
|
|
4119
|
+
return path11.relative(projectRoot, path11.resolve(filePath)).split(path11.sep).join("/");
|
|
4120
|
+
}
|
|
4121
|
+
function fileSignature(filePath) {
|
|
4122
|
+
const stat2 = statSync7(filePath);
|
|
4123
|
+
return { size: stat2.size, mtime: stat2.mtimeMs };
|
|
4124
|
+
}
|
|
4125
|
+
function loadManifest(manifestFile) {
|
|
4126
|
+
if (!existsSync14(manifestFile)) {
|
|
4127
|
+
return {};
|
|
4128
|
+
}
|
|
4129
|
+
return JSON.parse(readFileSync15(manifestFile, "utf8"));
|
|
4130
|
+
}
|
|
4131
|
+
function saveManifest(manifestFile, manifest) {
|
|
4132
|
+
const dir = path11.dirname(manifestFile);
|
|
4133
|
+
mkdirSync7(dir, { recursive: true });
|
|
4134
|
+
writeFileSync12(manifestFile, JSON.stringify(manifest, null, 2), "utf8");
|
|
4135
|
+
}
|
|
4136
|
+
function isProjectLibJar(jarName) {
|
|
4137
|
+
return jarName.startsWith("jeecg-");
|
|
4138
|
+
}
|
|
4139
|
+
function shouldUploadLibFile(projectRoot, localPath, remoteSize, manifest) {
|
|
4140
|
+
if (remoteSize === void 0) {
|
|
4141
|
+
return { shouldUpload: false, reason: "\u8FDC\u7A0B\u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7" };
|
|
4142
|
+
}
|
|
4143
|
+
const localSize = statSync7(localPath).size;
|
|
4144
|
+
if (localSize !== remoteSize) {
|
|
4145
|
+
return {
|
|
4146
|
+
shouldUpload: true,
|
|
4147
|
+
reason: `\u5927\u5C0F\u53D8\u5316 ${remoteSize} -> ${localSize}`
|
|
4148
|
+
};
|
|
4149
|
+
}
|
|
4150
|
+
const jarName = path11.basename(localPath);
|
|
4151
|
+
if (isProjectLibJar(jarName) && manifest) {
|
|
4152
|
+
const key = relativeKey(projectRoot, localPath);
|
|
4153
|
+
const current = fileSignature(localPath);
|
|
4154
|
+
const previous = manifest[key];
|
|
4155
|
+
if (!previous) {
|
|
4156
|
+
return { shouldUpload: true, reason: "\u9879\u76EE\u6A21\u5757\u672A\u8BB0\u5F55" };
|
|
4157
|
+
}
|
|
4158
|
+
if (previous.size !== current.size) {
|
|
4159
|
+
return { shouldUpload: true, reason: "\u9879\u76EE\u6A21\u5757\u5927\u5C0F\u53D8\u5316" };
|
|
4160
|
+
}
|
|
4161
|
+
if (previous.mtime < current.mtime) {
|
|
4162
|
+
return { shouldUpload: true, reason: "\u9879\u76EE\u6A21\u5757\u91CD\u65B0\u6784\u5EFA" };
|
|
4163
|
+
}
|
|
4164
|
+
}
|
|
4165
|
+
return { shouldUpload: false, reason: "\u5927\u5C0F\u4E00\u81F4\uFF0C\u8DF3\u8FC7" };
|
|
4166
|
+
}
|
|
4167
|
+
function listLibFilesToUpload(projectRoot, localLibDir, remoteStats, manifest) {
|
|
4168
|
+
const entries = [];
|
|
4169
|
+
const jarFiles = readdirSync6(localLibDir).filter((name) => name.endsWith(".jar")).sort();
|
|
4170
|
+
for (const jarName of jarFiles) {
|
|
4171
|
+
const jarPath = path11.join(localLibDir, jarName);
|
|
4172
|
+
const { shouldUpload, reason } = shouldUploadLibFile(
|
|
4173
|
+
projectRoot,
|
|
4174
|
+
jarPath,
|
|
4175
|
+
remoteStats.get(jarName),
|
|
4176
|
+
manifest
|
|
4177
|
+
);
|
|
4178
|
+
if (shouldUpload) {
|
|
4179
|
+
entries.push({ path: jarPath, arcname: jarName, reason });
|
|
4180
|
+
}
|
|
4181
|
+
}
|
|
4182
|
+
return entries;
|
|
4183
|
+
}
|
|
4184
|
+
async function createUpdatePackage(entries, packageName, cacheDir) {
|
|
4185
|
+
mkdirSync7(cacheDir, { recursive: true });
|
|
4186
|
+
const zipPath = path11.join(cacheDir, packageName);
|
|
4187
|
+
log(`\u521B\u5EFA\u66F4\u65B0\u5305: ${packageName}\uFF08${entries.length} \u4E2A\u6587\u4EF6\uFF09`);
|
|
4188
|
+
const zip = new JSZip2();
|
|
4189
|
+
for (const entry of entries) {
|
|
4190
|
+
const content = await readFile3(entry.path);
|
|
4191
|
+
zip.file(entry.arcname, content);
|
|
4192
|
+
log(` \u6253\u5305: ${entry.arcname} (${entry.reason})`);
|
|
4193
|
+
}
|
|
4194
|
+
const buffer = await zip.generateAsync({
|
|
4195
|
+
type: "nodebuffer",
|
|
4196
|
+
compression: "DEFLATE",
|
|
4197
|
+
compressionOptions: { level: 6 }
|
|
4198
|
+
});
|
|
4199
|
+
await writeFile2(zipPath, buffer);
|
|
4200
|
+
return zipPath;
|
|
4201
|
+
}
|
|
4202
|
+
function getMvnExecutable() {
|
|
4203
|
+
const candidates = process.platform === "win32" ? ["mvn.cmd", "mvn.bat", "mvn"] : ["mvn"];
|
|
4204
|
+
for (const name of candidates) {
|
|
4205
|
+
const result = spawnSyncShellCommand(`${name} --version`, {
|
|
4206
|
+
encoding: "utf8"
|
|
4207
|
+
});
|
|
4208
|
+
if (result.status === 0) {
|
|
4209
|
+
return name;
|
|
4210
|
+
}
|
|
4211
|
+
}
|
|
4212
|
+
fail("\u672A\u627E\u5230 mvn \u547D\u4EE4\uFF0C\u8BF7\u786E\u8BA4 Maven \u5DF2\u5B89\u88C5\u5E76\u52A0\u5165 PATH");
|
|
4213
|
+
return "mvn";
|
|
4214
|
+
}
|
|
4215
|
+
function runMavenBuild(projectRoot, mavenRepo, profile) {
|
|
4216
|
+
const mvn = getMvnExecutable();
|
|
4217
|
+
const repoArg = quoteShellArg(normalizeMavenLocalRepoPath(mavenRepo));
|
|
4218
|
+
const command = `${mvn} clean package -P${profile} -Dmaven.repo.local=${repoArg} -DskipTests`;
|
|
4219
|
+
log(`\u5F00\u59CB Maven \u6784\u5EFA: ${command}`);
|
|
4220
|
+
log(`Maven \u672C\u5730\u4ED3\u5E93: ${mavenRepo}`);
|
|
4221
|
+
const result = spawnSyncShellCommand(command, {
|
|
4222
|
+
cwd: projectRoot,
|
|
4223
|
+
stdio: "inherit"
|
|
4224
|
+
});
|
|
4225
|
+
if (result.status !== 0) {
|
|
4226
|
+
fail(`Maven \u6784\u5EFA\u5931\u8D25\uFF0C\u9000\u51FA\u7801: ${result.status ?? "unknown"}`);
|
|
4227
|
+
}
|
|
4228
|
+
}
|
|
4229
|
+
function locateLibDir(projectRoot, mavenModule) {
|
|
4230
|
+
const targetDir = path11.join(projectRoot, mavenModule, "target");
|
|
4231
|
+
if (!existsSync14(targetDir)) {
|
|
4232
|
+
fail(`\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728: ${targetDir}`);
|
|
4233
|
+
}
|
|
4234
|
+
const libDir = path11.join(targetDir, "lib");
|
|
4235
|
+
if (!existsSync14(libDir) || !statSync7(libDir).isDirectory()) {
|
|
4236
|
+
fail(`lib \u76EE\u5F55\u4E0D\u5B58\u5728: ${libDir}`);
|
|
4237
|
+
}
|
|
4238
|
+
const libJars = readdirSync6(libDir).filter((name) => name.endsWith(".jar"));
|
|
4239
|
+
if (libJars.length === 0) {
|
|
4240
|
+
fail(`lib \u76EE\u5F55\u4E0B\u6CA1\u6709\u4F9D\u8D56 JAR: ${libDir}`);
|
|
4241
|
+
}
|
|
4242
|
+
log(`\u5B9A\u4F4D lib \u4EA7\u7269: ${libJars.length} \u4E2A`);
|
|
4243
|
+
return libDir;
|
|
4244
|
+
}
|
|
4245
|
+
async function getRemoteFileStats(sftp, remoteDir) {
|
|
4246
|
+
const stats = /* @__PURE__ */ new Map();
|
|
4247
|
+
try {
|
|
4248
|
+
const list = await sftp.list(remoteDir);
|
|
4249
|
+
for (const item of list) {
|
|
4250
|
+
if (item.name.endsWith(".jar")) {
|
|
4251
|
+
stats.set(item.name, item.size);
|
|
4252
|
+
}
|
|
4253
|
+
}
|
|
4254
|
+
} catch {
|
|
4255
|
+
}
|
|
4256
|
+
return stats;
|
|
4257
|
+
}
|
|
4258
|
+
async function uploadUpdatePackage(sftp, zipPath, settings) {
|
|
4259
|
+
const remoteDir = settings.remotePath.replace(/\/$/, "");
|
|
4260
|
+
const remotePath = `${remoteDir}/${path11.basename(zipPath)}`;
|
|
4261
|
+
log(`\u4E0A\u4F20\u66F4\u65B0\u5305 -> ${remotePath}`);
|
|
4262
|
+
try {
|
|
4263
|
+
await sftp.put(zipPath, remotePath);
|
|
4264
|
+
log("\u66F4\u65B0\u5305\u4E0A\u4F20\u6210\u529F");
|
|
4265
|
+
} catch (err) {
|
|
4266
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4267
|
+
fail(`\u66F4\u65B0\u5305\u4E0A\u4F20\u5931\u8D25: ${message}`);
|
|
4268
|
+
}
|
|
4269
|
+
return remotePath;
|
|
4270
|
+
}
|
|
4271
|
+
function updateManifestEntries(projectRoot, manifest, entries) {
|
|
4272
|
+
for (const entry of entries) {
|
|
4273
|
+
manifest[relativeKey(projectRoot, entry.path)] = fileSignature(entry.path);
|
|
4274
|
+
}
|
|
4275
|
+
return manifest;
|
|
4276
|
+
}
|
|
4277
|
+
function runRemoteCommand(client, command, options = {}) {
|
|
4278
|
+
const { check = true, stream = false, getPty = false } = options;
|
|
4279
|
+
log(`\u8FDC\u7A0B\u6267\u884C: ${command}`);
|
|
4280
|
+
return new Promise((resolve5, reject) => {
|
|
4281
|
+
client.exec(command, { pty: getPty }, (err, execStream) => {
|
|
4282
|
+
if (err) {
|
|
4283
|
+
reject(err);
|
|
4284
|
+
return;
|
|
4285
|
+
}
|
|
4286
|
+
let stdout = "";
|
|
4287
|
+
let stderr = "";
|
|
4288
|
+
execStream.on("data", (data) => {
|
|
4289
|
+
const text = data.toString("utf8");
|
|
4290
|
+
stdout += text;
|
|
4291
|
+
if (stream) {
|
|
4292
|
+
process.stdout.write(text);
|
|
4293
|
+
}
|
|
4294
|
+
});
|
|
4295
|
+
execStream.stderr.on("data", (data) => {
|
|
4296
|
+
const text = data.toString("utf8");
|
|
4297
|
+
stderr += text;
|
|
4298
|
+
if (stream) {
|
|
4299
|
+
process.stdout.write(text);
|
|
4300
|
+
}
|
|
4301
|
+
});
|
|
4302
|
+
execStream.on("close", (code) => {
|
|
4303
|
+
const exitCode = code ?? 0;
|
|
4304
|
+
const out = stdout.trim();
|
|
4305
|
+
const errText = getPty ? "" : stderr.trim();
|
|
4306
|
+
if (!stream) {
|
|
4307
|
+
if (out) {
|
|
4308
|
+
console.log(out);
|
|
4309
|
+
}
|
|
4310
|
+
if (errText) {
|
|
4311
|
+
console.error(errText);
|
|
4312
|
+
}
|
|
4313
|
+
} else if (stdout && !stdout.endsWith("\n")) {
|
|
4314
|
+
console.log();
|
|
4315
|
+
}
|
|
4316
|
+
if (check && exitCode !== 0) {
|
|
4317
|
+
fail(`\u8FDC\u7A0B\u547D\u4EE4\u5931\u8D25 (exit ${exitCode}): ${command}`);
|
|
4318
|
+
}
|
|
4319
|
+
resolve5({ exitCode, stdout: out, stderr: errText });
|
|
4320
|
+
});
|
|
4321
|
+
});
|
|
4322
|
+
});
|
|
4323
|
+
}
|
|
4324
|
+
async function extractUpdatePackageOnRemote(client, settings, remoteZipPath) {
|
|
4325
|
+
const remoteLibDir = settings.remoteLibDir;
|
|
4326
|
+
const quotedZip = shellSingleQuote2(remoteZipPath);
|
|
4327
|
+
const quotedLib = shellSingleQuote2(remoteLibDir);
|
|
4328
|
+
const script = `
|
|
4329
|
+
set -e
|
|
4330
|
+
TMP=$(mktemp -d)
|
|
4331
|
+
trap 'rm -rf "$TMP"' EXIT
|
|
4332
|
+
unzip -oq ${quotedZip} -d "$TMP"
|
|
4333
|
+
updated=0
|
|
4334
|
+
while IFS= read -r -d '' src; do
|
|
4335
|
+
name=$(basename "$src")
|
|
4336
|
+
dest=${quotedLib}/"$name"
|
|
4337
|
+
if [ -f "$dest" ]; then
|
|
4338
|
+
cp -f "$src" "$dest"
|
|
4339
|
+
echo "\u8986\u76D6: $name"
|
|
4340
|
+
updated=$((updated + 1))
|
|
4341
|
+
else
|
|
4342
|
+
echo "\u8DF3\u8FC7(\u8FDC\u7A0B\u4E0D\u5B58\u5728): $name"
|
|
4343
|
+
fi
|
|
4344
|
+
done < <(find "$TMP" -name '*.jar' -type f -print0)
|
|
4345
|
+
echo "UPDATED_COUNT=$updated"
|
|
4346
|
+
`;
|
|
4347
|
+
const { stdout } = await runRemoteCommand(client, script);
|
|
4348
|
+
const match = stdout.match(/UPDATED_COUNT=(\d+)/);
|
|
4349
|
+
if (!match || match[1] === void 0) {
|
|
4350
|
+
fail(`\u8FDC\u7A0B\u89E3\u538B\u5931\u8D25\uFF0C\u672A\u83B7\u53D6\u66F4\u65B0\u6570\u91CF
|
|
4351
|
+
\u8F93\u51FA: ${stdout || "(\u7A7A)"}`);
|
|
4352
|
+
return 0;
|
|
4353
|
+
}
|
|
4354
|
+
const updated = Number.parseInt(match[1], 10);
|
|
4355
|
+
log(`lib \u89E3\u538B\u5B8C\u6210: \u8986\u76D6 ${updated} \u4E2A`);
|
|
4356
|
+
return updated;
|
|
4357
|
+
}
|
|
4358
|
+
function springbootOutputIndicatesSuccess(action, combined) {
|
|
4359
|
+
const lower = combined.toLowerCase();
|
|
4360
|
+
if (action === "health") {
|
|
4361
|
+
return combined.includes("\u5065\u5EB7\u68C0\u67E5\u901A\u8FC7");
|
|
4362
|
+
}
|
|
4363
|
+
if (action === "start" || action === "restart") {
|
|
4364
|
+
return combined.includes("is starting") || lower.includes("is running");
|
|
4365
|
+
}
|
|
4366
|
+
if (action === "stop") {
|
|
4367
|
+
return combined.includes("is stopping") || lower.includes("not running") || lower.includes("please check it");
|
|
4368
|
+
}
|
|
4369
|
+
if (action === "status") {
|
|
4370
|
+
return lower.includes("running") || lower.includes("not running");
|
|
4371
|
+
}
|
|
4372
|
+
return true;
|
|
4373
|
+
}
|
|
4374
|
+
async function runSpringbootAction(client, settings, action, args = []) {
|
|
4375
|
+
if (action === "start" || action === "restart") {
|
|
4376
|
+
if (args.length === 0) {
|
|
4377
|
+
fail(`\u8FDC\u7A0B ${action} \u7F3A\u5C11 jar \u53C2\u6570`);
|
|
4378
|
+
}
|
|
4379
|
+
const jar = args[0].trim().split(/\r?\n/)[0]?.trim() ?? "";
|
|
4380
|
+
if (!jar) {
|
|
4381
|
+
fail(`\u65E0\u6548\u7684 jar \u540D\u79F0: ${JSON.stringify(args[0])}`);
|
|
4382
|
+
}
|
|
4383
|
+
}
|
|
4384
|
+
const quotedAppDir = shellSingleQuote2(settings.remoteAppDir);
|
|
4385
|
+
const scriptArgs = [action, ...args].map(shellSingleQuote2).join(" ");
|
|
4386
|
+
const command = `cd ${quotedAppDir} && ./${SPRINGBOOT_SCRIPT} ${scriptArgs}`;
|
|
4387
|
+
const { exitCode, stdout, stderr } = await runRemoteCommand(client, command, {
|
|
4388
|
+
check: false
|
|
4389
|
+
});
|
|
4390
|
+
const combined = `${stdout}
|
|
4391
|
+
${stderr}`.trim();
|
|
4392
|
+
const outputOk = springbootOutputIndicatesSuccess(action, combined);
|
|
4393
|
+
if (action === "health") {
|
|
4394
|
+
if (exitCode !== 0 || !outputOk) {
|
|
4395
|
+
fail(`\u5065\u5EB7\u68C0\u67E5\u5931\u8D25
|
|
4396
|
+
\u547D\u4EE4: ${command}
|
|
4397
|
+
\u8F93\u51FA: ${combined || "(\u7A7A)"}`);
|
|
4398
|
+
}
|
|
4399
|
+
return combined;
|
|
4400
|
+
}
|
|
4401
|
+
if (exitCode !== 0 && !outputOk) {
|
|
4402
|
+
fail(`\u8FDC\u7A0B ${action} \u5931\u8D25: ${args.join(" ")}
|
|
4403
|
+
${combined}`);
|
|
4404
|
+
}
|
|
4405
|
+
if ((action === "start" || action === "restart") && !outputOk) {
|
|
4406
|
+
fail(
|
|
4407
|
+
`\u8FDC\u7A0B ${action} \u672A\u6210\u529F: ${args.join(" ")}
|
|
4408
|
+
\u547D\u4EE4: ${command}
|
|
4409
|
+
\u8F93\u51FA: ${combined || "(\u7A7A)"}`
|
|
4410
|
+
);
|
|
4411
|
+
}
|
|
4412
|
+
return combined;
|
|
4413
|
+
}
|
|
4414
|
+
async function getRunningJar(client, settings) {
|
|
4415
|
+
const combined = await runSpringbootAction(client, settings, "status", [
|
|
4416
|
+
settings.startupJar
|
|
4417
|
+
]);
|
|
4418
|
+
const text = combined.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "").trim().toLowerCase();
|
|
4419
|
+
if (text.includes("not running")) {
|
|
4420
|
+
return null;
|
|
4421
|
+
}
|
|
4422
|
+
if (text.includes("running")) {
|
|
4423
|
+
return settings.startupJar;
|
|
4424
|
+
}
|
|
4425
|
+
return null;
|
|
4426
|
+
}
|
|
4427
|
+
async function healthCheckService(client, settings) {
|
|
4428
|
+
const port = String(settings.healthCheck.port);
|
|
4429
|
+
const context = settings.healthCheck.context.trim();
|
|
4430
|
+
const timeout = String(settings.healthCheck.timeout);
|
|
4431
|
+
log(`\u5065\u5EB7\u68C0\u67E5: springboot.sh health ${port} ${context} ${timeout}`);
|
|
4432
|
+
await runSpringbootAction(client, settings, "health", [
|
|
4433
|
+
port,
|
|
4434
|
+
context,
|
|
4435
|
+
timeout
|
|
4436
|
+
]);
|
|
4437
|
+
}
|
|
4438
|
+
async function runWisdomJarDeploy(params) {
|
|
4439
|
+
const {
|
|
4440
|
+
projectRoot,
|
|
4441
|
+
deployCacheDir,
|
|
4442
|
+
configPath,
|
|
4443
|
+
settings,
|
|
4444
|
+
mavenModule = DEFAULT_MAVEN_MODULE,
|
|
4445
|
+
mavenProfile = DEFAULT_MAVEN_PROFILE,
|
|
4446
|
+
skipBuild = false
|
|
4447
|
+
} = params;
|
|
4448
|
+
const manifestFile = path11.join(deployCacheDir, "manifest.json");
|
|
4449
|
+
const mavenRepo = expandPath(settings.mavenLocalRepo);
|
|
4450
|
+
log(`=== \u81EA\u52A8\u90E8\u7F72: ${settings.projectName} ===`);
|
|
4451
|
+
log(`\u914D\u7F6E\u6587\u4EF6: ${configPath}`);
|
|
4452
|
+
log(`\u9879\u76EE\u6839\u76EE\u5F55: ${projectRoot}`);
|
|
4453
|
+
if (!skipBuild) {
|
|
4454
|
+
runMavenBuild(projectRoot, mavenRepo, mavenProfile);
|
|
4455
|
+
} else {
|
|
4456
|
+
log("\u8DF3\u8FC7 Maven \u6784\u5EFA\uFF08--skip-build\uFF09");
|
|
4457
|
+
}
|
|
4458
|
+
const libDir = locateLibDir(projectRoot, mavenModule);
|
|
4459
|
+
const manifest = loadManifest(manifestFile);
|
|
4460
|
+
const sftp = new SftpClient2();
|
|
4461
|
+
log(`\u8FDE\u63A5\u670D\u52A1\u5668 ${settings.username}@${settings.host}:${settings.port}`);
|
|
4462
|
+
try {
|
|
4463
|
+
await sftp.connect({
|
|
4464
|
+
host: settings.host,
|
|
4465
|
+
port: settings.port,
|
|
4466
|
+
username: settings.username,
|
|
4467
|
+
password: settings.password,
|
|
4468
|
+
readyTimeout: 3e4,
|
|
4469
|
+
tryKeyboard: true
|
|
4470
|
+
});
|
|
4471
|
+
const client = sftp.client;
|
|
4472
|
+
let needRestart = false;
|
|
4473
|
+
let libUploadEntries = [];
|
|
4474
|
+
const remoteLibStats = await getRemoteFileStats(
|
|
4475
|
+
sftp,
|
|
4476
|
+
settings.remoteLibDir
|
|
4477
|
+
);
|
|
4478
|
+
log("\u6536\u96C6 JAR \u66F4\u65B0...");
|
|
4479
|
+
libUploadEntries = listLibFilesToUpload(
|
|
4480
|
+
projectRoot,
|
|
4481
|
+
libDir,
|
|
4482
|
+
remoteLibStats,
|
|
4483
|
+
manifest
|
|
4484
|
+
);
|
|
4485
|
+
let updated = 0;
|
|
4486
|
+
if (libUploadEntries.length > 0) {
|
|
4487
|
+
const zipPath = await createUpdatePackage(
|
|
4488
|
+
libUploadEntries,
|
|
4489
|
+
settings.packageName,
|
|
4490
|
+
deployCacheDir
|
|
4491
|
+
);
|
|
4492
|
+
const remoteZipPath = await uploadUpdatePackage(sftp, zipPath, settings);
|
|
4493
|
+
log("\u8FDC\u7A0B\u89E3\u538B lib \u76EE\u5F55\uFF08\u4EC5\u8986\u76D6\u5DF2\u6709 JAR\uFF09...");
|
|
4494
|
+
updated = await extractUpdatePackageOnRemote(
|
|
4495
|
+
client,
|
|
4496
|
+
settings,
|
|
4497
|
+
remoteZipPath
|
|
4498
|
+
);
|
|
4499
|
+
} else {
|
|
4500
|
+
log("\u65E0 JAR \u9700\u8981\u66F4\u65B0\uFF0C\u8DF3\u8FC7\u66F4\u65B0\u5305\u4E0A\u4F20");
|
|
4501
|
+
}
|
|
4502
|
+
const runningJar = await getRunningJar(client, settings);
|
|
4503
|
+
needRestart = updated > 0;
|
|
4504
|
+
if (!needRestart && !runningJar) {
|
|
4505
|
+
log("\u670D\u52A1\u672A\u8FD0\u884C\uFF0C\u9700\u8981\u542F\u52A8");
|
|
4506
|
+
needRestart = true;
|
|
4507
|
+
} else if (!needRestart) {
|
|
4508
|
+
log("\u6CA1\u6709\u6587\u4EF6\u9700\u8981\u66F4\u65B0\uFF0C\u8DF3\u8FC7\u91CD\u542F");
|
|
4509
|
+
}
|
|
4510
|
+
if (needRestart) {
|
|
4511
|
+
log("\u91CD\u542F\u670D\u52A1...");
|
|
4512
|
+
await runSpringbootAction(client, settings, "restart", [
|
|
4513
|
+
settings.startupJar
|
|
4514
|
+
]);
|
|
4515
|
+
}
|
|
4516
|
+
await healthCheckService(client, settings);
|
|
4517
|
+
if (libUploadEntries.length > 0) {
|
|
4518
|
+
saveManifest(
|
|
4519
|
+
manifestFile,
|
|
4520
|
+
updateManifestEntries(projectRoot, manifest, libUploadEntries)
|
|
4521
|
+
);
|
|
4522
|
+
}
|
|
4523
|
+
} finally {
|
|
4524
|
+
await sftp.end();
|
|
4525
|
+
}
|
|
4526
|
+
log("\u90E8\u7F72\u5B8C\u6210");
|
|
4527
|
+
}
|
|
4528
|
+
|
|
4529
|
+
// src/commands/deploy/wisdom-jar.ts
|
|
4530
|
+
function registerDeployWisdomJarCommands(program) {
|
|
4531
|
+
program.command("deploy-wisdom-jar").description(
|
|
4532
|
+
"Maven \u6784\u5EFA Spring Boot \u9879\u76EE\uFF0C\u589E\u91CF\u4E0A\u4F20 lib JAR \u5E76\u901A\u8FC7 springboot.sh \u91CD\u542F\uFF08\u914D\u7F6E\u89C1 apm.config.json wisdomDeploy / healthCheck\uFF09"
|
|
4533
|
+
).option(
|
|
4534
|
+
"--config <path>",
|
|
4535
|
+
"apm.config.json \u8DEF\u5F84\uFF08\u9ED8\u8BA4 .apm/apm.config.json\uFF09"
|
|
4536
|
+
).option(
|
|
4537
|
+
"--module <path>",
|
|
4538
|
+
"Maven \u6A21\u5757\u76F8\u5BF9\u8DEF\u5F84\uFF08\u9ED8\u8BA4 jeecg-module-system/jeecg-system-start\uFF09",
|
|
4539
|
+
"jeecg-module-system/jeecg-system-start"
|
|
4540
|
+
).option("--profile <name>", "Maven profile\uFF08\u9ED8\u8BA4 dev\uFF09", "dev").option("--skip-build", "\u8DF3\u8FC7 Maven \u6784\u5EFA\uFF0C\u4EC5\u4E0A\u4F20\u4E0E\u91CD\u542F").action(
|
|
4541
|
+
async (opts) => {
|
|
4542
|
+
const cfg = loadApmConfig({ configPath: opts.config });
|
|
4543
|
+
const settings = resolveWisdomJarDeployFromApmConfig(cfg);
|
|
4544
|
+
const projectRoot = process.cwd();
|
|
4545
|
+
const apmDir = workspaceApmDir(projectRoot);
|
|
4546
|
+
const deployCacheDir = path12.join(apmDir, "deploy", ".deploy_cache");
|
|
4547
|
+
const configPath = path12.resolve(
|
|
4548
|
+
projectRoot,
|
|
4549
|
+
opts.config ?? path12.join(apmDir, "apm.config.json")
|
|
4550
|
+
);
|
|
4551
|
+
try {
|
|
4552
|
+
await runWisdomJarDeploy({
|
|
4553
|
+
projectRoot,
|
|
4554
|
+
deployCacheDir,
|
|
4555
|
+
configPath,
|
|
4556
|
+
settings,
|
|
4557
|
+
mavenModule: opts.module,
|
|
4558
|
+
mavenProfile: opts.profile,
|
|
4559
|
+
skipBuild: Boolean(opts.skipBuild)
|
|
4560
|
+
});
|
|
4561
|
+
} catch (err) {
|
|
4562
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4563
|
+
console.error("\u90E8\u7F72\u5931\u8D25:", message);
|
|
4564
|
+
process.exit(1);
|
|
4565
|
+
}
|
|
4566
|
+
}
|
|
4567
|
+
);
|
|
4568
|
+
}
|
|
4569
|
+
|
|
3717
4570
|
// src/commands/deploy/index.ts
|
|
3718
4571
|
function registerDeployCommands(program) {
|
|
4572
|
+
registerDeployRunCommands(program);
|
|
3719
4573
|
registerDeployBackendCommands(program);
|
|
3720
4574
|
registerDeployFrontendCommands(program);
|
|
3721
4575
|
registerDeploySftpCommands(program);
|
|
4576
|
+
registerDeployWisdomJarCommands(program);
|
|
3722
4577
|
}
|
|
3723
4578
|
|
|
3724
4579
|
// src/index.ts
|