@rynfar/meridian 1.50.0 → 1.51.0

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.
Files changed (40) hide show
  1. package/README.md +51 -19
  2. package/assets/banner.svg +28 -21
  3. package/assets/bot-icon-512.png +0 -0
  4. package/assets/bot-icon.svg +49 -43
  5. package/assets/how-it-works.svg +40 -40
  6. package/assets/icon-512.png +0 -0
  7. package/assets/icon.svg +16 -13
  8. package/assets/logo-512.png +0 -0
  9. package/assets/logo.svg +14 -12
  10. package/dist/{cli-yw8cx02m.js → cli-7df5eg5d.js} +828 -164
  11. package/dist/{cli-q6dz9r5p.js → cli-aybvv9nb.js} +7 -1
  12. package/dist/cli-xmweegb1.js +179 -0
  13. package/dist/cli.js +4 -4
  14. package/dist/{pluginPage-85s6t6k8.js → pluginPage-03q5j753.js} +4 -10
  15. package/dist/{profileCli-fbhe9p5z.js → profileCli-j01rr99j.js} +1 -1
  16. package/dist/{profilePage-txxpbc44.js → profilePage-naychnb8.js} +6 -5
  17. package/dist/proxy/oauthUsage.d.ts.map +1 -1
  18. package/dist/proxy/plugins/pluginPage.d.ts.map +1 -1
  19. package/dist/proxy/server.d.ts.map +1 -1
  20. package/dist/server.js +3 -3
  21. package/dist/telemetry/dashboard.d.ts.map +1 -1
  22. package/dist/telemetry/landing.d.ts +6 -2
  23. package/dist/telemetry/landing.d.ts.map +1 -1
  24. package/dist/telemetry/percentiles.d.ts +5 -1
  25. package/dist/telemetry/percentiles.d.ts.map +1 -1
  26. package/dist/telemetry/pricing.d.ts +63 -0
  27. package/dist/telemetry/pricing.d.ts.map +1 -0
  28. package/dist/telemetry/pricingStore.d.ts +32 -0
  29. package/dist/telemetry/pricingStore.d.ts.map +1 -0
  30. package/dist/telemetry/profileBar.d.ts +19 -9
  31. package/dist/telemetry/profileBar.d.ts.map +1 -1
  32. package/dist/telemetry/profileUsage.d.ts.map +1 -1
  33. package/dist/telemetry/settingsPage.d.ts +1 -1
  34. package/dist/telemetry/settingsPage.d.ts.map +1 -1
  35. package/dist/telemetry/sqlite.d.ts.map +1 -1
  36. package/dist/telemetry/store.d.ts.map +1 -1
  37. package/dist/telemetry/types.d.ts +31 -0
  38. package/dist/telemetry/types.d.ts.map +1 -1
  39. package/package.json +1 -1
  40. package/dist/cli-4rqtm83g.js +0 -148
@@ -1,3 +1,7 @@
1
+ import {
2
+ __esm
3
+ } from "./cli-p9swy5t3.js";
4
+
1
5
  // src/env.ts
2
6
  function env(suffix) {
3
7
  return process.env[`MERIDIAN_${suffix}`] ?? process.env[`CLAUDE_PROXY_${suffix}`];
@@ -21,8 +25,10 @@ function envInt(suffix, defaultValue) {
21
25
  const parsed = parseInt(val, 10);
22
26
  return Number.isFinite(parsed) ? parsed : defaultValue;
23
27
  }
28
+ var init_env = () => {};
24
29
 
25
30
  // src/proxy/models.ts
31
+ init_env();
26
32
  import { exec as execCallback, execFile as execFileCallback } from "child_process";
27
33
  import { existsSync, statSync } from "fs";
28
34
  import { fileURLToPath } from "url";
@@ -312,4 +318,4 @@ function isClosedControllerError(error) {
312
318
  return error.message.includes("Controller is already closed");
313
319
  }
314
320
 
315
- export { env, envBool, resolvePassthrough, envInt, resolveSdkModelDefaults, mapModelToClaudeModel, recordExtendedContextUnavailable, stripExtendedContext, hasExtendedContext, getAuthCacheInfo, getClaudeAuthStatusAsync, resolveClaudeExecutableSync, getResolvedClaudeExecutableInfo, resolveClaudeExecutableAsync, isClosedControllerError };
321
+ export { env, envBool, resolvePassthrough, envInt, init_env, resolveSdkModelDefaults, mapModelToClaudeModel, recordExtendedContextUnavailable, stripExtendedContext, hasExtendedContext, getAuthCacheInfo, getClaudeAuthStatusAsync, resolveClaudeExecutableSync, getResolvedClaudeExecutableInfo, resolveClaudeExecutableAsync, isClosedControllerError };
@@ -0,0 +1,179 @@
1
+ import {
2
+ __esm
3
+ } from "./cli-p9swy5t3.js";
4
+
5
+ // src/telemetry/profileBar.ts
6
+ var themeCss = `
7
+ :root {
8
+ /* Cool-gray neutral palette. High contrast, surface/border separation,
9
+ no color cast muddying the text. Blue is the primary accent; violet
10
+ is the fixed secondary — used together in the brand gradient and
11
+ individually for hover states and a handful of telemetry badges. */
12
+ --bg: #0d1117;
13
+ --surface: #161b22;
14
+ --surface2: #1c2128;
15
+ --border: #30363d;
16
+ /* Text */
17
+ --text: #e6edf3;
18
+ --muted: #8b949e;
19
+ /* Brand — blue primary, violet secondary */
20
+ --accent: #58a6ff;
21
+ --accent2: #bc8cff;
22
+ --violet: #bc8cff;
23
+ --lavender: #d2a8ff;
24
+ /* Semantic */
25
+ --green: #3fb950;
26
+ --yellow: #d29922;
27
+ --red: #f85149;
28
+ /* Telemetry-specific aliases (waterfall + lineage badges) */
29
+ --blue: #58a6ff;
30
+ --purple: #bc8cff;
31
+ --queue: #d29922;
32
+ --ttfb: #58a6ff;
33
+ --upstream: #3fb950;
34
+ }
35
+ /* Banner backsplash — the brand look: a gentle diagonal wash with soft
36
+ blue (top-left) and violet (bottom-right) glows. Pages must not set
37
+ their own body background so this shows through everywhere. */
38
+ body {
39
+ background:
40
+ radial-gradient(1200px 800px at 12% -8%, rgba(88,166,255,0.07), transparent 60%),
41
+ radial-gradient(1100px 800px at 92% 108%, rgba(188,140,255,0.06), transparent 60%),
42
+ linear-gradient(135deg, #0d1117 0%, #10151d 55%, #161b22 100%);
43
+ background-attachment: fixed;
44
+ background-color: var(--bg);
45
+ }
46
+ `, meridianLogoSvg = `<svg class="mh-logo" width="24" height="24" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
47
+ <defs>
48
+ <linearGradient id="mhGrad" x1="32" y1="4" x2="32" y2="60" gradientUnits="userSpaceOnUse">
49
+ <stop stop-color="#58a6ff"/>
50
+ <stop offset="1" stop-color="#bc8cff"/>
51
+ </linearGradient>
52
+ </defs>
53
+ <circle cx="32" cy="32" r="25" stroke="url(#mhGrad)" stroke-width="3.5"/>
54
+ <ellipse cx="32" cy="32" rx="10.5" ry="25" stroke="url(#mhGrad)" stroke-width="2.5" opacity="0.8"/>
55
+ <path d="M7 32h50" stroke="url(#mhGrad)" stroke-width="2" opacity="0.4"/>
56
+ <circle cx="32" cy="7" r="4.5" fill="#58a6ff"/>
57
+ <circle cx="32" cy="57" r="4.5" fill="#bc8cff"/>
58
+ </svg>`, profileBarCss = `
59
+ .meridian-header {
60
+ position: sticky; top: 0; z-index: 100;
61
+ display: flex; align-items: center; gap: 20px;
62
+ padding: 10px 24px;
63
+ background: rgba(13, 17, 23, 0.92);
64
+ backdrop-filter: blur(12px);
65
+ border-bottom: 1px solid var(--border, #30363d);
66
+ }
67
+ .meridian-header .mh-brand {
68
+ display: flex; align-items: center; gap: 10px;
69
+ text-decoration: none; color: var(--text, #e6edf3);
70
+ }
71
+ .meridian-header .mh-logo { display: block; }
72
+ .meridian-header .mh-name {
73
+ font-size: 15px; font-weight: 700; letter-spacing: 2px;
74
+ text-transform: uppercase;
75
+ }
76
+ .meridian-header .mh-nav { display: flex; align-items: center; gap: 2px; }
77
+ .meridian-header .mh-nav a {
78
+ color: var(--muted, #8b949e); text-decoration: none; font-size: 12px;
79
+ font-weight: 500; padding: 5px 10px; border-radius: 6px;
80
+ transition: color 0.15s, background 0.15s;
81
+ }
82
+ .meridian-header .mh-nav a:hover { color: var(--text, #e6edf3); background: var(--surface, #161b22); }
83
+ .meridian-header .mh-nav a.active { color: var(--accent, #58a6ff); background: var(--surface, #161b22); }
84
+ .meridian-header .mh-right {
85
+ margin-left: auto; display: flex; align-items: center; gap: 10px;
86
+ }
87
+ .meridian-header .mh-profile {
88
+ display: none; align-items: center; gap: 6px;
89
+ font-size: 11px; font-weight: 500; color: var(--text, #e6edf3);
90
+ padding: 3px 10px; border-radius: 20px;
91
+ background: var(--surface, #161b22); border: 1px solid var(--border, #30363d);
92
+ text-decoration: none; transition: border-color 0.15s;
93
+ }
94
+ .meridian-header .mh-profile:hover { border-color: var(--accent, #58a6ff); }
95
+ .meridian-header .mh-profile.visible { display: inline-flex; }
96
+ .meridian-header .mh-profile .mh-profile-type {
97
+ color: var(--muted, #8b949e); font-size: 10px;
98
+ }
99
+ .meridian-header .mh-status {
100
+ display: inline-flex; align-items: center; gap: 6px;
101
+ font-size: 11px; color: var(--muted, #8b949e); white-space: nowrap;
102
+ }
103
+ .meridian-header .mh-dot {
104
+ width: 8px; height: 8px; border-radius: 50%;
105
+ background: var(--muted, #8b949e); flex-shrink: 0;
106
+ }
107
+ .meridian-header .mh-dot.healthy { background: var(--green, #3fb950); box-shadow: 0 0 6px rgba(63,185,80,0.5); }
108
+ .meridian-header .mh-dot.degraded { background: var(--yellow, #d29922); }
109
+ .meridian-header .mh-dot.unhealthy { background: var(--red, #f85149); }
110
+ @media (max-width: 720px) {
111
+ .meridian-header { gap: 10px; padding: 10px 16px; flex-wrap: wrap; }
112
+ .meridian-header .mh-name { display: none; }
113
+ .meridian-header .mh-status .mh-status-text { display: none; }
114
+ }
115
+ `, profileBarHtml, profileBarJs = `
116
+ (function() {
117
+ var profileChip = document.getElementById('mhProfile');
118
+ var statusDot = document.getElementById('mhDot');
119
+ var statusText = document.getElementById('mhStatusText');
120
+
121
+ // Highlight active nav link
122
+ var path = location.pathname;
123
+ var navLinks = document.querySelectorAll('.mh-nav a');
124
+ navLinks.forEach(function(a) {
125
+ if (a.getAttribute('href') === path || (path === '/telemetry' && a.id === 'nav-telemetry') || (path === '/' && a.id === 'nav-home')) {
126
+ a.classList.add('active');
127
+ }
128
+ });
129
+
130
+ function esc(s) { var d = document.createElement('div'); d.textContent = s; return d.innerHTML; }
131
+
132
+ function loadHeader() {
133
+ fetch('/health').then(function(r) { return r.json(); }).then(function(h) {
134
+ var st = h.status === 'healthy' ? 'healthy' : h.status === 'degraded' ? 'degraded' : 'unhealthy';
135
+ statusDot.className = 'mh-dot ' + st;
136
+ statusText.textContent = st === 'healthy' ? 'Operational' : st === 'degraded' ? 'Degraded' : 'Offline';
137
+ }).catch(function() {
138
+ statusDot.className = 'mh-dot unhealthy';
139
+ statusText.textContent = 'Offline';
140
+ });
141
+
142
+ fetch('/profiles/list').then(function(r) { return r.json(); }).then(function(data) {
143
+ var current = (data.profiles || []).find(function(p) { return p.isActive; });
144
+ if (!current) { profileChip.classList.remove('visible'); return; }
145
+ profileChip.innerHTML = esc(current.id) + ' <span class="mh-profile-type">' + esc(current.type || '') + '</span>';
146
+ profileChip.classList.add('visible');
147
+ }).catch(function() {});
148
+ }
149
+
150
+ loadHeader();
151
+ setInterval(loadHeader, 10000);
152
+ // Pages call this after mutating state (e.g. switching the active profile)
153
+ // so the header chip updates immediately instead of on the next poll.
154
+ window.meridianHeaderRefresh = loadHeader;
155
+ })();
156
+ `;
157
+ var init_profileBar = __esm(() => {
158
+ profileBarHtml = `
159
+ <header class="meridian-header" id="meridianHeader">
160
+ <a class="mh-brand" href="/">
161
+ ${meridianLogoSvg}
162
+ <span class="mh-name">Meridian</span>
163
+ </a>
164
+ <nav class="mh-nav">
165
+ <a href="/" id="nav-home">Home</a>
166
+ <a href="/telemetry" id="nav-telemetry">Telemetry</a>
167
+ <a href="/profiles" id="nav-profiles">Profiles</a>
168
+ <a href="/settings" id="nav-settings">Settings</a>
169
+ <a href="/plugins" id="nav-plugins">Plugins</a>
170
+ </nav>
171
+ <div class="mh-right">
172
+ <a class="mh-profile" id="mhProfile" href="/" title="Active profile — switch from the home page"></a>
173
+ <span class="mh-status" id="mhStatus"><span class="mh-dot" id="mhDot"></span><span class="mh-status-text" id="mhStatusText"></span></span>
174
+ </div>
175
+ </header>
176
+ `;
177
+ });
178
+
179
+ export { themeCss, profileBarCss, profileBarHtml, profileBarJs, init_profileBar };
package/dist/cli.js CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startProxyServer
4
- } from "./cli-yw8cx02m.js";
4
+ } from "./cli-7df5eg5d.js";
5
5
  import"./cli-f0yqy2d2.js";
6
6
  import"./cli-sry5aqdj.js";
7
- import"./cli-4rqtm83g.js";
7
+ import"./cli-xmweegb1.js";
8
8
  import {
9
9
  resolveClaudeExecutableAsync
10
- } from "./cli-q6dz9r5p.js";
10
+ } from "./cli-aybvv9nb.js";
11
11
  import"./cli-340h1chz.js";
12
12
  import"./cli-je60fevk.js";
13
13
  import"./cli-aq5zz92m.js";
@@ -53,7 +53,7 @@ See https://github.com/rynfar/meridian for full documentation.`);
53
53
  process.exit(0);
54
54
  }
55
55
  if (args[0] === "profile") {
56
- const { profileAdd, profileAddOauthToken, profileList, profileRemove, profileSwitch, profileLogin, profileHelp } = await import("./profileCli-fbhe9p5z.js");
56
+ const { profileAdd, profileAddOauthToken, profileList, profileRemove, profileSwitch, profileLogin, profileHelp } = await import("./profileCli-j01rr99j.js");
57
57
  const subcommand = args[1];
58
58
  const profileId = args[2];
59
59
  const headless = args.includes("--headless");
@@ -4,7 +4,7 @@ import {
4
4
  profileBarHtml,
5
5
  profileBarJs,
6
6
  themeCss
7
- } from "./cli-4rqtm83g.js";
7
+ } from "./cli-xmweegb1.js";
8
8
  import"./cli-p9swy5t3.js";
9
9
 
10
10
  // src/proxy/plugins/pluginPage.ts
@@ -19,13 +19,9 @@ var pluginPageHtml = `<!DOCTYPE html>
19
19
  ${themeCss}
20
20
  * { box-sizing: border-box; margin: 0; padding: 0; }
21
21
  body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
22
- background: var(--bg); color: var(--text); line-height: 1.6; min-height: 100vh; }
22
+ color: var(--text); line-height: 1.6; min-height: 100vh; }
23
23
  .container { max-width: 960px; margin: 0 auto; padding: 32px 24px; }
24
24
 
25
- .back-link { display: inline-flex; align-items: center; gap: 6px; color: var(--muted);
26
- text-decoration: none; font-size: 13px; margin-bottom: 24px; transition: color 0.15s; }
27
- .back-link:hover { color: var(--text); }
28
-
29
25
  .page-header { display: flex; align-items: flex-start; justify-content: space-between;
30
26
  gap: 16px; margin-bottom: 6px; flex-wrap: wrap; }
31
27
  .page-header h1 { font-size: 24px; font-weight: 700; letter-spacing: 0.5px; }
@@ -34,7 +30,7 @@ var pluginPageHtml = `<!DOCTYPE html>
34
30
  .reload-btn { padding: 8px 18px; font-size: 13px; font-weight: 500;
35
31
  background: var(--surface2); color: var(--accent); border: 1px solid var(--accent);
36
32
  border-radius: 8px; cursor: pointer; transition: all 0.15s; white-space: nowrap; }
37
- .reload-btn:hover { background: rgba(139,92,246,0.15); }
33
+ .reload-btn:hover { background: rgba(88,166,255,0.12); }
38
34
  .reload-btn:disabled { opacity: 0.5; cursor: default; }
39
35
  .reload-btn.loading { opacity: 0.7; }
40
36
 
@@ -84,7 +80,7 @@ var pluginPageHtml = `<!DOCTYPE html>
84
80
  .empty-state p { font-size: 13px; line-height: 1.7; }
85
81
  .empty-state code { font-family: 'SF Mono', SFMono-Regular, Consolas, monospace;
86
82
  font-size: 12px; background: var(--surface2); padding: 2px 7px;
87
- border-radius: 4px; color: #a78bfa; }
83
+ border-radius: 4px; color: var(--accent2); }
88
84
 
89
85
  /* ── Hero panel: aggregate stats at a glance ── */
90
86
  .hero-panel { background: linear-gradient(135deg, var(--surface) 0%, var(--surface2) 100%);
@@ -145,8 +141,6 @@ var pluginPageHtml = `<!DOCTYPE html>
145
141
  <body>
146
142
  ` + profileBarHtml + `
147
143
  <div class="container">
148
- <a href="/" class="back-link">&#8592; Back to Meridian</a>
149
-
150
144
  <div class="page-header">
151
145
  <div>
152
146
  <h1>Plugins</h1>
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resolveClaudeExecutableSync
3
- } from "./cli-q6dz9r5p.js";
3
+ } from "./cli-aybvv9nb.js";
4
4
  import {
5
5
  setSetting
6
6
  } from "./cli-340h1chz.js";
@@ -4,7 +4,7 @@ import {
4
4
  profileBarHtml,
5
5
  profileBarJs,
6
6
  themeCss
7
- } from "./cli-4rqtm83g.js";
7
+ } from "./cli-xmweegb1.js";
8
8
  import"./cli-p9swy5t3.js";
9
9
 
10
10
  // src/telemetry/profilePage.ts
@@ -16,6 +16,7 @@ var WINDOW_LABELS = {
16
16
  seven_day: "7d",
17
17
  seven_day_opus: "7d Opus",
18
18
  seven_day_sonnet: "7d Sonnet",
19
+ seven_day_fable: "7d Fable",
19
20
  seven_day_oauth_apps: "7d Apps",
20
21
  seven_day_cowork: "7d Cowork",
21
22
  seven_day_omelette: "7d Omelette"
@@ -33,7 +34,7 @@ var profilePageHtml = `<!DOCTYPE html>
33
34
  ${themeCss}
34
35
  * { box-sizing: border-box; margin: 0; padding: 0; }
35
36
  body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
36
- background: var(--bg); color: var(--text); padding: 0; line-height: 1.5; }
37
+ color: var(--text); padding: 0; line-height: 1.5; }
37
38
  .container { max-width: 800px; margin: 0 auto; padding: 24px; }
38
39
  h1 { font-size: 20px; font-weight: 600; margin-bottom: 4px; }
39
40
  .subtitle { color: var(--muted); font-size: 13px; margin-bottom: 24px; }
@@ -85,7 +86,7 @@ var profilePageHtml = `<!DOCTYPE html>
85
86
  .guide li { margin-bottom: 8px; }
86
87
  .guide code {
87
88
  font-family: 'SF Mono', SFMono-Regular, Consolas, monospace; font-size: 12px;
88
- background: var(--bg); padding: 2px 6px; border-radius: 4px; color: var(--purple);
89
+ background: var(--bg); padding: 2px 6px; border-radius: 4px; color: var(--accent2);
89
90
  }
90
91
  .guide .warn {
91
92
  margin-top: 12px; padding: 12px 16px; background: rgba(210,153,34,0.1);
@@ -96,7 +97,7 @@ var profilePageHtml = `<!DOCTYPE html>
96
97
  .mono { font-family: 'SF Mono', SFMono-Regular, Consolas, monospace; font-size: 12px; }
97
98
  .copy-cmd {
98
99
  font-family: 'SF Mono', SFMono-Regular, Consolas, monospace; font-size: 12px;
99
- background: var(--bg); padding: 4px 10px; border-radius: 4px; color: var(--purple);
100
+ background: var(--bg); padding: 4px 10px; border-radius: 4px; color: var(--accent2);
100
101
  cursor: pointer; border: 1px solid var(--border); transition: border-color 0.15s;
101
102
  }
102
103
  .copy-btn {
@@ -185,7 +186,7 @@ var profilePageHtml = `<!DOCTYPE html>
185
186
 
186
187
  <h3 style="margin-top:16px">Switching profiles</h3>
187
188
  <ol>
188
- <li><strong>UI:</strong> Use the dropdown at the top of this page</li>
189
+ <li><strong>UI:</strong> Click an account card on the <a href="/" style="color:var(--accent)">home page</a>, or the Switch button on this page</li>
189
190
  <li><strong>CLI:</strong> <code>meridian profile switch &lt;name&gt;</code></li>
190
191
  <li><strong>Per-request:</strong> Send <code>x-meridian-profile: &lt;name&gt;</code> header</li>
191
192
  </ol>
@@ -1 +1 @@
1
- {"version":3,"file":"oauthUsage.d.ts","sourceRoot":"","sources":["../../src/proxy/oauthUsage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EAAoD,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAgCvG,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,OAAO,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,gBAAgB,EAAE,CAAA;IAC3B,UAAU,EAAE,mBAAmB,GAAG,IAAI,CAAA;IACtC,SAAS,EAAE,MAAM,CAAA;CAClB;AA6DD;8DAC8D;AAC9D,KAAK,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAmBzE,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,KAAK,CAAC,EAAE,eAAe,CAAA;IACvB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,SAAS,CAAC,EAAE,SAAS,CAAA;CACtB;AAcD,8CAA8C;AAC9C,wBAAgB,4BAA4B,CAC1C,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,mBAAmB,KAAK,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,GAC9E,IAAI,CAEN;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,eAAe,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAOpG;AAqDD,qFAAqF;AACrF,wBAAgB,oBAAoB,IAAI,IAAI,CAG3C"}
1
+ {"version":3,"file":"oauthUsage.d.ts","sourceRoot":"","sources":["../../src/proxy/oauthUsage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EAAoD,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAA;AA6CvG,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,OAAO,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,gBAAgB,EAAE,CAAA;IAC3B,UAAU,EAAE,mBAAmB,GAAG,IAAI,CAAA;IACtC,SAAS,EAAE,MAAM,CAAA;CAClB;AAyFD;8DAC8D;AAC9D,KAAK,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAmBzE,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,KAAK,CAAC,EAAE,eAAe,CAAA;IACvB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,SAAS,CAAC,EAAE,SAAS,CAAA;CACtB;AAcD,8CAA8C;AAC9C,wBAAgB,4BAA4B,CAC1C,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,mBAAmB,KAAK,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,GAC9E,IAAI,CAEN;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,eAAe,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAOpG;AAqDD,qFAAqF;AACrF,wBAAgB,oBAAoB,IAAI,IAAI,CAG3C"}
@@ -1 +1 @@
1
- {"version":3,"file":"pluginPage.d.ts","sourceRoot":"","sources":["../../../src/proxy/plugins/pluginPage.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,eAAO,MAAM,cAAc,QAsYnB,CAAA"}
1
+ {"version":3,"file":"pluginPage.d.ts","sourceRoot":"","sources":["../../../src/proxy/plugins/pluginPage.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,eAAO,MAAM,cAAc,QAgYnB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/proxy/server.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACtE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,CAAA;AAGvD,YAAY,EACV,SAAS,EACT,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,WAAW,GACZ,MAAM,aAAa,CAAA;AAKpB,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAuCnG,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EAEpB,KAAK,aAAa,EAGnB,MAAM,mBAAmB,CAAA;AAG1B,OAAO,EAA+B,iBAAiB,EAAE,mBAAmB,EAAsC,MAAM,iBAAiB,CAAA;AAGzI,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAA;AAChE,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,CAAA;AACjD,YAAY,EAAE,aAAa,EAAE,CAAA;AA+Q7B,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CAm4GhF;AAWD,wBAAgB,gCAAgC,IAAI,IAAI,CAavD;AAED,wBAAsB,gBAAgB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAmGhG"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/proxy/server.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACtE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,CAAA;AAGvD,YAAY,EACV,SAAS,EACT,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,WAAW,GACZ,MAAM,aAAa,CAAA;AAKpB,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAuCnG,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EAEpB,KAAK,aAAa,EAGnB,MAAM,mBAAmB,CAAA;AAG1B,OAAO,EAA+B,iBAAiB,EAAE,mBAAmB,EAAsC,MAAM,iBAAiB,CAAA;AAGzI,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAA;AAChE,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,CAAA;AACjD,YAAY,EAAE,aAAa,EAAE,CAAA;AA+Q7B,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CAo6GhF;AAWD,wBAAgB,gCAAgC,IAAI,IAAI,CAavD;AAED,wBAAsB,gBAAgB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAmGhG"}
package/dist/server.js CHANGED
@@ -11,11 +11,11 @@ import {
11
11
  runObserveHook,
12
12
  runTransformHook,
13
13
  startProxyServer
14
- } from "./cli-yw8cx02m.js";
14
+ } from "./cli-7df5eg5d.js";
15
15
  import"./cli-f0yqy2d2.js";
16
16
  import"./cli-sry5aqdj.js";
17
- import"./cli-4rqtm83g.js";
18
- import"./cli-q6dz9r5p.js";
17
+ import"./cli-xmweegb1.js";
18
+ import"./cli-aybvv9nb.js";
19
19
  import"./cli-340h1chz.js";
20
20
  import"./cli-je60fevk.js";
21
21
  import"./cli-aq5zz92m.js";
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../../src/telemetry/dashboard.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,eAAO,MAAM,aAAa,QAwdlB,CAAA"}
1
+ {"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../../src/telemetry/dashboard.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,eAAO,MAAM,aAAa,QAkhBlB,CAAA"}
@@ -1,7 +1,11 @@
1
1
  /**
2
2
  * Meridian landing page.
3
- * Shows system status, account info, quick stats, and agent setup snippets.
4
- * Fetches /health and /telemetry/summary client-side for live data.
3
+ *
4
+ * The at-a-glance dashboard: a short how-it-works intro, per-account cards
5
+ * (usage + est. cost, click to switch the active profile), and a compact
6
+ * 24h traffic strip. Site chrome (logo, nav, status) lives in the shared
7
+ * header from profileBar.ts. Fetches /health, /telemetry/summary,
8
+ * /v1/usage/quota/all and /profiles/list client-side for live data.
5
9
  */
6
10
  export declare const landingHtml: string;
7
11
  //# sourceMappingURL=landing.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"landing.d.ts","sourceRoot":"","sources":["../../src/telemetry/landing.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,eAAO,MAAM,WAAW,QAoHhB,CAAA"}
1
+ {"version":3,"file":"landing.d.ts","sourceRoot":"","sources":["../../src/telemetry/landing.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,eAAO,MAAM,WAAW,QAkNhB,CAAA"}
@@ -2,11 +2,15 @@
2
2
  * Shared percentile computation used by all telemetry store implementations.
3
3
  */
4
4
  import type { RequestMetric, PhaseTiming, TelemetrySummary } from "./types";
5
+ import { type ModelPricing } from "./pricing";
5
6
  export declare function computePercentiles(values: number[]): PhaseTiming;
6
7
  /**
7
8
  * Compute a TelemetrySummary from an array of RequestMetric.
8
9
  * Both MemoryTelemetryStore and SqliteTelemetryStore use this
9
10
  * to guarantee identical output.
11
+ *
12
+ * pricingOverrides: user-defined model rates (from pricingStore) applied
13
+ * to the cost estimate; omit for built-in pricing only.
10
14
  */
11
- export declare function computeSummary(metrics: RequestMetric[], windowMs: number): TelemetrySummary;
15
+ export declare function computeSummary(metrics: RequestMetric[], windowMs: number, pricingOverrides?: Record<string, ModelPricing>): TelemetrySummary;
12
16
  //# sourceMappingURL=percentiles.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"percentiles.d.ts","sourceRoot":"","sources":["../../src/telemetry/percentiles.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE3E,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,WAAW,CAchE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,aAAa,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CA0G3F"}
1
+ {"version":3,"file":"percentiles.d.ts","sourceRoot":"","sources":["../../src/telemetry/percentiles.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAC3E,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,WAAW,CAAA;AAElE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,WAAW,CAchE;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,aAAa,EAAE,EACxB,QAAQ,EAAE,MAAM,EAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GAC9C,gBAAgB,CA4GlB"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Static Anthropic API list pricing and cost estimation (PURE, no I/O).
3
+ *
4
+ * Meridian proxies Claude Max, so requests are covered by the subscription
5
+ * and never billed per token. The numbers produced here answer a different
6
+ * question: "what would this usage have cost at API list prices?", useful
7
+ * for judging how much value the subscription is delivering.
8
+ *
9
+ * Rates are USD per million tokens, verified against the official pricing
10
+ * docs (platform.claude.com/docs/en/about-claude/pricing, snapshot 2026-07).
11
+ * Users can override any rate, or add models missing from this table, via
12
+ * the settings page (persisted by pricingStore.ts; overrides win in
13
+ * resolveModelPricing). Cache rates are derived from the input rate:
14
+ * - cache read = 0.10× input
15
+ * - cache write = 1.25× input (5-minute TTL, the TTL the SDK uses, see
16
+ * MONITORING.md; 1-hour TTL writes bill at 2×, so if a client opted into
17
+ * the long TTL this estimate is a floor)
18
+ *
19
+ * Models without a table entry are excluded from the total and surfaced via
20
+ * `unpricedRequestCount` instead of silently costing $0.
21
+ */
22
+ import type { RequestMetric, CostEstimate } from "./types";
23
+ export interface ModelPricing {
24
+ /** USD per 1M uncached input tokens */
25
+ inputPerMTok: number;
26
+ /** USD per 1M output tokens */
27
+ outputPerMTok: number;
28
+ /** USD per 1M cache-read input tokens */
29
+ cacheReadPerMTok: number;
30
+ /** USD per 1M cache-write (creation) input tokens */
31
+ cacheWritePerMTok: number;
32
+ }
33
+ export declare const CACHE_READ_MULTIPLIER = 0.1;
34
+ export declare const CACHE_WRITE_MULTIPLIER = 1.25;
35
+ /** Normalize a model string for pricing lookup: trimmed, lowercase, [1m] suffix stripped. */
36
+ export declare function normalizeModelKey(model: string): string;
37
+ /**
38
+ * Exact-match table. Keys are lowercase, checked after stripping the [1m]
39
+ * suffix. Covers the SDK aliases meridian itself uses (opus, sonnet, ...)
40
+ * plus concrete API model IDs that clients commonly send. Dated snapshot
41
+ * IDs (e.g. claude-haiku-4-5-20251001) fall through to the family rules.
42
+ *
43
+ * Exported for the settings page, which lists these models with their
44
+ * default rates so users can override them.
45
+ */
46
+ export declare const BUILTIN_MODEL_PRICING: Record<string, ModelPricing>;
47
+ /**
48
+ * Resolve a model string to pricing. User-defined overrides win, then the
49
+ * exact built-in table, then family fallback so versioned/dated IDs
50
+ * (claude-opus-4-8, claude-haiku-4-5-20251001) and future releases still
51
+ * price at their family's current rate.
52
+ * Returns null for unrecognized models; callers must not treat that as $0.
53
+ */
54
+ export declare function resolveModelPricing(model: string, overrides?: Record<string, ModelPricing>): ModelPricing | null;
55
+ /** Estimated USD for a single request's token usage at the given rates. */
56
+ export declare function estimateRequestCostUsd(metric: RequestMetric, pricing: ModelPricing): number;
57
+ /**
58
+ * Aggregate estimated cost per model across a set of metrics.
59
+ * Grouping key is requestModel || model, matching computeSummary's byModel.
60
+ * Pass overrides (from pricingStore) to apply user-defined rates.
61
+ */
62
+ export declare function computeCostEstimate(metrics: RequestMetric[], overrides?: Record<string, ModelPricing>): CostEstimate;
63
+ //# sourceMappingURL=pricing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../../src/telemetry/pricing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAsB,MAAM,SAAS,CAAA;AAE9E,MAAM,WAAW,YAAY;IAC3B,uCAAuC;IACvC,YAAY,EAAE,MAAM,CAAA;IACpB,+BAA+B;IAC/B,aAAa,EAAE,MAAM,CAAA;IACrB,yCAAyC;IACzC,gBAAgB,EAAE,MAAM,CAAA;IACxB,qDAAqD;IACrD,iBAAiB,EAAE,MAAM,CAAA;CAC1B;AAED,eAAO,MAAM,qBAAqB,MAAM,CAAA;AACxC,eAAO,MAAM,sBAAsB,OAAO,CAAA;AAE1C,6FAA6F;AAC7F,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvD;AAsBD;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAqB9D,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GACvC,YAAY,GAAG,IAAI,CAwBrB;AAED,2EAA2E;AAC3E,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,YAAY,GAAG,MAAM,CAO3F;AAOD;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,aAAa,EAAE,EACxB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GACvC,YAAY,CA6Cd"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * User-defined model pricing overrides.
3
+ *
4
+ * Lets users correct a stale built-in rate or define pricing for models the
5
+ * static table in pricing.ts doesn't know about (custom routers, brand-new
6
+ * releases). Overrides win over the built-in table in resolveModelPricing.
7
+ *
8
+ * Persisted to ~/.config/meridian/model-pricing.json (override the path with
9
+ * MERIDIAN_PRICING_CONFIG). Read at request time with a short cache, so
10
+ * settings-page edits show up on the next dashboard refresh without a proxy
11
+ * restart. Mirrors the sdkFeatures.ts persistence pattern.
12
+ */
13
+ import { type ModelPricing } from "./pricing";
14
+ export type PricingOverrides = Record<string, ModelPricing>;
15
+ /** Current user-defined pricing overrides, keyed by normalized model string. */
16
+ export declare function getPricingOverrides(): PricingOverrides;
17
+ /**
18
+ * Validate a pricing update body into a complete ModelPricing.
19
+ * inputPerMTok and outputPerMTok are required; cache rates are optional and
20
+ * derived from the input rate (read 0.1x, write 1.25x) when omitted.
21
+ * Throws on invalid input.
22
+ */
23
+ export declare function validatePricingUpdate(raw: unknown): ModelPricing;
24
+ /** Validate the model key for an override. Throws on invalid input. */
25
+ export declare function validateModelKey(model: string): string;
26
+ /** Create or replace the pricing override for a model. */
27
+ export declare function setPricingOverride(model: string, pricing: ModelPricing): void;
28
+ /** Remove the pricing override for a model (reverts to the built-in table). */
29
+ export declare function deletePricingOverride(model: string): void;
30
+ /** Reset the read cache — for testing only (path changes between tests). */
31
+ export declare function resetPricingOverridesCache(): void;
32
+ //# sourceMappingURL=pricingStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricingStore.d.ts","sourceRoot":"","sources":["../../src/telemetry/pricingStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,WAAW,CAAA;AAElB,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;AA8D3D,gFAAgF;AAChF,wBAAgB,mBAAmB,IAAI,gBAAgB,CAEtD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,YAAY,CAwBhE;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOtD;AAED,0DAA0D;AAC1D,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,IAAI,CAI7E;AAED,+EAA+E;AAC/E,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAKzD;AAED,4EAA4E;AAC5E,wBAAgB,0BAA0B,IAAI,IAAI,CAIjD"}
@@ -1,12 +1,16 @@
1
1
  /**
2
- * Shared profile switcher bar — injected into all HTML pages.
2
+ * Shared site header — injected into all HTML pages.
3
3
  *
4
- * CSS and JS are self-contained. The bar auto-hides when no profiles
5
- * are configured. Profile changes take effect immediately via
6
- * POST /profiles/active no page reload needed.
4
+ * One piece of chrome for the whole app: brand (logo + wordmark), site nav,
5
+ * live health pill, and the active-profile chip. Profile *switching* happens
6
+ * on the home page (click an account card) or the Profiles page the header
7
+ * only shows which profile is active.
8
+ *
9
+ * CSS and JS are self-contained. Export names are kept from the old profile
10
+ * bar (profileBarCss/Html/Js) so every page picks the header up unchanged.
7
11
  */
8
12
  /**
9
- * Canonical Meridian theme — purple brand palette.
13
+ * Canonical Meridian theme.
10
14
  *
11
15
  * Every inline HTML page (landing, telemetry dashboard, profiles,
12
16
  * settings, plugins) prepends this block before its own styles so
@@ -16,8 +20,14 @@
16
20
  * so the telemetry waterfall and lineage-colored badges keep their
17
21
  * semantic meaning without needing per-page overrides.
18
22
  */
19
- export declare const themeCss = "\n :root {\n /* Cool-gray neutral palette \u2014 matches the original dashboard/profiles/\n settings look. Better for data-dense dashboards: high contrast,\n surface/border separation, no color cast muddying the text. Purple\n accents are retained as secondary brand color for hover states,\n gradients, and a handful of telemetry badges. */\n --bg: #0d1117;\n --surface: #161b22;\n --surface2: #1c2128;\n --border: #30363d;\n /* Text */\n --text: #e6edf3;\n --muted: #8b949e;\n /* Brand \u2014 blue primary, purple secondary */\n --accent: #58a6ff;\n --accent2: #bc8cff;\n --violet: #bc8cff;\n --lavender: #d2a8ff;\n /* Semantic */\n --green: #3fb950;\n --yellow: #d29922;\n --red: #f85149;\n /* Telemetry-specific aliases (waterfall + lineage badges) */\n --blue: #58a6ff;\n --purple: #bc8cff;\n --queue: #d29922;\n --ttfb: #58a6ff;\n --upstream: #3fb950;\n }\n";
20
- export declare const profileBarCss = "\n .meridian-profile-bar {\n position: sticky; top: 0; z-index: 100;\n display: none; align-items: center; gap: 12px;\n padding: 8px 24px;\n background: rgba(13, 17, 23, 0.92);\n backdrop-filter: blur(12px);\n border-bottom: 1px solid var(--border, #30363d);\n font-size: 12px;\n color: var(--muted, #8b949e);\n }\n .meridian-profile-bar.visible { display: flex; }\n .meridian-profile-bar .profile-label {\n font-weight: 500; text-transform: uppercase; letter-spacing: 0.5px;\n font-size: 10px; color: var(--muted, #8b949e);\n }\n .meridian-profile-bar select {\n background: var(--surface, #161b22); color: var(--text, #e6edf3);\n border: 1px solid var(--border, #30363d); border-radius: 6px;\n padding: 4px 24px 4px 10px; font-size: 12px; cursor: pointer;\n appearance: none;\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M3 5l3 3 3-3' fill='none' stroke='%238b949e' stroke-width='1.5'/%3E%3C/svg%3E\");\n background-repeat: no-repeat; background-position: right 6px center;\n }\n .meridian-profile-bar select:hover { border-color: var(--accent, #58a6ff); }\n .meridian-profile-bar select:focus { outline: none; border-color: var(--accent, #58a6ff); box-shadow: 0 0 0 1px var(--accent, #58a6ff); }\n .meridian-profile-bar .profile-status {\n font-size: 11px; color: var(--green, #3fb950); opacity: 0;\n transition: opacity 0.3s;\n }\n .meridian-profile-bar .profile-status.show { opacity: 1; }\n .meridian-profile-bar .profile-type {\n font-size: 10px; padding: 2px 8px; border-radius: 4px;\n background: var(--surface, #161b22); border: 1px solid var(--border, #30363d);\n }\n .meridian-profile-bar .spacer { flex: 1; }\n .meridian-profile-bar .profile-nav a {\n color: var(--muted, #8b949e); text-decoration: none; font-size: 11px;\n padding: 4px 8px; border-radius: 4px; transition: color 0.15s;\n }\n .meridian-profile-bar .profile-nav a:hover { color: var(--text, #e6edf3); }\n .meridian-profile-bar .profile-nav a.active { color: var(--accent, #58a6ff); }\n";
21
- export declare const profileBarHtml = "\n<div class=\"meridian-profile-bar\" id=\"meridianProfileBar\">\n <span class=\"profile-label\">Profile</span>\n <select id=\"meridianProfileSelect\"></select>\n <span class=\"profile-type\" id=\"meridianProfileType\"></span>\n <span class=\"profile-status\" id=\"meridianProfileStatus\">\u2713 Switched</span>\n <div class=\"spacer\"></div>\n <div class=\"profile-nav\">\n <a href=\"/\" id=\"nav-home\">Home</a>\n <a href=\"/settings\" id=\"nav-settings\">Settings</a>\n <a href=\"/profiles\" id=\"nav-profiles\">Profiles</a>\n <a href=\"/telemetry\" id=\"nav-telemetry\">Telemetry</a>\n <a href=\"/plugins\" id=\"nav-plugins\">Plugins</a>\n </div>\n</div>\n";
22
- export declare const profileBarJs = "\n(function() {\n var profileBar = document.getElementById('meridianProfileBar');\n var profileSelect = document.getElementById('meridianProfileSelect');\n var profileType = document.getElementById('meridianProfileType');\n var profileStatus = document.getElementById('meridianProfileStatus');\n var statusTimeout;\n\n // Highlight active nav link\n var path = location.pathname;\n var navLinks = document.querySelectorAll('.profile-nav a');\n navLinks.forEach(function(a) {\n if (a.getAttribute('href') === path || (path === '/telemetry' && a.id === 'nav-telemetry') || (path === '/' && a.id === 'nav-home')) {\n a.classList.add('active');\n }\n });\n\n function esc(s) { var d = document.createElement('div'); d.textContent = s; return d.innerHTML; }\n\n function loadProfiles() {\n fetch('/profiles/list').then(function(r) { return r.json(); }).then(function(data) {\n if (!data.profiles || data.profiles.length === 0) {\n profileBar.classList.remove('visible');\n return;\n }\n profileBar.classList.add('visible');\n var current = data.profiles.find(function(p) { return p.isActive; });\n profileSelect.innerHTML = data.profiles.map(function(p) {\n return '<option value=\"' + esc(p.id) + '\"' + (p.isActive ? ' selected' : '') + '>' + esc(p.id) + '</option>';\n }).join('');\n if (current) profileType.textContent = current.type;\n }).catch(function() {});\n }\n\n profileSelect.onchange = function() {\n fetch('/profiles/active', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ profile: profileSelect.value })\n }).then(function(r) { return r.json(); }).then(function(data) {\n if (data.success) {\n profileStatus.classList.add('show');\n clearTimeout(statusTimeout);\n statusTimeout = setTimeout(function() { profileStatus.classList.remove('show'); }, 2000);\n loadProfiles();\n }\n }).catch(function() {});\n };\n\n loadProfiles();\n setInterval(loadProfiles, 10000);\n})();\n";
23
+ export declare const themeCss = "\n :root {\n /* Cool-gray neutral palette. High contrast, surface/border separation,\n no color cast muddying the text. Blue is the primary accent; violet\n is the fixed secondary \u2014 used together in the brand gradient and\n individually for hover states and a handful of telemetry badges. */\n --bg: #0d1117;\n --surface: #161b22;\n --surface2: #1c2128;\n --border: #30363d;\n /* Text */\n --text: #e6edf3;\n --muted: #8b949e;\n /* Brand \u2014 blue primary, violet secondary */\n --accent: #58a6ff;\n --accent2: #bc8cff;\n --violet: #bc8cff;\n --lavender: #d2a8ff;\n /* Semantic */\n --green: #3fb950;\n --yellow: #d29922;\n --red: #f85149;\n /* Telemetry-specific aliases (waterfall + lineage badges) */\n --blue: #58a6ff;\n --purple: #bc8cff;\n --queue: #d29922;\n --ttfb: #58a6ff;\n --upstream: #3fb950;\n }\n /* Banner backsplash \u2014 the brand look: a gentle diagonal wash with soft\n blue (top-left) and violet (bottom-right) glows. Pages must not set\n their own body background so this shows through everywhere. */\n body {\n background:\n radial-gradient(1200px 800px at 12% -8%, rgba(88,166,255,0.07), transparent 60%),\n radial-gradient(1100px 800px at 92% 108%, rgba(188,140,255,0.06), transparent 60%),\n linear-gradient(135deg, #0d1117 0%, #10151d 55%, #161b22 100%);\n background-attachment: fixed;\n background-color: var(--bg);\n }\n";
24
+ /**
25
+ * The Meridian mark: a wireframe globe whose prime meridian runs the brand
26
+ * gradient from blue (north) to violet (south), pinned by a node at each
27
+ * pole. Scales cleanly from favicon to hero size.
28
+ */
29
+ export declare const meridianLogoSvg = "<svg class=\"mh-logo\" width=\"24\" height=\"24\" viewBox=\"0 0 64 64\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <defs>\n <linearGradient id=\"mhGrad\" x1=\"32\" y1=\"4\" x2=\"32\" y2=\"60\" gradientUnits=\"userSpaceOnUse\">\n <stop stop-color=\"#58a6ff\"/>\n <stop offset=\"1\" stop-color=\"#bc8cff\"/>\n </linearGradient>\n </defs>\n <circle cx=\"32\" cy=\"32\" r=\"25\" stroke=\"url(#mhGrad)\" stroke-width=\"3.5\"/>\n <ellipse cx=\"32\" cy=\"32\" rx=\"10.5\" ry=\"25\" stroke=\"url(#mhGrad)\" stroke-width=\"2.5\" opacity=\"0.8\"/>\n <path d=\"M7 32h50\" stroke=\"url(#mhGrad)\" stroke-width=\"2\" opacity=\"0.4\"/>\n <circle cx=\"32\" cy=\"7\" r=\"4.5\" fill=\"#58a6ff\"/>\n <circle cx=\"32\" cy=\"57\" r=\"4.5\" fill=\"#bc8cff\"/>\n</svg>";
30
+ export declare const profileBarCss = "\n .meridian-header {\n position: sticky; top: 0; z-index: 100;\n display: flex; align-items: center; gap: 20px;\n padding: 10px 24px;\n background: rgba(13, 17, 23, 0.92);\n backdrop-filter: blur(12px);\n border-bottom: 1px solid var(--border, #30363d);\n }\n .meridian-header .mh-brand {\n display: flex; align-items: center; gap: 10px;\n text-decoration: none; color: var(--text, #e6edf3);\n }\n .meridian-header .mh-logo { display: block; }\n .meridian-header .mh-name {\n font-size: 15px; font-weight: 700; letter-spacing: 2px;\n text-transform: uppercase;\n }\n .meridian-header .mh-nav { display: flex; align-items: center; gap: 2px; }\n .meridian-header .mh-nav a {\n color: var(--muted, #8b949e); text-decoration: none; font-size: 12px;\n font-weight: 500; padding: 5px 10px; border-radius: 6px;\n transition: color 0.15s, background 0.15s;\n }\n .meridian-header .mh-nav a:hover { color: var(--text, #e6edf3); background: var(--surface, #161b22); }\n .meridian-header .mh-nav a.active { color: var(--accent, #58a6ff); background: var(--surface, #161b22); }\n .meridian-header .mh-right {\n margin-left: auto; display: flex; align-items: center; gap: 10px;\n }\n .meridian-header .mh-profile {\n display: none; align-items: center; gap: 6px;\n font-size: 11px; font-weight: 500; color: var(--text, #e6edf3);\n padding: 3px 10px; border-radius: 20px;\n background: var(--surface, #161b22); border: 1px solid var(--border, #30363d);\n text-decoration: none; transition: border-color 0.15s;\n }\n .meridian-header .mh-profile:hover { border-color: var(--accent, #58a6ff); }\n .meridian-header .mh-profile.visible { display: inline-flex; }\n .meridian-header .mh-profile .mh-profile-type {\n color: var(--muted, #8b949e); font-size: 10px;\n }\n .meridian-header .mh-status {\n display: inline-flex; align-items: center; gap: 6px;\n font-size: 11px; color: var(--muted, #8b949e); white-space: nowrap;\n }\n .meridian-header .mh-dot {\n width: 8px; height: 8px; border-radius: 50%;\n background: var(--muted, #8b949e); flex-shrink: 0;\n }\n .meridian-header .mh-dot.healthy { background: var(--green, #3fb950); box-shadow: 0 0 6px rgba(63,185,80,0.5); }\n .meridian-header .mh-dot.degraded { background: var(--yellow, #d29922); }\n .meridian-header .mh-dot.unhealthy { background: var(--red, #f85149); }\n @media (max-width: 720px) {\n .meridian-header { gap: 10px; padding: 10px 16px; flex-wrap: wrap; }\n .meridian-header .mh-name { display: none; }\n .meridian-header .mh-status .mh-status-text { display: none; }\n }\n";
31
+ export declare const profileBarHtml = "\n<header class=\"meridian-header\" id=\"meridianHeader\">\n <a class=\"mh-brand\" href=\"/\">\n <svg class=\"mh-logo\" width=\"24\" height=\"24\" viewBox=\"0 0 64 64\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <defs>\n <linearGradient id=\"mhGrad\" x1=\"32\" y1=\"4\" x2=\"32\" y2=\"60\" gradientUnits=\"userSpaceOnUse\">\n <stop stop-color=\"#58a6ff\"/>\n <stop offset=\"1\" stop-color=\"#bc8cff\"/>\n </linearGradient>\n </defs>\n <circle cx=\"32\" cy=\"32\" r=\"25\" stroke=\"url(#mhGrad)\" stroke-width=\"3.5\"/>\n <ellipse cx=\"32\" cy=\"32\" rx=\"10.5\" ry=\"25\" stroke=\"url(#mhGrad)\" stroke-width=\"2.5\" opacity=\"0.8\"/>\n <path d=\"M7 32h50\" stroke=\"url(#mhGrad)\" stroke-width=\"2\" opacity=\"0.4\"/>\n <circle cx=\"32\" cy=\"7\" r=\"4.5\" fill=\"#58a6ff\"/>\n <circle cx=\"32\" cy=\"57\" r=\"4.5\" fill=\"#bc8cff\"/>\n</svg>\n <span class=\"mh-name\">Meridian</span>\n </a>\n <nav class=\"mh-nav\">\n <a href=\"/\" id=\"nav-home\">Home</a>\n <a href=\"/telemetry\" id=\"nav-telemetry\">Telemetry</a>\n <a href=\"/profiles\" id=\"nav-profiles\">Profiles</a>\n <a href=\"/settings\" id=\"nav-settings\">Settings</a>\n <a href=\"/plugins\" id=\"nav-plugins\">Plugins</a>\n </nav>\n <div class=\"mh-right\">\n <a class=\"mh-profile\" id=\"mhProfile\" href=\"/\" title=\"Active profile \u2014 switch from the home page\"></a>\n <span class=\"mh-status\" id=\"mhStatus\"><span class=\"mh-dot\" id=\"mhDot\"></span><span class=\"mh-status-text\" id=\"mhStatusText\"></span></span>\n </div>\n</header>\n";
32
+ export declare const profileBarJs = "\n(function() {\n var profileChip = document.getElementById('mhProfile');\n var statusDot = document.getElementById('mhDot');\n var statusText = document.getElementById('mhStatusText');\n\n // Highlight active nav link\n var path = location.pathname;\n var navLinks = document.querySelectorAll('.mh-nav a');\n navLinks.forEach(function(a) {\n if (a.getAttribute('href') === path || (path === '/telemetry' && a.id === 'nav-telemetry') || (path === '/' && a.id === 'nav-home')) {\n a.classList.add('active');\n }\n });\n\n function esc(s) { var d = document.createElement('div'); d.textContent = s; return d.innerHTML; }\n\n function loadHeader() {\n fetch('/health').then(function(r) { return r.json(); }).then(function(h) {\n var st = h.status === 'healthy' ? 'healthy' : h.status === 'degraded' ? 'degraded' : 'unhealthy';\n statusDot.className = 'mh-dot ' + st;\n statusText.textContent = st === 'healthy' ? 'Operational' : st === 'degraded' ? 'Degraded' : 'Offline';\n }).catch(function() {\n statusDot.className = 'mh-dot unhealthy';\n statusText.textContent = 'Offline';\n });\n\n fetch('/profiles/list').then(function(r) { return r.json(); }).then(function(data) {\n var current = (data.profiles || []).find(function(p) { return p.isActive; });\n if (!current) { profileChip.classList.remove('visible'); return; }\n profileChip.innerHTML = esc(current.id) + ' <span class=\"mh-profile-type\">' + esc(current.type || '') + '</span>';\n profileChip.classList.add('visible');\n }).catch(function() {});\n }\n\n loadHeader();\n setInterval(loadHeader, 10000);\n // Pages call this after mutating state (e.g. switching the active profile)\n // so the header chip updates immediately instead of on the next poll.\n window.meridianHeaderRefresh = loadHeader;\n})();\n";
23
33
  //# sourceMappingURL=profileBar.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"profileBar.d.ts","sourceRoot":"","sources":["../../src/telemetry/profileBar.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,igCA8BpB,CAAA;AAED,eAAO,MAAM,aAAa,wmEA0CzB,CAAA;AAED,eAAO,MAAM,cAAc,+qBAe1B,CAAA;AAED,eAAO,MAAM,YAAY,yiEAoDxB,CAAA"}
1
+ {"version":3,"file":"profileBar.d.ts","sourceRoot":"","sources":["../../src/telemetry/profileBar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,6gDAwCpB,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,syBAYrB,CAAA;AAEP,eAAO,MAAM,aAAa,glFAyDzB,CAAA;AAED,eAAO,MAAM,cAAc,ukDAkB1B,CAAA;AAED,eAAO,MAAM,YAAY,k0DAyCxB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"profileUsage.d.ts","sourceRoot":"","sources":["../../src/telemetry/profileUsage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,uDAAuD;AACvD,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAQhD,CAAA;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOnD;AAED;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAA;AAChD,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,WAAW,CAKvF;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG,GAAE,MAAmB,GAAG,MAAM,CAY1G;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,WAAW,CAAA;CACpB;AACD,wBAAgB,gBAAgB,CAAC,EAAE,EAAE;IACnC,SAAS,EAAE,OAAO,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,QAAQ,EAAE,MAAM,CAAA;CACjB,GAAG,IAAI,GAAG,SAAS,GAAG,iBAAiB,GAAG,IAAI,CAiB9C;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,UAAU;IACzB,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAA;IACjB,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAA;IACnB,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAA;IAChB;kEAC8D;IAC9D,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,yCAAyC;IACzC,MAAM,EAAE,OAAO,GAAG,IAAI,GAAG,OAAO,CAAA;IAChC,qEAAqE;IACrE,eAAe,EAAE,MAAM,CAAA;CACxB;AAQD,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACtC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACnC,GAAG,GAAE,MAAmB,GACvB,UAAU,GAAG,IAAI,CAoBnB"}
1
+ {"version":3,"file":"profileUsage.d.ts","sourceRoot":"","sources":["../../src/telemetry/profileUsage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,uDAAuD;AACvD,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAShD,CAAA;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOnD;AAED;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAA;AAChD,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,WAAW,CAKvF;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG,GAAE,MAAmB,GAAG,MAAM,CAY1G;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,WAAW,CAAA;CACpB;AACD,wBAAgB,gBAAgB,CAAC,EAAE,EAAE;IACnC,SAAS,EAAE,OAAO,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,QAAQ,EAAE,MAAM,CAAA;CACjB,GAAG,IAAI,GAAG,SAAS,GAAG,iBAAiB,GAAG,IAAI,CAiB9C;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,UAAU;IACzB,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAA;IACjB,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAA;IACnB,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAA;IAChB;kEAC8D;IAC9D,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,yCAAyC;IACzC,MAAM,EAAE,OAAO,GAAG,IAAI,GAAG,OAAO,CAAA;IAChC,qEAAqE;IACrE,eAAe,EAAE,MAAM,CAAA;CACxB;AAQD,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACtC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACnC,GAAG,GAAE,MAAmB,GACvB,UAAU,GAAG,IAAI,CAoBnB"}