@rynfar/meridian 1.41.0 → 1.42.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.
@@ -5,7 +5,7 @@ import {
5
5
  resolveProfile,
6
6
  restoreActiveProfile,
7
7
  setActiveProfile
8
- } from "./cli-vdp9s10c.js";
8
+ } from "./cli-cx463q74.js";
9
9
  import {
10
10
  isTrackedPlugin,
11
11
  recordError,
@@ -26,9 +26,12 @@ import {
26
26
  import {
27
27
  claudeLog,
28
28
  createPlatformCredentialStore,
29
+ ensureFreshToken,
29
30
  refreshOAuthToken,
31
+ startBackgroundRefresh,
32
+ stopBackgroundRefresh,
30
33
  withClaudeLogContext
31
- } from "./cli-0eky480v.js";
34
+ } from "./cli-7k1fcprd.js";
32
35
  import {
33
36
  __commonJS,
34
37
  __esm,
@@ -1178,14 +1181,14 @@ __export(exports_sdkFeatures, {
1178
1181
  getFeaturesForAdapter: () => getFeaturesForAdapter,
1179
1182
  getAllFeatureConfigs: () => getAllFeatureConfigs
1180
1183
  });
1181
- import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2, renameSync as renameSync2 } from "node:fs";
1182
- import { join as join6 } from "node:path";
1183
- import { homedir as homedir4 } from "node:os";
1184
+ import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2, renameSync as renameSync2 } from "node:fs";
1185
+ import { join as join5 } from "node:path";
1186
+ import { homedir as homedir3 } from "node:os";
1184
1187
  function getConfigPath() {
1185
- const dir = join6(homedir4(), ".config", "meridian");
1186
- if (!existsSync5(dir))
1188
+ const dir = join5(homedir3(), ".config", "meridian");
1189
+ if (!existsSync6(dir))
1187
1190
  mkdirSync2(dir, { recursive: true });
1188
- return join6(dir, "sdk-features.json");
1191
+ return join5(dir, "sdk-features.json");
1189
1192
  }
1190
1193
  function readConfig() {
1191
1194
  const now = Date.now();
@@ -1193,7 +1196,7 @@ function readConfig() {
1193
1196
  return cachedConfig;
1194
1197
  const path3 = getConfigPath();
1195
1198
  try {
1196
- if (existsSync5(path3)) {
1199
+ if (existsSync6(path3)) {
1197
1200
  cachedConfig = JSON.parse(readFileSync4(path3, "utf-8"));
1198
1201
  } else {
1199
1202
  cachedConfig = {};
@@ -1283,7 +1286,7 @@ function resetAdapterFeatures(adapterName) {
1283
1286
  var DEFAULT_FEATURES, ADAPTER_DEFAULTS, cachedConfig = null, lastReadTime = 0, CACHE_TTL_MS = 5000, VALID_CLAUDE_MD_VALUES, VALID_THINKING_VALUES;
1284
1287
  var init_sdkFeatures = __esm(() => {
1285
1288
  DEFAULT_FEATURES = {
1286
- codeSystemPrompt: false,
1289
+ codeSystemPrompt: true,
1287
1290
  clientSystemPrompt: true,
1288
1291
  claudeMd: "off",
1289
1292
  memory: false,
@@ -1296,7 +1299,11 @@ var init_sdkFeatures = __esm(() => {
1296
1299
  sdkDebug: false,
1297
1300
  additionalDirectories: ""
1298
1301
  };
1299
- ADAPTER_DEFAULTS = {};
1302
+ ADAPTER_DEFAULTS = {
1303
+ passthrough: {
1304
+ codeSystemPrompt: false
1305
+ }
1306
+ };
1300
1307
  VALID_CLAUDE_MD_VALUES = new Set(["off", "project", "full"]);
1301
1308
  VALID_THINKING_VALUES = new Set(["adaptive", "enabled", "disabled"]);
1302
1309
  });
@@ -3703,8 +3710,8 @@ var serve = (options, listeningListener) => {
3703
3710
  };
3704
3711
 
3705
3712
  // src/proxy/server.ts
3706
- import { homedir as homedir5 } from "node:os";
3707
- import { join as join7 } from "node:path";
3713
+ import { homedir as homedir4 } from "node:os";
3714
+ import { join as join6 } from "node:path";
3708
3715
  import { query } from "@anthropic-ai/claude-agent-sdk";
3709
3716
 
3710
3717
  // src/proxy/rateLimitStore.ts
@@ -3784,8 +3791,8 @@ async function readAccessToken(store) {
3784
3791
  const creds = await store.read();
3785
3792
  return creds?.claudeAiOauth?.accessToken ?? null;
3786
3793
  }
3787
- async function callAnthropic(token, signal) {
3788
- const res = await fetch(OAUTH_USAGE_URL, {
3794
+ async function callAnthropic(token, fetchImpl, signal) {
3795
+ const res = await fetchImpl(OAUTH_USAGE_URL, {
3789
3796
  headers: {
3790
3797
  Authorization: `Bearer ${token}`,
3791
3798
  "anthropic-beta": OAUTH_BETA_HEADER,
@@ -3797,9 +3804,17 @@ async function callAnthropic(token, signal) {
3797
3804
  return { __status: res.status };
3798
3805
  return await res.json();
3799
3806
  }
3807
+ var _testOverride = null;
3800
3808
  async function fetchOAuthUsage(opts) {
3809
+ if (_testOverride && !opts?.fetchImpl && !opts?.store) {
3810
+ return _testOverride(opts);
3811
+ }
3812
+ return fetchOAuthUsageImpl(opts);
3813
+ }
3814
+ async function fetchOAuthUsageImpl(opts) {
3801
3815
  const ttl = opts?.ttlMs ?? CACHE_TTL_MS_DEFAULT;
3802
3816
  const cacheKey2 = opts?.profileId ?? DEFAULT_KEY;
3817
+ const fetchImpl = opts?.fetchImpl ?? globalThis.fetch;
3803
3818
  if (!opts?.force) {
3804
3819
  const cached = cacheByProfile.get(cacheKey2);
3805
3820
  if (cached && Date.now() - cached.fetchedAt < ttl)
@@ -3814,7 +3829,7 @@ async function fetchOAuthUsage(opts) {
3814
3829
  const token = await readAccessToken(store);
3815
3830
  if (!token)
3816
3831
  return null;
3817
- let result = await callAnthropic(token);
3832
+ let result = await callAnthropic(token, fetchImpl);
3818
3833
  if ("__status" in result && result.__status === 401) {
3819
3834
  claudeLog("oauth_usage.token_refresh_attempt", { profile: cacheKey2 });
3820
3835
  const refreshed = await refreshOAuthToken(store);
@@ -3825,7 +3840,7 @@ async function fetchOAuthUsage(opts) {
3825
3840
  const newToken = await readAccessToken(store);
3826
3841
  if (!newToken)
3827
3842
  return null;
3828
- result = await callAnthropic(newToken);
3843
+ result = await callAnthropic(newToken, fetchImpl);
3829
3844
  }
3830
3845
  if ("__status" in result) {
3831
3846
  claudeLog("oauth_usage.upstream_error", { profile: cacheKey2, status: result.__status });
@@ -3845,6 +3860,17 @@ async function fetchOAuthUsage(opts) {
3845
3860
  return promise;
3846
3861
  }
3847
3862
 
3863
+ // src/proxy/cwd.ts
3864
+ import { existsSync } from "node:fs";
3865
+ function resolveSdkWorkingDirectory(opts) {
3866
+ const exists = opts.exists ?? existsSync;
3867
+ const claimed = opts.envOverride || opts.adapterCwd || opts.fallback;
3868
+ if (exists(claimed)) {
3869
+ return { workingDirectory: claimed, claimedWorkingDirectory: claimed, fellBack: false };
3870
+ }
3871
+ return { workingDirectory: opts.fallback, claimedWorkingDirectory: claimed, fellBack: true };
3872
+ }
3873
+
3848
3874
  // src/proxy/types.ts
3849
3875
  var DEFAULT_PROXY_CONFIG = {
3850
3876
  port: 3456,
@@ -8443,7 +8469,7 @@ class MemoryDiagnosticLogStore {
8443
8469
  }
8444
8470
  var diagnosticLog = new MemoryDiagnosticLogStore;
8445
8471
  // src/telemetry/routes.ts
8446
- import { existsSync, readFileSync } from "node:fs";
8472
+ import { existsSync as existsSync2, readFileSync } from "node:fs";
8447
8473
  import { resolve, dirname } from "node:path";
8448
8474
  import { fileURLToPath } from "node:url";
8449
8475
 
@@ -8797,7 +8823,7 @@ timer = setInterval(refresh, 5000);
8797
8823
 
8798
8824
  // src/telemetry/routes.ts
8799
8825
  var _iconPath = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "assets", "icon.svg");
8800
- var _iconSvg = existsSync(_iconPath) ? readFileSync(_iconPath, "utf-8") : null;
8826
+ var _iconSvg = existsSync2(_iconPath) ? readFileSync(_iconPath, "utf-8") : null;
8801
8827
  function createTelemetryRoutes() {
8802
8828
  const routes = new Hono2;
8803
8829
  routes.get("/", (c) => {
@@ -9135,7 +9161,13 @@ function classifyError(errMsg) {
9135
9161
  }
9136
9162
  function isExpiredTokenError(errMsg) {
9137
9163
  const lower = errMsg.toLowerCase();
9138
- return lower.includes("oauth token has expired") || lower.includes("not logged in");
9164
+ if (lower.includes("oauth token has expired") || lower.includes("not logged in"))
9165
+ return true;
9166
+ if (lower.includes("invalid_token") || lower.includes("token_expired"))
9167
+ return true;
9168
+ if (lower.includes("401") && (lower.includes("authentication") || lower.includes("unauthorized") || lower.includes("invalid")))
9169
+ return true;
9170
+ return false;
9139
9171
  }
9140
9172
  function isStaleSessionError(error) {
9141
9173
  if (!(error instanceof Error))
@@ -9230,19 +9262,20 @@ function formatSdkTermination(t, ctx) {
9230
9262
 
9231
9263
  // src/proxy/models.ts
9232
9264
  import { exec as execCallback } from "child_process";
9233
- import { existsSync as existsSync2 } from "fs";
9265
+ import { existsSync as existsSync3, statSync } from "fs";
9234
9266
  import { fileURLToPath as fileURLToPath2 } from "url";
9235
9267
  import { join as join2, dirname as dirname2 } from "path";
9236
9268
  import { promisify } from "util";
9237
9269
  var exec = promisify(execCallback);
9270
+ var STUB_SIZE_THRESHOLD = 4096;
9238
9271
  var CANONICAL_OPUS_MODEL = "claude-opus-4-7";
9239
9272
  var CANONICAL_SONNET_MODEL = "claude-sonnet-4-6";
9240
9273
  var CANONICAL_HAIKU_MODEL = "claude-haiku-4-5";
9241
- function resolveSdkModelDefaults() {
9274
+ function resolveSdkModelDefaults(env2 = process.env) {
9242
9275
  return {
9243
- ANTHROPIC_DEFAULT_OPUS_MODEL: process.env.MERIDIAN_DEFAULT_OPUS_MODEL ?? CANONICAL_OPUS_MODEL,
9244
- ANTHROPIC_DEFAULT_SONNET_MODEL: process.env.MERIDIAN_DEFAULT_SONNET_MODEL ?? CANONICAL_SONNET_MODEL,
9245
- ANTHROPIC_DEFAULT_HAIKU_MODEL: process.env.MERIDIAN_DEFAULT_HAIKU_MODEL ?? CANONICAL_HAIKU_MODEL
9276
+ ANTHROPIC_DEFAULT_OPUS_MODEL: env2.MERIDIAN_DEFAULT_OPUS_MODEL ?? CANONICAL_OPUS_MODEL,
9277
+ ANTHROPIC_DEFAULT_SONNET_MODEL: env2.MERIDIAN_DEFAULT_SONNET_MODEL ?? CANONICAL_SONNET_MODEL,
9278
+ ANTHROPIC_DEFAULT_HAIKU_MODEL: env2.MERIDIAN_DEFAULT_HAIKU_MODEL ?? CANONICAL_HAIKU_MODEL
9246
9279
  };
9247
9280
  }
9248
9281
  var AUTH_STATUS_CACHE_TTL_MS = 60000;
@@ -9372,42 +9405,112 @@ async function getClaudeAuthStatusAsync(profileId, envOverrides) {
9372
9405
  cachedAuthStatusPromise = null;
9373
9406
  }
9374
9407
  }
9375
- var cachedClaudePath = null;
9408
+ var cachedClaudeInfo = null;
9376
9409
  var cachedClaudePathPromise = null;
9410
+ var DEFAULT_DEPS = {
9411
+ existsSync: existsSync3,
9412
+ statSync: (p) => statSync(p),
9413
+ exec,
9414
+ resolvePackage: (specifier) => fileURLToPath2(import.meta.resolve(specifier)),
9415
+ envGet: (name) => process.env[name],
9416
+ platform: process.platform,
9417
+ arch: process.arch,
9418
+ isBun: typeof process.versions.bun !== "undefined"
9419
+ };
9420
+ function tryEnvOverride(deps) {
9421
+ const explicit = deps.envGet("MERIDIAN_CLAUDE_PATH");
9422
+ if (!explicit)
9423
+ return null;
9424
+ return deps.existsSync(explicit) ? explicit : null;
9425
+ }
9426
+ function tryBundledBinary(deps) {
9427
+ try {
9428
+ const pkgPath = deps.resolvePackage("@anthropic-ai/claude-code/package.json");
9429
+ const bundled = join2(dirname2(pkgPath), "bin", "claude.exe");
9430
+ if (!deps.existsSync(bundled))
9431
+ return null;
9432
+ const size = deps.statSync(bundled).size;
9433
+ if (size <= STUB_SIZE_THRESHOLD)
9434
+ return null;
9435
+ return bundled;
9436
+ } catch {
9437
+ return null;
9438
+ }
9439
+ }
9440
+ function tryPlatformPackage(deps) {
9441
+ const binName = deps.platform === "win32" ? "claude.exe" : "claude";
9442
+ const candidates = [`@anthropic-ai/claude-code-${deps.platform}-${deps.arch}`];
9443
+ if (deps.platform === "linux") {
9444
+ candidates.push(`@anthropic-ai/claude-code-${deps.platform}-${deps.arch}-musl`);
9445
+ }
9446
+ for (const pkg of candidates) {
9447
+ try {
9448
+ const pkgJson = deps.resolvePackage(`${pkg}/package.json`);
9449
+ const candidate = join2(dirname2(pkgJson), binName);
9450
+ if (deps.existsSync(candidate))
9451
+ return candidate;
9452
+ } catch {}
9453
+ }
9454
+ return null;
9455
+ }
9456
+ async function tryPathLookup(deps) {
9457
+ const cmd = deps.platform === "win32" ? "where claude" : "which claude";
9458
+ try {
9459
+ const { stdout } = await deps.exec(cmd);
9460
+ const candidates = stdout.split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
9461
+ for (const candidate of candidates) {
9462
+ if (deps.platform === "win32" && candidate.startsWith("/"))
9463
+ continue;
9464
+ if (deps.existsSync(candidate))
9465
+ return candidate;
9466
+ }
9467
+ } catch {}
9468
+ return null;
9469
+ }
9470
+ function tryLegacySdkCliJs(deps) {
9471
+ if (!deps.isBun)
9472
+ return null;
9473
+ try {
9474
+ const sdkPath = deps.resolvePackage("@anthropic-ai/claude-agent-sdk");
9475
+ const cliJs = join2(dirname2(sdkPath), "cli.js");
9476
+ return deps.existsSync(cliJs) ? cliJs : null;
9477
+ } catch {
9478
+ return null;
9479
+ }
9480
+ }
9481
+ async function resolveClaudeExecutableWithSource(deps = DEFAULT_DEPS) {
9482
+ const env2 = tryEnvOverride(deps);
9483
+ if (env2)
9484
+ return { path: env2, source: "env" };
9485
+ const bundled = tryBundledBinary(deps);
9486
+ if (bundled)
9487
+ return { path: bundled, source: "bundled" };
9488
+ const platformPkg = tryPlatformPackage(deps);
9489
+ if (platformPkg)
9490
+ return { path: platformPkg, source: "platform-package" };
9491
+ const pathLookup = await tryPathLookup(deps);
9492
+ if (pathLookup)
9493
+ return { path: pathLookup, source: "path-lookup" };
9494
+ const legacy = tryLegacySdkCliJs(deps);
9495
+ if (legacy)
9496
+ return { path: legacy, source: "legacy-cli-js" };
9497
+ return null;
9498
+ }
9499
+ function getResolvedClaudeExecutableInfo() {
9500
+ return cachedClaudeInfo;
9501
+ }
9377
9502
  async function resolveClaudeExecutableAsync() {
9378
- if (cachedClaudePath)
9379
- return cachedClaudePath;
9503
+ if (cachedClaudeInfo)
9504
+ return cachedClaudeInfo.path;
9380
9505
  if (cachedClaudePathPromise)
9381
9506
  return cachedClaudePathPromise;
9382
9507
  cachedClaudePathPromise = (async () => {
9383
- const runningUnderBun = typeof process.versions.bun !== "undefined";
9384
- try {
9385
- const pkgPath = fileURLToPath2(import.meta.resolve("@anthropic-ai/claude-code/package.json"));
9386
- const bundledBinary = join2(dirname2(pkgPath), "bin", "claude.exe");
9387
- if (existsSync2(bundledBinary)) {
9388
- cachedClaudePath = bundledBinary;
9389
- return bundledBinary;
9390
- }
9391
- } catch {}
9392
- try {
9393
- const { stdout } = await exec("which claude");
9394
- const claudePath = stdout.trim();
9395
- if (claudePath && existsSync2(claudePath)) {
9396
- cachedClaudePath = claudePath;
9397
- return claudePath;
9398
- }
9399
- } catch {}
9400
- if (runningUnderBun) {
9401
- try {
9402
- const sdkPath = fileURLToPath2(import.meta.resolve("@anthropic-ai/claude-agent-sdk"));
9403
- const sdkCliJs = join2(dirname2(sdkPath), "cli.js");
9404
- if (existsSync2(sdkCliJs)) {
9405
- cachedClaudePath = sdkCliJs;
9406
- return sdkCliJs;
9407
- }
9408
- } catch {}
9508
+ const resolved = await resolveClaudeExecutableWithSource();
9509
+ if (resolved) {
9510
+ cachedClaudeInfo = resolved;
9511
+ return resolved.path;
9409
9512
  }
9410
- throw new Error("Could not find Claude Code executable. Install via: npm install -g @anthropic-ai/claude-code");
9513
+ 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.");
9411
9514
  })();
9412
9515
  try {
9413
9516
  return await cachedClaudePathPromise;
@@ -10285,6 +10388,10 @@ var DROID_ALLOWED_MCP_TOOLS = [
10285
10388
  `mcp__${DROID_MCP_SERVER_NAME}__glob`,
10286
10389
  `mcp__${DROID_MCP_SERVER_NAME}__grep`
10287
10390
  ];
10391
+ function resolveDroidPassthrough() {
10392
+ const envVal = process.env.MERIDIAN_PASSTHROUGH ?? process.env.CLAUDE_PROXY_PASSTHROUGH;
10393
+ return envVal === "1" || envVal === "true" || envVal === "yes";
10394
+ }
10288
10395
  var droidTransforms = [
10289
10396
  {
10290
10397
  name: "droid-core",
@@ -10296,7 +10403,7 @@ var droidTransforms = [
10296
10403
  incompatibleTools: CLAUDE_CODE_ONLY_TOOLS,
10297
10404
  allowedMcpTools: DROID_ALLOWED_MCP_TOOLS,
10298
10405
  sdkAgents: {},
10299
- passthrough: false,
10406
+ passthrough: resolveDroidPassthrough(),
10300
10407
  leaksCwdViaSystemReminder: true
10301
10408
  };
10302
10409
  }
@@ -10367,7 +10474,8 @@ var droidAdapter = {
10367
10474
  return "";
10368
10475
  },
10369
10476
  usesPassthrough() {
10370
- return false;
10477
+ const envVal = process.env.MERIDIAN_PASSTHROUGH ?? process.env.CLAUDE_PROXY_PASSTHROUGH;
10478
+ return envVal === "1" || envVal === "true" || envVal === "yes";
10371
10479
  }
10372
10480
  };
10373
10481
 
@@ -10929,10 +11037,6 @@ function detectAdapter(c) {
10929
11037
  return defaultAdapter;
10930
11038
  }
10931
11039
 
10932
- // src/proxy/query.ts
10933
- import { join as join3 } from "node:path";
10934
- import { homedir as homedir2 } from "node:os";
10935
-
10936
11040
  // src/mcpTools.ts
10937
11041
  import { createSdkMcpServer as createSdkMcpServer2, tool } from "@anthropic-ai/claude-agent-sdk";
10938
11042
  import * as fs from "node:fs/promises";
@@ -16625,6 +16729,15 @@ function createOpencodeMcpServer() {
16625
16729
  }
16626
16730
 
16627
16731
  // src/proxy/query.ts
16732
+ function stripConfigDir(env2) {
16733
+ if (!("CLAUDE_CONFIG_DIR" in env2))
16734
+ return env2;
16735
+ if (env2.CLAUDE_CODE_OAUTH_TOKEN)
16736
+ return env2;
16737
+ const out = { ...env2 };
16738
+ delete out.CLAUDE_CONFIG_DIR;
16739
+ return out;
16740
+ }
16628
16741
  function computePassthroughMaxTurns(resumeSessionId, hasDeferredTools, advisorModel) {
16629
16742
  const hasResume = !!resumeSessionId;
16630
16743
  const base = hasResume && hasDeferredTools ? 4 : 3;
@@ -16729,10 +16842,9 @@ function buildQueryOptions(ctx) {
16729
16842
  } : {},
16730
16843
  ...onStderr ? { stderr: onStderr } : {},
16731
16844
  env: {
16732
- ...cleanEnv,
16845
+ ...sharedMemory ? stripConfigDir(cleanEnv) : cleanEnv,
16733
16846
  ENABLE_TOOL_SEARCH: hasDeferredTools ? "true" : "false",
16734
16847
  ...passthrough ? { ENABLE_CLAUDEAI_MCP_SERVERS: "false" } : {},
16735
- ...sharedMemory ? { CLAUDE_CONFIG_DIR: join3(homedir2(), ".claude") } : {},
16736
16848
  ...process.getuid?.() === 0 ? { IS_SANDBOX: "1" } : {}
16737
16849
  },
16738
16850
  ...Object.keys(sdkAgents).length > 0 ? { agents: sdkAgents } : {},
@@ -16766,8 +16878,9 @@ function getAdapterTransforms(adapterName) {
16766
16878
  }
16767
16879
 
16768
16880
  // src/proxy/plugins/loader.ts
16769
- import { readdirSync as readdirSync2, readFileSync as readFileSync2, existsSync as existsSync3 } from "fs";
16770
- import { join as join4, isAbsolute as isAbsolute2, extname } from "path";
16881
+ import { readdirSync as readdirSync2, readFileSync as readFileSync2, existsSync as existsSync4 } from "fs";
16882
+ import { join as join3, isAbsolute as isAbsolute2, extname } from "path";
16883
+ import { pathToFileURL } from "url";
16771
16884
 
16772
16885
  // src/proxy/plugins/validation.ts
16773
16886
  var KNOWN_ADAPTERS = ["opencode", "crush", "droid", "pi", "forgecode", "passthrough"];
@@ -16807,7 +16920,7 @@ function validateTransform(exported) {
16807
16920
  // src/proxy/plugins/loader.ts
16808
16921
  var loadCounter = 0;
16809
16922
  function parsePluginConfig(configPath) {
16810
- if (!existsSync3(configPath))
16923
+ if (!existsSync4(configPath))
16811
16924
  return [];
16812
16925
  try {
16813
16926
  const raw2 = readFileSync2(configPath, "utf-8");
@@ -16820,7 +16933,7 @@ function parsePluginConfig(configPath) {
16820
16933
  async function loadPlugins(pluginDir, configPath) {
16821
16934
  resetAllPluginStats();
16822
16935
  const config = configPath ? parsePluginConfig(configPath) : [];
16823
- const pluginDirExists = existsSync3(pluginDir);
16936
+ const pluginDirExists = existsSync4(pluginDir);
16824
16937
  let filenames = [];
16825
16938
  if (pluginDirExists) {
16826
16939
  try {
@@ -16851,7 +16964,7 @@ async function loadPlugins(pluginDir, configPath) {
16851
16964
  const loaded = [];
16852
16965
  const seenNames = new Set;
16853
16966
  for (const { filename, entry } of ordered) {
16854
- const filePath = isAbsolute2(filename) ? filename : join4(pluginDir, filename);
16967
+ const filePath = isAbsolute2(filename) ? filename : join3(pluginDir, filename);
16855
16968
  if (entry && !entry.enabled) {
16856
16969
  loaded.push({
16857
16970
  name: filename,
@@ -16864,7 +16977,8 @@ async function loadPlugins(pluginDir, configPath) {
16864
16977
  }
16865
16978
  try {
16866
16979
  const cacheBuster = `?t=${Date.now()}-${++loadCounter}`;
16867
- const mod = await import(filePath + cacheBuster);
16980
+ const specifier = process.platform === "win32" ? pathToFileURL(filePath).href + cacheBuster : filePath + cacheBuster;
16981
+ const mod = await import(specifier);
16868
16982
  const exported = mod.default ?? mod;
16869
16983
  const transforms = Array.isArray(exported) ? exported : [exported];
16870
16984
  for (const item of transforms) {
@@ -17222,17 +17336,17 @@ function verifyLineage(cached, messages, cacheKey2, cache) {
17222
17336
  // src/proxy/sessionStore.ts
17223
17337
  import {
17224
17338
  closeSync,
17225
- existsSync as existsSync4,
17339
+ existsSync as existsSync5,
17226
17340
  mkdirSync,
17227
17341
  openSync,
17228
17342
  readFileSync as readFileSync3,
17229
17343
  renameSync,
17230
- statSync,
17344
+ statSync as statSync2,
17231
17345
  unlinkSync,
17232
17346
  writeFileSync
17233
17347
  } from "node:fs";
17234
- import { homedir as homedir3 } from "node:os";
17235
- import { join as join5 } from "node:path";
17348
+ import { homedir as homedir2 } from "node:os";
17349
+ import { join as join4 } from "node:path";
17236
17350
  var DEFAULT_MAX_STORED_SESSIONS = 1e4;
17237
17351
  var STALE_LOCK_THRESHOLD_MS = 30000;
17238
17352
  function getMaxStoredSessions() {
@@ -17256,7 +17370,7 @@ function acquireLock(lockPath) {
17256
17370
  return false;
17257
17371
  }
17258
17372
  try {
17259
- const stat = statSync(lockPath);
17373
+ const stat = statSync2(lockPath);
17260
17374
  if (Date.now() - stat.mtimeMs > STALE_LOCK_THRESHOLD_MS) {
17261
17375
  unlinkSync(lockPath);
17262
17376
  const fd = openSync(lockPath, "wx");
@@ -17280,17 +17394,17 @@ var sessionDirOverride = null;
17280
17394
  var skipLocking = false;
17281
17395
  function getStorePath() {
17282
17396
  const dir = sessionDirOverride || process.env.MERIDIAN_SESSION_DIR || process.env.CLAUDE_PROXY_SESSION_DIR || getDefaultCacheDir();
17283
- if (!existsSync4(dir)) {
17397
+ if (!existsSync5(dir)) {
17284
17398
  mkdirSync(dir, { recursive: true });
17285
17399
  }
17286
- return join5(dir, "sessions.json");
17400
+ return join4(dir, "sessions.json");
17287
17401
  }
17288
17402
  function getDefaultCacheDir() {
17289
- const newDir = join5(homedir3(), ".cache", "meridian");
17290
- const oldDir = join5(homedir3(), ".cache", "opencode-claude-max-proxy");
17291
- if (existsSync4(newDir))
17403
+ const newDir = join4(homedir2(), ".cache", "meridian");
17404
+ const oldDir = join4(homedir2(), ".cache", "opencode-claude-max-proxy");
17405
+ if (existsSync5(newDir))
17292
17406
  return newDir;
17293
- if (existsSync4(oldDir)) {
17407
+ if (existsSync5(oldDir)) {
17294
17408
  try {
17295
17409
  const { symlinkSync } = __require("fs");
17296
17410
  symlinkSync(oldDir, newDir);
@@ -17303,7 +17417,7 @@ function getDefaultCacheDir() {
17303
17417
  }
17304
17418
  function readStore() {
17305
17419
  const path3 = getStorePath();
17306
- if (!existsSync4(path3))
17420
+ if (!existsSync5(path3))
17307
17421
  return {};
17308
17422
  try {
17309
17423
  const data = readFileSync3(path3, "utf-8");
@@ -17805,8 +17919,8 @@ function createProxyServer(config = {}) {
17805
17919
  const sessionDiscoveredTools = new Map;
17806
17920
  const sessionToolCache = new Map;
17807
17921
  const sessionMcpCache = new LRUMap(getMaxSessionsLimit());
17808
- const pluginDir = finalConfig.pluginDir ?? join7(homedir5(), ".config", "meridian", "plugins");
17809
- const pluginConfigPath = finalConfig.pluginConfigPath ?? join7(homedir5(), ".config", "meridian", "plugins.json");
17922
+ const pluginDir = finalConfig.pluginDir ?? join6(homedir4(), ".config", "meridian", "plugins");
17923
+ const pluginConfigPath = finalConfig.pluginConfigPath ?? join6(homedir4(), ".config", "meridian", "plugins.json");
17810
17924
  let loadedPlugins = [];
17811
17925
  let pluginTransforms = [];
17812
17926
  const app = new Hono2;
@@ -17820,6 +17934,8 @@ function createProxyServer(config = {}) {
17820
17934
  app.use("/profiles", requireAuth);
17821
17935
  app.use("/plugins/*", requireAuth);
17822
17936
  app.use("/plugins", requireAuth);
17937
+ app.use("/settings/*", requireAuth);
17938
+ app.use("/settings", requireAuth);
17823
17939
  app.use("/auth/*", requireAuth);
17824
17940
  app.get("/", (c) => {
17825
17941
  const accept = c.req.header("accept") || "";
@@ -17872,18 +17988,33 @@ function createProxyServer(config = {}) {
17872
17988
  if (!Array.isArray(body.messages)) {
17873
17989
  return c.json({ type: "error", error: { type: "invalid_request_error", message: "messages: Field required" } }, 400);
17874
17990
  }
17991
+ if (body.messages.length === 0) {
17992
+ return c.json({ type: "error", error: { type: "invalid_request_error", message: "messages: Cannot be empty — at least one message is required" } }, 400);
17993
+ }
17875
17994
  const profile = resolveProfile(finalConfig.profiles, finalConfig.defaultProfile, c.req.header("x-meridian-profile") || undefined);
17876
17995
  const authStatus = await getClaudeAuthStatusAsync(profile.id !== "default" ? profile.id : undefined, Object.keys(profile.env).length > 0 ? profile.env : undefined);
17877
17996
  const agentMode = c.req.header("x-opencode-agent-mode") ?? null;
17878
17997
  const requestSource = c.req.header("x-meridian-source")?.slice(0, 64) || undefined;
17879
17998
  let model = mapModelToClaudeModel(body.model || "sonnet", authStatus?.subscriptionType, agentMode);
17880
- const workingDirectory = (process.env.MERIDIAN_WORKDIR ?? process.env.CLAUDE_PROXY_WORKDIR) || adapter.extractWorkingDirectory(body) || process.cwd();
17881
- const clientWorkingDirectory = adapter.extractClientWorkingDirectory?.(body) || workingDirectory;
17999
+ const cwdResolution = resolveSdkWorkingDirectory({
18000
+ envOverride: process.env.MERIDIAN_WORKDIR ?? process.env.CLAUDE_PROXY_WORKDIR,
18001
+ adapterCwd: adapter.extractWorkingDirectory(body),
18002
+ fallback: process.cwd()
18003
+ });
18004
+ const workingDirectory = cwdResolution.workingDirectory;
18005
+ if (cwdResolution.fellBack) {
18006
+ claudeLog("cwd_fallback", {
18007
+ claimed: cwdResolution.claimedWorkingDirectory,
18008
+ usedInstead: workingDirectory
18009
+ });
18010
+ }
18011
+ const clientWorkingDirectory = adapter.extractClientWorkingDirectory?.(body) || cwdResolution.claimedWorkingDirectory;
17882
18012
  const {
17883
- CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS,
17884
18013
  ANTHROPIC_API_KEY: _dropApiKey,
17885
18014
  ANTHROPIC_BASE_URL: _dropBaseUrl,
17886
18015
  ANTHROPIC_AUTH_TOKEN: _dropAuthToken,
18016
+ CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS,
18017
+ CLAUDE_CODE_USE_POWERSHELL_TOOL: _dropUsePowershell,
17887
18018
  ...cleanEnv
17888
18019
  } = process.env;
17889
18020
  const sdkModelDefaults = resolveSdkModelDefaults();
@@ -18142,7 +18273,7 @@ function createProxyServer(config = {}) {
18142
18273
  const upstreamStartAt = Date.now();
18143
18274
  let firstChunkAt;
18144
18275
  let currentSessionId;
18145
- const sdkUuidMap = cachedSession?.sdkMessageUuids ? [...cachedSession.sdkMessageUuids] : new Array(allMessages.length - 1).fill(null);
18276
+ const sdkUuidMap = cachedSession?.sdkMessageUuids ? [...cachedSession.sdkMessageUuids] : [];
18146
18277
  while (sdkUuidMap.length < allMessages.length)
18147
18278
  sdkUuidMap.push(null);
18148
18279
  claudeLog("upstream.start", { mode: "non_stream", model });
@@ -18156,6 +18287,7 @@ function createProxyServer(config = {}) {
18156
18287
  const RATE_LIMIT_BASE_DELAY_MS = 1000;
18157
18288
  const response = async function* () {
18158
18289
  let rateLimitRetries = 0;
18290
+ await ensureFreshToken().catch(() => {});
18159
18291
  let tokenRefreshed = false;
18160
18292
  while (true) {
18161
18293
  let didYieldContent = false;
@@ -18187,7 +18319,7 @@ function createProxyServer(config = {}) {
18187
18319
  taskBudget,
18188
18320
  betas,
18189
18321
  settingSources,
18190
- codeSystemPrompt: sdkFeatures.codeSystemPrompt ? true : undefined,
18322
+ codeSystemPrompt: sdkFeatures.codeSystemPrompt,
18191
18323
  clientSystemPrompt: sdkFeatures.clientSystemPrompt === false ? false : undefined,
18192
18324
  memory: sdkFeatures.memory,
18193
18325
  dreaming: sdkFeatures.dreaming,
@@ -18249,7 +18381,7 @@ function createProxyServer(config = {}) {
18249
18381
  taskBudget,
18250
18382
  betas,
18251
18383
  settingSources,
18252
- codeSystemPrompt: sdkFeatures.codeSystemPrompt ? true : undefined,
18384
+ codeSystemPrompt: sdkFeatures.codeSystemPrompt,
18253
18385
  clientSystemPrompt: sdkFeatures.clientSystemPrompt === false ? false : undefined,
18254
18386
  memory: sdkFeatures.memory,
18255
18387
  dreaming: sdkFeatures.dreaming,
@@ -18360,6 +18492,12 @@ function createProxyServer(config = {}) {
18360
18492
  lastStopReason = message.message.stop_reason;
18361
18493
  }
18362
18494
  }
18495
+ if (message.type === "result") {
18496
+ const resultUsage = message.usage;
18497
+ if (resultUsage) {
18498
+ lastUsage = { ...lastUsage, ...resultUsage };
18499
+ }
18500
+ }
18363
18501
  }
18364
18502
  claudeLog("upstream.completed", {
18365
18503
  mode: "non_stream",
@@ -18539,7 +18677,7 @@ Subprocess stderr: ${stderrOutput}`;
18539
18677
  throw error;
18540
18678
  }
18541
18679
  };
18542
- const sdkUuidMap = cachedSession?.sdkMessageUuids ? [...cachedSession.sdkMessageUuids] : new Array(allMessages.length - 1).fill(null);
18680
+ const sdkUuidMap = cachedSession?.sdkMessageUuids ? [...cachedSession.sdkMessageUuids] : [];
18543
18681
  while (sdkUuidMap.length < allMessages.length)
18544
18682
  sdkUuidMap.push(null);
18545
18683
  let messageStartEmitted = false;
@@ -18551,6 +18689,7 @@ Subprocess stderr: ${stderrOutput}`;
18551
18689
  const RATE_LIMIT_BASE_DELAY_MS = 1000;
18552
18690
  const response = async function* () {
18553
18691
  let rateLimitRetries = 0;
18692
+ await ensureFreshToken().catch(() => {});
18554
18693
  let tokenRefreshed = false;
18555
18694
  while (true) {
18556
18695
  let didYieldClientEvent = false;
@@ -18582,7 +18721,7 @@ Subprocess stderr: ${stderrOutput}`;
18582
18721
  taskBudget,
18583
18722
  betas,
18584
18723
  settingSources,
18585
- codeSystemPrompt: sdkFeatures.codeSystemPrompt ? true : undefined,
18724
+ codeSystemPrompt: sdkFeatures.codeSystemPrompt,
18586
18725
  clientSystemPrompt: sdkFeatures.clientSystemPrompt === false ? false : undefined,
18587
18726
  memory: sdkFeatures.memory,
18588
18727
  dreaming: sdkFeatures.dreaming,
@@ -18644,7 +18783,7 @@ Subprocess stderr: ${stderrOutput}`;
18644
18783
  taskBudget,
18645
18784
  betas,
18646
18785
  settingSources,
18647
- codeSystemPrompt: sdkFeatures.codeSystemPrompt ? true : undefined,
18786
+ codeSystemPrompt: sdkFeatures.codeSystemPrompt,
18648
18787
  clientSystemPrompt: sdkFeatures.clientSystemPrompt === false ? false : undefined,
18649
18788
  memory: sdkFeatures.memory,
18650
18789
  dreaming: sdkFeatures.dreaming,
@@ -19368,6 +19507,7 @@ data: ${JSON.stringify({
19368
19507
  auth: { loggedIn: false }
19369
19508
  }, 503);
19370
19509
  }
19510
+ const claudeExecutableInfo = getResolvedClaudeExecutableInfo();
19371
19511
  return c.json({
19372
19512
  status: "healthy",
19373
19513
  version: serverVersion,
@@ -19377,6 +19517,7 @@ data: ${JSON.stringify({
19377
19517
  subscriptionType: auth.subscriptionType
19378
19518
  },
19379
19519
  mode: envBool("PASSTHROUGH") ? "passthrough" : "internal",
19520
+ ...claudeExecutableInfo ? { claudeExecutable: claudeExecutableInfo } : {},
19380
19521
  plugin: { opencode: checkPluginConfigured() ? "configured" : "not-configured" }
19381
19522
  });
19382
19523
  } catch {
@@ -19489,9 +19630,16 @@ data: ${JSON.stringify({
19489
19630
  if (!anthropicBody) {
19490
19631
  return c.json({ type: "error", error: { type: "invalid_request_error", message: "messages: Field required" } }, 400);
19491
19632
  }
19633
+ const internalHeaders = { "Content-Type": "application/json" };
19634
+ const xApiKey = c.req.header("x-api-key");
19635
+ if (xApiKey)
19636
+ internalHeaders["x-api-key"] = xApiKey;
19637
+ const authz = c.req.header("authorization");
19638
+ if (authz)
19639
+ internalHeaders["authorization"] = authz;
19492
19640
  const internalReq = new Request("http://internal/v1/messages", {
19493
19641
  method: "POST",
19494
- headers: { "Content-Type": "application/json" },
19642
+ headers: internalHeaders,
19495
19643
  body: JSON.stringify(anthropicBody)
19496
19644
  });
19497
19645
  const internalRes = await app.fetch(internalReq);
@@ -19770,6 +19918,10 @@ async function startProxyServer(config = {}) {
19770
19918
  console.log(`Telemetry dashboard: http://${finalConfig.host}:${info.port}/telemetry`);
19771
19919
  const pins = resolveSdkModelDefaults();
19772
19920
  console.log(`Model pins: opus=${pins.ANTHROPIC_DEFAULT_OPUS_MODEL} sonnet=${pins.ANTHROPIC_DEFAULT_SONNET_MODEL} haiku=${pins.ANTHROPIC_DEFAULT_HAIKU_MODEL}`);
19921
+ const claudeInfo = getResolvedClaudeExecutableInfo();
19922
+ if (claudeInfo) {
19923
+ console.log(`Claude executable: ${claudeInfo.path} (resolved via ${claudeInfo.source})`);
19924
+ }
19773
19925
  console.log(`
19774
19926
  Point any Anthropic-compatible tool at this endpoint:`);
19775
19927
  console.log(` ANTHROPIC_API_KEY=x ANTHROPIC_BASE_URL=http://${finalConfig.host}:${info.port}`);
@@ -19791,6 +19943,7 @@ Or use a different port:`);
19791
19943
  console.error(` MERIDIAN_PORT=4567 meridian`);
19792
19944
  }
19793
19945
  });
19946
+ startBackgroundRefresh();
19794
19947
  let authKeepaliveInterval;
19795
19948
  const effectiveProfiles = getEffectiveProfiles(finalConfig.profiles);
19796
19949
  if (effectiveProfiles.length > 0) {
@@ -19814,6 +19967,7 @@ Or use a different port:`);
19814
19967
  async close() {
19815
19968
  if (authKeepaliveInterval)
19816
19969
  clearInterval(authKeepaliveInterval);
19970
+ stopBackgroundRefresh();
19817
19971
  await new Promise((resolve3, reject) => {
19818
19972
  server.close((err) => err ? reject(err) : resolve3());
19819
19973
  });