@rynfar/meridian 1.53.0 → 1.55.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.
@@ -23,6 +23,10 @@ import {
23
23
  } from "./cli-xmweegb1.js";
24
24
  import {
25
25
  CANONICAL_SONNET_MODEL,
26
+ OAUTH_CLIENT_ID,
27
+ OAUTH_REDIRECT_URI,
28
+ OAUTH_TOKEN_URL,
29
+ createManualOAuthSession,
26
30
  env,
27
31
  envBool,
28
32
  envInt,
@@ -34,12 +38,13 @@ import {
34
38
  init_env,
35
39
  isClosedControllerError,
36
40
  mapModelToClaudeModel,
41
+ parseAuthorizationCodeInput,
37
42
  recordExtendedContextUnavailable,
38
43
  resolveClaudeExecutableAsync,
39
44
  resolvePassthrough,
40
45
  resolveSdkModelDefaults,
41
46
  stripExtendedContext
42
- } from "./cli-vjeftz4z.js";
47
+ } from "./cli-jhm27q0x.js";
43
48
  import {
44
49
  getSetting
45
50
  } from "./cli-340h1chz.js";
@@ -1482,13 +1487,13 @@ __export(exports_sdkFeatures, {
1482
1487
  getAllFeatureConfigs: () => getAllFeatureConfigs
1483
1488
  });
1484
1489
  import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync3, renameSync as renameSync3 } from "node:fs";
1485
- import { join as join6 } from "node:path";
1486
- import { homedir as homedir5 } from "node:os";
1490
+ import { join as join7 } from "node:path";
1491
+ import { homedir as homedir6 } from "node:os";
1487
1492
  function getConfigPath2() {
1488
- const dir = join6(homedir5(), ".config", "meridian");
1493
+ const dir = join7(homedir6(), ".config", "meridian");
1489
1494
  if (!existsSync7(dir))
1490
1495
  mkdirSync3(dir, { recursive: true });
1491
- return join6(dir, "sdk-features.json");
1496
+ return join7(dir, "sdk-features.json");
1492
1497
  }
1493
1498
  function readConfig() {
1494
1499
  const now = Date.now();
@@ -3641,6 +3646,112 @@ var cors = (options) => {
3641
3646
  };
3642
3647
  };
3643
3648
 
3649
+ // node_modules/hono/dist/utils/stream.js
3650
+ var StreamingApi = class {
3651
+ writer;
3652
+ encoder;
3653
+ writable;
3654
+ abortSubscribers = [];
3655
+ responseReadable;
3656
+ aborted = false;
3657
+ closed = false;
3658
+ constructor(writable, _readable) {
3659
+ this.writable = writable;
3660
+ this.writer = writable.getWriter();
3661
+ this.encoder = new TextEncoder;
3662
+ const reader = _readable.getReader();
3663
+ this.abortSubscribers.push(async () => {
3664
+ await reader.cancel();
3665
+ });
3666
+ this.responseReadable = new ReadableStream({
3667
+ async pull(controller) {
3668
+ const { done, value } = await reader.read();
3669
+ done ? controller.close() : controller.enqueue(value);
3670
+ },
3671
+ cancel: () => {
3672
+ this.abort();
3673
+ }
3674
+ });
3675
+ }
3676
+ async write(input) {
3677
+ try {
3678
+ if (typeof input === "string") {
3679
+ input = this.encoder.encode(input);
3680
+ }
3681
+ await this.writer.write(input);
3682
+ } catch {}
3683
+ return this;
3684
+ }
3685
+ async writeln(input) {
3686
+ await this.write(input + `
3687
+ `);
3688
+ return this;
3689
+ }
3690
+ sleep(ms) {
3691
+ return new Promise((res) => setTimeout(res, ms));
3692
+ }
3693
+ async close() {
3694
+ try {
3695
+ await this.writer.close();
3696
+ } catch {}
3697
+ this.closed = true;
3698
+ }
3699
+ async pipe(body) {
3700
+ this.writer.releaseLock();
3701
+ await body.pipeTo(this.writable, { preventClose: true });
3702
+ this.writer = this.writable.getWriter();
3703
+ }
3704
+ onAbort(listener) {
3705
+ this.abortSubscribers.push(listener);
3706
+ }
3707
+ abort() {
3708
+ if (!this.aborted) {
3709
+ this.aborted = true;
3710
+ this.abortSubscribers.forEach((subscriber) => subscriber());
3711
+ }
3712
+ }
3713
+ };
3714
+
3715
+ // node_modules/hono/dist/helper/streaming/utils.js
3716
+ var isOldBunVersion = () => {
3717
+ const version = typeof Bun !== "undefined" ? Bun.version : undefined;
3718
+ if (version === undefined) {
3719
+ return false;
3720
+ }
3721
+ const result = version.startsWith("1.1") || version.startsWith("1.0") || version.startsWith("0.");
3722
+ isOldBunVersion = () => result;
3723
+ return result;
3724
+ };
3725
+
3726
+ // node_modules/hono/dist/helper/streaming/stream.js
3727
+ var contextStash = /* @__PURE__ */ new WeakMap;
3728
+ var stream = (c, cb, onError) => {
3729
+ const { readable, writable } = new TransformStream;
3730
+ const stream2 = new StreamingApi(writable, readable);
3731
+ if (isOldBunVersion()) {
3732
+ c.req.raw.signal.addEventListener("abort", () => {
3733
+ if (!stream2.closed) {
3734
+ stream2.abort();
3735
+ }
3736
+ });
3737
+ }
3738
+ contextStash.set(stream2.responseReadable, c);
3739
+ (async () => {
3740
+ try {
3741
+ await cb(stream2);
3742
+ } catch (e) {
3743
+ if (e === undefined) {} else if (e instanceof Error && onError) {
3744
+ await onError(e, stream2);
3745
+ } else {
3746
+ console.error(e);
3747
+ }
3748
+ } finally {
3749
+ stream2.close();
3750
+ }
3751
+ })();
3752
+ return c.newResponse(stream2.responseReadable);
3753
+ };
3754
+
3644
3755
  // node_modules/@hono/node-server/dist/index.mjs
3645
3756
  import { createServer as createServerHTTP } from "http";
3646
3757
  import { Http2ServerRequest as Http2ServerRequest2 } from "http2";
@@ -3921,13 +4032,13 @@ function writeFromReadableStreamDefaultReader(reader, writable, currentReadPromi
3921
4032
  }
3922
4033
  }
3923
4034
  }
3924
- function writeFromReadableStream(stream, writable) {
3925
- if (stream.locked) {
4035
+ function writeFromReadableStream(stream2, writable) {
4036
+ if (stream2.locked) {
3926
4037
  throw new TypeError("ReadableStream is locked.");
3927
4038
  } else if (writable.destroyed) {
3928
4039
  return;
3929
4040
  }
3930
- return writeFromReadableStreamDefaultReader(stream.getReader(), writable);
4041
+ return writeFromReadableStreamDefaultReader(stream2.getReader(), writable);
3931
4042
  }
3932
4043
  var buildOutgoingHttpHeaders = (headers) => {
3933
4044
  const res = {};
@@ -4192,8 +4303,8 @@ var serve = (options, listeningListener) => {
4192
4303
  };
4193
4304
 
4194
4305
  // src/proxy/server.ts
4195
- import { homedir as homedir6 } from "node:os";
4196
- import { join as join7 } from "node:path";
4306
+ import { homedir as homedir7 } from "node:os";
4307
+ import { join as join8 } from "node:path";
4197
4308
  import { query } from "@anthropic-ai/claude-agent-sdk";
4198
4309
 
4199
4310
  // src/proxy/rateLimitStore.ts
@@ -4542,7 +4653,7 @@ function createRequestContext(params) {
4542
4653
  }
4543
4654
  // src/proxy/server.ts
4544
4655
  import { exec as execCallback } from "child_process";
4545
- import { promisify as promisify2 } from "util";
4656
+ import { promisify as promisify3 } from "util";
4546
4657
  import { randomUUID } from "crypto";
4547
4658
 
4548
4659
  // src/proxy/passthroughTools.ts
@@ -9877,7 +9988,7 @@ function introSection(h){
9877
9988
  meta.push('port '+location.port);
9878
9989
  return '<div class="intro">'
9879
9990
  +'<h2>Harness Claude, your way.</h2>'
9880
- +'<p>Meridian bridges any Anthropic-API agent to your Claude subscription — point the agent’s <code>ANTHROPIC_BASE_URL</code> at <code>http://'+esc(location.host)+'</code> and every request routes through the active account below. Setup guides for each agent live in the <a href="https://github.com/rynfar/meridian#readme">README</a>.</p>'
9991
+ +'<p>Meridian bridges any Anthropic-API agent to your Claude subscription — point the agent’s <code>ANTHROPIC_BASE_URL</code> at <code>http://'+esc(location.host)+'</code> and every request routes through the active account below. Setup guides for each agent live in the <a href="https://github.com/rynfar/meridian/blob/main/docs/agents.md">Agent Setup guide</a>.</p>'
9881
9992
  +'<div class="intro-meta">'+meta.join(' · ')+'</div>'
9882
9993
  +'</div>';
9883
9994
  }
@@ -10274,6 +10385,217 @@ function formatSdkTermination(t, ctx) {
10274
10385
  return `sdk_termination ${parts.join(" ")}`;
10275
10386
  }
10276
10387
 
10388
+ // src/proxy/design.ts
10389
+ import { homedir as homedir3 } from "node:os";
10390
+ import { join as join3, dirname as dirname3 } from "node:path";
10391
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
10392
+ var DESIGN_SCOPES = ["user:design:read", "user:design:write"];
10393
+ var DESIGN_UPSTREAM_ORIGIN = "https://api.anthropic.com";
10394
+ var EXPIRY_SKEW_MS = 60000;
10395
+ var LOGIN_SESSION_TTL_MS = 10 * 60 * 1000;
10396
+ function defaultDesignTokenPath() {
10397
+ return process.env.MERIDIAN_DESIGN_TOKEN_PATH || join3(homedir3(), ".config", "meridian", "design-token.json");
10398
+ }
10399
+ function createFileDesignTokenStore(path = defaultDesignTokenPath()) {
10400
+ return {
10401
+ async read() {
10402
+ try {
10403
+ const raw2 = await readFile(path, "utf-8");
10404
+ const data = JSON.parse(raw2);
10405
+ if (typeof data.accessToken === "string" && typeof data.expiresAt === "number") {
10406
+ return data;
10407
+ }
10408
+ return null;
10409
+ } catch {
10410
+ return null;
10411
+ }
10412
+ },
10413
+ async write(data) {
10414
+ await mkdir(dirname3(path), { recursive: true });
10415
+ await writeFile(path, JSON.stringify(data), { mode: 384 });
10416
+ }
10417
+ };
10418
+ }
10419
+ function isDesignTokenFresh(data, now = Date.now()) {
10420
+ return data.expiresAt > now + EXPIRY_SKEW_MS;
10421
+ }
10422
+ async function getDesignAccessToken(opts) {
10423
+ const now = opts.now ?? Date.now();
10424
+ const fetchFn = opts.fetchFn ?? fetch;
10425
+ const data = await opts.store.read();
10426
+ if (!data)
10427
+ return null;
10428
+ if (isDesignTokenFresh(data, now))
10429
+ return data.accessToken;
10430
+ if (!data.refreshToken)
10431
+ return null;
10432
+ let response;
10433
+ try {
10434
+ response = await fetchFn(OAUTH_TOKEN_URL, {
10435
+ method: "POST",
10436
+ headers: { "Content-Type": "application/json" },
10437
+ body: JSON.stringify({
10438
+ grant_type: "refresh_token",
10439
+ client_id: OAUTH_CLIENT_ID,
10440
+ refresh_token: data.refreshToken
10441
+ }),
10442
+ signal: AbortSignal.timeout(30000)
10443
+ });
10444
+ } catch {
10445
+ return null;
10446
+ }
10447
+ if (!response.ok)
10448
+ return null;
10449
+ let tokenData;
10450
+ try {
10451
+ tokenData = await response.json();
10452
+ } catch {
10453
+ return null;
10454
+ }
10455
+ if (!tokenData.access_token)
10456
+ return null;
10457
+ const refreshed = {
10458
+ accessToken: tokenData.access_token,
10459
+ refreshToken: tokenData.refresh_token ?? data.refreshToken,
10460
+ expiresAt: tokenData.expires_at ?? now + (tokenData.expires_in ?? 8 * 60 * 60) * 1000,
10461
+ scopes: tokenData.scope?.split(" ").filter(Boolean) ?? data.scopes
10462
+ };
10463
+ await opts.store.write(refreshed);
10464
+ return refreshed.accessToken;
10465
+ }
10466
+ async function resolveDesignAuthHeaders(opts) {
10467
+ if (opts.designToken)
10468
+ return { Authorization: `Bearer ${opts.designToken}` };
10469
+ const { profile } = opts;
10470
+ if (profile.type === "api" && profile.env.ANTHROPIC_API_KEY) {
10471
+ return { "x-api-key": profile.env.ANTHROPIC_API_KEY };
10472
+ }
10473
+ if (profile.type === "oauth-token" && profile.env.CLAUDE_CODE_OAUTH_TOKEN) {
10474
+ return { Authorization: `Bearer ${profile.env.CLAUDE_CODE_OAUTH_TOKEN}` };
10475
+ }
10476
+ if (opts.credentialStore) {
10477
+ if (opts.ensureFresh)
10478
+ await opts.ensureFresh(opts.credentialStore).catch(() => {});
10479
+ const creds = await opts.credentialStore.read();
10480
+ const token = creds?.claudeAiOauth?.accessToken;
10481
+ if (token)
10482
+ return { Authorization: `Bearer ${token}` };
10483
+ }
10484
+ return {};
10485
+ }
10486
+ function buildDesignForwardHeaders(getHeader, authHeaders) {
10487
+ const headers = {
10488
+ "anthropic-version": getHeader("anthropic-version") || "2023-06-01",
10489
+ "accept-encoding": "identity",
10490
+ ...authHeaders
10491
+ };
10492
+ for (const name of ["content-type", "anthropic-beta", "mcp-session-id"]) {
10493
+ const value = getHeader(name);
10494
+ if (value)
10495
+ headers[name] = value;
10496
+ }
10497
+ return headers;
10498
+ }
10499
+ var HOP_BY_HOP = new Set([
10500
+ "transfer-encoding",
10501
+ "connection",
10502
+ "keep-alive",
10503
+ "proxy-authenticate",
10504
+ "proxy-authorization",
10505
+ "te",
10506
+ "trailer",
10507
+ "upgrade"
10508
+ ]);
10509
+ function filterUpstreamResponseHeaders(headers) {
10510
+ const out = {};
10511
+ for (const [key, value] of headers) {
10512
+ if (!HOP_BY_HOP.has(key.toLowerCase()))
10513
+ out[key] = value;
10514
+ }
10515
+ return out;
10516
+ }
10517
+ function isDesignAuthFailure(status) {
10518
+ return status === 401 || status === 404;
10519
+ }
10520
+ function createDesignLogin(deps) {
10521
+ const createSession = deps.createSession ?? createManualOAuthSession;
10522
+ const fetchFn = deps.fetchFn ?? fetch;
10523
+ const now = deps.now ?? Date.now;
10524
+ const sessions = new Map;
10525
+ const startRaw = () => {
10526
+ for (const [state, session2] of sessions) {
10527
+ if (session2.expiresAt < now())
10528
+ sessions.delete(state);
10529
+ }
10530
+ const session = createSession(DESIGN_SCOPES);
10531
+ sessions.set(session.state, { codeVerifier: session.codeVerifier, expiresAt: now() + LOGIN_SESSION_TTL_MS });
10532
+ return { authorizeUrl: session.authorizeUrl, state: session.state };
10533
+ };
10534
+ return {
10535
+ startRaw,
10536
+ start() {
10537
+ const { authorizeUrl } = startRaw();
10538
+ return {
10539
+ authorizeUrl,
10540
+ instructions: 'Open the URL in your browser. After authorizing, POST the code to /design-login with body { "code": "<paste-code-here>" }'
10541
+ };
10542
+ },
10543
+ async exchange(rawBody) {
10544
+ const body = rawBody && typeof rawBody === "object" ? rawBody : {};
10545
+ const parsed = body.code ? parseAuthorizationCodeInput(body.code) : null;
10546
+ if (!parsed) {
10547
+ return { status: 400, body: { type: "error", error: { type: "invalid_request", message: "Missing or invalid 'code' field." } } };
10548
+ }
10549
+ const stateKey = parsed.state ?? body.state;
10550
+ const stored = stateKey ? sessions.get(stateKey) : undefined;
10551
+ if (!stateKey || !stored || stored.expiresAt < now()) {
10552
+ return {
10553
+ status: 400,
10554
+ body: { type: "error", error: { type: "session_expired", message: "OAuth session expired or not found. Call GET /design-login to start a new session." } }
10555
+ };
10556
+ }
10557
+ sessions.delete(stateKey);
10558
+ let response;
10559
+ try {
10560
+ response = await fetchFn(OAUTH_TOKEN_URL, {
10561
+ method: "POST",
10562
+ headers: { "Content-Type": "application/json" },
10563
+ body: JSON.stringify({
10564
+ grant_type: "authorization_code",
10565
+ client_id: OAUTH_CLIENT_ID,
10566
+ code: parsed.code,
10567
+ redirect_uri: OAUTH_REDIRECT_URI,
10568
+ code_verifier: stored.codeVerifier,
10569
+ state: stateKey
10570
+ }),
10571
+ signal: AbortSignal.timeout(30000)
10572
+ });
10573
+ } catch (err) {
10574
+ return { status: 502, body: { type: "error", error: { type: "upstream_error", message: err instanceof Error ? err.message : String(err) } } };
10575
+ }
10576
+ if (!response.ok) {
10577
+ const text = await response.text().catch(() => "");
10578
+ return {
10579
+ status: 502,
10580
+ body: { type: "error", error: { type: "token_exchange_failed", message: `Token exchange failed (${response.status}): ${text.slice(0, 200)}` } }
10581
+ };
10582
+ }
10583
+ const tokenData = await response.json();
10584
+ if (!tokenData.access_token) {
10585
+ return { status: 502, body: { type: "error", error: { type: "token_exchange_failed", message: "Token response missing access_token." } } };
10586
+ }
10587
+ const expiresAt = tokenData.expires_at ?? now() + (tokenData.expires_in ?? 8 * 60 * 60) * 1000;
10588
+ const scopes = tokenData.scope?.split(" ").filter(Boolean) ?? DESIGN_SCOPES;
10589
+ try {
10590
+ await deps.store.write({ accessToken: tokenData.access_token, refreshToken: tokenData.refresh_token, expiresAt, scopes });
10591
+ } catch (err) {
10592
+ return { status: 500, body: { type: "error", error: { type: "storage_error", message: `Failed to store design token: ${err instanceof Error ? err.message : String(err)}` } } };
10593
+ }
10594
+ return { status: 200, body: { success: true, scopes } };
10595
+ }
10596
+ };
10597
+ }
10598
+
10277
10599
  // src/proxy/openai.ts
10278
10600
  function extractOpenAiContent(content) {
10279
10601
  if (typeof content === "string")
@@ -12008,8 +12330,8 @@ function extractPiCwd(body) {
12008
12330
  }
12009
12331
  var piAdapter = {
12010
12332
  name: "pi",
12011
- getSessionId(_c) {
12012
- return;
12333
+ getSessionId(c) {
12334
+ return c.req.header("x-session-affinity");
12013
12335
  },
12014
12336
  extractWorkingDirectory(body) {
12015
12337
  return extractPiCwd(body);
@@ -12274,6 +12596,9 @@ var codexAdapter = {
12274
12596
  name: "codex",
12275
12597
  usesPassthrough() {
12276
12598
  return true;
12599
+ },
12600
+ getSessionId(c) {
12601
+ return c.req.header("x-codex-session");
12277
12602
  }
12278
12603
  };
12279
12604
 
@@ -12307,9 +12632,9 @@ var cherryAdapter = {
12307
12632
 
12308
12633
  // src/proxy/adapterInstances.ts
12309
12634
  import { existsSync as existsSync4, readFileSync as readFileSync3 } from "node:fs";
12310
- import { join as join3 } from "node:path";
12311
- import { homedir as homedir3 } from "node:os";
12312
- var CONFIG_FILE = join3(homedir3(), ".config", "meridian", "adapter-instances.json");
12635
+ import { join as join4 } from "node:path";
12636
+ import { homedir as homedir4 } from "node:os";
12637
+ var CONFIG_FILE = join4(homedir4(), ".config", "meridian", "adapter-instances.json");
12313
12638
  function parseAdapterInstances(raw2) {
12314
12639
  if (!raw2)
12315
12640
  return {};
@@ -12478,7 +12803,7 @@ import { createSdkMcpServer as createSdkMcpServer2, tool } from "@anthropic-ai/c
12478
12803
  import * as fs from "node:fs/promises";
12479
12804
  import * as path2 from "node:path";
12480
12805
  import { exec } from "node:child_process";
12481
- import { promisify } from "node:util";
12806
+ import { promisify as promisify2 } from "node:util";
12482
12807
 
12483
12808
  // node_modules/@isaacs/balanced-match/dist/esm/index.js
12484
12809
  var balanced = (a, b, str) => {
@@ -17999,7 +18324,7 @@ function globIterate(pattern, options = {}) {
17999
18324
  return new Glob(pattern, options).iterate();
18000
18325
  }
18001
18326
  var streamSync = globStreamSync;
18002
- var stream = Object.assign(globStream, { sync: globStreamSync });
18327
+ var stream2 = Object.assign(globStream, { sync: globStreamSync });
18003
18328
  var iterateSync = globIterateSync;
18004
18329
  var iterate = Object.assign(globIterate, {
18005
18330
  sync: globIterateSync
@@ -18013,7 +18338,7 @@ var glob = Object.assign(glob_, {
18013
18338
  globSync,
18014
18339
  sync,
18015
18340
  globStream,
18016
- stream,
18341
+ stream: stream2,
18017
18342
  globStreamSync,
18018
18343
  streamSync,
18019
18344
  globIterate,
@@ -18027,9 +18352,35 @@ var glob = Object.assign(glob_, {
18027
18352
  });
18028
18353
  glob.glob = glob;
18029
18354
 
18030
- // src/mcpTools.ts
18031
- var execAsync = promisify(exec);
18355
+ // src/grepTool.ts
18356
+ import { execFile } from "node:child_process";
18357
+ import { promisify } from "node:util";
18358
+ var execFileAsync = promisify(execFile);
18032
18359
  var getCwd = () => process.env.MERIDIAN_WORKDIR ?? process.env.CLAUDE_PROXY_WORKDIR ?? process.cwd();
18360
+ async function runGrepTool(args) {
18361
+ try {
18362
+ const searchPath = args.path || getCwd();
18363
+ const includePattern = args.include || "*";
18364
+ const grepArgs = ["-rn", `--include=${includePattern}`, args.pattern, searchPath];
18365
+ const { stdout } = await execFileAsync("grep", grepArgs, { maxBuffer: 10 * 1024 * 1024 });
18366
+ return {
18367
+ content: [{ type: "text", text: stdout || "(no matches)" }]
18368
+ };
18369
+ } catch (error) {
18370
+ const grepError = error;
18371
+ if (grepError.code === 1) {
18372
+ return { content: [{ type: "text", text: grepError.stdout || "(no matches)" }] };
18373
+ }
18374
+ return {
18375
+ content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }],
18376
+ isError: true
18377
+ };
18378
+ }
18379
+ }
18380
+
18381
+ // src/mcpTools.ts
18382
+ var execAsync = promisify2(exec);
18383
+ var getCwd2 = () => process.env.MERIDIAN_WORKDIR ?? process.env.CLAUDE_PROXY_WORKDIR ?? process.cwd();
18033
18384
  function createOpencodeMcpServer() {
18034
18385
  return createSdkMcpServer2({
18035
18386
  name: "opencode",
@@ -18040,7 +18391,7 @@ function createOpencodeMcpServer() {
18040
18391
  encoding: exports_external.string().optional().describe("File encoding, defaults to utf-8")
18041
18392
  }, async (args) => {
18042
18393
  try {
18043
- const filePath = path2.isAbsolute(args.path) ? args.path : path2.resolve(getCwd(), args.path);
18394
+ const filePath = path2.isAbsolute(args.path) ? args.path : path2.resolve(getCwd2(), args.path);
18044
18395
  const content = await fs.readFile(filePath, args.encoding || "utf-8");
18045
18396
  return {
18046
18397
  content: [{ type: "text", text: content }]
@@ -18057,7 +18408,7 @@ function createOpencodeMcpServer() {
18057
18408
  content: exports_external.string().describe("Content to write")
18058
18409
  }, async (args) => {
18059
18410
  try {
18060
- const filePath = path2.isAbsolute(args.path) ? args.path : path2.resolve(getCwd(), args.path);
18411
+ const filePath = path2.isAbsolute(args.path) ? args.path : path2.resolve(getCwd2(), args.path);
18061
18412
  await fs.mkdir(path2.dirname(filePath), { recursive: true });
18062
18413
  await fs.writeFile(filePath, args.content, "utf-8");
18063
18414
  return {
@@ -18076,7 +18427,7 @@ function createOpencodeMcpServer() {
18076
18427
  newString: exports_external.string().describe("The replacement text")
18077
18428
  }, async (args) => {
18078
18429
  try {
18079
- const filePath = path2.isAbsolute(args.path) ? args.path : path2.resolve(getCwd(), args.path);
18430
+ const filePath = path2.isAbsolute(args.path) ? args.path : path2.resolve(getCwd2(), args.path);
18080
18431
  const content = await fs.readFile(filePath, "utf-8");
18081
18432
  if (!content.includes(args.oldString)) {
18082
18433
  return {
@@ -18102,7 +18453,7 @@ function createOpencodeMcpServer() {
18102
18453
  }, async (args) => {
18103
18454
  try {
18104
18455
  const options = {
18105
- cwd: args.cwd || getCwd(),
18456
+ cwd: args.cwd || getCwd2(),
18106
18457
  timeout: 120000
18107
18458
  };
18108
18459
  const { stdout, stderr } = await execAsync(args.command, options);
@@ -18125,7 +18476,7 @@ function createOpencodeMcpServer() {
18125
18476
  }, async (args) => {
18126
18477
  try {
18127
18478
  const files = await glob(args.pattern, {
18128
- cwd: args.cwd || getCwd(),
18479
+ cwd: args.cwd || getCwd2(),
18129
18480
  nodir: true,
18130
18481
  ignore: ["**/node_modules/**", "**/.git/**"]
18131
18482
  });
@@ -18144,22 +18495,7 @@ function createOpencodeMcpServer() {
18144
18495
  pattern: exports_external.string().describe("Regex pattern to search for"),
18145
18496
  path: exports_external.string().optional().describe("Directory or file to search in"),
18146
18497
  include: exports_external.string().optional().describe("File pattern to include, e.g., *.ts")
18147
- }, async (args) => {
18148
- try {
18149
- const searchPath = args.path || getCwd();
18150
- const includePattern = args.include || "*";
18151
- let cmd = `grep -rn --include="${includePattern}" "${args.pattern}" "${searchPath}" 2>/dev/null || true`;
18152
- const { stdout } = await execAsync(cmd, { maxBuffer: 10 * 1024 * 1024 });
18153
- return {
18154
- content: [{ type: "text", text: stdout || "(no matches)" }]
18155
- };
18156
- } catch (error) {
18157
- return {
18158
- content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }],
18159
- isError: true
18160
- };
18161
- }
18162
- })
18498
+ }, runGrepTool)
18163
18499
  ]
18164
18500
  });
18165
18501
  }
@@ -18219,7 +18555,7 @@ function buildQueryOptions(ctx, abortController) {
18219
18555
  systemContext,
18220
18556
  claudeExecutable,
18221
18557
  passthrough,
18222
- stream: stream2,
18558
+ stream: stream3,
18223
18559
  sdkAgents,
18224
18560
  passthroughMcp,
18225
18561
  cleanEnv,
@@ -18261,7 +18597,7 @@ function buildQueryOptions(ctx, abortController) {
18261
18597
  model,
18262
18598
  pathToClaudeCodeExecutable: claudeExecutable,
18263
18599
  ...abortController ? { abortController } : {},
18264
- ...stream2 ? { includePartialMessages: true } : {},
18600
+ ...stream3 ? { includePartialMessages: true } : {},
18265
18601
  permissionMode: "bypassPermissions",
18266
18602
  allowDangerouslySkipPermissions: true,
18267
18603
  ...resolveSystemPrompt(systemContext, passthrough, settingSources, codeSystemPrompt, clientSystemPrompt, cwdNote),
@@ -18401,7 +18737,7 @@ function getAdapterTransforms(adapterName) {
18401
18737
 
18402
18738
  // src/proxy/plugins/loader.ts
18403
18739
  import { readdirSync as readdirSync2, readFileSync as readFileSync4, existsSync as existsSync5 } from "fs";
18404
- import { join as join4, isAbsolute as isAbsolute2, extname } from "path";
18740
+ import { join as join5, isAbsolute as isAbsolute2, extname } from "path";
18405
18741
  import { pathToFileURL } from "url";
18406
18742
 
18407
18743
  // src/proxy/plugins/validation.ts
@@ -18486,7 +18822,7 @@ async function loadPlugins(pluginDir, configPath) {
18486
18822
  const loaded = [];
18487
18823
  const seenNames = new Set;
18488
18824
  for (const { filename, entry } of ordered) {
18489
- const filePath = isAbsolute2(filename) ? filename : join4(pluginDir, filename);
18825
+ const filePath = isAbsolute2(filename) ? filename : join5(pluginDir, filename);
18490
18826
  if (entry && !entry.enabled) {
18491
18827
  loaded.push({
18492
18828
  name: filename,
@@ -18868,8 +19204,8 @@ import {
18868
19204
  unlinkSync,
18869
19205
  writeFileSync as writeFileSync2
18870
19206
  } from "node:fs";
18871
- import { homedir as homedir4 } from "node:os";
18872
- import { join as join5 } from "node:path";
19207
+ import { homedir as homedir5 } from "node:os";
19208
+ import { join as join6 } from "node:path";
18873
19209
  var DEFAULT_MAX_STORED_SESSIONS = 1e4;
18874
19210
  var STALE_LOCK_THRESHOLD_MS = 30000;
18875
19211
  function getMaxStoredSessions() {
@@ -18920,11 +19256,11 @@ function getStorePath() {
18920
19256
  if (!existsSync6(dir)) {
18921
19257
  mkdirSync2(dir, { recursive: true });
18922
19258
  }
18923
- return join5(dir, "sessions.json");
19259
+ return join6(dir, "sessions.json");
18924
19260
  }
18925
19261
  function getDefaultCacheDir() {
18926
- const newDir = join5(homedir4(), ".cache", "meridian");
18927
- const oldDir = join5(homedir4(), ".cache", "opencode-claude-max-proxy");
19262
+ const newDir = join6(homedir5(), ".cache", "meridian");
19263
+ const oldDir = join6(homedir5(), ".cache", "opencode-claude-max-proxy");
18928
19264
  if (existsSync6(newDir))
18929
19265
  return newDir;
18930
19266
  if (existsSync6(oldDir)) {
@@ -19264,7 +19600,7 @@ function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sd
19264
19600
  }
19265
19601
 
19266
19602
  // src/proxy/server.ts
19267
- var exec2 = promisify2(execCallback);
19603
+ var exec2 = promisify3(execCallback);
19268
19604
  var claudeExecutable = "";
19269
19605
  var UPSTREAM_IDLE_MS = 90000;
19270
19606
  function credentialStoreForProfile(profile) {
@@ -19494,8 +19830,8 @@ function createProxyServer(config = {}) {
19494
19830
  pendingSessionStores.delete(key);
19495
19831
  };
19496
19832
  };
19497
- const pluginDir = finalConfig.pluginDir ?? join7(homedir6(), ".config", "meridian", "plugins");
19498
- const pluginConfigPath = finalConfig.pluginConfigPath ?? join7(homedir6(), ".config", "meridian", "plugins.json");
19833
+ const pluginDir = finalConfig.pluginDir ?? join8(homedir7(), ".config", "meridian", "plugins");
19834
+ const pluginConfigPath = finalConfig.pluginConfigPath ?? join8(homedir7(), ".config", "meridian", "plugins.json");
19499
19835
  let loadedPlugins = [];
19500
19836
  let pluginTransforms = [];
19501
19837
  const app = new Hono2;
@@ -19511,6 +19847,7 @@ function createProxyServer(config = {}) {
19511
19847
  app.use("/plugins", requireAuth);
19512
19848
  app.use("/settings/*", requireAuth);
19513
19849
  app.use("/settings", requireAuth);
19850
+ app.use("/design-login", requireAuth);
19514
19851
  app.use("/auth/*", requireAuth);
19515
19852
  app.get("/", (c) => {
19516
19853
  const accept = c.req.header("accept") || "";
@@ -19519,7 +19856,7 @@ function createProxyServer(config = {}) {
19519
19856
  status: "ok",
19520
19857
  service: "meridian",
19521
19858
  format: "anthropic",
19522
- endpoints: ["/v1/messages", "/messages", "/v1/chat/completions", "/v1/responses", "/v1/models", "/telemetry", "/metrics", "/health"]
19859
+ endpoints: ["/v1/messages", "/messages", "/v1/chat/completions", "/v1/responses", "/v1/models", "/v1/design/*", "/design-login", "/telemetry", "/metrics", "/health"]
19523
19860
  });
19524
19861
  }
19525
19862
  return c.html(landingHtml);
@@ -19632,7 +19969,7 @@ function createProxyServer(config = {}) {
19632
19969
  stream: body.stream ?? false,
19633
19970
  workingDirectory
19634
19971
  }), adapterBase);
19635
- const stream2 = pipelineCtx.prefersStreaming !== undefined ? pipelineCtx.prefersStreaming : body.stream ?? false;
19972
+ const stream3 = pipelineCtx.prefersStreaming !== undefined ? pipelineCtx.prefersStreaming : body.stream ?? false;
19636
19973
  const effortHeader = c.req.header("x-opencode-effort");
19637
19974
  const thinkingHeader = c.req.header("x-opencode-thinking");
19638
19975
  const taskBudgetHeader = c.req.header("x-opencode-task-budget");
@@ -19679,7 +20016,7 @@ function createProxyServer(config = {}) {
19679
20016
  const lastMessage = Array.isArray(body.messages) ? body.messages[body.messages.length - 1] : undefined;
19680
20017
  const lastIsToolResult = Array.isArray(lastMessage?.content) && lastMessage.content.some((b) => b?.type === "tool_result");
19681
20018
  const isClientDrivenLoop = adapterBase !== "claude-code" && !agentSessionId && lastIsToolResult;
19682
- const isIndependentSession = requestSource?.startsWith("fork-") || requestSource?.startsWith("subagent-") || isClientDrivenLoop || false;
20019
+ const isIndependentSession = !agentSessionId && (requestSource?.startsWith("fork-") || requestSource?.startsWith("subagent-")) || isClientDrivenLoop || false;
19683
20020
  if (!isIndependentSession && profileSessionId) {
19684
20021
  const pendingStore = pendingSessionStores.get(profileSessionId);
19685
20022
  if (pendingStore) {
@@ -19707,7 +20044,7 @@ function createProxyServer(config = {}) {
19707
20044
  const lineageType = lineageResult.type === "diverged" && !cachedSession ? "new" : lineageResult.type;
19708
20045
  const msgCount = Array.isArray(body.messages) ? body.messages.length : 0;
19709
20046
  const toolCount = body.tools?.length ?? 0;
19710
- const requestLogLine = `${requestMeta.requestId} adapter=${adapter.name}${requestSource ? ` source=${requestSource}` : ""}${profile.id !== "default" ? ` profile=${profile.id}${routingMode === "sticky" ? "(sticky)" : ""}` : ""} 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}`;
20047
+ const requestLogLine = `${requestMeta.requestId} adapter=${adapter.name}${requestSource ? ` source=${requestSource}` : ""}${profile.id !== "default" ? ` profile=${profile.id}${routingMode === "sticky" ? "(sticky)" : ""}` : ""} model=${model} stream=${stream3} 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}`;
19711
20048
  plog(`[PROXY] ${requestLogLine} msgs=${msgSummary}`);
19712
20049
  diagnosticLog2.session(`${requestLogLine}`, requestMeta.requestId);
19713
20050
  if (lineageResult.type === "diverged" && profileSessionId && !isIndependentSession) {
@@ -19721,7 +20058,7 @@ function createProxyServer(config = {}) {
19721
20058
  }
19722
20059
  claudeLog("request.received", {
19723
20060
  model,
19724
- stream: stream2,
20061
+ stream: stream3,
19725
20062
  queueWaitMs: requestMeta.queueStartedAt - requestMeta.queueEnteredAt,
19726
20063
  messageCount: Array.isArray(body.messages) ? body.messages.length : 0,
19727
20064
  hasSystemPrompt: Boolean(body.system)
@@ -19960,7 +20297,7 @@ function createProxyServer(config = {}) {
19960
20297
  stderrLines.push(data.trimEnd());
19961
20298
  claudeLog("subprocess.stderr", { line: data.trimEnd() });
19962
20299
  };
19963
- if (!stream2) {
20300
+ if (!stream3) {
19964
20301
  const contentBlocks = [];
19965
20302
  let assistantMessages = 0;
19966
20303
  let hasStructuredOutput = false;
@@ -21897,6 +22234,10 @@ data: ${JSON.stringify({
21897
22234
  "Content-Type": "application/json",
21898
22235
  "x-meridian-agent": "codex"
21899
22236
  };
22237
+ const promptCacheKey = rawBody.prompt_cache_key;
22238
+ if (typeof promptCacheKey === "string" && promptCacheKey.length > 0) {
22239
+ internalHeaders["x-codex-session"] = promptCacheKey;
22240
+ }
21900
22241
  const xApiKey = c.req.header("x-api-key");
21901
22242
  if (xApiKey)
21902
22243
  internalHeaders["x-api-key"] = xApiKey;
@@ -22147,6 +22488,65 @@ data: ${JSON.stringify({ response: { id: responseId, status: "failed", error: {
22147
22488
  } : {}
22148
22489
  });
22149
22490
  });
22491
+ const designTokenStore = createFileDesignTokenStore();
22492
+ const designLogin = createDesignLogin({ store: designTokenStore });
22493
+ app.get("/design-login", (c) => c.json(designLogin.start()));
22494
+ app.post("/design-login", async (c) => {
22495
+ let body;
22496
+ try {
22497
+ body = await c.req.json();
22498
+ } catch {
22499
+ return c.json({ type: "error", error: { type: "invalid_request", message: "Request body must be JSON with a 'code' field." } }, 400);
22500
+ }
22501
+ const result = await designLogin.exchange(body);
22502
+ if (result.status === 200)
22503
+ plog(`[PROXY] Design token stored via /design-login`);
22504
+ return new Response(JSON.stringify(result.body), {
22505
+ status: result.status,
22506
+ headers: { "content-type": "application/json" }
22507
+ });
22508
+ });
22509
+ app.get("/v1/design/*", (c) => {
22510
+ c.status(200);
22511
+ c.header("content-type", "text/event-stream");
22512
+ c.header("cache-control", "no-cache");
22513
+ return stream(c, async (s) => {
22514
+ while (!s.aborted) {
22515
+ await s.write(`: keepalive
22516
+
22517
+ `);
22518
+ await s.sleep(15000);
22519
+ }
22520
+ });
22521
+ });
22522
+ app.post("/v1/design/*", async (c) => {
22523
+ const profile = resolveProfile(finalConfig.profiles, finalConfig.defaultProfile, c.req.header("x-meridian-profile") || undefined);
22524
+ const url = new URL(c.req.url);
22525
+ const upstreamUrl = `${DESIGN_UPSTREAM_ORIGIN}${url.pathname}${url.search}`;
22526
+ const designToken = await getDesignAccessToken({ store: designTokenStore });
22527
+ const authHeaders = await resolveDesignAuthHeaders({
22528
+ designToken,
22529
+ profile,
22530
+ credentialStore: credentialStoreForProfile(profile),
22531
+ ensureFresh: ensureFreshToken
22532
+ });
22533
+ const body = await c.req.arrayBuffer();
22534
+ const forwardHeaders = buildDesignForwardHeaders((name) => c.req.header(name), authHeaders);
22535
+ let upstreamRes;
22536
+ try {
22537
+ upstreamRes = await fetch(upstreamUrl, { method: "POST", headers: forwardHeaders, body });
22538
+ } catch (err) {
22539
+ return c.json({ type: "error", error: { type: "upstream_error", message: err instanceof Error ? err.message : String(err) } }, 502);
22540
+ }
22541
+ if (isDesignAuthFailure(upstreamRes.status)) {
22542
+ return c.json({ type: "error", error: { type: "auth_error", message: "Unauthorized. Run /design-login to authorize Claude Design (adds user:design:read/write scopes)." } }, 401);
22543
+ }
22544
+ plog(`[PROXY] DESIGN upstream=${upstreamRes.status}`);
22545
+ return new Response(upstreamRes.body, {
22546
+ status: upstreamRes.status,
22547
+ headers: filterUpstreamResponseHeaders(upstreamRes.headers.entries())
22548
+ });
22549
+ });
22150
22550
  app.all("*", (c) => {
22151
22551
  plog(`[PROXY] UNHANDLED ${c.req.method} ${c.req.url}`);
22152
22552
  return c.json({ error: { type: "not_found", message: `Endpoint not supported: ${c.req.method} ${new URL(c.req.url).pathname}` } }, 404);