@relayplane/proxy 1.9.18 → 1.9.21

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>
@@ -3202,6 +3228,19 @@ async function startProxy(config = {}) {
3202
3228
  log(`Budget manager init failed: ${err}`);
3203
3229
  }
3204
3230
  }
3231
+ // Initialize budget tracker (dailyCapUSD enforcement)
3232
+ const budgetTracker = (0, budget_js_1.getBudgetTracker)(proxyConfig.budget?.dailyCapUSD !== undefined
3233
+ ? { dailyCapUSD: proxyConfig.budget.dailyCapUSD, warningThreshold: proxyConfig.budget.warningThreshold }
3234
+ : undefined);
3235
+ try {
3236
+ budgetTracker.init();
3237
+ if (proxyConfig.budget?.dailyCapUSD !== undefined) {
3238
+ log(`Budget tracker initialized: dailyCapUSD=$${proxyConfig.budget.dailyCapUSD}`);
3239
+ }
3240
+ }
3241
+ catch (err) {
3242
+ log(`Budget tracker init failed: ${err}`);
3243
+ }
3205
3244
  // Initialize anomaly detector
3206
3245
  const anomalyDetector = (0, anomaly_js_1.getAnomalyDetector)(proxyConfig.anomaly);
3207
3246
  // Initialize alert manager
@@ -3269,6 +3308,16 @@ async function startProxy(config = {}) {
3269
3308
  (0, downgrade_js_1.applyDowngradeHeaders)(headers, dr);
3270
3309
  }
3271
3310
  }
3311
+ // BudgetTracker: enforce dailyCapUSD
3312
+ const trackerResult = budgetTracker.check();
3313
+ if (!trackerResult.allowed) {
3314
+ headers['x-relayplane-budget-exceeded'] = 'daily-cap';
3315
+ return { blocked: true, model: finalModel, headers, downgraded: false };
3316
+ }
3317
+ if (trackerResult.warn && trackerResult.cap !== null) {
3318
+ headers['x-relayplane-budget-warning'] =
3319
+ `${((trackerResult.spent / trackerResult.cap) * 100).toFixed(1)}% of daily cap $${trackerResult.cap}`;
3320
+ }
3272
3321
  return { blocked: false, model: finalModel, headers, downgraded };
3273
3322
  }
3274
3323
  /**
@@ -3277,6 +3326,7 @@ async function startProxy(config = {}) {
3277
3326
  function postRequestRecord(model, tokensIn, tokensOut, costUsd) {
3278
3327
  // Record spend
3279
3328
  budgetManager.recordSpend(costUsd, model);
3329
+ budgetTracker.record(costUsd, model);
3280
3330
  // Anomaly detection
3281
3331
  const anomalyResult = anomalyDetector.recordAndAnalyze({
3282
3332
  model,
@@ -3307,6 +3357,10 @@ async function startProxy(config = {}) {
3307
3357
  proxyConfig = await loadProxyConfig(configPath, log);
3308
3358
  cooldownManager.updateConfig(getCooldownConfig(proxyConfig));
3309
3359
  budgetManager.updateConfig({ ...budgetManager.getConfig(), ...(proxyConfig.budget ?? {}) });
3360
+ budgetTracker.updateConfig({
3361
+ dailyCapUSD: proxyConfig.budget?.dailyCapUSD,
3362
+ warningThreshold: proxyConfig.budget?.warningThreshold,
3363
+ });
3310
3364
  anomalyDetector.updateConfig({ ...anomalyDetector.getConfig(), ...(proxyConfig.anomaly ?? {}) });
3311
3365
  alertManager.updateConfig({ ...alertManager.getConfig(), ...(proxyConfig.alerts ?? {}) });
3312
3366
  downgradeConfig = { ...downgrade_js_1.DEFAULT_DOWNGRADE_CONFIG, ...(proxyConfig.downgrade ?? {}) };
@@ -3385,6 +3439,8 @@ async function startProxy(config = {}) {
3385
3439
  return;
3386
3440
  }
3387
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');
3388
3444
  const latest = await getLatestProxyVersion();
3389
3445
  const status = (0, version_status_js_1.getVersionStatus)(PROXY_VERSION, latest);
3390
3446
  res.writeHead(200, { 'Content-Type': 'application/json', 'Cache-Control': 'public, max-age=60' });
@@ -4015,6 +4071,66 @@ async function startProxy(config = {}) {
4015
4071
  }));
4016
4072
  return;
4017
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
+ }
4018
4134
  if (req.method === 'GET' && pathname === '/v1/mesh/stats') {
4019
4135
  res.writeHead(200, { 'Content-Type': 'application/json' });
4020
4136
  res.end(JSON.stringify(meshHandle.getStats()));
@@ -5962,6 +6078,29 @@ async function startProxy(config = {}) {
5962
6078
  console.log(` Streaming: ✅ Enabled`);
5963
6079
  startWatchdog();
5964
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);
5965
6104
  resolve(server);
5966
6105
  });
5967
6106
  });