repowisestage 0.0.68 → 0.0.69
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/bin/repowise.js +1494 -1348
- package/package.json +1 -1
package/dist/bin/repowise.js
CHANGED
|
@@ -620,388 +620,69 @@ var init_process_manager = __esm({
|
|
|
620
620
|
}
|
|
621
621
|
});
|
|
622
622
|
|
|
623
|
-
// ../listener/dist/
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
if (err) {
|
|
640
|
-
reject(err);
|
|
641
|
-
return;
|
|
642
|
-
}
|
|
643
|
-
resolve5(String(stdout ?? ""));
|
|
644
|
-
});
|
|
645
|
-
});
|
|
646
|
-
}
|
|
647
|
-
function plistPath() {
|
|
648
|
-
return join13(homedir3(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
649
|
-
}
|
|
650
|
-
function logDir() {
|
|
651
|
-
return join13(getConfigDir(), "logs");
|
|
652
|
-
}
|
|
653
|
-
function buildPlist() {
|
|
654
|
-
const cmd = resolveListenerCommand();
|
|
655
|
-
const logs = logDir();
|
|
656
|
-
const programArgs = [process.execPath, cmd.script, ...cmd.args].map((a) => ` <string>${a}</string>`).join("\n");
|
|
657
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
658
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
|
659
|
-
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
660
|
-
<plist version="1.0">
|
|
661
|
-
<dict>
|
|
662
|
-
<key>Label</key>
|
|
663
|
-
<string>${PLIST_LABEL}</string>
|
|
664
|
-
<key>ProgramArguments</key>
|
|
665
|
-
<array>
|
|
666
|
-
${programArgs}
|
|
667
|
-
</array>
|
|
668
|
-
<key>RunAtLoad</key>
|
|
669
|
-
<true/>
|
|
670
|
-
<key>KeepAlive</key>
|
|
671
|
-
<true/>
|
|
672
|
-
<key>StandardOutPath</key>
|
|
673
|
-
<string>${join13(logs, "listener-stdout.log")}</string>
|
|
674
|
-
<key>StandardErrorPath</key>
|
|
675
|
-
<string>${join13(logs, "listener-stderr.log")}</string>
|
|
676
|
-
<key>ProcessType</key>
|
|
677
|
-
<string>Background</string>
|
|
678
|
-
<!--
|
|
679
|
-
SoftResourceLimits: raise the open-file descriptor limit from the macOS
|
|
680
|
-
default of 256 to 4096. When multiple LSP child processes (rust-analyzer,
|
|
681
|
-
jdtls, etc.) share the listener's fd budget they can exhaust the default
|
|
682
|
-
very quickly, causing EMFILE errors. 4096 matches the system-wide default
|
|
683
|
-
set by /etc/launchd.conf on modern macOS and gives enough headroom for
|
|
684
|
-
~10 concurrent LSP servers each opening ~300 fds.
|
|
685
|
-
|
|
686
|
-
MemoryLimit: intentionally NOT set here. The launchd MemoryLimit key
|
|
687
|
-
(available since macOS 12.3) terminates the entire LaunchAgent when RSS
|
|
688
|
-
exceeds the threshold \u2014 it cannot be scoped to individual child processes.
|
|
689
|
-
Killing the listener to evict a memory-hungry rust-analyzer instance is
|
|
690
|
-
undesirable. Per-child memory pressure is better handled by the LSP
|
|
691
|
-
process manager (process-manager.ts) or by OS-level memory pressure
|
|
692
|
-
signals (SIGTERM on macOS, OOM killer on Linux).
|
|
693
|
-
-->
|
|
694
|
-
<key>SoftResourceLimits</key>
|
|
695
|
-
<dict>
|
|
696
|
-
<key>NumberOfFiles</key>
|
|
697
|
-
<integer>4096</integer>
|
|
698
|
-
</dict>
|
|
699
|
-
</dict>
|
|
700
|
-
</plist>`;
|
|
701
|
-
}
|
|
702
|
-
async function darwinInstall() {
|
|
703
|
-
await mkdir8(logDir(), { recursive: true });
|
|
704
|
-
await mkdir8(join13(homedir3(), "Library", "LaunchAgents"), { recursive: true });
|
|
705
|
-
try {
|
|
706
|
-
await exec("launchctl", ["unload", plistPath()]);
|
|
707
|
-
} catch {
|
|
708
|
-
}
|
|
709
|
-
await writeFile8(plistPath(), buildPlist());
|
|
710
|
-
await exec("launchctl", ["load", plistPath()]);
|
|
711
|
-
}
|
|
712
|
-
async function darwinUninstall() {
|
|
713
|
-
try {
|
|
714
|
-
await exec("launchctl", ["unload", plistPath()]);
|
|
715
|
-
} catch {
|
|
716
|
-
}
|
|
717
|
-
try {
|
|
718
|
-
await unlink6(plistPath());
|
|
719
|
-
} catch {
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
async function darwinIsInstalled() {
|
|
723
|
-
try {
|
|
724
|
-
const stdout = await exec("launchctl", ["list"]);
|
|
725
|
-
return stdout.includes(PLIST_LABEL);
|
|
726
|
-
} catch {
|
|
727
|
-
return false;
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
function unitPath() {
|
|
731
|
-
return join13(homedir3(), ".config", "systemd", "user", `${SYSTEMD_SERVICE}.service`);
|
|
732
|
-
}
|
|
733
|
-
function buildUnit() {
|
|
734
|
-
const cmd = resolveListenerCommand();
|
|
735
|
-
const execStart = [process.execPath, cmd.script, ...cmd.args].join(" ");
|
|
736
|
-
const logs = logDir();
|
|
737
|
-
return `[Unit]
|
|
738
|
-
Description=RepoWise Listener
|
|
739
|
-
After=network-online.target
|
|
740
|
-
Wants=network-online.target
|
|
741
|
-
|
|
742
|
-
[Service]
|
|
743
|
-
Type=simple
|
|
744
|
-
ExecStart=${execStart}
|
|
745
|
-
Restart=always
|
|
746
|
-
RestartSec=10
|
|
747
|
-
StandardOutput=append:${join13(logs, "listener-stdout.log")}
|
|
748
|
-
StandardError=append:${join13(logs, "listener-stderr.log")}
|
|
749
|
-
|
|
750
|
-
[Install]
|
|
751
|
-
WantedBy=default.target`;
|
|
752
|
-
}
|
|
753
|
-
async function linuxInstall() {
|
|
754
|
-
await mkdir8(logDir(), { recursive: true });
|
|
755
|
-
await mkdir8(join13(homedir3(), ".config", "systemd", "user"), { recursive: true });
|
|
756
|
-
await writeFile8(unitPath(), buildUnit());
|
|
757
|
-
await exec("systemctl", ["--user", "daemon-reload"]);
|
|
758
|
-
await exec("systemctl", ["--user", "enable", SYSTEMD_SERVICE]);
|
|
759
|
-
await exec("systemctl", ["--user", "start", SYSTEMD_SERVICE]);
|
|
760
|
-
}
|
|
761
|
-
async function linuxUninstall() {
|
|
762
|
-
try {
|
|
763
|
-
await exec("systemctl", ["--user", "stop", SYSTEMD_SERVICE]);
|
|
764
|
-
} catch {
|
|
765
|
-
}
|
|
766
|
-
try {
|
|
767
|
-
await exec("systemctl", ["--user", "disable", SYSTEMD_SERVICE]);
|
|
768
|
-
} catch {
|
|
769
|
-
}
|
|
770
|
-
try {
|
|
771
|
-
await unlink6(unitPath());
|
|
772
|
-
} catch {
|
|
773
|
-
}
|
|
774
|
-
try {
|
|
775
|
-
await exec("systemctl", ["--user", "daemon-reload"]);
|
|
776
|
-
} catch {
|
|
777
|
-
}
|
|
623
|
+
// ../listener/dist/lsp/registry.js
|
|
624
|
+
function resolvePhpLspOverride(env = process.env, configOverrides) {
|
|
625
|
+
const envChoice = env["PHP_LSP"];
|
|
626
|
+
const configChoice = configOverrides?.["php"];
|
|
627
|
+
const choice = envChoice ?? configChoice;
|
|
628
|
+
if (choice !== "intelephense")
|
|
629
|
+
return null;
|
|
630
|
+
return {
|
|
631
|
+
id: "intelephense",
|
|
632
|
+
displayName: "intelephense (user-provided)",
|
|
633
|
+
command: "intelephense",
|
|
634
|
+
args: ["--stdio"],
|
|
635
|
+
extensions: [".php"],
|
|
636
|
+
lspLanguageId: "php",
|
|
637
|
+
installHint: "Intelephense override is enabled \u2014 install it yourself: `npm i -g intelephense` (commercial licence required for premium features)."
|
|
638
|
+
};
|
|
778
639
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
const
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
return false;
|
|
640
|
+
function getEffectiveConfig(lang, env = process.env, configOverrides) {
|
|
641
|
+
if (lang === "php") {
|
|
642
|
+
const override = resolvePhpLspOverride(env, configOverrides);
|
|
643
|
+
if (override)
|
|
644
|
+
return override;
|
|
785
645
|
}
|
|
646
|
+
return LSP_REGISTRY[lang]?.[0];
|
|
786
647
|
}
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
"/create",
|
|
793
|
-
"/tn",
|
|
794
|
-
TASK_NAME,
|
|
795
|
-
"/tr",
|
|
796
|
-
taskCmd,
|
|
797
|
-
"/sc",
|
|
798
|
-
"onlogon",
|
|
799
|
-
"/ru",
|
|
800
|
-
process.env.USERNAME ?? "",
|
|
801
|
-
"/f"
|
|
802
|
-
]);
|
|
803
|
-
await exec("schtasks", ["/run", "/tn", TASK_NAME]);
|
|
804
|
-
}
|
|
805
|
-
async function win32Uninstall() {
|
|
806
|
-
try {
|
|
807
|
-
await exec("schtasks", ["/end", "/tn", TASK_NAME]);
|
|
808
|
-
} catch {
|
|
809
|
-
}
|
|
810
|
-
try {
|
|
811
|
-
await exec("schtasks", ["/delete", "/tn", TASK_NAME, "/f"]);
|
|
812
|
-
} catch {
|
|
648
|
+
function getEffectiveConfigList(lang, env = process.env, configOverrides) {
|
|
649
|
+
if (lang === "php") {
|
|
650
|
+
const override = resolvePhpLspOverride(env, configOverrides);
|
|
651
|
+
if (override)
|
|
652
|
+
return [override];
|
|
813
653
|
}
|
|
654
|
+
return LSP_REGISTRY[lang] ?? [];
|
|
814
655
|
}
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
return
|
|
819
|
-
|
|
820
|
-
|
|
656
|
+
function detectLanguage(path) {
|
|
657
|
+
const dot = path.lastIndexOf(".");
|
|
658
|
+
if (dot < 0)
|
|
659
|
+
return null;
|
|
660
|
+
const ext = path.slice(dot).toLowerCase();
|
|
661
|
+
for (const [lang, configs] of Object.entries(LSP_REGISTRY)) {
|
|
662
|
+
if (configs.some((c) => c.extensions.includes(ext))) {
|
|
663
|
+
return lang;
|
|
664
|
+
}
|
|
821
665
|
}
|
|
666
|
+
return null;
|
|
822
667
|
}
|
|
823
|
-
async function
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
668
|
+
async function probeServers(isAvailable, env = process.env, configOverrides) {
|
|
669
|
+
const out = [];
|
|
670
|
+
for (const lang of Object.keys(LSP_REGISTRY)) {
|
|
671
|
+
const configs = getEffectiveConfigList(lang, env, configOverrides);
|
|
672
|
+
const checkedCommands = configs.map((c) => c.command);
|
|
673
|
+
let found = null;
|
|
674
|
+
for (const config2 of configs) {
|
|
675
|
+
if (await isAvailable(config2.command)) {
|
|
676
|
+
found = config2;
|
|
677
|
+
break;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
out.push({ language: lang, config: found, checkedCommands });
|
|
827
681
|
}
|
|
682
|
+
return out;
|
|
828
683
|
}
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
}
|
|
832
|
-
async function win32Stop() {
|
|
833
|
-
await exec("schtasks", ["/end", "/tn", TASK_NAME]);
|
|
834
|
-
}
|
|
835
|
-
async function darwinIsServiceRunning() {
|
|
836
|
-
try {
|
|
837
|
-
const stdout = await exec("launchctl", ["list", PLIST_LABEL]);
|
|
838
|
-
const pidMatch = stdout.match(/"PID"\s*=\s*(\d+)/);
|
|
839
|
-
return pidMatch !== null && parseInt(pidMatch[1], 10) > 0;
|
|
840
|
-
} catch {
|
|
841
|
-
return false;
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
async function linuxIsServiceRunning() {
|
|
845
|
-
try {
|
|
846
|
-
const stdout = await exec("systemctl", ["--user", "is-active", SYSTEMD_SERVICE]);
|
|
847
|
-
return stdout.trim() === "active";
|
|
848
|
-
} catch {
|
|
849
|
-
return false;
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
async function win32IsServiceRunning() {
|
|
853
|
-
try {
|
|
854
|
-
const stdout = await exec("schtasks", ["/query", "/tn", TASK_NAME, "/fo", "CSV"]);
|
|
855
|
-
return stdout.includes("Running");
|
|
856
|
-
} catch {
|
|
857
|
-
return false;
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
async function stopService() {
|
|
861
|
-
switch (process.platform) {
|
|
862
|
-
case "darwin":
|
|
863
|
-
await darwinStop();
|
|
864
|
-
break;
|
|
865
|
-
case "linux":
|
|
866
|
-
await linuxStop();
|
|
867
|
-
break;
|
|
868
|
-
case "win32":
|
|
869
|
-
await win32Stop();
|
|
870
|
-
break;
|
|
871
|
-
default:
|
|
872
|
-
throw new Error(`Unsupported platform: ${process.platform}`);
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
async function install() {
|
|
876
|
-
switch (process.platform) {
|
|
877
|
-
case "darwin":
|
|
878
|
-
await darwinInstall();
|
|
879
|
-
break;
|
|
880
|
-
case "linux":
|
|
881
|
-
await linuxInstall();
|
|
882
|
-
break;
|
|
883
|
-
case "win32":
|
|
884
|
-
await win32Install();
|
|
885
|
-
break;
|
|
886
|
-
default:
|
|
887
|
-
throw new Error(`Unsupported platform: ${process.platform}`);
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
async function uninstall() {
|
|
891
|
-
switch (process.platform) {
|
|
892
|
-
case "darwin":
|
|
893
|
-
await darwinUninstall();
|
|
894
|
-
break;
|
|
895
|
-
case "linux":
|
|
896
|
-
await linuxUninstall();
|
|
897
|
-
break;
|
|
898
|
-
case "win32":
|
|
899
|
-
await win32Uninstall();
|
|
900
|
-
break;
|
|
901
|
-
default:
|
|
902
|
-
throw new Error(`Unsupported platform: ${process.platform}`);
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
async function isInstalled() {
|
|
906
|
-
switch (process.platform) {
|
|
907
|
-
case "darwin":
|
|
908
|
-
return darwinIsInstalled();
|
|
909
|
-
case "linux":
|
|
910
|
-
return linuxIsInstalled();
|
|
911
|
-
case "win32":
|
|
912
|
-
return win32IsInstalled();
|
|
913
|
-
default:
|
|
914
|
-
return false;
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
async function isServiceRunning() {
|
|
918
|
-
switch (process.platform) {
|
|
919
|
-
case "darwin":
|
|
920
|
-
return darwinIsServiceRunning();
|
|
921
|
-
case "linux":
|
|
922
|
-
return linuxIsServiceRunning();
|
|
923
|
-
case "win32":
|
|
924
|
-
return win32IsServiceRunning();
|
|
925
|
-
default:
|
|
926
|
-
return false;
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
var IS_STAGING, PLIST_LABEL, SYSTEMD_SERVICE, TASK_NAME;
|
|
930
|
-
var init_service_installer = __esm({
|
|
931
|
-
"../listener/dist/service-installer.js"() {
|
|
932
|
-
"use strict";
|
|
933
|
-
init_config_dir();
|
|
934
|
-
init_process_manager();
|
|
935
|
-
IS_STAGING = true ? true : false;
|
|
936
|
-
PLIST_LABEL = IS_STAGING ? "com.repowise-staging.listener" : "com.repowise.listener";
|
|
937
|
-
SYSTEMD_SERVICE = IS_STAGING ? "repowise-staging-listener" : "repowise-listener";
|
|
938
|
-
TASK_NAME = IS_STAGING ? "RepoWise Staging Listener" : "RepoWise Listener";
|
|
939
|
-
}
|
|
940
|
-
});
|
|
941
|
-
|
|
942
|
-
// ../listener/dist/lsp/registry.js
|
|
943
|
-
function resolvePhpLspOverride(env = process.env, configOverrides) {
|
|
944
|
-
const envChoice = env["PHP_LSP"];
|
|
945
|
-
const configChoice = configOverrides?.["php"];
|
|
946
|
-
const choice = envChoice ?? configChoice;
|
|
947
|
-
if (choice !== "intelephense")
|
|
948
|
-
return null;
|
|
949
|
-
return {
|
|
950
|
-
id: "intelephense",
|
|
951
|
-
displayName: "intelephense (user-provided)",
|
|
952
|
-
command: "intelephense",
|
|
953
|
-
args: ["--stdio"],
|
|
954
|
-
extensions: [".php"],
|
|
955
|
-
lspLanguageId: "php",
|
|
956
|
-
installHint: "Intelephense override is enabled \u2014 install it yourself: `npm i -g intelephense` (commercial licence required for premium features)."
|
|
957
|
-
};
|
|
958
|
-
}
|
|
959
|
-
function getEffectiveConfig(lang, env = process.env, configOverrides) {
|
|
960
|
-
if (lang === "php") {
|
|
961
|
-
const override = resolvePhpLspOverride(env, configOverrides);
|
|
962
|
-
if (override)
|
|
963
|
-
return override;
|
|
964
|
-
}
|
|
965
|
-
return LSP_REGISTRY[lang]?.[0];
|
|
966
|
-
}
|
|
967
|
-
function getEffectiveConfigList(lang, env = process.env, configOverrides) {
|
|
968
|
-
if (lang === "php") {
|
|
969
|
-
const override = resolvePhpLspOverride(env, configOverrides);
|
|
970
|
-
if (override)
|
|
971
|
-
return [override];
|
|
972
|
-
}
|
|
973
|
-
return LSP_REGISTRY[lang] ?? [];
|
|
974
|
-
}
|
|
975
|
-
function detectLanguage(path) {
|
|
976
|
-
const dot = path.lastIndexOf(".");
|
|
977
|
-
if (dot < 0)
|
|
978
|
-
return null;
|
|
979
|
-
const ext = path.slice(dot).toLowerCase();
|
|
980
|
-
for (const [lang, configs] of Object.entries(LSP_REGISTRY)) {
|
|
981
|
-
if (configs.some((c) => c.extensions.includes(ext))) {
|
|
982
|
-
return lang;
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
return null;
|
|
986
|
-
}
|
|
987
|
-
async function probeServers(isAvailable, env = process.env, configOverrides) {
|
|
988
|
-
const out = [];
|
|
989
|
-
for (const lang of Object.keys(LSP_REGISTRY)) {
|
|
990
|
-
const configs = getEffectiveConfigList(lang, env, configOverrides);
|
|
991
|
-
const checkedCommands = configs.map((c) => c.command);
|
|
992
|
-
let found = null;
|
|
993
|
-
for (const config2 of configs) {
|
|
994
|
-
if (await isAvailable(config2.command)) {
|
|
995
|
-
found = config2;
|
|
996
|
-
break;
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
out.push({ language: lang, config: found, checkedCommands });
|
|
1000
|
-
}
|
|
1001
|
-
return out;
|
|
1002
|
-
}
|
|
1003
|
-
function getBuildSpec(language) {
|
|
1004
|
-
return BUILD_SPECS[language] ?? null;
|
|
684
|
+
function getBuildSpec(language) {
|
|
685
|
+
return BUILD_SPECS[language] ?? null;
|
|
1005
686
|
}
|
|
1006
687
|
var LSP_REGISTRY, BUILD_SPECS;
|
|
1007
688
|
var init_registry = __esm({
|
|
@@ -1415,7 +1096,7 @@ var init_registry = __esm({
|
|
|
1415
1096
|
});
|
|
1416
1097
|
|
|
1417
1098
|
// ../listener/dist/lsp/jdk-probe.js
|
|
1418
|
-
import { spawn as
|
|
1099
|
+
import { spawn as spawn2 } from "child_process";
|
|
1419
1100
|
function parseJavaMajorVersion(stderr) {
|
|
1420
1101
|
const modernMatch = /\b(?:openjdk|java) version "(\d+)(?:\.\d+)*(?:[._]\w+)?"/.exec(stderr);
|
|
1421
1102
|
if (!modernMatch?.[1])
|
|
@@ -1430,166 +1111,503 @@ function parseJavaMajorVersion(stderr) {
|
|
|
1430
1111
|
return Number.isFinite(legacy) ? legacy : void 0;
|
|
1431
1112
|
}
|
|
1432
1113
|
}
|
|
1433
|
-
return first;
|
|
1114
|
+
return first;
|
|
1115
|
+
}
|
|
1116
|
+
async function probeJdk(options = {}) {
|
|
1117
|
+
const timeoutMs = options.timeoutMs ?? 5e3;
|
|
1118
|
+
return new Promise((resolve5) => {
|
|
1119
|
+
let stderr = "";
|
|
1120
|
+
let settled = false;
|
|
1121
|
+
const child = spawn2("java", ["-version"], { stdio: ["ignore", "pipe", "pipe"] });
|
|
1122
|
+
const timer = setTimeout(() => {
|
|
1123
|
+
if (settled)
|
|
1124
|
+
return;
|
|
1125
|
+
settled = true;
|
|
1126
|
+
try {
|
|
1127
|
+
child.kill("SIGKILL");
|
|
1128
|
+
} catch {
|
|
1129
|
+
}
|
|
1130
|
+
resolve5({ present: false });
|
|
1131
|
+
}, timeoutMs);
|
|
1132
|
+
child.stderr.on("data", (chunk) => {
|
|
1133
|
+
stderr += chunk.toString("utf8");
|
|
1134
|
+
});
|
|
1135
|
+
child.on("error", () => {
|
|
1136
|
+
if (settled)
|
|
1137
|
+
return;
|
|
1138
|
+
settled = true;
|
|
1139
|
+
clearTimeout(timer);
|
|
1140
|
+
resolve5({ present: false });
|
|
1141
|
+
});
|
|
1142
|
+
child.on("close", (code) => {
|
|
1143
|
+
if (settled)
|
|
1144
|
+
return;
|
|
1145
|
+
settled = true;
|
|
1146
|
+
clearTimeout(timer);
|
|
1147
|
+
if (code !== 0) {
|
|
1148
|
+
resolve5({ present: false, rawOutput: stderr.trim() });
|
|
1149
|
+
return;
|
|
1150
|
+
}
|
|
1151
|
+
const major = parseJavaMajorVersion(stderr);
|
|
1152
|
+
if (major === void 0) {
|
|
1153
|
+
resolve5({ present: true, rawOutput: stderr.trim() });
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
resolve5({ present: true, majorVersion: major, rawOutput: stderr.trim() });
|
|
1157
|
+
});
|
|
1158
|
+
});
|
|
1159
|
+
}
|
|
1160
|
+
var init_jdk_probe = __esm({
|
|
1161
|
+
"../listener/dist/lsp/jdk-probe.js"() {
|
|
1162
|
+
"use strict";
|
|
1163
|
+
}
|
|
1164
|
+
});
|
|
1165
|
+
|
|
1166
|
+
// ../listener/dist/lsp/native-installer.js
|
|
1167
|
+
import { createWriteStream } from "fs";
|
|
1168
|
+
import { promises as fs } from "fs";
|
|
1169
|
+
import { createGunzip } from "zlib";
|
|
1170
|
+
import { spawn as spawn3 } from "child_process";
|
|
1171
|
+
import { pipeline } from "stream/promises";
|
|
1172
|
+
import { createHash as createHash3 } from "crypto";
|
|
1173
|
+
import { join as join13, dirname as dirname5 } from "path";
|
|
1174
|
+
function getNativeLspDir() {
|
|
1175
|
+
return join13(getLspInstallDir(), "native");
|
|
1176
|
+
}
|
|
1177
|
+
function getNativeLspBinDir() {
|
|
1178
|
+
return join13(getNativeLspDir(), "bin");
|
|
1179
|
+
}
|
|
1180
|
+
async function ensureNativeLspInstalled(lspKey) {
|
|
1181
|
+
const entry = NATIVE_LSP_VERSIONS[lspKey];
|
|
1182
|
+
if (!entry)
|
|
1183
|
+
return { installed: false, alreadyPresent: false, skipped: "no-native-version" };
|
|
1184
|
+
const platformKey = getCurrentPlatformKey();
|
|
1185
|
+
if (!platformKey) {
|
|
1186
|
+
return { installed: false, alreadyPresent: false, skipped: "unsupported-platform" };
|
|
1187
|
+
}
|
|
1188
|
+
const asset = entry.assets[platformKey];
|
|
1189
|
+
if (!asset) {
|
|
1190
|
+
return { installed: false, alreadyPresent: false, skipped: "unsupported-platform" };
|
|
1191
|
+
}
|
|
1192
|
+
if (entry.requiresJdk !== void 0) {
|
|
1193
|
+
const jdk = await probeJdk();
|
|
1194
|
+
if (!jdk.present || jdk.majorVersion === void 0 || jdk.majorVersion < entry.requiresJdk) {
|
|
1195
|
+
return {
|
|
1196
|
+
installed: false,
|
|
1197
|
+
alreadyPresent: false,
|
|
1198
|
+
skipped: "jvm-missing",
|
|
1199
|
+
error: `${lspKey} requires JDK ${entry.requiresJdk.toString()}+ (found: ${jdk.present ? `Java ${jdk.majorVersion?.toString() ?? "?"}` : "no java on PATH"}). Install with: brew install openjdk@21 (macOS) or apt install openjdk-21-jdk (Linux)`
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
const usesWrapper = Boolean(asset.launcherJarGlob && asset.wrapperBinaryName);
|
|
1204
|
+
const versionDir = join13(getNativeLspDir(), lspKey, entry.version);
|
|
1205
|
+
if (usesWrapper && asset.launcherJarGlob && asset.wrapperBinaryName) {
|
|
1206
|
+
const jarPath = await resolveGlobInsideDir(versionDir, asset.launcherJarGlob);
|
|
1207
|
+
const shimPath = join13(getNativeLspBinDir(), asset.wrapperBinaryName);
|
|
1208
|
+
if (jarPath && await pathExists(shimPath)) {
|
|
1209
|
+
return { installed: false, alreadyPresent: true, binaryPath: shimPath };
|
|
1210
|
+
}
|
|
1211
|
+
} else {
|
|
1212
|
+
const installedBinary = join13(versionDir, asset.binaryPath);
|
|
1213
|
+
if (await pathExists(installedBinary)) {
|
|
1214
|
+
await ensureSymlinkInBin(lspKey, installedBinary, asset.binaryPath);
|
|
1215
|
+
return { installed: false, alreadyPresent: true, binaryPath: installedBinary };
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
try {
|
|
1219
|
+
await fs.mkdir(versionDir, { recursive: true });
|
|
1220
|
+
const tmpDownload = join13(versionDir, `.${asset.filename}.download`);
|
|
1221
|
+
const downloadUrl = entry.source.kind === "github-release" ? `https://github.com/${entry.source.repo}/releases/download/${entry.version}/${asset.filename}` : asset.url ?? "";
|
|
1222
|
+
if (!downloadUrl) {
|
|
1223
|
+
return {
|
|
1224
|
+
installed: false,
|
|
1225
|
+
alreadyPresent: false,
|
|
1226
|
+
error: `direct-url asset for ${lspKey} missing required \`url\` field`
|
|
1227
|
+
};
|
|
1228
|
+
}
|
|
1229
|
+
await downloadFile(downloadUrl, tmpDownload);
|
|
1230
|
+
if (asset.sha256) {
|
|
1231
|
+
const actual = await sha256File(tmpDownload);
|
|
1232
|
+
if (actual !== asset.sha256) {
|
|
1233
|
+
await fs.unlink(tmpDownload).catch(() => void 0);
|
|
1234
|
+
return {
|
|
1235
|
+
installed: false,
|
|
1236
|
+
alreadyPresent: false,
|
|
1237
|
+
error: `SHA256 mismatch for ${lspKey}: expected ${asset.sha256}, got ${actual}`
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
} else {
|
|
1241
|
+
console.warn(`[lsp-install] WARN: SHA256 verification skipped for ${lspKey} (no pin in NATIVE_LSP_VERSIONS) \u2014 run apps/listener/scripts/pin-native-shas.ts to record one`);
|
|
1242
|
+
}
|
|
1243
|
+
await extractAsset(tmpDownload, versionDir, asset);
|
|
1244
|
+
await fs.unlink(tmpDownload).catch(() => void 0);
|
|
1245
|
+
if (usesWrapper && asset.launcherJarGlob && asset.wrapperBinaryName) {
|
|
1246
|
+
const jarPath = await resolveGlobInsideDir(versionDir, asset.launcherJarGlob);
|
|
1247
|
+
if (!jarPath) {
|
|
1248
|
+
return {
|
|
1249
|
+
installed: false,
|
|
1250
|
+
alreadyPresent: false,
|
|
1251
|
+
error: `extract completed but no JAR matched ${asset.launcherJarGlob} in ${versionDir}`
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
const shimPath = await writeJdtlsWrapper({
|
|
1255
|
+
binaryName: asset.wrapperBinaryName,
|
|
1256
|
+
versionDir,
|
|
1257
|
+
launcherJarPath: jarPath
|
|
1258
|
+
});
|
|
1259
|
+
await pruneStaleVersions(lspKey, entry.version);
|
|
1260
|
+
return { installed: true, alreadyPresent: false, binaryPath: shimPath };
|
|
1261
|
+
}
|
|
1262
|
+
const installedBinary = join13(versionDir, asset.binaryPath);
|
|
1263
|
+
if (!await pathExists(installedBinary)) {
|
|
1264
|
+
return {
|
|
1265
|
+
installed: false,
|
|
1266
|
+
alreadyPresent: false,
|
|
1267
|
+
error: `extract completed but binary missing at ${installedBinary}`
|
|
1268
|
+
};
|
|
1269
|
+
}
|
|
1270
|
+
await fs.chmod(installedBinary, 493).catch(() => void 0);
|
|
1271
|
+
await ensureSymlinkInBin(lspKey, installedBinary, asset.binaryPath);
|
|
1272
|
+
await pruneStaleVersions(lspKey, entry.version);
|
|
1273
|
+
return { installed: true, alreadyPresent: false, binaryPath: installedBinary };
|
|
1274
|
+
} catch (err) {
|
|
1275
|
+
return {
|
|
1276
|
+
installed: false,
|
|
1277
|
+
alreadyPresent: false,
|
|
1278
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1279
|
+
};
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
async function resolveGlobInsideDir(versionDir, glob) {
|
|
1283
|
+
const parts = glob.split("/");
|
|
1284
|
+
let dir = versionDir;
|
|
1285
|
+
for (let i = 0; i < parts.length - 1; i += 1) {
|
|
1286
|
+
dir = join13(dir, parts[i]);
|
|
1287
|
+
}
|
|
1288
|
+
const pattern = parts[parts.length - 1];
|
|
1289
|
+
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
1290
|
+
const re = new RegExp(`^${escaped}$`);
|
|
1291
|
+
let entries;
|
|
1292
|
+
try {
|
|
1293
|
+
entries = await fs.readdir(dir);
|
|
1294
|
+
} catch {
|
|
1295
|
+
return null;
|
|
1296
|
+
}
|
|
1297
|
+
const matches = entries.filter((e) => re.test(e)).sort();
|
|
1298
|
+
const last = matches[matches.length - 1];
|
|
1299
|
+
return last ? join13(dir, last) : null;
|
|
1300
|
+
}
|
|
1301
|
+
async function pruneStaleVersions(lspKey, currentVersion) {
|
|
1302
|
+
const lspDir = join13(getNativeLspDir(), lspKey);
|
|
1303
|
+
let entries;
|
|
1304
|
+
try {
|
|
1305
|
+
entries = await fs.readdir(lspDir);
|
|
1306
|
+
} catch {
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1309
|
+
await Promise.all(entries.filter((name) => name !== currentVersion).map(async (name) => {
|
|
1310
|
+
try {
|
|
1311
|
+
await fs.rm(join13(lspDir, name), { recursive: true, force: true });
|
|
1312
|
+
} catch {
|
|
1313
|
+
}
|
|
1314
|
+
}));
|
|
1315
|
+
}
|
|
1316
|
+
function shQuote(value) {
|
|
1317
|
+
return value.replace(/[\\"$`]/g, (ch) => `\\${ch}`);
|
|
1318
|
+
}
|
|
1319
|
+
function cmdQuote(value) {
|
|
1320
|
+
return value.replace(/"/g, '""').replace(/%/g, "%%");
|
|
1321
|
+
}
|
|
1322
|
+
async function writeJdtlsWrapper(input) {
|
|
1323
|
+
const binDir = getNativeLspBinDir();
|
|
1324
|
+
await fs.mkdir(binDir, { recursive: true });
|
|
1325
|
+
const shimPath = join13(binDir, input.binaryName);
|
|
1326
|
+
const platformCfgDir = process.platform === "darwin" ? "config_mac" : process.platform === "win32" ? "config_win" : "config_linux";
|
|
1327
|
+
const workspaceBase = join13(input.versionDir, "workspaces");
|
|
1328
|
+
const qLauncher = shQuote(input.launcherJarPath);
|
|
1329
|
+
const qConfig = shQuote(`${input.versionDir}/${platformCfgDir}`);
|
|
1330
|
+
const qWorkspaceBase = shQuote(workspaceBase);
|
|
1331
|
+
const unixScript = `#!/usr/bin/env bash
|
|
1332
|
+
# Generated by repowise listener \u2014 do not edit by hand.
|
|
1333
|
+
# Invokes Eclipse JDT.LS via the equinox launcher.
|
|
1334
|
+
set -euo pipefail
|
|
1335
|
+
WS_NAME="\${REPOWISE_JDTLS_WS_NAME:-default}"
|
|
1336
|
+
WS_DIR="${qWorkspaceBase}/\${WS_NAME}"
|
|
1337
|
+
mkdir -p "$WS_DIR"
|
|
1338
|
+
exec java \\
|
|
1339
|
+
-Declipse.application=org.eclipse.jdt.ls.core.id1 \\
|
|
1340
|
+
-Dosgi.bundles.defaultStartLevel=4 \\
|
|
1341
|
+
-Declipse.product=org.eclipse.jdt.ls.core.product \\
|
|
1342
|
+
-Dlog.level=ALL \\
|
|
1343
|
+
-Xms1g \\
|
|
1344
|
+
--add-modules=ALL-SYSTEM \\
|
|
1345
|
+
--add-opens java.base/java.util=ALL-UNNAMED \\
|
|
1346
|
+
--add-opens java.base/java.lang=ALL-UNNAMED \\
|
|
1347
|
+
-jar "${qLauncher}" \\
|
|
1348
|
+
-configuration "${qConfig}" \\
|
|
1349
|
+
-data "$WS_DIR" \\
|
|
1350
|
+
"$@"
|
|
1351
|
+
`;
|
|
1352
|
+
await fs.writeFile(shimPath, unixScript);
|
|
1353
|
+
await fs.chmod(shimPath, 493);
|
|
1354
|
+
if (process.platform === "win32") {
|
|
1355
|
+
const winShim = `${shimPath}.cmd`;
|
|
1356
|
+
const cqLauncher = cmdQuote(input.launcherJarPath);
|
|
1357
|
+
const cqConfig = cmdQuote(`${input.versionDir}\\${platformCfgDir}`);
|
|
1358
|
+
const cqWorkspaceBase = cmdQuote(workspaceBase);
|
|
1359
|
+
const winScript = `@echo off\r
|
|
1360
|
+
rem Generated by repowise listener\r
|
|
1361
|
+
if "%REPOWISE_JDTLS_WS_NAME%"=="" set REPOWISE_JDTLS_WS_NAME=default\r
|
|
1362
|
+
set WS_DIR=${cqWorkspaceBase}\\%REPOWISE_JDTLS_WS_NAME%\r
|
|
1363
|
+
if not exist "%WS_DIR%" mkdir "%WS_DIR%"\r
|
|
1364
|
+
java ^\r
|
|
1365
|
+
-Declipse.application=org.eclipse.jdt.ls.core.id1 ^\r
|
|
1366
|
+
-Dosgi.bundles.defaultStartLevel=4 ^\r
|
|
1367
|
+
-Declipse.product=org.eclipse.jdt.ls.core.product ^\r
|
|
1368
|
+
-Dlog.level=ALL ^\r
|
|
1369
|
+
-Xms1g ^\r
|
|
1370
|
+
--add-modules=ALL-SYSTEM ^\r
|
|
1371
|
+
--add-opens java.base/java.util=ALL-UNNAMED ^\r
|
|
1372
|
+
--add-opens java.base/java.lang=ALL-UNNAMED ^\r
|
|
1373
|
+
-jar "${cqLauncher}" ^\r
|
|
1374
|
+
-configuration "${cqConfig}" ^\r
|
|
1375
|
+
-data "%WS_DIR%" ^\r
|
|
1376
|
+
%*\r
|
|
1377
|
+
`;
|
|
1378
|
+
await fs.writeFile(winShim, winScript);
|
|
1379
|
+
}
|
|
1380
|
+
return shimPath;
|
|
1381
|
+
}
|
|
1382
|
+
async function ensureSymlinkInBin(lspKey, target, sourceAssetPath) {
|
|
1383
|
+
const binDir = getNativeLspBinDir();
|
|
1384
|
+
await fs.mkdir(binDir, { recursive: true });
|
|
1385
|
+
const winExtMatch = process.platform === "win32" && sourceAssetPath ? /\.(exe|bat|cmd|ps1)$/i.exec(sourceAssetPath) : null;
|
|
1386
|
+
const linkName = winExtMatch ? `${lspKey}${winExtMatch[0].toLowerCase()}` : lspKey;
|
|
1387
|
+
const linkPath = join13(binDir, linkName);
|
|
1388
|
+
try {
|
|
1389
|
+
await fs.unlink(linkPath);
|
|
1390
|
+
} catch {
|
|
1391
|
+
}
|
|
1392
|
+
try {
|
|
1393
|
+
await fs.symlink(target, linkPath);
|
|
1394
|
+
} catch (err) {
|
|
1395
|
+
if (err.code === "EPERM" || process.platform === "win32") {
|
|
1396
|
+
await fs.copyFile(target, linkPath);
|
|
1397
|
+
} else {
|
|
1398
|
+
throw err;
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
async function downloadFile(url, dest) {
|
|
1403
|
+
const res = await fetch(url);
|
|
1404
|
+
if (!res.ok || !res.body) {
|
|
1405
|
+
throw new Error(`download ${url} failed: HTTP ${res.status.toString()}`);
|
|
1406
|
+
}
|
|
1407
|
+
await fs.mkdir(dirname5(dest), { recursive: true });
|
|
1408
|
+
const sink = createWriteStream(dest);
|
|
1409
|
+
await pipeline(res.body, sink);
|
|
1410
|
+
}
|
|
1411
|
+
async function sha256File(path) {
|
|
1412
|
+
const hash = createHash3("sha256");
|
|
1413
|
+
const data = await fs.readFile(path);
|
|
1414
|
+
hash.update(data);
|
|
1415
|
+
return hash.digest("hex");
|
|
1416
|
+
}
|
|
1417
|
+
async function extractAsset(source, destDir, asset) {
|
|
1418
|
+
const filename = asset.filename.toLowerCase();
|
|
1419
|
+
if (filename.endsWith(".tar.gz") || filename.endsWith(".tgz")) {
|
|
1420
|
+
await runExternal("tar", ["-xzf", source, "-C", destDir]);
|
|
1421
|
+
return;
|
|
1422
|
+
}
|
|
1423
|
+
if (filename.endsWith(".zip")) {
|
|
1424
|
+
if (process.platform === "win32") {
|
|
1425
|
+
await runExternal("powershell.exe", [
|
|
1426
|
+
"-NoProfile",
|
|
1427
|
+
"-Command",
|
|
1428
|
+
`Expand-Archive -Path '${source}' -DestinationPath '${destDir}' -Force`
|
|
1429
|
+
]);
|
|
1430
|
+
} else {
|
|
1431
|
+
await runExternal("unzip", ["-o", "-q", source, "-d", destDir]);
|
|
1432
|
+
}
|
|
1433
|
+
return;
|
|
1434
|
+
}
|
|
1435
|
+
if (filename.endsWith(".gz")) {
|
|
1436
|
+
const outPath = join13(destDir, asset.binaryPath);
|
|
1437
|
+
await fs.mkdir(dirname5(outPath), { recursive: true });
|
|
1438
|
+
await pipeline((await fs.open(source)).createReadStream(), createGunzip(), createWriteStream(outPath));
|
|
1439
|
+
return;
|
|
1440
|
+
}
|
|
1441
|
+
await fs.copyFile(source, join13(destDir, asset.binaryPath));
|
|
1434
1442
|
}
|
|
1435
|
-
async function
|
|
1436
|
-
|
|
1437
|
-
|
|
1443
|
+
async function runExternal(cmd, args) {
|
|
1444
|
+
return new Promise((resolve5, reject) => {
|
|
1445
|
+
const child = spawn3(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
1438
1446
|
let stderr = "";
|
|
1439
|
-
let settled = false;
|
|
1440
|
-
const child = spawn3("java", ["-version"], { stdio: ["ignore", "pipe", "pipe"] });
|
|
1441
|
-
const timer = setTimeout(() => {
|
|
1442
|
-
if (settled)
|
|
1443
|
-
return;
|
|
1444
|
-
settled = true;
|
|
1445
|
-
try {
|
|
1446
|
-
child.kill("SIGKILL");
|
|
1447
|
-
} catch {
|
|
1448
|
-
}
|
|
1449
|
-
resolve5({ present: false });
|
|
1450
|
-
}, timeoutMs);
|
|
1451
1447
|
child.stderr.on("data", (chunk) => {
|
|
1452
|
-
stderr += chunk.toString(
|
|
1453
|
-
});
|
|
1454
|
-
child.on("error", () => {
|
|
1455
|
-
if (settled)
|
|
1456
|
-
return;
|
|
1457
|
-
settled = true;
|
|
1458
|
-
clearTimeout(timer);
|
|
1459
|
-
resolve5({ present: false });
|
|
1448
|
+
stderr += chunk.toString();
|
|
1460
1449
|
});
|
|
1450
|
+
child.on("error", reject);
|
|
1461
1451
|
child.on("close", (code) => {
|
|
1462
|
-
if (
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
if (code !== 0) {
|
|
1467
|
-
resolve5({ present: false, rawOutput: stderr.trim() });
|
|
1468
|
-
return;
|
|
1469
|
-
}
|
|
1470
|
-
const major = parseJavaMajorVersion(stderr);
|
|
1471
|
-
if (major === void 0) {
|
|
1472
|
-
resolve5({ present: true, rawOutput: stderr.trim() });
|
|
1473
|
-
return;
|
|
1474
|
-
}
|
|
1475
|
-
resolve5({ present: true, majorVersion: major, rawOutput: stderr.trim() });
|
|
1452
|
+
if (code === 0)
|
|
1453
|
+
resolve5();
|
|
1454
|
+
else
|
|
1455
|
+
reject(new Error(`${cmd} ${args.join(" ")} exited ${(code ?? -1).toString()}: ${stderr.split("\n").slice(0, 3).join(" ")}`));
|
|
1476
1456
|
});
|
|
1477
1457
|
});
|
|
1478
1458
|
}
|
|
1479
|
-
|
|
1480
|
-
|
|
1459
|
+
async function pathExists(p) {
|
|
1460
|
+
try {
|
|
1461
|
+
await fs.access(p);
|
|
1462
|
+
return true;
|
|
1463
|
+
} catch {
|
|
1464
|
+
return false;
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
var init_native_installer = __esm({
|
|
1468
|
+
"../listener/dist/lsp/native-installer.js"() {
|
|
1481
1469
|
"use strict";
|
|
1470
|
+
init_src();
|
|
1471
|
+
init_installer();
|
|
1472
|
+
init_jdk_probe();
|
|
1482
1473
|
}
|
|
1483
1474
|
});
|
|
1484
1475
|
|
|
1485
|
-
// ../listener/dist/lsp/
|
|
1486
|
-
import {
|
|
1487
|
-
import { promises as fs } from "fs";
|
|
1488
|
-
import { createGunzip } from "zlib";
|
|
1476
|
+
// ../listener/dist/lsp/coursier-installer.js
|
|
1477
|
+
import { promises as fs2 } from "fs";
|
|
1489
1478
|
import { spawn as spawn4 } from "child_process";
|
|
1490
|
-
import {
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
function getNativeLspDir() {
|
|
1494
|
-
return join15(getLspInstallDir(), "native");
|
|
1495
|
-
}
|
|
1496
|
-
function getNativeLspBinDir() {
|
|
1497
|
-
return join15(getNativeLspDir(), "bin");
|
|
1479
|
+
import { join as join14 } from "path";
|
|
1480
|
+
function getCoursierBinDir() {
|
|
1481
|
+
return join14(getLspInstallDir(), "coursier-bin");
|
|
1498
1482
|
}
|
|
1499
|
-
async function
|
|
1500
|
-
const entry =
|
|
1501
|
-
if (!entry)
|
|
1502
|
-
return { installed: false, alreadyPresent: false, skipped: "no-
|
|
1503
|
-
const platformKey = getCurrentPlatformKey();
|
|
1504
|
-
if (!platformKey) {
|
|
1505
|
-
return { installed: false, alreadyPresent: false, skipped: "unsupported-platform" };
|
|
1483
|
+
async function ensureCoursierLspInstalled(key) {
|
|
1484
|
+
const entry = COURSIER_LSPS[key];
|
|
1485
|
+
if (!entry) {
|
|
1486
|
+
return { installed: false, alreadyPresent: false, skipped: "no-coursier-entry" };
|
|
1506
1487
|
}
|
|
1507
|
-
const
|
|
1508
|
-
|
|
1509
|
-
|
|
1488
|
+
const binDir = getCoursierBinDir();
|
|
1489
|
+
const binaryPath = join14(binDir, entry.appName);
|
|
1490
|
+
const binaryPathExe = join14(binDir, `${entry.appName}.bat`);
|
|
1491
|
+
if (await pathExists2(binaryPath) || await pathExists2(binaryPathExe)) {
|
|
1492
|
+
return { installed: false, alreadyPresent: true, binaryPath };
|
|
1510
1493
|
}
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
};
|
|
1520
|
-
}
|
|
1494
|
+
const csBootstrap = await ensureNativeLspInstalled("coursier");
|
|
1495
|
+
if (csBootstrap.skipped || !csBootstrap.installed && !csBootstrap.alreadyPresent) {
|
|
1496
|
+
return {
|
|
1497
|
+
installed: false,
|
|
1498
|
+
alreadyPresent: false,
|
|
1499
|
+
skipped: "coursier-bootstrap-failed",
|
|
1500
|
+
error: csBootstrap.error ?? csBootstrap.skipped
|
|
1501
|
+
};
|
|
1521
1502
|
}
|
|
1522
|
-
const
|
|
1523
|
-
const
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
if (await pathExists(installedBinary)) {
|
|
1533
|
-
await ensureSymlinkInBin(lspKey, installedBinary, asset.binaryPath);
|
|
1534
|
-
return { installed: false, alreadyPresent: true, binaryPath: installedBinary };
|
|
1535
|
-
}
|
|
1503
|
+
const csPath = join14(getNativeLspBinDir(), "coursier");
|
|
1504
|
+
const csPathExe = join14(getNativeLspBinDir(), "coursier.exe");
|
|
1505
|
+
const cs = await pathExists2(csPath) ? csPath : await pathExists2(csPathExe) ? csPathExe : null;
|
|
1506
|
+
if (!cs) {
|
|
1507
|
+
return {
|
|
1508
|
+
installed: false,
|
|
1509
|
+
alreadyPresent: false,
|
|
1510
|
+
skipped: "coursier-bootstrap-failed",
|
|
1511
|
+
error: `coursier launcher not found at ${csPath} after bootstrap`
|
|
1512
|
+
};
|
|
1536
1513
|
}
|
|
1537
1514
|
try {
|
|
1538
|
-
await
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
if (!downloadUrl) {
|
|
1515
|
+
await fs2.mkdir(binDir, { recursive: true });
|
|
1516
|
+
await runCs(cs, ["install", "--install-dir", binDir, entry.appName]);
|
|
1517
|
+
if (!await pathExists2(binaryPath) && !await pathExists2(binaryPathExe)) {
|
|
1542
1518
|
return {
|
|
1543
1519
|
installed: false,
|
|
1544
1520
|
alreadyPresent: false,
|
|
1545
|
-
error: `
|
|
1521
|
+
error: `cs install ${entry.appName} completed but launcher missing at ${binaryPath}`
|
|
1546
1522
|
};
|
|
1547
1523
|
}
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
if (
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1524
|
+
return { installed: true, alreadyPresent: false, binaryPath };
|
|
1525
|
+
} catch (err) {
|
|
1526
|
+
return {
|
|
1527
|
+
installed: false,
|
|
1528
|
+
alreadyPresent: false,
|
|
1529
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1530
|
+
};
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
async function runCs(cs, args) {
|
|
1534
|
+
return new Promise((resolve5, reject) => {
|
|
1535
|
+
const child = spawn4(cs, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
1536
|
+
let stderr = "";
|
|
1537
|
+
child.stderr.on("data", (chunk) => {
|
|
1538
|
+
stderr += chunk.toString();
|
|
1539
|
+
});
|
|
1540
|
+
child.on("error", reject);
|
|
1541
|
+
child.on("close", (code) => {
|
|
1542
|
+
if (code === 0)
|
|
1543
|
+
resolve5();
|
|
1544
|
+
else
|
|
1545
|
+
reject(new Error(`cs ${args.join(" ")} exited ${(code ?? -1).toString()}: ${stderr.split("\n").slice(0, 3).join(" ")}`));
|
|
1546
|
+
});
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
async function pathExists2(p) {
|
|
1550
|
+
try {
|
|
1551
|
+
await fs2.access(p);
|
|
1552
|
+
return true;
|
|
1553
|
+
} catch {
|
|
1554
|
+
return false;
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
var init_coursier_installer = __esm({
|
|
1558
|
+
"../listener/dist/lsp/coursier-installer.js"() {
|
|
1559
|
+
"use strict";
|
|
1560
|
+
init_src();
|
|
1561
|
+
init_installer();
|
|
1562
|
+
init_native_installer();
|
|
1563
|
+
}
|
|
1564
|
+
});
|
|
1565
|
+
|
|
1566
|
+
// ../listener/dist/lsp/toolchain-installer.js
|
|
1567
|
+
import { spawn as spawn5 } from "child_process";
|
|
1568
|
+
import { promises as fs3 } from "fs";
|
|
1569
|
+
import { homedir as homedir3 } from "os";
|
|
1570
|
+
import { join as join15 } from "path";
|
|
1571
|
+
async function ensureToolchainLspInstalled(key) {
|
|
1572
|
+
const entry = TOOLCHAIN_LSPS[key];
|
|
1573
|
+
if (!entry) {
|
|
1574
|
+
return { installed: false, alreadyPresent: false, skipped: "no-toolchain-entry" };
|
|
1575
|
+
}
|
|
1576
|
+
if (entry.bundled) {
|
|
1577
|
+
const found = await isCommandOnPath(entry.binaryName);
|
|
1578
|
+
if (found)
|
|
1579
|
+
return { installed: false, alreadyPresent: true };
|
|
1580
|
+
return { installed: false, alreadyPresent: false, skipped: "bundled-not-installable" };
|
|
1581
|
+
}
|
|
1582
|
+
if (await isCommandOnPath(entry.binaryName)) {
|
|
1583
|
+
return { installed: false, alreadyPresent: true, skipped: "already-on-path" };
|
|
1584
|
+
}
|
|
1585
|
+
if (!entry.toolchain) {
|
|
1586
|
+
return { installed: false, alreadyPresent: false, skipped: "toolchain-missing" };
|
|
1587
|
+
}
|
|
1588
|
+
if (!await isCommandOnPath(entry.toolchain)) {
|
|
1589
|
+
return {
|
|
1590
|
+
installed: false,
|
|
1591
|
+
alreadyPresent: false,
|
|
1592
|
+
skipped: "toolchain-missing",
|
|
1593
|
+
error: `${entry.toolchain} toolchain not on PATH`
|
|
1594
|
+
};
|
|
1595
|
+
}
|
|
1596
|
+
let binDir;
|
|
1597
|
+
try {
|
|
1598
|
+
binDir = await resolveToolchainBinDir(entry);
|
|
1599
|
+
} catch (err) {
|
|
1600
|
+
return {
|
|
1601
|
+
installed: false,
|
|
1602
|
+
alreadyPresent: false,
|
|
1603
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1604
|
+
};
|
|
1605
|
+
}
|
|
1606
|
+
try {
|
|
1607
|
+
if (!entry.installArgs) {
|
|
1608
|
+
return { installed: false, alreadyPresent: false, skipped: "toolchain-missing" };
|
|
1588
1609
|
}
|
|
1589
|
-
await
|
|
1590
|
-
await ensureSymlinkInBin(lspKey, installedBinary, asset.binaryPath);
|
|
1591
|
-
await pruneStaleVersions(lspKey, entry.version);
|
|
1592
|
-
return { installed: true, alreadyPresent: false, binaryPath: installedBinary };
|
|
1610
|
+
await runToolchain(entry.toolchain, entry.installArgs);
|
|
1593
1611
|
} catch (err) {
|
|
1594
1612
|
return {
|
|
1595
1613
|
installed: false,
|
|
@@ -1597,714 +1615,766 @@ async function ensureNativeLspInstalled(lspKey) {
|
|
|
1597
1615
|
error: err instanceof Error ? err.message : String(err)
|
|
1598
1616
|
};
|
|
1599
1617
|
}
|
|
1618
|
+
const binaryPath = join15(binDir, entry.binaryName);
|
|
1619
|
+
if (!await pathExists3(binaryPath)) {
|
|
1620
|
+
return {
|
|
1621
|
+
installed: false,
|
|
1622
|
+
alreadyPresent: false,
|
|
1623
|
+
error: `${entry.toolchain} install of ${entry.binaryName} completed but binary missing at ${binaryPath}`,
|
|
1624
|
+
binDir
|
|
1625
|
+
};
|
|
1626
|
+
}
|
|
1627
|
+
return { installed: true, alreadyPresent: false, binaryPath, binDir };
|
|
1600
1628
|
}
|
|
1601
|
-
async function
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
for (let i = 0; i < parts.length - 1; i += 1) {
|
|
1605
|
-
dir = join15(dir, parts[i]);
|
|
1629
|
+
async function resolveToolchainBinDir(entry) {
|
|
1630
|
+
if (!entry.toolchain) {
|
|
1631
|
+
throw new Error("cannot resolve bin dir for entry without toolchain");
|
|
1606
1632
|
}
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
} catch {
|
|
1614
|
-
return null;
|
|
1633
|
+
if (entry.toolchainBinDir) {
|
|
1634
|
+
if (entry.toolchain === "go") {
|
|
1635
|
+
const gopath = process.env["GOPATH"] ?? join15(homedir3(), "go");
|
|
1636
|
+
return join15(gopath, "bin");
|
|
1637
|
+
}
|
|
1638
|
+
return join15(homedir3(), entry.toolchainBinDir);
|
|
1615
1639
|
}
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
}
|
|
1620
|
-
async function pruneStaleVersions(lspKey, currentVersion) {
|
|
1621
|
-
const lspDir = join15(getNativeLspDir(), lspKey);
|
|
1622
|
-
let entries;
|
|
1623
|
-
try {
|
|
1624
|
-
entries = await fs.readdir(lspDir);
|
|
1625
|
-
} catch {
|
|
1626
|
-
return;
|
|
1640
|
+
if (entry.toolchain === "gem") {
|
|
1641
|
+
const userdir = await captureStdout("gem", ["env", "userdir"]);
|
|
1642
|
+
return join15(userdir.trim(), "bin");
|
|
1627
1643
|
}
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1644
|
+
throw new Error(`no bin-dir resolution for toolchain ${entry.toolchain}`);
|
|
1645
|
+
}
|
|
1646
|
+
function getToolchainBinDirs() {
|
|
1647
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
1648
|
+
for (const entry of Object.values(TOOLCHAIN_LSPS)) {
|
|
1649
|
+
if (entry.bundled || !entry.toolchain || !entry.toolchainBinDir)
|
|
1650
|
+
continue;
|
|
1651
|
+
if (entry.toolchain === "go") {
|
|
1652
|
+
const gopath = process.env["GOPATH"] ?? join15(homedir3(), "go");
|
|
1653
|
+
dirs.add(join15(gopath, "bin"));
|
|
1654
|
+
} else {
|
|
1655
|
+
dirs.add(join15(homedir3(), entry.toolchainBinDir));
|
|
1632
1656
|
}
|
|
1633
|
-
}
|
|
1657
|
+
}
|
|
1658
|
+
return [...dirs];
|
|
1634
1659
|
}
|
|
1635
|
-
function
|
|
1636
|
-
return
|
|
1660
|
+
function runToolchain(cmd, args) {
|
|
1661
|
+
return new Promise((resolve5, reject) => {
|
|
1662
|
+
const child = spawn5(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
1663
|
+
let stderr = "";
|
|
1664
|
+
child.stderr.on("data", (chunk) => {
|
|
1665
|
+
stderr += chunk.toString();
|
|
1666
|
+
});
|
|
1667
|
+
child.on("error", reject);
|
|
1668
|
+
child.on("close", (code) => {
|
|
1669
|
+
if (code === 0)
|
|
1670
|
+
resolve5();
|
|
1671
|
+
else
|
|
1672
|
+
reject(new Error(`${cmd} ${args.join(" ")} exited ${(code ?? -1).toString()}: ${stderr.split("\n").slice(0, 3).join(" ")}`));
|
|
1673
|
+
});
|
|
1674
|
+
});
|
|
1637
1675
|
}
|
|
1638
|
-
function
|
|
1639
|
-
return
|
|
1676
|
+
function captureStdout(cmd, args) {
|
|
1677
|
+
return new Promise((resolve5, reject) => {
|
|
1678
|
+
const child = spawn5(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
1679
|
+
let stdout = "";
|
|
1680
|
+
let stderr = "";
|
|
1681
|
+
child.stdout.on("data", (chunk) => {
|
|
1682
|
+
stdout += chunk.toString();
|
|
1683
|
+
});
|
|
1684
|
+
child.stderr.on("data", (chunk) => {
|
|
1685
|
+
stderr += chunk.toString();
|
|
1686
|
+
});
|
|
1687
|
+
child.on("error", reject);
|
|
1688
|
+
child.on("close", (code) => {
|
|
1689
|
+
if (code === 0)
|
|
1690
|
+
resolve5(stdout);
|
|
1691
|
+
else
|
|
1692
|
+
reject(new Error(`${cmd} ${args.join(" ")} exited ${(code ?? -1).toString()}: ${stderr}`));
|
|
1693
|
+
});
|
|
1694
|
+
});
|
|
1640
1695
|
}
|
|
1641
|
-
async function
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
const
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
const unixScript = `#!/usr/bin/env bash
|
|
1651
|
-
# Generated by repowise listener \u2014 do not edit by hand.
|
|
1652
|
-
# Invokes Eclipse JDT.LS via the equinox launcher.
|
|
1653
|
-
set -euo pipefail
|
|
1654
|
-
WS_NAME="\${REPOWISE_JDTLS_WS_NAME:-default}"
|
|
1655
|
-
WS_DIR="${qWorkspaceBase}/\${WS_NAME}"
|
|
1656
|
-
mkdir -p "$WS_DIR"
|
|
1657
|
-
exec java \\
|
|
1658
|
-
-Declipse.application=org.eclipse.jdt.ls.core.id1 \\
|
|
1659
|
-
-Dosgi.bundles.defaultStartLevel=4 \\
|
|
1660
|
-
-Declipse.product=org.eclipse.jdt.ls.core.product \\
|
|
1661
|
-
-Dlog.level=ALL \\
|
|
1662
|
-
-Xms1g \\
|
|
1663
|
-
--add-modules=ALL-SYSTEM \\
|
|
1664
|
-
--add-opens java.base/java.util=ALL-UNNAMED \\
|
|
1665
|
-
--add-opens java.base/java.lang=ALL-UNNAMED \\
|
|
1666
|
-
-jar "${qLauncher}" \\
|
|
1667
|
-
-configuration "${qConfig}" \\
|
|
1668
|
-
-data "$WS_DIR" \\
|
|
1669
|
-
"$@"
|
|
1670
|
-
`;
|
|
1671
|
-
await fs.writeFile(shimPath, unixScript);
|
|
1672
|
-
await fs.chmod(shimPath, 493);
|
|
1673
|
-
if (process.platform === "win32") {
|
|
1674
|
-
const winShim = `${shimPath}.cmd`;
|
|
1675
|
-
const cqLauncher = cmdQuote(input.launcherJarPath);
|
|
1676
|
-
const cqConfig = cmdQuote(`${input.versionDir}\\${platformCfgDir}`);
|
|
1677
|
-
const cqWorkspaceBase = cmdQuote(workspaceBase);
|
|
1678
|
-
const winScript = `@echo off\r
|
|
1679
|
-
rem Generated by repowise listener\r
|
|
1680
|
-
if "%REPOWISE_JDTLS_WS_NAME%"=="" set REPOWISE_JDTLS_WS_NAME=default\r
|
|
1681
|
-
set WS_DIR=${cqWorkspaceBase}\\%REPOWISE_JDTLS_WS_NAME%\r
|
|
1682
|
-
if not exist "%WS_DIR%" mkdir "%WS_DIR%"\r
|
|
1683
|
-
java ^\r
|
|
1684
|
-
-Declipse.application=org.eclipse.jdt.ls.core.id1 ^\r
|
|
1685
|
-
-Dosgi.bundles.defaultStartLevel=4 ^\r
|
|
1686
|
-
-Declipse.product=org.eclipse.jdt.ls.core.product ^\r
|
|
1687
|
-
-Dlog.level=ALL ^\r
|
|
1688
|
-
-Xms1g ^\r
|
|
1689
|
-
--add-modules=ALL-SYSTEM ^\r
|
|
1690
|
-
--add-opens java.base/java.util=ALL-UNNAMED ^\r
|
|
1691
|
-
--add-opens java.base/java.lang=ALL-UNNAMED ^\r
|
|
1692
|
-
-jar "${cqLauncher}" ^\r
|
|
1693
|
-
-configuration "${cqConfig}" ^\r
|
|
1694
|
-
-data "%WS_DIR%" ^\r
|
|
1695
|
-
%*\r
|
|
1696
|
-
`;
|
|
1697
|
-
await fs.writeFile(winShim, winScript);
|
|
1698
|
-
}
|
|
1699
|
-
return shimPath;
|
|
1696
|
+
async function isCommandOnPath(command) {
|
|
1697
|
+
if (/[^\w./+-]/.test(command))
|
|
1698
|
+
return false;
|
|
1699
|
+
const probeCmd = process.platform === "win32" ? "where" : "which";
|
|
1700
|
+
return new Promise((resolve5) => {
|
|
1701
|
+
const child = spawn5(probeCmd, [command], { stdio: "ignore", timeout: 5e3 });
|
|
1702
|
+
child.on("error", () => resolve5(false));
|
|
1703
|
+
child.on("close", (code) => resolve5(code === 0));
|
|
1704
|
+
});
|
|
1700
1705
|
}
|
|
1701
|
-
async function
|
|
1702
|
-
const binDir = getNativeLspBinDir();
|
|
1703
|
-
await fs.mkdir(binDir, { recursive: true });
|
|
1704
|
-
const winExtMatch = process.platform === "win32" && sourceAssetPath ? /\.(exe|bat|cmd|ps1)$/i.exec(sourceAssetPath) : null;
|
|
1705
|
-
const linkName = winExtMatch ? `${lspKey}${winExtMatch[0].toLowerCase()}` : lspKey;
|
|
1706
|
-
const linkPath = join15(binDir, linkName);
|
|
1706
|
+
async function pathExists3(p) {
|
|
1707
1707
|
try {
|
|
1708
|
-
await
|
|
1708
|
+
await fs3.access(p);
|
|
1709
|
+
return true;
|
|
1709
1710
|
} catch {
|
|
1710
|
-
|
|
1711
|
-
try {
|
|
1712
|
-
await fs.symlink(target, linkPath);
|
|
1713
|
-
} catch (err) {
|
|
1714
|
-
if (err.code === "EPERM" || process.platform === "win32") {
|
|
1715
|
-
await fs.copyFile(target, linkPath);
|
|
1716
|
-
} else {
|
|
1717
|
-
throw err;
|
|
1718
|
-
}
|
|
1711
|
+
return false;
|
|
1719
1712
|
}
|
|
1720
1713
|
}
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1714
|
+
var init_toolchain_installer = __esm({
|
|
1715
|
+
"../listener/dist/lsp/toolchain-installer.js"() {
|
|
1716
|
+
"use strict";
|
|
1717
|
+
init_src();
|
|
1725
1718
|
}
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1719
|
+
});
|
|
1720
|
+
|
|
1721
|
+
// ../listener/dist/lsp/installer.js
|
|
1722
|
+
import { promises as fs4 } from "fs";
|
|
1723
|
+
import { join as join16, dirname as dirname6 } from "path";
|
|
1724
|
+
import { spawn as spawn6 } from "child_process";
|
|
1725
|
+
import lockfile3 from "proper-lockfile";
|
|
1726
|
+
function getLspInstallDir() {
|
|
1727
|
+
return join16(getConfigDir(), "lsp-servers");
|
|
1729
1728
|
}
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
const data = await fs.readFile(path);
|
|
1733
|
-
hash.update(data);
|
|
1734
|
-
return hash.digest("hex");
|
|
1729
|
+
function getLspBinDir() {
|
|
1730
|
+
return join16(getLspInstallDir(), "node_modules", ".bin");
|
|
1735
1731
|
}
|
|
1736
|
-
async function
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1732
|
+
async function ensureNpmLspInstalled(config2) {
|
|
1733
|
+
if (!config2.npmPackage)
|
|
1734
|
+
return { installed: false, skipped: "no-npm-package" };
|
|
1735
|
+
const installDir = getLspInstallDir();
|
|
1736
|
+
const binPath = join16(installDir, "node_modules", ".bin", config2.command);
|
|
1737
|
+
if (await pathExists4(binPath)) {
|
|
1738
|
+
return { installed: false, skipped: "already-present" };
|
|
1739
|
+
}
|
|
1740
|
+
try {
|
|
1741
|
+
await fs4.mkdir(installDir, { recursive: true });
|
|
1742
|
+
let release = null;
|
|
1743
|
+
try {
|
|
1744
|
+
release = await lockfile3.lock(installDir, {
|
|
1745
|
+
stale: 18e4,
|
|
1746
|
+
retries: { retries: 20, factor: 1.5, minTimeout: 500, maxTimeout: 5e3 },
|
|
1747
|
+
realpath: false
|
|
1748
|
+
});
|
|
1749
|
+
} catch {
|
|
1750
|
+
if (await pathExists4(binPath)) {
|
|
1751
|
+
return { installed: false, skipped: "already-present" };
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
try {
|
|
1755
|
+
if (await pathExists4(binPath)) {
|
|
1756
|
+
return { installed: false, skipped: "already-present" };
|
|
1757
|
+
}
|
|
1758
|
+
const pkgJsonPath = join16(installDir, "package.json");
|
|
1759
|
+
if (!await pathExists4(pkgJsonPath)) {
|
|
1760
|
+
await fs4.writeFile(pkgJsonPath, JSON.stringify({ name: "repowise-lsp-servers", private: true, version: "0.0.0" }, null, 2), "utf-8");
|
|
1761
|
+
}
|
|
1762
|
+
await runNpmInstall(installDir, config2.npmPackage);
|
|
1763
|
+
if (!await pathExists4(binPath)) {
|
|
1764
|
+
return {
|
|
1765
|
+
installed: false,
|
|
1766
|
+
error: `npm install completed but ${config2.command} still not at ${binPath}`
|
|
1767
|
+
};
|
|
1768
|
+
}
|
|
1769
|
+
return { installed: true };
|
|
1770
|
+
} finally {
|
|
1771
|
+
if (release) {
|
|
1772
|
+
try {
|
|
1773
|
+
await release();
|
|
1774
|
+
} catch {
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
} catch (err) {
|
|
1779
|
+
return {
|
|
1780
|
+
installed: false,
|
|
1781
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1782
|
+
};
|
|
1741
1783
|
}
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
}
|
|
1750
|
-
await runExternal("unzip", ["-o", "-q", source, "-d", destDir]);
|
|
1784
|
+
}
|
|
1785
|
+
async function resolveNpmCommand() {
|
|
1786
|
+
const nodeDir = dirname6(process.execPath);
|
|
1787
|
+
for (const candidate of [join16(nodeDir, "npm"), join16(nodeDir, "npm.cmd")]) {
|
|
1788
|
+
try {
|
|
1789
|
+
await fs4.access(candidate);
|
|
1790
|
+
return candidate;
|
|
1791
|
+
} catch {
|
|
1751
1792
|
}
|
|
1752
|
-
return;
|
|
1753
1793
|
}
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1794
|
+
for (const candidate of ["/opt/homebrew/bin/npm", "/usr/local/bin/npm", "/usr/bin/npm"]) {
|
|
1795
|
+
try {
|
|
1796
|
+
await fs4.access(candidate);
|
|
1797
|
+
return candidate;
|
|
1798
|
+
} catch {
|
|
1799
|
+
}
|
|
1759
1800
|
}
|
|
1760
|
-
|
|
1801
|
+
return "npm";
|
|
1761
1802
|
}
|
|
1762
|
-
async function
|
|
1803
|
+
async function runNpmInstall(cwd, pkg2) {
|
|
1804
|
+
const npmCmd = await resolveNpmCommand();
|
|
1805
|
+
const nodeDir = dirname6(process.execPath);
|
|
1806
|
+
const augmentedPath = process.env.PATH ? `${nodeDir}:${process.env.PATH}` : nodeDir;
|
|
1763
1807
|
return new Promise((resolve5, reject) => {
|
|
1764
|
-
const child =
|
|
1808
|
+
const child = spawn6(npmCmd, ["install", "--no-audit", "--no-fund", "--silent", pkg2], {
|
|
1809
|
+
cwd,
|
|
1810
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1811
|
+
env: { ...process.env, PATH: augmentedPath }
|
|
1812
|
+
});
|
|
1765
1813
|
let stderr = "";
|
|
1766
1814
|
child.stderr.on("data", (chunk) => {
|
|
1767
1815
|
stderr += chunk.toString();
|
|
1768
1816
|
});
|
|
1769
|
-
child.on("error", reject);
|
|
1817
|
+
child.on("error", (err) => reject(err));
|
|
1770
1818
|
child.on("close", (code) => {
|
|
1771
|
-
if (code === 0)
|
|
1819
|
+
if (code === 0) {
|
|
1772
1820
|
resolve5();
|
|
1773
|
-
else
|
|
1774
|
-
reject(new Error(
|
|
1821
|
+
} else {
|
|
1822
|
+
reject(new Error(`npm install ${pkg2} exited ${(code ?? -1).toString()}: ${sanitizeNpmStderr(stderr)}`));
|
|
1823
|
+
}
|
|
1775
1824
|
});
|
|
1776
1825
|
});
|
|
1777
1826
|
}
|
|
1778
|
-
|
|
1827
|
+
function sanitizeNpmStderr(raw) {
|
|
1828
|
+
const truncated = raw.split("\n").slice(0, 3).join(" ").trim();
|
|
1829
|
+
return truncated.replace(/(https?:\/\/)[^@\s/]*@/g, (_match, scheme) => `${scheme}<redacted>@`);
|
|
1830
|
+
}
|
|
1831
|
+
async function pathExists4(p) {
|
|
1779
1832
|
try {
|
|
1780
|
-
await
|
|
1833
|
+
await fs4.access(p);
|
|
1781
1834
|
return true;
|
|
1782
1835
|
} catch {
|
|
1783
1836
|
return false;
|
|
1784
1837
|
}
|
|
1785
1838
|
}
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
}
|
|
1813
|
-
const csBootstrap = await ensureNativeLspInstalled("coursier");
|
|
1814
|
-
if (csBootstrap.skipped || !csBootstrap.installed && !csBootstrap.alreadyPresent) {
|
|
1815
|
-
return {
|
|
1816
|
-
installed: false,
|
|
1817
|
-
alreadyPresent: false,
|
|
1818
|
-
skipped: "coursier-bootstrap-failed",
|
|
1819
|
-
error: csBootstrap.error ?? csBootstrap.skipped
|
|
1820
|
-
};
|
|
1821
|
-
}
|
|
1822
|
-
const csPath = join16(getNativeLspBinDir(), "coursier");
|
|
1823
|
-
const csPathExe = join16(getNativeLspBinDir(), "coursier.exe");
|
|
1824
|
-
const cs = await pathExists2(csPath) ? csPath : await pathExists2(csPathExe) ? csPathExe : null;
|
|
1825
|
-
if (!cs) {
|
|
1826
|
-
return {
|
|
1827
|
-
installed: false,
|
|
1828
|
-
alreadyPresent: false,
|
|
1829
|
-
skipped: "coursier-bootstrap-failed",
|
|
1830
|
-
error: `coursier launcher not found at ${csPath} after bootstrap`
|
|
1831
|
-
};
|
|
1839
|
+
async function detectRepoLanguages(repoRoot) {
|
|
1840
|
+
const found = /* @__PURE__ */ new Set();
|
|
1841
|
+
let inspected = 0;
|
|
1842
|
+
const MAX_INSPECT = 200;
|
|
1843
|
+
async function inspect(dir) {
|
|
1844
|
+
if (inspected >= MAX_INSPECT)
|
|
1845
|
+
return;
|
|
1846
|
+
let entries;
|
|
1847
|
+
try {
|
|
1848
|
+
entries = await fs4.readdir(dir);
|
|
1849
|
+
} catch {
|
|
1850
|
+
return;
|
|
1851
|
+
}
|
|
1852
|
+
for (const name of entries) {
|
|
1853
|
+
if (inspected >= MAX_INSPECT)
|
|
1854
|
+
return;
|
|
1855
|
+
if (name.startsWith("."))
|
|
1856
|
+
continue;
|
|
1857
|
+
if (name === "node_modules" || name === "dist" || name === "build")
|
|
1858
|
+
continue;
|
|
1859
|
+
const lang = detectLanguage(name);
|
|
1860
|
+
if (lang) {
|
|
1861
|
+
found.add(lang);
|
|
1862
|
+
inspected += 1;
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1832
1865
|
}
|
|
1866
|
+
await inspect(repoRoot);
|
|
1867
|
+
let topEntries = [];
|
|
1833
1868
|
try {
|
|
1834
|
-
await
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1869
|
+
topEntries = await fs4.readdir(repoRoot);
|
|
1870
|
+
} catch {
|
|
1871
|
+
return found;
|
|
1872
|
+
}
|
|
1873
|
+
for (const name of topEntries) {
|
|
1874
|
+
if (name.startsWith("."))
|
|
1875
|
+
continue;
|
|
1876
|
+
if (name === "node_modules" || name === "dist" || name === "build")
|
|
1877
|
+
continue;
|
|
1878
|
+
const sub = join16(repoRoot, name);
|
|
1879
|
+
try {
|
|
1880
|
+
const stat8 = await fs4.stat(sub);
|
|
1881
|
+
if (stat8.isDirectory())
|
|
1882
|
+
await inspect(sub);
|
|
1883
|
+
} catch {
|
|
1842
1884
|
}
|
|
1843
|
-
return { installed: true, alreadyPresent: false, binaryPath };
|
|
1844
|
-
} catch (err) {
|
|
1845
|
-
return {
|
|
1846
|
-
installed: false,
|
|
1847
|
-
alreadyPresent: false,
|
|
1848
|
-
error: err instanceof Error ? err.message : String(err)
|
|
1849
|
-
};
|
|
1850
1885
|
}
|
|
1886
|
+
return found;
|
|
1851
1887
|
}
|
|
1852
|
-
async function
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1888
|
+
async function prepareLspServersForRepos(repos, options = {}) {
|
|
1889
|
+
const detected = /* @__PURE__ */ new Set();
|
|
1890
|
+
for (const r of repos) {
|
|
1891
|
+
if (!r.localPath)
|
|
1892
|
+
continue;
|
|
1893
|
+
try {
|
|
1894
|
+
const stat8 = await fs4.stat(r.localPath);
|
|
1895
|
+
if (!stat8.isDirectory())
|
|
1896
|
+
continue;
|
|
1897
|
+
} catch {
|
|
1898
|
+
continue;
|
|
1899
|
+
}
|
|
1900
|
+
try {
|
|
1901
|
+
const langs = await detectRepoLanguages(r.localPath);
|
|
1902
|
+
for (const l of langs)
|
|
1903
|
+
detected.add(l);
|
|
1904
|
+
} catch {
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
const results = [];
|
|
1908
|
+
for (const language of detected) {
|
|
1909
|
+
const configs = getEffectiveConfigList(language, process.env, options.lspOverrides);
|
|
1910
|
+
const npmConfig = configs.find((c) => c.npmPackage);
|
|
1911
|
+
if (npmConfig) {
|
|
1912
|
+
const outcome = await ensureNpmLspInstalled(npmConfig);
|
|
1913
|
+
results.push({
|
|
1914
|
+
language,
|
|
1915
|
+
installed: outcome.installed,
|
|
1916
|
+
alreadyPresent: outcome.skipped === "already-present",
|
|
1917
|
+
skippedNoNpmPackage: false,
|
|
1918
|
+
installMethod: "npm",
|
|
1919
|
+
error: outcome.error
|
|
1920
|
+
});
|
|
1921
|
+
continue;
|
|
1922
|
+
}
|
|
1923
|
+
const nativeConfig = configs.find((c) => c.nativeInstallKey);
|
|
1924
|
+
if (nativeConfig?.nativeInstallKey) {
|
|
1925
|
+
const outcome = await ensureNativeLspInstalled(nativeConfig.nativeInstallKey);
|
|
1926
|
+
results.push({
|
|
1927
|
+
language,
|
|
1928
|
+
installed: outcome.installed,
|
|
1929
|
+
alreadyPresent: outcome.alreadyPresent,
|
|
1930
|
+
skippedNoNpmPackage: false,
|
|
1931
|
+
installMethod: "native",
|
|
1932
|
+
skipped: outcome.skipped,
|
|
1933
|
+
hint: outcome.skipped ? nativeConfig.installHint : void 0,
|
|
1934
|
+
error: outcome.error
|
|
1935
|
+
});
|
|
1936
|
+
continue;
|
|
1937
|
+
}
|
|
1938
|
+
const coursierConfig = configs.find((c) => c.coursierInstallKey);
|
|
1939
|
+
if (coursierConfig?.coursierInstallKey) {
|
|
1940
|
+
const outcome = await ensureCoursierLspInstalled(coursierConfig.coursierInstallKey);
|
|
1941
|
+
results.push({
|
|
1942
|
+
language,
|
|
1943
|
+
installed: outcome.installed,
|
|
1944
|
+
alreadyPresent: outcome.alreadyPresent,
|
|
1945
|
+
skippedNoNpmPackage: false,
|
|
1946
|
+
installMethod: "coursier",
|
|
1947
|
+
skipped: outcome.skipped,
|
|
1948
|
+
hint: outcome.skipped ? coursierConfig.installHint : void 0,
|
|
1949
|
+
error: outcome.error
|
|
1950
|
+
});
|
|
1951
|
+
continue;
|
|
1952
|
+
}
|
|
1953
|
+
const toolchainConfig = configs.find((c) => c.toolchainInstallKey);
|
|
1954
|
+
if (toolchainConfig?.toolchainInstallKey) {
|
|
1955
|
+
const outcome = await ensureToolchainLspInstalled(toolchainConfig.toolchainInstallKey);
|
|
1956
|
+
results.push({
|
|
1957
|
+
language,
|
|
1958
|
+
installed: outcome.installed,
|
|
1959
|
+
alreadyPresent: outcome.alreadyPresent,
|
|
1960
|
+
skippedNoNpmPackage: false,
|
|
1961
|
+
installMethod: "toolchain",
|
|
1962
|
+
skipped: outcome.skipped,
|
|
1963
|
+
// Surface the install hint for any skip reason — toolchain
|
|
1964
|
+
// missing means user needs to install it; bundled SDK means
|
|
1965
|
+
// user needs the SDK.
|
|
1966
|
+
hint: outcome.skipped ? toolchainConfig.installHint : void 0,
|
|
1967
|
+
error: outcome.error
|
|
1968
|
+
});
|
|
1969
|
+
continue;
|
|
1970
|
+
}
|
|
1971
|
+
results.push({
|
|
1972
|
+
language,
|
|
1973
|
+
installed: false,
|
|
1974
|
+
alreadyPresent: false,
|
|
1975
|
+
skippedNoNpmPackage: true,
|
|
1976
|
+
hint: configs[0]?.installHint
|
|
1865
1977
|
});
|
|
1866
|
-
});
|
|
1867
|
-
}
|
|
1868
|
-
async function pathExists2(p) {
|
|
1869
|
-
try {
|
|
1870
|
-
await fs2.access(p);
|
|
1871
|
-
return true;
|
|
1872
|
-
} catch {
|
|
1873
|
-
return false;
|
|
1874
1978
|
}
|
|
1979
|
+
return results;
|
|
1875
1980
|
}
|
|
1876
|
-
var
|
|
1877
|
-
"../listener/dist/lsp/
|
|
1981
|
+
var init_installer = __esm({
|
|
1982
|
+
"../listener/dist/lsp/installer.js"() {
|
|
1878
1983
|
"use strict";
|
|
1879
|
-
|
|
1880
|
-
|
|
1984
|
+
init_config_dir();
|
|
1985
|
+
init_registry();
|
|
1881
1986
|
init_native_installer();
|
|
1987
|
+
init_coursier_installer();
|
|
1988
|
+
init_toolchain_installer();
|
|
1882
1989
|
}
|
|
1883
1990
|
});
|
|
1884
1991
|
|
|
1885
|
-
// ../listener/dist/lsp/
|
|
1886
|
-
import { spawn as spawn6 } from "child_process";
|
|
1887
|
-
import { promises as fs3 } from "fs";
|
|
1992
|
+
// ../listener/dist/lsp/daemon-path.js
|
|
1888
1993
|
import { homedir as homedir4 } from "os";
|
|
1889
|
-
import { join as join17 } from "path";
|
|
1890
|
-
|
|
1891
|
-
const
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1994
|
+
import { delimiter, dirname as dirname7, join as join17 } from "path";
|
|
1995
|
+
function commonBinDirs() {
|
|
1996
|
+
const home = homedir4();
|
|
1997
|
+
return [
|
|
1998
|
+
"/opt/homebrew/bin",
|
|
1999
|
+
// Apple Silicon Homebrew
|
|
2000
|
+
"/opt/homebrew/sbin",
|
|
2001
|
+
"/usr/local/bin",
|
|
2002
|
+
// Intel Homebrew / common installs
|
|
2003
|
+
"/usr/bin",
|
|
2004
|
+
"/bin",
|
|
2005
|
+
"/usr/sbin",
|
|
2006
|
+
"/sbin",
|
|
2007
|
+
join17(home, ".local", "bin"),
|
|
2008
|
+
// pipx / pip --user / uv
|
|
2009
|
+
join17(home, ".npm-global", "bin"),
|
|
2010
|
+
// common npm global prefix
|
|
2011
|
+
join17(home, "go", "bin"),
|
|
2012
|
+
// gopls
|
|
2013
|
+
join17(home, ".cargo", "bin")
|
|
2014
|
+
// rust-analyzer via cargo
|
|
2015
|
+
];
|
|
2016
|
+
}
|
|
2017
|
+
function managedBinDirs() {
|
|
2018
|
+
return [getLspBinDir(), getNativeLspBinDir(), getCoursierBinDir(), ...getToolchainBinDirs()];
|
|
2019
|
+
}
|
|
2020
|
+
function dedup(dirs) {
|
|
2021
|
+
return [...new Set(dirs.filter((d) => d.length > 0))];
|
|
2022
|
+
}
|
|
2023
|
+
function daemonPathDirs() {
|
|
2024
|
+
return dedup([dirname7(process.execPath), ...commonBinDirs(), ...managedBinDirs()]);
|
|
2025
|
+
}
|
|
2026
|
+
function buildDaemonPath() {
|
|
2027
|
+
return daemonPathDirs().join(delimiter);
|
|
2028
|
+
}
|
|
2029
|
+
var init_daemon_path = __esm({
|
|
2030
|
+
"../listener/dist/lsp/daemon-path.js"() {
|
|
2031
|
+
"use strict";
|
|
2032
|
+
init_installer();
|
|
2033
|
+
init_native_installer();
|
|
2034
|
+
init_coursier_installer();
|
|
2035
|
+
init_toolchain_installer();
|
|
1924
2036
|
}
|
|
2037
|
+
});
|
|
2038
|
+
|
|
2039
|
+
// ../listener/dist/service-installer.js
|
|
2040
|
+
var service_installer_exports = {};
|
|
2041
|
+
__export(service_installer_exports, {
|
|
2042
|
+
install: () => install,
|
|
2043
|
+
isInstalled: () => isInstalled,
|
|
2044
|
+
isServiceRunning: () => isServiceRunning,
|
|
2045
|
+
stopService: () => stopService,
|
|
2046
|
+
uninstall: () => uninstall
|
|
2047
|
+
});
|
|
2048
|
+
import { execFile as execFile4 } from "child_process";
|
|
2049
|
+
import { writeFile as writeFile8, mkdir as mkdir8, unlink as unlink6 } from "fs/promises";
|
|
2050
|
+
import { homedir as homedir5 } from "os";
|
|
2051
|
+
import { join as join18 } from "path";
|
|
2052
|
+
function exec(cmd, args) {
|
|
2053
|
+
return new Promise((resolve5, reject) => {
|
|
2054
|
+
execFile4(cmd, args, (err, stdout) => {
|
|
2055
|
+
if (err) {
|
|
2056
|
+
reject(err);
|
|
2057
|
+
return;
|
|
2058
|
+
}
|
|
2059
|
+
resolve5(String(stdout ?? ""));
|
|
2060
|
+
});
|
|
2061
|
+
});
|
|
2062
|
+
}
|
|
2063
|
+
function xmlEscape(value) {
|
|
2064
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
2065
|
+
}
|
|
2066
|
+
function sanitizeEnvValue(value) {
|
|
2067
|
+
return Array.from(value).filter((ch) => ch.charCodeAt(0) >= 32).join("");
|
|
2068
|
+
}
|
|
2069
|
+
function plistPath() {
|
|
2070
|
+
return join18(homedir5(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
2071
|
+
}
|
|
2072
|
+
function logDir() {
|
|
2073
|
+
return join18(getConfigDir(), "logs");
|
|
2074
|
+
}
|
|
2075
|
+
function buildPlist() {
|
|
2076
|
+
const cmd = resolveListenerCommand();
|
|
2077
|
+
const logs = logDir();
|
|
2078
|
+
const programArgs = [process.execPath, cmd.script, ...cmd.args].map((a) => ` <string>${xmlEscape(a)}</string>`).join("\n");
|
|
2079
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
2080
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
|
2081
|
+
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
2082
|
+
<plist version="1.0">
|
|
2083
|
+
<dict>
|
|
2084
|
+
<key>Label</key>
|
|
2085
|
+
<string>${xmlEscape(PLIST_LABEL)}</string>
|
|
2086
|
+
<key>ProgramArguments</key>
|
|
2087
|
+
<array>
|
|
2088
|
+
${programArgs}
|
|
2089
|
+
</array>
|
|
2090
|
+
<key>RunAtLoad</key>
|
|
2091
|
+
<true/>
|
|
2092
|
+
<key>KeepAlive</key>
|
|
2093
|
+
<true/>
|
|
2094
|
+
<!--
|
|
2095
|
+
FIX-8: launchd inherits a minimal PATH (/usr/bin:/bin:\u2026) that excludes
|
|
2096
|
+
/opt/homebrew/bin + the npm-global bin, so user-installed LSP servers
|
|
2097
|
+
(pyright, rust-analyzer, \u2026) cannot be spawned and the typed-resolution
|
|
2098
|
+
which-probe fails. Inject a rich PATH (single source of truth:
|
|
2099
|
+
buildDaemonPath in lsp/daemon-path.ts) so the daemon resolves them.
|
|
2100
|
+
-->
|
|
2101
|
+
<key>EnvironmentVariables</key>
|
|
2102
|
+
<dict>
|
|
2103
|
+
<key>PATH</key>
|
|
2104
|
+
<string>${xmlEscape(buildDaemonPath())}</string>
|
|
2105
|
+
</dict>
|
|
2106
|
+
<key>StandardOutPath</key>
|
|
2107
|
+
<string>${xmlEscape(join18(logs, "listener-stdout.log"))}</string>
|
|
2108
|
+
<key>StandardErrorPath</key>
|
|
2109
|
+
<string>${xmlEscape(join18(logs, "listener-stderr.log"))}</string>
|
|
2110
|
+
<key>ProcessType</key>
|
|
2111
|
+
<string>Background</string>
|
|
2112
|
+
<!--
|
|
2113
|
+
SoftResourceLimits: raise the open-file descriptor limit from the macOS
|
|
2114
|
+
default of 256 to 4096. When multiple LSP child processes (rust-analyzer,
|
|
2115
|
+
jdtls, etc.) share the listener's fd budget they can exhaust the default
|
|
2116
|
+
very quickly, causing EMFILE errors. 4096 matches the system-wide default
|
|
2117
|
+
set by /etc/launchd.conf on modern macOS and gives enough headroom for
|
|
2118
|
+
~10 concurrent LSP servers each opening ~300 fds.
|
|
2119
|
+
|
|
2120
|
+
MemoryLimit: intentionally NOT set here. The launchd MemoryLimit key
|
|
2121
|
+
(available since macOS 12.3) terminates the entire LaunchAgent when RSS
|
|
2122
|
+
exceeds the threshold \u2014 it cannot be scoped to individual child processes.
|
|
2123
|
+
Killing the listener to evict a memory-hungry rust-analyzer instance is
|
|
2124
|
+
undesirable. Per-child memory pressure is better handled by the LSP
|
|
2125
|
+
process manager (process-manager.ts) or by OS-level memory pressure
|
|
2126
|
+
signals (SIGTERM on macOS, OOM killer on Linux).
|
|
2127
|
+
-->
|
|
2128
|
+
<key>SoftResourceLimits</key>
|
|
2129
|
+
<dict>
|
|
2130
|
+
<key>NumberOfFiles</key>
|
|
2131
|
+
<integer>4096</integer>
|
|
2132
|
+
</dict>
|
|
2133
|
+
</dict>
|
|
2134
|
+
</plist>`;
|
|
2135
|
+
}
|
|
2136
|
+
async function darwinInstall() {
|
|
2137
|
+
await mkdir8(logDir(), { recursive: true });
|
|
2138
|
+
await mkdir8(join18(homedir5(), "Library", "LaunchAgents"), { recursive: true });
|
|
1925
2139
|
try {
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
}
|
|
1929
|
-
await runToolchain(entry.toolchain, entry.installArgs);
|
|
1930
|
-
} catch (err) {
|
|
1931
|
-
return {
|
|
1932
|
-
installed: false,
|
|
1933
|
-
alreadyPresent: false,
|
|
1934
|
-
error: err instanceof Error ? err.message : String(err)
|
|
1935
|
-
};
|
|
1936
|
-
}
|
|
1937
|
-
const binaryPath = join17(binDir, entry.binaryName);
|
|
1938
|
-
if (!await pathExists3(binaryPath)) {
|
|
1939
|
-
return {
|
|
1940
|
-
installed: false,
|
|
1941
|
-
alreadyPresent: false,
|
|
1942
|
-
error: `${entry.toolchain} install of ${entry.binaryName} completed but binary missing at ${binaryPath}`,
|
|
1943
|
-
binDir
|
|
1944
|
-
};
|
|
2140
|
+
await exec("launchctl", ["unload", plistPath()]);
|
|
2141
|
+
} catch {
|
|
1945
2142
|
}
|
|
1946
|
-
|
|
2143
|
+
await writeFile8(plistPath(), buildPlist());
|
|
2144
|
+
await exec("launchctl", ["load", plistPath()]);
|
|
1947
2145
|
}
|
|
1948
|
-
async function
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
}
|
|
1952
|
-
if (entry.toolchainBinDir) {
|
|
1953
|
-
if (entry.toolchain === "go") {
|
|
1954
|
-
const gopath = process.env["GOPATH"] ?? join17(homedir4(), "go");
|
|
1955
|
-
return join17(gopath, "bin");
|
|
1956
|
-
}
|
|
1957
|
-
return join17(homedir4(), entry.toolchainBinDir);
|
|
2146
|
+
async function darwinUninstall() {
|
|
2147
|
+
try {
|
|
2148
|
+
await exec("launchctl", ["unload", plistPath()]);
|
|
2149
|
+
} catch {
|
|
1958
2150
|
}
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
2151
|
+
try {
|
|
2152
|
+
await unlink6(plistPath());
|
|
2153
|
+
} catch {
|
|
1962
2154
|
}
|
|
1963
|
-
throw new Error(`no bin-dir resolution for toolchain ${entry.toolchain}`);
|
|
1964
2155
|
}
|
|
1965
|
-
function
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
const gopath = process.env["GOPATH"] ?? join17(homedir4(), "go");
|
|
1972
|
-
dirs.add(join17(gopath, "bin"));
|
|
1973
|
-
} else {
|
|
1974
|
-
dirs.add(join17(homedir4(), entry.toolchainBinDir));
|
|
1975
|
-
}
|
|
2156
|
+
async function darwinIsInstalled() {
|
|
2157
|
+
try {
|
|
2158
|
+
const stdout = await exec("launchctl", ["list"]);
|
|
2159
|
+
return stdout.includes(PLIST_LABEL);
|
|
2160
|
+
} catch {
|
|
2161
|
+
return false;
|
|
1976
2162
|
}
|
|
1977
|
-
return [...dirs];
|
|
1978
2163
|
}
|
|
1979
|
-
function
|
|
1980
|
-
return
|
|
1981
|
-
const child = spawn6(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
1982
|
-
let stderr = "";
|
|
1983
|
-
child.stderr.on("data", (chunk) => {
|
|
1984
|
-
stderr += chunk.toString();
|
|
1985
|
-
});
|
|
1986
|
-
child.on("error", reject);
|
|
1987
|
-
child.on("close", (code) => {
|
|
1988
|
-
if (code === 0)
|
|
1989
|
-
resolve5();
|
|
1990
|
-
else
|
|
1991
|
-
reject(new Error(`${cmd} ${args.join(" ")} exited ${(code ?? -1).toString()}: ${stderr.split("\n").slice(0, 3).join(" ")}`));
|
|
1992
|
-
});
|
|
1993
|
-
});
|
|
2164
|
+
function unitPath() {
|
|
2165
|
+
return join18(homedir5(), ".config", "systemd", "user", `${SYSTEMD_SERVICE}.service`);
|
|
1994
2166
|
}
|
|
1995
|
-
function
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2167
|
+
function buildUnit() {
|
|
2168
|
+
const cmd = resolveListenerCommand();
|
|
2169
|
+
const execStart = [process.execPath, cmd.script, ...cmd.args].join(" ");
|
|
2170
|
+
const logs = logDir();
|
|
2171
|
+
return `[Unit]
|
|
2172
|
+
Description=RepoWise Listener
|
|
2173
|
+
After=network-online.target
|
|
2174
|
+
Wants=network-online.target
|
|
2175
|
+
|
|
2176
|
+
[Service]
|
|
2177
|
+
Type=simple
|
|
2178
|
+
# FIX-8: systemd user services run with a minimal PATH and don't load shell
|
|
2179
|
+
# profiles. Inject a rich PATH so LSP servers + the typed-resolution probe
|
|
2180
|
+
# resolve (single source of truth: buildDaemonPath() in lsp/daemon-path.ts).
|
|
2181
|
+
Environment=PATH=${sanitizeEnvValue(buildDaemonPath())}
|
|
2182
|
+
ExecStart=${execStart}
|
|
2183
|
+
Restart=always
|
|
2184
|
+
RestartSec=10
|
|
2185
|
+
StandardOutput=append:${join18(logs, "listener-stdout.log")}
|
|
2186
|
+
StandardError=append:${join18(logs, "listener-stderr.log")}
|
|
2187
|
+
|
|
2188
|
+
[Install]
|
|
2189
|
+
WantedBy=default.target`;
|
|
2014
2190
|
}
|
|
2015
|
-
async function
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
child.on("close", (code) => resolve5(code === 0));
|
|
2023
|
-
});
|
|
2191
|
+
async function linuxInstall() {
|
|
2192
|
+
await mkdir8(logDir(), { recursive: true });
|
|
2193
|
+
await mkdir8(join18(homedir5(), ".config", "systemd", "user"), { recursive: true });
|
|
2194
|
+
await writeFile8(unitPath(), buildUnit());
|
|
2195
|
+
await exec("systemctl", ["--user", "daemon-reload"]);
|
|
2196
|
+
await exec("systemctl", ["--user", "enable", SYSTEMD_SERVICE]);
|
|
2197
|
+
await exec("systemctl", ["--user", "start", SYSTEMD_SERVICE]);
|
|
2024
2198
|
}
|
|
2025
|
-
async function
|
|
2199
|
+
async function linuxUninstall() {
|
|
2026
2200
|
try {
|
|
2027
|
-
await
|
|
2028
|
-
|
|
2201
|
+
await exec("systemctl", ["--user", "stop", SYSTEMD_SERVICE]);
|
|
2202
|
+
} catch {
|
|
2203
|
+
}
|
|
2204
|
+
try {
|
|
2205
|
+
await exec("systemctl", ["--user", "disable", SYSTEMD_SERVICE]);
|
|
2206
|
+
} catch {
|
|
2207
|
+
}
|
|
2208
|
+
try {
|
|
2209
|
+
await unlink6(unitPath());
|
|
2210
|
+
} catch {
|
|
2211
|
+
}
|
|
2212
|
+
try {
|
|
2213
|
+
await exec("systemctl", ["--user", "daemon-reload"]);
|
|
2029
2214
|
} catch {
|
|
2030
|
-
return false;
|
|
2031
2215
|
}
|
|
2032
2216
|
}
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
"
|
|
2036
|
-
|
|
2217
|
+
async function linuxIsInstalled() {
|
|
2218
|
+
try {
|
|
2219
|
+
const stdout = await exec("systemctl", ["--user", "is-enabled", SYSTEMD_SERVICE]);
|
|
2220
|
+
return stdout.trim() === "enabled";
|
|
2221
|
+
} catch {
|
|
2222
|
+
return false;
|
|
2037
2223
|
}
|
|
2038
|
-
});
|
|
2039
|
-
|
|
2040
|
-
// ../listener/dist/lsp/installer.js
|
|
2041
|
-
import { promises as fs4 } from "fs";
|
|
2042
|
-
import { join as join18, dirname as dirname6 } from "path";
|
|
2043
|
-
import { spawn as spawn7 } from "child_process";
|
|
2044
|
-
import lockfile3 from "proper-lockfile";
|
|
2045
|
-
function getLspInstallDir() {
|
|
2046
|
-
return join18(getConfigDir(), "lsp-servers");
|
|
2047
2224
|
}
|
|
2048
|
-
function
|
|
2049
|
-
|
|
2225
|
+
async function win32Install() {
|
|
2226
|
+
await mkdir8(logDir(), { recursive: true });
|
|
2227
|
+
const cmd = resolveListenerCommand();
|
|
2228
|
+
const taskCmd = [process.execPath, cmd.script, ...cmd.args].map((a) => `"${a}"`).join(" ");
|
|
2229
|
+
await exec("schtasks", [
|
|
2230
|
+
"/create",
|
|
2231
|
+
"/tn",
|
|
2232
|
+
TASK_NAME,
|
|
2233
|
+
"/tr",
|
|
2234
|
+
taskCmd,
|
|
2235
|
+
"/sc",
|
|
2236
|
+
"onlogon",
|
|
2237
|
+
"/ru",
|
|
2238
|
+
process.env.USERNAME ?? "",
|
|
2239
|
+
"/f"
|
|
2240
|
+
]);
|
|
2241
|
+
await exec("schtasks", ["/run", "/tn", TASK_NAME]);
|
|
2050
2242
|
}
|
|
2051
|
-
async function
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
const binPath = join18(installDir, "node_modules", ".bin", config2.command);
|
|
2056
|
-
if (await pathExists4(binPath)) {
|
|
2057
|
-
return { installed: false, skipped: "already-present" };
|
|
2243
|
+
async function win32Uninstall() {
|
|
2244
|
+
try {
|
|
2245
|
+
await exec("schtasks", ["/end", "/tn", TASK_NAME]);
|
|
2246
|
+
} catch {
|
|
2058
2247
|
}
|
|
2059
2248
|
try {
|
|
2060
|
-
await
|
|
2061
|
-
|
|
2062
|
-
try {
|
|
2063
|
-
release = await lockfile3.lock(installDir, {
|
|
2064
|
-
stale: 18e4,
|
|
2065
|
-
retries: { retries: 20, factor: 1.5, minTimeout: 500, maxTimeout: 5e3 },
|
|
2066
|
-
realpath: false
|
|
2067
|
-
});
|
|
2068
|
-
} catch {
|
|
2069
|
-
if (await pathExists4(binPath)) {
|
|
2070
|
-
return { installed: false, skipped: "already-present" };
|
|
2071
|
-
}
|
|
2072
|
-
}
|
|
2073
|
-
try {
|
|
2074
|
-
if (await pathExists4(binPath)) {
|
|
2075
|
-
return { installed: false, skipped: "already-present" };
|
|
2076
|
-
}
|
|
2077
|
-
const pkgJsonPath = join18(installDir, "package.json");
|
|
2078
|
-
if (!await pathExists4(pkgJsonPath)) {
|
|
2079
|
-
await fs4.writeFile(pkgJsonPath, JSON.stringify({ name: "repowise-lsp-servers", private: true, version: "0.0.0" }, null, 2), "utf-8");
|
|
2080
|
-
}
|
|
2081
|
-
await runNpmInstall(installDir, config2.npmPackage);
|
|
2082
|
-
if (!await pathExists4(binPath)) {
|
|
2083
|
-
return {
|
|
2084
|
-
installed: false,
|
|
2085
|
-
error: `npm install completed but ${config2.command} still not at ${binPath}`
|
|
2086
|
-
};
|
|
2087
|
-
}
|
|
2088
|
-
return { installed: true };
|
|
2089
|
-
} finally {
|
|
2090
|
-
if (release) {
|
|
2091
|
-
try {
|
|
2092
|
-
await release();
|
|
2093
|
-
} catch {
|
|
2094
|
-
}
|
|
2095
|
-
}
|
|
2096
|
-
}
|
|
2097
|
-
} catch (err) {
|
|
2098
|
-
return {
|
|
2099
|
-
installed: false,
|
|
2100
|
-
error: err instanceof Error ? err.message : String(err)
|
|
2101
|
-
};
|
|
2249
|
+
await exec("schtasks", ["/delete", "/tn", TASK_NAME, "/f"]);
|
|
2250
|
+
} catch {
|
|
2102
2251
|
}
|
|
2103
2252
|
}
|
|
2104
|
-
async function
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
} catch {
|
|
2111
|
-
}
|
|
2253
|
+
async function win32IsInstalled() {
|
|
2254
|
+
try {
|
|
2255
|
+
await exec("schtasks", ["/query", "/tn", TASK_NAME]);
|
|
2256
|
+
return true;
|
|
2257
|
+
} catch {
|
|
2258
|
+
return false;
|
|
2112
2259
|
}
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
}
|
|
2260
|
+
}
|
|
2261
|
+
async function darwinStop() {
|
|
2262
|
+
try {
|
|
2263
|
+
await exec("launchctl", ["unload", plistPath()]);
|
|
2264
|
+
} catch {
|
|
2119
2265
|
}
|
|
2120
|
-
return "npm";
|
|
2121
2266
|
}
|
|
2122
|
-
async function
|
|
2123
|
-
|
|
2124
|
-
const nodeDir = dirname6(process.execPath);
|
|
2125
|
-
const augmentedPath = process.env.PATH ? `${nodeDir}:${process.env.PATH}` : nodeDir;
|
|
2126
|
-
return new Promise((resolve5, reject) => {
|
|
2127
|
-
const child = spawn7(npmCmd, ["install", "--no-audit", "--no-fund", "--silent", pkg2], {
|
|
2128
|
-
cwd,
|
|
2129
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
2130
|
-
env: { ...process.env, PATH: augmentedPath }
|
|
2131
|
-
});
|
|
2132
|
-
let stderr = "";
|
|
2133
|
-
child.stderr.on("data", (chunk) => {
|
|
2134
|
-
stderr += chunk.toString();
|
|
2135
|
-
});
|
|
2136
|
-
child.on("error", (err) => reject(err));
|
|
2137
|
-
child.on("close", (code) => {
|
|
2138
|
-
if (code === 0) {
|
|
2139
|
-
resolve5();
|
|
2140
|
-
} else {
|
|
2141
|
-
reject(new Error(`npm install ${pkg2} exited ${(code ?? -1).toString()}: ${sanitizeNpmStderr(stderr)}`));
|
|
2142
|
-
}
|
|
2143
|
-
});
|
|
2144
|
-
});
|
|
2267
|
+
async function linuxStop() {
|
|
2268
|
+
await exec("systemctl", ["--user", "stop", SYSTEMD_SERVICE]);
|
|
2145
2269
|
}
|
|
2146
|
-
function
|
|
2147
|
-
|
|
2148
|
-
return truncated.replace(/(https?:\/\/)[^@\s/]*@/g, (_match, scheme) => `${scheme}<redacted>@`);
|
|
2270
|
+
async function win32Stop() {
|
|
2271
|
+
await exec("schtasks", ["/end", "/tn", TASK_NAME]);
|
|
2149
2272
|
}
|
|
2150
|
-
async function
|
|
2273
|
+
async function darwinIsServiceRunning() {
|
|
2151
2274
|
try {
|
|
2152
|
-
await
|
|
2153
|
-
|
|
2275
|
+
const stdout = await exec("launchctl", ["list", PLIST_LABEL]);
|
|
2276
|
+
const pidMatch = stdout.match(/"PID"\s*=\s*(\d+)/);
|
|
2277
|
+
return pidMatch !== null && parseInt(pidMatch[1], 10) > 0;
|
|
2154
2278
|
} catch {
|
|
2155
2279
|
return false;
|
|
2156
2280
|
}
|
|
2157
2281
|
}
|
|
2158
|
-
async function
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
return;
|
|
2165
|
-
let entries;
|
|
2166
|
-
try {
|
|
2167
|
-
entries = await fs4.readdir(dir);
|
|
2168
|
-
} catch {
|
|
2169
|
-
return;
|
|
2170
|
-
}
|
|
2171
|
-
for (const name of entries) {
|
|
2172
|
-
if (inspected >= MAX_INSPECT)
|
|
2173
|
-
return;
|
|
2174
|
-
if (name.startsWith("."))
|
|
2175
|
-
continue;
|
|
2176
|
-
if (name === "node_modules" || name === "dist" || name === "build")
|
|
2177
|
-
continue;
|
|
2178
|
-
const lang = detectLanguage(name);
|
|
2179
|
-
if (lang) {
|
|
2180
|
-
found.add(lang);
|
|
2181
|
-
inspected += 1;
|
|
2182
|
-
}
|
|
2183
|
-
}
|
|
2282
|
+
async function linuxIsServiceRunning() {
|
|
2283
|
+
try {
|
|
2284
|
+
const stdout = await exec("systemctl", ["--user", "is-active", SYSTEMD_SERVICE]);
|
|
2285
|
+
return stdout.trim() === "active";
|
|
2286
|
+
} catch {
|
|
2287
|
+
return false;
|
|
2184
2288
|
}
|
|
2185
|
-
|
|
2186
|
-
|
|
2289
|
+
}
|
|
2290
|
+
async function win32IsServiceRunning() {
|
|
2187
2291
|
try {
|
|
2188
|
-
|
|
2292
|
+
const stdout = await exec("schtasks", ["/query", "/tn", TASK_NAME, "/fo", "CSV"]);
|
|
2293
|
+
return stdout.includes("Running");
|
|
2189
2294
|
} catch {
|
|
2190
|
-
return
|
|
2295
|
+
return false;
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2298
|
+
async function stopService() {
|
|
2299
|
+
switch (process.platform) {
|
|
2300
|
+
case "darwin":
|
|
2301
|
+
await darwinStop();
|
|
2302
|
+
break;
|
|
2303
|
+
case "linux":
|
|
2304
|
+
await linuxStop();
|
|
2305
|
+
break;
|
|
2306
|
+
case "win32":
|
|
2307
|
+
await win32Stop();
|
|
2308
|
+
break;
|
|
2309
|
+
default:
|
|
2310
|
+
throw new Error(`Unsupported platform: ${process.platform}`);
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
async function install() {
|
|
2314
|
+
switch (process.platform) {
|
|
2315
|
+
case "darwin":
|
|
2316
|
+
await darwinInstall();
|
|
2317
|
+
break;
|
|
2318
|
+
case "linux":
|
|
2319
|
+
await linuxInstall();
|
|
2320
|
+
break;
|
|
2321
|
+
case "win32":
|
|
2322
|
+
await win32Install();
|
|
2323
|
+
break;
|
|
2324
|
+
default:
|
|
2325
|
+
throw new Error(`Unsupported platform: ${process.platform}`);
|
|
2191
2326
|
}
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2327
|
+
}
|
|
2328
|
+
async function uninstall() {
|
|
2329
|
+
switch (process.platform) {
|
|
2330
|
+
case "darwin":
|
|
2331
|
+
await darwinUninstall();
|
|
2332
|
+
break;
|
|
2333
|
+
case "linux":
|
|
2334
|
+
await linuxUninstall();
|
|
2335
|
+
break;
|
|
2336
|
+
case "win32":
|
|
2337
|
+
await win32Uninstall();
|
|
2338
|
+
break;
|
|
2339
|
+
default:
|
|
2340
|
+
throw new Error(`Unsupported platform: ${process.platform}`);
|
|
2204
2341
|
}
|
|
2205
|
-
return found;
|
|
2206
2342
|
}
|
|
2207
|
-
async function
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
continue;
|
|
2218
|
-
}
|
|
2219
|
-
try {
|
|
2220
|
-
const langs = await detectRepoLanguages(r.localPath);
|
|
2221
|
-
for (const l of langs)
|
|
2222
|
-
detected.add(l);
|
|
2223
|
-
} catch {
|
|
2224
|
-
}
|
|
2343
|
+
async function isInstalled() {
|
|
2344
|
+
switch (process.platform) {
|
|
2345
|
+
case "darwin":
|
|
2346
|
+
return darwinIsInstalled();
|
|
2347
|
+
case "linux":
|
|
2348
|
+
return linuxIsInstalled();
|
|
2349
|
+
case "win32":
|
|
2350
|
+
return win32IsInstalled();
|
|
2351
|
+
default:
|
|
2352
|
+
return false;
|
|
2225
2353
|
}
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
installMethod: "npm",
|
|
2238
|
-
error: outcome.error
|
|
2239
|
-
});
|
|
2240
|
-
continue;
|
|
2241
|
-
}
|
|
2242
|
-
const nativeConfig = configs.find((c) => c.nativeInstallKey);
|
|
2243
|
-
if (nativeConfig?.nativeInstallKey) {
|
|
2244
|
-
const outcome = await ensureNativeLspInstalled(nativeConfig.nativeInstallKey);
|
|
2245
|
-
results.push({
|
|
2246
|
-
language,
|
|
2247
|
-
installed: outcome.installed,
|
|
2248
|
-
alreadyPresent: outcome.alreadyPresent,
|
|
2249
|
-
skippedNoNpmPackage: false,
|
|
2250
|
-
installMethod: "native",
|
|
2251
|
-
skipped: outcome.skipped,
|
|
2252
|
-
hint: outcome.skipped ? nativeConfig.installHint : void 0,
|
|
2253
|
-
error: outcome.error
|
|
2254
|
-
});
|
|
2255
|
-
continue;
|
|
2256
|
-
}
|
|
2257
|
-
const coursierConfig = configs.find((c) => c.coursierInstallKey);
|
|
2258
|
-
if (coursierConfig?.coursierInstallKey) {
|
|
2259
|
-
const outcome = await ensureCoursierLspInstalled(coursierConfig.coursierInstallKey);
|
|
2260
|
-
results.push({
|
|
2261
|
-
language,
|
|
2262
|
-
installed: outcome.installed,
|
|
2263
|
-
alreadyPresent: outcome.alreadyPresent,
|
|
2264
|
-
skippedNoNpmPackage: false,
|
|
2265
|
-
installMethod: "coursier",
|
|
2266
|
-
skipped: outcome.skipped,
|
|
2267
|
-
hint: outcome.skipped ? coursierConfig.installHint : void 0,
|
|
2268
|
-
error: outcome.error
|
|
2269
|
-
});
|
|
2270
|
-
continue;
|
|
2271
|
-
}
|
|
2272
|
-
const toolchainConfig = configs.find((c) => c.toolchainInstallKey);
|
|
2273
|
-
if (toolchainConfig?.toolchainInstallKey) {
|
|
2274
|
-
const outcome = await ensureToolchainLspInstalled(toolchainConfig.toolchainInstallKey);
|
|
2275
|
-
results.push({
|
|
2276
|
-
language,
|
|
2277
|
-
installed: outcome.installed,
|
|
2278
|
-
alreadyPresent: outcome.alreadyPresent,
|
|
2279
|
-
skippedNoNpmPackage: false,
|
|
2280
|
-
installMethod: "toolchain",
|
|
2281
|
-
skipped: outcome.skipped,
|
|
2282
|
-
// Surface the install hint for any skip reason — toolchain
|
|
2283
|
-
// missing means user needs to install it; bundled SDK means
|
|
2284
|
-
// user needs the SDK.
|
|
2285
|
-
hint: outcome.skipped ? toolchainConfig.installHint : void 0,
|
|
2286
|
-
error: outcome.error
|
|
2287
|
-
});
|
|
2288
|
-
continue;
|
|
2289
|
-
}
|
|
2290
|
-
results.push({
|
|
2291
|
-
language,
|
|
2292
|
-
installed: false,
|
|
2293
|
-
alreadyPresent: false,
|
|
2294
|
-
skippedNoNpmPackage: true,
|
|
2295
|
-
hint: configs[0]?.installHint
|
|
2296
|
-
});
|
|
2354
|
+
}
|
|
2355
|
+
async function isServiceRunning() {
|
|
2356
|
+
switch (process.platform) {
|
|
2357
|
+
case "darwin":
|
|
2358
|
+
return darwinIsServiceRunning();
|
|
2359
|
+
case "linux":
|
|
2360
|
+
return linuxIsServiceRunning();
|
|
2361
|
+
case "win32":
|
|
2362
|
+
return win32IsServiceRunning();
|
|
2363
|
+
default:
|
|
2364
|
+
return false;
|
|
2297
2365
|
}
|
|
2298
|
-
return results;
|
|
2299
2366
|
}
|
|
2300
|
-
var
|
|
2301
|
-
|
|
2367
|
+
var IS_STAGING, PLIST_LABEL, SYSTEMD_SERVICE, TASK_NAME;
|
|
2368
|
+
var init_service_installer = __esm({
|
|
2369
|
+
"../listener/dist/service-installer.js"() {
|
|
2302
2370
|
"use strict";
|
|
2303
2371
|
init_config_dir();
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2372
|
+
init_process_manager();
|
|
2373
|
+
init_daemon_path();
|
|
2374
|
+
IS_STAGING = true ? true : false;
|
|
2375
|
+
PLIST_LABEL = IS_STAGING ? "com.repowise-staging.listener" : "com.repowise.listener";
|
|
2376
|
+
SYSTEMD_SERVICE = IS_STAGING ? "repowise-staging-listener" : "repowise-listener";
|
|
2377
|
+
TASK_NAME = IS_STAGING ? "RepoWise Staging Listener" : "RepoWise Listener";
|
|
2308
2378
|
}
|
|
2309
2379
|
});
|
|
2310
2380
|
|
|
@@ -2316,7 +2386,7 @@ __export(sidecar_cache_exports, {
|
|
|
2316
2386
|
sidecarCachePaths: () => sidecarCachePaths
|
|
2317
2387
|
});
|
|
2318
2388
|
import { mkdir as mkdir9, readFile as readFile8, readdir as readdir3, stat as stat2, unlink as unlink8, writeFile as writeFile9 } from "fs/promises";
|
|
2319
|
-
import { dirname as
|
|
2389
|
+
import { dirname as dirname9, join as join20 } from "path";
|
|
2320
2390
|
function repoIdSafe(repoId) {
|
|
2321
2391
|
return /^[A-Za-z0-9_.-]{1,128}$/.test(repoId) && !repoId.startsWith(".");
|
|
2322
2392
|
}
|
|
@@ -2330,8 +2400,8 @@ function sidecarCachePaths(repoId, commitSha) {
|
|
|
2330
2400
|
if (!commitShaSafe(commitSha)) {
|
|
2331
2401
|
throw new Error(`unsafe commitSha: ${commitSha}`);
|
|
2332
2402
|
}
|
|
2333
|
-
const repoDir =
|
|
2334
|
-
const fullPath =
|
|
2403
|
+
const repoDir = join20(getConfigDir(), "typed-resolution", repoId);
|
|
2404
|
+
const fullPath = join20(repoDir, `${commitSha}.jsonl`);
|
|
2335
2405
|
return { fullPath, repoDir };
|
|
2336
2406
|
}
|
|
2337
2407
|
async function persistMergedSidecar(repoId, commitSha, sidecar) {
|
|
@@ -2354,7 +2424,7 @@ async function sweepSidecarDir(repoDir) {
|
|
|
2354
2424
|
const withMtime = [];
|
|
2355
2425
|
for (const name of jsonlFiles) {
|
|
2356
2426
|
try {
|
|
2357
|
-
const s = await stat2(
|
|
2427
|
+
const s = await stat2(join20(repoDir, name));
|
|
2358
2428
|
withMtime.push({ name, mtimeMs: s.mtimeMs });
|
|
2359
2429
|
} catch {
|
|
2360
2430
|
}
|
|
@@ -2365,7 +2435,7 @@ async function sweepSidecarDir(repoDir) {
|
|
|
2365
2435
|
for (const { name, mtimeMs } of candidates) {
|
|
2366
2436
|
if (now - mtimeMs >= SWEEP_MAX_AGE_MS) {
|
|
2367
2437
|
try {
|
|
2368
|
-
await unlink8(
|
|
2438
|
+
await unlink8(join20(repoDir, name));
|
|
2369
2439
|
} catch {
|
|
2370
2440
|
}
|
|
2371
2441
|
}
|
|
@@ -2469,7 +2539,10 @@ async function uploadSidecar(req, fetchImpl = fetch) {
|
|
|
2469
2539
|
throw new Error("upload-url response missing `data.url`");
|
|
2470
2540
|
const putRes = await fetchImpl(presignedUrl, {
|
|
2471
2541
|
method: "PUT",
|
|
2472
|
-
headers: {
|
|
2542
|
+
headers: {
|
|
2543
|
+
"content-type": "application/json",
|
|
2544
|
+
"x-amz-server-side-encryption": "aws:kms"
|
|
2545
|
+
},
|
|
2473
2546
|
body
|
|
2474
2547
|
});
|
|
2475
2548
|
if (!putRes.ok) {
|
|
@@ -3060,13 +3133,13 @@ var init_telemetry = __esm({
|
|
|
3060
3133
|
// bin/repowise.ts
|
|
3061
3134
|
import { readFileSync as readFileSync3 } from "fs";
|
|
3062
3135
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
3063
|
-
import { dirname as
|
|
3136
|
+
import { dirname as dirname23, join as join61 } from "path";
|
|
3064
3137
|
import { Command } from "commander";
|
|
3065
3138
|
|
|
3066
3139
|
// ../listener/dist/main.js
|
|
3067
3140
|
init_config_dir();
|
|
3068
3141
|
import { readFile as readFile13, writeFile as writeFile15, mkdir as mkdir16, stat as fsStat } from "fs/promises";
|
|
3069
|
-
import { join as
|
|
3142
|
+
import { join as join41, dirname as dirname17 } from "path";
|
|
3070
3143
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
3071
3144
|
import lockfile4 from "proper-lockfile";
|
|
3072
3145
|
|
|
@@ -4877,7 +4950,7 @@ init_config_dir();
|
|
|
4877
4950
|
init_process_manager();
|
|
4878
4951
|
init_service_installer();
|
|
4879
4952
|
import { unlink as unlink7 } from "fs/promises";
|
|
4880
|
-
import { join as
|
|
4953
|
+
import { join as join19 } from "path";
|
|
4881
4954
|
async function getListenerStatus() {
|
|
4882
4955
|
const pid = await readPid();
|
|
4883
4956
|
if (pid !== null) {
|
|
@@ -4886,7 +4959,7 @@ async function getListenerStatus() {
|
|
|
4886
4959
|
return { running: true, method: "pid", pid, serviceInstalled: serviceInstalled2 };
|
|
4887
4960
|
}
|
|
4888
4961
|
try {
|
|
4889
|
-
await unlink7(
|
|
4962
|
+
await unlink7(join19(getConfigDir(), "listener.pid"));
|
|
4890
4963
|
} catch {
|
|
4891
4964
|
}
|
|
4892
4965
|
}
|
|
@@ -4931,15 +5004,15 @@ async function stopListener() {
|
|
|
4931
5004
|
|
|
4932
5005
|
// ../listener/dist/mcp/bootstrap.js
|
|
4933
5006
|
init_config_dir();
|
|
4934
|
-
import { join as
|
|
5007
|
+
import { join as join23 } from "path";
|
|
4935
5008
|
|
|
4936
5009
|
// ../listener/dist/lsp/workspace-session.js
|
|
4937
5010
|
import { pathToFileURL } from "url";
|
|
4938
5011
|
import { readFile as readFile7 } from "fs/promises";
|
|
4939
|
-
import { resolve as pathResolve2, sep as pathSep, dirname as
|
|
5012
|
+
import { resolve as pathResolve2, sep as pathSep, dirname as dirname8, delimiter as delimiter2 } from "path";
|
|
4940
5013
|
|
|
4941
5014
|
// ../listener/dist/lsp/lsp-client.js
|
|
4942
|
-
import { spawn as
|
|
5015
|
+
import { spawn as spawn7 } from "child_process";
|
|
4943
5016
|
import { EventEmitter, once } from "events";
|
|
4944
5017
|
import { Buffer as Buffer2 } from "buffer";
|
|
4945
5018
|
var LspTimeoutError = class extends Error {
|
|
@@ -4984,7 +5057,7 @@ var LspClient = class extends EventEmitter {
|
|
|
4984
5057
|
closed = false;
|
|
4985
5058
|
constructor(options) {
|
|
4986
5059
|
super();
|
|
4987
|
-
this.child = options.spawn ? options.spawn() :
|
|
5060
|
+
this.child = options.spawn ? options.spawn() : spawn7(options.command, [...options.args ?? []], {
|
|
4988
5061
|
cwd: options.cwd,
|
|
4989
5062
|
env: options.env ?? process.env,
|
|
4990
5063
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -5207,10 +5280,7 @@ var LspClient = class extends EventEmitter {
|
|
|
5207
5280
|
};
|
|
5208
5281
|
|
|
5209
5282
|
// ../listener/dist/lsp/workspace-session.js
|
|
5210
|
-
|
|
5211
|
-
init_native_installer();
|
|
5212
|
-
init_coursier_installer();
|
|
5213
|
-
init_toolchain_installer();
|
|
5283
|
+
init_daemon_path();
|
|
5214
5284
|
|
|
5215
5285
|
// ../listener/dist/lsp/bloop-lifecycle.js
|
|
5216
5286
|
import { spawn as spawn8 } from "child_process";
|
|
@@ -5270,13 +5340,7 @@ var WorkspaceManager = class {
|
|
|
5270
5340
|
this.rssSampler = opts.rssSampler ?? (() => process.memoryUsage().rss);
|
|
5271
5341
|
this.clientFactory = opts.clientFactory ?? ((config2, cwd) => {
|
|
5272
5342
|
const env = { ...process.env };
|
|
5273
|
-
|
|
5274
|
-
getLspBinDir(),
|
|
5275
|
-
getNativeLspBinDir(),
|
|
5276
|
-
getCoursierBinDir(),
|
|
5277
|
-
...getToolchainBinDirs()
|
|
5278
|
-
];
|
|
5279
|
-
env.PATH = buildLspSpawnPath(env.PATH, dirname7(process.execPath), lspBinDirs);
|
|
5343
|
+
env.PATH = buildLspSpawnPath(env.PATH, dirname8(process.execPath), managedBinDirs());
|
|
5280
5344
|
const client = new LspClient({
|
|
5281
5345
|
command: config2.command,
|
|
5282
5346
|
args: config2.args,
|
|
@@ -5645,14 +5709,19 @@ var PathEscapeError = class extends Error {
|
|
|
5645
5709
|
}
|
|
5646
5710
|
};
|
|
5647
5711
|
function buildLspSpawnPath(basePath, nodeDir, lspBinDirs) {
|
|
5648
|
-
const
|
|
5649
|
-
|
|
5712
|
+
const parts = [
|
|
5713
|
+
nodeDir,
|
|
5714
|
+
...basePath ? basePath.split(delimiter2) : [],
|
|
5715
|
+
...commonBinDirs(),
|
|
5716
|
+
...lspBinDirs
|
|
5717
|
+
];
|
|
5718
|
+
return [...new Set(parts.filter((d) => d.length > 0))].join(delimiter2);
|
|
5650
5719
|
}
|
|
5651
5720
|
|
|
5652
5721
|
// ../listener/dist/mcp/graph-cache.js
|
|
5653
5722
|
init_config_dir();
|
|
5654
5723
|
import { readFile as readFile9, stat as stat3 } from "fs/promises";
|
|
5655
|
-
import { join as
|
|
5724
|
+
import { join as join21 } from "path";
|
|
5656
5725
|
var EVICT_DEBOUNCE_MS = 6e4;
|
|
5657
5726
|
function assertSafeRepoId(repoId) {
|
|
5658
5727
|
if (!repoId || typeof repoId !== "string") {
|
|
@@ -5667,7 +5736,7 @@ function assertSafeRepoId(repoId) {
|
|
|
5667
5736
|
}
|
|
5668
5737
|
function createGraphCache(options = {}) {
|
|
5669
5738
|
const evictMs = options.evictDebounceMs ?? EVICT_DEBOUNCE_MS;
|
|
5670
|
-
const resolvePath = options.resolveGraphPath ?? ((repoId) =>
|
|
5739
|
+
const resolvePath = options.resolveGraphPath ?? ((repoId) => join21(defaultRepoWiseHome(), "graphs", `${repoId}.json`));
|
|
5671
5740
|
const entries = /* @__PURE__ */ new Map();
|
|
5672
5741
|
const inFlight = /* @__PURE__ */ new Map();
|
|
5673
5742
|
async function loadFromDisk(repoId) {
|
|
@@ -5761,7 +5830,7 @@ function defaultRepoWiseHome() {
|
|
|
5761
5830
|
|
|
5762
5831
|
// ../listener/dist/mcp/graph-downloader.js
|
|
5763
5832
|
import { mkdir as mkdir10, rename as rename3, writeFile as writeFile10 } from "fs/promises";
|
|
5764
|
-
import { dirname as
|
|
5833
|
+
import { dirname as dirname10 } from "path";
|
|
5765
5834
|
function createGraphDownloader(options) {
|
|
5766
5835
|
const fetchFn = options.fetchImpl ?? fetch;
|
|
5767
5836
|
let lastSha = null;
|
|
@@ -5812,7 +5881,7 @@ function createGraphDownloader(options) {
|
|
|
5812
5881
|
message: `graph parse: ${err instanceof Error ? err.message : String(err)}`
|
|
5813
5882
|
};
|
|
5814
5883
|
}
|
|
5815
|
-
await mkdir10(
|
|
5884
|
+
await mkdir10(dirname10(options.targetPath), { recursive: true });
|
|
5816
5885
|
const tmpPath = `${options.targetPath}.${Date.now()}.tmp`;
|
|
5817
5886
|
await writeFile10(tmpPath, body, { encoding: "utf-8", mode: 384 });
|
|
5818
5887
|
await rename3(tmpPath, options.targetPath);
|
|
@@ -5838,7 +5907,7 @@ function createGraphDownloader(options) {
|
|
|
5838
5907
|
// ../listener/dist/mcp/log-encryption.js
|
|
5839
5908
|
import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
|
|
5840
5909
|
import { mkdir as mkdir11, readFile as readFile10, stat as stat4, writeFile as writeFile11 } from "fs/promises";
|
|
5841
|
-
import { dirname as
|
|
5910
|
+
import { dirname as dirname11 } from "path";
|
|
5842
5911
|
var KEY_BYTES = 32;
|
|
5843
5912
|
var IV_BYTES = 12;
|
|
5844
5913
|
var TAG_BYTES = 16;
|
|
@@ -5885,7 +5954,7 @@ function createFileKeyStore(keyPath) {
|
|
|
5885
5954
|
return parsed;
|
|
5886
5955
|
}
|
|
5887
5956
|
const fresh = randomBytes(KEY_BYTES);
|
|
5888
|
-
await mkdir11(
|
|
5957
|
+
await mkdir11(dirname11(keyPath), { recursive: true });
|
|
5889
5958
|
const tmp = `${keyPath}.tmp`;
|
|
5890
5959
|
await writeFile11(tmp, fresh.toString("base64") + "\n", {
|
|
5891
5960
|
encoding: "utf-8",
|
|
@@ -5927,7 +5996,7 @@ function decryptLine(encoded, key) {
|
|
|
5927
5996
|
|
|
5928
5997
|
// ../listener/dist/mcp/mcp-logger.js
|
|
5929
5998
|
import { appendFile, mkdir as mkdir12, stat as stat5, unlink as unlink9, writeFile as writeFile12, readFile as readFile11 } from "fs/promises";
|
|
5930
|
-
import { dirname as
|
|
5999
|
+
import { dirname as dirname12 } from "path";
|
|
5931
6000
|
var DEFAULT_MAX_BYTES = 100 * 1024 * 1024;
|
|
5932
6001
|
function createMcpLogger(options) {
|
|
5933
6002
|
const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
@@ -5950,7 +6019,7 @@ function createMcpLogger(options) {
|
|
|
5950
6019
|
await migrateLegacyPlaintext();
|
|
5951
6020
|
const key = await options.keyStore.getKey();
|
|
5952
6021
|
const encoded = encryptLine(JSON.stringify(entry), key) + "\n";
|
|
5953
|
-
await mkdir12(
|
|
6022
|
+
await mkdir12(dirname12(options.filePath), { recursive: true });
|
|
5954
6023
|
await appendFile(options.filePath, encoded, { encoding: "utf-8", mode: 384 });
|
|
5955
6024
|
try {
|
|
5956
6025
|
const s = await stat5(options.filePath);
|
|
@@ -5984,7 +6053,7 @@ async function rotate(path, size) {
|
|
|
5984
6053
|
// ../listener/dist/mcp/mcp-log-uploader.js
|
|
5985
6054
|
import { randomUUID } from "crypto";
|
|
5986
6055
|
import { readFile as readFile12, writeFile as writeFile13, mkdir as mkdir13 } from "fs/promises";
|
|
5987
|
-
import { dirname as
|
|
6056
|
+
import { dirname as dirname13 } from "path";
|
|
5988
6057
|
var DEFAULT_MAX_ENTRIES = 5e3;
|
|
5989
6058
|
var DEFAULT_MAX_BYTES2 = 1 * 1024 * 1024;
|
|
5990
6059
|
function createMcpLogUploader(options) {
|
|
@@ -6134,7 +6203,7 @@ async function readPendingBatch(watermarkPath) {
|
|
|
6134
6203
|
}
|
|
6135
6204
|
}
|
|
6136
6205
|
async function writePendingBatch(watermarkPath, batchId) {
|
|
6137
|
-
await mkdir13(
|
|
6206
|
+
await mkdir13(dirname13(watermarkPath), { recursive: true });
|
|
6138
6207
|
await writeFile13(pendingPath(watermarkPath), batchId, { encoding: "utf-8", mode: 384 });
|
|
6139
6208
|
}
|
|
6140
6209
|
async function clearPendingBatch(watermarkPath) {
|
|
@@ -6156,7 +6225,7 @@ async function readWatermark(path) {
|
|
|
6156
6225
|
}
|
|
6157
6226
|
}
|
|
6158
6227
|
async function writeWatermark(path, offset) {
|
|
6159
|
-
await mkdir13(
|
|
6228
|
+
await mkdir13(dirname13(path), { recursive: true });
|
|
6160
6229
|
const tmp = `${path}.tmp`;
|
|
6161
6230
|
await writeFile13(tmp, offset.toString() + "\n", { encoding: "utf-8", mode: 384 });
|
|
6162
6231
|
const { rename: rename5 } = await import("fs/promises");
|
|
@@ -6176,7 +6245,7 @@ async function isLocallyConsented(flagPath2) {
|
|
|
6176
6245
|
init_config_dir();
|
|
6177
6246
|
import { createServer } from "http";
|
|
6178
6247
|
import { mkdir as mkdir14, writeFile as writeFile14 } from "fs/promises";
|
|
6179
|
-
import { dirname as
|
|
6248
|
+
import { dirname as dirname14, join as join22 } from "path";
|
|
6180
6249
|
import { createHash as createHash4, randomUUID as randomUUID2 } from "crypto";
|
|
6181
6250
|
|
|
6182
6251
|
// ../listener/dist/mcp/sanitize.js
|
|
@@ -7694,7 +7763,7 @@ async function readJson(req) {
|
|
|
7694
7763
|
}
|
|
7695
7764
|
}
|
|
7696
7765
|
async function writeEndpointFile(path, endpoint, secret) {
|
|
7697
|
-
await mkdir14(
|
|
7766
|
+
await mkdir14(dirname14(path), { recursive: true });
|
|
7698
7767
|
await writeFile14(path, formatEndpointFile({ endpoint, secret }), {
|
|
7699
7768
|
encoding: "utf-8",
|
|
7700
7769
|
mode: 384
|
|
@@ -7708,7 +7777,7 @@ function extractBearer(header) {
|
|
|
7708
7777
|
return m ? m[1] : null;
|
|
7709
7778
|
}
|
|
7710
7779
|
function defaultEndpointFile() {
|
|
7711
|
-
return
|
|
7780
|
+
return join22(getConfigDir(), "listener.endpoint");
|
|
7712
7781
|
}
|
|
7713
7782
|
|
|
7714
7783
|
// ../listener/dist/mcp/bootstrap.js
|
|
@@ -7716,7 +7785,7 @@ async function startMcp(options) {
|
|
|
7716
7785
|
const disabled = process.env.REPOWISE_MCP_DISABLED === "true";
|
|
7717
7786
|
const graphsDir = options.graphsDir ?? defaultGraphsDir();
|
|
7718
7787
|
const graphCache = createGraphCache({
|
|
7719
|
-
resolveGraphPath: (repoId) =>
|
|
7788
|
+
resolveGraphPath: (repoId) => join23(graphsDir, `${repoId}.json`)
|
|
7720
7789
|
});
|
|
7721
7790
|
if (disabled) {
|
|
7722
7791
|
return {
|
|
@@ -7739,12 +7808,12 @@ async function startMcp(options) {
|
|
|
7739
7808
|
}
|
|
7740
7809
|
const firstRepoLocal = options.repos.find((r) => r.localPath)?.localPath;
|
|
7741
7810
|
const mcpHome = defaultMcpHome();
|
|
7742
|
-
const logFilePath =
|
|
7743
|
-
const keyStore = createFileKeyStore(
|
|
7811
|
+
const logFilePath = join23(mcpHome, "mcp-log.jsonl.enc");
|
|
7812
|
+
const keyStore = createFileKeyStore(join23(mcpHome, "mcp-log.key"));
|
|
7744
7813
|
const mcpLogger = createMcpLogger({ filePath: logFilePath, keyStore });
|
|
7745
|
-
const flagFilePath =
|
|
7746
|
-
const watermarkFilePath =
|
|
7747
|
-
const consentSentMarkerPath =
|
|
7814
|
+
const flagFilePath = join23(mcpHome, "mcp-log.flag");
|
|
7815
|
+
const watermarkFilePath = join23(mcpHome, "mcp-log.watermark");
|
|
7816
|
+
const consentSentMarkerPath = join23(mcpHome, "mcp-log.consent-sent");
|
|
7748
7817
|
let uploader = null;
|
|
7749
7818
|
let lastConsentState = false;
|
|
7750
7819
|
let serverConsentEnsuredThisProcess = false;
|
|
@@ -7791,7 +7860,7 @@ async function startMcp(options) {
|
|
|
7791
7860
|
apiBaseUrl: repo.apiUrl,
|
|
7792
7861
|
getAuthToken: options.getAuthToken,
|
|
7793
7862
|
repoId: repo.repoId,
|
|
7794
|
-
targetPath:
|
|
7863
|
+
targetPath: join23(graphsDir, `${repo.repoId}.json`),
|
|
7795
7864
|
graphCache,
|
|
7796
7865
|
...options.fetchImpl ? { fetchImpl: options.fetchImpl } : {}
|
|
7797
7866
|
}));
|
|
@@ -7876,7 +7945,7 @@ async function startMcp(options) {
|
|
|
7876
7945
|
apiBaseUrl: repo.apiUrl,
|
|
7877
7946
|
getAuthToken: options.getAuthToken,
|
|
7878
7947
|
repoId: repo.repoId,
|
|
7879
|
-
targetPath:
|
|
7948
|
+
targetPath: join23(graphsDir, `${repo.repoId}.json`),
|
|
7880
7949
|
graphCache,
|
|
7881
7950
|
...options.fetchImpl ? { fetchImpl: options.fetchImpl } : {}
|
|
7882
7951
|
}));
|
|
@@ -7897,7 +7966,7 @@ async function startMcp(options) {
|
|
|
7897
7966
|
};
|
|
7898
7967
|
}
|
|
7899
7968
|
function defaultGraphsDir() {
|
|
7900
|
-
return
|
|
7969
|
+
return join23(getConfigDir(), "graphs");
|
|
7901
7970
|
}
|
|
7902
7971
|
function defaultMcpHome() {
|
|
7903
7972
|
return getConfigDir();
|
|
@@ -7950,16 +8019,16 @@ async function ensureServerConsent(opts) {
|
|
|
7950
8019
|
}
|
|
7951
8020
|
|
|
7952
8021
|
// ../listener/dist/mcp/auto-config/index.js
|
|
7953
|
-
import { homedir as
|
|
8022
|
+
import { homedir as homedir6 } from "os";
|
|
7954
8023
|
import { basename as basename3 } from "path";
|
|
7955
8024
|
|
|
7956
8025
|
// ../listener/dist/mcp/auto-config/writers/claude-code.js
|
|
7957
8026
|
import { promises as fs6 } from "fs";
|
|
7958
|
-
import { join as
|
|
8027
|
+
import { join as join24 } from "path";
|
|
7959
8028
|
|
|
7960
8029
|
// ../listener/dist/mcp/auto-config/markers.js
|
|
7961
8030
|
import { promises as fs5 } from "fs";
|
|
7962
|
-
import { dirname as
|
|
8031
|
+
import { dirname as dirname15 } from "path";
|
|
7963
8032
|
async function writeMergedConfig(params) {
|
|
7964
8033
|
const current = await readConfig(params.path);
|
|
7965
8034
|
const servers = { ...current.mcpServers ?? {} };
|
|
@@ -7969,7 +8038,7 @@ async function writeMergedConfig(params) {
|
|
|
7969
8038
|
const existing = safeExistingContent(params.path, current);
|
|
7970
8039
|
if (existing === canonical)
|
|
7971
8040
|
return "unchanged";
|
|
7972
|
-
await fs5.mkdir(
|
|
8041
|
+
await fs5.mkdir(dirname15(params.path), { recursive: true });
|
|
7973
8042
|
await fs5.writeFile(params.path, canonical, "utf-8");
|
|
7974
8043
|
return "written";
|
|
7975
8044
|
}
|
|
@@ -8035,10 +8104,10 @@ function safeExistingContent(path, current) {
|
|
|
8035
8104
|
var claudeCodeWriter = {
|
|
8036
8105
|
tool: "claude-code",
|
|
8037
8106
|
async detect(repoRoot, home) {
|
|
8038
|
-
return await fileExists2(
|
|
8107
|
+
return await fileExists2(join24(repoRoot, "CLAUDE.md")) || await hasClaudeBinary(home);
|
|
8039
8108
|
},
|
|
8040
8109
|
async write(ctx) {
|
|
8041
|
-
const path =
|
|
8110
|
+
const path = join24(ctx.repoRoot, ".mcp.json");
|
|
8042
8111
|
await removeFromConfig(path, `repowise-${ctx.repoId}`);
|
|
8043
8112
|
const status2 = await writeMergedConfig({
|
|
8044
8113
|
path,
|
|
@@ -8048,7 +8117,7 @@ var claudeCodeWriter = {
|
|
|
8048
8117
|
return { status: status2, path };
|
|
8049
8118
|
},
|
|
8050
8119
|
async remove(ctx) {
|
|
8051
|
-
const path =
|
|
8120
|
+
const path = join24(ctx.repoRoot, ".mcp.json");
|
|
8052
8121
|
await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
|
|
8053
8122
|
await removeFromConfig(path, `repowise-${ctx.repoId}`);
|
|
8054
8123
|
}
|
|
@@ -8062,20 +8131,20 @@ async function fileExists2(path) {
|
|
|
8062
8131
|
}
|
|
8063
8132
|
}
|
|
8064
8133
|
async function hasClaudeBinary(home) {
|
|
8065
|
-
return fileExists2(
|
|
8134
|
+
return fileExists2(join24(home, ".claude", "claude.json"));
|
|
8066
8135
|
}
|
|
8067
8136
|
|
|
8068
8137
|
// ../listener/dist/mcp/auto-config/writers/claude-code-hooks.js
|
|
8069
8138
|
import { promises as fs7 } from "fs";
|
|
8070
|
-
import { join as
|
|
8139
|
+
import { join as join25 } from "path";
|
|
8071
8140
|
var claudeCodeHooksWriter = {
|
|
8072
8141
|
tool: "claude-code-hooks",
|
|
8073
8142
|
async detect(repoRoot, home) {
|
|
8074
|
-
return await fileExists3(
|
|
8143
|
+
return await fileExists3(join25(repoRoot, "CLAUDE.md")) || await fileExists3(join25(home, ".claude.json"));
|
|
8075
8144
|
},
|
|
8076
8145
|
async write(ctx) {
|
|
8077
8146
|
const result = await writeClaudeHooksToRepo(ctx.repoRoot, ctx.contextFolder ?? "repowise-context", ctx.variant);
|
|
8078
|
-
return { status: result.status, path:
|
|
8147
|
+
return { status: result.status, path: join25(ctx.repoRoot, result.relPath) };
|
|
8079
8148
|
},
|
|
8080
8149
|
async remove(ctx) {
|
|
8081
8150
|
await removeClaudeHooksFromRepo(ctx.repoRoot);
|
|
@@ -8092,14 +8161,14 @@ async function fileExists3(path) {
|
|
|
8092
8161
|
|
|
8093
8162
|
// ../listener/dist/mcp/auto-config/writers/cline.js
|
|
8094
8163
|
import { promises as fs8 } from "fs";
|
|
8095
|
-
import { join as
|
|
8164
|
+
import { join as join26 } from "path";
|
|
8096
8165
|
var clineWriter = {
|
|
8097
8166
|
tool: "cline",
|
|
8098
8167
|
async detect(repoRoot, home) {
|
|
8099
|
-
return await fileExists4(
|
|
8168
|
+
return await fileExists4(join26(repoRoot, ".clinerules")) || await fileExists4(join26(home, ".cline"));
|
|
8100
8169
|
},
|
|
8101
8170
|
async write(ctx) {
|
|
8102
|
-
const path =
|
|
8171
|
+
const path = join26(ctx.home, ".cline", "mcp.json");
|
|
8103
8172
|
await removeFromConfig(path, `repowise-${ctx.repoId}`);
|
|
8104
8173
|
const status2 = await writeMergedConfig({
|
|
8105
8174
|
path,
|
|
@@ -8109,7 +8178,7 @@ var clineWriter = {
|
|
|
8109
8178
|
return { status: status2, path };
|
|
8110
8179
|
},
|
|
8111
8180
|
async remove(ctx) {
|
|
8112
|
-
const path =
|
|
8181
|
+
const path = join26(ctx.home, ".cline", "mcp.json");
|
|
8113
8182
|
await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
|
|
8114
8183
|
await removeFromConfig(path, `repowise-${ctx.repoId}`);
|
|
8115
8184
|
}
|
|
@@ -8125,13 +8194,13 @@ async function fileExists4(path) {
|
|
|
8125
8194
|
|
|
8126
8195
|
// ../listener/dist/mcp/auto-config/writers/cline-hooks.js
|
|
8127
8196
|
import { promises as fs9 } from "fs";
|
|
8128
|
-
import { join as
|
|
8197
|
+
import { join as join27 } from "path";
|
|
8129
8198
|
var SCRIPT_REL = ".clinerules/hooks/UserPromptSubmit";
|
|
8130
8199
|
var OWNERSHIP_MARKER = "managed by RepoWise";
|
|
8131
8200
|
var clineHooksWriter = {
|
|
8132
8201
|
tool: "cline-hooks",
|
|
8133
8202
|
async detect(repoRoot) {
|
|
8134
|
-
return fileExists5(
|
|
8203
|
+
return fileExists5(join27(repoRoot, ".clinerules"));
|
|
8135
8204
|
},
|
|
8136
8205
|
async write(ctx) {
|
|
8137
8206
|
if (await isGitTracked(ctx.repoRoot, SCRIPT_REL)) {
|
|
@@ -8141,7 +8210,7 @@ var clineHooksWriter = {
|
|
|
8141
8210
|
reason: `${SCRIPT_REL} is git-tracked; update it manually to opt in`
|
|
8142
8211
|
};
|
|
8143
8212
|
}
|
|
8144
|
-
const path =
|
|
8213
|
+
const path = join27(ctx.repoRoot, SCRIPT_REL);
|
|
8145
8214
|
const next = buildClineHookScript({
|
|
8146
8215
|
repoName: ctx.repoName,
|
|
8147
8216
|
contextFolder: ctx.contextFolder ?? "repowise-context",
|
|
@@ -8158,14 +8227,14 @@ var clineHooksWriter = {
|
|
|
8158
8227
|
await ensureExecutable(path);
|
|
8159
8228
|
return { status: "unchanged", path };
|
|
8160
8229
|
}
|
|
8161
|
-
await fs9.mkdir(
|
|
8230
|
+
await fs9.mkdir(join27(ctx.repoRoot, ".clinerules", "hooks"), { recursive: true });
|
|
8162
8231
|
await fs9.writeFile(path, next, { encoding: "utf-8", mode: 493 });
|
|
8163
8232
|
await ensureExecutable(path);
|
|
8164
8233
|
await applyGitignoreChanges(ctx.repoRoot, { add: [SCRIPT_REL] });
|
|
8165
8234
|
return { status: "written", path };
|
|
8166
8235
|
},
|
|
8167
8236
|
async remove(ctx) {
|
|
8168
|
-
const path =
|
|
8237
|
+
const path = join27(ctx.repoRoot, SCRIPT_REL);
|
|
8169
8238
|
const existing = await readOrNull(path);
|
|
8170
8239
|
if (existing !== null && existing.includes(OWNERSHIP_MARKER)) {
|
|
8171
8240
|
await fs9.unlink(path);
|
|
@@ -8200,14 +8269,14 @@ async function readOrNull(path) {
|
|
|
8200
8269
|
|
|
8201
8270
|
// ../listener/dist/mcp/auto-config/writers/codex.js
|
|
8202
8271
|
import { promises as fs10 } from "fs";
|
|
8203
|
-
import { join as
|
|
8272
|
+
import { join as join28 } from "path";
|
|
8204
8273
|
var codexWriter = {
|
|
8205
8274
|
tool: "codex",
|
|
8206
8275
|
async detect(_repoRoot, home) {
|
|
8207
|
-
return fileExists6(
|
|
8276
|
+
return fileExists6(join28(home, ".codex"));
|
|
8208
8277
|
},
|
|
8209
8278
|
async write(ctx) {
|
|
8210
|
-
const path =
|
|
8279
|
+
const path = join28(ctx.home, ".codex", "mcp.json");
|
|
8211
8280
|
await removeFromConfig(path, `repowise-${ctx.repoId}`);
|
|
8212
8281
|
const status2 = await writeMergedConfig({
|
|
8213
8282
|
path,
|
|
@@ -8217,7 +8286,7 @@ var codexWriter = {
|
|
|
8217
8286
|
return { status: status2, path };
|
|
8218
8287
|
},
|
|
8219
8288
|
async remove(ctx) {
|
|
8220
|
-
const path =
|
|
8289
|
+
const path = join28(ctx.home, ".codex", "mcp.json");
|
|
8221
8290
|
await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
|
|
8222
8291
|
await removeFromConfig(path, `repowise-${ctx.repoId}`);
|
|
8223
8292
|
}
|
|
@@ -8233,15 +8302,15 @@ async function fileExists6(path) {
|
|
|
8233
8302
|
|
|
8234
8303
|
// ../listener/dist/mcp/auto-config/writers/codex-hooks.js
|
|
8235
8304
|
import { promises as fs11 } from "fs";
|
|
8236
|
-
import { join as
|
|
8305
|
+
import { join as join29 } from "path";
|
|
8237
8306
|
var HOOKS_REL = ".codex/hooks.json";
|
|
8238
8307
|
var codexHooksWriter = {
|
|
8239
8308
|
tool: "codex-hooks",
|
|
8240
8309
|
async detect(repoRoot, home) {
|
|
8241
|
-
return await fileExists7(
|
|
8310
|
+
return await fileExists7(join29(home, ".codex")) || await fileExists7(join29(repoRoot, ".codex"));
|
|
8242
8311
|
},
|
|
8243
8312
|
async write(ctx) {
|
|
8244
|
-
const repoSignal = await fileExists7(
|
|
8313
|
+
const repoSignal = await fileExists7(join29(ctx.repoRoot, ".codex"));
|
|
8245
8314
|
const selected = ctx.selectedTools?.includes("codex") ?? false;
|
|
8246
8315
|
if (!repoSignal && !selected) {
|
|
8247
8316
|
return { status: "skipped", reason: "no repo-scoped Codex signal and not selected" };
|
|
@@ -8253,7 +8322,7 @@ var codexHooksWriter = {
|
|
|
8253
8322
|
reason: `${HOOKS_REL} is git-tracked; add the RepoWise UserPromptSubmit hook manually to opt in`
|
|
8254
8323
|
};
|
|
8255
8324
|
}
|
|
8256
|
-
const path =
|
|
8325
|
+
const path = join29(ctx.repoRoot, HOOKS_REL);
|
|
8257
8326
|
const existing = await readOrNull2(path);
|
|
8258
8327
|
const merged = mergeCodexHookSettings(existing, {
|
|
8259
8328
|
repoName: ctx.repoName,
|
|
@@ -8263,13 +8332,13 @@ var codexHooksWriter = {
|
|
|
8263
8332
|
if (merged === existing) {
|
|
8264
8333
|
return { status: "unchanged", path };
|
|
8265
8334
|
}
|
|
8266
|
-
await fs11.mkdir(
|
|
8335
|
+
await fs11.mkdir(join29(ctx.repoRoot, ".codex"), { recursive: true });
|
|
8267
8336
|
await fs11.writeFile(path, merged, "utf-8");
|
|
8268
8337
|
await applyGitignoreChanges(ctx.repoRoot, { add: [HOOKS_REL] });
|
|
8269
8338
|
return { status: "written", path };
|
|
8270
8339
|
},
|
|
8271
8340
|
async remove(ctx) {
|
|
8272
|
-
const path =
|
|
8341
|
+
const path = join29(ctx.repoRoot, HOOKS_REL);
|
|
8273
8342
|
const existing = await readOrNull2(path);
|
|
8274
8343
|
if (existing !== null) {
|
|
8275
8344
|
const next = removeCodexHookSettings(existing);
|
|
@@ -8300,14 +8369,14 @@ async function readOrNull2(path) {
|
|
|
8300
8369
|
|
|
8301
8370
|
// ../listener/dist/mcp/auto-config/writers/copilot.js
|
|
8302
8371
|
import { promises as fs12 } from "fs";
|
|
8303
|
-
import { join as
|
|
8372
|
+
import { join as join30 } from "path";
|
|
8304
8373
|
var copilotWriter = {
|
|
8305
8374
|
tool: "copilot",
|
|
8306
8375
|
async detect(repoRoot) {
|
|
8307
|
-
return fileExists8(
|
|
8376
|
+
return fileExists8(join30(repoRoot, ".vscode"));
|
|
8308
8377
|
},
|
|
8309
8378
|
async write(ctx) {
|
|
8310
|
-
const path =
|
|
8379
|
+
const path = join30(ctx.repoRoot, ".vscode", "mcp.json");
|
|
8311
8380
|
await removeFromConfig(path, `repowise-${ctx.repoId}`);
|
|
8312
8381
|
const status2 = await writeMergedConfig({
|
|
8313
8382
|
path,
|
|
@@ -8317,7 +8386,7 @@ var copilotWriter = {
|
|
|
8317
8386
|
return { status: status2, path };
|
|
8318
8387
|
},
|
|
8319
8388
|
async remove(ctx) {
|
|
8320
|
-
const path =
|
|
8389
|
+
const path = join30(ctx.repoRoot, ".vscode", "mcp.json");
|
|
8321
8390
|
await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
|
|
8322
8391
|
await removeFromConfig(path, `repowise-${ctx.repoId}`);
|
|
8323
8392
|
}
|
|
@@ -8333,15 +8402,15 @@ async function fileExists8(path) {
|
|
|
8333
8402
|
|
|
8334
8403
|
// ../listener/dist/mcp/auto-config/writers/copilot-hooks.js
|
|
8335
8404
|
import { promises as fs13 } from "fs";
|
|
8336
|
-
import { join as
|
|
8405
|
+
import { join as join31 } from "path";
|
|
8337
8406
|
var HOOKS_REL2 = ".github/hooks/repowise.json";
|
|
8338
8407
|
var copilotHooksWriter = {
|
|
8339
8408
|
tool: "copilot-hooks",
|
|
8340
8409
|
async detect(repoRoot) {
|
|
8341
|
-
return fileExists9(
|
|
8410
|
+
return fileExists9(join31(repoRoot, ".github", "copilot-instructions.md"));
|
|
8342
8411
|
},
|
|
8343
8412
|
async write(ctx) {
|
|
8344
|
-
const path =
|
|
8413
|
+
const path = join31(ctx.repoRoot, HOOKS_REL2);
|
|
8345
8414
|
const next = buildCopilotHooksFile({
|
|
8346
8415
|
repoName: ctx.repoName,
|
|
8347
8416
|
contextFolder: ctx.contextFolder ?? "repowise-context",
|
|
@@ -8361,14 +8430,14 @@ var copilotHooksWriter = {
|
|
|
8361
8430
|
if (existing === next) {
|
|
8362
8431
|
return { status: "unchanged", path };
|
|
8363
8432
|
}
|
|
8364
|
-
await fs13.mkdir(
|
|
8433
|
+
await fs13.mkdir(join31(ctx.repoRoot, ".github", "hooks"), { recursive: true });
|
|
8365
8434
|
await fs13.writeFile(path, next, "utf-8");
|
|
8366
8435
|
const userSelected = ctx.selectedTools?.includes("copilot") ?? false;
|
|
8367
8436
|
await applyGitignoreChanges(ctx.repoRoot, userSelected ? { remove: [HOOKS_REL2] } : { add: [HOOKS_REL2] });
|
|
8368
8437
|
return { status: "written", path };
|
|
8369
8438
|
},
|
|
8370
8439
|
async remove(ctx) {
|
|
8371
|
-
const path =
|
|
8440
|
+
const path = join31(ctx.repoRoot, HOOKS_REL2);
|
|
8372
8441
|
if (!await isGitTracked(ctx.repoRoot, HOOKS_REL2)) {
|
|
8373
8442
|
await fs13.unlink(path).catch(() => {
|
|
8374
8443
|
});
|
|
@@ -8394,14 +8463,14 @@ async function readOrNull3(path) {
|
|
|
8394
8463
|
|
|
8395
8464
|
// ../listener/dist/mcp/auto-config/writers/cursor.js
|
|
8396
8465
|
import { promises as fs14 } from "fs";
|
|
8397
|
-
import { join as
|
|
8466
|
+
import { join as join32 } from "path";
|
|
8398
8467
|
var cursorWriter = {
|
|
8399
8468
|
tool: "cursor",
|
|
8400
8469
|
async detect(repoRoot) {
|
|
8401
|
-
return await fileExists10(
|
|
8470
|
+
return await fileExists10(join32(repoRoot, ".cursor")) || await fileExists10(join32(repoRoot, ".cursorrules"));
|
|
8402
8471
|
},
|
|
8403
8472
|
async write(ctx) {
|
|
8404
|
-
const path =
|
|
8473
|
+
const path = join32(ctx.repoRoot, ".cursor", "mcp.json");
|
|
8405
8474
|
await removeFromConfig(path, `repowise-${ctx.repoId}`);
|
|
8406
8475
|
const status2 = await writeMergedConfig({
|
|
8407
8476
|
path,
|
|
@@ -8411,7 +8480,7 @@ var cursorWriter = {
|
|
|
8411
8480
|
return { status: status2, path };
|
|
8412
8481
|
},
|
|
8413
8482
|
async remove(ctx) {
|
|
8414
|
-
const path =
|
|
8483
|
+
const path = join32(ctx.repoRoot, ".cursor", "mcp.json");
|
|
8415
8484
|
await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
|
|
8416
8485
|
await removeFromConfig(path, `repowise-${ctx.repoId}`);
|
|
8417
8486
|
}
|
|
@@ -8427,14 +8496,14 @@ async function fileExists10(path) {
|
|
|
8427
8496
|
|
|
8428
8497
|
// ../listener/dist/mcp/auto-config/writers/gemini-cli.js
|
|
8429
8498
|
import { promises as fs15 } from "fs";
|
|
8430
|
-
import { join as
|
|
8499
|
+
import { join as join33 } from "path";
|
|
8431
8500
|
var geminiCliWriter = {
|
|
8432
8501
|
tool: "gemini-cli",
|
|
8433
8502
|
async detect(_repoRoot, home) {
|
|
8434
|
-
return fileExists11(
|
|
8503
|
+
return fileExists11(join33(home, ".gemini"));
|
|
8435
8504
|
},
|
|
8436
8505
|
async write(ctx) {
|
|
8437
|
-
const path =
|
|
8506
|
+
const path = join33(ctx.home, ".gemini", "settings.json");
|
|
8438
8507
|
await removeFromConfig(path, `repowise-${ctx.repoId}`);
|
|
8439
8508
|
const status2 = await writeMergedConfig({
|
|
8440
8509
|
path,
|
|
@@ -8444,7 +8513,7 @@ var geminiCliWriter = {
|
|
|
8444
8513
|
return { status: status2, path };
|
|
8445
8514
|
},
|
|
8446
8515
|
async remove(ctx) {
|
|
8447
|
-
const path =
|
|
8516
|
+
const path = join33(ctx.home, ".gemini", "settings.json");
|
|
8448
8517
|
await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
|
|
8449
8518
|
await removeFromConfig(path, `repowise-${ctx.repoId}`);
|
|
8450
8519
|
}
|
|
@@ -8460,15 +8529,15 @@ async function fileExists11(path) {
|
|
|
8460
8529
|
|
|
8461
8530
|
// ../listener/dist/mcp/auto-config/writers/gemini-hooks.js
|
|
8462
8531
|
import { promises as fs16 } from "fs";
|
|
8463
|
-
import { join as
|
|
8532
|
+
import { join as join34 } from "path";
|
|
8464
8533
|
var SETTINGS_REL = ".gemini/settings.json";
|
|
8465
8534
|
var geminiHooksWriter = {
|
|
8466
8535
|
tool: "gemini-hooks",
|
|
8467
8536
|
async detect(repoRoot, home) {
|
|
8468
|
-
return await fileExists12(
|
|
8537
|
+
return await fileExists12(join34(home, ".gemini")) || await fileExists12(join34(repoRoot, ".gemini"));
|
|
8469
8538
|
},
|
|
8470
8539
|
async write(ctx) {
|
|
8471
|
-
const repoSignal = await fileExists12(
|
|
8540
|
+
const repoSignal = await fileExists12(join34(ctx.repoRoot, ".gemini")) || await fileExists12(join34(ctx.repoRoot, "GEMINI.md"));
|
|
8472
8541
|
const selected = ctx.selectedTools?.includes("gemini") ?? false;
|
|
8473
8542
|
if (!repoSignal && !selected) {
|
|
8474
8543
|
return { status: "skipped", reason: "no repo-scoped Gemini signal and not selected" };
|
|
@@ -8480,7 +8549,7 @@ var geminiHooksWriter = {
|
|
|
8480
8549
|
reason: `${SETTINGS_REL} is git-tracked; add the RepoWise BeforeAgent hook manually to opt in`
|
|
8481
8550
|
};
|
|
8482
8551
|
}
|
|
8483
|
-
const path =
|
|
8552
|
+
const path = join34(ctx.repoRoot, SETTINGS_REL);
|
|
8484
8553
|
const existing = await readOrNull4(path);
|
|
8485
8554
|
const merged = mergeGeminiHookSettings(existing, {
|
|
8486
8555
|
repoName: ctx.repoName,
|
|
@@ -8490,13 +8559,13 @@ var geminiHooksWriter = {
|
|
|
8490
8559
|
if (merged === existing) {
|
|
8491
8560
|
return { status: "unchanged", path };
|
|
8492
8561
|
}
|
|
8493
|
-
await fs16.mkdir(
|
|
8562
|
+
await fs16.mkdir(join34(ctx.repoRoot, ".gemini"), { recursive: true });
|
|
8494
8563
|
await fs16.writeFile(path, merged, "utf-8");
|
|
8495
8564
|
await applyGitignoreChanges(ctx.repoRoot, { add: [SETTINGS_REL] });
|
|
8496
8565
|
return { status: "written", path };
|
|
8497
8566
|
},
|
|
8498
8567
|
async remove(ctx) {
|
|
8499
|
-
const path =
|
|
8568
|
+
const path = join34(ctx.repoRoot, SETTINGS_REL);
|
|
8500
8569
|
const existing = await readOrNull4(path);
|
|
8501
8570
|
if (existing !== null) {
|
|
8502
8571
|
const next = removeGeminiHookSettings(existing);
|
|
@@ -8527,14 +8596,14 @@ async function readOrNull4(path) {
|
|
|
8527
8596
|
|
|
8528
8597
|
// ../listener/dist/mcp/auto-config/writers/roo.js
|
|
8529
8598
|
import { promises as fs17 } from "fs";
|
|
8530
|
-
import { join as
|
|
8599
|
+
import { join as join35 } from "path";
|
|
8531
8600
|
var rooWriter = {
|
|
8532
8601
|
tool: "roo",
|
|
8533
8602
|
async detect(repoRoot, home) {
|
|
8534
|
-
return await fileExists13(
|
|
8603
|
+
return await fileExists13(join35(repoRoot, ".roo")) || await fileExists13(join35(home, ".roo"));
|
|
8535
8604
|
},
|
|
8536
8605
|
async write(ctx) {
|
|
8537
|
-
const repoConfig =
|
|
8606
|
+
const repoConfig = join35(ctx.repoRoot, ".roo", "mcp.json");
|
|
8538
8607
|
const before = await readOrNull5(repoConfig);
|
|
8539
8608
|
await this.remove(ctx);
|
|
8540
8609
|
const after = await readOrNull5(repoConfig);
|
|
@@ -8544,9 +8613,9 @@ var rooWriter = {
|
|
|
8544
8613
|
};
|
|
8545
8614
|
},
|
|
8546
8615
|
async remove(ctx) {
|
|
8547
|
-
const repoConfig =
|
|
8548
|
-
const homeConfig =
|
|
8549
|
-
const homeSettings =
|
|
8616
|
+
const repoConfig = join35(ctx.repoRoot, ".roo", "mcp.json");
|
|
8617
|
+
const homeConfig = join35(ctx.home, ".roo", "mcp.json");
|
|
8618
|
+
const homeSettings = join35(ctx.home, ".roo", "settings.json");
|
|
8550
8619
|
for (const path of [repoConfig, homeConfig, homeSettings]) {
|
|
8551
8620
|
await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`).catch(() => {
|
|
8552
8621
|
});
|
|
@@ -8573,14 +8642,14 @@ async function readOrNull5(path) {
|
|
|
8573
8642
|
|
|
8574
8643
|
// ../listener/dist/mcp/auto-config/writers/windsurf.js
|
|
8575
8644
|
import { promises as fs18 } from "fs";
|
|
8576
|
-
import { join as
|
|
8645
|
+
import { join as join36 } from "path";
|
|
8577
8646
|
var windsurfWriter = {
|
|
8578
8647
|
tool: "windsurf",
|
|
8579
8648
|
async detect(_repoRoot, home) {
|
|
8580
|
-
return fileExists14(
|
|
8649
|
+
return fileExists14(join36(home, ".codeium", "windsurf"));
|
|
8581
8650
|
},
|
|
8582
8651
|
async write(ctx) {
|
|
8583
|
-
const path =
|
|
8652
|
+
const path = join36(ctx.home, ".codeium", "windsurf", "mcp_config.json");
|
|
8584
8653
|
await removeFromConfig(path, `repowise-${ctx.repoId}`);
|
|
8585
8654
|
const status2 = await writeMergedConfig({
|
|
8586
8655
|
path,
|
|
@@ -8590,7 +8659,7 @@ var windsurfWriter = {
|
|
|
8590
8659
|
return { status: status2, path };
|
|
8591
8660
|
},
|
|
8592
8661
|
async remove(ctx) {
|
|
8593
|
-
const path =
|
|
8662
|
+
const path = join36(ctx.home, ".codeium", "windsurf", "mcp_config.json");
|
|
8594
8663
|
await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
|
|
8595
8664
|
await removeFromConfig(path, `repowise-${ctx.repoId}`);
|
|
8596
8665
|
}
|
|
@@ -8623,7 +8692,7 @@ var WRITERS = [
|
|
|
8623
8692
|
|
|
8624
8693
|
// ../listener/dist/mcp/auto-config/index.js
|
|
8625
8694
|
async function runAutoConfig(opts) {
|
|
8626
|
-
const home = opts.home ??
|
|
8695
|
+
const home = opts.home ?? homedir6();
|
|
8627
8696
|
const writers = opts.writers ?? WRITERS;
|
|
8628
8697
|
const results = [];
|
|
8629
8698
|
for (const writer of writers) {
|
|
@@ -8672,7 +8741,7 @@ init_installer();
|
|
|
8672
8741
|
init_registry();
|
|
8673
8742
|
init_lsp_tools();
|
|
8674
8743
|
import { promises as fs19 } from "fs";
|
|
8675
|
-
import { join as
|
|
8744
|
+
import { join as join37 } from "path";
|
|
8676
8745
|
async function collectRepresentativeFiles(repoRoot) {
|
|
8677
8746
|
const reps = /* @__PURE__ */ new Map();
|
|
8678
8747
|
let inspected = 0;
|
|
@@ -8713,7 +8782,7 @@ async function collectRepresentativeFiles(repoRoot) {
|
|
|
8713
8782
|
continue;
|
|
8714
8783
|
if (name === "node_modules" || name === "dist" || name === "build")
|
|
8715
8784
|
continue;
|
|
8716
|
-
const sub =
|
|
8785
|
+
const sub = join37(repoRoot, name);
|
|
8717
8786
|
try {
|
|
8718
8787
|
const stat8 = await fs19.stat(sub);
|
|
8719
8788
|
if (stat8.isDirectory())
|
|
@@ -8761,7 +8830,7 @@ init_config_dir();
|
|
|
8761
8830
|
import { promises as fs20 } from "fs";
|
|
8762
8831
|
import { spawn as spawn9, execFile as execFile5 } from "child_process";
|
|
8763
8832
|
import { createWriteStream as createWriteStream2 } from "fs";
|
|
8764
|
-
import { join as
|
|
8833
|
+
import { join as join38 } from "path";
|
|
8765
8834
|
import { promisify as promisify3 } from "util";
|
|
8766
8835
|
var execFileAsync3 = promisify3(execFile5);
|
|
8767
8836
|
async function isCommandOnPath2(cmd) {
|
|
@@ -8825,7 +8894,7 @@ async function maybeWriteClangdStub(repoRoot, repoId) {
|
|
|
8825
8894
|
if (e.isDirectory()) {
|
|
8826
8895
|
if (skip.has(e.name) || e.name.startsWith("."))
|
|
8827
8896
|
continue;
|
|
8828
|
-
await rec(
|
|
8897
|
+
await rec(join38(dir, e.name));
|
|
8829
8898
|
continue;
|
|
8830
8899
|
}
|
|
8831
8900
|
const dot = e.name.lastIndexOf(".");
|
|
@@ -8834,7 +8903,7 @@ async function maybeWriteClangdStub(repoRoot, repoId) {
|
|
|
8834
8903
|
const ext = e.name.slice(dot);
|
|
8835
8904
|
if (!C_EXTS.has(ext) && !CPP_EXTS.has(ext))
|
|
8836
8905
|
continue;
|
|
8837
|
-
sources.push({ path:
|
|
8906
|
+
sources.push({ path: join38(dir, e.name), ext });
|
|
8838
8907
|
}
|
|
8839
8908
|
}
|
|
8840
8909
|
await rec(repoRoot);
|
|
@@ -8850,9 +8919,9 @@ async function maybeWriteClangdStub(repoRoot, repoId) {
|
|
|
8850
8919
|
command: `${driver} ${std} -I. -I./include -c "${src}"`
|
|
8851
8920
|
};
|
|
8852
8921
|
});
|
|
8853
|
-
const outDir =
|
|
8922
|
+
const outDir = join38(repoRoot, ".repowise");
|
|
8854
8923
|
await fs20.mkdir(outDir, { recursive: true });
|
|
8855
|
-
const outPath =
|
|
8924
|
+
const outPath = join38(outDir, "compile_commands.json");
|
|
8856
8925
|
await fs20.writeFile(outPath, JSON.stringify(entries, null, 2));
|
|
8857
8926
|
const capHit = sources.length >= MAX_STUB_SOURCES;
|
|
8858
8927
|
console.log(`[workspace-prep] ${repoId}: wrote ${entries.length.toString()}-entry compile_commands stub to .repowise/${capHit ? " (cap reached \u2014 additional sources truncated)" : ""}`);
|
|
@@ -8861,7 +8930,7 @@ async function maybeWriteClangdStub(repoRoot, repoId) {
|
|
|
8861
8930
|
var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
8862
8931
|
async function fileExists15(repoRoot, name) {
|
|
8863
8932
|
try {
|
|
8864
|
-
await fs20.access(
|
|
8933
|
+
await fs20.access(join38(repoRoot, name));
|
|
8865
8934
|
return true;
|
|
8866
8935
|
} catch {
|
|
8867
8936
|
return false;
|
|
@@ -8872,7 +8941,7 @@ async function detectWorkspaceDeps(repoRoot) {
|
|
|
8872
8941
|
if (await fileExists15(repoRoot, "package.json")) {
|
|
8873
8942
|
let pkgRaw = "";
|
|
8874
8943
|
try {
|
|
8875
|
-
pkgRaw = await fs20.readFile(
|
|
8944
|
+
pkgRaw = await fs20.readFile(join38(repoRoot, "package.json"), "utf8");
|
|
8876
8945
|
} catch {
|
|
8877
8946
|
}
|
|
8878
8947
|
if (pkgRaw) {
|
|
@@ -8975,7 +9044,7 @@ async function runWorkspacePreps(opts) {
|
|
|
8975
9044
|
if (opts.missing.length === 0)
|
|
8976
9045
|
return [];
|
|
8977
9046
|
const safeRepoId = opts.repoId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
8978
|
-
const logPath2 =
|
|
9047
|
+
const logPath2 = join38(getConfigDir(), `install-log.${safeRepoId}.txt`);
|
|
8979
9048
|
await fs20.mkdir(getConfigDir(), { recursive: true });
|
|
8980
9049
|
const stream = opts.logStream ?? createWriteStream2(logPath2, { flags: "a" });
|
|
8981
9050
|
stream.on("error", () => {
|
|
@@ -9126,7 +9195,7 @@ async function prepareWorkspaceDepsForRepos(repos) {
|
|
|
9126
9195
|
init_registry();
|
|
9127
9196
|
import { execFile as execFileCb, spawn as spawn10 } from "child_process";
|
|
9128
9197
|
import { access as access3, readdir as readdir4 } from "fs/promises";
|
|
9129
|
-
import { join as
|
|
9198
|
+
import { join as join39 } from "path";
|
|
9130
9199
|
import { promisify as promisify4 } from "util";
|
|
9131
9200
|
var execFile6 = promisify4(execFileCb);
|
|
9132
9201
|
var DEFAULT_BUILD_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
@@ -9179,7 +9248,7 @@ async function ensureIndexable(indexRoot, language, options = {}) {
|
|
|
9179
9248
|
}
|
|
9180
9249
|
}
|
|
9181
9250
|
try {
|
|
9182
|
-
await access3(
|
|
9251
|
+
await access3(join39(indexRoot, spec.indexStoreProbe));
|
|
9183
9252
|
return { ready: true, built: false };
|
|
9184
9253
|
} catch {
|
|
9185
9254
|
}
|
|
@@ -9219,7 +9288,9 @@ async function ensureIndexable(indexRoot, language, options = {}) {
|
|
|
9219
9288
|
init_src();
|
|
9220
9289
|
init_registry();
|
|
9221
9290
|
import { execFile as execFileCb2 } from "child_process";
|
|
9291
|
+
import { dirname as dirname16 } from "path";
|
|
9222
9292
|
import { promisify as promisify5 } from "util";
|
|
9293
|
+
init_daemon_path();
|
|
9223
9294
|
|
|
9224
9295
|
// ../listener/dist/typed-resolution/lsp-upgrade-strategy.js
|
|
9225
9296
|
var goStrategy = {
|
|
@@ -9701,7 +9772,13 @@ async function isCommandOnPath4(command) {
|
|
|
9701
9772
|
return false;
|
|
9702
9773
|
const probeCmd = process.platform === "win32" ? "where" : "which";
|
|
9703
9774
|
try {
|
|
9704
|
-
await execFile7(probeCmd, [command], {
|
|
9775
|
+
await execFile7(probeCmd, [command], {
|
|
9776
|
+
timeout: 5e3,
|
|
9777
|
+
env: {
|
|
9778
|
+
...process.env,
|
|
9779
|
+
PATH: buildLspSpawnPath(process.env.PATH, dirname16(process.execPath), managedBinDirs())
|
|
9780
|
+
}
|
|
9781
|
+
});
|
|
9705
9782
|
return true;
|
|
9706
9783
|
} catch {
|
|
9707
9784
|
return false;
|
|
@@ -9905,13 +9982,13 @@ init_config_dir();
|
|
|
9905
9982
|
import { execFile as execFileCb3 } from "child_process";
|
|
9906
9983
|
import { createHash as createHash5 } from "crypto";
|
|
9907
9984
|
import { mkdir as mkdir15, readdir as readdir5, rm, stat as stat6 } from "fs/promises";
|
|
9908
|
-
import { join as
|
|
9985
|
+
import { join as join40 } from "path";
|
|
9909
9986
|
import { promisify as promisify6 } from "util";
|
|
9910
9987
|
var execFile8 = promisify6(execFileCb3);
|
|
9911
9988
|
var GIT_OP_TIMEOUT_MS = 6e4;
|
|
9912
9989
|
var MAX_CONCURRENT_WORKTREES = 3;
|
|
9913
9990
|
function worktreeBaseDir() {
|
|
9914
|
-
return
|
|
9991
|
+
return join40(getConfigDir(), "lsp-worktrees");
|
|
9915
9992
|
}
|
|
9916
9993
|
async function reapOrphanWorktrees(maxAgeMs = 24 * 60 * 60 * 1e3, base = worktreeBaseDir()) {
|
|
9917
9994
|
let reaped = 0;
|
|
@@ -9923,7 +10000,7 @@ async function reapOrphanWorktrees(maxAgeMs = 24 * 60 * 60 * 1e3, base = worktre
|
|
|
9923
10000
|
}
|
|
9924
10001
|
const cutoff = Date.now() - maxAgeMs;
|
|
9925
10002
|
for (const entry of entries) {
|
|
9926
|
-
const dir =
|
|
10003
|
+
const dir = join40(base, entry);
|
|
9927
10004
|
try {
|
|
9928
10005
|
const info = await stat6(dir);
|
|
9929
10006
|
if (!info.isDirectory() || info.mtimeMs > cutoff)
|
|
@@ -9956,7 +10033,7 @@ async function prepareCommittedTree(repoRoot, commitSha, options = {}) {
|
|
|
9956
10033
|
}
|
|
9957
10034
|
const base = options.worktreeBase ?? worktreeBaseDir();
|
|
9958
10035
|
const dirName = createHash5("sha256").update(`${repoRoot}\0${commitSha}`).digest("hex").slice(0, 16) + "-" + commitSha.slice(0, 12);
|
|
9959
|
-
const worktreePath =
|
|
10036
|
+
const worktreePath = join40(base, dirName);
|
|
9960
10037
|
try {
|
|
9961
10038
|
await mkdir15(base, { recursive: true });
|
|
9962
10039
|
await git(["-C", repoRoot, "worktree", "prune"]).catch(() => void 0);
|
|
@@ -10292,7 +10369,7 @@ var CRASH_LOOP_WINDOW_MS = 3e4;
|
|
|
10292
10369
|
var CRASH_LOOP_THRESHOLD = 3;
|
|
10293
10370
|
async function readRawToolConfig() {
|
|
10294
10371
|
try {
|
|
10295
|
-
const configPath =
|
|
10372
|
+
const configPath = join41(getConfigDir(), "config.json");
|
|
10296
10373
|
const data = await readFile13(configPath, "utf-8");
|
|
10297
10374
|
const raw = JSON.parse(data);
|
|
10298
10375
|
return {
|
|
@@ -10367,11 +10444,11 @@ function resolveAuditRoots() {
|
|
|
10367
10444
|
return override.split(":").map((s) => s.trim()).filter(Boolean);
|
|
10368
10445
|
}
|
|
10369
10446
|
const out = /* @__PURE__ */ new Set();
|
|
10370
|
-
let dir =
|
|
10447
|
+
let dir = dirname17(process.execPath);
|
|
10371
10448
|
for (let i = 0; i < 8; i += 1) {
|
|
10372
|
-
out.add(
|
|
10373
|
-
out.add(
|
|
10374
|
-
const parent =
|
|
10449
|
+
out.add(join41(dir, "node_modules"));
|
|
10450
|
+
out.add(join41(dir, "lib", "node_modules"));
|
|
10451
|
+
const parent = dirname17(dir);
|
|
10375
10452
|
if (parent === dir)
|
|
10376
10453
|
break;
|
|
10377
10454
|
dir = parent;
|
|
@@ -10635,7 +10712,7 @@ async function checkStaleContext(repos, state, groups) {
|
|
|
10635
10712
|
if (group?.offline.isOffline)
|
|
10636
10713
|
continue;
|
|
10637
10714
|
const { statSync: statSync2, readdirSync: readdirSync2 } = await import("fs");
|
|
10638
|
-
const contextPath =
|
|
10715
|
+
const contextPath = join41(repo.localPath, "repowise-context");
|
|
10639
10716
|
let isMissingOrEmpty = false;
|
|
10640
10717
|
try {
|
|
10641
10718
|
const s = statSync2(contextPath);
|
|
@@ -10700,7 +10777,7 @@ async function reconcileAgentInstructions(repos) {
|
|
|
10700
10777
|
const { graphOnly } = await readRawToolConfig();
|
|
10701
10778
|
const variant = graphOnly ? "graph" : "full";
|
|
10702
10779
|
for (const repo of repos) {
|
|
10703
|
-
const path =
|
|
10780
|
+
const path = join41(repo.localPath, "repowise-context", "project-overview.md");
|
|
10704
10781
|
let content;
|
|
10705
10782
|
try {
|
|
10706
10783
|
content = await readFile13(path, "utf-8");
|
|
@@ -10784,7 +10861,7 @@ async function startListener() {
|
|
|
10784
10861
|
}
|
|
10785
10862
|
const configDir = getConfigDir();
|
|
10786
10863
|
await mkdir16(configDir, { recursive: true });
|
|
10787
|
-
const lockPath =
|
|
10864
|
+
const lockPath = join41(configDir, "listener.lock");
|
|
10788
10865
|
await writeFile15(lockPath, "", { flag: "a" });
|
|
10789
10866
|
let lockIsHeld = false;
|
|
10790
10867
|
try {
|
|
@@ -10827,7 +10904,7 @@ async function startListener() {
|
|
|
10827
10904
|
return;
|
|
10828
10905
|
}
|
|
10829
10906
|
if (config2.repos.length === 0 && !config2.autoDiscoverRepos) {
|
|
10830
|
-
console.error(`No repos configured. Add repos to ${
|
|
10907
|
+
console.error(`No repos configured. Add repos to ${join41(configDir, "config.json")}`);
|
|
10831
10908
|
await releaseLockAndExit();
|
|
10832
10909
|
process.exitCode = 1;
|
|
10833
10910
|
return;
|
|
@@ -10894,8 +10971,8 @@ async function startListener() {
|
|
|
10894
10971
|
const packageName = true ? "repowisestage" : "repowise";
|
|
10895
10972
|
let currentVersion = "";
|
|
10896
10973
|
try {
|
|
10897
|
-
const selfDir =
|
|
10898
|
-
const pkgJsonPath =
|
|
10974
|
+
const selfDir = dirname17(fileURLToPath3(import.meta.url));
|
|
10975
|
+
const pkgJsonPath = join41(selfDir, "..", "..", "package.json");
|
|
10899
10976
|
const pkgJson = JSON.parse(await readFile13(pkgJsonPath, "utf-8"));
|
|
10900
10977
|
currentVersion = pkgJson.version;
|
|
10901
10978
|
} catch (err) {
|
|
@@ -11398,7 +11475,7 @@ async function startListener() {
|
|
|
11398
11475
|
} catch {
|
|
11399
11476
|
}
|
|
11400
11477
|
}
|
|
11401
|
-
const credentialsPath =
|
|
11478
|
+
const credentialsPath = join41(getConfigDir(), "credentials.json");
|
|
11402
11479
|
let credentialsChanged = false;
|
|
11403
11480
|
let watcher = null;
|
|
11404
11481
|
try {
|
|
@@ -11443,8 +11520,8 @@ if (isDirectRun) {
|
|
|
11443
11520
|
}
|
|
11444
11521
|
|
|
11445
11522
|
// src/lib/env.ts
|
|
11446
|
-
import { homedir as
|
|
11447
|
-
import { join as
|
|
11523
|
+
import { homedir as homedir7 } from "os";
|
|
11524
|
+
import { join as join42 } from "path";
|
|
11448
11525
|
var IS_STAGING2 = true ? true : false;
|
|
11449
11526
|
var PRODUCTION = {
|
|
11450
11527
|
apiUrl: "https://api.repowise.ai",
|
|
@@ -11464,7 +11541,7 @@ function getEnvConfig() {
|
|
|
11464
11541
|
return IS_STAGING2 ? STAGING : PRODUCTION;
|
|
11465
11542
|
}
|
|
11466
11543
|
function getConfigDir2() {
|
|
11467
|
-
return
|
|
11544
|
+
return join42(homedir7(), IS_STAGING2 ? ".repowise-staging" : ".repowise");
|
|
11468
11545
|
}
|
|
11469
11546
|
function getPackageName() {
|
|
11470
11547
|
return true ? "repowisestage" : "repowise";
|
|
@@ -11475,11 +11552,11 @@ import chalk from "chalk";
|
|
|
11475
11552
|
|
|
11476
11553
|
// src/lib/config.ts
|
|
11477
11554
|
import { readFile as readFile14, writeFile as writeFile16, mkdir as mkdir17, rename as rename4, unlink as unlink10 } from "fs/promises";
|
|
11478
|
-
import { join as
|
|
11555
|
+
import { join as join43 } from "path";
|
|
11479
11556
|
import lockfile5 from "proper-lockfile";
|
|
11480
11557
|
async function getConfig() {
|
|
11481
11558
|
try {
|
|
11482
|
-
const data = await readFile14(
|
|
11559
|
+
const data = await readFile14(join43(getConfigDir2(), "config.json"), "utf-8");
|
|
11483
11560
|
return JSON.parse(data);
|
|
11484
11561
|
} catch {
|
|
11485
11562
|
return {};
|
|
@@ -11487,7 +11564,7 @@ async function getConfig() {
|
|
|
11487
11564
|
}
|
|
11488
11565
|
async function saveConfig(config2) {
|
|
11489
11566
|
const dir = getConfigDir2();
|
|
11490
|
-
const path =
|
|
11567
|
+
const path = join43(dir, "config.json");
|
|
11491
11568
|
await mkdir17(dir, { recursive: true });
|
|
11492
11569
|
const tmpPath = path + ".tmp";
|
|
11493
11570
|
try {
|
|
@@ -11503,7 +11580,7 @@ async function saveConfig(config2) {
|
|
|
11503
11580
|
}
|
|
11504
11581
|
async function mergeAndSaveConfig(updates) {
|
|
11505
11582
|
const dir = getConfigDir2();
|
|
11506
|
-
const path =
|
|
11583
|
+
const path = join43(dir, "config.json");
|
|
11507
11584
|
await mkdir17(dir, { recursive: true });
|
|
11508
11585
|
try {
|
|
11509
11586
|
await writeFile16(path, "", { flag: "a" });
|
|
@@ -11574,7 +11651,7 @@ async function showWelcome(currentVersion) {
|
|
|
11574
11651
|
|
|
11575
11652
|
// src/commands/create.ts
|
|
11576
11653
|
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
11577
|
-
import { dirname as
|
|
11654
|
+
import { dirname as dirname19, join as join49 } from "path";
|
|
11578
11655
|
init_src();
|
|
11579
11656
|
import chalk8 from "chalk";
|
|
11580
11657
|
import ora from "ora";
|
|
@@ -11583,7 +11660,7 @@ import ora from "ora";
|
|
|
11583
11660
|
import { createHash as createHash6, randomBytes as randomBytes3 } from "crypto";
|
|
11584
11661
|
import { readFile as readFile15, writeFile as writeFile17, mkdir as mkdir18, chmod as chmod4, unlink as unlink11 } from "fs/promises";
|
|
11585
11662
|
import http from "http";
|
|
11586
|
-
import { join as
|
|
11663
|
+
import { join as join44 } from "path";
|
|
11587
11664
|
var CLI_CALLBACK_PORT = 19876;
|
|
11588
11665
|
var CALLBACK_TIMEOUT_MS = 12e4;
|
|
11589
11666
|
function getCognitoConfigForStorage() {
|
|
@@ -11749,7 +11826,7 @@ async function refreshTokens2(refreshToken) {
|
|
|
11749
11826
|
}
|
|
11750
11827
|
async function getStoredCredentials2() {
|
|
11751
11828
|
try {
|
|
11752
|
-
const credPath =
|
|
11829
|
+
const credPath = join44(getConfigDir2(), "credentials.json");
|
|
11753
11830
|
const data = await readFile15(credPath, "utf-8");
|
|
11754
11831
|
return JSON.parse(data);
|
|
11755
11832
|
} catch (err) {
|
|
@@ -11761,14 +11838,14 @@ async function getStoredCredentials2() {
|
|
|
11761
11838
|
}
|
|
11762
11839
|
async function storeCredentials2(credentials) {
|
|
11763
11840
|
const dir = getConfigDir2();
|
|
11764
|
-
const credPath =
|
|
11841
|
+
const credPath = join44(dir, "credentials.json");
|
|
11765
11842
|
await mkdir18(dir, { recursive: true, mode: 448 });
|
|
11766
11843
|
await writeFile17(credPath, JSON.stringify(credentials, null, 2));
|
|
11767
11844
|
await chmod4(credPath, 384);
|
|
11768
11845
|
}
|
|
11769
11846
|
async function clearCredentials() {
|
|
11770
11847
|
try {
|
|
11771
|
-
await unlink11(
|
|
11848
|
+
await unlink11(join44(getConfigDir2(), "credentials.json"));
|
|
11772
11849
|
} catch (err) {
|
|
11773
11850
|
if (err.code !== "ENOENT") throw err;
|
|
11774
11851
|
}
|
|
@@ -11976,9 +12053,9 @@ async function selectAiTools() {
|
|
|
11976
12053
|
|
|
11977
12054
|
// src/lib/gitignore.ts
|
|
11978
12055
|
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2 } from "fs";
|
|
11979
|
-
import { join as
|
|
12056
|
+
import { join as join45 } from "path";
|
|
11980
12057
|
function ensureGitignore(repoRoot, entry) {
|
|
11981
|
-
const gitignorePath =
|
|
12058
|
+
const gitignorePath = join45(repoRoot, ".gitignore");
|
|
11982
12059
|
if (existsSync2(gitignorePath)) {
|
|
11983
12060
|
const content = readFileSync2(gitignorePath, "utf-8");
|
|
11984
12061
|
const lines = content.split("\n").map((l) => l.trim());
|
|
@@ -11995,7 +12072,7 @@ function ensureGitignore(repoRoot, entry) {
|
|
|
11995
12072
|
// src/lib/graph-cache.ts
|
|
11996
12073
|
init_config_dir();
|
|
11997
12074
|
import { mkdirSync, writeFileSync as writeFileSync2, renameSync, unlinkSync } from "fs";
|
|
11998
|
-
import { dirname as
|
|
12075
|
+
import { dirname as dirname18, join as join46 } from "path";
|
|
11999
12076
|
var SAFE_REPO_ID = /^[A-Za-z0-9_.-]{1,128}$/;
|
|
12000
12077
|
function assertSafeRepoId2(repoId) {
|
|
12001
12078
|
if (!repoId || typeof repoId !== "string" || !SAFE_REPO_ID.test(repoId) || repoId === "." || repoId === ".." || repoId.startsWith(".")) {
|
|
@@ -12004,7 +12081,7 @@ function assertSafeRepoId2(repoId) {
|
|
|
12004
12081
|
}
|
|
12005
12082
|
function graphCachePath(repoId) {
|
|
12006
12083
|
assertSafeRepoId2(repoId);
|
|
12007
|
-
return
|
|
12084
|
+
return join46(getConfigDir(), "graphs", `${repoId}.json`);
|
|
12008
12085
|
}
|
|
12009
12086
|
function isUsableGraph(parsed) {
|
|
12010
12087
|
if (typeof parsed !== "object" || parsed === null) return false;
|
|
@@ -12022,9 +12099,27 @@ function readErrorCode(body) {
|
|
|
12022
12099
|
return void 0;
|
|
12023
12100
|
}
|
|
12024
12101
|
}
|
|
12102
|
+
async function readBodyBounded(res, ctrl, bodyTimeoutMs) {
|
|
12103
|
+
const bodyPromise = res.text();
|
|
12104
|
+
void bodyPromise.catch(() => {
|
|
12105
|
+
});
|
|
12106
|
+
let timer;
|
|
12107
|
+
const timeout = new Promise((_, reject) => {
|
|
12108
|
+
timer = setTimeout(() => {
|
|
12109
|
+
ctrl.abort();
|
|
12110
|
+
reject(new Error("GRAPH_BODY_TIMEOUT"));
|
|
12111
|
+
}, bodyTimeoutMs);
|
|
12112
|
+
});
|
|
12113
|
+
try {
|
|
12114
|
+
return await Promise.race([bodyPromise, timeout]);
|
|
12115
|
+
} finally {
|
|
12116
|
+
if (timer) clearTimeout(timer);
|
|
12117
|
+
}
|
|
12118
|
+
}
|
|
12025
12119
|
async function ensureGraphDownloaded(opts) {
|
|
12026
12120
|
const fetchFn = opts.fetchFn ?? fetch;
|
|
12027
12121
|
const timeoutMs = opts.timeoutMs ?? 1e4;
|
|
12122
|
+
const bodyTimeoutMs = opts.bodyTimeoutMs ?? 12e4;
|
|
12028
12123
|
const maxRetries = opts.maxRetries ?? 3;
|
|
12029
12124
|
const sleep2 = opts.sleepFn ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
12030
12125
|
let targetPath;
|
|
@@ -12081,7 +12176,16 @@ async function ensureGraphDownloaded(opts) {
|
|
|
12081
12176
|
if (!res.ok) {
|
|
12082
12177
|
return { status: "error", message: `HTTP ${String(res.status)}` };
|
|
12083
12178
|
}
|
|
12084
|
-
|
|
12179
|
+
let body;
|
|
12180
|
+
try {
|
|
12181
|
+
body = await readBodyBounded(res, ctrl, bodyTimeoutMs);
|
|
12182
|
+
} catch (err) {
|
|
12183
|
+
if (attempt < maxRetries) {
|
|
12184
|
+
await sleep2(2e3 * attempt);
|
|
12185
|
+
continue;
|
|
12186
|
+
}
|
|
12187
|
+
return { status: "error", message: err instanceof Error ? err.message : String(err) };
|
|
12188
|
+
}
|
|
12085
12189
|
let parsed;
|
|
12086
12190
|
try {
|
|
12087
12191
|
parsed = JSON.parse(body);
|
|
@@ -12096,7 +12200,7 @@ async function ensureGraphDownloaded(opts) {
|
|
|
12096
12200
|
return { status: "not_found", message: "EMPTY_GRAPH" };
|
|
12097
12201
|
}
|
|
12098
12202
|
try {
|
|
12099
|
-
mkdirSync(
|
|
12203
|
+
mkdirSync(dirname18(targetPath), { recursive: true });
|
|
12100
12204
|
const tmp = `${targetPath}.${String(process.pid)}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
12101
12205
|
try {
|
|
12102
12206
|
writeFileSync2(tmp, body, { encoding: "utf-8", mode: 384 });
|
|
@@ -12247,7 +12351,7 @@ async function handleInterview(syncId, questionId, questionText, questionContext
|
|
|
12247
12351
|
const message = err instanceof Error ? err.message : String(err);
|
|
12248
12352
|
if (message.includes("not awaiting input") || message.includes("expired")) {
|
|
12249
12353
|
console.log(chalk3.dim(" Pipeline has already moved on \u2014 continuing."));
|
|
12250
|
-
return;
|
|
12354
|
+
return { finished: true };
|
|
12251
12355
|
}
|
|
12252
12356
|
try {
|
|
12253
12357
|
await new Promise((r) => setTimeout(r, 1e3));
|
|
@@ -12257,7 +12361,7 @@ async function handleInterview(syncId, questionId, questionText, questionContext
|
|
|
12257
12361
|
});
|
|
12258
12362
|
} catch {
|
|
12259
12363
|
console.log(chalk3.yellow(" Could not submit answer \u2014 pipeline will continue."));
|
|
12260
|
-
return;
|
|
12364
|
+
return { finished: false };
|
|
12261
12365
|
}
|
|
12262
12366
|
}
|
|
12263
12367
|
if (action === "done") {
|
|
@@ -12268,6 +12372,7 @@ async function handleInterview(syncId, questionId, questionText, questionContext
|
|
|
12268
12372
|
console.log(chalk3.dim(" Answer recorded."));
|
|
12269
12373
|
}
|
|
12270
12374
|
console.log("");
|
|
12375
|
+
return { finished: action === "done" || questionCounter >= MAX_QUESTIONS };
|
|
12271
12376
|
}
|
|
12272
12377
|
|
|
12273
12378
|
// src/lib/progress-renderer.ts
|
|
@@ -12300,6 +12405,12 @@ var ProgressRenderer = class {
|
|
|
12300
12405
|
privacyShieldShown = false;
|
|
12301
12406
|
discoveryShown = false;
|
|
12302
12407
|
interviewCompleteShown = false;
|
|
12408
|
+
// FIX-10: the scan runs in PARALLEL with the interview, so scanProgress
|
|
12409
|
+
// arrives mid-interview. Hold scan rendering until the interview is
|
|
12410
|
+
// definitively over (setInterviewComplete) so the coffee/Code-Analysis/graph
|
|
12411
|
+
// block never interrupts the Q&A. Buffer the latest progress meanwhile.
|
|
12412
|
+
interviewComplete = false;
|
|
12413
|
+
pendingScanProgress = null;
|
|
12303
12414
|
scanHeaderShown = false;
|
|
12304
12415
|
scanSummaryShown = false;
|
|
12305
12416
|
graphSubtitleShown = false;
|
|
@@ -12601,6 +12712,21 @@ var ProgressRenderer = class {
|
|
|
12601
12712
|
}
|
|
12602
12713
|
return `${stepLabel}... ${chalk4.dim(`(${overallPct}%)`)}`;
|
|
12603
12714
|
}
|
|
12715
|
+
/**
|
|
12716
|
+
* FIX-10 — mark the interview definitively over. Unlocks scan-progress
|
|
12717
|
+
* rendering and flushes any progress buffered during the interview, so the
|
|
12718
|
+
* coffee → Code-Analysis → graph-ready block appears in order AFTER the last
|
|
12719
|
+
* question (renderScanProgress is idempotent via its *Shown latches).
|
|
12720
|
+
* Idempotent; safe to call from multiple completion paths.
|
|
12721
|
+
*/
|
|
12722
|
+
setInterviewComplete(spinner) {
|
|
12723
|
+
if (this.interviewComplete) return;
|
|
12724
|
+
this.interviewComplete = true;
|
|
12725
|
+
if (this.pendingScanProgress) {
|
|
12726
|
+
this.renderScanProgress(this.pendingScanProgress, spinner);
|
|
12727
|
+
this.pendingScanProgress = null;
|
|
12728
|
+
}
|
|
12729
|
+
}
|
|
12604
12730
|
update(syncResult, spinner) {
|
|
12605
12731
|
if (syncResult.privacyShieldEnabled !== void 0) {
|
|
12606
12732
|
this.renderPrivacyShield(syncResult.privacyShieldEnabled, spinner);
|
|
@@ -12609,7 +12735,11 @@ var ProgressRenderer = class {
|
|
|
12609
12735
|
this.renderDiscovery(syncResult.discoveryResult, spinner);
|
|
12610
12736
|
}
|
|
12611
12737
|
if (syncResult.scanProgress) {
|
|
12612
|
-
this.
|
|
12738
|
+
if (this.interviewComplete) {
|
|
12739
|
+
this.renderScanProgress(syncResult.scanProgress, spinner);
|
|
12740
|
+
} else {
|
|
12741
|
+
this.pendingScanProgress = syncResult.scanProgress;
|
|
12742
|
+
}
|
|
12613
12743
|
}
|
|
12614
12744
|
if (syncResult.generationProgress?.fileStatuses && syncResult.generationProgress.fileStatuses.length > 0) {
|
|
12615
12745
|
this.renderFileStatuses(syncResult.generationProgress.fileStatuses, spinner);
|
|
@@ -12669,7 +12799,7 @@ import chalk6 from "chalk";
|
|
|
12669
12799
|
|
|
12670
12800
|
// src/lib/dep-installer.ts
|
|
12671
12801
|
import { promises as fs21 } from "fs";
|
|
12672
|
-
import { join as
|
|
12802
|
+
import { join as join47 } from "path";
|
|
12673
12803
|
var SUPPORTED_DEP_LANGUAGES = /* @__PURE__ */ new Set([
|
|
12674
12804
|
"typescript",
|
|
12675
12805
|
"javascript",
|
|
@@ -12686,7 +12816,7 @@ var exists = async (p) => {
|
|
|
12686
12816
|
}
|
|
12687
12817
|
};
|
|
12688
12818
|
async function fileExists16(repoRoot, name) {
|
|
12689
|
-
return exists(
|
|
12819
|
+
return exists(join47(repoRoot, name));
|
|
12690
12820
|
}
|
|
12691
12821
|
async function detectNodePackageManager(repoRoot) {
|
|
12692
12822
|
if (await fileExists16(repoRoot, "pnpm-lock.yaml")) {
|
|
@@ -12764,11 +12894,11 @@ async function detectMissingDeps(repoRoot, scopedLanguages) {
|
|
|
12764
12894
|
import { spawn as spawn11 } from "child_process";
|
|
12765
12895
|
import { createWriteStream as createWriteStream3 } from "fs";
|
|
12766
12896
|
import { promises as fs22 } from "fs";
|
|
12767
|
-
import { join as
|
|
12897
|
+
import { join as join48 } from "path";
|
|
12768
12898
|
var DEFAULT_INSTALL_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
12769
12899
|
async function runMissingDepInstalls(opts) {
|
|
12770
12900
|
const safeRepoId = opts.repoId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
12771
|
-
const logPath2 =
|
|
12901
|
+
const logPath2 = join48(getConfigDir2(), `install-log.${safeRepoId}.txt`);
|
|
12772
12902
|
await fs22.mkdir(getConfigDir2(), { recursive: true });
|
|
12773
12903
|
const stream = opts.logStream ?? createWriteStream3(logPath2, { flags: "a" });
|
|
12774
12904
|
stream.on("error", () => {
|
|
@@ -12815,7 +12945,7 @@ async function runOne2(dep, repoRoot, stream, timeoutMs) {
|
|
|
12815
12945
|
}
|
|
12816
12946
|
async function runPipFlow(repoRoot, stream, timeoutMs) {
|
|
12817
12947
|
await runSimple(["python3", "-m", "venv", ".venv"], repoRoot, stream, timeoutMs);
|
|
12818
|
-
const venvPip = process.platform === "win32" ?
|
|
12948
|
+
const venvPip = process.platform === "win32" ? join48(".venv", "Scripts", "pip.exe") : join48(".venv", "bin", "pip");
|
|
12819
12949
|
await runSimple([venvPip, "install", "-r", "requirements.txt"], repoRoot, stream, timeoutMs);
|
|
12820
12950
|
}
|
|
12821
12951
|
function runSimple(cmd, repoRoot, stream, timeoutMs) {
|
|
@@ -13229,6 +13359,8 @@ async function create() {
|
|
|
13229
13359
|
const progressRenderer = new ProgressRenderer();
|
|
13230
13360
|
let depInstallShown = false;
|
|
13231
13361
|
let seenAwaitingInput = false;
|
|
13362
|
+
let interviewComplete = false;
|
|
13363
|
+
let interviewQuestionsAnswered = 0;
|
|
13232
13364
|
let lspWaitStartedAt = null;
|
|
13233
13365
|
let lspFellBackNoticeShown = false;
|
|
13234
13366
|
let depInstallPromise = null;
|
|
@@ -13269,6 +13401,8 @@ async function create() {
|
|
|
13269
13401
|
try {
|
|
13270
13402
|
while (true) {
|
|
13271
13403
|
if (++pollAttempts > MAX_POLL_ATTEMPTS) {
|
|
13404
|
+
progressRenderer.setInterviewComplete(spinner);
|
|
13405
|
+
progressRenderer.finalize();
|
|
13272
13406
|
spinner.fail(chalk8.red("Pipeline timed out. Check dashboard for status."));
|
|
13273
13407
|
process.exitCode = 1;
|
|
13274
13408
|
return;
|
|
@@ -13288,6 +13422,10 @@ async function create() {
|
|
|
13288
13422
|
if (sigintBusy) {
|
|
13289
13423
|
continue;
|
|
13290
13424
|
}
|
|
13425
|
+
if (!interviewComplete && graphOnly) {
|
|
13426
|
+
interviewComplete = true;
|
|
13427
|
+
progressRenderer.setInterviewComplete(spinner);
|
|
13428
|
+
}
|
|
13291
13429
|
progressRenderer.update(syncResult, spinner);
|
|
13292
13430
|
if (!graphOnly && syncResult.status === "in_progress") {
|
|
13293
13431
|
if (syncResult.lspWaitState === "awaiting" || syncResult.lspWaitState === "analyzing") {
|
|
@@ -13307,13 +13445,19 @@ async function create() {
|
|
|
13307
13445
|
}
|
|
13308
13446
|
if (syncResult.status === "awaiting_input" && syncResult.questionId && syncResult.questionText) {
|
|
13309
13447
|
spinner.stop();
|
|
13310
|
-
|
|
13448
|
+
const estimatedQ = syncResult.discoveryResult?.estimatedInterviewQuestions;
|
|
13449
|
+
const { finished } = await handleInterview(
|
|
13311
13450
|
syncId,
|
|
13312
13451
|
syncResult.questionId,
|
|
13313
13452
|
syncResult.questionText,
|
|
13314
13453
|
syncResult.questionContext ?? void 0,
|
|
13315
|
-
|
|
13454
|
+
estimatedQ
|
|
13316
13455
|
);
|
|
13456
|
+
interviewQuestionsAnswered++;
|
|
13457
|
+
if (!interviewComplete && (finished || estimatedQ !== void 0 && estimatedQ > 0 && interviewQuestionsAnswered >= estimatedQ)) {
|
|
13458
|
+
interviewComplete = true;
|
|
13459
|
+
progressRenderer.setInterviewComplete(spinner);
|
|
13460
|
+
}
|
|
13317
13461
|
spinner.start("Resuming pipeline...");
|
|
13318
13462
|
continue;
|
|
13319
13463
|
}
|
|
@@ -13333,6 +13477,7 @@ async function create() {
|
|
|
13333
13477
|
depInstallPromise = result.installPromise;
|
|
13334
13478
|
}
|
|
13335
13479
|
if (syncResult.status === "completed") {
|
|
13480
|
+
progressRenderer.setInterviewComplete(spinner);
|
|
13336
13481
|
progressRenderer.finalize();
|
|
13337
13482
|
const generatedFiles = syncResult.filesGenerated ?? [];
|
|
13338
13483
|
const fileCount = generatedFiles.length;
|
|
@@ -13357,6 +13502,7 @@ async function create() {
|
|
|
13357
13502
|
break;
|
|
13358
13503
|
}
|
|
13359
13504
|
if (syncResult.status === "failed") {
|
|
13505
|
+
progressRenderer.setInterviewComplete(spinner);
|
|
13360
13506
|
progressRenderer.finalize();
|
|
13361
13507
|
spinner.fail(chalk8.red(`Pipeline failed: ${syncResult.error ?? "Unknown error"}`));
|
|
13362
13508
|
process.exitCode = 1;
|
|
@@ -13372,7 +13518,7 @@ async function create() {
|
|
|
13372
13518
|
const listResult = await apiRequest(`/v1/repos/${repoId}/context`);
|
|
13373
13519
|
const files = listResult.data?.files ?? listResult.files ?? [];
|
|
13374
13520
|
if (files.length > 0) {
|
|
13375
|
-
const contextDir =
|
|
13521
|
+
const contextDir = join49(repoRoot, DEFAULT_CONTEXT_FOLDER);
|
|
13376
13522
|
mkdirSync2(contextDir, { recursive: true });
|
|
13377
13523
|
let downloadedCount = 0;
|
|
13378
13524
|
let failedCount = 0;
|
|
@@ -13386,8 +13532,8 @@ async function create() {
|
|
|
13386
13532
|
const response = await fetch(presignedUrl);
|
|
13387
13533
|
if (response.ok) {
|
|
13388
13534
|
const content = await response.text();
|
|
13389
|
-
const filePath =
|
|
13390
|
-
mkdirSync2(
|
|
13535
|
+
const filePath = join49(contextDir, file.fileName);
|
|
13536
|
+
mkdirSync2(dirname19(filePath), { recursive: true });
|
|
13391
13537
|
writeFileSync3(filePath, content, "utf-8");
|
|
13392
13538
|
downloadedCount++;
|
|
13393
13539
|
} else {
|
|
@@ -13637,7 +13783,7 @@ Files are stored on our servers (not in git). Retry when online.`
|
|
|
13637
13783
|
|
|
13638
13784
|
// src/commands/member.ts
|
|
13639
13785
|
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
13640
|
-
import { dirname as
|
|
13786
|
+
import { dirname as dirname20, join as join50, resolve, sep } from "path";
|
|
13641
13787
|
import chalk9 from "chalk";
|
|
13642
13788
|
import ora2 from "ora";
|
|
13643
13789
|
var DEFAULT_CONTEXT_FOLDER2 = "repowise-context";
|
|
@@ -13791,7 +13937,7 @@ async function member() {
|
|
|
13791
13937
|
spinner.succeed(`Found ${chalk9.bold(files.length)} context files on server`);
|
|
13792
13938
|
const { tools } = await selectAiTools();
|
|
13793
13939
|
spinner.start("Downloading context files...");
|
|
13794
|
-
const contextDir =
|
|
13940
|
+
const contextDir = join50(repoRoot, DEFAULT_CONTEXT_FOLDER2);
|
|
13795
13941
|
mkdirSync3(contextDir, { recursive: true });
|
|
13796
13942
|
let downloadedCount = 0;
|
|
13797
13943
|
let failedCount = 0;
|
|
@@ -13812,7 +13958,7 @@ async function member() {
|
|
|
13812
13958
|
const response = await fetch(presignedUrl);
|
|
13813
13959
|
if (response.ok) {
|
|
13814
13960
|
const content = await response.text();
|
|
13815
|
-
mkdirSync3(
|
|
13961
|
+
mkdirSync3(dirname20(safePath), { recursive: true });
|
|
13816
13962
|
writeFileSync4(safePath, content, "utf-8");
|
|
13817
13963
|
downloadedCount++;
|
|
13818
13964
|
} else {
|
|
@@ -13990,10 +14136,10 @@ import ora3 from "ora";
|
|
|
13990
14136
|
|
|
13991
14137
|
// src/lib/tenant-graph-purge.ts
|
|
13992
14138
|
import { promises as fs23 } from "fs";
|
|
13993
|
-
import { homedir as
|
|
13994
|
-
import { join as
|
|
13995
|
-
async function purgeForeignGraphs(validRepoIds, home =
|
|
13996
|
-
const graphsDir =
|
|
14139
|
+
import { homedir as homedir8 } from "os";
|
|
14140
|
+
import { join as join51 } from "path";
|
|
14141
|
+
async function purgeForeignGraphs(validRepoIds, home = homedir8()) {
|
|
14142
|
+
const graphsDir = join51(home, ".repowise", "graphs");
|
|
13997
14143
|
const result = { kept: [], removed: [] };
|
|
13998
14144
|
let entries;
|
|
13999
14145
|
try {
|
|
@@ -14011,7 +14157,7 @@ async function purgeForeignGraphs(validRepoIds, home = homedir7()) {
|
|
|
14011
14157
|
result.kept.push(entry);
|
|
14012
14158
|
continue;
|
|
14013
14159
|
}
|
|
14014
|
-
const path =
|
|
14160
|
+
const path = join51(graphsDir, entry);
|
|
14015
14161
|
try {
|
|
14016
14162
|
const stat8 = await fs23.lstat(path);
|
|
14017
14163
|
if (stat8.isSymbolicLink()) {
|
|
@@ -14104,11 +14250,11 @@ async function logout() {
|
|
|
14104
14250
|
|
|
14105
14251
|
// src/commands/status.ts
|
|
14106
14252
|
import { readFile as readFile16 } from "fs/promises";
|
|
14107
|
-
import { basename as basename4, join as
|
|
14253
|
+
import { basename as basename4, join as join52 } from "path";
|
|
14108
14254
|
async function status() {
|
|
14109
14255
|
const configDir = getConfigDir2();
|
|
14110
|
-
const STATE_PATH =
|
|
14111
|
-
const CONFIG_PATH =
|
|
14256
|
+
const STATE_PATH = join52(configDir, "listener-state.json");
|
|
14257
|
+
const CONFIG_PATH = join52(configDir, "config.json");
|
|
14112
14258
|
let state = null;
|
|
14113
14259
|
try {
|
|
14114
14260
|
const data = await readFile16(STATE_PATH, "utf-8");
|
|
@@ -14156,7 +14302,7 @@ async function status() {
|
|
|
14156
14302
|
|
|
14157
14303
|
// src/commands/sync.ts
|
|
14158
14304
|
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
14159
|
-
import { dirname as
|
|
14305
|
+
import { dirname as dirname21, join as join53 } from "path";
|
|
14160
14306
|
import chalk12 from "chalk";
|
|
14161
14307
|
import ora4 from "ora";
|
|
14162
14308
|
var POLL_INTERVAL_MS2 = 3e3;
|
|
@@ -14305,7 +14451,7 @@ async function sync() {
|
|
|
14305
14451
|
const listResult = await apiRequest(`/v1/repos/${repoId}/context`);
|
|
14306
14452
|
const files = listResult.data?.files ?? listResult.files ?? [];
|
|
14307
14453
|
if (files.length > 0) {
|
|
14308
|
-
const contextDir =
|
|
14454
|
+
const contextDir = join53(repoRoot, DEFAULT_CONTEXT_FOLDER3);
|
|
14309
14455
|
mkdirSync4(contextDir, { recursive: true });
|
|
14310
14456
|
let downloadedCount = 0;
|
|
14311
14457
|
let failedCount = 0;
|
|
@@ -14319,8 +14465,8 @@ async function sync() {
|
|
|
14319
14465
|
const response = await fetch(presignedUrl);
|
|
14320
14466
|
if (response.ok) {
|
|
14321
14467
|
const content = await response.text();
|
|
14322
|
-
const filePath =
|
|
14323
|
-
mkdirSync4(
|
|
14468
|
+
const filePath = join53(contextDir, file.fileName);
|
|
14469
|
+
mkdirSync4(dirname21(filePath), { recursive: true });
|
|
14324
14470
|
writeFileSync5(filePath, content, "utf-8");
|
|
14325
14471
|
downloadedCount++;
|
|
14326
14472
|
} else {
|
|
@@ -14575,7 +14721,7 @@ async function config() {
|
|
|
14575
14721
|
// src/commands/mcp-log.ts
|
|
14576
14722
|
import { createDecipheriv as createDecipheriv2 } from "crypto";
|
|
14577
14723
|
import { mkdir as mkdir19, readFile as readFile17, stat as stat7, writeFile as writeFile18 } from "fs/promises";
|
|
14578
|
-
import { dirname as
|
|
14724
|
+
import { dirname as dirname22, join as join54 } from "path";
|
|
14579
14725
|
var FLAG_FILE = "mcp-log.flag";
|
|
14580
14726
|
var LOG_FILE = "mcp-log.jsonl.enc";
|
|
14581
14727
|
var KEY_FILE = "mcp-log.key";
|
|
@@ -14583,14 +14729,14 @@ var ENDPOINT_FILE = "listener.endpoint";
|
|
|
14583
14729
|
var IV_BYTES2 = 12;
|
|
14584
14730
|
var TAG_BYTES2 = 16;
|
|
14585
14731
|
function flagPath() {
|
|
14586
|
-
return
|
|
14732
|
+
return join54(getConfigDir2(), FLAG_FILE);
|
|
14587
14733
|
}
|
|
14588
14734
|
function logPath() {
|
|
14589
|
-
return
|
|
14735
|
+
return join54(getConfigDir2(), LOG_FILE);
|
|
14590
14736
|
}
|
|
14591
14737
|
async function writeFlag(flag) {
|
|
14592
14738
|
const path = flagPath();
|
|
14593
|
-
await mkdir19(
|
|
14739
|
+
await mkdir19(dirname22(path), { recursive: true });
|
|
14594
14740
|
await writeFile18(path, JSON.stringify(flag, null, 2), { encoding: "utf-8", mode: 384 });
|
|
14595
14741
|
}
|
|
14596
14742
|
async function mcpLogOn() {
|
|
@@ -14627,14 +14773,14 @@ async function trySendConsentToServer() {
|
|
|
14627
14773
|
let apiUrl = null;
|
|
14628
14774
|
let token = null;
|
|
14629
14775
|
try {
|
|
14630
|
-
const body = await readFile17(
|
|
14776
|
+
const body = await readFile17(join54(getConfigDir2(), "config.json"), "utf-8");
|
|
14631
14777
|
const parsed = JSON.parse(body);
|
|
14632
14778
|
apiUrl = parsed.repos?.find((r) => Boolean(r.apiUrl))?.apiUrl ?? parsed.defaultApiUrl ?? null;
|
|
14633
14779
|
} catch {
|
|
14634
14780
|
return false;
|
|
14635
14781
|
}
|
|
14636
14782
|
try {
|
|
14637
|
-
const body = await readFile17(
|
|
14783
|
+
const body = await readFile17(join54(getConfigDir2(), "credentials.json"), "utf-8");
|
|
14638
14784
|
const parsed = JSON.parse(body);
|
|
14639
14785
|
token = parsed.idToken ?? null;
|
|
14640
14786
|
} catch {
|
|
@@ -14704,7 +14850,7 @@ async function mcpLogStatus() {
|
|
|
14704
14850
|
process.stderr.write("Log size: no file yet\n");
|
|
14705
14851
|
}
|
|
14706
14852
|
try {
|
|
14707
|
-
const endpointBody = await readFile17(
|
|
14853
|
+
const endpointBody = await readFile17(join54(getConfigDir2(), ENDPOINT_FILE), "utf-8");
|
|
14708
14854
|
const match = /endpoint=([^\n]+)/.exec(endpointBody);
|
|
14709
14855
|
process.stderr.write(`MCP endpoint: ${match?.[1] ?? "(malformed endpoint file)"}
|
|
14710
14856
|
`);
|
|
@@ -14735,7 +14881,7 @@ async function mcpLogViewingFlags(flags = {}) {
|
|
|
14735
14881
|
const key = await readKey();
|
|
14736
14882
|
if (!key) {
|
|
14737
14883
|
process.stderr.write(
|
|
14738
|
-
`No encryption key at ${
|
|
14884
|
+
`No encryption key at ${join54(getConfigDir2(), KEY_FILE)} \u2014 listener may not have started yet.
|
|
14739
14885
|
`
|
|
14740
14886
|
);
|
|
14741
14887
|
return;
|
|
@@ -14811,7 +14957,7 @@ async function mcpLogViewingFlags(flags = {}) {
|
|
|
14811
14957
|
}
|
|
14812
14958
|
async function readKey() {
|
|
14813
14959
|
try {
|
|
14814
|
-
const body = await readFile17(
|
|
14960
|
+
const body = await readFile17(join54(getConfigDir2(), KEY_FILE), "utf-8");
|
|
14815
14961
|
const parsed = Buffer.from(body.trim(), "base64");
|
|
14816
14962
|
if (parsed.length !== 32) return null;
|
|
14817
14963
|
return parsed;
|
|
@@ -14855,7 +15001,7 @@ import chalk14 from "chalk";
|
|
|
14855
15001
|
|
|
14856
15002
|
// src/lib/graph-loader.ts
|
|
14857
15003
|
import { promises as fs24 } from "fs";
|
|
14858
|
-
import { join as
|
|
15004
|
+
import { join as join55, resolve as resolve2 } from "path";
|
|
14859
15005
|
import { gunzipSync } from "zlib";
|
|
14860
15006
|
var RELATIVE_GRAPH_PATH = "repowise-context/.meta/dependency-graph.json";
|
|
14861
15007
|
var GZIPPED_GRAPH_PATH = "repowise-context/.meta/dependency-graph.json.gz";
|
|
@@ -14872,8 +15018,8 @@ var GraphNotFoundError = class extends Error {
|
|
|
14872
15018
|
var cache = /* @__PURE__ */ new Map();
|
|
14873
15019
|
async function loadGraph(repoRoot = process.cwd()) {
|
|
14874
15020
|
const root = resolve2(repoRoot);
|
|
14875
|
-
const gzPath =
|
|
14876
|
-
const plainPath =
|
|
15021
|
+
const gzPath = join55(root, GZIPPED_GRAPH_PATH);
|
|
15022
|
+
const plainPath = join55(root, RELATIVE_GRAPH_PATH);
|
|
14877
15023
|
const cached = cache.get(root);
|
|
14878
15024
|
if (cached) {
|
|
14879
15025
|
return { graph: cached, path: plainPath, bytes: 0, parseMs: 0, fromCache: true };
|
|
@@ -15288,13 +15434,13 @@ function registerQueryCommand(program2) {
|
|
|
15288
15434
|
|
|
15289
15435
|
// src/commands/uninstall.ts
|
|
15290
15436
|
import { promises as fs28 } from "fs";
|
|
15291
|
-
import { homedir as
|
|
15292
|
-
import { join as
|
|
15437
|
+
import { homedir as homedir10 } from "os";
|
|
15438
|
+
import { join as join59 } from "path";
|
|
15293
15439
|
import chalk15 from "chalk";
|
|
15294
15440
|
|
|
15295
15441
|
// src/lib/cleanup/marker-blocks.ts
|
|
15296
15442
|
import { promises as fs25 } from "fs";
|
|
15297
|
-
import { join as
|
|
15443
|
+
import { join as join56 } from "path";
|
|
15298
15444
|
var MARKER_START = "<!-- repowise-start -->";
|
|
15299
15445
|
var MARKER_END = "<!-- repowise-end -->";
|
|
15300
15446
|
var CONTEXT_FILES = [
|
|
@@ -15334,7 +15480,7 @@ async function stripMarkerBlock(filePath) {
|
|
|
15334
15480
|
async function stripAllMarkerBlocks(repoRoot) {
|
|
15335
15481
|
const out = [];
|
|
15336
15482
|
for (const relative of CONTEXT_FILES) {
|
|
15337
|
-
const full =
|
|
15483
|
+
const full = join56(repoRoot, relative);
|
|
15338
15484
|
const result = await stripMarkerBlock(full).catch((err) => ({
|
|
15339
15485
|
path: full,
|
|
15340
15486
|
status: "untouched",
|
|
@@ -15347,18 +15493,18 @@ async function stripAllMarkerBlocks(repoRoot) {
|
|
|
15347
15493
|
|
|
15348
15494
|
// src/lib/cleanup/mcp-configs.ts
|
|
15349
15495
|
import { promises as fs26 } from "fs";
|
|
15350
|
-
import { join as
|
|
15496
|
+
import { join as join57 } from "path";
|
|
15351
15497
|
function mcpConfigPaths(repoRoot, home) {
|
|
15352
15498
|
return [
|
|
15353
|
-
|
|
15354
|
-
|
|
15355
|
-
|
|
15356
|
-
|
|
15357
|
-
|
|
15358
|
-
|
|
15359
|
-
|
|
15360
|
-
|
|
15361
|
-
|
|
15499
|
+
join57(repoRoot, ".mcp.json"),
|
|
15500
|
+
join57(repoRoot, ".cursor", "mcp.json"),
|
|
15501
|
+
join57(repoRoot, ".vscode", "mcp.json"),
|
|
15502
|
+
join57(repoRoot, ".roo", "mcp.json"),
|
|
15503
|
+
join57(home, ".cline", "mcp.json"),
|
|
15504
|
+
join57(home, ".codeium", "windsurf", "mcp_config.json"),
|
|
15505
|
+
join57(home, ".gemini", "settings.json"),
|
|
15506
|
+
join57(home, ".codex", "mcp.json"),
|
|
15507
|
+
join57(home, ".roo", "mcp.json")
|
|
15362
15508
|
];
|
|
15363
15509
|
}
|
|
15364
15510
|
async function removeRepowiseFromConfig(path, serverName) {
|
|
@@ -15398,11 +15544,11 @@ async function removeAllMcpEntries(repoRoot, home, repoId) {
|
|
|
15398
15544
|
|
|
15399
15545
|
// src/lib/cleanup/local-state.ts
|
|
15400
15546
|
import { promises as fs27 } from "fs";
|
|
15401
|
-
import { homedir as
|
|
15402
|
-
import { join as
|
|
15547
|
+
import { homedir as homedir9 } from "os";
|
|
15548
|
+
import { join as join58, resolve as resolve3 } from "path";
|
|
15403
15549
|
async function clearLocalState(homeOverride) {
|
|
15404
|
-
const home = homeOverride ??
|
|
15405
|
-
const target = resolve3(
|
|
15550
|
+
const home = homeOverride ?? homedir9();
|
|
15551
|
+
const target = resolve3(join58(home, ".repowise"));
|
|
15406
15552
|
if (target === resolve3(home) || !target.startsWith(resolve3(home))) {
|
|
15407
15553
|
return { path: target, status: "error", error: "refused: not under home" };
|
|
15408
15554
|
}
|
|
@@ -15437,7 +15583,7 @@ async function stopAndUninstallService(uninstaller) {
|
|
|
15437
15583
|
// src/commands/uninstall.ts
|
|
15438
15584
|
async function uninstall2(opts = {}) {
|
|
15439
15585
|
const tier = opts.tier ?? "uninstall";
|
|
15440
|
-
const home = opts.home ??
|
|
15586
|
+
const home = opts.home ?? homedir10();
|
|
15441
15587
|
const repoRoot = opts.repoRoot ?? process.cwd();
|
|
15442
15588
|
const loadRepoIds = opts.loadRepoIds ?? defaultLoadRepoIds;
|
|
15443
15589
|
const report = { tier, removed: [], preserved: [], skipped: [] };
|
|
@@ -15446,7 +15592,7 @@ async function uninstall2(opts = {}) {
|
|
|
15446
15592
|
else if (svc.error) report.skipped.push({ path: "listener service", reason: svc.error });
|
|
15447
15593
|
if (tier === "stop") return report;
|
|
15448
15594
|
try {
|
|
15449
|
-
await fs28.unlink(
|
|
15595
|
+
await fs28.unlink(join59(home, ".repowise", "credentials.json"));
|
|
15450
15596
|
report.removed.push("credentials");
|
|
15451
15597
|
} catch (err) {
|
|
15452
15598
|
if (err.code !== "ENOENT") {
|
|
@@ -15485,7 +15631,7 @@ async function uninstall2(opts = {}) {
|
|
|
15485
15631
|
}
|
|
15486
15632
|
async function defaultLoadRepoIds(home) {
|
|
15487
15633
|
try {
|
|
15488
|
-
const raw = await fs28.readFile(
|
|
15634
|
+
const raw = await fs28.readFile(join59(home, ".repowise", "config.json"), "utf-8");
|
|
15489
15635
|
const parsed = JSON.parse(raw);
|
|
15490
15636
|
return (parsed.repos ?? []).map((r) => r.repoId);
|
|
15491
15637
|
} catch {
|
|
@@ -15534,10 +15680,10 @@ Done \u2014 ${report.removed.length} removed, ${report.skipped.length} skipped.
|
|
|
15534
15680
|
init_config_dir();
|
|
15535
15681
|
import { promises as fs29 } from "fs";
|
|
15536
15682
|
import { createInterface as createInterface2 } from "readline";
|
|
15537
|
-
import { join as
|
|
15683
|
+
import { join as join60 } from "path";
|
|
15538
15684
|
var DEFAULT_MAX = 200 * 1024;
|
|
15539
15685
|
async function mcpShim(opts) {
|
|
15540
|
-
const endpointPath = opts.endpointFile ??
|
|
15686
|
+
const endpointPath = opts.endpointFile ?? join60(getConfigDir(), "listener.endpoint");
|
|
15541
15687
|
const stdin = opts.stdin ?? process.stdin;
|
|
15542
15688
|
const stdout = opts.stdout ?? process.stdout;
|
|
15543
15689
|
const stderr = opts.stderr ?? process.stderr;
|
|
@@ -16285,8 +16431,8 @@ async function lspOn() {
|
|
|
16285
16431
|
|
|
16286
16432
|
// bin/repowise.ts
|
|
16287
16433
|
var __filename = fileURLToPath4(import.meta.url);
|
|
16288
|
-
var __dirname =
|
|
16289
|
-
var pkg = JSON.parse(readFileSync3(
|
|
16434
|
+
var __dirname = dirname23(__filename);
|
|
16435
|
+
var pkg = JSON.parse(readFileSync3(join61(__dirname, "..", "..", "package.json"), "utf-8"));
|
|
16290
16436
|
var program = new Command();
|
|
16291
16437
|
program.name(getPackageName()).description("AI-optimized codebase context generator").version(pkg.version).hook("preAction", async () => {
|
|
16292
16438
|
await showWelcome(pkg.version);
|