@relayplane/proxy 1.5.46 â 1.7.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/README.md +297 -20
- package/assets/relayplane-proxy.service +20 -0
- package/dist/alerts.d.ts +72 -0
- package/dist/alerts.d.ts.map +1 -0
- package/dist/alerts.js +290 -0
- package/dist/alerts.js.map +1 -0
- package/dist/anomaly.d.ts +65 -0
- package/dist/anomaly.d.ts.map +1 -0
- package/dist/anomaly.js +193 -0
- package/dist/anomaly.js.map +1 -0
- package/dist/budget.d.ts +98 -0
- package/dist/budget.d.ts.map +1 -0
- package/dist/budget.js +356 -0
- package/dist/budget.js.map +1 -0
- package/dist/cli.js +512 -93
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +28 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +122 -24
- package/dist/config.js.map +1 -1
- package/dist/downgrade.d.ts +37 -0
- package/dist/downgrade.d.ts.map +1 -0
- package/dist/downgrade.js +79 -0
- package/dist/downgrade.js.map +1 -0
- package/dist/mesh/capture.d.ts +11 -0
- package/dist/mesh/capture.d.ts.map +1 -0
- package/dist/mesh/capture.js +43 -0
- package/dist/mesh/capture.js.map +1 -0
- package/dist/mesh/fitness.d.ts +14 -0
- package/dist/mesh/fitness.d.ts.map +1 -0
- package/dist/mesh/fitness.js +40 -0
- package/dist/mesh/fitness.js.map +1 -0
- package/dist/mesh/index.d.ts +39 -0
- package/dist/mesh/index.d.ts.map +1 -0
- package/dist/mesh/index.js +118 -0
- package/dist/mesh/index.js.map +1 -0
- package/dist/mesh/store.d.ts +30 -0
- package/dist/mesh/store.d.ts.map +1 -0
- package/dist/mesh/store.js +174 -0
- package/dist/mesh/store.js.map +1 -0
- package/dist/mesh/sync.d.ts +37 -0
- package/dist/mesh/sync.d.ts.map +1 -0
- package/dist/mesh/sync.js +154 -0
- package/dist/mesh/sync.js.map +1 -0
- package/dist/mesh/types.d.ts +57 -0
- package/dist/mesh/types.d.ts.map +1 -0
- package/dist/mesh/types.js +7 -0
- package/dist/mesh/types.js.map +1 -0
- package/dist/rate-limiter.d.ts +64 -0
- package/dist/rate-limiter.d.ts.map +1 -0
- package/dist/rate-limiter.js +159 -0
- package/dist/rate-limiter.js.map +1 -0
- package/dist/relay-config.d.ts +9 -0
- package/dist/relay-config.d.ts.map +1 -1
- package/dist/relay-config.js +2 -0
- package/dist/relay-config.js.map +1 -1
- package/dist/response-cache.d.ts +139 -0
- package/dist/response-cache.d.ts.map +1 -0
- package/dist/response-cache.js +515 -0
- package/dist/response-cache.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +5 -1
- package/dist/server.js.map +1 -1
- package/dist/standalone-proxy.d.ts +2 -1
- package/dist/standalone-proxy.d.ts.map +1 -1
- package/dist/standalone-proxy.js +736 -50
- package/dist/standalone-proxy.js.map +1 -1
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +21 -5
- package/dist/telemetry.js.map +1 -1
- package/dist/utils/model-suggestions.d.ts.map +1 -1
- package/dist/utils/model-suggestions.js +19 -2
- package/dist/utils/model-suggestions.js.map +1 -1
- package/dist/utils/version-status.d.ts +9 -0
- package/dist/utils/version-status.d.ts.map +1 -0
- package/dist/utils/version-status.js +28 -0
- package/dist/utils/version-status.js.map +1 -0
- package/package.json +7 -3
package/dist/cli.js
CHANGED
|
@@ -43,6 +43,10 @@ const telemetry_js_1 = require("./telemetry.js");
|
|
|
43
43
|
const fs_1 = require("fs");
|
|
44
44
|
const path_1 = require("path");
|
|
45
45
|
const os_1 = require("os");
|
|
46
|
+
const response_cache_js_1 = require("./response-cache.js");
|
|
47
|
+
const budget_js_1 = require("./budget.js");
|
|
48
|
+
const alerts_js_1 = require("./alerts.js");
|
|
49
|
+
// __dirname is available natively in CJS
|
|
46
50
|
let VERSION = '0.0.0';
|
|
47
51
|
try {
|
|
48
52
|
const pkgPath = (0, path_1.join)(__dirname, '..', 'package.json');
|
|
@@ -389,6 +393,7 @@ Usage:
|
|
|
389
393
|
|
|
390
394
|
Commands:
|
|
391
395
|
(default) Start the proxy server
|
|
396
|
+
init Initialize config files
|
|
392
397
|
login Log in to RelayPlane (opens browser)
|
|
393
398
|
logout Clear stored credentials
|
|
394
399
|
status Show proxy status, plan, and cloud sync
|
|
@@ -398,8 +403,13 @@ Commands:
|
|
|
398
403
|
telemetry [on|off|status] Manage telemetry settings
|
|
399
404
|
stats Show usage statistics
|
|
400
405
|
config Show configuration
|
|
401
|
-
|
|
402
|
-
|
|
406
|
+
config set-key <key> Set RelayPlane API key
|
|
407
|
+
budget [status|set|reset] Manage spend budgets and limits
|
|
408
|
+
alerts [list|counts] View cost alerts and anomaly history
|
|
409
|
+
cache [on|off|status|clear|stats] Manage response cache
|
|
410
|
+
service [install|uninstall|status] Manage system service (systemd/launchd)
|
|
411
|
+
autostart [on|off|status] Manage autostart on boot (systemd, legacy)
|
|
412
|
+
mesh [status|on|off|sync|contribute] Mesh learning layer management
|
|
403
413
|
|
|
404
414
|
Options:
|
|
405
415
|
--port <number> Port to listen on (default: 4100)
|
|
@@ -431,8 +441,8 @@ Example:
|
|
|
431
441
|
npx @relayplane/proxy telemetry off
|
|
432
442
|
|
|
433
443
|
# Point your agent at the proxy:
|
|
434
|
-
# ANTHROPIC_BASE_URL=http://localhost:
|
|
435
|
-
# OPENAI_BASE_URL=http://localhost:
|
|
444
|
+
# ANTHROPIC_BASE_URL=http://localhost:4100 your-agent
|
|
445
|
+
# OPENAI_BASE_URL=http://localhost:4100/v1 your-agent
|
|
436
446
|
|
|
437
447
|
Learn more: https://relayplane.com/docs
|
|
438
448
|
`);
|
|
@@ -731,56 +741,61 @@ WantedBy=multi-user.target
|
|
|
731
741
|
console.log('');
|
|
732
742
|
}
|
|
733
743
|
async function handleMeshCommand(args) {
|
|
734
|
-
const
|
|
735
|
-
const config = resolveMeshConfig();
|
|
744
|
+
const meshCfg = (0, config_js_1.getMeshConfig)();
|
|
736
745
|
const sub = args[0] ?? 'status';
|
|
737
746
|
if (sub === 'status') {
|
|
738
|
-
// Try hitting the running proxy's mesh
|
|
747
|
+
// Try hitting the running proxy's mesh stats endpoint
|
|
739
748
|
try {
|
|
740
749
|
const controller = new AbortController();
|
|
741
750
|
const timeout = setTimeout(() => controller.abort(), 2000);
|
|
742
|
-
const res = await fetch('http://127.0.0.1:4100/v1/mesh/
|
|
751
|
+
const res = await fetch('http://127.0.0.1:4100/v1/mesh/stats', { signal: controller.signal });
|
|
743
752
|
clearTimeout(timeout);
|
|
744
753
|
if (res.ok) {
|
|
745
754
|
const data = await res.json();
|
|
746
|
-
const m = data.mesh;
|
|
747
755
|
console.log('');
|
|
748
756
|
console.log('đ§ Mesh Learning Layer');
|
|
749
757
|
console.log('ââââââââââââââââââââââ');
|
|
750
|
-
console.log(`
|
|
751
|
-
console.log(`
|
|
752
|
-
console.log(` Atoms:
|
|
753
|
-
console.log(`
|
|
754
|
-
console.log(`
|
|
755
|
-
console.log(` Data dir: ${m.dataDir}`);
|
|
758
|
+
console.log(` Enabled: ${data.enabled ? 'â
' : 'â'}`);
|
|
759
|
+
console.log(` Atoms (local): ${data.atoms_local}`);
|
|
760
|
+
console.log(` Atoms (synced): ${data.atoms_synced}`);
|
|
761
|
+
console.log(` Last sync: ${data.last_sync ?? 'never'}`);
|
|
762
|
+
console.log(` Endpoint: ${data.endpoint}`);
|
|
756
763
|
console.log('');
|
|
757
764
|
return;
|
|
758
765
|
}
|
|
759
766
|
}
|
|
760
767
|
catch {
|
|
761
|
-
// Proxy not running
|
|
768
|
+
// Proxy not running
|
|
762
769
|
}
|
|
763
770
|
console.log('');
|
|
764
771
|
console.log('đ§ Mesh Learning Layer (proxy not running)');
|
|
765
772
|
console.log('ââââââââââââââââââââââââââââââââââââââââââ');
|
|
766
|
-
console.log(` Enabled:
|
|
767
|
-
console.log(` Contribute:
|
|
768
|
-
console.log(`
|
|
769
|
-
console.log(`
|
|
770
|
-
console.log(` Sync interval: ${config.syncIntervalMs / 1000}s`);
|
|
771
|
-
console.log(` Inject interval: ${config.injectIntervalMs / 1000}s`);
|
|
773
|
+
console.log(` Enabled: ${meshCfg.enabled ? 'â
' : 'â'}`);
|
|
774
|
+
console.log(` Contribute: ${meshCfg.contribute ? 'â
' : 'â'}`);
|
|
775
|
+
console.log(` Endpoint: ${meshCfg.endpoint}`);
|
|
776
|
+
console.log(` Sync interval: ${meshCfg.sync_interval_ms / 1000}s`);
|
|
772
777
|
console.log('');
|
|
773
778
|
console.log(' Start the proxy to see live status.');
|
|
774
779
|
console.log('');
|
|
775
780
|
return;
|
|
776
781
|
}
|
|
782
|
+
if (sub === 'on') {
|
|
783
|
+
(0, config_js_1.updateMeshConfig)({ enabled: true });
|
|
784
|
+
console.log(' â
Mesh enabled. Restart the proxy for changes to take effect.');
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
if (sub === 'off') {
|
|
788
|
+
(0, config_js_1.updateMeshConfig)({ enabled: false });
|
|
789
|
+
console.log(' â Mesh disabled. Restart the proxy for changes to take effect.');
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
777
792
|
if (sub === 'sync') {
|
|
778
793
|
try {
|
|
779
794
|
const res = await fetch('http://127.0.0.1:4100/v1/mesh/sync', { method: 'POST' });
|
|
780
795
|
if (res.ok) {
|
|
781
796
|
const data = await res.json();
|
|
782
|
-
if (data.sync.
|
|
783
|
-
console.log(`â ī¸ ${data.sync.
|
|
797
|
+
if (data.sync.errors && data.sync.errors.length > 0) {
|
|
798
|
+
console.log(`â ī¸ Sync errors: ${data.sync.errors.join('; ')}`);
|
|
784
799
|
}
|
|
785
800
|
else {
|
|
786
801
|
console.log(`â
Synced: pushed ${data.sync.pushed ?? 0}, pulled ${data.sync.pulled ?? 0}`);
|
|
@@ -795,93 +810,308 @@ async function handleMeshCommand(args) {
|
|
|
795
810
|
}
|
|
796
811
|
return;
|
|
797
812
|
}
|
|
798
|
-
if (sub === '
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
813
|
+
if (sub === 'contribute') {
|
|
814
|
+
const value = args[1]?.toLowerCase();
|
|
815
|
+
if (!value || value === 'status') {
|
|
816
|
+
console.log(`\n Mesh contribution: ${meshCfg.contribute ? 'â
Enabled' : 'â Disabled'}`);
|
|
817
|
+
console.log('');
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
if (value === 'on') {
|
|
821
|
+
(0, config_js_1.updateMeshConfig)({ contribute: true });
|
|
822
|
+
console.log('\n â
Mesh contribution enabled. Restart proxy for changes.\n');
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
if (value === 'off') {
|
|
826
|
+
(0, config_js_1.updateMeshConfig)({ contribute: false });
|
|
827
|
+
console.log('\n â Mesh contribution disabled. Restart proxy for changes.\n');
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
console.log('Usage: relayplane mesh contribute [on|off|status]');
|
|
831
|
+
return;
|
|
832
|
+
}
|
|
833
|
+
console.log('Unknown mesh subcommand. Available: status, on, off, sync, contribute');
|
|
834
|
+
}
|
|
835
|
+
// ============================================
|
|
836
|
+
// SERVICE INSTALL/UNINSTALL/STATUS COMMAND
|
|
837
|
+
// ============================================
|
|
838
|
+
function getServiceAssetPath() {
|
|
839
|
+
return (0, path_1.join)(__dirname, '..', 'assets', 'relayplane-proxy.service');
|
|
840
|
+
}
|
|
841
|
+
function generateLaunchdPlist(binPath) {
|
|
842
|
+
const envKeys = ['ANTHROPIC_API_KEY', 'OPENAI_API_KEY', 'OPENROUTER_API_KEY', 'GEMINI_API_KEY', 'XAI_API_KEY'];
|
|
843
|
+
const envDict = envKeys
|
|
844
|
+
.filter(k => process.env[k])
|
|
845
|
+
.map(k => ` <key>${k}</key>\n <string>${process.env[k]}</string>`)
|
|
846
|
+
.join('\n');
|
|
847
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
848
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
849
|
+
<plist version="1.0">
|
|
850
|
+
<dict>
|
|
851
|
+
<key>Label</key>
|
|
852
|
+
<string>com.relayplane.proxy</string>
|
|
853
|
+
<key>ProgramArguments</key>
|
|
854
|
+
<array>
|
|
855
|
+
<string>${binPath}</string>
|
|
856
|
+
</array>
|
|
857
|
+
<key>RunAtLoad</key>
|
|
858
|
+
<true/>
|
|
859
|
+
<key>KeepAlive</key>
|
|
860
|
+
<true/>
|
|
861
|
+
<key>StandardOutPath</key>
|
|
862
|
+
<string>${(0, os_1.homedir)()}/Library/Logs/relayplane-proxy.log</string>
|
|
863
|
+
<key>StandardErrorPath</key>
|
|
864
|
+
<string>${(0, os_1.homedir)()}/Library/Logs/relayplane-proxy.error.log</string>
|
|
865
|
+
<key>EnvironmentVariables</key>
|
|
866
|
+
<dict>
|
|
867
|
+
<key>HOME</key>
|
|
868
|
+
<string>${(0, os_1.homedir)()}</string>
|
|
869
|
+
<key>PATH</key>
|
|
870
|
+
<string>/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin</string>
|
|
871
|
+
<key>NODE_ENV</key>
|
|
872
|
+
<string>production</string>
|
|
873
|
+
${envDict}
|
|
874
|
+
</dict>
|
|
875
|
+
</dict>
|
|
876
|
+
</plist>
|
|
877
|
+
`;
|
|
878
|
+
}
|
|
879
|
+
async function handleServiceCommand(args) {
|
|
880
|
+
const sub = args[0] ?? 'status';
|
|
881
|
+
const dryRun = args.includes('--dry-run');
|
|
882
|
+
const isMac = process.platform === 'darwin';
|
|
883
|
+
const isLinux = process.platform === 'linux';
|
|
884
|
+
if (!isMac && !isLinux) {
|
|
885
|
+
console.log(' â ī¸ Service management is only supported on Linux (systemd) and macOS (launchd).');
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
const { execSync } = require('child_process');
|
|
889
|
+
// Detect binary path
|
|
890
|
+
let binPath;
|
|
891
|
+
try {
|
|
892
|
+
binPath = execSync('which relayplane', { encoding: 'utf8' }).trim();
|
|
893
|
+
}
|
|
894
|
+
catch {
|
|
895
|
+
binPath = process.argv[0] ?? 'relayplane';
|
|
896
|
+
}
|
|
897
|
+
if (sub === 'install') {
|
|
898
|
+
if (isLinux) {
|
|
899
|
+
if (!hasSystemd()) {
|
|
900
|
+
console.log(' â ī¸ systemd not found on this system.');
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
if (!isRoot() && !dryRun) {
|
|
904
|
+
console.log(' â ī¸ Service install requires root. Try: sudo relayplane service install');
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
// Read the shipped service template and patch ExecStart
|
|
908
|
+
const assetPath = getServiceAssetPath();
|
|
909
|
+
let serviceContent;
|
|
910
|
+
if ((0, fs_1.existsSync)(assetPath)) {
|
|
911
|
+
serviceContent = (0, fs_1.readFileSync)(assetPath, 'utf8');
|
|
912
|
+
serviceContent = serviceContent.replace(/^ExecStart=.*$/m, `ExecStart=${binPath}`);
|
|
913
|
+
}
|
|
914
|
+
else {
|
|
915
|
+
// Fallback: generate inline
|
|
916
|
+
const envKeys = ['ANTHROPIC_API_KEY', 'OPENAI_API_KEY', 'OPENROUTER_API_KEY', 'GEMINI_API_KEY', 'XAI_API_KEY'];
|
|
917
|
+
const envLines = envKeys
|
|
918
|
+
.filter(k => process.env[k])
|
|
919
|
+
.map(k => `Environment=${k}=${process.env[k]}`)
|
|
920
|
+
.join('\n');
|
|
921
|
+
serviceContent = `[Unit]
|
|
922
|
+
Description=RelayPlane Proxy - Intelligent AI Model Routing
|
|
923
|
+
After=network.target
|
|
924
|
+
StartLimitIntervalSec=300
|
|
925
|
+
StartLimitBurst=5
|
|
926
|
+
|
|
927
|
+
[Service]
|
|
928
|
+
Type=notify
|
|
929
|
+
User=root
|
|
930
|
+
ExecStart=${binPath}
|
|
931
|
+
Restart=always
|
|
932
|
+
RestartSec=5
|
|
933
|
+
WatchdogSec=30
|
|
934
|
+
StandardOutput=journal
|
|
935
|
+
StandardError=journal
|
|
936
|
+
Environment=HOME=/root
|
|
937
|
+
Environment=NODE_ENV=production
|
|
938
|
+
${envLines}
|
|
939
|
+
|
|
940
|
+
[Install]
|
|
941
|
+
WantedBy=multi-user.target
|
|
942
|
+
`;
|
|
943
|
+
}
|
|
944
|
+
// Append current env API keys not already in template
|
|
945
|
+
const envKeys = ['ANTHROPIC_API_KEY', 'OPENAI_API_KEY', 'OPENROUTER_API_KEY', 'GEMINI_API_KEY', 'XAI_API_KEY'];
|
|
946
|
+
for (const key of envKeys) {
|
|
947
|
+
if (process.env[key] && !serviceContent.includes(`Environment=${key}=`)) {
|
|
948
|
+
serviceContent = serviceContent.replace(/\[Install\]/, `Environment=${key}=${process.env[key]}\n\n[Install]`);
|
|
806
949
|
}
|
|
950
|
+
}
|
|
951
|
+
if (dryRun) {
|
|
807
952
|
console.log('');
|
|
808
|
-
console.log('
|
|
809
|
-
console.log('
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
}
|
|
953
|
+
console.log(' [DRY RUN] Would write to /etc/systemd/system/relayplane-proxy.service:');
|
|
954
|
+
console.log(' âââââââââââââââââââââââââââââââââââââââââ');
|
|
955
|
+
console.log(serviceContent);
|
|
956
|
+
console.log(' âââââââââââââââââââââââââââââââââââââââââ');
|
|
957
|
+
console.log(' [DRY RUN] Would run: systemctl daemon-reload && systemctl enable --now relayplane-proxy');
|
|
814
958
|
console.log('');
|
|
959
|
+
return;
|
|
815
960
|
}
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
961
|
+
(0, fs_1.writeFileSync)(SERVICE_PATH, serviceContent);
|
|
962
|
+
execSync('systemctl daemon-reload && systemctl enable --now relayplane-proxy', { stdio: 'inherit' });
|
|
963
|
+
const config = loadRelayplaneConfig();
|
|
964
|
+
config.autostart = true;
|
|
965
|
+
saveRelayplaneConfig(config);
|
|
966
|
+
console.log('');
|
|
967
|
+
console.log(' â
Service installed and started.');
|
|
968
|
+
console.log(' RelayPlane will start on boot and restart on crash.');
|
|
969
|
+
console.log(' Run `relayplane service status` to verify.');
|
|
970
|
+
console.log('');
|
|
819
971
|
}
|
|
820
|
-
|
|
821
|
-
|
|
972
|
+
else if (isMac) {
|
|
973
|
+
const plistPath = (0, path_1.join)((0, os_1.homedir)(), 'Library', 'LaunchAgents', 'com.relayplane.proxy.plist');
|
|
974
|
+
const plistContent = generateLaunchdPlist(binPath);
|
|
975
|
+
if (dryRun) {
|
|
976
|
+
console.log('');
|
|
977
|
+
console.log(` [DRY RUN] Would write to ${plistPath}:`);
|
|
978
|
+
console.log(' âââââââââââââââââââââââââââââââââââââââââ');
|
|
979
|
+
console.log(plistContent);
|
|
980
|
+
console.log(' âââââââââââââââââââââââââââââââââââââââââ');
|
|
981
|
+
console.log(` [DRY RUN] Would run: launchctl load ${plistPath}`);
|
|
982
|
+
console.log('');
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
const dir = (0, path_1.dirname)(plistPath);
|
|
986
|
+
if (!(0, fs_1.existsSync)(dir))
|
|
987
|
+
(0, fs_1.mkdirSync)(dir, { recursive: true });
|
|
988
|
+
(0, fs_1.writeFileSync)(plistPath, plistContent);
|
|
989
|
+
execSync(`launchctl load "${plistPath}"`, { stdio: 'inherit' });
|
|
990
|
+
console.log('');
|
|
991
|
+
console.log(' â
Service installed and loaded via launchd.');
|
|
992
|
+
console.log(' RelayPlane will start on login and restart on crash.');
|
|
993
|
+
console.log('');
|
|
822
994
|
}
|
|
823
995
|
return;
|
|
824
996
|
}
|
|
825
|
-
if (sub === '
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
let config = {};
|
|
831
|
-
try {
|
|
832
|
-
if ((0, fs_1.existsSync)(configPath)) {
|
|
833
|
-
config = JSON.parse((0, fs_1.readFileSync)(configPath, 'utf8'));
|
|
997
|
+
if (sub === 'uninstall') {
|
|
998
|
+
if (isLinux) {
|
|
999
|
+
if (!isRoot() && !dryRun) {
|
|
1000
|
+
console.log(' â ī¸ Service uninstall requires root. Try: sudo relayplane service uninstall');
|
|
1001
|
+
return;
|
|
834
1002
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
console.log(' You are sharing anonymized routing data with the collective mesh.');
|
|
843
|
-
console.log(' This improves routing for everyone on the network.');
|
|
844
|
-
console.log(' To disable: relayplane mesh contribute off');
|
|
1003
|
+
if (dryRun) {
|
|
1004
|
+
console.log('');
|
|
1005
|
+
console.log(' [DRY RUN] Would run: systemctl stop relayplane-proxy && systemctl disable relayplane-proxy');
|
|
1006
|
+
console.log(' [DRY RUN] Would remove /etc/systemd/system/relayplane-proxy.service');
|
|
1007
|
+
console.log(' [DRY RUN] Would run: systemctl daemon-reload');
|
|
1008
|
+
console.log('');
|
|
1009
|
+
return;
|
|
845
1010
|
}
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
1011
|
+
try {
|
|
1012
|
+
execSync('systemctl stop relayplane-proxy && systemctl disable relayplane-proxy', { stdio: 'inherit' });
|
|
1013
|
+
}
|
|
1014
|
+
catch { /* may not exist */ }
|
|
1015
|
+
try {
|
|
1016
|
+
if ((0, fs_1.existsSync)(SERVICE_PATH)) {
|
|
1017
|
+
const { unlinkSync } = require('fs');
|
|
1018
|
+
unlinkSync(SERVICE_PATH);
|
|
1019
|
+
execSync('systemctl daemon-reload', { stdio: 'inherit' });
|
|
1020
|
+
}
|
|
850
1021
|
}
|
|
1022
|
+
catch { }
|
|
1023
|
+
const config = loadRelayplaneConfig();
|
|
1024
|
+
config.autostart = false;
|
|
1025
|
+
saveRelayplaneConfig(config);
|
|
851
1026
|
console.log('');
|
|
852
|
-
console.log('
|
|
853
|
-
console.log(' âĸ Task type (code_review, file_read, etc.)');
|
|
854
|
-
console.log(' âĸ Model used and whether it succeeded');
|
|
855
|
-
console.log(' âĸ Token count and latency');
|
|
856
|
-
console.log(' âĸ Cost estimate');
|
|
1027
|
+
console.log(' â
Service uninstalled.');
|
|
857
1028
|
console.log('');
|
|
858
|
-
|
|
1029
|
+
}
|
|
1030
|
+
else if (isMac) {
|
|
1031
|
+
const plistPath = (0, path_1.join)((0, os_1.homedir)(), 'Library', 'LaunchAgents', 'com.relayplane.proxy.plist');
|
|
1032
|
+
if (dryRun) {
|
|
1033
|
+
console.log('');
|
|
1034
|
+
console.log(` [DRY RUN] Would run: launchctl unload ${plistPath}`);
|
|
1035
|
+
console.log(` [DRY RUN] Would remove ${plistPath}`);
|
|
1036
|
+
console.log('');
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
try {
|
|
1040
|
+
execSync(`launchctl unload "${plistPath}"`, { stdio: 'inherit' });
|
|
1041
|
+
}
|
|
1042
|
+
catch { }
|
|
1043
|
+
try {
|
|
1044
|
+
if ((0, fs_1.existsSync)(plistPath)) {
|
|
1045
|
+
const { unlinkSync } = require('fs');
|
|
1046
|
+
unlinkSync(plistPath);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
catch { }
|
|
1050
|
+
console.log('');
|
|
1051
|
+
console.log(' â
Service uninstalled from launchd.');
|
|
859
1052
|
console.log('');
|
|
860
|
-
return;
|
|
861
1053
|
}
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
// status (default)
|
|
1057
|
+
if (isLinux && hasSystemd()) {
|
|
1058
|
+
let isEnabled = false;
|
|
1059
|
+
let isActive = false;
|
|
1060
|
+
let statusOutput = '';
|
|
1061
|
+
try {
|
|
1062
|
+
const enabled = execSync(`systemctl is-enabled ${SERVICE_NAME} 2>/dev/null`, { encoding: 'utf8' }).trim();
|
|
1063
|
+
isEnabled = enabled === 'enabled';
|
|
871
1064
|
}
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
(0, fs_1.writeFileSync)(configPath, JSON.stringify(config, null, 2));
|
|
877
|
-
console.log('\n â Mesh contribution disabled');
|
|
878
|
-
console.log(' Your data stays local. Restart the proxy for changes to take effect.\n');
|
|
879
|
-
return;
|
|
1065
|
+
catch { }
|
|
1066
|
+
try {
|
|
1067
|
+
const active = execSync(`systemctl is-active ${SERVICE_NAME} 2>/dev/null`, { encoding: 'utf8' }).trim();
|
|
1068
|
+
isActive = active === 'active';
|
|
880
1069
|
}
|
|
881
|
-
|
|
882
|
-
|
|
1070
|
+
catch { }
|
|
1071
|
+
try {
|
|
1072
|
+
statusOutput = execSync(`systemctl status ${SERVICE_NAME} 2>&1 || true`, { encoding: 'utf8' });
|
|
1073
|
+
}
|
|
1074
|
+
catch { }
|
|
1075
|
+
console.log('');
|
|
1076
|
+
console.log(' đ§ Service Status (systemd)');
|
|
1077
|
+
console.log(' âââââââââââââââââââââââââââ');
|
|
1078
|
+
console.log(` Enabled: ${isEnabled ? 'â
Yes' : 'â No'}`);
|
|
1079
|
+
console.log(` Running: ${isActive ? 'đĸ Active' : 'đ´ Inactive'}`);
|
|
1080
|
+
console.log('');
|
|
1081
|
+
if (statusOutput) {
|
|
1082
|
+
console.log(statusOutput.split('\n').map(l => ' ' + l).join('\n'));
|
|
1083
|
+
}
|
|
1084
|
+
if (!isEnabled) {
|
|
1085
|
+
console.log(' To install: sudo relayplane service install');
|
|
1086
|
+
}
|
|
1087
|
+
else {
|
|
1088
|
+
console.log(' To uninstall: sudo relayplane service uninstall');
|
|
1089
|
+
}
|
|
1090
|
+
console.log('');
|
|
1091
|
+
}
|
|
1092
|
+
else if (isMac) {
|
|
1093
|
+
const plistPath = (0, path_1.join)((0, os_1.homedir)(), 'Library', 'LaunchAgents', 'com.relayplane.proxy.plist');
|
|
1094
|
+
const installed = (0, fs_1.existsSync)(plistPath);
|
|
1095
|
+
let isLoaded = false;
|
|
1096
|
+
try {
|
|
1097
|
+
const output = execSync('launchctl list com.relayplane.proxy 2>&1', { encoding: 'utf8' });
|
|
1098
|
+
isLoaded = !output.includes('Could not find');
|
|
1099
|
+
}
|
|
1100
|
+
catch { }
|
|
1101
|
+
console.log('');
|
|
1102
|
+
console.log(' đ§ Service Status (launchd)');
|
|
1103
|
+
console.log(' âââââââââââââââââââââââââââ');
|
|
1104
|
+
console.log(` Installed: ${installed ? 'â
Yes' : 'â No'}`);
|
|
1105
|
+
console.log(` Loaded: ${isLoaded ? 'đĸ Yes' : 'đ´ No'}`);
|
|
1106
|
+
console.log('');
|
|
1107
|
+
if (!installed) {
|
|
1108
|
+
console.log(' To install: relayplane service install');
|
|
1109
|
+
}
|
|
1110
|
+
else {
|
|
1111
|
+
console.log(' To uninstall: relayplane service uninstall');
|
|
1112
|
+
}
|
|
1113
|
+
console.log('');
|
|
883
1114
|
}
|
|
884
|
-
console.log('Unknown mesh subcommand. Available: status, sync, tips, contribute');
|
|
885
1115
|
}
|
|
886
1116
|
async function main() {
|
|
887
1117
|
const args = process.argv.slice(2);
|
|
@@ -922,6 +1152,15 @@ async function main() {
|
|
|
922
1152
|
// "relayplane start" just falls through to start the server
|
|
923
1153
|
args.shift();
|
|
924
1154
|
}
|
|
1155
|
+
const knownCommands = new Set([
|
|
1156
|
+
'init', 'start', 'telemetry', 'stats', 'config', 'login', 'logout', 'upgrade',
|
|
1157
|
+
'status', 'autostart', 'service', 'mesh', 'cache', 'budget', 'alerts', 'enable', 'disable',
|
|
1158
|
+
]);
|
|
1159
|
+
if (command && !command.startsWith('-') && !knownCommands.has(command)) {
|
|
1160
|
+
console.error(`Unknown command: ${command}`);
|
|
1161
|
+
console.error('Run relayplane --help to see available commands.');
|
|
1162
|
+
process.exit(1);
|
|
1163
|
+
}
|
|
925
1164
|
if (command === 'telemetry') {
|
|
926
1165
|
handleTelemetryCommand(args.slice(1));
|
|
927
1166
|
process.exit(0);
|
|
@@ -954,10 +1193,26 @@ async function main() {
|
|
|
954
1193
|
await handleAutostartCommand(args.slice(1));
|
|
955
1194
|
process.exit(0);
|
|
956
1195
|
}
|
|
1196
|
+
if (command === 'service') {
|
|
1197
|
+
await handleServiceCommand(args.slice(1));
|
|
1198
|
+
process.exit(0);
|
|
1199
|
+
}
|
|
957
1200
|
if (command === 'mesh') {
|
|
958
1201
|
await handleMeshCommand(args.slice(1));
|
|
959
1202
|
process.exit(0);
|
|
960
1203
|
}
|
|
1204
|
+
if (command === 'cache') {
|
|
1205
|
+
handleCacheCommand(args.slice(1));
|
|
1206
|
+
process.exit(0);
|
|
1207
|
+
}
|
|
1208
|
+
if (command === 'budget') {
|
|
1209
|
+
handleBudgetCommand(args.slice(1));
|
|
1210
|
+
process.exit(0);
|
|
1211
|
+
}
|
|
1212
|
+
if (command === 'alerts') {
|
|
1213
|
+
handleAlertsCommand(args.slice(1));
|
|
1214
|
+
process.exit(0);
|
|
1215
|
+
}
|
|
961
1216
|
if (command === 'enable') {
|
|
962
1217
|
handleEnableDisableCommand(true);
|
|
963
1218
|
process.exit(0);
|
|
@@ -1086,5 +1341,169 @@ async function main() {
|
|
|
1086
1341
|
process.exit(1);
|
|
1087
1342
|
}
|
|
1088
1343
|
}
|
|
1344
|
+
function handleAlertsCommand(args) {
|
|
1345
|
+
const sub = args[0] ?? 'list';
|
|
1346
|
+
const alertMgr = (0, alerts_js_1.getAlertManager)({ enabled: true });
|
|
1347
|
+
try {
|
|
1348
|
+
alertMgr.init();
|
|
1349
|
+
}
|
|
1350
|
+
catch { /* ok */ }
|
|
1351
|
+
if (sub === 'list' || sub === 'recent') {
|
|
1352
|
+
const limit = parseInt(args[1] ?? '20', 10);
|
|
1353
|
+
const recent = alertMgr.getRecent(limit);
|
|
1354
|
+
console.log('');
|
|
1355
|
+
console.log('đ Recent Alerts');
|
|
1356
|
+
console.log('âââââââââââââââââ');
|
|
1357
|
+
if (recent.length === 0) {
|
|
1358
|
+
console.log(' No alerts yet.');
|
|
1359
|
+
}
|
|
1360
|
+
else {
|
|
1361
|
+
for (const a of recent) {
|
|
1362
|
+
const icon = a.severity === 'critical' ? 'đ´' : a.severity === 'warning' ? 'đĄ' : 'âšī¸';
|
|
1363
|
+
const time = new Date(a.timestamp).toISOString().slice(0, 19);
|
|
1364
|
+
console.log(` ${icon} [${time}] ${a.type}: ${a.message}`);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
console.log('');
|
|
1368
|
+
alertMgr.close();
|
|
1369
|
+
return;
|
|
1370
|
+
}
|
|
1371
|
+
if (sub === 'counts') {
|
|
1372
|
+
const counts = alertMgr.getCounts();
|
|
1373
|
+
console.log('');
|
|
1374
|
+
console.log('đ Alert Counts');
|
|
1375
|
+
console.log(` Threshold: ${counts.threshold}`);
|
|
1376
|
+
console.log(` Anomaly: ${counts.anomaly}`);
|
|
1377
|
+
console.log(` Breach: ${counts.breach}`);
|
|
1378
|
+
console.log('');
|
|
1379
|
+
alertMgr.close();
|
|
1380
|
+
return;
|
|
1381
|
+
}
|
|
1382
|
+
console.log('Usage: relayplane alerts [list|counts]');
|
|
1383
|
+
alertMgr.close();
|
|
1384
|
+
}
|
|
1385
|
+
function handleBudgetCommand(args) {
|
|
1386
|
+
const sub = args[0] ?? 'status';
|
|
1387
|
+
const budget = (0, budget_js_1.getBudgetManager)();
|
|
1388
|
+
if (sub === 'status') {
|
|
1389
|
+
try {
|
|
1390
|
+
budget.init();
|
|
1391
|
+
}
|
|
1392
|
+
catch { /* ok */ }
|
|
1393
|
+
const status = budget.getStatus();
|
|
1394
|
+
const config = budget.getConfig();
|
|
1395
|
+
console.log('');
|
|
1396
|
+
console.log('đ° Budget Status');
|
|
1397
|
+
console.log(` Enabled: ${config.enabled ? 'â
' : 'â'}`);
|
|
1398
|
+
console.log(` Daily: $${status.dailySpend.toFixed(4)} / $${status.dailyLimit} (${status.dailyPercent.toFixed(1)}%)`);
|
|
1399
|
+
console.log(` Hourly: $${status.hourlySpend.toFixed(4)} / $${status.hourlyLimit} (${status.hourlyPercent.toFixed(1)}%)`);
|
|
1400
|
+
console.log(` Per-request: max $${config.perRequestUsd}`);
|
|
1401
|
+
console.log(` On breach: ${config.onBreach}`);
|
|
1402
|
+
if (status.breached) {
|
|
1403
|
+
console.log(` â ī¸ BREACHED: ${status.breachType}`);
|
|
1404
|
+
}
|
|
1405
|
+
console.log('');
|
|
1406
|
+
budget.close();
|
|
1407
|
+
return;
|
|
1408
|
+
}
|
|
1409
|
+
if (sub === 'set') {
|
|
1410
|
+
const daily = args.find((a, i) => args[i - 1] === '--daily');
|
|
1411
|
+
const hourly = args.find((a, i) => args[i - 1] === '--hourly');
|
|
1412
|
+
const perReq = args.find((a, i) => args[i - 1] === '--per-request');
|
|
1413
|
+
if (daily)
|
|
1414
|
+
budget.setLimits({ dailyUsd: parseFloat(daily) });
|
|
1415
|
+
if (hourly)
|
|
1416
|
+
budget.setLimits({ hourlyUsd: parseFloat(hourly) });
|
|
1417
|
+
if (perReq)
|
|
1418
|
+
budget.setLimits({ perRequestUsd: parseFloat(perReq) });
|
|
1419
|
+
console.log('â
Budget limits updated');
|
|
1420
|
+
budget.close();
|
|
1421
|
+
return;
|
|
1422
|
+
}
|
|
1423
|
+
if (sub === 'reset') {
|
|
1424
|
+
try {
|
|
1425
|
+
budget.init();
|
|
1426
|
+
}
|
|
1427
|
+
catch { /* ok */ }
|
|
1428
|
+
budget.reset();
|
|
1429
|
+
console.log('â
Budget spend reset for current window');
|
|
1430
|
+
budget.close();
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1433
|
+
console.log('Usage: relayplane budget [status|set|reset]');
|
|
1434
|
+
console.log(' set --daily <usd> --hourly <usd> --per-request <usd>');
|
|
1435
|
+
budget.close();
|
|
1436
|
+
}
|
|
1437
|
+
function handleCacheCommand(args) {
|
|
1438
|
+
const sub = args[0] ?? 'status';
|
|
1439
|
+
const cache = (0, response_cache_js_1.getResponseCache)();
|
|
1440
|
+
if (sub === 'status') {
|
|
1441
|
+
try {
|
|
1442
|
+
cache.init();
|
|
1443
|
+
}
|
|
1444
|
+
catch { /* ok */ }
|
|
1445
|
+
const status = cache.getStatus();
|
|
1446
|
+
console.log('');
|
|
1447
|
+
console.log('đĻ Response Cache Status');
|
|
1448
|
+
console.log(` Enabled: ${status.enabled ? 'â
' : 'â'}`);
|
|
1449
|
+
console.log(` Entries: ${status.entries}`);
|
|
1450
|
+
console.log(` Size: ${status.sizeMb} MB`);
|
|
1451
|
+
console.log(` Hit rate: ${status.hitRate}`);
|
|
1452
|
+
console.log(` Saved: $${status.savedCostUsd}`);
|
|
1453
|
+
console.log('');
|
|
1454
|
+
return;
|
|
1455
|
+
}
|
|
1456
|
+
if (sub === 'clear') {
|
|
1457
|
+
try {
|
|
1458
|
+
cache.init();
|
|
1459
|
+
}
|
|
1460
|
+
catch { /* ok */ }
|
|
1461
|
+
cache.clear();
|
|
1462
|
+
console.log('â
Cache cleared');
|
|
1463
|
+
return;
|
|
1464
|
+
}
|
|
1465
|
+
if (sub === 'stats') {
|
|
1466
|
+
try {
|
|
1467
|
+
cache.init();
|
|
1468
|
+
}
|
|
1469
|
+
catch { /* ok */ }
|
|
1470
|
+
const stats = cache.getStats();
|
|
1471
|
+
console.log('');
|
|
1472
|
+
console.log('đ Cache Statistics');
|
|
1473
|
+
console.log(` Total entries: ${stats.totalEntries}`);
|
|
1474
|
+
console.log(` Total size: ${(stats.totalSizeBytes / (1024 * 1024)).toFixed(2)} MB`);
|
|
1475
|
+
console.log(` Hit rate: ${(stats.hitRate * 100).toFixed(1)}%`);
|
|
1476
|
+
console.log(` Hits: ${stats.hits} Misses: ${stats.misses} Bypasses: ${stats.bypasses}`);
|
|
1477
|
+
console.log(` Saved: $${stats.savedCostUsd.toFixed(4)} across ${stats.savedRequests} requests`);
|
|
1478
|
+
console.log('');
|
|
1479
|
+
const models = Object.entries(stats.byModel);
|
|
1480
|
+
if (models.length > 0) {
|
|
1481
|
+
console.log(' By Model:');
|
|
1482
|
+
for (const [model, m] of models) {
|
|
1483
|
+
console.log(` ${model}: ${m.entries} entries, ${m.hits} hits, $${m.savedCostUsd.toFixed(4)} saved`);
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
const tasks = Object.entries(stats.byTaskType);
|
|
1487
|
+
if (tasks.length > 0) {
|
|
1488
|
+
console.log(' By Task Type:');
|
|
1489
|
+
for (const [task, t] of tasks) {
|
|
1490
|
+
console.log(` ${task}: ${t.entries} entries, ${t.hits} hits, $${t.savedCostUsd.toFixed(4)} saved`);
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
console.log('');
|
|
1494
|
+
return;
|
|
1495
|
+
}
|
|
1496
|
+
if (sub === 'on') {
|
|
1497
|
+
cache.setEnabled(true);
|
|
1498
|
+
console.log('â
Cache enabled');
|
|
1499
|
+
return;
|
|
1500
|
+
}
|
|
1501
|
+
if (sub === 'off') {
|
|
1502
|
+
cache.setEnabled(false);
|
|
1503
|
+
console.log('â
Cache disabled');
|
|
1504
|
+
return;
|
|
1505
|
+
}
|
|
1506
|
+
console.log('Usage: relayplane cache [status|clear|stats|on|off]');
|
|
1507
|
+
}
|
|
1089
1508
|
main();
|
|
1090
1509
|
//# sourceMappingURL=cli.js.map
|