@rynfar/meridian 1.27.5 → 1.28.1
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 +78 -0
- package/dist/cli-340h1chz.js +33 -0
- package/dist/cli-g9ypdz51.js +113 -0
- package/dist/cli-vdp9s10c.js +115 -0
- package/dist/{cli-td95gtqj.js → cli-zcxn6xmn.js} +188 -35
- package/dist/cli.js +39 -2
- package/dist/profileCli-pdqrpw0m.js +239 -0
- package/dist/profilePage-9nkbct3w.js +275 -0
- package/dist/profiles-ntgacztq.js +26 -0
- package/dist/proxy/models.d.ts +13 -1
- package/dist/proxy/models.d.ts.map +1 -1
- package/dist/proxy/profiles.d.ts +81 -0
- package/dist/proxy/profiles.d.ts.map +1 -0
- package/dist/proxy/query.d.ts.map +1 -1
- package/dist/proxy/server.d.ts.map +1 -1
- package/dist/proxy/settings.d.ts +22 -0
- package/dist/proxy/settings.d.ts.map +1 -0
- package/dist/proxy/types.d.ts +5 -0
- package/dist/proxy/types.d.ts.map +1 -1
- package/dist/server.js +4 -1
- package/dist/telemetry/dashboard.d.ts +1 -1
- package/dist/telemetry/dashboard.d.ts.map +1 -1
- package/dist/telemetry/landing.d.ts +1 -1
- package/dist/telemetry/landing.d.ts.map +1 -1
- package/dist/telemetry/profileBar.d.ts +11 -0
- package/dist/telemetry/profileBar.d.ts.map +1 -0
- package/dist/telemetry/profilePage.d.ts +6 -0
- package/dist/telemetry/profilePage.d.ts.map +1 -0
- package/package.json +3 -3
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
profileBarCss,
|
|
3
|
+
profileBarHtml,
|
|
4
|
+
profileBarJs
|
|
5
|
+
} from "./cli-g9ypdz51.js";
|
|
1
6
|
import {
|
|
2
7
|
checkPluginConfigured
|
|
3
8
|
} from "./cli-rtab0qa6.js";
|
|
@@ -6,6 +11,14 @@ import {
|
|
|
6
11
|
refreshOAuthToken,
|
|
7
12
|
withClaudeLogContext
|
|
8
13
|
} from "./cli-m9pfb7h9.js";
|
|
14
|
+
import {
|
|
15
|
+
getActiveProfileId,
|
|
16
|
+
getEffectiveProfiles,
|
|
17
|
+
listProfiles,
|
|
18
|
+
resolveProfile,
|
|
19
|
+
restoreActiveProfile,
|
|
20
|
+
setActiveProfile
|
|
21
|
+
} from "./cli-vdp9s10c.js";
|
|
9
22
|
import {
|
|
10
23
|
__export,
|
|
11
24
|
__require
|
|
@@ -2172,7 +2185,9 @@ var DEFAULT_PROXY_CONFIG = {
|
|
|
2172
2185
|
host: "127.0.0.1",
|
|
2173
2186
|
debug: (process.env.MERIDIAN_DEBUG ?? process.env.CLAUDE_PROXY_DEBUG) === "1",
|
|
2174
2187
|
idleTimeoutSeconds: 120,
|
|
2175
|
-
silent: false
|
|
2188
|
+
silent: false,
|
|
2189
|
+
profiles: undefined,
|
|
2190
|
+
defaultProfile: undefined
|
|
2176
2191
|
};
|
|
2177
2192
|
|
|
2178
2193
|
// src/env.ts
|
|
@@ -6421,7 +6436,7 @@ var dashboardHtml = `<!DOCTYPE html>
|
|
|
6421
6436
|
}
|
|
6422
6437
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
6423
6438
|
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
|
|
6424
|
-
background: var(--bg); color: var(--text); padding:
|
|
6439
|
+
background: var(--bg); color: var(--text); padding: 0; line-height: 1.5; }
|
|
6425
6440
|
h1 { font-size: 20px; font-weight: 600; margin-bottom: 4px; }
|
|
6426
6441
|
.subtitle { color: var(--muted); font-size: 13px; margin-bottom: 24px; }
|
|
6427
6442
|
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; margin-bottom: 24px; }
|
|
@@ -6480,9 +6495,12 @@ var dashboardHtml = `<!DOCTYPE html>
|
|
|
6480
6495
|
transition: all 0.15s; }
|
|
6481
6496
|
.log-filter:hover { border-color: var(--accent); color: var(--text); }
|
|
6482
6497
|
.log-filter.active { background: rgba(88,166,255,0.1); border-color: var(--accent); color: var(--accent); }
|
|
6498
|
+
` + profileBarCss + `
|
|
6483
6499
|
</style>
|
|
6484
6500
|
</head>
|
|
6485
6501
|
<body>
|
|
6502
|
+
` + profileBarHtml + `
|
|
6503
|
+
<div style="padding:24px">
|
|
6486
6504
|
<h1>Meridian</h1>
|
|
6487
6505
|
<div class="subtitle">Request Performance Telemetry</div>
|
|
6488
6506
|
|
|
@@ -6507,6 +6525,8 @@ let timer;
|
|
|
6507
6525
|
let activeTab = 'requests';
|
|
6508
6526
|
let activeLogFilter = 'all';
|
|
6509
6527
|
|
|
6528
|
+
|
|
6529
|
+
|
|
6510
6530
|
function ms(v) {
|
|
6511
6531
|
if (v == null) return '—';
|
|
6512
6532
|
if (v < 1000) return v + 'ms';
|
|
@@ -6728,6 +6748,7 @@ $('#window').addEventListener('change', refresh);
|
|
|
6728
6748
|
|
|
6729
6749
|
refresh();
|
|
6730
6750
|
timer = setInterval(refresh, 5000);
|
|
6751
|
+
` + profileBarJs + `
|
|
6731
6752
|
</script>
|
|
6732
6753
|
</body>
|
|
6733
6754
|
</html>`;
|
|
@@ -6845,9 +6866,11 @@ var landingHtml = `<!DOCTYPE html>
|
|
|
6845
6866
|
.footer { margin-top: 48px; padding-top: 24px; border-top: 1px solid var(--border);
|
|
6846
6867
|
font-size: 11px; color: var(--muted); text-align: center; }
|
|
6847
6868
|
.footer a { color: var(--violet); text-decoration: none; }
|
|
6869
|
+
` + profileBarCss + `
|
|
6848
6870
|
</style>
|
|
6849
6871
|
</head>
|
|
6850
6872
|
<body>
|
|
6873
|
+
` + profileBarHtml + `
|
|
6851
6874
|
<div class="container">
|
|
6852
6875
|
<div class="header">
|
|
6853
6876
|
<svg width="40" height="40" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
@@ -6888,12 +6911,13 @@ function render(h,s){
|
|
|
6888
6911
|
o+='</div>';
|
|
6889
6912
|
if(s.byModel&&Object.keys(s.byModel).length>0){o+='<div class="section"><div class="section-title">Models (24h)</div><div class="grid">';for(const[n,d]of Object.entries(s.byModel))o+=card(n,d.count,'avg '+ms(d.avgTotalMs),'');o+='</div></div>'}
|
|
6890
6913
|
o+='<div class="section"><div class="section-title">Connect an Agent</div><div class="snippet"><div class="snippet-tabs"><div class="snippet-tab active" onclick="showTab(this,'opencode')">OpenCode</div><div class="snippet-tab" onclick="showTab(this,'crush')">Crush</div><div class="snippet-tab" onclick="showTab(this,'generic')">Any Tool</div></div><div id="tab-opencode"><code>ANTHROPIC_API_KEY=x ANTHROPIC_BASE_URL=http://'+location.host+' opencode</code></div><div id="tab-crush" style="display:none"><code>'+JSON.stringify({providers:{meridian:{type:"anthropic",base_url:"http://"+location.host,api_key:"x",models:[{id:"claude-sonnet-4-5-20250514",name:"Sonnet 4.5"}]}}},null,2)+'</code></div><div id="tab-generic" style="display:none"><code>export ANTHROPIC_API_KEY=x\\nexport ANTHROPIC_BASE_URL=http://'+location.host+'</code></div></div></div>';
|
|
6891
|
-
o+='<div class="links"><a href="/telemetry" class="link">\uD83D\uDCCA Telemetry</a><a href="/health" class="link">\uD83E\uDE7A Health</a><a href="/telemetry/summary" class="link">\uD83D\uDCC8 Stats API</a><a href="https://github.com/rynfar/meridian" class="link">⚙️ GitHub</a></div>';
|
|
6914
|
+
o+='<div class="links"><a href="/telemetry" class="link">\uD83D\uDCCA Telemetry</a><a href="/profiles" class="link">\uD83D\uDC64 Profiles</a><a href="/health" class="link">\uD83E\uDE7A Health</a><a href="/telemetry/summary" class="link">\uD83D\uDCC8 Stats API</a><a href="https://github.com/rynfar/meridian" class="link">⚙️ GitHub</a></div>';
|
|
6892
6915
|
o+='<div class="footer">Meridian · Built on the <a href="https://github.com/anthropics/claude-code-sdk-js">Claude Code SDK</a></div>';
|
|
6893
6916
|
document.getElementById('content').innerHTML=o;
|
|
6894
6917
|
}
|
|
6895
6918
|
function showTab(el,id){document.querySelectorAll('.snippet-tab').forEach(t=>t.classList.remove('active'));el.classList.add('active');document.querySelectorAll('[id^="tab-"]').forEach(t=>t.style.display='none');document.getElementById('tab-'+id).style.display='block'}
|
|
6896
6919
|
refresh();setInterval(refresh,10000);
|
|
6920
|
+
` + profileBarJs + `
|
|
6897
6921
|
</script>
|
|
6898
6922
|
</body>
|
|
6899
6923
|
</html>`;
|
|
@@ -7055,33 +7079,83 @@ function stripExtendedContext(model) {
|
|
|
7055
7079
|
function hasExtendedContext(model) {
|
|
7056
7080
|
return model.endsWith("[1m]");
|
|
7057
7081
|
}
|
|
7058
|
-
|
|
7059
|
-
|
|
7060
|
-
if (
|
|
7061
|
-
return
|
|
7062
|
-
}
|
|
7063
|
-
|
|
7064
|
-
|
|
7065
|
-
|
|
7082
|
+
var profileAuthCaches = new Map;
|
|
7083
|
+
function getAuthCacheInfo(profileId) {
|
|
7084
|
+
if (!profileId) {
|
|
7085
|
+
return { lastCheckedAt: cachedAuthStatusAt, lastSuccessAt: cachedAuthStatusIsFailure ? 0 : cachedAuthStatusAt, isFailure: cachedAuthStatusIsFailure };
|
|
7086
|
+
}
|
|
7087
|
+
const cache = profileAuthCaches.get(profileId);
|
|
7088
|
+
if (!cache)
|
|
7089
|
+
return { lastCheckedAt: 0, lastSuccessAt: 0, isFailure: false };
|
|
7090
|
+
return { lastCheckedAt: cache.at, lastSuccessAt: cache.lastSuccessAt, isFailure: cache.isFailure };
|
|
7091
|
+
}
|
|
7092
|
+
function getAuthCache(key) {
|
|
7093
|
+
let cache = profileAuthCaches.get(key);
|
|
7094
|
+
if (!cache) {
|
|
7095
|
+
cache = { status: null, lastKnownGood: null, at: 0, isFailure: false, promise: null, lastSuccessAt: 0 };
|
|
7096
|
+
profileAuthCaches.set(key, cache);
|
|
7097
|
+
}
|
|
7098
|
+
return cache;
|
|
7099
|
+
}
|
|
7100
|
+
async function getClaudeAuthStatusAsync(profileId, envOverrides) {
|
|
7101
|
+
const isDefault = !profileId;
|
|
7102
|
+
const cache = isDefault ? null : getAuthCache(profileId);
|
|
7103
|
+
const c_status = cache ? cache.status : cachedAuthStatus;
|
|
7104
|
+
const c_lastKnownGood = cache ? cache.lastKnownGood : lastKnownGoodAuthStatus;
|
|
7105
|
+
const c_at = cache ? cache.at : cachedAuthStatusAt;
|
|
7106
|
+
const c_isFailure = cache ? cache.isFailure : cachedAuthStatusIsFailure;
|
|
7107
|
+
let c_promise = cache ? cache.promise : cachedAuthStatusPromise;
|
|
7108
|
+
const ttl = c_isFailure ? AUTH_STATUS_FAILURE_TTL_MS : AUTH_STATUS_CACHE_TTL_MS;
|
|
7109
|
+
if (c_at > 0 && Date.now() - c_at < ttl) {
|
|
7110
|
+
return c_status ?? c_lastKnownGood;
|
|
7111
|
+
}
|
|
7112
|
+
if (c_promise)
|
|
7113
|
+
return c_promise;
|
|
7114
|
+
c_promise = (async () => {
|
|
7066
7115
|
try {
|
|
7067
|
-
const { stdout } = await exec("claude auth status", {
|
|
7116
|
+
const { stdout } = await exec("claude auth status", {
|
|
7117
|
+
timeout: 5000,
|
|
7118
|
+
...envOverrides ? { env: { ...process.env, ...envOverrides } } : {}
|
|
7119
|
+
});
|
|
7068
7120
|
const parsed = JSON.parse(stdout);
|
|
7069
|
-
|
|
7070
|
-
|
|
7071
|
-
|
|
7072
|
-
|
|
7121
|
+
if (cache) {
|
|
7122
|
+
cache.status = parsed;
|
|
7123
|
+
cache.lastKnownGood = parsed;
|
|
7124
|
+
cache.at = Date.now();
|
|
7125
|
+
cache.isFailure = false;
|
|
7126
|
+
cache.lastSuccessAt = Date.now();
|
|
7127
|
+
} else {
|
|
7128
|
+
cachedAuthStatus = parsed;
|
|
7129
|
+
lastKnownGoodAuthStatus = parsed;
|
|
7130
|
+
cachedAuthStatusAt = Date.now();
|
|
7131
|
+
cachedAuthStatusIsFailure = false;
|
|
7132
|
+
}
|
|
7073
7133
|
return parsed;
|
|
7074
7134
|
} catch {
|
|
7075
|
-
|
|
7076
|
-
|
|
7077
|
-
|
|
7078
|
-
|
|
7135
|
+
if (cache) {
|
|
7136
|
+
cache.isFailure = true;
|
|
7137
|
+
cache.at = Date.now();
|
|
7138
|
+
cache.status = null;
|
|
7139
|
+
return cache.lastKnownGood;
|
|
7140
|
+
} else {
|
|
7141
|
+
cachedAuthStatusIsFailure = true;
|
|
7142
|
+
cachedAuthStatusAt = Date.now();
|
|
7143
|
+
cachedAuthStatus = null;
|
|
7144
|
+
return lastKnownGoodAuthStatus;
|
|
7145
|
+
}
|
|
7079
7146
|
}
|
|
7080
7147
|
})();
|
|
7148
|
+
if (cache)
|
|
7149
|
+
cache.promise = c_promise;
|
|
7150
|
+
else
|
|
7151
|
+
cachedAuthStatusPromise = c_promise;
|
|
7081
7152
|
try {
|
|
7082
|
-
return await
|
|
7153
|
+
return await c_promise;
|
|
7083
7154
|
} finally {
|
|
7084
|
-
|
|
7155
|
+
if (cache)
|
|
7156
|
+
cache.promise = null;
|
|
7157
|
+
else
|
|
7158
|
+
cachedAuthStatusPromise = null;
|
|
7085
7159
|
}
|
|
7086
7160
|
}
|
|
7087
7161
|
var cachedClaudePath = null;
|
|
@@ -13744,7 +13818,8 @@ function buildQueryOptions(ctx) {
|
|
|
13744
13818
|
env: {
|
|
13745
13819
|
...cleanEnv,
|
|
13746
13820
|
ENABLE_TOOL_SEARCH: "false",
|
|
13747
|
-
...passthrough ? { ENABLE_CLAUDEAI_MCP_SERVERS: "false" } : {}
|
|
13821
|
+
...passthrough ? { ENABLE_CLAUDEAI_MCP_SERVERS: "false" } : {},
|
|
13822
|
+
...process.getuid?.() === 0 ? { IS_SANDBOX: "1" } : {}
|
|
13748
13823
|
},
|
|
13749
13824
|
...Object.keys(sdkAgents).length > 0 ? { agents: sdkAgents } : {},
|
|
13750
13825
|
...resumeSessionId ? { resume: resumeSessionId } : {},
|
|
@@ -14388,6 +14463,7 @@ function logUsage(requestId, usage) {
|
|
|
14388
14463
|
}
|
|
14389
14464
|
function createProxyServer(config = {}) {
|
|
14390
14465
|
const finalConfig = { ...DEFAULT_PROXY_CONFIG, ...config };
|
|
14466
|
+
restoreActiveProfile(finalConfig.profiles);
|
|
14391
14467
|
const app = new Hono2;
|
|
14392
14468
|
app.use("*", cors());
|
|
14393
14469
|
app.get("/", (c) => {
|
|
@@ -14451,7 +14527,8 @@ function createProxyServer(config = {}) {
|
|
|
14451
14527
|
if (!Array.isArray(body.messages)) {
|
|
14452
14528
|
return c.json({ type: "error", error: { type: "invalid_request_error", message: "messages: Field required" } }, 400);
|
|
14453
14529
|
}
|
|
14454
|
-
const
|
|
14530
|
+
const profile = resolveProfile(finalConfig.profiles, finalConfig.defaultProfile, c.req.header("x-meridian-profile") || undefined);
|
|
14531
|
+
const authStatus = await getClaudeAuthStatusAsync(profile.id !== "default" ? profile.id : undefined, Object.keys(profile.env).length > 0 ? profile.env : undefined);
|
|
14455
14532
|
const agentMode = c.req.header("x-opencode-agent-mode") ?? null;
|
|
14456
14533
|
let model = mapModelToClaudeModel(body.model || "sonnet", authStatus?.subscriptionType, agentMode);
|
|
14457
14534
|
const adapterStreamPref = adapter.prefersStreaming?.(body);
|
|
@@ -14464,6 +14541,7 @@ function createProxyServer(config = {}) {
|
|
|
14464
14541
|
ANTHROPIC_AUTH_TOKEN: _dropAuthToken,
|
|
14465
14542
|
...cleanEnv
|
|
14466
14543
|
} = process.env;
|
|
14544
|
+
const profileEnv = { ...cleanEnv, ...profile.env };
|
|
14467
14545
|
let systemContext = "";
|
|
14468
14546
|
if (body.system) {
|
|
14469
14547
|
if (typeof body.system === "string") {
|
|
@@ -14476,7 +14554,7 @@ function createProxyServer(config = {}) {
|
|
|
14476
14554
|
const effortHeader = c.req.header("x-opencode-effort");
|
|
14477
14555
|
const thinkingHeader = c.req.header("x-opencode-thinking");
|
|
14478
14556
|
const taskBudgetHeader = c.req.header("x-opencode-task-budget");
|
|
14479
|
-
const betaHeader = c.req.header("anthropic-beta");
|
|
14557
|
+
const betaHeader = profile.type === "api" ? c.req.header("anthropic-beta") : undefined;
|
|
14480
14558
|
const effort = effortHeader || body.effort || undefined;
|
|
14481
14559
|
let thinking = body.thinking || undefined;
|
|
14482
14560
|
if (thinkingHeader !== undefined) {
|
|
@@ -14489,8 +14567,13 @@ function createProxyServer(config = {}) {
|
|
|
14489
14567
|
const parsedBudget = taskBudgetHeader ? Number.parseInt(taskBudgetHeader, 10) : NaN;
|
|
14490
14568
|
const taskBudget = Number.isFinite(parsedBudget) ? { total: parsedBudget } : body.task_budget ? { total: body.task_budget.total ?? body.task_budget } : undefined;
|
|
14491
14569
|
const betas = betaHeader ? betaHeader.split(",").map((b) => b.trim()).filter(Boolean) : undefined;
|
|
14570
|
+
if (!betaHeader && c.req.header("anthropic-beta")) {
|
|
14571
|
+
console.error(`[PROXY] ${requestMeta.requestId} stripped anthropic-beta header (Max subscription — betas trigger extra usage billing)`);
|
|
14572
|
+
}
|
|
14492
14573
|
const agentSessionId = adapter.getSessionId(c);
|
|
14493
|
-
const
|
|
14574
|
+
const profileSessionId = profile.id !== "default" && agentSessionId ? `${profile.id}:${agentSessionId}` : agentSessionId;
|
|
14575
|
+
const profileScopedCwd = profile.id !== "default" ? `${workingDirectory}::profile=${profile.id}` : workingDirectory;
|
|
14576
|
+
const lineageResult = lookupSession(profileSessionId, body.messages || [], profileScopedCwd);
|
|
14494
14577
|
const isResume = lineageResult.type === "continuation" || lineageResult.type === "compaction";
|
|
14495
14578
|
const isUndo = lineageResult.type === "undo";
|
|
14496
14579
|
const cachedSession = lineageResult.type !== "diverged" ? lineageResult.session : undefined;
|
|
@@ -14685,7 +14768,7 @@ function createProxyServer(config = {}) {
|
|
|
14685
14768
|
stream: false,
|
|
14686
14769
|
sdkAgents,
|
|
14687
14770
|
passthroughMcp,
|
|
14688
|
-
cleanEnv,
|
|
14771
|
+
cleanEnv: profileEnv,
|
|
14689
14772
|
resumeSessionId,
|
|
14690
14773
|
isUndo,
|
|
14691
14774
|
undoRollbackUuid,
|
|
@@ -14714,7 +14797,7 @@ function createProxyServer(config = {}) {
|
|
|
14714
14797
|
resumeSessionId
|
|
14715
14798
|
});
|
|
14716
14799
|
console.error(`[PROXY] Stale session UUID, evicting and retrying as fresh session`);
|
|
14717
|
-
evictSession(
|
|
14800
|
+
evictSession(profileSessionId, profileScopedCwd, allMessages);
|
|
14718
14801
|
sdkUuidMap.length = 0;
|
|
14719
14802
|
for (let i = 0;i < allMessages.length; i++)
|
|
14720
14803
|
sdkUuidMap.push(null);
|
|
@@ -14728,7 +14811,7 @@ function createProxyServer(config = {}) {
|
|
|
14728
14811
|
stream: false,
|
|
14729
14812
|
sdkAgents,
|
|
14730
14813
|
passthroughMcp,
|
|
14731
|
-
cleanEnv,
|
|
14814
|
+
cleanEnv: profileEnv,
|
|
14732
14815
|
resumeSessionId: undefined,
|
|
14733
14816
|
isUndo: false,
|
|
14734
14817
|
undoRollbackUuid: undefined,
|
|
@@ -14927,7 +15010,7 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
14927
15010
|
error: null
|
|
14928
15011
|
});
|
|
14929
15012
|
if (currentSessionId) {
|
|
14930
|
-
storeSession(
|
|
15013
|
+
storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage);
|
|
14931
15014
|
}
|
|
14932
15015
|
const responseSessionId = currentSessionId || resumeSessionId || `session_${Date.now()}`;
|
|
14933
15016
|
return new Response(JSON.stringify({
|
|
@@ -15002,7 +15085,7 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
15002
15085
|
stream: true,
|
|
15003
15086
|
sdkAgents,
|
|
15004
15087
|
passthroughMcp,
|
|
15005
|
-
cleanEnv,
|
|
15088
|
+
cleanEnv: profileEnv,
|
|
15006
15089
|
resumeSessionId,
|
|
15007
15090
|
isUndo,
|
|
15008
15091
|
undoRollbackUuid,
|
|
@@ -15031,7 +15114,7 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
15031
15114
|
resumeSessionId
|
|
15032
15115
|
});
|
|
15033
15116
|
console.error(`[PROXY] Stale session UUID, evicting and retrying as fresh session`);
|
|
15034
|
-
evictSession(
|
|
15117
|
+
evictSession(profileSessionId, profileScopedCwd, allMessages);
|
|
15035
15118
|
sdkUuidMap.length = 0;
|
|
15036
15119
|
for (let i = 0;i < allMessages.length; i++)
|
|
15037
15120
|
sdkUuidMap.push(null);
|
|
@@ -15045,7 +15128,7 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
15045
15128
|
stream: true,
|
|
15046
15129
|
sdkAgents,
|
|
15047
15130
|
passthroughMcp,
|
|
15048
|
-
cleanEnv,
|
|
15131
|
+
cleanEnv: profileEnv,
|
|
15049
15132
|
resumeSessionId: undefined,
|
|
15050
15133
|
isUndo: false,
|
|
15051
15134
|
undoRollbackUuid: undefined,
|
|
@@ -15269,7 +15352,7 @@ data: ${JSON.stringify({ type: "message_stop" })}
|
|
|
15269
15352
|
if (lastUsage)
|
|
15270
15353
|
logUsage(requestMeta.requestId, lastUsage);
|
|
15271
15354
|
if (currentSessionId) {
|
|
15272
|
-
storeSession(
|
|
15355
|
+
storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage);
|
|
15273
15356
|
}
|
|
15274
15357
|
if (!streamClosed) {
|
|
15275
15358
|
const unseenToolUses = capturedToolUses.filter((tu) => !streamedToolUseIds.has(tu.id));
|
|
@@ -15362,6 +15445,8 @@ data: {"type":"message_stop"}
|
|
|
15362
15445
|
bytesSent,
|
|
15363
15446
|
durationMs: Date.now() - requestStartAt
|
|
15364
15447
|
});
|
|
15448
|
+
}
|
|
15449
|
+
{
|
|
15365
15450
|
const streamTotalDurationMs = Date.now() - requestStartAt;
|
|
15366
15451
|
claudeLog("response.completed", {
|
|
15367
15452
|
mode: "stream",
|
|
@@ -15524,7 +15609,9 @@ data: ${JSON.stringify({
|
|
|
15524
15609
|
app.route("/telemetry", createTelemetryRoutes());
|
|
15525
15610
|
app.get("/health", async (c) => {
|
|
15526
15611
|
try {
|
|
15527
|
-
const
|
|
15612
|
+
const healthProfile = resolveProfile(finalConfig.profiles, finalConfig.defaultProfile);
|
|
15613
|
+
const profileEnvOverrides = Object.keys(healthProfile.env).length > 0 ? healthProfile.env : undefined;
|
|
15614
|
+
const auth = await getClaudeAuthStatusAsync(healthProfile.id !== "default" ? healthProfile.id : undefined, profileEnvOverrides);
|
|
15528
15615
|
if (!auth) {
|
|
15529
15616
|
return c.json({
|
|
15530
15617
|
status: "degraded",
|
|
@@ -15557,6 +15644,53 @@ data: ${JSON.stringify({
|
|
|
15557
15644
|
});
|
|
15558
15645
|
}
|
|
15559
15646
|
});
|
|
15647
|
+
app.get("/profiles/list", async (c) => {
|
|
15648
|
+
const profiles = listProfiles(finalConfig.profiles, finalConfig.defaultProfile);
|
|
15649
|
+
const enriched = await Promise.all(profiles.map(async (p) => {
|
|
15650
|
+
const resolved = resolveProfile(finalConfig.profiles, finalConfig.defaultProfile, p.id);
|
|
15651
|
+
const envOverrides = Object.keys(resolved.env).length > 0 ? resolved.env : undefined;
|
|
15652
|
+
const auth = await getClaudeAuthStatusAsync(p.id !== "default" ? p.id : undefined, envOverrides);
|
|
15653
|
+
const cacheInfo = getAuthCacheInfo(p.id !== "default" ? p.id : undefined);
|
|
15654
|
+
return {
|
|
15655
|
+
...p,
|
|
15656
|
+
email: auth?.email || null,
|
|
15657
|
+
subscriptionType: auth?.subscriptionType || null,
|
|
15658
|
+
loggedIn: auth?.loggedIn ?? false,
|
|
15659
|
+
lastCheckedAt: cacheInfo.lastCheckedAt || null,
|
|
15660
|
+
lastSuccessAt: cacheInfo.lastSuccessAt || null
|
|
15661
|
+
};
|
|
15662
|
+
}));
|
|
15663
|
+
return c.json({
|
|
15664
|
+
profiles: enriched,
|
|
15665
|
+
activeProfile: getActiveProfileId() || finalConfig.defaultProfile || profiles[0]?.id || "default"
|
|
15666
|
+
});
|
|
15667
|
+
});
|
|
15668
|
+
app.get("/profiles", async (c) => {
|
|
15669
|
+
const { profilePageHtml } = await import("./profilePage-9nkbct3w.js");
|
|
15670
|
+
return c.html(profilePageHtml);
|
|
15671
|
+
});
|
|
15672
|
+
app.post("/profiles/active", async (c) => {
|
|
15673
|
+
let body;
|
|
15674
|
+
try {
|
|
15675
|
+
body = await c.req.json();
|
|
15676
|
+
} catch {
|
|
15677
|
+
return c.json({ error: "Invalid JSON in request body" }, 400);
|
|
15678
|
+
}
|
|
15679
|
+
if (!body.profile) {
|
|
15680
|
+
return c.json({ error: "Missing 'profile' in request body" }, 400);
|
|
15681
|
+
}
|
|
15682
|
+
const effective = getEffectiveProfiles(finalConfig.profiles);
|
|
15683
|
+
if (effective.length === 0) {
|
|
15684
|
+
return c.json({ error: "No profiles configured" }, 400);
|
|
15685
|
+
}
|
|
15686
|
+
if (!effective.find((p) => p.id === body.profile)) {
|
|
15687
|
+
return c.json({ error: `Unknown profile: ${body.profile}. Available: ${effective.map((p) => p.id).join(", ")}` }, 400);
|
|
15688
|
+
}
|
|
15689
|
+
setActiveProfile(body.profile);
|
|
15690
|
+
clearSessionCache();
|
|
15691
|
+
console.error(`[PROXY] Active profile switched to: ${body.profile} (session cache cleared)`);
|
|
15692
|
+
return c.json({ success: true, activeProfile: body.profile });
|
|
15693
|
+
});
|
|
15560
15694
|
app.post("/auth/refresh", async (c) => {
|
|
15561
15695
|
const success = await refreshOAuthToken();
|
|
15562
15696
|
if (success) {
|
|
@@ -15702,10 +15836,29 @@ Or use a different port:`);
|
|
|
15702
15836
|
console.error(` MERIDIAN_PORT=4567 meridian`);
|
|
15703
15837
|
}
|
|
15704
15838
|
});
|
|
15839
|
+
let authKeepaliveInterval;
|
|
15840
|
+
const effectiveProfiles = getEffectiveProfiles(finalConfig.profiles);
|
|
15841
|
+
if (effectiveProfiles.length > 0) {
|
|
15842
|
+
const AUTH_KEEPALIVE_MS = 45000;
|
|
15843
|
+
authKeepaliveInterval = setInterval(async () => {
|
|
15844
|
+
const currentProfiles = getEffectiveProfiles(finalConfig.profiles);
|
|
15845
|
+
for (const profile of currentProfiles) {
|
|
15846
|
+
const resolved = resolveProfile(finalConfig.profiles, finalConfig.defaultProfile, profile.id);
|
|
15847
|
+
if (Object.keys(resolved.env).length > 0) {
|
|
15848
|
+
getClaudeAuthStatusAsync(resolved.id, resolved.env).catch(() => {});
|
|
15849
|
+
}
|
|
15850
|
+
}
|
|
15851
|
+
getClaudeAuthStatusAsync().catch(() => {});
|
|
15852
|
+
}, AUTH_KEEPALIVE_MS);
|
|
15853
|
+
if (authKeepaliveInterval.unref)
|
|
15854
|
+
authKeepaliveInterval.unref();
|
|
15855
|
+
}
|
|
15705
15856
|
return {
|
|
15706
15857
|
server,
|
|
15707
15858
|
config: finalConfig,
|
|
15708
15859
|
async close() {
|
|
15860
|
+
if (authKeepaliveInterval)
|
|
15861
|
+
clearInterval(authKeepaliveInterval);
|
|
15709
15862
|
await new Promise((resolve3, reject) => {
|
|
15710
15863
|
server.close((err) => err ? reject(err) : resolve3());
|
|
15711
15864
|
});
|
package/dist/cli.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
startProxyServer
|
|
4
|
-
} from "./cli-
|
|
4
|
+
} from "./cli-zcxn6xmn.js";
|
|
5
|
+
import"./cli-g9ypdz51.js";
|
|
5
6
|
import"./cli-rtab0qa6.js";
|
|
6
7
|
import"./cli-m9pfb7h9.js";
|
|
8
|
+
import"./cli-vdp9s10c.js";
|
|
9
|
+
import"./cli-340h1chz.js";
|
|
7
10
|
import {
|
|
8
11
|
__require
|
|
9
12
|
} from "./cli-a05ws7rb.js";
|
|
@@ -29,6 +32,7 @@ Usage: meridian [command] [options]
|
|
|
29
32
|
Commands:
|
|
30
33
|
(default) Start the proxy server
|
|
31
34
|
setup Configure the OpenCode plugin (run once after install)
|
|
35
|
+
profile Manage Claude account profiles (add, list, switch, remove)
|
|
32
36
|
refresh-token Refresh the Claude Code OAuth token
|
|
33
37
|
|
|
34
38
|
Options:
|
|
@@ -44,6 +48,24 @@ Environment variables:
|
|
|
44
48
|
See https://github.com/rynfar/meridian for full documentation.`);
|
|
45
49
|
process.exit(0);
|
|
46
50
|
}
|
|
51
|
+
if (args[0] === "profile") {
|
|
52
|
+
const { profileAdd, profileList, profileRemove, profileSwitch, profileLogin, profileHelp } = await import("./profileCli-pdqrpw0m.js");
|
|
53
|
+
const subcommand = args[1];
|
|
54
|
+
const profileId = args[2];
|
|
55
|
+
if (subcommand === "add" && profileId)
|
|
56
|
+
profileAdd(profileId);
|
|
57
|
+
else if (subcommand === "list" || subcommand === "ls")
|
|
58
|
+
profileList();
|
|
59
|
+
else if (subcommand === "remove" && profileId)
|
|
60
|
+
profileRemove(profileId);
|
|
61
|
+
else if (subcommand === "switch" && profileId)
|
|
62
|
+
await profileSwitch(profileId);
|
|
63
|
+
else if (subcommand === "login" && profileId)
|
|
64
|
+
profileLogin(profileId);
|
|
65
|
+
else
|
|
66
|
+
profileHelp();
|
|
67
|
+
process.exit(0);
|
|
68
|
+
}
|
|
47
69
|
if (args[0] === "setup") {
|
|
48
70
|
const { findPluginPath, runSetup } = await import("./setup-5x116vbs.js");
|
|
49
71
|
const pluginPath = findPluginPath(import.meta.url);
|
|
@@ -86,6 +108,17 @@ process.on("unhandledRejection", (reason) => {
|
|
|
86
108
|
var port = parseInt(process.env.MERIDIAN_PORT ?? process.env.CLAUDE_PROXY_PORT ?? "3456", 10);
|
|
87
109
|
var host = process.env.MERIDIAN_HOST ?? process.env.CLAUDE_PROXY_HOST ?? "127.0.0.1";
|
|
88
110
|
var idleTimeoutSeconds = parseInt(process.env.MERIDIAN_IDLE_TIMEOUT_SECONDS ?? process.env.CLAUDE_PROXY_IDLE_TIMEOUT_SECONDS ?? "120", 10);
|
|
111
|
+
var profiles;
|
|
112
|
+
var defaultProfile;
|
|
113
|
+
try {
|
|
114
|
+
const raw = process.env.MERIDIAN_PROFILES;
|
|
115
|
+
if (raw) {
|
|
116
|
+
profiles = JSON.parse(raw);
|
|
117
|
+
defaultProfile = process.env.MERIDIAN_DEFAULT_PROFILE || undefined;
|
|
118
|
+
}
|
|
119
|
+
} catch (e) {
|
|
120
|
+
console.error(`[meridian] Failed to parse MERIDIAN_PROFILES: ${e instanceof Error ? e.message : e}`);
|
|
121
|
+
}
|
|
89
122
|
async function runCli(start = startProxyServer, runExec = exec) {
|
|
90
123
|
try {
|
|
91
124
|
const { findOpencodeConfigPath, checkPluginConfigured, findPluginPath } = await import("./setup-5x116vbs.js");
|
|
@@ -112,7 +145,11 @@ async function runCli(start = startProxyServer, runExec = exec) {
|
|
|
112
145
|
} catch {
|
|
113
146
|
console.error("\x1B[33m⚠ Could not verify Claude auth status. If requests fail, run: claude login\x1B[0m");
|
|
114
147
|
}
|
|
115
|
-
|
|
148
|
+
if (!profiles) {
|
|
149
|
+
const { enableDiskProfileDiscovery } = await import("./profiles-ntgacztq.js");
|
|
150
|
+
enableDiskProfileDiscovery();
|
|
151
|
+
}
|
|
152
|
+
const proxy = await start({ port, host, idleTimeoutSeconds, profiles, defaultProfile });
|
|
116
153
|
proxy.server.on("error", (error) => {
|
|
117
154
|
if (error.code === "EADDRINUSE") {
|
|
118
155
|
process.exit(1);
|