skalpel 1.0.0 → 1.0.2
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/cli/index.js +433 -348
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/proxy-runner.js +60 -11
- package/dist/cli/proxy-runner.js.map +1 -1
- package/dist/index.cjs +61 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +61 -12
- package/dist/index.js.map +1 -1
- package/dist/proxy/index.cjs +60 -11
- package/dist/proxy/index.cjs.map +1 -1
- package/dist/proxy/index.js +60 -11
- package/dist/proxy/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -481,8 +481,8 @@ async function runReplay(filePaths) {
|
|
|
481
481
|
|
|
482
482
|
// src/cli/start.ts
|
|
483
483
|
import { spawn } from "child_process";
|
|
484
|
-
import
|
|
485
|
-
import { fileURLToPath } from "url";
|
|
484
|
+
import path9 from "path";
|
|
485
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
486
486
|
|
|
487
487
|
// src/proxy/config.ts
|
|
488
488
|
import fs5 from "fs";
|
|
@@ -557,6 +557,345 @@ function removePid(pidFile) {
|
|
|
557
557
|
}
|
|
558
558
|
}
|
|
559
559
|
|
|
560
|
+
// src/cli/service/install.ts
|
|
561
|
+
import fs7 from "fs";
|
|
562
|
+
import path8 from "path";
|
|
563
|
+
import os4 from "os";
|
|
564
|
+
import { execSync as execSync2 } from "child_process";
|
|
565
|
+
import { fileURLToPath } from "url";
|
|
566
|
+
|
|
567
|
+
// src/cli/service/detect-os.ts
|
|
568
|
+
import os2 from "os";
|
|
569
|
+
import { execSync } from "child_process";
|
|
570
|
+
function detectShell() {
|
|
571
|
+
if (process.platform === "win32") {
|
|
572
|
+
if (process.env.PSModulePath || process.env.POWERSHELL_DISTRIBUTION_CHANNEL) {
|
|
573
|
+
return "powershell";
|
|
574
|
+
}
|
|
575
|
+
return "cmd";
|
|
576
|
+
}
|
|
577
|
+
const shellPath = process.env.SHELL ?? "";
|
|
578
|
+
if (shellPath.includes("zsh")) return "zsh";
|
|
579
|
+
if (shellPath.includes("fish")) return "fish";
|
|
580
|
+
if (shellPath.includes("bash")) return "bash";
|
|
581
|
+
try {
|
|
582
|
+
if (process.platform === "darwin") {
|
|
583
|
+
const result = execSync(`dscl . -read /Users/${os2.userInfo().username} UserShell`, {
|
|
584
|
+
encoding: "utf-8",
|
|
585
|
+
timeout: 3e3
|
|
586
|
+
}).trim();
|
|
587
|
+
const shell = result.split(":").pop()?.trim() ?? "";
|
|
588
|
+
if (shell.includes("zsh")) return "zsh";
|
|
589
|
+
if (shell.includes("fish")) return "fish";
|
|
590
|
+
if (shell.includes("bash")) return "bash";
|
|
591
|
+
} else {
|
|
592
|
+
const result = execSync(`getent passwd ${os2.userInfo().username}`, {
|
|
593
|
+
encoding: "utf-8",
|
|
594
|
+
timeout: 3e3
|
|
595
|
+
}).trim();
|
|
596
|
+
const shell = result.split(":").pop() ?? "";
|
|
597
|
+
if (shell.includes("zsh")) return "zsh";
|
|
598
|
+
if (shell.includes("fish")) return "fish";
|
|
599
|
+
if (shell.includes("bash")) return "bash";
|
|
600
|
+
}
|
|
601
|
+
} catch {
|
|
602
|
+
}
|
|
603
|
+
return "bash";
|
|
604
|
+
}
|
|
605
|
+
function detectOS() {
|
|
606
|
+
let platform;
|
|
607
|
+
switch (process.platform) {
|
|
608
|
+
case "darwin":
|
|
609
|
+
platform = "macos";
|
|
610
|
+
break;
|
|
611
|
+
case "win32":
|
|
612
|
+
platform = "windows";
|
|
613
|
+
break;
|
|
614
|
+
default:
|
|
615
|
+
platform = "linux";
|
|
616
|
+
break;
|
|
617
|
+
}
|
|
618
|
+
return {
|
|
619
|
+
platform,
|
|
620
|
+
shell: detectShell(),
|
|
621
|
+
homeDir: os2.homedir()
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// src/cli/service/templates.ts
|
|
626
|
+
import os3 from "os";
|
|
627
|
+
import path7 from "path";
|
|
628
|
+
function generateLaunchdPlist(config, proxyRunnerPath) {
|
|
629
|
+
const logDir = path7.join(os3.homedir(), ".skalpel", "logs");
|
|
630
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
631
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
632
|
+
<plist version="1.0">
|
|
633
|
+
<dict>
|
|
634
|
+
<key>Label</key>
|
|
635
|
+
<string>ai.skalpel.proxy</string>
|
|
636
|
+
<key>ProgramArguments</key>
|
|
637
|
+
<array>
|
|
638
|
+
<string>${process.execPath}</string>
|
|
639
|
+
<string>${proxyRunnerPath}</string>
|
|
640
|
+
</array>
|
|
641
|
+
<key>RunAtLoad</key>
|
|
642
|
+
<true/>
|
|
643
|
+
<key>KeepAlive</key>
|
|
644
|
+
<true/>
|
|
645
|
+
<key>StandardOutPath</key>
|
|
646
|
+
<string>${path7.join(logDir, "proxy-stdout.log")}</string>
|
|
647
|
+
<key>StandardErrorPath</key>
|
|
648
|
+
<string>${path7.join(logDir, "proxy-stderr.log")}</string>
|
|
649
|
+
<key>EnvironmentVariables</key>
|
|
650
|
+
<dict>
|
|
651
|
+
<key>SKALPEL_ANTHROPIC_PORT</key>
|
|
652
|
+
<string>${config.anthropicPort}</string>
|
|
653
|
+
<key>SKALPEL_OPENAI_PORT</key>
|
|
654
|
+
<string>${config.openaiPort}</string>
|
|
655
|
+
</dict>
|
|
656
|
+
</dict>
|
|
657
|
+
</plist>`;
|
|
658
|
+
}
|
|
659
|
+
function generateSystemdUnit(config, proxyRunnerPath) {
|
|
660
|
+
return `[Unit]
|
|
661
|
+
Description=Skalpel Proxy
|
|
662
|
+
After=network.target
|
|
663
|
+
|
|
664
|
+
[Service]
|
|
665
|
+
Type=simple
|
|
666
|
+
ExecStart=${process.execPath} ${proxyRunnerPath}
|
|
667
|
+
Restart=always
|
|
668
|
+
RestartSec=5
|
|
669
|
+
Environment=SKALPEL_ANTHROPIC_PORT=${config.anthropicPort}
|
|
670
|
+
Environment=SKALPEL_OPENAI_PORT=${config.openaiPort}
|
|
671
|
+
|
|
672
|
+
[Install]
|
|
673
|
+
WantedBy=default.target`;
|
|
674
|
+
}
|
|
675
|
+
function generateWindowsTask(config, proxyRunnerPath) {
|
|
676
|
+
return [
|
|
677
|
+
"/create",
|
|
678
|
+
"/tn",
|
|
679
|
+
"SkalpelProxy",
|
|
680
|
+
"/tr",
|
|
681
|
+
`"${process.execPath}" "${proxyRunnerPath}"`,
|
|
682
|
+
"/sc",
|
|
683
|
+
"ONLOGON",
|
|
684
|
+
"/rl",
|
|
685
|
+
"LIMITED",
|
|
686
|
+
"/f"
|
|
687
|
+
];
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// src/cli/service/install.ts
|
|
691
|
+
var __dirname = path8.dirname(fileURLToPath(import.meta.url));
|
|
692
|
+
function resolveProxyRunnerPath() {
|
|
693
|
+
const candidates = [
|
|
694
|
+
path8.join(__dirname, "..", "proxy-runner.js"),
|
|
695
|
+
// dist/cli/proxy-runner.js relative to dist/cli/service/
|
|
696
|
+
path8.join(__dirname, "proxy-runner.js"),
|
|
697
|
+
// same dir
|
|
698
|
+
path8.join(__dirname, "..", "..", "cli", "proxy-runner.js")
|
|
699
|
+
// dist/cli/proxy-runner.js from deeper
|
|
700
|
+
];
|
|
701
|
+
for (const candidate of candidates) {
|
|
702
|
+
if (fs7.existsSync(candidate)) {
|
|
703
|
+
return path8.resolve(candidate);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
try {
|
|
707
|
+
const npmRoot = execSync2("npm root -g", { encoding: "utf-8" }).trim();
|
|
708
|
+
const globalPath = path8.join(npmRoot, "skalpel", "dist", "cli", "proxy-runner.js");
|
|
709
|
+
if (fs7.existsSync(globalPath)) return globalPath;
|
|
710
|
+
} catch {
|
|
711
|
+
}
|
|
712
|
+
const devPath = path8.resolve(process.cwd(), "dist", "cli", "proxy-runner.js");
|
|
713
|
+
return devPath;
|
|
714
|
+
}
|
|
715
|
+
function getMacOSPlistPath() {
|
|
716
|
+
return path8.join(os4.homedir(), "Library", "LaunchAgents", "ai.skalpel.proxy.plist");
|
|
717
|
+
}
|
|
718
|
+
function getLinuxUnitPath() {
|
|
719
|
+
return path8.join(os4.homedir(), ".config", "systemd", "user", "skalpel-proxy.service");
|
|
720
|
+
}
|
|
721
|
+
function installService(config) {
|
|
722
|
+
const osInfo = detectOS();
|
|
723
|
+
const proxyRunnerPath = resolveProxyRunnerPath();
|
|
724
|
+
const logDir = path8.join(os4.homedir(), ".skalpel", "logs");
|
|
725
|
+
fs7.mkdirSync(logDir, { recursive: true });
|
|
726
|
+
switch (osInfo.platform) {
|
|
727
|
+
case "macos": {
|
|
728
|
+
const plistPath = getMacOSPlistPath();
|
|
729
|
+
const plistDir = path8.dirname(plistPath);
|
|
730
|
+
fs7.mkdirSync(plistDir, { recursive: true });
|
|
731
|
+
const plist = generateLaunchdPlist(config, proxyRunnerPath);
|
|
732
|
+
fs7.writeFileSync(plistPath, plist);
|
|
733
|
+
try {
|
|
734
|
+
execSync2(`launchctl unload "${plistPath}" 2>/dev/null || true`, { stdio: "pipe" });
|
|
735
|
+
execSync2(`launchctl load "${plistPath}"`, { stdio: "pipe" });
|
|
736
|
+
} catch (err) {
|
|
737
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
738
|
+
console.warn(` Warning: Could not register launchd service: ${msg}`);
|
|
739
|
+
console.warn(` You can manually load it: launchctl load "${plistPath}"`);
|
|
740
|
+
}
|
|
741
|
+
break;
|
|
742
|
+
}
|
|
743
|
+
case "linux": {
|
|
744
|
+
const unitPath = getLinuxUnitPath();
|
|
745
|
+
const unitDir = path8.dirname(unitPath);
|
|
746
|
+
fs7.mkdirSync(unitDir, { recursive: true });
|
|
747
|
+
const unit = generateSystemdUnit(config, proxyRunnerPath);
|
|
748
|
+
fs7.writeFileSync(unitPath, unit);
|
|
749
|
+
try {
|
|
750
|
+
execSync2("systemctl --user daemon-reload", { stdio: "pipe" });
|
|
751
|
+
execSync2("systemctl --user enable skalpel-proxy", { stdio: "pipe" });
|
|
752
|
+
execSync2("systemctl --user start skalpel-proxy", { stdio: "pipe" });
|
|
753
|
+
} catch {
|
|
754
|
+
try {
|
|
755
|
+
const autostartDir = path8.join(os4.homedir(), ".config", "autostart");
|
|
756
|
+
fs7.mkdirSync(autostartDir, { recursive: true });
|
|
757
|
+
const desktopEntry = `[Desktop Entry]
|
|
758
|
+
Type=Application
|
|
759
|
+
Name=Skalpel Proxy
|
|
760
|
+
Exec=${process.execPath} ${proxyRunnerPath}
|
|
761
|
+
Hidden=false
|
|
762
|
+
NoDisplay=true
|
|
763
|
+
X-GNOME-Autostart-enabled=true
|
|
764
|
+
`;
|
|
765
|
+
fs7.writeFileSync(path8.join(autostartDir, "skalpel-proxy.desktop"), desktopEntry);
|
|
766
|
+
console.warn(" Warning: systemd --user not available. Created .desktop autostart entry instead.");
|
|
767
|
+
} catch (err2) {
|
|
768
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
769
|
+
console.warn(` Warning: Could not register service: ${msg}`);
|
|
770
|
+
console.warn(" You can start the proxy manually: skalpel start");
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
break;
|
|
774
|
+
}
|
|
775
|
+
case "windows": {
|
|
776
|
+
const args = generateWindowsTask(config, proxyRunnerPath);
|
|
777
|
+
try {
|
|
778
|
+
execSync2(`schtasks ${args.join(" ")}`, { stdio: "pipe" });
|
|
779
|
+
} catch (err) {
|
|
780
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
781
|
+
console.warn(` Warning: Could not create scheduled task: ${msg}`);
|
|
782
|
+
console.warn(" You can start the proxy manually: skalpel start");
|
|
783
|
+
}
|
|
784
|
+
break;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
function isServiceInstalled() {
|
|
789
|
+
const osInfo = detectOS();
|
|
790
|
+
switch (osInfo.platform) {
|
|
791
|
+
case "macos": {
|
|
792
|
+
const plistPath = getMacOSPlistPath();
|
|
793
|
+
return fs7.existsSync(plistPath);
|
|
794
|
+
}
|
|
795
|
+
case "linux": {
|
|
796
|
+
const unitPath = getLinuxUnitPath();
|
|
797
|
+
return fs7.existsSync(unitPath);
|
|
798
|
+
}
|
|
799
|
+
case "windows": {
|
|
800
|
+
try {
|
|
801
|
+
execSync2("schtasks /query /tn SkalpelProxy", { stdio: "pipe" });
|
|
802
|
+
return true;
|
|
803
|
+
} catch {
|
|
804
|
+
return false;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
function stopService() {
|
|
810
|
+
const osInfo = detectOS();
|
|
811
|
+
switch (osInfo.platform) {
|
|
812
|
+
case "macos": {
|
|
813
|
+
const plistPath = getMacOSPlistPath();
|
|
814
|
+
if (!fs7.existsSync(plistPath)) return;
|
|
815
|
+
try {
|
|
816
|
+
execSync2(`launchctl unload "${plistPath}"`, { stdio: "pipe" });
|
|
817
|
+
} catch {
|
|
818
|
+
}
|
|
819
|
+
break;
|
|
820
|
+
}
|
|
821
|
+
case "linux": {
|
|
822
|
+
try {
|
|
823
|
+
execSync2("systemctl --user stop skalpel-proxy", { stdio: "pipe" });
|
|
824
|
+
} catch {
|
|
825
|
+
}
|
|
826
|
+
break;
|
|
827
|
+
}
|
|
828
|
+
case "windows": {
|
|
829
|
+
try {
|
|
830
|
+
execSync2("schtasks /end /tn SkalpelProxy", { stdio: "pipe" });
|
|
831
|
+
} catch {
|
|
832
|
+
}
|
|
833
|
+
break;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
function startService() {
|
|
838
|
+
const osInfo = detectOS();
|
|
839
|
+
switch (osInfo.platform) {
|
|
840
|
+
case "macos": {
|
|
841
|
+
const plistPath = getMacOSPlistPath();
|
|
842
|
+
if (!fs7.existsSync(plistPath)) return;
|
|
843
|
+
try {
|
|
844
|
+
execSync2(`launchctl load "${plistPath}"`, { stdio: "pipe" });
|
|
845
|
+
} catch {
|
|
846
|
+
}
|
|
847
|
+
break;
|
|
848
|
+
}
|
|
849
|
+
case "linux": {
|
|
850
|
+
try {
|
|
851
|
+
execSync2("systemctl --user start skalpel-proxy", { stdio: "pipe" });
|
|
852
|
+
} catch {
|
|
853
|
+
}
|
|
854
|
+
break;
|
|
855
|
+
}
|
|
856
|
+
case "windows": {
|
|
857
|
+
try {
|
|
858
|
+
execSync2("schtasks /run /tn SkalpelProxy", { stdio: "pipe" });
|
|
859
|
+
} catch {
|
|
860
|
+
}
|
|
861
|
+
break;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
function uninstallService() {
|
|
866
|
+
const osInfo = detectOS();
|
|
867
|
+
switch (osInfo.platform) {
|
|
868
|
+
case "macos": {
|
|
869
|
+
const plistPath = getMacOSPlistPath();
|
|
870
|
+
try {
|
|
871
|
+
execSync2(`launchctl unload "${plistPath}" 2>/dev/null || true`, { stdio: "pipe" });
|
|
872
|
+
} catch {
|
|
873
|
+
}
|
|
874
|
+
if (fs7.existsSync(plistPath)) fs7.unlinkSync(plistPath);
|
|
875
|
+
break;
|
|
876
|
+
}
|
|
877
|
+
case "linux": {
|
|
878
|
+
try {
|
|
879
|
+
execSync2("systemctl --user stop skalpel-proxy 2>/dev/null || true", { stdio: "pipe" });
|
|
880
|
+
execSync2("systemctl --user disable skalpel-proxy 2>/dev/null || true", { stdio: "pipe" });
|
|
881
|
+
} catch {
|
|
882
|
+
}
|
|
883
|
+
const unitPath = getLinuxUnitPath();
|
|
884
|
+
if (fs7.existsSync(unitPath)) fs7.unlinkSync(unitPath);
|
|
885
|
+
const desktopPath = path8.join(os4.homedir(), ".config", "autostart", "skalpel-proxy.desktop");
|
|
886
|
+
if (fs7.existsSync(desktopPath)) fs7.unlinkSync(desktopPath);
|
|
887
|
+
break;
|
|
888
|
+
}
|
|
889
|
+
case "windows": {
|
|
890
|
+
try {
|
|
891
|
+
execSync2("schtasks /delete /tn SkalpelProxy /f", { stdio: "pipe" });
|
|
892
|
+
} catch {
|
|
893
|
+
}
|
|
894
|
+
break;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
560
899
|
// src/cli/start.ts
|
|
561
900
|
function print5(msg) {
|
|
562
901
|
console.log(msg);
|
|
@@ -572,8 +911,13 @@ async function runStart() {
|
|
|
572
911
|
print5(` Proxy is already running (pid=${existingPid}).`);
|
|
573
912
|
return;
|
|
574
913
|
}
|
|
575
|
-
|
|
576
|
-
|
|
914
|
+
if (isServiceInstalled()) {
|
|
915
|
+
startService();
|
|
916
|
+
print5(` Skalpel proxy started via system service on ports ${config.anthropicPort} and ${config.openaiPort}`);
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
const dirname = path9.dirname(fileURLToPath2(import.meta.url));
|
|
920
|
+
const runnerScript = path9.resolve(dirname, "proxy-runner.js");
|
|
577
921
|
const child = spawn(process.execPath, [runnerScript], {
|
|
578
922
|
detached: true,
|
|
579
923
|
stdio: "ignore"
|
|
@@ -586,8 +930,8 @@ async function runStart() {
|
|
|
586
930
|
import http from "http";
|
|
587
931
|
|
|
588
932
|
// src/proxy/logger.ts
|
|
589
|
-
import
|
|
590
|
-
import
|
|
933
|
+
import fs8 from "fs";
|
|
934
|
+
import path10 from "path";
|
|
591
935
|
var MAX_SIZE = 5 * 1024 * 1024;
|
|
592
936
|
|
|
593
937
|
// src/proxy/server.ts
|
|
@@ -619,6 +963,9 @@ function print6(msg) {
|
|
|
619
963
|
}
|
|
620
964
|
async function runStop() {
|
|
621
965
|
const config = loadConfig();
|
|
966
|
+
if (isServiceInstalled()) {
|
|
967
|
+
stopService();
|
|
968
|
+
}
|
|
622
969
|
const stopped = stopProxy(config);
|
|
623
970
|
if (stopped) {
|
|
624
971
|
print6(" Skalpel proxy stopped.");
|
|
@@ -648,7 +995,7 @@ async function runStatus() {
|
|
|
648
995
|
}
|
|
649
996
|
|
|
650
997
|
// src/cli/logs.ts
|
|
651
|
-
import
|
|
998
|
+
import fs9 from "fs";
|
|
652
999
|
function print8(msg) {
|
|
653
1000
|
console.log(msg);
|
|
654
1001
|
}
|
|
@@ -656,26 +1003,26 @@ async function runLogs(options) {
|
|
|
656
1003
|
const config = loadConfig();
|
|
657
1004
|
const logFile = config.logFile;
|
|
658
1005
|
const lineCount = parseInt(options.lines ?? "50", 10);
|
|
659
|
-
if (!
|
|
1006
|
+
if (!fs9.existsSync(logFile)) {
|
|
660
1007
|
print8(` No log file found at ${logFile}`);
|
|
661
1008
|
return;
|
|
662
1009
|
}
|
|
663
|
-
const content =
|
|
1010
|
+
const content = fs9.readFileSync(logFile, "utf-8");
|
|
664
1011
|
const lines = content.trimEnd().split("\n");
|
|
665
1012
|
const tail = lines.slice(-lineCount);
|
|
666
1013
|
for (const line of tail) {
|
|
667
1014
|
print8(line);
|
|
668
1015
|
}
|
|
669
1016
|
if (options.follow) {
|
|
670
|
-
let position =
|
|
671
|
-
|
|
1017
|
+
let position = fs9.statSync(logFile).size;
|
|
1018
|
+
fs9.watchFile(logFile, { interval: 500 }, () => {
|
|
672
1019
|
try {
|
|
673
|
-
const stat =
|
|
1020
|
+
const stat = fs9.statSync(logFile);
|
|
674
1021
|
if (stat.size > position) {
|
|
675
|
-
const fd =
|
|
1022
|
+
const fd = fs9.openSync(logFile, "r");
|
|
676
1023
|
const buf = Buffer.alloc(stat.size - position);
|
|
677
|
-
|
|
678
|
-
|
|
1024
|
+
fs9.readSync(fd, buf, 0, buf.length, position);
|
|
1025
|
+
fs9.closeSync(fd);
|
|
679
1026
|
process.stdout.write(buf.toString("utf-8"));
|
|
680
1027
|
position = stat.size;
|
|
681
1028
|
}
|
|
@@ -786,16 +1133,16 @@ import * as os8 from "os";
|
|
|
786
1133
|
import { execSync as execSync4 } from "child_process";
|
|
787
1134
|
|
|
788
1135
|
// src/cli/agents/detect.ts
|
|
789
|
-
import { execSync } from "child_process";
|
|
790
|
-
import
|
|
791
|
-
import
|
|
792
|
-
import
|
|
1136
|
+
import { execSync as execSync3 } from "child_process";
|
|
1137
|
+
import fs10 from "fs";
|
|
1138
|
+
import path11 from "path";
|
|
1139
|
+
import os5 from "os";
|
|
793
1140
|
function whichCommand() {
|
|
794
1141
|
return process.platform === "win32" ? "where" : "which";
|
|
795
1142
|
}
|
|
796
1143
|
function tryExec(cmd) {
|
|
797
1144
|
try {
|
|
798
|
-
return
|
|
1145
|
+
return execSync3(cmd, { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
799
1146
|
} catch {
|
|
800
1147
|
return null;
|
|
801
1148
|
}
|
|
@@ -809,8 +1156,8 @@ function detectClaudeCode() {
|
|
|
809
1156
|
};
|
|
810
1157
|
const binaryPath = tryExec(`${whichCommand()} claude`);
|
|
811
1158
|
const hasBinary = binaryPath !== null && binaryPath.length > 0;
|
|
812
|
-
const claudeDir =
|
|
813
|
-
const hasConfigDir =
|
|
1159
|
+
const claudeDir = path11.join(os5.homedir(), ".claude");
|
|
1160
|
+
const hasConfigDir = fs10.existsSync(claudeDir);
|
|
814
1161
|
agent.installed = hasBinary || hasConfigDir;
|
|
815
1162
|
if (hasBinary) {
|
|
816
1163
|
const versionOutput = tryExec("claude --version");
|
|
@@ -819,8 +1166,8 @@ function detectClaudeCode() {
|
|
|
819
1166
|
agent.version = match ? match[1] : versionOutput;
|
|
820
1167
|
}
|
|
821
1168
|
}
|
|
822
|
-
const settingsPath =
|
|
823
|
-
if (
|
|
1169
|
+
const settingsPath = path11.join(claudeDir, "settings.json");
|
|
1170
|
+
if (fs10.existsSync(settingsPath)) {
|
|
824
1171
|
agent.configPath = settingsPath;
|
|
825
1172
|
} else if (hasConfigDir) {
|
|
826
1173
|
agent.configPath = settingsPath;
|
|
@@ -836,8 +1183,8 @@ function detectCodex() {
|
|
|
836
1183
|
};
|
|
837
1184
|
const binaryPath = tryExec(`${whichCommand()} codex`);
|
|
838
1185
|
const hasBinary = binaryPath !== null && binaryPath.length > 0;
|
|
839
|
-
const codexConfigDir = process.platform === "win32" ?
|
|
840
|
-
const hasConfigDir =
|
|
1186
|
+
const codexConfigDir = process.platform === "win32" ? path11.join(os5.homedir(), "AppData", "Roaming", "codex") : path11.join(os5.homedir(), ".codex");
|
|
1187
|
+
const hasConfigDir = fs10.existsSync(codexConfigDir);
|
|
841
1188
|
agent.installed = hasBinary || hasConfigDir;
|
|
842
1189
|
if (hasBinary) {
|
|
843
1190
|
const versionOutput = tryExec("codex --version");
|
|
@@ -846,8 +1193,8 @@ function detectCodex() {
|
|
|
846
1193
|
agent.version = match ? match[1] : versionOutput;
|
|
847
1194
|
}
|
|
848
1195
|
}
|
|
849
|
-
const configFile =
|
|
850
|
-
if (
|
|
1196
|
+
const configFile = path11.join(codexConfigDir, "config.json");
|
|
1197
|
+
if (fs10.existsSync(configFile)) {
|
|
851
1198
|
agent.configPath = configFile;
|
|
852
1199
|
} else if (hasConfigDir) {
|
|
853
1200
|
agent.configPath = configFile;
|
|
@@ -859,31 +1206,31 @@ function detectAgents() {
|
|
|
859
1206
|
}
|
|
860
1207
|
|
|
861
1208
|
// src/cli/agents/shell.ts
|
|
862
|
-
import
|
|
863
|
-
import
|
|
864
|
-
import
|
|
1209
|
+
import fs11 from "fs";
|
|
1210
|
+
import path12 from "path";
|
|
1211
|
+
import os6 from "os";
|
|
865
1212
|
var BEGIN_MARKER = "# BEGIN SKALPEL PROXY - do not edit manually";
|
|
866
1213
|
var END_MARKER = "# END SKALPEL PROXY";
|
|
867
1214
|
var PS_BEGIN_MARKER = "# BEGIN SKALPEL PROXY - do not edit manually";
|
|
868
1215
|
var PS_END_MARKER = "# END SKALPEL PROXY";
|
|
869
1216
|
function getUnixProfilePaths() {
|
|
870
|
-
const home =
|
|
1217
|
+
const home = os6.homedir();
|
|
871
1218
|
const candidates = [
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
1219
|
+
path12.join(home, ".bashrc"),
|
|
1220
|
+
path12.join(home, ".zshrc"),
|
|
1221
|
+
path12.join(home, ".bash_profile"),
|
|
1222
|
+
path12.join(home, ".profile")
|
|
876
1223
|
];
|
|
877
|
-
return candidates.filter((p) =>
|
|
1224
|
+
return candidates.filter((p) => fs11.existsSync(p));
|
|
878
1225
|
}
|
|
879
1226
|
function getPowerShellProfilePath() {
|
|
880
1227
|
if (process.platform !== "win32") return null;
|
|
881
1228
|
if (process.env.PROFILE) return process.env.PROFILE;
|
|
882
|
-
const docsDir =
|
|
883
|
-
const psProfile =
|
|
884
|
-
const wpProfile =
|
|
885
|
-
if (
|
|
886
|
-
if (
|
|
1229
|
+
const docsDir = path12.join(os6.homedir(), "Documents");
|
|
1230
|
+
const psProfile = path12.join(docsDir, "PowerShell", "Microsoft.PowerShell_profile.ps1");
|
|
1231
|
+
const wpProfile = path12.join(docsDir, "WindowsPowerShell", "Microsoft.PowerShell_profile.ps1");
|
|
1232
|
+
if (fs11.existsSync(psProfile)) return psProfile;
|
|
1233
|
+
if (fs11.existsSync(wpProfile)) return wpProfile;
|
|
887
1234
|
return psProfile;
|
|
888
1235
|
}
|
|
889
1236
|
function generateUnixBlock(proxyConfig) {
|
|
@@ -904,13 +1251,13 @@ function generatePowerShellBlock(proxyConfig) {
|
|
|
904
1251
|
}
|
|
905
1252
|
function createBackup(filePath) {
|
|
906
1253
|
const backupPath = `${filePath}.skalpel-backup`;
|
|
907
|
-
|
|
1254
|
+
fs11.copyFileSync(filePath, backupPath);
|
|
908
1255
|
}
|
|
909
1256
|
function updateProfileFile(filePath, block, beginMarker, endMarker) {
|
|
910
|
-
if (
|
|
1257
|
+
if (fs11.existsSync(filePath)) {
|
|
911
1258
|
createBackup(filePath);
|
|
912
1259
|
}
|
|
913
|
-
let content =
|
|
1260
|
+
let content = fs11.existsSync(filePath) ? fs11.readFileSync(filePath, "utf-8") : "";
|
|
914
1261
|
const beginIdx = content.indexOf(beginMarker);
|
|
915
1262
|
const endIdx = content.indexOf(endMarker);
|
|
916
1263
|
if (beginIdx !== -1 && endIdx !== -1) {
|
|
@@ -923,15 +1270,15 @@ function updateProfileFile(filePath, block, beginMarker, endMarker) {
|
|
|
923
1270
|
content = block + "\n";
|
|
924
1271
|
}
|
|
925
1272
|
}
|
|
926
|
-
|
|
1273
|
+
fs11.writeFileSync(filePath, content);
|
|
927
1274
|
}
|
|
928
1275
|
function configureShellEnvVars(_agents, proxyConfig) {
|
|
929
1276
|
const modified = [];
|
|
930
1277
|
if (process.platform === "win32") {
|
|
931
1278
|
const psProfile = getPowerShellProfilePath();
|
|
932
1279
|
if (psProfile) {
|
|
933
|
-
const dir =
|
|
934
|
-
|
|
1280
|
+
const dir = path12.dirname(psProfile);
|
|
1281
|
+
fs11.mkdirSync(dir, { recursive: true });
|
|
935
1282
|
const block = generatePowerShellBlock(proxyConfig);
|
|
936
1283
|
updateProfileFile(psProfile, block, PS_BEGIN_MARKER, PS_END_MARKER);
|
|
937
1284
|
modified.push(psProfile);
|
|
@@ -948,32 +1295,32 @@ function configureShellEnvVars(_agents, proxyConfig) {
|
|
|
948
1295
|
}
|
|
949
1296
|
function removeShellEnvVars() {
|
|
950
1297
|
const restored = [];
|
|
951
|
-
const home =
|
|
1298
|
+
const home = os6.homedir();
|
|
952
1299
|
const allProfiles = [
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
1300
|
+
path12.join(home, ".bashrc"),
|
|
1301
|
+
path12.join(home, ".zshrc"),
|
|
1302
|
+
path12.join(home, ".bash_profile"),
|
|
1303
|
+
path12.join(home, ".profile")
|
|
957
1304
|
];
|
|
958
1305
|
if (process.platform === "win32") {
|
|
959
1306
|
const psProfile = getPowerShellProfilePath();
|
|
960
1307
|
if (psProfile) allProfiles.push(psProfile);
|
|
961
1308
|
}
|
|
962
1309
|
for (const profilePath of allProfiles) {
|
|
963
|
-
if (!
|
|
964
|
-
const content =
|
|
1310
|
+
if (!fs11.existsSync(profilePath)) continue;
|
|
1311
|
+
const content = fs11.readFileSync(profilePath, "utf-8");
|
|
965
1312
|
const beginIdx = content.indexOf(BEGIN_MARKER);
|
|
966
1313
|
const endIdx = content.indexOf(END_MARKER);
|
|
967
1314
|
if (beginIdx === -1 || endIdx === -1) continue;
|
|
968
1315
|
const backupPath = `${profilePath}.skalpel-backup`;
|
|
969
|
-
if (
|
|
970
|
-
|
|
971
|
-
|
|
1316
|
+
if (fs11.existsSync(backupPath)) {
|
|
1317
|
+
fs11.copyFileSync(backupPath, profilePath);
|
|
1318
|
+
fs11.unlinkSync(backupPath);
|
|
972
1319
|
} else {
|
|
973
1320
|
const before = content.slice(0, beginIdx);
|
|
974
1321
|
const after = content.slice(endIdx + END_MARKER.length);
|
|
975
1322
|
const cleaned = (before.replace(/\n+$/, "") + after.replace(/^\n+/, "\n")).trimEnd() + "\n";
|
|
976
|
-
|
|
1323
|
+
fs11.writeFileSync(profilePath, cleaned);
|
|
977
1324
|
}
|
|
978
1325
|
restored.push(profilePath);
|
|
979
1326
|
}
|
|
@@ -981,27 +1328,27 @@ function removeShellEnvVars() {
|
|
|
981
1328
|
}
|
|
982
1329
|
|
|
983
1330
|
// src/cli/agents/configure.ts
|
|
984
|
-
import
|
|
985
|
-
import
|
|
986
|
-
import
|
|
1331
|
+
import fs12 from "fs";
|
|
1332
|
+
import path13 from "path";
|
|
1333
|
+
import os7 from "os";
|
|
987
1334
|
function ensureDir(dir) {
|
|
988
|
-
|
|
1335
|
+
fs12.mkdirSync(dir, { recursive: true });
|
|
989
1336
|
}
|
|
990
1337
|
function createBackup2(filePath) {
|
|
991
|
-
if (
|
|
992
|
-
|
|
1338
|
+
if (fs12.existsSync(filePath)) {
|
|
1339
|
+
fs12.copyFileSync(filePath, `${filePath}.skalpel-backup`);
|
|
993
1340
|
}
|
|
994
1341
|
}
|
|
995
1342
|
function readJsonFile(filePath) {
|
|
996
1343
|
try {
|
|
997
|
-
return JSON.parse(
|
|
1344
|
+
return JSON.parse(fs12.readFileSync(filePath, "utf-8"));
|
|
998
1345
|
} catch {
|
|
999
1346
|
return {};
|
|
1000
1347
|
}
|
|
1001
1348
|
}
|
|
1002
1349
|
function configureClaudeCode(agent, proxyConfig) {
|
|
1003
|
-
const configPath = agent.configPath ??
|
|
1004
|
-
const configDir =
|
|
1350
|
+
const configPath = agent.configPath ?? path13.join(os7.homedir(), ".claude", "settings.json");
|
|
1351
|
+
const configDir = path13.dirname(configPath);
|
|
1005
1352
|
ensureDir(configDir);
|
|
1006
1353
|
createBackup2(configPath);
|
|
1007
1354
|
const config = readJsonFile(configPath);
|
|
@@ -1009,16 +1356,16 @@ function configureClaudeCode(agent, proxyConfig) {
|
|
|
1009
1356
|
config.env = {};
|
|
1010
1357
|
}
|
|
1011
1358
|
config.env.ANTHROPIC_BASE_URL = `http://localhost:${proxyConfig.anthropicPort}`;
|
|
1012
|
-
|
|
1359
|
+
fs12.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
1013
1360
|
}
|
|
1014
1361
|
function configureCodex(agent, proxyConfig) {
|
|
1015
|
-
const configDir = process.platform === "win32" ?
|
|
1016
|
-
const configPath = agent.configPath ??
|
|
1017
|
-
ensureDir(
|
|
1362
|
+
const configDir = process.platform === "win32" ? path13.join(os7.homedir(), "AppData", "Roaming", "codex") : path13.join(os7.homedir(), ".codex");
|
|
1363
|
+
const configPath = agent.configPath ?? path13.join(configDir, "config.json");
|
|
1364
|
+
ensureDir(path13.dirname(configPath));
|
|
1018
1365
|
createBackup2(configPath);
|
|
1019
1366
|
const config = readJsonFile(configPath);
|
|
1020
1367
|
config.apiBaseUrl = `http://localhost:${proxyConfig.openaiPort}`;
|
|
1021
|
-
|
|
1368
|
+
fs12.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
1022
1369
|
}
|
|
1023
1370
|
function configureAgent(agent, proxyConfig) {
|
|
1024
1371
|
switch (agent.name) {
|
|
@@ -1031,14 +1378,14 @@ function configureAgent(agent, proxyConfig) {
|
|
|
1031
1378
|
}
|
|
1032
1379
|
}
|
|
1033
1380
|
function unconfigureClaudeCode(agent) {
|
|
1034
|
-
const configPath = agent.configPath ??
|
|
1381
|
+
const configPath = agent.configPath ?? path13.join(os7.homedir(), ".claude", "settings.json");
|
|
1035
1382
|
const backupPath = `${configPath}.skalpel-backup`;
|
|
1036
|
-
if (
|
|
1037
|
-
|
|
1038
|
-
|
|
1383
|
+
if (fs12.existsSync(backupPath)) {
|
|
1384
|
+
fs12.copyFileSync(backupPath, configPath);
|
|
1385
|
+
fs12.unlinkSync(backupPath);
|
|
1039
1386
|
return;
|
|
1040
1387
|
}
|
|
1041
|
-
if (!
|
|
1388
|
+
if (!fs12.existsSync(configPath)) return;
|
|
1042
1389
|
const config = readJsonFile(configPath);
|
|
1043
1390
|
if (config.env && typeof config.env === "object") {
|
|
1044
1391
|
delete config.env.ANTHROPIC_BASE_URL;
|
|
@@ -1046,21 +1393,21 @@ function unconfigureClaudeCode(agent) {
|
|
|
1046
1393
|
delete config.env;
|
|
1047
1394
|
}
|
|
1048
1395
|
}
|
|
1049
|
-
|
|
1396
|
+
fs12.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
1050
1397
|
}
|
|
1051
1398
|
function unconfigureCodex(agent) {
|
|
1052
|
-
const configDir = process.platform === "win32" ?
|
|
1053
|
-
const configPath = agent.configPath ??
|
|
1399
|
+
const configDir = process.platform === "win32" ? path13.join(os7.homedir(), "AppData", "Roaming", "codex") : path13.join(os7.homedir(), ".codex");
|
|
1400
|
+
const configPath = agent.configPath ?? path13.join(configDir, "config.json");
|
|
1054
1401
|
const backupPath = `${configPath}.skalpel-backup`;
|
|
1055
|
-
if (
|
|
1056
|
-
|
|
1057
|
-
|
|
1402
|
+
if (fs12.existsSync(backupPath)) {
|
|
1403
|
+
fs12.copyFileSync(backupPath, configPath);
|
|
1404
|
+
fs12.unlinkSync(backupPath);
|
|
1058
1405
|
return;
|
|
1059
1406
|
}
|
|
1060
|
-
if (!
|
|
1407
|
+
if (!fs12.existsSync(configPath)) return;
|
|
1061
1408
|
const config = readJsonFile(configPath);
|
|
1062
1409
|
delete config.apiBaseUrl;
|
|
1063
|
-
|
|
1410
|
+
fs12.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
1064
1411
|
}
|
|
1065
1412
|
function unconfigureAgent(agent) {
|
|
1066
1413
|
switch (agent.name) {
|
|
@@ -1073,268 +1420,6 @@ function unconfigureAgent(agent) {
|
|
|
1073
1420
|
}
|
|
1074
1421
|
}
|
|
1075
1422
|
|
|
1076
|
-
// src/cli/service/install.ts
|
|
1077
|
-
import fs12 from "fs";
|
|
1078
|
-
import path13 from "path";
|
|
1079
|
-
import os7 from "os";
|
|
1080
|
-
import { execSync as execSync3 } from "child_process";
|
|
1081
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1082
|
-
|
|
1083
|
-
// src/cli/service/detect-os.ts
|
|
1084
|
-
import os5 from "os";
|
|
1085
|
-
import { execSync as execSync2 } from "child_process";
|
|
1086
|
-
function detectShell() {
|
|
1087
|
-
if (process.platform === "win32") {
|
|
1088
|
-
if (process.env.PSModulePath || process.env.POWERSHELL_DISTRIBUTION_CHANNEL) {
|
|
1089
|
-
return "powershell";
|
|
1090
|
-
}
|
|
1091
|
-
return "cmd";
|
|
1092
|
-
}
|
|
1093
|
-
const shellPath = process.env.SHELL ?? "";
|
|
1094
|
-
if (shellPath.includes("zsh")) return "zsh";
|
|
1095
|
-
if (shellPath.includes("fish")) return "fish";
|
|
1096
|
-
if (shellPath.includes("bash")) return "bash";
|
|
1097
|
-
try {
|
|
1098
|
-
if (process.platform === "darwin") {
|
|
1099
|
-
const result = execSync2(`dscl . -read /Users/${os5.userInfo().username} UserShell`, {
|
|
1100
|
-
encoding: "utf-8",
|
|
1101
|
-
timeout: 3e3
|
|
1102
|
-
}).trim();
|
|
1103
|
-
const shell = result.split(":").pop()?.trim() ?? "";
|
|
1104
|
-
if (shell.includes("zsh")) return "zsh";
|
|
1105
|
-
if (shell.includes("fish")) return "fish";
|
|
1106
|
-
if (shell.includes("bash")) return "bash";
|
|
1107
|
-
} else {
|
|
1108
|
-
const result = execSync2(`getent passwd ${os5.userInfo().username}`, {
|
|
1109
|
-
encoding: "utf-8",
|
|
1110
|
-
timeout: 3e3
|
|
1111
|
-
}).trim();
|
|
1112
|
-
const shell = result.split(":").pop() ?? "";
|
|
1113
|
-
if (shell.includes("zsh")) return "zsh";
|
|
1114
|
-
if (shell.includes("fish")) return "fish";
|
|
1115
|
-
if (shell.includes("bash")) return "bash";
|
|
1116
|
-
}
|
|
1117
|
-
} catch {
|
|
1118
|
-
}
|
|
1119
|
-
return "bash";
|
|
1120
|
-
}
|
|
1121
|
-
function detectOS() {
|
|
1122
|
-
let platform;
|
|
1123
|
-
switch (process.platform) {
|
|
1124
|
-
case "darwin":
|
|
1125
|
-
platform = "macos";
|
|
1126
|
-
break;
|
|
1127
|
-
case "win32":
|
|
1128
|
-
platform = "windows";
|
|
1129
|
-
break;
|
|
1130
|
-
default:
|
|
1131
|
-
platform = "linux";
|
|
1132
|
-
break;
|
|
1133
|
-
}
|
|
1134
|
-
return {
|
|
1135
|
-
platform,
|
|
1136
|
-
shell: detectShell(),
|
|
1137
|
-
homeDir: os5.homedir()
|
|
1138
|
-
};
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
// src/cli/service/templates.ts
|
|
1142
|
-
import os6 from "os";
|
|
1143
|
-
import path12 from "path";
|
|
1144
|
-
function generateLaunchdPlist(config, proxyRunnerPath) {
|
|
1145
|
-
const logDir = path12.join(os6.homedir(), ".skalpel", "logs");
|
|
1146
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
1147
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
1148
|
-
<plist version="1.0">
|
|
1149
|
-
<dict>
|
|
1150
|
-
<key>Label</key>
|
|
1151
|
-
<string>ai.skalpel.proxy</string>
|
|
1152
|
-
<key>ProgramArguments</key>
|
|
1153
|
-
<array>
|
|
1154
|
-
<string>${process.execPath}</string>
|
|
1155
|
-
<string>${proxyRunnerPath}</string>
|
|
1156
|
-
</array>
|
|
1157
|
-
<key>RunAtLoad</key>
|
|
1158
|
-
<true/>
|
|
1159
|
-
<key>KeepAlive</key>
|
|
1160
|
-
<true/>
|
|
1161
|
-
<key>StandardOutPath</key>
|
|
1162
|
-
<string>${path12.join(logDir, "proxy-stdout.log")}</string>
|
|
1163
|
-
<key>StandardErrorPath</key>
|
|
1164
|
-
<string>${path12.join(logDir, "proxy-stderr.log")}</string>
|
|
1165
|
-
<key>EnvironmentVariables</key>
|
|
1166
|
-
<dict>
|
|
1167
|
-
<key>SKALPEL_ANTHROPIC_PORT</key>
|
|
1168
|
-
<string>${config.anthropicPort}</string>
|
|
1169
|
-
<key>SKALPEL_OPENAI_PORT</key>
|
|
1170
|
-
<string>${config.openaiPort}</string>
|
|
1171
|
-
</dict>
|
|
1172
|
-
</dict>
|
|
1173
|
-
</plist>`;
|
|
1174
|
-
}
|
|
1175
|
-
function generateSystemdUnit(config, proxyRunnerPath) {
|
|
1176
|
-
return `[Unit]
|
|
1177
|
-
Description=Skalpel Proxy
|
|
1178
|
-
After=network.target
|
|
1179
|
-
|
|
1180
|
-
[Service]
|
|
1181
|
-
Type=simple
|
|
1182
|
-
ExecStart=${process.execPath} ${proxyRunnerPath}
|
|
1183
|
-
Restart=always
|
|
1184
|
-
RestartSec=5
|
|
1185
|
-
Environment=SKALPEL_ANTHROPIC_PORT=${config.anthropicPort}
|
|
1186
|
-
Environment=SKALPEL_OPENAI_PORT=${config.openaiPort}
|
|
1187
|
-
|
|
1188
|
-
[Install]
|
|
1189
|
-
WantedBy=default.target`;
|
|
1190
|
-
}
|
|
1191
|
-
function generateWindowsTask(config, proxyRunnerPath) {
|
|
1192
|
-
return [
|
|
1193
|
-
"/create",
|
|
1194
|
-
"/tn",
|
|
1195
|
-
"SkalpelProxy",
|
|
1196
|
-
"/tr",
|
|
1197
|
-
`"${process.execPath}" "${proxyRunnerPath}"`,
|
|
1198
|
-
"/sc",
|
|
1199
|
-
"ONLOGON",
|
|
1200
|
-
"/rl",
|
|
1201
|
-
"LIMITED",
|
|
1202
|
-
"/f"
|
|
1203
|
-
];
|
|
1204
|
-
}
|
|
1205
|
-
|
|
1206
|
-
// src/cli/service/install.ts
|
|
1207
|
-
var __dirname = path13.dirname(fileURLToPath2(import.meta.url));
|
|
1208
|
-
function resolveProxyRunnerPath() {
|
|
1209
|
-
const candidates = [
|
|
1210
|
-
path13.join(__dirname, "..", "proxy-runner.js"),
|
|
1211
|
-
// dist/cli/proxy-runner.js relative to dist/cli/service/
|
|
1212
|
-
path13.join(__dirname, "proxy-runner.js"),
|
|
1213
|
-
// same dir
|
|
1214
|
-
path13.join(__dirname, "..", "..", "cli", "proxy-runner.js")
|
|
1215
|
-
// dist/cli/proxy-runner.js from deeper
|
|
1216
|
-
];
|
|
1217
|
-
for (const candidate of candidates) {
|
|
1218
|
-
if (fs12.existsSync(candidate)) {
|
|
1219
|
-
return path13.resolve(candidate);
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
try {
|
|
1223
|
-
const npmRoot = execSync3("npm root -g", { encoding: "utf-8" }).trim();
|
|
1224
|
-
const globalPath = path13.join(npmRoot, "skalpel", "dist", "cli", "proxy-runner.js");
|
|
1225
|
-
if (fs12.existsSync(globalPath)) return globalPath;
|
|
1226
|
-
} catch {
|
|
1227
|
-
}
|
|
1228
|
-
const devPath = path13.resolve(process.cwd(), "dist", "cli", "proxy-runner.js");
|
|
1229
|
-
return devPath;
|
|
1230
|
-
}
|
|
1231
|
-
function getMacOSPlistPath() {
|
|
1232
|
-
return path13.join(os7.homedir(), "Library", "LaunchAgents", "ai.skalpel.proxy.plist");
|
|
1233
|
-
}
|
|
1234
|
-
function getLinuxUnitPath() {
|
|
1235
|
-
return path13.join(os7.homedir(), ".config", "systemd", "user", "skalpel-proxy.service");
|
|
1236
|
-
}
|
|
1237
|
-
function installService(config) {
|
|
1238
|
-
const osInfo = detectOS();
|
|
1239
|
-
const proxyRunnerPath = resolveProxyRunnerPath();
|
|
1240
|
-
const logDir = path13.join(os7.homedir(), ".skalpel", "logs");
|
|
1241
|
-
fs12.mkdirSync(logDir, { recursive: true });
|
|
1242
|
-
switch (osInfo.platform) {
|
|
1243
|
-
case "macos": {
|
|
1244
|
-
const plistPath = getMacOSPlistPath();
|
|
1245
|
-
const plistDir = path13.dirname(plistPath);
|
|
1246
|
-
fs12.mkdirSync(plistDir, { recursive: true });
|
|
1247
|
-
const plist = generateLaunchdPlist(config, proxyRunnerPath);
|
|
1248
|
-
fs12.writeFileSync(plistPath, plist);
|
|
1249
|
-
try {
|
|
1250
|
-
execSync3(`launchctl unload "${plistPath}" 2>/dev/null || true`, { stdio: "pipe" });
|
|
1251
|
-
execSync3(`launchctl load "${plistPath}"`, { stdio: "pipe" });
|
|
1252
|
-
} catch (err) {
|
|
1253
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1254
|
-
console.warn(` Warning: Could not register launchd service: ${msg}`);
|
|
1255
|
-
console.warn(` You can manually load it: launchctl load "${plistPath}"`);
|
|
1256
|
-
}
|
|
1257
|
-
break;
|
|
1258
|
-
}
|
|
1259
|
-
case "linux": {
|
|
1260
|
-
const unitPath = getLinuxUnitPath();
|
|
1261
|
-
const unitDir = path13.dirname(unitPath);
|
|
1262
|
-
fs12.mkdirSync(unitDir, { recursive: true });
|
|
1263
|
-
const unit = generateSystemdUnit(config, proxyRunnerPath);
|
|
1264
|
-
fs12.writeFileSync(unitPath, unit);
|
|
1265
|
-
try {
|
|
1266
|
-
execSync3("systemctl --user daemon-reload", { stdio: "pipe" });
|
|
1267
|
-
execSync3("systemctl --user enable skalpel-proxy", { stdio: "pipe" });
|
|
1268
|
-
execSync3("systemctl --user start skalpel-proxy", { stdio: "pipe" });
|
|
1269
|
-
} catch {
|
|
1270
|
-
try {
|
|
1271
|
-
const autostartDir = path13.join(os7.homedir(), ".config", "autostart");
|
|
1272
|
-
fs12.mkdirSync(autostartDir, { recursive: true });
|
|
1273
|
-
const desktopEntry = `[Desktop Entry]
|
|
1274
|
-
Type=Application
|
|
1275
|
-
Name=Skalpel Proxy
|
|
1276
|
-
Exec=${process.execPath} ${proxyRunnerPath}
|
|
1277
|
-
Hidden=false
|
|
1278
|
-
NoDisplay=true
|
|
1279
|
-
X-GNOME-Autostart-enabled=true
|
|
1280
|
-
`;
|
|
1281
|
-
fs12.writeFileSync(path13.join(autostartDir, "skalpel-proxy.desktop"), desktopEntry);
|
|
1282
|
-
console.warn(" Warning: systemd --user not available. Created .desktop autostart entry instead.");
|
|
1283
|
-
} catch (err2) {
|
|
1284
|
-
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
1285
|
-
console.warn(` Warning: Could not register service: ${msg}`);
|
|
1286
|
-
console.warn(" You can start the proxy manually: skalpel start");
|
|
1287
|
-
}
|
|
1288
|
-
}
|
|
1289
|
-
break;
|
|
1290
|
-
}
|
|
1291
|
-
case "windows": {
|
|
1292
|
-
const args = generateWindowsTask(config, proxyRunnerPath);
|
|
1293
|
-
try {
|
|
1294
|
-
execSync3(`schtasks ${args.join(" ")}`, { stdio: "pipe" });
|
|
1295
|
-
} catch (err) {
|
|
1296
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1297
|
-
console.warn(` Warning: Could not create scheduled task: ${msg}`);
|
|
1298
|
-
console.warn(" You can start the proxy manually: skalpel start");
|
|
1299
|
-
}
|
|
1300
|
-
break;
|
|
1301
|
-
}
|
|
1302
|
-
}
|
|
1303
|
-
}
|
|
1304
|
-
function uninstallService() {
|
|
1305
|
-
const osInfo = detectOS();
|
|
1306
|
-
switch (osInfo.platform) {
|
|
1307
|
-
case "macos": {
|
|
1308
|
-
const plistPath = getMacOSPlistPath();
|
|
1309
|
-
try {
|
|
1310
|
-
execSync3(`launchctl unload "${plistPath}" 2>/dev/null || true`, { stdio: "pipe" });
|
|
1311
|
-
} catch {
|
|
1312
|
-
}
|
|
1313
|
-
if (fs12.existsSync(plistPath)) fs12.unlinkSync(plistPath);
|
|
1314
|
-
break;
|
|
1315
|
-
}
|
|
1316
|
-
case "linux": {
|
|
1317
|
-
try {
|
|
1318
|
-
execSync3("systemctl --user stop skalpel-proxy 2>/dev/null || true", { stdio: "pipe" });
|
|
1319
|
-
execSync3("systemctl --user disable skalpel-proxy 2>/dev/null || true", { stdio: "pipe" });
|
|
1320
|
-
} catch {
|
|
1321
|
-
}
|
|
1322
|
-
const unitPath = getLinuxUnitPath();
|
|
1323
|
-
if (fs12.existsSync(unitPath)) fs12.unlinkSync(unitPath);
|
|
1324
|
-
const desktopPath = path13.join(os7.homedir(), ".config", "autostart", "skalpel-proxy.desktop");
|
|
1325
|
-
if (fs12.existsSync(desktopPath)) fs12.unlinkSync(desktopPath);
|
|
1326
|
-
break;
|
|
1327
|
-
}
|
|
1328
|
-
case "windows": {
|
|
1329
|
-
try {
|
|
1330
|
-
execSync3("schtasks /delete /tn SkalpelProxy /f", { stdio: "pipe" });
|
|
1331
|
-
} catch {
|
|
1332
|
-
}
|
|
1333
|
-
break;
|
|
1334
|
-
}
|
|
1335
|
-
}
|
|
1336
|
-
}
|
|
1337
|
-
|
|
1338
1423
|
// src/cli/wizard.ts
|
|
1339
1424
|
function print11(msg) {
|
|
1340
1425
|
console.log(msg);
|