@rynfar/meridian 1.27.6 → 1.29.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.
@@ -0,0 +1,275 @@
1
+ import {
2
+ profileBarCss,
3
+ profileBarHtml,
4
+ profileBarJs
5
+ } from "./cli-g9ypdz51.js";
6
+ import"./cli-a05ws7rb.js";
7
+
8
+ // src/telemetry/profilePage.ts
9
+ var profilePageHtml = `<!DOCTYPE html>
10
+ <html lang="en">
11
+ <head>
12
+ <meta charset="utf-8">
13
+ <meta name="viewport" content="width=device-width, initial-scale=1">
14
+ <title>Meridian — Profiles</title>
15
+ <style>
16
+ :root {
17
+ --bg: #0d1117; --surface: #161b22; --border: #30363d;
18
+ --text: #e6edf3; --muted: #8b949e; --accent: #58a6ff;
19
+ --green: #3fb950; --yellow: #d29922; --red: #f85149;
20
+ --purple: #bc8cff;
21
+ }
22
+ * { box-sizing: border-box; margin: 0; padding: 0; }
23
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
24
+ background: var(--bg); color: var(--text); padding: 0; line-height: 1.5; }
25
+ .container { max-width: 800px; margin: 0 auto; padding: 24px; }
26
+ h1 { font-size: 20px; font-weight: 600; margin-bottom: 4px; }
27
+ .subtitle { color: var(--muted); font-size: 13px; margin-bottom: 24px; }
28
+ .section { margin-bottom: 32px; }
29
+ .section-title { font-size: 12px; font-weight: 600; color: var(--muted); text-transform: uppercase;
30
+ letter-spacing: 0.5px; margin-bottom: 12px; }
31
+
32
+ .profile-card {
33
+ background: var(--surface); border: 1px solid var(--border); border-radius: 10px;
34
+ padding: 20px; margin-bottom: 12px; transition: border-color 0.2s;
35
+ }
36
+ .profile-card.active { border-color: var(--accent); }
37
+ .profile-card-header { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
38
+ .profile-name { font-size: 16px; font-weight: 600; }
39
+ .profile-badge {
40
+ font-size: 10px; padding: 2px 8px; border-radius: 4px; text-transform: uppercase;
41
+ letter-spacing: 0.5px; font-weight: 500;
42
+ }
43
+ .badge-active { background: rgba(88,166,255,0.15); color: var(--accent); }
44
+ .badge-type { background: var(--bg); color: var(--muted); border: 1px solid var(--border); }
45
+ .profile-details {
46
+ display: grid; grid-template-columns: 120px 1fr; gap: 6px 16px; font-size: 13px;
47
+ }
48
+ .detail-label { color: var(--muted); }
49
+ .detail-value { font-family: 'SF Mono', SFMono-Regular, Consolas, monospace; font-size: 12px; }
50
+ .status-ok { color: var(--green); }
51
+ .status-err { color: var(--red); }
52
+ .switch-btn {
53
+ margin-top: 12px; padding: 6px 16px; font-size: 12px; font-weight: 500;
54
+ background: var(--bg); color: var(--accent); border: 1px solid var(--accent);
55
+ border-radius: 6px; cursor: pointer; transition: all 0.15s;
56
+ }
57
+ .switch-btn:hover { background: rgba(88,166,255,0.1); }
58
+ .switch-btn:disabled { opacity: 0.4; cursor: default; }
59
+ .switch-btn.current { border-color: var(--border); color: var(--muted); cursor: default; }
60
+
61
+ .empty-state {
62
+ text-align: center; padding: 48px; color: var(--muted);
63
+ background: var(--surface); border: 1px solid var(--border); border-radius: 10px;
64
+ }
65
+ .empty-state h2 { font-size: 16px; margin-bottom: 8px; color: var(--text); }
66
+
67
+ .guide {
68
+ background: var(--surface); border: 1px solid var(--border); border-radius: 10px;
69
+ padding: 20px;
70
+ }
71
+ .guide h3 { font-size: 14px; margin-bottom: 12px; }
72
+ .guide ol { padding-left: 20px; font-size: 13px; }
73
+ .guide li { margin-bottom: 8px; }
74
+ .guide code {
75
+ font-family: 'SF Mono', SFMono-Regular, Consolas, monospace; font-size: 12px;
76
+ background: var(--bg); padding: 2px 6px; border-radius: 4px; color: var(--purple);
77
+ }
78
+ .guide .warn {
79
+ margin-top: 12px; padding: 12px 16px; background: rgba(210,153,34,0.1);
80
+ border: 1px solid rgba(210,153,34,0.3); border-radius: 8px; font-size: 12px;
81
+ }
82
+ .guide .warn strong { color: var(--yellow); }
83
+
84
+ .mono { font-family: 'SF Mono', SFMono-Regular, Consolas, monospace; font-size: 12px; }
85
+ .copy-cmd {
86
+ font-family: 'SF Mono', SFMono-Regular, Consolas, monospace; font-size: 12px;
87
+ background: var(--bg); padding: 4px 10px; border-radius: 4px; color: var(--purple);
88
+ cursor: pointer; border: 1px solid var(--border); transition: border-color 0.15s;
89
+ }
90
+ .copy-btn {
91
+ background: var(--bg); color: var(--muted); border: 1px solid var(--border);
92
+ border-radius: 4px; padding: 4px 6px; cursor: pointer; display: inline-flex;
93
+ align-items: center; transition: all 0.15s;
94
+ }
95
+ .copy-btn:hover { border-color: var(--accent); color: var(--accent); }
96
+ .copy-btn.copied { color: var(--green); border-color: var(--green); }
97
+ ` + profileBarCss + `
98
+ </style>
99
+ </head>
100
+ <body>
101
+ ` + profileBarHtml + `
102
+ <div class="container">
103
+ <h1>Profiles</h1>
104
+ <div class="subtitle">Manage Claude account profiles</div>
105
+
106
+ <div id="content"><div style="color:var(--muted);padding:40px;text-align:center">Loading…</div></div>
107
+
108
+ <div class="section" style="margin-top:32px">
109
+ <div class="section-title">Setup Guide</div>
110
+ <div class="guide">
111
+ <h3>How profiles work</h3>
112
+ <p style="font-size:13px;color:var(--muted);margin-bottom:12px">
113
+ Each profile is a separate Claude account with its own login credentials.
114
+ Meridian stores them in isolated config directories and switches between them instantly.
115
+ </p>
116
+
117
+ <h3 style="margin-top:16px">Adding a new profile</h3>
118
+ <ol>
119
+ <li>Open a terminal and run: <code>meridian profile add &lt;name&gt;</code></li>
120
+ <li>This opens your browser for Claude login</li>
121
+ <li>Done — the profile is ready to use</li>
122
+ </ol>
123
+
124
+ <div class="warn">
125
+ <strong>⚠ Important for adding a second account:</strong> Before running
126
+ <code>meridian profile add</code> for a different account, sign out of claude.ai
127
+ in your browser first, then sign in with the other account. Claude’s OAuth
128
+ reuses your browser session — if you’re already signed in, the login will
129
+ silently use the same account.
130
+ </div>
131
+
132
+ <h3 style="margin-top:16px">Switching profiles</h3>
133
+ <ol>
134
+ <li><strong>UI:</strong> Use the dropdown at the top of this page</li>
135
+ <li><strong>CLI:</strong> <code>meridian profile switch &lt;name&gt;</code></li>
136
+ <li><strong>Per-request:</strong> Send <code>x-meridian-profile: &lt;name&gt;</code> header</li>
137
+ </ol>
138
+
139
+ <h3 style="margin-top:16px">Other commands</h3>
140
+ <div style="font-size:13px;margin-top:8px">
141
+ <code>meridian profile list</code> — show all profiles and auth status<br>
142
+ <code>meridian profile login &lt;name&gt;</code> — re-authenticate an expired profile<br>
143
+ <code>meridian profile remove &lt;name&gt;</code> — remove a profile
144
+ </div>
145
+ </div>
146
+ </div>
147
+ </div>
148
+
149
+ <script>
150
+ async function refresh() {
151
+ try {
152
+ const res = await fetch('/profiles/list');
153
+ const data = await res.json();
154
+ render(data);
155
+ } catch {
156
+ document.getElementById('content').innerHTML = '<div class="empty-state"><h2>Could not load profiles</h2><p>Is Meridian running?</p></div>';
157
+ }
158
+ }
159
+
160
+ function esc(s) { var d = document.createElement('div'); d.textContent = s; return d.innerHTML; }
161
+
162
+ function render(data) {
163
+ const profiles = data.profiles || [];
164
+ const active = data.activeProfile;
165
+
166
+ if (profiles.length === 0) {
167
+ document.getElementById('content').innerHTML = '<div class="empty-state">'
168
+ + '<h2>No profiles configured</h2>'
169
+ + '<p style="margin-top:8px">Add your first profile from the terminal:</p>'
170
+ + '<p style="margin-top:8px"><code class="mono" style="background:var(--bg);padding:8px 16px;border-radius:6px;display:inline-block">meridian profile add personal</code></p>'
171
+ + '</div>';
172
+ return;
173
+ }
174
+
175
+ let html = '<div class="section"><div class="section-title">Configured Profiles</div>';
176
+
177
+ for (const p of profiles) {
178
+ const isActive = p.id === active;
179
+ html += '<div class="profile-card' + (isActive ? ' active' : '') + '">';
180
+ html += '<div class="profile-card-header">';
181
+ html += '<span class="profile-name">' + esc(p.id) + '</span>';
182
+ if (isActive) html += '<span class="profile-badge badge-active">active</span>';
183
+ html += '<span class="profile-badge badge-type">' + esc(p.type || 'claude-max') + '</span>';
184
+ html += '</div>';
185
+
186
+ html += '<div class="profile-details">';
187
+ html += '<span class="detail-label">Status</span>';
188
+ html += '<span class="detail-value ' + (p.loggedIn ? 'status-ok' : 'status-err') + '">'
189
+ + (p.loggedIn ? '✓ Authenticated' : '✗ Not logged in') + '</span>';
190
+
191
+ if (p.email) {
192
+ html += '<span class="detail-label">Email</span>';
193
+ html += '<span class="detail-value">' + esc(p.email) + '</span>';
194
+ }
195
+ if (p.subscriptionType) {
196
+ html += '<span class="detail-label">Plan</span>';
197
+ html += '<span class="detail-value">' + esc(p.subscriptionType) + '</span>';
198
+ }
199
+ if (p.lastSuccessAt) {
200
+ html += '<span class="detail-label">Last Verified</span>';
201
+ html += '<span class="detail-value" style="color:var(--green)">' + timeAgo(p.lastSuccessAt) + '</span>';
202
+ }
203
+ if (p.lastCheckedAt && (!p.lastSuccessAt || p.lastCheckedAt !== p.lastSuccessAt)) {
204
+ html += '<span class="detail-label">Last Checked</span>';
205
+ html += '<span class="detail-value">' + timeAgo(p.lastCheckedAt) + '</span>';
206
+ }
207
+ html += '</div>';
208
+
209
+ if (!p.loggedIn) {
210
+ html += '<div style="margin-top:12px;padding:10px 14px;background:rgba(210,153,34,0.1);border:1px solid rgba(210,153,34,0.3);border-radius:8px;font-size:12px">';
211
+ html += '<strong style="color:var(--yellow)">⚠ Needs re-authentication</strong>';
212
+ html += '</div>';
213
+ }
214
+
215
+ html += '<div style="margin-top:10px;display:flex;align-items:center;gap:8px;flex-wrap:wrap">';
216
+ html += '<span style="font-size:11px;color:var(--muted)">Login:</span> ';
217
+ html += '<code class="copy-cmd">meridian profile login ' + esc(p.id) + '</code>';
218
+ html += '<button class="copy-btn" data-cmd="meridian profile login ' + esc(p.id) + '" onclick="copyCmd(this)" title="Copy to clipboard">';
219
+ html += '<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25zM5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25z"/></svg>';
220
+ html += '</button>';
221
+ html += '</div>';
222
+
223
+ if (!isActive) {
224
+ html += '<button class="switch-btn" onclick="switchProfile(&quot;'+esc(p.id)+'&quot;)">Switch to ' + esc(p.id) + '</button>';
225
+ } else {
226
+ html += '<button class="switch-btn current" disabled>Currently active</button>';
227
+ }
228
+
229
+ html += '</div>';
230
+ }
231
+
232
+ html += '</div>';
233
+ document.getElementById('content').innerHTML = html;
234
+ }
235
+
236
+ function timeAgo(ts) {
237
+ if (!ts) return '—';
238
+ var s = Math.floor((Date.now() - ts) / 1000);
239
+ if (s < 5) return 'just now';
240
+ if (s < 60) return s + 's ago';
241
+ if (s < 3600) return Math.floor(s/60) + 'm ago';
242
+ if (s < 86400) return Math.floor(s/3600) + 'h ago';
243
+ return new Date(ts).toLocaleString();
244
+ }
245
+
246
+ function copyCmd(btn) {
247
+ var cmd = btn.getAttribute('data-cmd');
248
+ navigator.clipboard.writeText(cmd);
249
+ btn.classList.add('copied');
250
+ btn.innerHTML = '✓';
251
+ setTimeout(function() {
252
+ btn.classList.remove('copied');
253
+ btn.innerHTML = '<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25zM5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25z"/></svg>';
254
+ }, 1500);
255
+ }
256
+
257
+ async function switchProfile(id) {
258
+ const res = await fetch('/profiles/active', {
259
+ method: 'POST',
260
+ headers: { 'Content-Type': 'application/json' },
261
+ body: JSON.stringify({ profile: id })
262
+ });
263
+ const data = await res.json();
264
+ if (data.success) refresh();
265
+ }
266
+
267
+ refresh();
268
+ setInterval(refresh, 10000);
269
+ ` + profileBarJs + `
270
+ </script>
271
+ </body>
272
+ </html>`;
273
+ export {
274
+ profilePageHtml
275
+ };
@@ -0,0 +1,26 @@
1
+ import {
2
+ enableDiskProfileDiscovery,
3
+ getActiveProfileId,
4
+ getEffectiveProfiles,
5
+ hasProfiles,
6
+ listProfiles,
7
+ loadProfilesFromDisk,
8
+ resetActiveProfile,
9
+ resolveProfile,
10
+ restoreActiveProfile,
11
+ setActiveProfile
12
+ } from "./cli-vdp9s10c.js";
13
+ import"./cli-340h1chz.js";
14
+ import"./cli-a05ws7rb.js";
15
+ export {
16
+ setActiveProfile,
17
+ restoreActiveProfile,
18
+ resolveProfile,
19
+ resetActiveProfile,
20
+ loadProfilesFromDisk,
21
+ listProfiles,
22
+ hasProfiles,
23
+ getEffectiveProfiles,
24
+ getActiveProfileId,
25
+ enableDiskProfileDiscovery
26
+ };
@@ -33,7 +33,19 @@ export declare function stripExtendedContext(model: ClaudeModel): ClaudeModel;
33
33
  * Check whether a model is using extended (1M) context.
34
34
  */
35
35
  export declare function hasExtendedContext(model: ClaudeModel): boolean;
36
- export declare function getClaudeAuthStatusAsync(): Promise<ClaudeAuthStatus | null>;
36
+ /** Get the last successful auth check timestamp for a profile.
37
+ * @param profileId - Profile ID to look up (uses default cache when omitted) */
38
+ export declare function getAuthCacheInfo(profileId?: string): {
39
+ lastCheckedAt: number;
40
+ lastSuccessAt: number;
41
+ isFailure: boolean;
42
+ };
43
+ /**
44
+ * @param profileId - Profile ID for per-profile cache keying (e.g. "work", "personal").
45
+ * When undefined, uses the default (global) auth context.
46
+ * @param envOverrides - Optional env vars for per-profile auth (e.g. CLAUDE_CONFIG_DIR).
47
+ */
48
+ export declare function getClaudeAuthStatusAsync(profileId?: string, envOverrides?: Record<string, string>): Promise<ClaudeAuthStatus | null>;
37
49
  /**
38
50
  * Resolve the Claude executable path asynchronously (non-blocking).
39
51
  *
@@ -1 +1 @@
1
- {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/proxy/models.ts"],"names":[],"mappings":"AAAA;;GAEG;AAUH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,YAAY,GAAG,MAAM,GAAG,UAAU,GAAG,OAAO,CAAA;AACjF,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AA0BD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,WAAW,CA8B7H;AAWD;;;;;;GAMG;AACH,wBAAgB,gCAAgC,IAAI,IAAI,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,iCAAiC,IAAI,OAAO,CAG3D;AAED,0EAA0E;AAC1E,wBAAgB,+BAA+B,IAAI,IAAI,CAEtD;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,WAAW,GAAG,WAAW,CAIpE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAE9D;AAED,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAkCjF;AAOD;;;;;;;;;;GAUG;AACH,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,MAAM,CAAC,CA4DpE;AAED,2CAA2C;AAC3C,wBAAgB,qBAAqB,IAAI,IAAI,CAG5C;AAED,kDAAkD;AAClD,wBAAgB,2BAA2B,IAAI,IAAI,CAMlD;AAED;;6DAE6D;AAC7D,wBAAgB,qBAAqB,IAAI,IAAI,CAG5C;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAG/D"}
1
+ {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/proxy/models.ts"],"names":[],"mappings":"AAAA;;GAEG;AAUH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,YAAY,GAAG,MAAM,GAAG,UAAU,GAAG,OAAO,CAAA;AACjF,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AA0BD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,WAAW,CA8B7H;AAWD;;;;;;GAMG;AACH,wBAAgB,gCAAgC,IAAI,IAAI,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,iCAAiC,IAAI,OAAO,CAG3D;AAED,0EAA0E;AAC1E,wBAAgB,+BAA+B,IAAI,IAAI,CAEtD;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,WAAW,GAAG,WAAW,CAIpE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAE9D;AAaD;gFACgF;AAChF,wBAAgB,gBAAgB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAOzH;AAWD;;;;GAIG;AACH,wBAAsB,wBAAwB,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAuD1I;AAOD;;;;;;;;;;GAUG;AACH,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,MAAM,CAAC,CA4DpE;AAED,2CAA2C;AAC3C,wBAAgB,qBAAqB,IAAI,IAAI,CAG5C;AAED,kDAAkD;AAClD,wBAAgB,2BAA2B,IAAI,IAAI,CAOlD;AAED;;6DAE6D;AAC7D,wBAAgB,qBAAqB,IAAI,IAAI,CAO5C;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAG/D"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Multi-profile support.
3
+ *
4
+ * Allows a single Meridian instance to route requests to different Claude
5
+ * accounts. Each profile is a named auth context (a CLAUDE_CONFIG_DIR for
6
+ * Max subscriptions, or an API key for direct API access).
7
+ *
8
+ * Profile selection priority:
9
+ * 1. x-meridian-profile request header (per-request override)
10
+ * 2. Active profile (set via POST /profiles/active or UI)
11
+ * 3. First configured profile (or implicit "default" if none configured)
12
+ *
13
+ * This is a leaf module — no imports from server.ts or session/.
14
+ */
15
+ /**
16
+ * Load profiles from ~/.config/meridian/profiles.json.
17
+ * Cached with a 5s TTL so new profiles are picked up without restart,
18
+ * while avoiding synchronous disk I/O on every request.
19
+ */
20
+ export declare function loadProfilesFromDisk(): ProfileConfig[];
21
+ export type ProfileType = "claude-max" | "api";
22
+ export interface ProfileConfig {
23
+ /** Unique profile identifier (e.g. "personal", "work") */
24
+ id: string;
25
+ /** Auth type — "claude-max" uses CLAUDE_CONFIG_DIR, "api" uses ANTHROPIC_API_KEY */
26
+ type?: ProfileType;
27
+ /** Path to .claude config directory (claude-max profiles) */
28
+ claudeConfigDir?: string;
29
+ /** Anthropic API key (api profiles) */
30
+ apiKey?: string;
31
+ /** Anthropic base URL override (api profiles) */
32
+ baseUrl?: string;
33
+ }
34
+ export interface ResolvedProfile {
35
+ id: string;
36
+ type: ProfileType;
37
+ /** Env vars to overlay on the SDK subprocess environment */
38
+ env: Record<string, string>;
39
+ }
40
+ /**
41
+ * Set the active profile. All requests without an explicit x-meridian-profile
42
+ * header will use this profile. Persisted to ~/.config/meridian/settings.json.
43
+ */
44
+ export declare function setActiveProfile(profileId: string): void;
45
+ /**
46
+ * Get the current active profile ID.
47
+ */
48
+ export declare function getActiveProfileId(): string | undefined;
49
+ /** Reset active profile — for testing only. */
50
+ export declare function resetActiveProfile(): void;
51
+ /**
52
+ * Load persisted active profile from settings. Called once at startup
53
+ * to restore the user's last selection. Only restores when disk
54
+ * discovery is enabled (i.e. real CLI startup, not tests).
55
+ * Validates the saved profile actually exists before restoring.
56
+ */
57
+ export declare function restoreActiveProfile(configProfiles?: ProfileConfig[]): void;
58
+ /** Enable disk auto-discovery of profiles. Called by the CLI when
59
+ * no MERIDIAN_PROFILES env var is set, so the server picks up
60
+ * profiles from ~/.config/meridian/profiles.json dynamically. */
61
+ export declare function enableDiskProfileDiscovery(): void;
62
+ export declare function getEffectiveProfiles(configProfiles: ProfileConfig[] | undefined): ProfileConfig[];
63
+ /** Check if any profiles are available from any source */
64
+ export declare function hasProfiles(configProfiles: ProfileConfig[] | undefined): boolean;
65
+ /**
66
+ * Resolve a profile from the configuration.
67
+ *
68
+ * @param profiles - Configured profiles (from ProxyConfig)
69
+ * @param defaultProfile - Default profile ID (from ProxyConfig)
70
+ * @param requestedId - Explicit profile ID from request header
71
+ */
72
+ export declare function resolveProfile(profiles: ProfileConfig[] | undefined, defaultProfile: string | undefined, requestedId?: string): ResolvedProfile;
73
+ /**
74
+ * Get all configured profile IDs with their types.
75
+ */
76
+ export declare function listProfiles(profiles: ProfileConfig[] | undefined, defaultProfile: string | undefined): Array<{
77
+ id: string;
78
+ type: ProfileType;
79
+ isActive: boolean;
80
+ }>;
81
+ //# sourceMappingURL=profiles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiles.d.ts","sourceRoot":"","sources":["../../src/proxy/profiles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAcH;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,aAAa,EAAE,CAkBtD;AAED,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,KAAK,CAAA;AAE9C,MAAM,WAAW,aAAa;IAC5B,0DAA0D;IAC1D,EAAE,EAAE,MAAM,CAAA;IACV,oFAAoF;IACpF,IAAI,CAAC,EAAE,WAAW,CAAA;IAClB,6DAA6D;IAC7D,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,WAAW,CAAA;IACjB,4DAA4D;IAC5D,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC5B;AAOD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAGxD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,GAAG,SAAS,CAEvD;AAED,+CAA+C;AAC/C,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,cAAc,CAAC,EAAE,aAAa,EAAE,GAAG,IAAI,CAY3E;AAUD;;kEAEkE;AAClE,wBAAgB,0BAA0B,IAAI,IAAI,CAEjD;AAED,wBAAgB,oBAAoB,CAAC,cAAc,EAAE,aAAa,EAAE,GAAG,SAAS,GAAG,aAAa,EAAE,CAOjG;AAED,0DAA0D;AAC1D,wBAAgB,WAAW,CAAC,cAAc,EAAE,aAAa,EAAE,GAAG,SAAS,GAAG,OAAO,CAEhF;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,aAAa,EAAE,GAAG,SAAS,EACrC,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,WAAW,CAAC,EAAE,MAAM,GACnB,eAAe,CAkBjB;AAqBD;;GAEG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,aAAa,EAAE,GAAG,SAAS,EACrC,cAAc,EAAE,MAAM,GAAG,SAAS,GACjC,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,CAU7D"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/proxy/server.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACtE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,CAAA;AAoBvD,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EACpB,KAAK,aAAa,EAEnB,MAAM,mBAAmB,CAAA;AAG1B,OAAO,EAA+B,iBAAiB,EAAE,mBAAmB,EAAsC,MAAM,iBAAiB,CAAA;AAEzI,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAA;AAChE,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,CAAA;AACjD,YAAY,EAAE,aAAa,EAAE,CAAA;AAoG7B,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CA0/ChF;AAED,wBAAsB,gBAAgB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA0ChG"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/proxy/server.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACtE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,CAAA;AAqBvD,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EACpB,KAAK,aAAa,EAEnB,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;AAoG7B,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CA0pDhF;AAED,wBAAsB,gBAAgB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAiEhG"}
@@ -77,20 +77,37 @@ export declare function computeMessageHashes(messages: Array<{
77
77
  }>): string[];
78
78
  /**
79
79
  * Measure how many stored hashes match from the START of the stored array
80
- * against the incoming hashes (order-preserving).
80
+ * against the incoming hashes (positional comparison).
81
81
  *
82
82
  * Prefix overlap means the beginning of the conversation is intact (undo
83
83
  * changes the end but preserves the beginning).
84
+ *
85
+ * NOTE: Compares stored[i] === incoming[i] positionally. An earlier
86
+ * implementation used a Set for O(1) lookups, but that allowed a stored
87
+ * hash at position i to match an incoming hash at a completely different
88
+ * position, inflating the overlap count when duplicate messages exist
89
+ * in the conversation history.
84
90
  */
85
- export declare function measurePrefixOverlap(storedHashes: string[], incomingSet: Set<string>): number;
91
+ export declare function measurePrefixOverlap(storedHashes: string[], incomingHashes: string[]): number;
86
92
  /**
87
- * Measure how many stored hashes match from the END of the stored array
88
- * against the incoming hashes (order-preserving).
93
+ * Measure how many consecutive messages at the END of the stored array
94
+ * appear as a contiguous run in the incoming array.
89
95
  *
90
96
  * Suffix overlap means the recent conversation is intact (compaction
91
97
  * changes the beginning but preserves the end).
98
+ *
99
+ * Algorithm: find the last stored hash in the incoming array, then walk
100
+ * backward through both arrays verifying contiguous matches. This handles
101
+ * the real-world compaction pattern where new messages are appended AFTER
102
+ * the preserved suffix.
103
+ *
104
+ * NOTE: An earlier implementation used a Set for O(1) lookups, but that
105
+ * allowed a stored suffix hash to match an incoming hash at a completely
106
+ * different position — producing false compaction when duplicate messages
107
+ * exist in the conversation. The current approach verifies positional
108
+ * contiguity.
92
109
  */
93
- export declare function measureSuffixOverlap(storedHashes: string[], incomingSet: Set<string>): number;
110
+ export declare function measureSuffixOverlap(storedHashes: string[], incomingHashes: string[]): number;
94
111
  /** Cache-like interface for verifyLineage — only needs get/set/delete */
95
112
  export interface SessionCacheLike {
96
113
  delete(key: string): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"lineage.d.ts","sourceRoot":"","sources":["../../../src/proxy/session/lineage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,4EAA4E;AAC5E,MAAM,WAAW,UAAU;IACzB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,uBAAuB,CAAC,EAAE,MAAM,CAAA;IAChC,2BAA2B,CAAC,EAAE,MAAM,CAAA;CACrC;AAED;0EAC0E;AAC1E,eAAO,MAAM,yBAAyB,IAAI,CAAA;AAE1C,MAAM,WAAW,YAAY;IAC3B,eAAe,EAAE,MAAM,CAAA;IACvB,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB;;qDAEiD;IACjD,WAAW,EAAE,MAAM,CAAA;IACnB;;kCAE8B;IAC9B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB;;oDAEgD;IAChD,eAAe,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IACtC,iGAAiG;IACjG,YAAY,CAAC,EAAE,UAAU,CAAA;CAC1B;AAED;;;GAGG;AACH,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,YAAY,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,YAAY,CAAC;IAAG,OAAO,EAAE,YAAY,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,MAAM,CAAC;IAAS,OAAO,EAAE,YAAY,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GACxG;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAAA;AAIxB;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,GAAG,MAAM,CAI1F;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,GAAG,MAAM,CAK3E;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,GAAG,MAAM,EAAE,CAG9F;AAID;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,CAO7F;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,CAO7F;AAID,yEAAyE;AACzE,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAC7B;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,EAC/C,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,gBAAgB,GACtB,aAAa,CAqFf"}
1
+ {"version":3,"file":"lineage.d.ts","sourceRoot":"","sources":["../../../src/proxy/session/lineage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,4EAA4E;AAC5E,MAAM,WAAW,UAAU;IACzB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,uBAAuB,CAAC,EAAE,MAAM,CAAA;IAChC,2BAA2B,CAAC,EAAE,MAAM,CAAA;CACrC;AAED;0EAC0E;AAC1E,eAAO,MAAM,yBAAyB,IAAI,CAAA;AAE1C,MAAM,WAAW,YAAY;IAC3B,eAAe,EAAE,MAAM,CAAA;IACvB,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB;;qDAEiD;IACjD,WAAW,EAAE,MAAM,CAAA;IACnB;;kCAE8B;IAC9B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB;;oDAEgD;IAChD,eAAe,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IACtC,iGAAiG;IACjG,YAAY,CAAC,EAAE,UAAU,CAAA;CAC1B;AAED;;;GAGG;AACH,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,YAAY,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,YAAY,CAAC;IAAG,OAAO,EAAE,YAAY,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,MAAM,CAAC;IAAS,OAAO,EAAE,YAAY,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GACxG;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAAA;AAIxB;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,GAAG,MAAM,CAI1F;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,GAAG,MAAM,CAK3E;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,GAAG,MAAM,EAAE,CAG9F;AAID;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,MAAM,CAQ7F;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,MAAM,CA6B7F;AAyBD,yEAAyE;AACzE,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAC7B;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,EAC/C,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,gBAAgB,GACtB,aAAa,CA6Ff"}
@@ -23,6 +23,10 @@ export interface StoredSession {
23
23
  sdkMessageUuids?: Array<string | null>;
24
24
  /** Last observed token usage for this Claude session */
25
25
  contextUsage?: TokenUsage;
26
+ /** Previous Claude session ID preserved when the session mapping is replaced.
27
+ * Enables recovery when a lineage bug (e.g. false compaction) causes the
28
+ * original session to be abandoned and a new one started. */
29
+ previousClaudeSessionId?: string;
26
30
  }
27
31
  /** Set an explicit session store directory. Takes priority over env var.
28
32
  * Pass null to clear. For testing only.
@@ -36,5 +40,25 @@ export declare function storeSharedSession(key: string, claudeSessionId: string,
36
40
  /** Remove a single session from the shared file store.
37
41
  * Used when a session is detected as stale (e.g. expired upstream). */
38
42
  export declare function evictSharedSession(key: string): void;
43
+ /** Look up recovery information for a session key.
44
+ * Returns the current and previous Claude session IDs, plus derived
45
+ * file paths and CLI commands for conversation recovery. */
46
+ export declare function lookupSessionRecovery(key: string): {
47
+ claudeSessionId: string;
48
+ previousClaudeSessionId?: string;
49
+ createdAt: number;
50
+ lastUsedAt: number;
51
+ messageCount: number;
52
+ } | undefined;
53
+ /** List all stored session keys and their Claude session IDs.
54
+ * Used by the recovery endpoint to find sessions by partial match. */
55
+ export declare function listStoredSessions(): Array<{
56
+ key: string;
57
+ claudeSessionId: string;
58
+ previousClaudeSessionId?: string;
59
+ createdAt: number;
60
+ lastUsedAt: number;
61
+ messageCount: number;
62
+ }>;
39
63
  export declare function clearSharedSessions(): void;
40
64
  //# sourceMappingURL=sessionStore.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sessionStore.d.ts","sourceRoot":"","sources":["../../src/proxy/sessionStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAeH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAEnD,MAAM,WAAW,aAAa;IAC5B,eAAe,EAAE,MAAM,CAAA;IACvB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,iFAAiF;IACjF,eAAe,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IACtC,wDAAwD;IACxD,YAAY,CAAC,EAAE,UAAU,CAAA;CAC1B;AA0DD;;oFAEoF;AACpF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAG7F;AAsED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAG1E;AAED,wBAAgB,6BAA6B,CAAC,eAAe,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAYhG;AAED,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,EACX,eAAe,EAAE,MAAM,EACvB,YAAY,CAAC,EAAE,MAAM,EACrB,WAAW,CAAC,EAAE,MAAM,EACpB,aAAa,CAAC,EAAE,MAAM,EAAE,EACxB,eAAe,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,EACtC,YAAY,CAAC,EAAE,UAAU,GACxB,IAAI,CAsCN;AAED;wEACwE;AACxE,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAkBpD;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAO1C"}
1
+ {"version":3,"file":"sessionStore.d.ts","sourceRoot":"","sources":["../../src/proxy/sessionStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAeH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAEnD,MAAM,WAAW,aAAa;IAC5B,eAAe,EAAE,MAAM,CAAA;IACvB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,iFAAiF;IACjF,eAAe,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IACtC,wDAAwD;IACxD,YAAY,CAAC,EAAE,UAAU,CAAA;IACzB;;kEAE8D;IAC9D,uBAAuB,CAAC,EAAE,MAAM,CAAA;CACjC;AA0DD;;oFAEoF;AACpF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAG7F;AAsED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAG1E;AAED,wBAAgB,6BAA6B,CAAC,eAAe,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAYhG;AAED,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,EACX,eAAe,EAAE,MAAM,EACvB,YAAY,CAAC,EAAE,MAAM,EACrB,WAAW,CAAC,EAAE,MAAM,EACpB,aAAa,CAAC,EAAE,MAAM,EAAE,EACxB,eAAe,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,EACtC,YAAY,CAAC,EAAE,UAAU,GACxB,IAAI,CA8CN;AAED;wEACwE;AACxE,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAkBpD;AAED;;6DAE6D;AAC7D,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG;IAClD,eAAe,EAAE,MAAM,CAAA;IACvB,uBAAuB,CAAC,EAAE,MAAM,CAAA;IAChC,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;CACrB,GAAG,SAAS,CAWZ;AAED;uEACuE;AACvE,wBAAgB,kBAAkB,IAAI,KAAK,CAAC;IAC1C,GAAG,EAAE,MAAM,CAAA;IACX,eAAe,EAAE,MAAM,CAAA;IACvB,uBAAuB,CAAC,EAAE,MAAM,CAAA;IAChC,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;CACrB,CAAC,CAUD;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAO1C"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Persistent server settings.
3
+ *
4
+ * Stored in ~/.config/meridian/settings.json. Survives proxy restarts.
5
+ * Shared between CLI, UI, and API — browser localStorage is only used
6
+ * for client-only preferences (theme, collapsed sections, etc.).
7
+ *
8
+ * This is a leaf module — no imports from server.ts or session/.
9
+ */
10
+ export interface MeridianSettings {
11
+ /** Last active profile ID — restored on proxy startup */
12
+ activeProfile?: string;
13
+ }
14
+ /** Read settings from disk. Returns empty object if file doesn't exist or is invalid. */
15
+ export declare function loadSettings(): MeridianSettings;
16
+ /** Write settings to disk. Merges with existing settings (doesn't clobber unknown keys). */
17
+ export declare function saveSettings(updates: Partial<MeridianSettings>): void;
18
+ /** Get a single setting value */
19
+ export declare function getSetting<K extends keyof MeridianSettings>(key: K): MeridianSettings[K];
20
+ /** Set a single setting value and persist */
21
+ export declare function setSetting<K extends keyof MeridianSettings>(key: K, value: MeridianSettings[K]): void;
22
+ //# sourceMappingURL=settings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/proxy/settings.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH,MAAM,WAAW,gBAAgB;IAC/B,yDAAyD;IACzD,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,yFAAyF;AACzF,wBAAgB,YAAY,IAAI,gBAAgB,CAO/C;AAED,4FAA4F;AAC5F,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,IAAI,CASrE;AAED,iCAAiC;AACjC,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,gBAAgB,EAAE,GAAG,EAAE,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAExF;AAED,6CAA6C;AAC7C,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,gBAAgB,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,IAAI,CAErG"}
@@ -1,10 +1,15 @@
1
1
  import type { Server } from "node:http";
2
+ import type { ProfileConfig } from "./profiles";
2
3
  export interface ProxyConfig {
3
4
  port: number;
4
5
  host: string;
5
6
  debug: boolean;
6
7
  idleTimeoutSeconds: number;
7
8
  silent: boolean;
9
+ /** Named auth profiles for multi-account support */
10
+ profiles?: ProfileConfig[];
11
+ /** Default profile ID when no header is sent */
12
+ defaultProfile?: string;
8
13
  }
9
14
  export interface ProxyInstance {
10
15
  /** The underlying http.Server */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/proxy/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AAEvC,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,OAAO,CAAA;IACd,kBAAkB,EAAE,MAAM,CAAA;IAC1B,MAAM,EAAE,OAAO,CAAA;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAA;IACd,uCAAuC;IACvC,MAAM,EAAE,WAAW,CAAA;IACnB,mEAAmE;IACnE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB;AAED,oFAAoF;AACpF,MAAM,WAAW,WAAW;IAC1B,+DAA+D;IAC/D,GAAG,EAAE;QAAE,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;KAAE,CAAA;IAClF,uCAAuC;IACvC,MAAM,EAAE,WAAW,CAAA;CACpB;AAED,eAAO,MAAM,oBAAoB,EAAE,WAMlC,CAAA"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/proxy/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AACvC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,OAAO,CAAA;IACd,kBAAkB,EAAE,MAAM,CAAA;IAC1B,MAAM,EAAE,OAAO,CAAA;IACf,oDAAoD;IACpD,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAA;IAC1B,gDAAgD;IAChD,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAA;IACd,uCAAuC;IACvC,MAAM,EAAE,WAAW,CAAA;IACnB,mEAAmE;IACnE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB;AAED,oFAAoF;AACpF,MAAM,WAAW,WAAW;IAC1B,+DAA+D;IAC/D,GAAG,EAAE;QAAE,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;KAAE,CAAA;IAClF,uCAAuC;IACvC,MAAM,EAAE,WAAW,CAAA;CACpB;AAED,eAAO,MAAM,oBAAoB,EAAE,WAQlC,CAAA"}
package/dist/server.js CHANGED
@@ -6,9 +6,12 @@ import {
6
6
  getMaxSessionsLimit,
7
7
  hashMessage,
8
8
  startProxyServer
9
- } from "./cli-z8ny8e85.js";
9
+ } from "./cli-msyx6dnk.js";
10
+ import"./cli-g9ypdz51.js";
10
11
  import"./cli-rtab0qa6.js";
11
12
  import"./cli-m9pfb7h9.js";
13
+ import"./cli-vdp9s10c.js";
14
+ import"./cli-340h1chz.js";
12
15
  import"./cli-a05ws7rb.js";
13
16
  export {
14
17
  startProxyServer,