@relayplane/proxy 1.9.20 → 1.9.22

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.
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ /**
3
+ * Routing Log
4
+ *
5
+ * In-memory ring buffer (1000 entries) with JSONL persistence at
6
+ * ~/.relayplane/routing-log.jsonl. Tracks all routing decisions for
7
+ * observability and debugging.
8
+ */
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.LOG_FILE = void 0;
44
+ exports._resetRoutingLog = _resetRoutingLog;
45
+ exports.initRoutingLog = initRoutingLog;
46
+ exports.appendRoutingLog = appendRoutingLog;
47
+ exports.getRoutingLog = getRoutingLog;
48
+ exports.flushRoutingLog = flushRoutingLog;
49
+ const fs = __importStar(require("node:fs"));
50
+ const path = __importStar(require("node:path"));
51
+ const os = __importStar(require("node:os"));
52
+ // ─── Constants ────────────────────────────────────────────────────────────────
53
+ const LOG_DIR = path.join(os.homedir(), '.relayplane');
54
+ exports.LOG_FILE = path.join(LOG_DIR, 'routing-log.jsonl');
55
+ const BAK_FILE = path.join(LOG_DIR, 'routing-log.jsonl.bak');
56
+ const MAX_ENTRIES = 1000;
57
+ const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 MB
58
+ // ─── State ────────────────────────────────────────────────────────────────────
59
+ let _buffer = [];
60
+ /** Reset for testing */
61
+ function _resetRoutingLog() {
62
+ _buffer = [];
63
+ }
64
+ // ─── Public API ───────────────────────────────────────────────────────────────
65
+ /**
66
+ * Initialize routing log by loading the last 1000 lines from file.
67
+ * Called once on proxy startup.
68
+ */
69
+ function initRoutingLog() {
70
+ if (!fs.existsSync(exports.LOG_FILE))
71
+ return;
72
+ try {
73
+ const content = fs.readFileSync(exports.LOG_FILE, 'utf-8');
74
+ const lines = content.split('\n').filter(l => l.trim().length > 0);
75
+ const last = lines.slice(-MAX_ENTRIES);
76
+ _buffer = [];
77
+ for (const line of last) {
78
+ try {
79
+ const entry = JSON.parse(line);
80
+ _buffer.push(entry);
81
+ }
82
+ catch {
83
+ // Skip malformed lines
84
+ }
85
+ }
86
+ }
87
+ catch {
88
+ // Non-critical — start with empty buffer
89
+ }
90
+ }
91
+ /**
92
+ * Append a routing decision to the in-memory buffer and JSONL file.
93
+ */
94
+ function appendRoutingLog(entry) {
95
+ // Ring buffer: drop oldest when full
96
+ if (_buffer.length >= MAX_ENTRIES) {
97
+ _buffer.shift();
98
+ }
99
+ _buffer.push(entry);
100
+ // Persist to file
101
+ try {
102
+ fs.mkdirSync(LOG_DIR, { recursive: true });
103
+ // Rotate if file is too large
104
+ try {
105
+ const stat = fs.statSync(exports.LOG_FILE);
106
+ if (stat.size > MAX_FILE_SIZE) {
107
+ fs.renameSync(exports.LOG_FILE, BAK_FILE);
108
+ }
109
+ }
110
+ catch {
111
+ // File may not exist yet — that's fine
112
+ }
113
+ fs.appendFileSync(exports.LOG_FILE, JSON.stringify(entry) + '\n', 'utf-8');
114
+ }
115
+ catch {
116
+ // Non-critical — don't break the proxy
117
+ }
118
+ }
119
+ /**
120
+ * Get routing log entries from the in-memory buffer with optional filters.
121
+ */
122
+ function getRoutingLog(opts) {
123
+ let entries = _buffer.slice();
124
+ if (opts?.agentFingerprint) {
125
+ const fp = opts.agentFingerprint;
126
+ entries = entries.filter(e => e.agentFingerprint === fp || e.agentName === fp);
127
+ }
128
+ if (opts?.taskType) {
129
+ const tt = opts.taskType;
130
+ entries = entries.filter(e => e.taskType === tt);
131
+ }
132
+ const limit = Math.min(opts?.limit ?? 100, MAX_ENTRIES);
133
+ return entries.slice(-limit);
134
+ }
135
+ /**
136
+ * No-op flush — writes are synchronous. Called for symmetry on shutdown.
137
+ */
138
+ function flushRoutingLog() {
139
+ // No-op: all writes are synchronous appends
140
+ }
141
+ //# sourceMappingURL=routing-log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routing-log.js","sourceRoot":"","sources":["../src/routing-log.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"standalone-proxy.d.ts","sourceRoot":"","sources":["../src/standalone-proxy.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"standalone-proxy.d.ts","sourceRoot":"","sources":["../src/standalone-proxy.ts"],"names":[],"mappings":"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"}
@@ -84,10 +84,13 @@ const anomaly_js_1 = require("./anomaly.js");
84
84
  const alerts_js_1 = require("./alerts.js");
85
85
  const downgrade_js_1 = require("./downgrade.js");
86
86
  const agent_tracker_js_1 = require("./agent-tracker.js");
87
+ const routing_log_js_1 = require("./routing-log.js");
88
+ const agent_policy_js_1 = require("./agent-policy.js");
87
89
  const version_status_js_1 = require("./utils/version-status.js");
88
90
  const signup_nudge_js_1 = require("./signup-nudge.js");
89
91
  const star_nudge_js_1 = require("./star-nudge.js");
90
92
  const estimate_js_1 = require("./estimate.js");
93
+ const telemetryPinger_js_1 = require("./telemetryPinger.js");
91
94
  // Per-IP rate limit state for /v1/estimate (60 req/min per IP)
92
95
  const estimateRateMap = new Map();
93
96
  // Fix A: Purge expired rate-limit entries every 5 minutes to prevent memory leak.
@@ -1088,11 +1091,13 @@ function extractPromptText(messages) {
1088
1091
  if (!messages || !Array.isArray(messages))
1089
1092
  return '';
1090
1093
  return messages
1094
+ .filter((msg) => Boolean(msg))
1091
1095
  .map((msg) => {
1092
1096
  if (typeof msg.content === 'string')
1093
1097
  return msg.content;
1094
1098
  if (Array.isArray(msg.content)) {
1095
1099
  return msg.content
1100
+ .filter((c) => Boolean(c))
1096
1101
  .map((c) => {
1097
1102
  const part = c;
1098
1103
  return part.type === 'text' ? (part.text ?? '') : '';
@@ -1105,12 +1110,14 @@ function extractPromptText(messages) {
1105
1110
  }
1106
1111
  function extractMessageText(messages) {
1107
1112
  return messages
1113
+ .filter((msg) => Boolean(msg))
1108
1114
  .map((msg) => {
1109
1115
  const content = msg.content;
1110
1116
  if (typeof content === 'string')
1111
1117
  return content;
1112
1118
  if (Array.isArray(content)) {
1113
1119
  return content
1120
+ .filter((c) => Boolean(c))
1114
1121
  .map((c) => {
1115
1122
  const part = c;
1116
1123
  return part.type === 'text' ? (part.text ?? '') : '';
@@ -2627,6 +2634,25 @@ td{padding:8px 12px;border-bottom:1px solid #111318}
2627
2634
  .rename-btn{background:none;border:none;cursor:pointer;font-size:.75rem;opacity:.5;padding:2px}.rename-btn:hover{opacity:1}
2628
2635
  </style></head><body>
2629
2636
  <div class="header"><div><h1>⚡ RelayPlane Dashboard</h1></div><div class="meta"><a href="/dashboard/config">Config</a> · <span id="ver"></span><span id="vstat" class="vstat unavailable">Unable to check</span> · up <span id="uptime"></span> · refreshes every 5s</div></div>
2637
+ <div id="policy-nudge" style="display:none;background:#1a1a2e;border:1px solid #4a9eff;border-radius:6px;padding:12px 16px;margin-bottom:16px;display:flex;align-items:center;justify-content:space-between;font-family:monospace;font-size:13px;color:#e0e0e0"><span>You've routed <strong id="nudge-reqs">0</strong> requests across <strong id="nudge-agents">0</strong> detected agent<span id="nudge-plural">s</span>. Run <code>relayplane policy auto</code> to optimize routing. Estimated savings: ~<strong id="nudge-savings">$0</strong>/mo.</span><div style="display:flex;gap:8px;margin-left:16px"><button onclick="fetch('/v1/policy-auto',{method:'POST'}).then(()=>location.reload())" style="background:#4a9eff;color:#000;border:none;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:12px">Run now</button><button onclick="document.getElementById('policy-nudge').style.display='none';localStorage.setItem('nudge-dismissed','1')" style="background:transparent;color:#888;border:1px solid #444;padding:4px 8px;border-radius:4px;cursor:pointer;font-size:12px">Dismiss</button></div></div>
2638
+ <script>
2639
+ (async function(){
2640
+ if(localStorage.getItem('nudge-dismissed'))return;
2641
+ try{
2642
+ const n=await fetch('/v1/policy-nudge').then(r=>r.json());
2643
+ if(n.show){
2644
+ const el=document.getElementById('policy-nudge');
2645
+ if(el){
2646
+ el.style.display='flex';
2647
+ document.getElementById('nudge-reqs').textContent=n.requestCount;
2648
+ document.getElementById('nudge-agents').textContent=n.agentCount;
2649
+ document.getElementById('nudge-plural').textContent=n.agentCount===1?'':'s';
2650
+ document.getElementById('nudge-savings').textContent='$'+n.estimatedMonthlySavings;
2651
+ }
2652
+ }
2653
+ }catch(e){}
2654
+ })();
2655
+ </script>
2630
2656
  <div class="cards">
2631
2657
  <div class="card"><div class="label">Requests (7d window, max 10k)</div><div class="value" id="totalReq">—</div><div id="totalReqDetail" style="font-size:.75rem;color:#64748b;margin-top:4px">—</div></div>
2632
2658
  <div class="card"><div class="label">Total Cost</div><div class="value" id="totalCost">—</div></div>
@@ -3413,6 +3439,8 @@ async function startProxy(config = {}) {
3413
3439
  return;
3414
3440
  }
3415
3441
  if (req.method === 'GET' && pathname === '/v1/version-status') {
3442
+ // This endpoint is hit by the dashboard, so trigger the dashboard ping.
3443
+ (0, telemetryPinger_js_1.sendPing)('dashboard');
3416
3444
  const latest = await getLatestProxyVersion();
3417
3445
  const status = (0, version_status_js_1.getVersionStatus)(PROXY_VERSION, latest);
3418
3446
  res.writeHead(200, { 'Content-Type': 'application/json', 'Cache-Control': 'public, max-age=60' });
@@ -4043,6 +4071,66 @@ async function startProxy(config = {}) {
4043
4071
  }));
4044
4072
  return;
4045
4073
  }
4074
+ // === Policy nudge endpoint ===
4075
+ if (req.method === 'GET' && pathname === '/v1/policy-nudge') {
4076
+ const log = (0, routing_log_js_1.getRoutingLog)({ limit: 100 });
4077
+ const agentCount = new Set(log.map(e => e.agentFingerprint).filter(Boolean)).size;
4078
+ const policy = (0, agent_policy_js_1.loadPolicy)();
4079
+ const policyActive = Object.keys(policy.agents ?? {}).length > 0;
4080
+ const show = log.length >= 10 && !policyActive;
4081
+ const totalCost = Object.values((0, agent_tracker_js_1.getAgentRegistry)()).reduce((sum, a) => sum + a.totalCost, 0);
4082
+ const estimatedMonthlySavings = show ? Math.round(totalCost * 0.4 * 30) : 0;
4083
+ res.writeHead(200, { 'Content-Type': 'application/json' });
4084
+ res.end(JSON.stringify({ show, requestCount: log.length, agentCount, estimatedMonthlySavings, policyActive }));
4085
+ return;
4086
+ }
4087
+ // === Policy auto endpoint (from dashboard "Run now" button) ===
4088
+ if (req.method === 'POST' && pathname === '/v1/policy-auto') {
4089
+ try {
4090
+ const { analyzeTraffic } = await import('./policy-analyzer.js');
4091
+ const { detectAvailableProviders, suggestPolicies } = await import('./policy-suggestions.js');
4092
+ const { dump: yamlDumpLocal } = await import('js-yaml');
4093
+ const analyses = await analyzeTraffic({ lookbackDays: 7 });
4094
+ if (analyses.length === 0) {
4095
+ res.writeHead(200, { 'Content-Type': 'application/json' });
4096
+ res.end(JSON.stringify({ success: false, error: 'No traffic data found' }));
4097
+ return;
4098
+ }
4099
+ const providers = detectAvailableProviders();
4100
+ const suggestions = suggestPolicies(analyses, providers);
4101
+ const isoDate = new Date().toISOString().slice(0, 10);
4102
+ const header = `# RelayPlane routing policy\n# Generated by dashboard on ${isoDate}\n# Edit manually or re-run \`relayplane policy auto\` to regenerate.\n\n`;
4103
+ const agents = {};
4104
+ for (let i = 0; i < analyses.length; i++) {
4105
+ const a = analyses[i];
4106
+ const s = suggestions[i];
4107
+ if (s.noSuggestion)
4108
+ continue;
4109
+ const entry = { fingerprint: a.fingerprint, preferred: s.suggestedModel };
4110
+ if (s.escalateTo)
4111
+ entry['escalateTo'] = s.escalateTo;
4112
+ if (s.escalateOn)
4113
+ entry['escalateOn'] = s.escalateOn;
4114
+ if (s.neverDowngrade)
4115
+ entry['neverDowngrade'] = true;
4116
+ agents[a.name] = entry;
4117
+ }
4118
+ const body = yamlDumpLocal({ version: 1, agents }, { lineWidth: 120 });
4119
+ fs.mkdirSync(path.dirname(agent_policy_js_1.POLICY_FILE), { recursive: true });
4120
+ const tmp = agent_policy_js_1.POLICY_FILE + '.tmp';
4121
+ fs.writeFileSync(tmp, header + body, 'utf-8');
4122
+ fs.renameSync(tmp, agent_policy_js_1.POLICY_FILE);
4123
+ const agentsConfigured = Object.keys(agents).length;
4124
+ res.writeHead(200, { 'Content-Type': 'application/json' });
4125
+ res.end(JSON.stringify({ success: true, agentsConfigured }));
4126
+ }
4127
+ catch (err) {
4128
+ const msg = err instanceof Error ? err.message : String(err);
4129
+ res.writeHead(500, { 'Content-Type': 'application/json' });
4130
+ res.end(JSON.stringify({ success: false, error: msg }));
4131
+ }
4132
+ return;
4133
+ }
4046
4134
  if (req.method === 'GET' && pathname === '/v1/mesh/stats') {
4047
4135
  res.writeHead(200, { 'Content-Type': 'application/json' });
4048
4136
  res.end(JSON.stringify(meshHandle.getStats()));
@@ -5990,6 +6078,29 @@ async function startProxy(config = {}) {
5990
6078
  console.log(` Streaming: ✅ Enabled`);
5991
6079
  startWatchdog();
5992
6080
  log('Health watchdog started (30s interval, sd_notify enabled)');
6081
+ // Fire startup ping.
6082
+ // Use setImmediate to ensure it runs after startup logs are printed.
6083
+ setImmediate(() => (0, telemetryPinger_js_1.sendPing)('startup'));
6084
+ // Policy nudge — check once, 5 seconds after startup, only if nudge conditions met
6085
+ setTimeout(async () => {
6086
+ try {
6087
+ const nudgeLog = (0, routing_log_js_1.getRoutingLog)({ limit: 100 });
6088
+ const nudgePolicy = (0, agent_policy_js_1.loadPolicy)();
6089
+ const nudgePolicyActive = Object.keys(nudgePolicy.agents ?? {}).length > 0;
6090
+ if (nudgeLog.length >= 10 && !nudgePolicyActive) {
6091
+ const agentCount = new Set(nudgeLog.map(e => e.agentFingerprint).filter(Boolean)).size;
6092
+ const totalCost = Object.values((0, agent_tracker_js_1.getAgentRegistry)()).reduce((s, a) => s + a.totalCost, 0);
6093
+ const savings = Math.round(totalCost * 0.4 * 30);
6094
+ console.log('');
6095
+ console.log(' ─────────────────────────────────────────────────────');
6096
+ console.log(` 💡 ${nudgeLog.length} requests routed across ${agentCount} agent${agentCount !== 1 ? 's' : ''}.`);
6097
+ console.log(` Run \`relayplane policy auto\` to optimize routing (~$${savings}/mo savings estimated).`);
6098
+ console.log(' ─────────────────────────────────────────────────────');
6099
+ console.log('');
6100
+ }
6101
+ }
6102
+ catch { /* non-critical */ }
6103
+ }, 5000);
5993
6104
  resolve(server);
5994
6105
  });
5995
6106
  });