@rynfar/meridian 1.41.1 → 1.42.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 +43 -4
- package/dist/cli-3jqvrake.js +279 -0
- package/dist/{cli-e289rj3k.js → cli-7k1fcprd.js} +64 -1
- package/dist/{cli-51a9sav0.js → cli-8xzxm1cq.js} +115 -283
- package/dist/{cli-vdp9s10c.js → cli-cx463q74.js} +8 -0
- package/dist/cli.js +25 -12
- package/dist/{profileCli-5f15dx7k.js → profileCli-c7cvkv5q.js} +129 -17
- package/dist/{profiles-edzz1ffd.js → profiles-rdd84b45.js} +1 -1
- package/dist/proxy/cwd.d.ts +42 -0
- package/dist/proxy/cwd.d.ts.map +1 -0
- package/dist/proxy/errors.d.ts +14 -4
- package/dist/proxy/errors.d.ts.map +1 -1
- package/dist/proxy/models.d.ts +57 -4
- package/dist/proxy/models.d.ts.map +1 -1
- package/dist/proxy/oauthUsage.d.ts +17 -7
- package/dist/proxy/oauthUsage.d.ts.map +1 -1
- package/dist/proxy/plugins/loader.d.ts.map +1 -1
- package/dist/proxy/profiles.d.ts +12 -4
- package/dist/proxy/profiles.d.ts.map +1 -1
- package/dist/proxy/query.d.ts.map +1 -1
- package/dist/proxy/server.d.ts.map +1 -1
- package/dist/proxy/tokenRefresh.d.ts +41 -0
- package/dist/proxy/tokenRefresh.d.ts.map +1 -1
- package/dist/server.js +4 -3
- package/dist/{tokenRefresh-psq94r54.js → tokenRefresh-swetnf89.js} +10 -2
- package/package.json +2 -2
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
resolveProfile,
|
|
6
6
|
restoreActiveProfile,
|
|
7
7
|
setActiveProfile
|
|
8
|
-
} from "./cli-
|
|
8
|
+
} from "./cli-cx463q74.js";
|
|
9
9
|
import {
|
|
10
10
|
isTrackedPlugin,
|
|
11
11
|
recordError,
|
|
@@ -20,15 +20,30 @@ import {
|
|
|
20
20
|
profileBarJs,
|
|
21
21
|
themeCss
|
|
22
22
|
} from "./cli-4rqtm83g.js";
|
|
23
|
+
import {
|
|
24
|
+
getAuthCacheInfo,
|
|
25
|
+
getClaudeAuthStatusAsync,
|
|
26
|
+
getResolvedClaudeExecutableInfo,
|
|
27
|
+
hasExtendedContext,
|
|
28
|
+
isClosedControllerError,
|
|
29
|
+
mapModelToClaudeModel,
|
|
30
|
+
recordExtendedContextUnavailable,
|
|
31
|
+
resolveClaudeExecutableAsync,
|
|
32
|
+
resolveSdkModelDefaults,
|
|
33
|
+
stripExtendedContext
|
|
34
|
+
} from "./cli-3jqvrake.js";
|
|
23
35
|
import {
|
|
24
36
|
checkPluginConfigured
|
|
25
37
|
} from "./cli-rtab0qa6.js";
|
|
26
38
|
import {
|
|
27
39
|
claudeLog,
|
|
28
40
|
createPlatformCredentialStore,
|
|
41
|
+
ensureFreshToken,
|
|
29
42
|
refreshOAuthToken,
|
|
43
|
+
startBackgroundRefresh,
|
|
44
|
+
stopBackgroundRefresh,
|
|
30
45
|
withClaudeLogContext
|
|
31
|
-
} from "./cli-
|
|
46
|
+
} from "./cli-7k1fcprd.js";
|
|
32
47
|
import {
|
|
33
48
|
__commonJS,
|
|
34
49
|
__esm,
|
|
@@ -1179,13 +1194,13 @@ __export(exports_sdkFeatures, {
|
|
|
1179
1194
|
getAllFeatureConfigs: () => getAllFeatureConfigs
|
|
1180
1195
|
});
|
|
1181
1196
|
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2, renameSync as renameSync2 } from "node:fs";
|
|
1182
|
-
import { join as
|
|
1197
|
+
import { join as join4 } from "node:path";
|
|
1183
1198
|
import { homedir as homedir3 } from "node:os";
|
|
1184
1199
|
function getConfigPath() {
|
|
1185
|
-
const dir =
|
|
1200
|
+
const dir = join4(homedir3(), ".config", "meridian");
|
|
1186
1201
|
if (!existsSync5(dir))
|
|
1187
1202
|
mkdirSync2(dir, { recursive: true });
|
|
1188
|
-
return
|
|
1203
|
+
return join4(dir, "sdk-features.json");
|
|
1189
1204
|
}
|
|
1190
1205
|
function readConfig() {
|
|
1191
1206
|
const now = Date.now();
|
|
@@ -3708,7 +3723,7 @@ var serve = (options, listeningListener) => {
|
|
|
3708
3723
|
|
|
3709
3724
|
// src/proxy/server.ts
|
|
3710
3725
|
import { homedir as homedir4 } from "node:os";
|
|
3711
|
-
import { join as
|
|
3726
|
+
import { join as join5 } from "node:path";
|
|
3712
3727
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
3713
3728
|
|
|
3714
3729
|
// src/proxy/rateLimitStore.ts
|
|
@@ -3788,8 +3803,8 @@ async function readAccessToken(store) {
|
|
|
3788
3803
|
const creds = await store.read();
|
|
3789
3804
|
return creds?.claudeAiOauth?.accessToken ?? null;
|
|
3790
3805
|
}
|
|
3791
|
-
async function callAnthropic(token, signal) {
|
|
3792
|
-
const res = await
|
|
3806
|
+
async function callAnthropic(token, fetchImpl, signal) {
|
|
3807
|
+
const res = await fetchImpl(OAUTH_USAGE_URL, {
|
|
3793
3808
|
headers: {
|
|
3794
3809
|
Authorization: `Bearer ${token}`,
|
|
3795
3810
|
"anthropic-beta": OAUTH_BETA_HEADER,
|
|
@@ -3801,9 +3816,17 @@ async function callAnthropic(token, signal) {
|
|
|
3801
3816
|
return { __status: res.status };
|
|
3802
3817
|
return await res.json();
|
|
3803
3818
|
}
|
|
3819
|
+
var _testOverride = null;
|
|
3804
3820
|
async function fetchOAuthUsage(opts) {
|
|
3821
|
+
if (_testOverride && !opts?.fetchImpl && !opts?.store) {
|
|
3822
|
+
return _testOverride(opts);
|
|
3823
|
+
}
|
|
3824
|
+
return fetchOAuthUsageImpl(opts);
|
|
3825
|
+
}
|
|
3826
|
+
async function fetchOAuthUsageImpl(opts) {
|
|
3805
3827
|
const ttl = opts?.ttlMs ?? CACHE_TTL_MS_DEFAULT;
|
|
3806
3828
|
const cacheKey2 = opts?.profileId ?? DEFAULT_KEY;
|
|
3829
|
+
const fetchImpl = opts?.fetchImpl ?? globalThis.fetch;
|
|
3807
3830
|
if (!opts?.force) {
|
|
3808
3831
|
const cached = cacheByProfile.get(cacheKey2);
|
|
3809
3832
|
if (cached && Date.now() - cached.fetchedAt < ttl)
|
|
@@ -3818,7 +3841,7 @@ async function fetchOAuthUsage(opts) {
|
|
|
3818
3841
|
const token = await readAccessToken(store);
|
|
3819
3842
|
if (!token)
|
|
3820
3843
|
return null;
|
|
3821
|
-
let result = await callAnthropic(token);
|
|
3844
|
+
let result = await callAnthropic(token, fetchImpl);
|
|
3822
3845
|
if ("__status" in result && result.__status === 401) {
|
|
3823
3846
|
claudeLog("oauth_usage.token_refresh_attempt", { profile: cacheKey2 });
|
|
3824
3847
|
const refreshed = await refreshOAuthToken(store);
|
|
@@ -3829,7 +3852,7 @@ async function fetchOAuthUsage(opts) {
|
|
|
3829
3852
|
const newToken = await readAccessToken(store);
|
|
3830
3853
|
if (!newToken)
|
|
3831
3854
|
return null;
|
|
3832
|
-
result = await callAnthropic(newToken);
|
|
3855
|
+
result = await callAnthropic(newToken, fetchImpl);
|
|
3833
3856
|
}
|
|
3834
3857
|
if ("__status" in result) {
|
|
3835
3858
|
claudeLog("oauth_usage.upstream_error", { profile: cacheKey2, status: result.__status });
|
|
@@ -3849,6 +3872,17 @@ async function fetchOAuthUsage(opts) {
|
|
|
3849
3872
|
return promise;
|
|
3850
3873
|
}
|
|
3851
3874
|
|
|
3875
|
+
// src/proxy/cwd.ts
|
|
3876
|
+
import { existsSync } from "node:fs";
|
|
3877
|
+
function resolveSdkWorkingDirectory(opts) {
|
|
3878
|
+
const exists = opts.exists ?? existsSync;
|
|
3879
|
+
const claimed = opts.envOverride || opts.adapterCwd || opts.fallback;
|
|
3880
|
+
if (exists(claimed)) {
|
|
3881
|
+
return { workingDirectory: claimed, claimedWorkingDirectory: claimed, fellBack: false };
|
|
3882
|
+
}
|
|
3883
|
+
return { workingDirectory: opts.fallback, claimedWorkingDirectory: claimed, fellBack: true };
|
|
3884
|
+
}
|
|
3885
|
+
|
|
3852
3886
|
// src/proxy/types.ts
|
|
3853
3887
|
var DEFAULT_PROXY_CONFIG = {
|
|
3854
3888
|
port: 3456,
|
|
@@ -3945,8 +3979,8 @@ function createRequestContext(params) {
|
|
|
3945
3979
|
};
|
|
3946
3980
|
}
|
|
3947
3981
|
// src/proxy/server.ts
|
|
3948
|
-
import { exec as
|
|
3949
|
-
import { promisify as
|
|
3982
|
+
import { exec as execCallback } from "child_process";
|
|
3983
|
+
import { promisify as promisify2 } from "util";
|
|
3950
3984
|
import { randomUUID } from "crypto";
|
|
3951
3985
|
|
|
3952
3986
|
// src/proxy/passthroughTools.ts
|
|
@@ -8447,7 +8481,7 @@ class MemoryDiagnosticLogStore {
|
|
|
8447
8481
|
}
|
|
8448
8482
|
var diagnosticLog = new MemoryDiagnosticLogStore;
|
|
8449
8483
|
// src/telemetry/routes.ts
|
|
8450
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
8484
|
+
import { existsSync as existsSync2, readFileSync } from "node:fs";
|
|
8451
8485
|
import { resolve, dirname } from "node:path";
|
|
8452
8486
|
import { fileURLToPath } from "node:url";
|
|
8453
8487
|
|
|
@@ -8801,7 +8835,7 @@ timer = setInterval(refresh, 5000);
|
|
|
8801
8835
|
|
|
8802
8836
|
// src/telemetry/routes.ts
|
|
8803
8837
|
var _iconPath = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "assets", "icon.svg");
|
|
8804
|
-
var _iconSvg =
|
|
8838
|
+
var _iconSvg = existsSync2(_iconPath) ? readFileSync(_iconPath, "utf-8") : null;
|
|
8805
8839
|
function createTelemetryRoutes() {
|
|
8806
8840
|
const routes = new Hono2;
|
|
8807
8841
|
routes.get("/", (c) => {
|
|
@@ -9139,7 +9173,13 @@ function classifyError(errMsg) {
|
|
|
9139
9173
|
}
|
|
9140
9174
|
function isExpiredTokenError(errMsg) {
|
|
9141
9175
|
const lower = errMsg.toLowerCase();
|
|
9142
|
-
|
|
9176
|
+
if (lower.includes("oauth token has expired") || lower.includes("not logged in"))
|
|
9177
|
+
return true;
|
|
9178
|
+
if (lower.includes("invalid_token") || lower.includes("token_expired"))
|
|
9179
|
+
return true;
|
|
9180
|
+
if (lower.includes("401") && (lower.includes("authentication") || lower.includes("unauthorized") || lower.includes("invalid")))
|
|
9181
|
+
return true;
|
|
9182
|
+
return false;
|
|
9143
9183
|
}
|
|
9144
9184
|
function isStaleSessionError(error) {
|
|
9145
9185
|
if (!(error instanceof Error))
|
|
@@ -9232,252 +9272,6 @@ function formatSdkTermination(t, ctx) {
|
|
|
9232
9272
|
return `sdk_termination ${parts.join(" ")}`;
|
|
9233
9273
|
}
|
|
9234
9274
|
|
|
9235
|
-
// src/proxy/models.ts
|
|
9236
|
-
import { exec as execCallback } from "child_process";
|
|
9237
|
-
import { existsSync as existsSync2, statSync } from "fs";
|
|
9238
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9239
|
-
import { join as join2, dirname as dirname2 } from "path";
|
|
9240
|
-
import { promisify } from "util";
|
|
9241
|
-
var exec = promisify(execCallback);
|
|
9242
|
-
var STUB_SIZE_THRESHOLD = 4096;
|
|
9243
|
-
var CANONICAL_OPUS_MODEL = "claude-opus-4-7";
|
|
9244
|
-
var CANONICAL_SONNET_MODEL = "claude-sonnet-4-6";
|
|
9245
|
-
var CANONICAL_HAIKU_MODEL = "claude-haiku-4-5";
|
|
9246
|
-
function resolveSdkModelDefaults() {
|
|
9247
|
-
return {
|
|
9248
|
-
ANTHROPIC_DEFAULT_OPUS_MODEL: process.env.MERIDIAN_DEFAULT_OPUS_MODEL ?? CANONICAL_OPUS_MODEL,
|
|
9249
|
-
ANTHROPIC_DEFAULT_SONNET_MODEL: process.env.MERIDIAN_DEFAULT_SONNET_MODEL ?? CANONICAL_SONNET_MODEL,
|
|
9250
|
-
ANTHROPIC_DEFAULT_HAIKU_MODEL: process.env.MERIDIAN_DEFAULT_HAIKU_MODEL ?? CANONICAL_HAIKU_MODEL
|
|
9251
|
-
};
|
|
9252
|
-
}
|
|
9253
|
-
var AUTH_STATUS_CACHE_TTL_MS = 60000;
|
|
9254
|
-
var AUTH_STATUS_FAILURE_TTL_MS = 5000;
|
|
9255
|
-
var cachedAuthStatus = null;
|
|
9256
|
-
var lastKnownGoodAuthStatus = null;
|
|
9257
|
-
var cachedAuthStatusAt = 0;
|
|
9258
|
-
var cachedAuthStatusIsFailure = false;
|
|
9259
|
-
var cachedAuthStatusPromise = null;
|
|
9260
|
-
function supports1mContext(model) {
|
|
9261
|
-
if (model.includes("4-5") || model.includes("4.5"))
|
|
9262
|
-
return false;
|
|
9263
|
-
return true;
|
|
9264
|
-
}
|
|
9265
|
-
function mapModelToClaudeModel(model, subscriptionType, agentMode) {
|
|
9266
|
-
if (model.includes("haiku"))
|
|
9267
|
-
return "haiku";
|
|
9268
|
-
const use1m = supports1mContext(model);
|
|
9269
|
-
const isSubagent = agentMode === "subagent";
|
|
9270
|
-
if (model.includes("opus")) {
|
|
9271
|
-
if (use1m && !isSubagent && !isExtendedContextKnownUnavailable())
|
|
9272
|
-
return "opus[1m]";
|
|
9273
|
-
return "opus";
|
|
9274
|
-
}
|
|
9275
|
-
const sonnetOverride = process.env.MERIDIAN_SONNET_MODEL ?? process.env.CLAUDE_PROXY_SONNET_MODEL;
|
|
9276
|
-
if (sonnetOverride === "sonnet[1m]") {
|
|
9277
|
-
if (!use1m || isSubagent || isExtendedContextKnownUnavailable())
|
|
9278
|
-
return "sonnet";
|
|
9279
|
-
return "sonnet[1m]";
|
|
9280
|
-
}
|
|
9281
|
-
return "sonnet";
|
|
9282
|
-
}
|
|
9283
|
-
var EXTRA_USAGE_RETRY_MS = 60 * 60 * 1000;
|
|
9284
|
-
var extraUsageUnavailableAt = 0;
|
|
9285
|
-
function recordExtendedContextUnavailable() {
|
|
9286
|
-
extraUsageUnavailableAt = Date.now();
|
|
9287
|
-
}
|
|
9288
|
-
function isExtendedContextKnownUnavailable() {
|
|
9289
|
-
return extraUsageUnavailableAt > 0 && Date.now() - extraUsageUnavailableAt < EXTRA_USAGE_RETRY_MS;
|
|
9290
|
-
}
|
|
9291
|
-
function stripExtendedContext(model) {
|
|
9292
|
-
if (model === "opus[1m]")
|
|
9293
|
-
return "opus";
|
|
9294
|
-
if (model === "sonnet[1m]")
|
|
9295
|
-
return "sonnet";
|
|
9296
|
-
return model;
|
|
9297
|
-
}
|
|
9298
|
-
function hasExtendedContext(model) {
|
|
9299
|
-
return model.endsWith("[1m]");
|
|
9300
|
-
}
|
|
9301
|
-
var profileAuthCaches = new Map;
|
|
9302
|
-
function getAuthCacheInfo(profileId) {
|
|
9303
|
-
if (!profileId) {
|
|
9304
|
-
return { lastCheckedAt: cachedAuthStatusAt, lastSuccessAt: cachedAuthStatusIsFailure ? 0 : cachedAuthStatusAt, isFailure: cachedAuthStatusIsFailure };
|
|
9305
|
-
}
|
|
9306
|
-
const cache = profileAuthCaches.get(profileId);
|
|
9307
|
-
if (!cache)
|
|
9308
|
-
return { lastCheckedAt: 0, lastSuccessAt: 0, isFailure: false };
|
|
9309
|
-
return { lastCheckedAt: cache.at, lastSuccessAt: cache.lastSuccessAt, isFailure: cache.isFailure };
|
|
9310
|
-
}
|
|
9311
|
-
function getAuthCache(key) {
|
|
9312
|
-
let cache = profileAuthCaches.get(key);
|
|
9313
|
-
if (!cache) {
|
|
9314
|
-
cache = { status: null, lastKnownGood: null, at: 0, isFailure: false, promise: null, lastSuccessAt: 0 };
|
|
9315
|
-
profileAuthCaches.set(key, cache);
|
|
9316
|
-
}
|
|
9317
|
-
return cache;
|
|
9318
|
-
}
|
|
9319
|
-
async function getClaudeAuthStatusAsync(profileId, envOverrides) {
|
|
9320
|
-
const isDefault = !profileId;
|
|
9321
|
-
const cache = isDefault ? null : getAuthCache(profileId);
|
|
9322
|
-
const c_status = cache ? cache.status : cachedAuthStatus;
|
|
9323
|
-
const c_lastKnownGood = cache ? cache.lastKnownGood : lastKnownGoodAuthStatus;
|
|
9324
|
-
const c_at = cache ? cache.at : cachedAuthStatusAt;
|
|
9325
|
-
const c_isFailure = cache ? cache.isFailure : cachedAuthStatusIsFailure;
|
|
9326
|
-
let c_promise = cache ? cache.promise : cachedAuthStatusPromise;
|
|
9327
|
-
const ttl = c_isFailure ? AUTH_STATUS_FAILURE_TTL_MS : AUTH_STATUS_CACHE_TTL_MS;
|
|
9328
|
-
if (c_at > 0 && Date.now() - c_at < ttl) {
|
|
9329
|
-
return c_status ?? c_lastKnownGood;
|
|
9330
|
-
}
|
|
9331
|
-
if (c_promise)
|
|
9332
|
-
return c_promise;
|
|
9333
|
-
c_promise = (async () => {
|
|
9334
|
-
try {
|
|
9335
|
-
const { stdout } = await exec("claude auth status", {
|
|
9336
|
-
timeout: 5000,
|
|
9337
|
-
...envOverrides ? { env: { ...process.env, ...envOverrides } } : {}
|
|
9338
|
-
});
|
|
9339
|
-
const parsed = JSON.parse(stdout);
|
|
9340
|
-
if (cache) {
|
|
9341
|
-
cache.status = parsed;
|
|
9342
|
-
cache.lastKnownGood = parsed;
|
|
9343
|
-
cache.at = Date.now();
|
|
9344
|
-
cache.isFailure = false;
|
|
9345
|
-
cache.lastSuccessAt = Date.now();
|
|
9346
|
-
} else {
|
|
9347
|
-
cachedAuthStatus = parsed;
|
|
9348
|
-
lastKnownGoodAuthStatus = parsed;
|
|
9349
|
-
cachedAuthStatusAt = Date.now();
|
|
9350
|
-
cachedAuthStatusIsFailure = false;
|
|
9351
|
-
}
|
|
9352
|
-
return parsed;
|
|
9353
|
-
} catch {
|
|
9354
|
-
if (cache) {
|
|
9355
|
-
cache.isFailure = true;
|
|
9356
|
-
cache.at = Date.now();
|
|
9357
|
-
cache.status = null;
|
|
9358
|
-
return cache.lastKnownGood;
|
|
9359
|
-
} else {
|
|
9360
|
-
cachedAuthStatusIsFailure = true;
|
|
9361
|
-
cachedAuthStatusAt = Date.now();
|
|
9362
|
-
cachedAuthStatus = null;
|
|
9363
|
-
return lastKnownGoodAuthStatus;
|
|
9364
|
-
}
|
|
9365
|
-
}
|
|
9366
|
-
})();
|
|
9367
|
-
if (cache)
|
|
9368
|
-
cache.promise = c_promise;
|
|
9369
|
-
else
|
|
9370
|
-
cachedAuthStatusPromise = c_promise;
|
|
9371
|
-
try {
|
|
9372
|
-
return await c_promise;
|
|
9373
|
-
} finally {
|
|
9374
|
-
if (cache)
|
|
9375
|
-
cache.promise = null;
|
|
9376
|
-
else
|
|
9377
|
-
cachedAuthStatusPromise = null;
|
|
9378
|
-
}
|
|
9379
|
-
}
|
|
9380
|
-
var cachedClaudePath = null;
|
|
9381
|
-
var cachedClaudePathPromise = null;
|
|
9382
|
-
var DEFAULT_DEPS = {
|
|
9383
|
-
existsSync: existsSync2,
|
|
9384
|
-
statSync: (p) => statSync(p),
|
|
9385
|
-
exec,
|
|
9386
|
-
resolvePackage: (specifier) => fileURLToPath2(import.meta.resolve(specifier)),
|
|
9387
|
-
envGet: (name) => process.env[name],
|
|
9388
|
-
platform: process.platform,
|
|
9389
|
-
arch: process.arch,
|
|
9390
|
-
isBun: typeof process.versions.bun !== "undefined"
|
|
9391
|
-
};
|
|
9392
|
-
function tryEnvOverride(deps) {
|
|
9393
|
-
const explicit = deps.envGet("MERIDIAN_CLAUDE_PATH");
|
|
9394
|
-
if (!explicit)
|
|
9395
|
-
return null;
|
|
9396
|
-
return deps.existsSync(explicit) ? explicit : null;
|
|
9397
|
-
}
|
|
9398
|
-
function tryBundledBinary(deps) {
|
|
9399
|
-
try {
|
|
9400
|
-
const pkgPath = deps.resolvePackage("@anthropic-ai/claude-code/package.json");
|
|
9401
|
-
const bundled = join2(dirname2(pkgPath), "bin", "claude.exe");
|
|
9402
|
-
if (!deps.existsSync(bundled))
|
|
9403
|
-
return null;
|
|
9404
|
-
const size = deps.statSync(bundled).size;
|
|
9405
|
-
if (size <= STUB_SIZE_THRESHOLD)
|
|
9406
|
-
return null;
|
|
9407
|
-
return bundled;
|
|
9408
|
-
} catch {
|
|
9409
|
-
return null;
|
|
9410
|
-
}
|
|
9411
|
-
}
|
|
9412
|
-
function tryPlatformPackage(deps) {
|
|
9413
|
-
const binName = deps.platform === "win32" ? "claude.exe" : "claude";
|
|
9414
|
-
const candidates = [`@anthropic-ai/claude-code-${deps.platform}-${deps.arch}`];
|
|
9415
|
-
if (deps.platform === "linux") {
|
|
9416
|
-
candidates.push(`@anthropic-ai/claude-code-${deps.platform}-${deps.arch}-musl`);
|
|
9417
|
-
}
|
|
9418
|
-
for (const pkg of candidates) {
|
|
9419
|
-
try {
|
|
9420
|
-
const pkgJson = deps.resolvePackage(`${pkg}/package.json`);
|
|
9421
|
-
const candidate = join2(dirname2(pkgJson), binName);
|
|
9422
|
-
if (deps.existsSync(candidate))
|
|
9423
|
-
return candidate;
|
|
9424
|
-
} catch {}
|
|
9425
|
-
}
|
|
9426
|
-
return null;
|
|
9427
|
-
}
|
|
9428
|
-
async function tryPathLookup(deps) {
|
|
9429
|
-
const cmd = deps.platform === "win32" ? "where claude" : "which claude";
|
|
9430
|
-
try {
|
|
9431
|
-
const { stdout } = await deps.exec(cmd);
|
|
9432
|
-
const candidates = stdout.split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
|
|
9433
|
-
for (const candidate of candidates) {
|
|
9434
|
-
if (deps.platform === "win32" && candidate.startsWith("/"))
|
|
9435
|
-
continue;
|
|
9436
|
-
if (deps.existsSync(candidate))
|
|
9437
|
-
return candidate;
|
|
9438
|
-
}
|
|
9439
|
-
} catch {}
|
|
9440
|
-
return null;
|
|
9441
|
-
}
|
|
9442
|
-
function tryLegacySdkCliJs(deps) {
|
|
9443
|
-
if (!deps.isBun)
|
|
9444
|
-
return null;
|
|
9445
|
-
try {
|
|
9446
|
-
const sdkPath = deps.resolvePackage("@anthropic-ai/claude-agent-sdk");
|
|
9447
|
-
const cliJs = join2(dirname2(sdkPath), "cli.js");
|
|
9448
|
-
return deps.existsSync(cliJs) ? cliJs : null;
|
|
9449
|
-
} catch {
|
|
9450
|
-
return null;
|
|
9451
|
-
}
|
|
9452
|
-
}
|
|
9453
|
-
async function resolveClaudeExecutable(deps = DEFAULT_DEPS) {
|
|
9454
|
-
return tryEnvOverride(deps) ?? tryBundledBinary(deps) ?? tryPlatformPackage(deps) ?? await tryPathLookup(deps) ?? tryLegacySdkCliJs(deps);
|
|
9455
|
-
}
|
|
9456
|
-
async function resolveClaudeExecutableAsync() {
|
|
9457
|
-
if (cachedClaudePath)
|
|
9458
|
-
return cachedClaudePath;
|
|
9459
|
-
if (cachedClaudePathPromise)
|
|
9460
|
-
return cachedClaudePathPromise;
|
|
9461
|
-
cachedClaudePathPromise = (async () => {
|
|
9462
|
-
const resolved = await resolveClaudeExecutable();
|
|
9463
|
-
if (resolved) {
|
|
9464
|
-
cachedClaudePath = resolved;
|
|
9465
|
-
return resolved;
|
|
9466
|
-
}
|
|
9467
|
-
throw new Error("Could not find Claude Code executable. Install via: npm install -g @anthropic-ai/claude-code, " + "or set MERIDIAN_CLAUDE_PATH=/path/to/claude to point at an existing binary.");
|
|
9468
|
-
})();
|
|
9469
|
-
try {
|
|
9470
|
-
return await cachedClaudePathPromise;
|
|
9471
|
-
} finally {
|
|
9472
|
-
cachedClaudePathPromise = null;
|
|
9473
|
-
}
|
|
9474
|
-
}
|
|
9475
|
-
function isClosedControllerError(error) {
|
|
9476
|
-
if (!(error instanceof Error))
|
|
9477
|
-
return false;
|
|
9478
|
-
return error.message.includes("Controller is already closed");
|
|
9479
|
-
}
|
|
9480
|
-
|
|
9481
9275
|
// src/proxy/openai.ts
|
|
9482
9276
|
function extractOpenAiContent(content) {
|
|
9483
9277
|
if (typeof content === "string")
|
|
@@ -10995,8 +10789,8 @@ function detectAdapter(c) {
|
|
|
10995
10789
|
import { createSdkMcpServer as createSdkMcpServer2, tool } from "@anthropic-ai/claude-agent-sdk";
|
|
10996
10790
|
import * as fs from "node:fs/promises";
|
|
10997
10791
|
import * as path2 from "node:path";
|
|
10998
|
-
import { exec
|
|
10999
|
-
import { promisify
|
|
10792
|
+
import { exec } from "node:child_process";
|
|
10793
|
+
import { promisify } from "node:util";
|
|
11000
10794
|
|
|
11001
10795
|
// node_modules/@isaacs/balanced-match/dist/esm/index.js
|
|
11002
10796
|
var balanced = (a, b, str) => {
|
|
@@ -12433,7 +12227,7 @@ minimatch.escape = escape;
|
|
|
12433
12227
|
minimatch.unescape = unescape;
|
|
12434
12228
|
|
|
12435
12229
|
// node_modules/glob/dist/esm/glob.js
|
|
12436
|
-
import { fileURLToPath as
|
|
12230
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
12437
12231
|
|
|
12438
12232
|
// node_modules/lru-cache/dist/esm/index.js
|
|
12439
12233
|
var defaultPerf = typeof performance === "object" && performance && typeof performance.now === "function" ? performance : Date;
|
|
@@ -13596,7 +13390,7 @@ class LRUCache {
|
|
|
13596
13390
|
|
|
13597
13391
|
// node_modules/path-scurry/dist/esm/index.js
|
|
13598
13392
|
import { posix, win32 } from "node:path";
|
|
13599
|
-
import { fileURLToPath as
|
|
13393
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
13600
13394
|
import { lstatSync, readdir as readdirCB, readdirSync, readlinkSync, realpathSync as rps } from "fs";
|
|
13601
13395
|
import * as actualFS from "node:fs";
|
|
13602
13396
|
import { lstat, readdir, readlink, realpath } from "node:fs/promises";
|
|
@@ -15051,7 +14845,7 @@ class PathScurryBase {
|
|
|
15051
14845
|
constructor(cwd = process.cwd(), pathImpl, sep2, { nocase, childrenCacheSize = 16 * 1024, fs = defaultFS } = {}) {
|
|
15052
14846
|
this.#fs = fsFromOption(fs);
|
|
15053
14847
|
if (cwd instanceof URL || cwd.startsWith("file://")) {
|
|
15054
|
-
cwd =
|
|
14848
|
+
cwd = fileURLToPath2(cwd);
|
|
15055
14849
|
}
|
|
15056
14850
|
const cwdPath = pathImpl.resolve(cwd);
|
|
15057
14851
|
this.roots = Object.create(null);
|
|
@@ -16355,7 +16149,7 @@ class Glob {
|
|
|
16355
16149
|
if (!opts.cwd) {
|
|
16356
16150
|
this.cwd = "";
|
|
16357
16151
|
} else if (opts.cwd instanceof URL || opts.cwd.startsWith("file://")) {
|
|
16358
|
-
opts.cwd =
|
|
16152
|
+
opts.cwd = fileURLToPath3(opts.cwd);
|
|
16359
16153
|
}
|
|
16360
16154
|
this.cwd = opts.cwd || "";
|
|
16361
16155
|
this.root = opts.root;
|
|
@@ -16546,7 +16340,7 @@ var glob = Object.assign(glob_, {
|
|
|
16546
16340
|
glob.glob = glob;
|
|
16547
16341
|
|
|
16548
16342
|
// src/mcpTools.ts
|
|
16549
|
-
var execAsync =
|
|
16343
|
+
var execAsync = promisify(exec);
|
|
16550
16344
|
var getCwd = () => process.env.MERIDIAN_WORKDIR ?? process.env.CLAUDE_PROXY_WORKDIR ?? process.cwd();
|
|
16551
16345
|
function createOpencodeMcpServer() {
|
|
16552
16346
|
return createSdkMcpServer2({
|
|
@@ -16686,6 +16480,8 @@ function createOpencodeMcpServer() {
|
|
|
16686
16480
|
function stripConfigDir(env2) {
|
|
16687
16481
|
if (!("CLAUDE_CONFIG_DIR" in env2))
|
|
16688
16482
|
return env2;
|
|
16483
|
+
if (env2.CLAUDE_CODE_OAUTH_TOKEN)
|
|
16484
|
+
return env2;
|
|
16689
16485
|
const out = { ...env2 };
|
|
16690
16486
|
delete out.CLAUDE_CONFIG_DIR;
|
|
16691
16487
|
return out;
|
|
@@ -16719,6 +16515,8 @@ function resolveSystemPrompt(systemContext, passthrough, settingSources, codeSys
|
|
|
16719
16515
|
}
|
|
16720
16516
|
if (append)
|
|
16721
16517
|
return { systemPrompt: append };
|
|
16518
|
+
if (codeSystemPrompt === false)
|
|
16519
|
+
return { systemPrompt: "" };
|
|
16722
16520
|
return {};
|
|
16723
16521
|
}
|
|
16724
16522
|
function buildQueryOptions(ctx) {
|
|
@@ -16774,6 +16572,8 @@ function buildQueryOptions(ctx) {
|
|
|
16774
16572
|
allowDangerouslySkipPermissions: true,
|
|
16775
16573
|
...resolveSystemPrompt(systemContext, passthrough, settingSources, codeSystemPrompt, clientSystemPrompt, cwdNote),
|
|
16776
16574
|
...passthrough ? {
|
|
16575
|
+
tools: [],
|
|
16576
|
+
settingSources: [],
|
|
16777
16577
|
disallowedTools: [...allBlockedTools],
|
|
16778
16578
|
...passthroughMcp ? {
|
|
16779
16579
|
allowedTools: [...passthroughMcp.toolNames],
|
|
@@ -16831,7 +16631,8 @@ function getAdapterTransforms(adapterName) {
|
|
|
16831
16631
|
|
|
16832
16632
|
// src/proxy/plugins/loader.ts
|
|
16833
16633
|
import { readdirSync as readdirSync2, readFileSync as readFileSync2, existsSync as existsSync3 } from "fs";
|
|
16834
|
-
import { join as
|
|
16634
|
+
import { join as join2, isAbsolute as isAbsolute2, extname } from "path";
|
|
16635
|
+
import { pathToFileURL } from "url";
|
|
16835
16636
|
|
|
16836
16637
|
// src/proxy/plugins/validation.ts
|
|
16837
16638
|
var KNOWN_ADAPTERS = ["opencode", "crush", "droid", "pi", "forgecode", "passthrough"];
|
|
@@ -16915,7 +16716,7 @@ async function loadPlugins(pluginDir, configPath) {
|
|
|
16915
16716
|
const loaded = [];
|
|
16916
16717
|
const seenNames = new Set;
|
|
16917
16718
|
for (const { filename, entry } of ordered) {
|
|
16918
|
-
const filePath = isAbsolute2(filename) ? filename :
|
|
16719
|
+
const filePath = isAbsolute2(filename) ? filename : join2(pluginDir, filename);
|
|
16919
16720
|
if (entry && !entry.enabled) {
|
|
16920
16721
|
loaded.push({
|
|
16921
16722
|
name: filename,
|
|
@@ -16928,7 +16729,8 @@ async function loadPlugins(pluginDir, configPath) {
|
|
|
16928
16729
|
}
|
|
16929
16730
|
try {
|
|
16930
16731
|
const cacheBuster = `?t=${Date.now()}-${++loadCounter}`;
|
|
16931
|
-
const
|
|
16732
|
+
const specifier = process.platform === "win32" ? pathToFileURL(filePath).href + cacheBuster : filePath + cacheBuster;
|
|
16733
|
+
const mod = await import(specifier);
|
|
16932
16734
|
const exported = mod.default ?? mod;
|
|
16933
16735
|
const transforms = Array.isArray(exported) ? exported : [exported];
|
|
16934
16736
|
for (const item of transforms) {
|
|
@@ -17291,12 +17093,12 @@ import {
|
|
|
17291
17093
|
openSync,
|
|
17292
17094
|
readFileSync as readFileSync3,
|
|
17293
17095
|
renameSync,
|
|
17294
|
-
statSync
|
|
17096
|
+
statSync,
|
|
17295
17097
|
unlinkSync,
|
|
17296
17098
|
writeFileSync
|
|
17297
17099
|
} from "node:fs";
|
|
17298
17100
|
import { homedir as homedir2 } from "node:os";
|
|
17299
|
-
import { join as
|
|
17101
|
+
import { join as join3 } from "node:path";
|
|
17300
17102
|
var DEFAULT_MAX_STORED_SESSIONS = 1e4;
|
|
17301
17103
|
var STALE_LOCK_THRESHOLD_MS = 30000;
|
|
17302
17104
|
function getMaxStoredSessions() {
|
|
@@ -17320,7 +17122,7 @@ function acquireLock(lockPath) {
|
|
|
17320
17122
|
return false;
|
|
17321
17123
|
}
|
|
17322
17124
|
try {
|
|
17323
|
-
const stat =
|
|
17125
|
+
const stat = statSync(lockPath);
|
|
17324
17126
|
if (Date.now() - stat.mtimeMs > STALE_LOCK_THRESHOLD_MS) {
|
|
17325
17127
|
unlinkSync(lockPath);
|
|
17326
17128
|
const fd = openSync(lockPath, "wx");
|
|
@@ -17347,11 +17149,11 @@ function getStorePath() {
|
|
|
17347
17149
|
if (!existsSync4(dir)) {
|
|
17348
17150
|
mkdirSync(dir, { recursive: true });
|
|
17349
17151
|
}
|
|
17350
|
-
return
|
|
17152
|
+
return join3(dir, "sessions.json");
|
|
17351
17153
|
}
|
|
17352
17154
|
function getDefaultCacheDir() {
|
|
17353
|
-
const newDir =
|
|
17354
|
-
const oldDir =
|
|
17155
|
+
const newDir = join3(homedir2(), ".cache", "meridian");
|
|
17156
|
+
const oldDir = join3(homedir2(), ".cache", "opencode-claude-max-proxy");
|
|
17355
17157
|
if (existsSync4(newDir))
|
|
17356
17158
|
return newDir;
|
|
17357
17159
|
if (existsSync4(oldDir)) {
|
|
@@ -17691,7 +17493,7 @@ function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sd
|
|
|
17691
17493
|
}
|
|
17692
17494
|
|
|
17693
17495
|
// src/proxy/server.ts
|
|
17694
|
-
var
|
|
17496
|
+
var exec2 = promisify2(execCallback);
|
|
17695
17497
|
var claudeExecutable = "";
|
|
17696
17498
|
var MULTIMODAL_TYPES = new Set(["image", "document", "file"]);
|
|
17697
17499
|
function hasMultimodalContent(content) {
|
|
@@ -17869,8 +17671,8 @@ function createProxyServer(config = {}) {
|
|
|
17869
17671
|
const sessionDiscoveredTools = new Map;
|
|
17870
17672
|
const sessionToolCache = new Map;
|
|
17871
17673
|
const sessionMcpCache = new LRUMap(getMaxSessionsLimit());
|
|
17872
|
-
const pluginDir = finalConfig.pluginDir ??
|
|
17873
|
-
const pluginConfigPath = finalConfig.pluginConfigPath ??
|
|
17674
|
+
const pluginDir = finalConfig.pluginDir ?? join5(homedir4(), ".config", "meridian", "plugins");
|
|
17675
|
+
const pluginConfigPath = finalConfig.pluginConfigPath ?? join5(homedir4(), ".config", "meridian", "plugins.json");
|
|
17874
17676
|
let loadedPlugins = [];
|
|
17875
17677
|
let pluginTransforms = [];
|
|
17876
17678
|
const app = new Hono2;
|
|
@@ -17884,6 +17686,8 @@ function createProxyServer(config = {}) {
|
|
|
17884
17686
|
app.use("/profiles", requireAuth);
|
|
17885
17687
|
app.use("/plugins/*", requireAuth);
|
|
17886
17688
|
app.use("/plugins", requireAuth);
|
|
17689
|
+
app.use("/settings/*", requireAuth);
|
|
17690
|
+
app.use("/settings", requireAuth);
|
|
17887
17691
|
app.use("/auth/*", requireAuth);
|
|
17888
17692
|
app.get("/", (c) => {
|
|
17889
17693
|
const accept = c.req.header("accept") || "";
|
|
@@ -17944,8 +17748,19 @@ function createProxyServer(config = {}) {
|
|
|
17944
17748
|
const agentMode = c.req.header("x-opencode-agent-mode") ?? null;
|
|
17945
17749
|
const requestSource = c.req.header("x-meridian-source")?.slice(0, 64) || undefined;
|
|
17946
17750
|
let model = mapModelToClaudeModel(body.model || "sonnet", authStatus?.subscriptionType, agentMode);
|
|
17947
|
-
const
|
|
17948
|
-
|
|
17751
|
+
const cwdResolution = resolveSdkWorkingDirectory({
|
|
17752
|
+
envOverride: process.env.MERIDIAN_WORKDIR ?? process.env.CLAUDE_PROXY_WORKDIR,
|
|
17753
|
+
adapterCwd: adapter.extractWorkingDirectory(body),
|
|
17754
|
+
fallback: process.cwd()
|
|
17755
|
+
});
|
|
17756
|
+
const workingDirectory = cwdResolution.workingDirectory;
|
|
17757
|
+
if (cwdResolution.fellBack) {
|
|
17758
|
+
claudeLog("cwd_fallback", {
|
|
17759
|
+
claimed: cwdResolution.claimedWorkingDirectory,
|
|
17760
|
+
usedInstead: workingDirectory
|
|
17761
|
+
});
|
|
17762
|
+
}
|
|
17763
|
+
const clientWorkingDirectory = adapter.extractClientWorkingDirectory?.(body) || cwdResolution.claimedWorkingDirectory;
|
|
17949
17764
|
const {
|
|
17950
17765
|
ANTHROPIC_API_KEY: _dropApiKey,
|
|
17951
17766
|
ANTHROPIC_BASE_URL: _dropBaseUrl,
|
|
@@ -18224,6 +18039,7 @@ function createProxyServer(config = {}) {
|
|
|
18224
18039
|
const RATE_LIMIT_BASE_DELAY_MS = 1000;
|
|
18225
18040
|
const response = async function* () {
|
|
18226
18041
|
let rateLimitRetries = 0;
|
|
18042
|
+
await ensureFreshToken().catch(() => {});
|
|
18227
18043
|
let tokenRefreshed = false;
|
|
18228
18044
|
while (true) {
|
|
18229
18045
|
let didYieldContent = false;
|
|
@@ -18625,6 +18441,7 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
18625
18441
|
const RATE_LIMIT_BASE_DELAY_MS = 1000;
|
|
18626
18442
|
const response = async function* () {
|
|
18627
18443
|
let rateLimitRetries = 0;
|
|
18444
|
+
await ensureFreshToken().catch(() => {});
|
|
18628
18445
|
let tokenRefreshed = false;
|
|
18629
18446
|
while (true) {
|
|
18630
18447
|
let didYieldClientEvent = false;
|
|
@@ -19442,6 +19259,7 @@ data: ${JSON.stringify({
|
|
|
19442
19259
|
auth: { loggedIn: false }
|
|
19443
19260
|
}, 503);
|
|
19444
19261
|
}
|
|
19262
|
+
const claudeExecutableInfo = getResolvedClaudeExecutableInfo();
|
|
19445
19263
|
return c.json({
|
|
19446
19264
|
status: "healthy",
|
|
19447
19265
|
version: serverVersion,
|
|
@@ -19451,6 +19269,7 @@ data: ${JSON.stringify({
|
|
|
19451
19269
|
subscriptionType: auth.subscriptionType
|
|
19452
19270
|
},
|
|
19453
19271
|
mode: envBool("PASSTHROUGH") ? "passthrough" : "internal",
|
|
19272
|
+
...claudeExecutableInfo ? { claudeExecutable: claudeExecutableInfo } : {},
|
|
19454
19273
|
plugin: { opencode: checkPluginConfigured() ? "configured" : "not-configured" }
|
|
19455
19274
|
});
|
|
19456
19275
|
} catch {
|
|
@@ -19563,9 +19382,16 @@ data: ${JSON.stringify({
|
|
|
19563
19382
|
if (!anthropicBody) {
|
|
19564
19383
|
return c.json({ type: "error", error: { type: "invalid_request_error", message: "messages: Field required" } }, 400);
|
|
19565
19384
|
}
|
|
19385
|
+
const internalHeaders = { "Content-Type": "application/json" };
|
|
19386
|
+
const xApiKey = c.req.header("x-api-key");
|
|
19387
|
+
if (xApiKey)
|
|
19388
|
+
internalHeaders["x-api-key"] = xApiKey;
|
|
19389
|
+
const authz = c.req.header("authorization");
|
|
19390
|
+
if (authz)
|
|
19391
|
+
internalHeaders["authorization"] = authz;
|
|
19566
19392
|
const internalReq = new Request("http://internal/v1/messages", {
|
|
19567
19393
|
method: "POST",
|
|
19568
|
-
headers:
|
|
19394
|
+
headers: internalHeaders,
|
|
19569
19395
|
body: JSON.stringify(anthropicBody)
|
|
19570
19396
|
});
|
|
19571
19397
|
const internalRes = await app.fetch(internalReq);
|
|
@@ -19844,6 +19670,10 @@ async function startProxyServer(config = {}) {
|
|
|
19844
19670
|
console.log(`Telemetry dashboard: http://${finalConfig.host}:${info.port}/telemetry`);
|
|
19845
19671
|
const pins = resolveSdkModelDefaults();
|
|
19846
19672
|
console.log(`Model pins: opus=${pins.ANTHROPIC_DEFAULT_OPUS_MODEL} sonnet=${pins.ANTHROPIC_DEFAULT_SONNET_MODEL} haiku=${pins.ANTHROPIC_DEFAULT_HAIKU_MODEL}`);
|
|
19673
|
+
const claudeInfo = getResolvedClaudeExecutableInfo();
|
|
19674
|
+
if (claudeInfo) {
|
|
19675
|
+
console.log(`Claude executable: ${claudeInfo.path} (resolved via ${claudeInfo.source})`);
|
|
19676
|
+
}
|
|
19847
19677
|
console.log(`
|
|
19848
19678
|
Point any Anthropic-compatible tool at this endpoint:`);
|
|
19849
19679
|
console.log(` ANTHROPIC_API_KEY=x ANTHROPIC_BASE_URL=http://${finalConfig.host}:${info.port}`);
|
|
@@ -19865,6 +19695,7 @@ Or use a different port:`);
|
|
|
19865
19695
|
console.error(` MERIDIAN_PORT=4567 meridian`);
|
|
19866
19696
|
}
|
|
19867
19697
|
});
|
|
19698
|
+
startBackgroundRefresh();
|
|
19868
19699
|
let authKeepaliveInterval;
|
|
19869
19700
|
const effectiveProfiles = getEffectiveProfiles(finalConfig.profiles);
|
|
19870
19701
|
if (effectiveProfiles.length > 0) {
|
|
@@ -19888,6 +19719,7 @@ Or use a different port:`);
|
|
|
19888
19719
|
async close() {
|
|
19889
19720
|
if (authKeepaliveInterval)
|
|
19890
19721
|
clearInterval(authKeepaliveInterval);
|
|
19722
|
+
stopBackgroundRefresh();
|
|
19891
19723
|
await new Promise((resolve3, reject) => {
|
|
19892
19724
|
server.close((err) => err ? reject(err) : resolve3());
|
|
19893
19725
|
});
|