@rynfar/meridian 1.43.0 → 1.44.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 +40 -7
- package/dist/{cli-7k1fcprd.js → cli-1kbcm3yn.js} +37 -20
- package/dist/{cli-1x5gqsqc.js → cli-fc6mt326.js} +121 -46
- package/dist/{cli-rtab0qa6.js → cli-je60fevk.js} +38 -20
- package/dist/cli.js +25 -18
- package/dist/{profileCli-0h4nc2h8.js → profileCli-xcmmr5w4.js} +208 -46
- package/dist/proxy/adapters/detect.d.ts.map +1 -1
- package/dist/proxy/adapters/openai.d.ts +23 -0
- package/dist/proxy/adapters/openai.d.ts.map +1 -0
- package/dist/proxy/effort.d.ts +21 -0
- package/dist/proxy/effort.d.ts.map +1 -0
- package/dist/proxy/openai.d.ts +12 -0
- package/dist/proxy/openai.d.ts.map +1 -1
- package/dist/proxy/query.d.ts +3 -2
- package/dist/proxy/query.d.ts.map +1 -1
- package/dist/proxy/sdkFeatures.d.ts.map +1 -1
- package/dist/proxy/server.d.ts +1 -0
- package/dist/proxy/server.d.ts.map +1 -1
- package/dist/proxy/setup.d.ts +9 -0
- package/dist/proxy/setup.d.ts.map +1 -1
- package/dist/proxy/tokenRefresh.d.ts +2 -0
- package/dist/proxy/tokenRefresh.d.ts.map +1 -1
- package/dist/proxy/transforms/registry.d.ts.map +1 -1
- package/dist/proxy/types.d.ts +7 -0
- package/dist/proxy/types.d.ts.map +1 -1
- package/dist/server.js +5 -3
- package/dist/{setup-v5pnqe04.js → setup-6c11e8d6.js} +4 -2
- package/dist/{tokenRefresh-swetnf89.js → tokenRefresh-pvc2q8ea.js} +1 -1
- package/package.json +4 -3
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
} from "./cli-yeazzt32.js";
|
|
35
35
|
import {
|
|
36
36
|
checkPluginConfigured
|
|
37
|
-
} from "./cli-
|
|
37
|
+
} from "./cli-je60fevk.js";
|
|
38
38
|
import {
|
|
39
39
|
claudeLog,
|
|
40
40
|
createPlatformCredentialStore,
|
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
startBackgroundRefresh,
|
|
44
44
|
stopBackgroundRefresh,
|
|
45
45
|
withClaudeLogContext
|
|
46
|
-
} from "./cli-
|
|
46
|
+
} from "./cli-1kbcm3yn.js";
|
|
47
47
|
import {
|
|
48
48
|
__commonJS,
|
|
49
49
|
__esm,
|
|
@@ -1242,7 +1242,7 @@ function getFeaturesForAdapter(adapterName) {
|
|
|
1242
1242
|
};
|
|
1243
1243
|
}
|
|
1244
1244
|
function getAllFeatureConfigs() {
|
|
1245
|
-
const adapters = ["opencode", "crush", "forgecode", "pi", "droid", "passthrough"];
|
|
1245
|
+
const adapters = ["opencode", "crush", "forgecode", "pi", "droid", "passthrough", "openai"];
|
|
1246
1246
|
const result = {};
|
|
1247
1247
|
for (const name of adapters) {
|
|
1248
1248
|
result[name] = getFeaturesForAdapter(name);
|
|
@@ -1314,6 +1314,9 @@ var init_sdkFeatures = __esm(() => {
|
|
|
1314
1314
|
ADAPTER_DEFAULTS = {
|
|
1315
1315
|
passthrough: {
|
|
1316
1316
|
codeSystemPrompt: false
|
|
1317
|
+
},
|
|
1318
|
+
openai: {
|
|
1319
|
+
codeSystemPrompt: false
|
|
1317
1320
|
}
|
|
1318
1321
|
};
|
|
1319
1322
|
VALID_CLAUDE_MD_VALUES = new Set(["off", "project", "full"]);
|
|
@@ -3889,7 +3892,7 @@ var DEFAULT_PROXY_CONFIG = {
|
|
|
3889
3892
|
host: "127.0.0.1",
|
|
3890
3893
|
debug: (process.env.MERIDIAN_DEBUG ?? process.env.CLAUDE_PROXY_DEBUG) === "1",
|
|
3891
3894
|
idleTimeoutSeconds: 120,
|
|
3892
|
-
silent:
|
|
3895
|
+
silent: (process.env.MERIDIAN_SILENT ?? process.env.CLAUDE_PROXY_SILENT) === "1",
|
|
3893
3896
|
profiles: undefined,
|
|
3894
3897
|
defaultProfile: undefined,
|
|
3895
3898
|
version: undefined
|
|
@@ -9479,6 +9482,10 @@ ${historyBlock}` : historyBlock;
|
|
|
9479
9482
|
result.temperature = body.temperature;
|
|
9480
9483
|
if (body.top_p !== undefined)
|
|
9481
9484
|
result.top_p = body.top_p;
|
|
9485
|
+
if (body.reasoning_effort !== undefined)
|
|
9486
|
+
result.reasoning_effort = body.reasoning_effort;
|
|
9487
|
+
if (body.output_config?.effort !== undefined)
|
|
9488
|
+
result.output_config = { effort: body.output_config.effort };
|
|
9482
9489
|
return result;
|
|
9483
9490
|
}
|
|
9484
9491
|
function toFinishReason(stopReason) {
|
|
@@ -10740,6 +10747,12 @@ var claudeCodeAdapter = {
|
|
|
10740
10747
|
}
|
|
10741
10748
|
};
|
|
10742
10749
|
|
|
10750
|
+
// src/proxy/adapters/openai.ts
|
|
10751
|
+
var openAiAdapter = {
|
|
10752
|
+
...openCodeAdapter,
|
|
10753
|
+
name: "openai"
|
|
10754
|
+
};
|
|
10755
|
+
|
|
10743
10756
|
// src/proxy/adapters/detect.ts
|
|
10744
10757
|
var ADAPTER_MAP = {
|
|
10745
10758
|
opencode: openCodeAdapter,
|
|
@@ -10749,7 +10762,8 @@ var ADAPTER_MAP = {
|
|
|
10749
10762
|
pi: piAdapter,
|
|
10750
10763
|
forgecode: forgeCodeAdapter,
|
|
10751
10764
|
"claude-code": claudeCodeAdapter,
|
|
10752
|
-
claudecode: claudeCodeAdapter
|
|
10765
|
+
claudecode: claudeCodeAdapter,
|
|
10766
|
+
openai: openAiAdapter
|
|
10753
10767
|
};
|
|
10754
10768
|
var envDefault = process.env.MERIDIAN_DEFAULT_AGENT || "";
|
|
10755
10769
|
if (envDefault && !ADAPTER_MAP[envDefault]) {
|
|
@@ -16625,6 +16639,12 @@ function buildQueryOptions(ctx) {
|
|
|
16625
16639
|
};
|
|
16626
16640
|
}
|
|
16627
16641
|
|
|
16642
|
+
// src/proxy/effort.ts
|
|
16643
|
+
var VALID_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
16644
|
+
function normalizeEffort(value) {
|
|
16645
|
+
return typeof value === "string" && VALID_EFFORTS.includes(value) ? value : undefined;
|
|
16646
|
+
}
|
|
16647
|
+
|
|
16628
16648
|
// src/proxy/transforms/registry.ts
|
|
16629
16649
|
var ADAPTER_TRANSFORMS = {
|
|
16630
16650
|
opencode: openCodeTransforms,
|
|
@@ -16632,7 +16652,8 @@ var ADAPTER_TRANSFORMS = {
|
|
|
16632
16652
|
droid: droidTransforms,
|
|
16633
16653
|
pi: piTransforms,
|
|
16634
16654
|
forgecode: forgeCodeTransforms,
|
|
16635
|
-
passthrough: passthroughTransforms
|
|
16655
|
+
passthrough: passthroughTransforms,
|
|
16656
|
+
openai: openCodeTransforms
|
|
16636
16657
|
};
|
|
16637
16658
|
function getAdapterTransforms(adapterName) {
|
|
16638
16659
|
return ADAPTER_TRANSFORMS[adapterName] ?? [];
|
|
@@ -17504,6 +17525,22 @@ function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sd
|
|
|
17504
17525
|
// src/proxy/server.ts
|
|
17505
17526
|
var exec2 = promisify2(execCallback);
|
|
17506
17527
|
var claudeExecutable = "";
|
|
17528
|
+
function credentialStoreForProfile(profile) {
|
|
17529
|
+
if (profile.type !== "claude-max")
|
|
17530
|
+
return;
|
|
17531
|
+
return createPlatformCredentialStore(profile.env.CLAUDE_CONFIG_DIR ? { claudeConfigDir: profile.env.CLAUDE_CONFIG_DIR } : undefined);
|
|
17532
|
+
}
|
|
17533
|
+
async function ensureFreshTokenForProfiles(config) {
|
|
17534
|
+
const profiles = getEffectiveProfiles(config.profiles);
|
|
17535
|
+
if (profiles.length === 0)
|
|
17536
|
+
return;
|
|
17537
|
+
for (const profile of profiles) {
|
|
17538
|
+
const resolved = resolveProfile(config.profiles, config.defaultProfile, profile.id);
|
|
17539
|
+
const store = credentialStoreForProfile(resolved);
|
|
17540
|
+
if (store)
|
|
17541
|
+
await ensureFreshToken(store).catch(() => {});
|
|
17542
|
+
}
|
|
17543
|
+
}
|
|
17507
17544
|
var MULTIMODAL_TYPES = new Set(["image", "document", "file"]);
|
|
17508
17545
|
function hasMultimodalContent(content) {
|
|
17509
17546
|
if (!Array.isArray(content))
|
|
@@ -17627,8 +17664,13 @@ function buildFreshPrompt(messages, sanitizeOpts = {}) {
|
|
|
17627
17664
|
|
|
17628
17665
|
`) || "";
|
|
17629
17666
|
}
|
|
17667
|
+
var proxyLogSilent = false;
|
|
17668
|
+
function plog(message) {
|
|
17669
|
+
if (!proxyLogSilent)
|
|
17670
|
+
console.error(message);
|
|
17671
|
+
}
|
|
17630
17672
|
function logUsage(requestId, usage) {
|
|
17631
|
-
|
|
17673
|
+
plog(`[PROXY] ${requestId} usage: ${formatUsageSummary(usage)}`);
|
|
17632
17674
|
}
|
|
17633
17675
|
function checkTokenHealth(requestId, sdkSessionId, usage, turnNumber, isResume, isPassthrough) {
|
|
17634
17676
|
if (!usage || !sdkSessionId)
|
|
@@ -17661,7 +17703,7 @@ function checkTokenHealth(requestId, sdkSessionId, usage, turnNumber, isResume,
|
|
|
17661
17703
|
if (anomalies.length > 0) {
|
|
17662
17704
|
const alerts = formatAnomalyAlerts(requestId, anomalies);
|
|
17663
17705
|
for (const line of alerts) {
|
|
17664
|
-
|
|
17706
|
+
plog(line);
|
|
17665
17707
|
}
|
|
17666
17708
|
for (const a of anomalies) {
|
|
17667
17709
|
diagnosticLog2.log({
|
|
@@ -17675,6 +17717,7 @@ function checkTokenHealth(requestId, sdkSessionId, usage, turnNumber, isResume,
|
|
|
17675
17717
|
}
|
|
17676
17718
|
function createProxyServer(config = {}) {
|
|
17677
17719
|
const finalConfig = { ...DEFAULT_PROXY_CONFIG, ...config };
|
|
17720
|
+
proxyLogSilent = finalConfig.silent;
|
|
17678
17721
|
const serverVersion = finalConfig.version ?? "unknown";
|
|
17679
17722
|
restoreActiveProfile(finalConfig.profiles);
|
|
17680
17723
|
const sessionDiscoveredTools = new Map;
|
|
@@ -17782,6 +17825,7 @@ function createProxyServer(config = {}) {
|
|
|
17782
17825
|
} = process.env;
|
|
17783
17826
|
const sdkModelDefaults = resolveSdkModelDefaults();
|
|
17784
17827
|
const profileEnv = { ...sdkModelDefaults, ...cleanEnv, ...profile.env };
|
|
17828
|
+
const profileCredentialStore = credentialStoreForProfile(profile);
|
|
17785
17829
|
let systemContext = "";
|
|
17786
17830
|
if (body.system) {
|
|
17787
17831
|
if (typeof body.system === "string") {
|
|
@@ -17811,15 +17855,15 @@ function createProxyServer(config = {}) {
|
|
|
17811
17855
|
const rawBetaHeader = c.req.header("anthropic-beta");
|
|
17812
17856
|
const betaFilter = filterBetasForProfile(rawBetaHeader, profile.type, getBetaPolicyFromEnv());
|
|
17813
17857
|
if (betaFilter.stripped.length > 0) {
|
|
17814
|
-
|
|
17858
|
+
plog(`[PROXY] ${requestMeta.requestId} stripped anthropic-beta(s) for Max profile: ${betaFilter.stripped.join(", ")}`);
|
|
17815
17859
|
}
|
|
17816
|
-
const effort = effortHeader || body.effort ||
|
|
17860
|
+
const effort = normalizeEffort(effortHeader || body.effort || body.reasoning_effort || body.output_config?.effort);
|
|
17817
17861
|
let thinking = body.thinking || undefined;
|
|
17818
17862
|
if (thinkingHeader !== undefined) {
|
|
17819
17863
|
try {
|
|
17820
17864
|
thinking = JSON.parse(thinkingHeader);
|
|
17821
17865
|
} catch (e) {
|
|
17822
|
-
|
|
17866
|
+
plog(`[PROXY] ${requestMeta.requestId} ignoring malformed x-opencode-thinking header: ${e instanceof Error ? e.message : String(e)}`);
|
|
17823
17867
|
}
|
|
17824
17868
|
}
|
|
17825
17869
|
const { getFeaturesForAdapter: getFeaturesForAdapter2 } = (init_sdkFeatures(), __toCommonJS(exports_sdkFeatures));
|
|
@@ -17834,7 +17878,7 @@ function createProxyServer(config = {}) {
|
|
|
17834
17878
|
if (thinkingBetaStripped) {
|
|
17835
17879
|
thinking = { type: "disabled" };
|
|
17836
17880
|
if (betaFilter.stripped.length > 0) {
|
|
17837
|
-
|
|
17881
|
+
plog(`[PROXY] ${requestMeta.requestId} thinking disabled (thinking beta stripped by ${getBetaPolicyFromEnv()} policy)`);
|
|
17838
17882
|
}
|
|
17839
17883
|
}
|
|
17840
17884
|
const parsedBudget = taskBudgetHeader ? Number.parseInt(taskBudgetHeader, 10) : NaN;
|
|
@@ -17861,14 +17905,14 @@ function createProxyServer(config = {}) {
|
|
|
17861
17905
|
const msgCount = Array.isArray(body.messages) ? body.messages.length : 0;
|
|
17862
17906
|
const toolCount = body.tools?.length ?? 0;
|
|
17863
17907
|
const requestLogLine = `${requestMeta.requestId} adapter=${adapter.name}${requestSource ? ` source=${requestSource}` : ""} model=${model} stream=${stream2} tools=${toolCount} lineage=${lineageType} session=${resumeSessionId?.slice(0, 8) || "new"}${isUndo && undoRollbackUuid ? ` rollback=${undoRollbackUuid.slice(0, 8)}` : ""}${agentMode ? ` agent=${agentMode}` : ""} active=${activeSessions}/${MAX_CONCURRENT_SESSIONS} msgCount=${msgCount}`;
|
|
17864
|
-
|
|
17908
|
+
plog(`[PROXY] ${requestLogLine} msgs=${msgSummary}`);
|
|
17865
17909
|
diagnosticLog2.session(`${requestLogLine}`, requestMeta.requestId);
|
|
17866
17910
|
if (lineageResult.type === "diverged" && profileSessionId && !isIndependentSession) {
|
|
17867
17911
|
const recovery = lookupSessionRecovery(profileSessionId);
|
|
17868
17912
|
if (recovery) {
|
|
17869
17913
|
const prevId = recovery.previousClaudeSessionId || recovery.claudeSessionId;
|
|
17870
17914
|
const recoveryMsg = `${requestMeta.requestId} SESSION RECOVERY: previous conversation available. Run: claude --resume ${prevId}`;
|
|
17871
|
-
|
|
17915
|
+
plog(`[PROXY] ${recoveryMsg}`);
|
|
17872
17916
|
diagnosticLog2.session(recoveryMsg, requestMeta.requestId);
|
|
17873
17917
|
}
|
|
17874
17918
|
}
|
|
@@ -17964,7 +18008,7 @@ function createProxyServer(config = {}) {
|
|
|
17964
18008
|
const cached = sessionToolCache.get(profileSessionId);
|
|
17965
18009
|
if (cached && cached.length > 0) {
|
|
17966
18010
|
requestTools = cached;
|
|
17967
|
-
|
|
18011
|
+
plog(`[PROXY] ${requestMeta.requestId} tools_restored: client sent 0 tools but session had ${cached.length} — reusing cached tools to preserve prompt cache`);
|
|
17968
18012
|
}
|
|
17969
18013
|
}
|
|
17970
18014
|
if (passthrough && requestTools.length > 0) {
|
|
@@ -17977,7 +18021,7 @@ function createProxyServer(config = {}) {
|
|
|
17977
18021
|
if (profileSessionId) {
|
|
17978
18022
|
sessionMcpCache.set(profileSessionId, { key: toolSetKey, mcp: passthroughMcp });
|
|
17979
18023
|
if (cachedMcp) {
|
|
17980
|
-
|
|
18024
|
+
plog(`[PROXY] ${requestMeta.requestId} tools_changed: MCP server recreated (prompt cache likely invalidates)`);
|
|
17981
18025
|
}
|
|
17982
18026
|
}
|
|
17983
18027
|
}
|
|
@@ -17989,7 +18033,7 @@ function createProxyServer(config = {}) {
|
|
|
17989
18033
|
const coreSet = coreNames ? new Set(coreNames.map((n) => n.toLowerCase())) : undefined;
|
|
17990
18034
|
const deferredToolCount = hasDeferredTools && requestTools.length > 0 ? requestTools.filter((t) => t.defer_loading === true || coreSet && !coreSet.has(String(t.name).toLowerCase())).length : 0;
|
|
17991
18035
|
if (hasDeferredTools) {
|
|
17992
|
-
|
|
18036
|
+
plog(`[PROXY] ${requestMeta.requestId} deferred=${deferredToolCount}/${toolCount} tools (core: ${coreNames?.join(",") ?? "none"})`);
|
|
17993
18037
|
}
|
|
17994
18038
|
const mcpPrefix = `mcp__${adapter.getMcpServerName()}__`;
|
|
17995
18039
|
const trackFileChanges = !(process.env.MERIDIAN_NO_FILE_CHANGES ?? process.env.CLAUDE_PROXY_NO_FILE_CHANGES) && pipelineCtx.shouldTrackFileChanges;
|
|
@@ -18050,7 +18094,9 @@ function createProxyServer(config = {}) {
|
|
|
18050
18094
|
const RATE_LIMIT_BASE_DELAY_MS = 1000;
|
|
18051
18095
|
const response = async function* () {
|
|
18052
18096
|
let rateLimitRetries = 0;
|
|
18053
|
-
|
|
18097
|
+
if (profileCredentialStore) {
|
|
18098
|
+
await ensureFreshToken(profileCredentialStore).catch(() => {});
|
|
18099
|
+
}
|
|
18054
18100
|
let tokenRefreshed = false;
|
|
18055
18101
|
let didFreshBaseRetry = false;
|
|
18056
18102
|
while (true) {
|
|
@@ -18114,7 +18160,7 @@ function createProxyServer(config = {}) {
|
|
|
18114
18160
|
rollbackUuid: undoRollbackUuid,
|
|
18115
18161
|
resumeSessionId
|
|
18116
18162
|
});
|
|
18117
|
-
|
|
18163
|
+
plog(`[PROXY] Stale session UUID, evicting and retrying as fresh session`);
|
|
18118
18164
|
evictSession(profileSessionId, profileScopedCwd, allMessages);
|
|
18119
18165
|
sdkUuidMap.length = 0;
|
|
18120
18166
|
for (let i = 0;i < allMessages.length; i++)
|
|
@@ -18170,7 +18216,7 @@ function createProxyServer(config = {}) {
|
|
|
18170
18216
|
to: model,
|
|
18171
18217
|
reason: "extra_usage_required"
|
|
18172
18218
|
});
|
|
18173
|
-
|
|
18219
|
+
plog(`[PROXY] ${requestMeta.requestId} extra usage required for [1m], falling back to ${model} (skipping [1m] for 1h)`);
|
|
18174
18220
|
continue;
|
|
18175
18221
|
}
|
|
18176
18222
|
if (isExtraUsageRequiredError(errMsg) && resumeSessionId && !didFreshBaseRetry) {
|
|
@@ -18180,7 +18226,7 @@ function createProxyServer(config = {}) {
|
|
|
18180
18226
|
model,
|
|
18181
18227
|
reason: "extra_usage_required_resume"
|
|
18182
18228
|
});
|
|
18183
|
-
|
|
18229
|
+
plog(`[PROXY] ${requestMeta.requestId} extra usage persisted on resumed ${model}, retrying as fresh session`);
|
|
18184
18230
|
evictSession(profileSessionId, profileScopedCwd, allMessages);
|
|
18185
18231
|
sdkUuidMap.length = 0;
|
|
18186
18232
|
for (let i = 0;i < allMessages.length; i++)
|
|
@@ -18228,10 +18274,10 @@ function createProxyServer(config = {}) {
|
|
|
18228
18274
|
}
|
|
18229
18275
|
if (isExpiredTokenError(errMsg) && !tokenRefreshed) {
|
|
18230
18276
|
tokenRefreshed = true;
|
|
18231
|
-
const refreshed = await refreshOAuthToken();
|
|
18277
|
+
const refreshed = profileCredentialStore ? await refreshOAuthToken(profileCredentialStore) : false;
|
|
18232
18278
|
if (refreshed) {
|
|
18233
18279
|
claudeLog("token_refresh.retrying", { mode: "non_stream" });
|
|
18234
|
-
|
|
18280
|
+
plog(`[PROXY] ${requestMeta.requestId} OAuth token expired — refreshed, retrying`);
|
|
18235
18281
|
continue;
|
|
18236
18282
|
}
|
|
18237
18283
|
}
|
|
@@ -18245,7 +18291,7 @@ function createProxyServer(config = {}) {
|
|
|
18245
18291
|
to: model,
|
|
18246
18292
|
reason: "rate_limit"
|
|
18247
18293
|
});
|
|
18248
|
-
|
|
18294
|
+
plog(`[PROXY] ${requestMeta.requestId} rate-limited on [1m], retrying with ${model}`);
|
|
18249
18295
|
continue;
|
|
18250
18296
|
}
|
|
18251
18297
|
if (rateLimitRetries < MAX_RATE_LIMIT_RETRIES) {
|
|
@@ -18258,7 +18304,7 @@ function createProxyServer(config = {}) {
|
|
|
18258
18304
|
maxAttempts: MAX_RATE_LIMIT_RETRIES,
|
|
18259
18305
|
delayMs: delay
|
|
18260
18306
|
});
|
|
18261
|
-
|
|
18307
|
+
plog(`[PROXY] ${requestMeta.requestId} rate-limited on ${model}, retry ${rateLimitRetries}/${MAX_RATE_LIMIT_RETRIES} in ${delay}ms`);
|
|
18262
18308
|
await new Promise((r) => setTimeout(r, delay));
|
|
18263
18309
|
continue;
|
|
18264
18310
|
}
|
|
@@ -18334,7 +18380,7 @@ function createProxyServer(config = {}) {
|
|
|
18334
18380
|
sessionDiscoveredTools.get(sessId).add(t);
|
|
18335
18381
|
const newNames = [...discoveredTools].join(", ");
|
|
18336
18382
|
const allNames = [...sessionDiscoveredTools.get(sessId)];
|
|
18337
|
-
|
|
18383
|
+
plog(`[PROXY] ${requestMeta.requestId} discovered=${discoveredTools.size} (${newNames}) session_total=${allNames.length}`);
|
|
18338
18384
|
}
|
|
18339
18385
|
} catch (error) {
|
|
18340
18386
|
const stderrOutput = stderrLines.join(`
|
|
@@ -18508,7 +18554,9 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
18508
18554
|
const RATE_LIMIT_BASE_DELAY_MS = 1000;
|
|
18509
18555
|
const response = async function* () {
|
|
18510
18556
|
let rateLimitRetries = 0;
|
|
18511
|
-
|
|
18557
|
+
if (profileCredentialStore) {
|
|
18558
|
+
await ensureFreshToken(profileCredentialStore).catch(() => {});
|
|
18559
|
+
}
|
|
18512
18560
|
let tokenRefreshed = false;
|
|
18513
18561
|
let didFreshBaseRetry = false;
|
|
18514
18562
|
while (true) {
|
|
@@ -18572,7 +18620,7 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
18572
18620
|
rollbackUuid: undoRollbackUuid,
|
|
18573
18621
|
resumeSessionId
|
|
18574
18622
|
});
|
|
18575
|
-
|
|
18623
|
+
plog(`[PROXY] Stale session UUID, evicting and retrying as fresh session`);
|
|
18576
18624
|
evictSession(profileSessionId, profileScopedCwd, allMessages);
|
|
18577
18625
|
sdkUuidMap.length = 0;
|
|
18578
18626
|
for (let i = 0;i < allMessages.length; i++)
|
|
@@ -18628,7 +18676,7 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
18628
18676
|
to: model,
|
|
18629
18677
|
reason: "extra_usage_required"
|
|
18630
18678
|
});
|
|
18631
|
-
|
|
18679
|
+
plog(`[PROXY] ${requestMeta.requestId} extra usage required for [1m], falling back to ${model} (skipping [1m] for 1h)`);
|
|
18632
18680
|
continue;
|
|
18633
18681
|
}
|
|
18634
18682
|
if (isExtraUsageRequiredError(errMsg) && resumeSessionId && !didFreshBaseRetry) {
|
|
@@ -18638,7 +18686,7 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
18638
18686
|
model,
|
|
18639
18687
|
reason: "extra_usage_required_resume"
|
|
18640
18688
|
});
|
|
18641
|
-
|
|
18689
|
+
plog(`[PROXY] ${requestMeta.requestId} extra usage persisted on resumed ${model}, retrying as fresh session`);
|
|
18642
18690
|
evictSession(profileSessionId, profileScopedCwd, allMessages);
|
|
18643
18691
|
sdkUuidMap.length = 0;
|
|
18644
18692
|
for (let i = 0;i < allMessages.length; i++)
|
|
@@ -18686,10 +18734,10 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
18686
18734
|
}
|
|
18687
18735
|
if (isExpiredTokenError(errMsg) && !tokenRefreshed) {
|
|
18688
18736
|
tokenRefreshed = true;
|
|
18689
|
-
const refreshed = await refreshOAuthToken();
|
|
18737
|
+
const refreshed = profileCredentialStore ? await refreshOAuthToken(profileCredentialStore) : false;
|
|
18690
18738
|
if (refreshed) {
|
|
18691
18739
|
claudeLog("token_refresh.retrying", { mode: "stream" });
|
|
18692
|
-
|
|
18740
|
+
plog(`[PROXY] ${requestMeta.requestId} OAuth token expired — refreshed, retrying`);
|
|
18693
18741
|
continue;
|
|
18694
18742
|
}
|
|
18695
18743
|
}
|
|
@@ -18703,7 +18751,7 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
18703
18751
|
to: model,
|
|
18704
18752
|
reason: "rate_limit"
|
|
18705
18753
|
});
|
|
18706
|
-
|
|
18754
|
+
plog(`[PROXY] ${requestMeta.requestId} rate-limited on [1m], retrying with ${model}`);
|
|
18707
18755
|
continue;
|
|
18708
18756
|
}
|
|
18709
18757
|
if (rateLimitRetries < MAX_RATE_LIMIT_RETRIES) {
|
|
@@ -18716,7 +18764,7 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
18716
18764
|
maxAttempts: MAX_RATE_LIMIT_RETRIES,
|
|
18717
18765
|
delayMs: delay
|
|
18718
18766
|
});
|
|
18719
|
-
|
|
18767
|
+
plog(`[PROXY] ${requestMeta.requestId} rate-limited on ${model}, retry ${rateLimitRetries}/${MAX_RATE_LIMIT_RETRIES} in ${delay}ms`);
|
|
18720
18768
|
await new Promise((r) => setTimeout(r, delay));
|
|
18721
18769
|
continue;
|
|
18722
18770
|
}
|
|
@@ -18930,7 +18978,7 @@ data: ${JSON.stringify({ type: "message_stop" })}
|
|
|
18930
18978
|
sessionDiscoveredTools.get(sessId).add(t);
|
|
18931
18979
|
const newNames = [...discoveredTools].join(", ");
|
|
18932
18980
|
const allNames = [...sessionDiscoveredTools.get(sessId)];
|
|
18933
|
-
|
|
18981
|
+
plog(`[PROXY] ${requestMeta.requestId} discovered=${discoveredTools.size} (${newNames}) session_total=${allNames.length}`);
|
|
18934
18982
|
}
|
|
18935
18983
|
if (currentSessionId && !isIndependentSession) {
|
|
18936
18984
|
storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage);
|
|
@@ -19449,7 +19497,7 @@ data: ${JSON.stringify({
|
|
|
19449
19497
|
setActiveProfile(body.profile);
|
|
19450
19498
|
clearSessionCache();
|
|
19451
19499
|
rateLimitStore.clear();
|
|
19452
|
-
|
|
19500
|
+
plog(`[PROXY] Active profile switched to: ${body.profile} (session + rate-limit caches cleared)`);
|
|
19453
19501
|
return c.json({ success: true, activeProfile: body.profile });
|
|
19454
19502
|
});
|
|
19455
19503
|
app.get("/plugins/list", async (c) => {
|
|
@@ -19473,7 +19521,7 @@ data: ${JSON.stringify({
|
|
|
19473
19521
|
loadedPlugins = await loadPlugins(pluginDir, pluginConfigPath);
|
|
19474
19522
|
pluginTransforms = getActiveTransforms(loadedPlugins);
|
|
19475
19523
|
const active = loadedPlugins.filter((p) => p.status === "active").length;
|
|
19476
|
-
|
|
19524
|
+
plog(`[PROXY] Plugins reloaded: ${active} active`);
|
|
19477
19525
|
return c.json({
|
|
19478
19526
|
success: true,
|
|
19479
19527
|
plugins: loadedPlugins.map((p) => ({
|
|
@@ -19492,10 +19540,12 @@ data: ${JSON.stringify({
|
|
|
19492
19540
|
return c.html(pluginPageHtml);
|
|
19493
19541
|
});
|
|
19494
19542
|
app.post("/auth/refresh", async (c) => {
|
|
19495
|
-
const
|
|
19543
|
+
const profile = resolveProfile(finalConfig.profiles, finalConfig.defaultProfile, c.req.header("x-meridian-profile") || undefined);
|
|
19544
|
+
const store = credentialStoreForProfile(profile);
|
|
19545
|
+
const success = store ? await refreshOAuthToken(store) : false;
|
|
19496
19546
|
if (success) {
|
|
19497
19547
|
rateLimitStore.clear();
|
|
19498
|
-
return c.json({ success: true, message: "OAuth token refreshed successfully" });
|
|
19548
|
+
return c.json({ success: true, message: "OAuth token refreshed successfully", profile: profile.id });
|
|
19499
19549
|
}
|
|
19500
19550
|
return c.json({ success: false, message: "Token refresh failed. If the problem persists, run 'claude login'." }, 500);
|
|
19501
19551
|
});
|
|
@@ -19505,7 +19555,10 @@ data: ${JSON.stringify({
|
|
|
19505
19555
|
if (!anthropicBody) {
|
|
19506
19556
|
return c.json({ type: "error", error: { type: "invalid_request_error", message: "messages: Field required" } }, 400);
|
|
19507
19557
|
}
|
|
19508
|
-
const internalHeaders = {
|
|
19558
|
+
const internalHeaders = {
|
|
19559
|
+
"Content-Type": "application/json",
|
|
19560
|
+
"x-meridian-agent": "openai"
|
|
19561
|
+
};
|
|
19509
19562
|
const xApiKey = c.req.header("x-api-key");
|
|
19510
19563
|
if (xApiKey)
|
|
19511
19564
|
internalHeaders["x-api-key"] = xApiKey;
|
|
@@ -19526,8 +19579,7 @@ data: ${JSON.stringify({
|
|
|
19526
19579
|
const created = Math.floor(Date.now() / 1000);
|
|
19527
19580
|
const model = typeof rawBody.model === "string" && rawBody.model ? rawBody.model : "claude-sonnet-4-6";
|
|
19528
19581
|
const { getFeaturesForAdapter: getFeaturesForAdapter2 } = (init_sdkFeatures(), __toCommonJS(exports_sdkFeatures));
|
|
19529
|
-
const
|
|
19530
|
-
const sdkFeatures = getFeaturesForAdapter2(adapter.name);
|
|
19582
|
+
const sdkFeatures = getFeaturesForAdapter2("openai");
|
|
19531
19583
|
if (!anthropicBody.stream) {
|
|
19532
19584
|
const anthropicRes = await internalRes.json();
|
|
19533
19585
|
return c.json(translateAnthropicToOpenAi(anthropicRes, completionId, model, created, {
|
|
@@ -19758,7 +19810,7 @@ data: ${JSON.stringify({
|
|
|
19758
19810
|
});
|
|
19759
19811
|
});
|
|
19760
19812
|
app.all("*", (c) => {
|
|
19761
|
-
|
|
19813
|
+
plog(`[PROXY] UNHANDLED ${c.req.method} ${c.req.url}`);
|
|
19762
19814
|
return c.json({ error: { type: "not_found", message: `Endpoint not supported: ${c.req.method} ${new URL(c.req.url).pathname}` } }, 404);
|
|
19763
19815
|
});
|
|
19764
19816
|
async function initPluginsAsync() {
|
|
@@ -19769,19 +19821,34 @@ data: ${JSON.stringify({
|
|
|
19769
19821
|
const active = loadedPlugins.filter((p) => p.status === "active").length;
|
|
19770
19822
|
const disabled = loadedPlugins.filter((p) => p.status === "disabled").length;
|
|
19771
19823
|
const errored = loadedPlugins.filter((p) => p.status === "error").length;
|
|
19772
|
-
|
|
19824
|
+
plog(`[PROXY] Plugins loaded: ${active} active, ${disabled} disabled, ${errored} errors`);
|
|
19773
19825
|
}
|
|
19774
19826
|
} catch (err) {
|
|
19775
|
-
|
|
19827
|
+
plog(`[PROXY] Plugin loading failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
19776
19828
|
}
|
|
19777
19829
|
}
|
|
19778
19830
|
return { app, config: finalConfig, initPlugins: initPluginsAsync };
|
|
19779
19831
|
}
|
|
19832
|
+
var processErrorHandlersInstalled = false;
|
|
19833
|
+
function installProxyProcessErrorHandlers() {
|
|
19834
|
+
if (processErrorHandlersInstalled)
|
|
19835
|
+
return;
|
|
19836
|
+
processErrorHandlersInstalled = true;
|
|
19837
|
+
process.on("uncaughtException", (err) => {
|
|
19838
|
+
console.error(`[PROXY] Uncaught exception (recovered): ${err.message}`);
|
|
19839
|
+
});
|
|
19840
|
+
process.on("unhandledRejection", (reason) => {
|
|
19841
|
+
console.error(`[PROXY] Unhandled rejection (recovered): ${reason instanceof Error ? reason.message : reason}`);
|
|
19842
|
+
});
|
|
19843
|
+
}
|
|
19780
19844
|
async function startProxyServer(config = {}) {
|
|
19781
19845
|
claudeExecutable = await resolveClaudeExecutableAsync();
|
|
19782
19846
|
const { app, config: finalConfig, initPlugins } = createProxyServer(config);
|
|
19783
19847
|
if (initPlugins)
|
|
19784
19848
|
await initPlugins();
|
|
19849
|
+
if (finalConfig.installProcessErrorHandlers) {
|
|
19850
|
+
installProxyProcessErrorHandlers();
|
|
19851
|
+
}
|
|
19785
19852
|
const server = serve({
|
|
19786
19853
|
fetch: app.fetch,
|
|
19787
19854
|
port: finalConfig.port,
|
|
@@ -19819,6 +19886,13 @@ Or use a different port:`);
|
|
|
19819
19886
|
}
|
|
19820
19887
|
});
|
|
19821
19888
|
startBackgroundRefresh();
|
|
19889
|
+
const PROFILE_TOKEN_REFRESH_MS = 45000;
|
|
19890
|
+
ensureFreshTokenForProfiles(finalConfig);
|
|
19891
|
+
const profileTokenRefreshInterval = setInterval(() => {
|
|
19892
|
+
ensureFreshTokenForProfiles(finalConfig);
|
|
19893
|
+
}, PROFILE_TOKEN_REFRESH_MS);
|
|
19894
|
+
if (profileTokenRefreshInterval.unref)
|
|
19895
|
+
profileTokenRefreshInterval.unref();
|
|
19822
19896
|
let authKeepaliveInterval;
|
|
19823
19897
|
const effectiveProfiles = getEffectiveProfiles(finalConfig.profiles);
|
|
19824
19898
|
if (effectiveProfiles.length > 0) {
|
|
@@ -19840,6 +19914,7 @@ Or use a different port:`);
|
|
|
19840
19914
|
server,
|
|
19841
19915
|
config: finalConfig,
|
|
19842
19916
|
async close() {
|
|
19917
|
+
clearInterval(profileTokenRefreshInterval);
|
|
19843
19918
|
if (authKeepaliveInterval)
|
|
19844
19919
|
clearInterval(authKeepaliveInterval);
|
|
19845
19920
|
stopBackgroundRefresh();
|
|
@@ -19850,4 +19925,4 @@ Or use a different port:`);
|
|
|
19850
19925
|
};
|
|
19851
19926
|
}
|
|
19852
19927
|
|
|
19853
|
-
export { runTransformHook, runObserveHook, buildPipeline, createRequestContext, computeLineageHash, hashMessage, computeMessageHashes, getMaxSessionsLimit, clearSessionCache, createProxyServer, startProxyServer };
|
|
19928
|
+
export { runTransformHook, runObserveHook, buildPipeline, createRequestContext, computeLineageHash, hashMessage, computeMessageHashes, getMaxSessionsLimit, clearSessionCache, createProxyServer, installProxyProcessErrorHandlers, startProxyServer };
|
|
@@ -3,6 +3,24 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
|
3
3
|
import { homedir, platform } from "os";
|
|
4
4
|
import { dirname, join } from "path";
|
|
5
5
|
import { fileURLToPath } from "url";
|
|
6
|
+
import { applyEdits, modify, parse as parseJsonc } from "jsonc-parser";
|
|
7
|
+
|
|
8
|
+
class UnparseableConfigError extends Error {
|
|
9
|
+
configPath;
|
|
10
|
+
constructor(configPath) {
|
|
11
|
+
super(`Could not parse ${configPath} — it may contain a syntax error.`);
|
|
12
|
+
this.configPath = configPath;
|
|
13
|
+
this.name = "UnparseableConfigError";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function parseOpencodeConfig(text) {
|
|
17
|
+
const errors = [];
|
|
18
|
+
const parsed = parseJsonc(text, errors, { allowTrailingComma: true });
|
|
19
|
+
if (errors.length > 0 || parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
return parsed;
|
|
23
|
+
}
|
|
6
24
|
function findOpencodeConfigPath() {
|
|
7
25
|
if (process.env.OPENCODE_CONFIG_DIR) {
|
|
8
26
|
return join(process.env.OPENCODE_CONFIG_DIR, "opencode.json");
|
|
@@ -31,37 +49,37 @@ function checkPluginConfigured(configPath) {
|
|
|
31
49
|
const path = configPath ?? findOpencodeConfigPath();
|
|
32
50
|
if (!existsSync(path))
|
|
33
51
|
return false;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const config = JSON.parse(raw);
|
|
37
|
-
const plugins = Array.isArray(config.plugin) ? config.plugin : [];
|
|
38
|
-
return plugins.some((p) => typeof p === "string" && isMeridianEntry(p));
|
|
39
|
-
} catch {
|
|
52
|
+
const config = parseOpencodeConfig(readFileSync(path, "utf-8"));
|
|
53
|
+
if (config === null)
|
|
40
54
|
return false;
|
|
41
|
-
|
|
55
|
+
const plugins = Array.isArray(config.plugin) ? config.plugin : [];
|
|
56
|
+
return plugins.some((p) => typeof p === "string" && isMeridianEntry(p));
|
|
42
57
|
}
|
|
43
58
|
function runSetup(pluginPath, configPath) {
|
|
44
59
|
const path = configPath ?? findOpencodeConfigPath();
|
|
45
60
|
const dir = dirname(path);
|
|
46
|
-
|
|
47
|
-
let created = false;
|
|
48
|
-
if (existsSync(path)) {
|
|
49
|
-
try {
|
|
50
|
-
config = JSON.parse(readFileSync(path, "utf-8"));
|
|
51
|
-
} catch {}
|
|
52
|
-
} else {
|
|
53
|
-
created = true;
|
|
61
|
+
if (!existsSync(path)) {
|
|
54
62
|
if (!existsSync(dir))
|
|
55
63
|
mkdirSync(dir, { recursive: true });
|
|
64
|
+
writeFileSync(path, `${JSON.stringify({ plugin: [pluginPath] }, null, 2)}
|
|
65
|
+
`, "utf-8");
|
|
66
|
+
return { configPath: path, pluginPath, alreadyConfigured: false, removedStale: [], created: true };
|
|
67
|
+
}
|
|
68
|
+
const text = readFileSync(path, "utf-8");
|
|
69
|
+
const config = parseOpencodeConfig(text);
|
|
70
|
+
if (config === null) {
|
|
71
|
+
throw new UnparseableConfigError(path);
|
|
56
72
|
}
|
|
57
73
|
const existing = Array.isArray(config.plugin) ? config.plugin.filter((p) => typeof p === "string") : [];
|
|
58
74
|
const removedStale = existing.filter(isMeridianEntry);
|
|
59
75
|
const others = existing.filter((p) => !isMeridianEntry(p));
|
|
60
76
|
const alreadyConfigured = removedStale.some((p) => p === pluginPath);
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
77
|
+
const newPlugins = [...others, pluginPath];
|
|
78
|
+
const edits = modify(text, ["plugin"], newPlugins, {
|
|
79
|
+
formattingOptions: { insertSpaces: true, tabSize: 2 }
|
|
80
|
+
});
|
|
81
|
+
writeFileSync(path, applyEdits(text, edits), "utf-8");
|
|
82
|
+
return { configPath: path, pluginPath, alreadyConfigured, removedStale, created: false };
|
|
65
83
|
}
|
|
66
84
|
|
|
67
|
-
export { findOpencodeConfigPath, findPluginPath, checkPluginConfigured, runSetup };
|
|
85
|
+
export { UnparseableConfigError, findOpencodeConfigPath, findPluginPath, checkPluginConfigured, runSetup };
|