@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.
- package/README.md +51 -19
- package/assets/banner.svg +28 -21
- package/assets/bot-icon-512.png +0 -0
- package/assets/bot-icon.svg +49 -43
- package/assets/how-it-works.svg +40 -40
- package/assets/icon-512.png +0 -0
- package/assets/icon.svg +16 -13
- package/assets/logo-512.png +0 -0
- package/assets/logo.svg +14 -12
- package/dist/{cli-yw8cx02m.js → cli-7df5eg5d.js} +828 -164
- package/dist/{cli-q6dz9r5p.js → cli-aybvv9nb.js} +7 -1
- package/dist/cli-xmweegb1.js +179 -0
- package/dist/cli.js +4 -4
- package/dist/{pluginPage-85s6t6k8.js → pluginPage-03q5j753.js} +4 -10
- package/dist/{profileCli-fbhe9p5z.js → profileCli-j01rr99j.js} +1 -1
- package/dist/{profilePage-txxpbc44.js → profilePage-naychnb8.js} +6 -5
- package/dist/proxy/oauthUsage.d.ts.map +1 -1
- package/dist/proxy/plugins/pluginPage.d.ts.map +1 -1
- package/dist/proxy/server.d.ts.map +1 -1
- package/dist/server.js +3 -3
- package/dist/telemetry/dashboard.d.ts.map +1 -1
- package/dist/telemetry/landing.d.ts +6 -2
- package/dist/telemetry/landing.d.ts.map +1 -1
- package/dist/telemetry/percentiles.d.ts +5 -1
- package/dist/telemetry/percentiles.d.ts.map +1 -1
- package/dist/telemetry/pricing.d.ts +63 -0
- package/dist/telemetry/pricing.d.ts.map +1 -0
- package/dist/telemetry/pricingStore.d.ts +32 -0
- package/dist/telemetry/pricingStore.d.ts.map +1 -0
- package/dist/telemetry/profileBar.d.ts +19 -9
- package/dist/telemetry/profileBar.d.ts.map +1 -1
- package/dist/telemetry/profileUsage.d.ts.map +1 -1
- package/dist/telemetry/settingsPage.d.ts +1 -1
- package/dist/telemetry/settingsPage.d.ts.map +1 -1
- package/dist/telemetry/sqlite.d.ts.map +1 -1
- package/dist/telemetry/store.d.ts.map +1 -1
- package/dist/telemetry/types.d.ts +31 -0
- package/dist/telemetry/types.d.ts.map +1 -1
- package/package.json +1 -1
- 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-
|
|
4
|
+
} from "./cli-7df5eg5d.js";
|
|
5
5
|
import"./cli-f0yqy2d2.js";
|
|
6
6
|
import"./cli-sry5aqdj.js";
|
|
7
|
-
import"./cli-
|
|
7
|
+
import"./cli-xmweegb1.js";
|
|
8
8
|
import {
|
|
9
9
|
resolveClaudeExecutableAsync
|
|
10
|
-
} from "./cli-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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(
|
|
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:
|
|
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">← Back to Meridian</a>
|
|
149
|
-
|
|
150
144
|
<div class="page-header">
|
|
151
145
|
<div>
|
|
152
146
|
<h1>Plugins</h1>
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
profileBarHtml,
|
|
5
5
|
profileBarJs,
|
|
6
6
|
themeCss
|
|
7
|
-
} from "./cli-
|
|
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
|
-
|
|
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(--
|
|
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(--
|
|
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>
|
|
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 <name></code></li>
|
|
190
191
|
<li><strong>Per-request:</strong> Send <code>x-meridian-profile: <name></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;
|
|
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,
|
|
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,
|
|
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-
|
|
14
|
+
} from "./cli-7df5eg5d.js";
|
|
15
15
|
import"./cli-f0yqy2d2.js";
|
|
16
16
|
import"./cli-sry5aqdj.js";
|
|
17
|
-
import"./cli-
|
|
18
|
-
import"./cli-
|
|
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,
|
|
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
|
-
*
|
|
4
|
-
*
|
|
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
|
|
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;
|
|
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
|
|
2
|
+
* Shared site header — injected into all HTML pages.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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
|
|
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
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
|
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,
|
|
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"}
|