@rynfar/meridian 1.52.0 → 1.54.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();
@@ -1534,7 +1539,7 @@ function getExplicitThinking(adapterName) {
1534
1539
  return typeof explicit === "string" && VALID_THINKING_VALUES.has(explicit) ? explicit : undefined;
1535
1540
  }
1536
1541
  function getAllFeatureConfigs() {
1537
- const adapters = ["opencode", "crush", "forgecode", "pi", "droid", "passthrough", "openai"];
1542
+ const adapters = ["opencode", "crush", "forgecode", "pi", "droid", "passthrough", "openai", "codex"];
1538
1543
  const result = {};
1539
1544
  for (const name of adapters) {
1540
1545
  result[name] = getFeaturesForAdapter(name);
@@ -1612,6 +1617,9 @@ var init_sdkFeatures = __esm(() => {
1612
1617
  },
1613
1618
  cherry: {
1614
1619
  codeSystemPrompt: false
1620
+ },
1621
+ codex: {
1622
+ codeSystemPrompt: false
1615
1623
  }
1616
1624
  };
1617
1625
  VALID_CLAUDE_MD_VALUES = new Set(["off", "project", "full"]);
@@ -3638,6 +3646,112 @@ var cors = (options) => {
3638
3646
  };
3639
3647
  };
3640
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
+
3641
3755
  // node_modules/@hono/node-server/dist/index.mjs
3642
3756
  import { createServer as createServerHTTP } from "http";
3643
3757
  import { Http2ServerRequest as Http2ServerRequest2 } from "http2";
@@ -3918,13 +4032,13 @@ function writeFromReadableStreamDefaultReader(reader, writable, currentReadPromi
3918
4032
  }
3919
4033
  }
3920
4034
  }
3921
- function writeFromReadableStream(stream, writable) {
3922
- if (stream.locked) {
4035
+ function writeFromReadableStream(stream2, writable) {
4036
+ if (stream2.locked) {
3923
4037
  throw new TypeError("ReadableStream is locked.");
3924
4038
  } else if (writable.destroyed) {
3925
4039
  return;
3926
4040
  }
3927
- return writeFromReadableStreamDefaultReader(stream.getReader(), writable);
4041
+ return writeFromReadableStreamDefaultReader(stream2.getReader(), writable);
3928
4042
  }
3929
4043
  var buildOutgoingHttpHeaders = (headers) => {
3930
4044
  const res = {};
@@ -4189,8 +4303,8 @@ var serve = (options, listeningListener) => {
4189
4303
  };
4190
4304
 
4191
4305
  // src/proxy/server.ts
4192
- import { homedir as homedir6 } from "node:os";
4193
- import { join as join7 } from "node:path";
4306
+ import { homedir as homedir7 } from "node:os";
4307
+ import { join as join8 } from "node:path";
4194
4308
  import { query } from "@anthropic-ai/claude-agent-sdk";
4195
4309
 
4196
4310
  // src/proxy/rateLimitStore.ts
@@ -4539,7 +4653,7 @@ function createRequestContext(params) {
4539
4653
  }
4540
4654
  // src/proxy/server.ts
4541
4655
  import { exec as execCallback } from "child_process";
4542
- import { promisify as promisify2 } from "util";
4656
+ import { promisify as promisify3 } from "util";
4543
4657
  import { randomUUID } from "crypto";
4544
4658
 
4545
4659
  // src/proxy/passthroughTools.ts
@@ -10271,6 +10385,217 @@ function formatSdkTermination(t, ctx) {
10271
10385
  return `sdk_termination ${parts.join(" ")}`;
10272
10386
  }
10273
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
+
10274
10599
  // src/proxy/openai.ts
10275
10600
  function extractOpenAiContent(content) {
10276
10601
  if (typeof content === "string")
@@ -10738,6 +11063,331 @@ function buildModelList(isMaxSubscription, now = Math.floor(Date.now() / 1000))
10738
11063
  ];
10739
11064
  }
10740
11065
 
11066
+ // src/proxy/openaiResponses.ts
11067
+ function partsToText(content) {
11068
+ if (typeof content === "string")
11069
+ return content;
11070
+ return content.filter((p) => typeof p.text === "string").map((p) => p.text).join("");
11071
+ }
11072
+ function mapToolChoice(tc) {
11073
+ if (tc === "auto")
11074
+ return { type: "auto" };
11075
+ if (tc === "required")
11076
+ return { type: "any" };
11077
+ if (tc && typeof tc === "object") {
11078
+ const o = tc;
11079
+ if (o.type === "function" && o.name)
11080
+ return { type: "tool", name: o.name };
11081
+ if (o.type === "auto")
11082
+ return { type: "auto" };
11083
+ if (o.type === "any" || o.type === "required")
11084
+ return { type: "any" };
11085
+ }
11086
+ return;
11087
+ }
11088
+ function translateResponsesToAnthropic(body) {
11089
+ if (body.input === undefined || body.input === null)
11090
+ return null;
11091
+ const items = typeof body.input === "string" ? [{ type: "message", role: "user", content: [{ type: "input_text", text: body.input }] }] : body.input;
11092
+ const systemParts = [];
11093
+ if (body.instructions)
11094
+ systemParts.push(body.instructions);
11095
+ const messages = [];
11096
+ const pushBlock = (role, block) => {
11097
+ const last = messages[messages.length - 1];
11098
+ if (last && last.role === role && Array.isArray(last.content)) {
11099
+ last.content.push(block);
11100
+ } else {
11101
+ messages.push({ role, content: [block] });
11102
+ }
11103
+ };
11104
+ for (const item of items) {
11105
+ switch (item.type) {
11106
+ case "message": {
11107
+ const msg = item;
11108
+ if (msg.role === "developer" || msg.role === "system") {
11109
+ const t = partsToText(msg.content);
11110
+ if (t)
11111
+ systemParts.push(t);
11112
+ break;
11113
+ }
11114
+ const text = partsToText(msg.content);
11115
+ if (text)
11116
+ pushBlock(msg.role === "assistant" ? "assistant" : "user", { type: "text", text });
11117
+ break;
11118
+ }
11119
+ case "function_call": {
11120
+ const fc = item;
11121
+ let input = {};
11122
+ try {
11123
+ input = fc.arguments ? JSON.parse(fc.arguments) : {};
11124
+ } catch {
11125
+ input = {};
11126
+ }
11127
+ pushBlock("assistant", { type: "tool_use", id: fc.call_id, name: fc.name, input });
11128
+ break;
11129
+ }
11130
+ case "function_call_output": {
11131
+ const fo = item;
11132
+ pushBlock("user", { type: "tool_result", tool_use_id: fo.call_id, content: fo.output });
11133
+ break;
11134
+ }
11135
+ case "reasoning":
11136
+ break;
11137
+ default:
11138
+ break;
11139
+ }
11140
+ }
11141
+ const result = {
11142
+ model: typeof body.model === "string" ? body.model : "",
11143
+ messages,
11144
+ max_tokens: body.max_output_tokens ?? 8192,
11145
+ stream: body.stream ?? false
11146
+ };
11147
+ if (systemParts.length > 0)
11148
+ result.system = systemParts.join(`
11149
+
11150
+ `);
11151
+ if (Array.isArray(body.tools) && body.tools.length > 0) {
11152
+ result.tools = body.tools.filter((t) => t.type === "function").map((t) => ({
11153
+ name: t.name,
11154
+ description: t.description ?? "",
11155
+ input_schema: t.parameters ?? { type: "object", properties: {} }
11156
+ }));
11157
+ }
11158
+ const tc = mapToolChoice(body.tool_choice);
11159
+ if (tc)
11160
+ result.tool_choice = tc;
11161
+ if (body.temperature !== undefined)
11162
+ result.temperature = body.temperature;
11163
+ if (body.top_p !== undefined)
11164
+ result.top_p = body.top_p;
11165
+ if (body.reasoning?.effort !== undefined)
11166
+ result.reasoning_effort = body.reasoning.effort;
11167
+ return result;
11168
+ }
11169
+ function reasoningRequested(body) {
11170
+ if (body.reasoning && typeof body.reasoning === "object")
11171
+ return true;
11172
+ const include = body.include;
11173
+ return Array.isArray(include) && include.some((v) => typeof v === "string" && v.startsWith("reasoning"));
11174
+ }
11175
+ function mapUsage(usage) {
11176
+ const input = usage?.input_tokens ?? 0;
11177
+ const output = usage?.output_tokens ?? 0;
11178
+ return { input_tokens: input, output_tokens: output, total_tokens: input + output };
11179
+ }
11180
+ function translateAnthropicToResponses(res, ctx) {
11181
+ const output = [];
11182
+ const textParts = [];
11183
+ for (const block of res.content ?? []) {
11184
+ if (block.type === "text" && typeof block.text === "string") {
11185
+ textParts.push({ type: "output_text", text: block.text, annotations: [] });
11186
+ } else if (block.type === "tool_use") {
11187
+ output.push({
11188
+ type: "function_call",
11189
+ id: `fc_${block.id}`,
11190
+ call_id: block.id,
11191
+ name: block.name,
11192
+ arguments: JSON.stringify(block.input ?? {}),
11193
+ status: "completed"
11194
+ });
11195
+ }
11196
+ }
11197
+ if (textParts.length > 0) {
11198
+ output.unshift({
11199
+ type: "message",
11200
+ id: `msg_${ctx.responseId}`,
11201
+ status: "completed",
11202
+ role: "assistant",
11203
+ content: textParts
11204
+ });
11205
+ }
11206
+ if (ctx.reasoningRequested) {
11207
+ output.unshift({
11208
+ type: "reasoning",
11209
+ id: `rs_${ctx.responseId}`,
11210
+ summary: [],
11211
+ encrypted_content: null,
11212
+ status: "completed"
11213
+ });
11214
+ }
11215
+ return {
11216
+ id: ctx.responseId,
11217
+ object: "response",
11218
+ created_at: ctx.created,
11219
+ model: ctx.model,
11220
+ status: "completed",
11221
+ output,
11222
+ usage: mapUsage(res.usage),
11223
+ parallel_tool_calls: true,
11224
+ tool_choice: "auto",
11225
+ tools: []
11226
+ };
11227
+ }
11228
+ function createResponsesSseTranslator(ctx) {
11229
+ let seq = 0;
11230
+ let outputIndex = 0;
11231
+ let inputTokens = 0;
11232
+ let outputTokens = 0;
11233
+ let createdEmitted = false;
11234
+ const blocks = new Map;
11235
+ const finalOutput = [];
11236
+ const emit = (event, data) => ({
11237
+ event,
11238
+ data: { type: event, ...data, sequence_number: seq++ }
11239
+ });
11240
+ const responseEnvelope = (status, extra = {}) => ({
11241
+ id: ctx.responseId,
11242
+ object: "response",
11243
+ created_at: ctx.created,
11244
+ model: ctx.model,
11245
+ status,
11246
+ ...extra
11247
+ });
11248
+ return (event) => {
11249
+ const out = [];
11250
+ switch (event.type) {
11251
+ case "message_start": {
11252
+ inputTokens = event.message?.usage?.input_tokens ?? 0;
11253
+ if (!createdEmitted) {
11254
+ createdEmitted = true;
11255
+ out.push(emit("response.created", { response: responseEnvelope("in_progress", { output: [] }) }));
11256
+ out.push(emit("response.in_progress", { response: responseEnvelope("in_progress", { output: [] }) }));
11257
+ if (ctx.reasoningRequested) {
11258
+ const oi = outputIndex++;
11259
+ const itemId = `rs_${ctx.responseId}`;
11260
+ const item = { type: "reasoning", id: itemId, summary: [], encrypted_content: null, status: "completed" };
11261
+ out.push(emit("response.output_item.added", { output_index: oi, item: { ...item, status: "in_progress" } }));
11262
+ finalOutput.push(item);
11263
+ out.push(emit("response.output_item.done", { output_index: oi, item }));
11264
+ }
11265
+ }
11266
+ break;
11267
+ }
11268
+ case "content_block_start": {
11269
+ const idx = event.index ?? 0;
11270
+ const cb = event.content_block ?? {};
11271
+ if (cb.type === "text") {
11272
+ const oi = outputIndex++;
11273
+ const itemId = `msg_${ctx.responseId}_${oi}`;
11274
+ blocks.set(idx, { kind: "text", outputIndex: oi, itemId, text: "", args: "" });
11275
+ out.push(emit("response.output_item.added", {
11276
+ output_index: oi,
11277
+ item: { type: "message", id: itemId, status: "in_progress", role: "assistant", content: [] }
11278
+ }));
11279
+ out.push(emit("response.content_part.added", {
11280
+ item_id: itemId,
11281
+ output_index: oi,
11282
+ content_index: 0,
11283
+ part: { type: "output_text", text: "", annotations: [] }
11284
+ }));
11285
+ } else if (cb.type === "tool_use") {
11286
+ const oi = outputIndex++;
11287
+ const itemId = `fc_${cb.id}`;
11288
+ blocks.set(idx, { kind: "tool", outputIndex: oi, itemId, text: "", args: "", callId: cb.id, name: cb.name });
11289
+ out.push(emit("response.output_item.added", {
11290
+ output_index: oi,
11291
+ item: { type: "function_call", id: itemId, call_id: cb.id, name: cb.name, arguments: "", status: "in_progress" }
11292
+ }));
11293
+ } else {
11294
+ blocks.set(idx, { kind: "skip", outputIndex: -1, itemId: "", text: "", args: "" });
11295
+ }
11296
+ break;
11297
+ }
11298
+ case "content_block_delta": {
11299
+ const idx = event.index ?? 0;
11300
+ const st = blocks.get(idx);
11301
+ if (!st || st.kind === "skip")
11302
+ break;
11303
+ if (st.kind === "text" && event.delta?.type === "text_delta" && typeof event.delta.text === "string") {
11304
+ st.text += event.delta.text;
11305
+ out.push(emit("response.output_text.delta", {
11306
+ item_id: st.itemId,
11307
+ output_index: st.outputIndex,
11308
+ content_index: 0,
11309
+ delta: event.delta.text
11310
+ }));
11311
+ } else if (st.kind === "tool" && event.delta?.type === "input_json_delta" && typeof event.delta.partial_json === "string") {
11312
+ st.args += event.delta.partial_json;
11313
+ out.push(emit("response.function_call_arguments.delta", {
11314
+ item_id: st.itemId,
11315
+ output_index: st.outputIndex,
11316
+ delta: event.delta.partial_json
11317
+ }));
11318
+ }
11319
+ break;
11320
+ }
11321
+ case "content_block_stop": {
11322
+ const idx = event.index ?? 0;
11323
+ const st = blocks.get(idx);
11324
+ if (!st || st.kind === "skip")
11325
+ break;
11326
+ if (st.kind === "text") {
11327
+ out.push(emit("response.output_text.done", {
11328
+ item_id: st.itemId,
11329
+ output_index: st.outputIndex,
11330
+ content_index: 0,
11331
+ text: st.text
11332
+ }));
11333
+ out.push(emit("response.content_part.done", {
11334
+ item_id: st.itemId,
11335
+ output_index: st.outputIndex,
11336
+ content_index: 0,
11337
+ part: { type: "output_text", text: st.text, annotations: [] }
11338
+ }));
11339
+ const item = {
11340
+ type: "message",
11341
+ id: st.itemId,
11342
+ status: "completed",
11343
+ role: "assistant",
11344
+ content: [{ type: "output_text", text: st.text, annotations: [] }]
11345
+ };
11346
+ finalOutput.push(item);
11347
+ out.push(emit("response.output_item.done", { output_index: st.outputIndex, item }));
11348
+ } else if (st.kind === "tool") {
11349
+ out.push(emit("response.function_call_arguments.done", {
11350
+ item_id: st.itemId,
11351
+ output_index: st.outputIndex,
11352
+ arguments: st.args
11353
+ }));
11354
+ const item = {
11355
+ type: "function_call",
11356
+ id: st.itemId,
11357
+ call_id: st.callId,
11358
+ name: st.name,
11359
+ arguments: st.args,
11360
+ status: "completed"
11361
+ };
11362
+ finalOutput.push(item);
11363
+ out.push(emit("response.output_item.done", { output_index: st.outputIndex, item }));
11364
+ }
11365
+ break;
11366
+ }
11367
+ case "message_delta": {
11368
+ if (typeof event.usage?.output_tokens === "number")
11369
+ outputTokens = event.usage.output_tokens;
11370
+ break;
11371
+ }
11372
+ case "message_stop": {
11373
+ out.push(emit("response.completed", {
11374
+ response: responseEnvelope("completed", {
11375
+ output: finalOutput,
11376
+ usage: { input_tokens: inputTokens, output_tokens: outputTokens, total_tokens: inputTokens + outputTokens },
11377
+ parallel_tool_calls: true,
11378
+ tool_choice: "auto",
11379
+ tools: []
11380
+ })
11381
+ }));
11382
+ break;
11383
+ }
11384
+ default:
11385
+ break;
11386
+ }
11387
+ return out;
11388
+ };
11389
+ }
11390
+
10741
11391
  // src/proxy/messages.ts
10742
11392
  function stripCacheControlForHashing(obj) {
10743
11393
  if (!obj || typeof obj !== "object")
@@ -11136,7 +11786,7 @@ init_env();
11136
11786
  var openCodeTransforms = [
11137
11787
  {
11138
11788
  name: "opencode-core",
11139
- adapters: ["opencode", "openai"],
11789
+ adapters: ["opencode", "openai", "codex"],
11140
11790
  onRequest(ctx) {
11141
11791
  const body = ctx.body;
11142
11792
  const blockedTools = BLOCKED_BUILTIN_TOOLS;
@@ -11940,6 +12590,15 @@ var openAiAdapter = {
11940
12590
  name: "openai"
11941
12591
  };
11942
12592
 
12593
+ // src/proxy/adapters/codex.ts
12594
+ var codexAdapter = {
12595
+ ...openCodeAdapter,
12596
+ name: "codex",
12597
+ usesPassthrough() {
12598
+ return true;
12599
+ }
12600
+ };
12601
+
11943
12602
  // src/proxy/adapters/cherry.ts
11944
12603
  var CHERRY_WEB_TOOLS = ["WebSearch", "WebFetch"];
11945
12604
  var isWebTool = (t) => CHERRY_WEB_TOOLS.includes(t);
@@ -11970,9 +12629,9 @@ var cherryAdapter = {
11970
12629
 
11971
12630
  // src/proxy/adapterInstances.ts
11972
12631
  import { existsSync as existsSync4, readFileSync as readFileSync3 } from "node:fs";
11973
- import { join as join3 } from "node:path";
11974
- import { homedir as homedir3 } from "node:os";
11975
- var CONFIG_FILE = join3(homedir3(), ".config", "meridian", "adapter-instances.json");
12632
+ import { join as join4 } from "node:path";
12633
+ import { homedir as homedir4 } from "node:os";
12634
+ var CONFIG_FILE = join4(homedir4(), ".config", "meridian", "adapter-instances.json");
11976
12635
  function parseAdapterInstances(raw2) {
11977
12636
  if (!raw2)
11978
12637
  return {};
@@ -12061,7 +12720,8 @@ var ADAPTER_MAP = {
12061
12720
  claudecode: claudeCodeAdapter,
12062
12721
  cherry: cherryAdapter,
12063
12722
  cherrystudio: cherryAdapter,
12064
- openai: openAiAdapter
12723
+ openai: openAiAdapter,
12724
+ codex: codexAdapter
12065
12725
  };
12066
12726
  var envDefault = process.env.MERIDIAN_DEFAULT_AGENT || "";
12067
12727
  if (envDefault && !ADAPTER_MAP[envDefault]) {
@@ -12140,7 +12800,7 @@ import { createSdkMcpServer as createSdkMcpServer2, tool } from "@anthropic-ai/c
12140
12800
  import * as fs from "node:fs/promises";
12141
12801
  import * as path2 from "node:path";
12142
12802
  import { exec } from "node:child_process";
12143
- import { promisify } from "node:util";
12803
+ import { promisify as promisify2 } from "node:util";
12144
12804
 
12145
12805
  // node_modules/@isaacs/balanced-match/dist/esm/index.js
12146
12806
  var balanced = (a, b, str) => {
@@ -17661,7 +18321,7 @@ function globIterate(pattern, options = {}) {
17661
18321
  return new Glob(pattern, options).iterate();
17662
18322
  }
17663
18323
  var streamSync = globStreamSync;
17664
- var stream = Object.assign(globStream, { sync: globStreamSync });
18324
+ var stream2 = Object.assign(globStream, { sync: globStreamSync });
17665
18325
  var iterateSync = globIterateSync;
17666
18326
  var iterate = Object.assign(globIterate, {
17667
18327
  sync: globIterateSync
@@ -17675,7 +18335,7 @@ var glob = Object.assign(glob_, {
17675
18335
  globSync,
17676
18336
  sync,
17677
18337
  globStream,
17678
- stream,
18338
+ stream: stream2,
17679
18339
  globStreamSync,
17680
18340
  streamSync,
17681
18341
  globIterate,
@@ -17689,9 +18349,35 @@ var glob = Object.assign(glob_, {
17689
18349
  });
17690
18350
  glob.glob = glob;
17691
18351
 
17692
- // src/mcpTools.ts
17693
- var execAsync = promisify(exec);
18352
+ // src/grepTool.ts
18353
+ import { execFile } from "node:child_process";
18354
+ import { promisify } from "node:util";
18355
+ var execFileAsync = promisify(execFile);
17694
18356
  var getCwd = () => process.env.MERIDIAN_WORKDIR ?? process.env.CLAUDE_PROXY_WORKDIR ?? process.cwd();
18357
+ async function runGrepTool(args) {
18358
+ try {
18359
+ const searchPath = args.path || getCwd();
18360
+ const includePattern = args.include || "*";
18361
+ const grepArgs = ["-rn", `--include=${includePattern}`, args.pattern, searchPath];
18362
+ const { stdout } = await execFileAsync("grep", grepArgs, { maxBuffer: 10 * 1024 * 1024 });
18363
+ return {
18364
+ content: [{ type: "text", text: stdout || "(no matches)" }]
18365
+ };
18366
+ } catch (error) {
18367
+ const grepError = error;
18368
+ if (grepError.code === 1) {
18369
+ return { content: [{ type: "text", text: grepError.stdout || "(no matches)" }] };
18370
+ }
18371
+ return {
18372
+ content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }],
18373
+ isError: true
18374
+ };
18375
+ }
18376
+ }
18377
+
18378
+ // src/mcpTools.ts
18379
+ var execAsync = promisify2(exec);
18380
+ var getCwd2 = () => process.env.MERIDIAN_WORKDIR ?? process.env.CLAUDE_PROXY_WORKDIR ?? process.cwd();
17695
18381
  function createOpencodeMcpServer() {
17696
18382
  return createSdkMcpServer2({
17697
18383
  name: "opencode",
@@ -17702,7 +18388,7 @@ function createOpencodeMcpServer() {
17702
18388
  encoding: exports_external.string().optional().describe("File encoding, defaults to utf-8")
17703
18389
  }, async (args) => {
17704
18390
  try {
17705
- const filePath = path2.isAbsolute(args.path) ? args.path : path2.resolve(getCwd(), args.path);
18391
+ const filePath = path2.isAbsolute(args.path) ? args.path : path2.resolve(getCwd2(), args.path);
17706
18392
  const content = await fs.readFile(filePath, args.encoding || "utf-8");
17707
18393
  return {
17708
18394
  content: [{ type: "text", text: content }]
@@ -17719,7 +18405,7 @@ function createOpencodeMcpServer() {
17719
18405
  content: exports_external.string().describe("Content to write")
17720
18406
  }, async (args) => {
17721
18407
  try {
17722
- const filePath = path2.isAbsolute(args.path) ? args.path : path2.resolve(getCwd(), args.path);
18408
+ const filePath = path2.isAbsolute(args.path) ? args.path : path2.resolve(getCwd2(), args.path);
17723
18409
  await fs.mkdir(path2.dirname(filePath), { recursive: true });
17724
18410
  await fs.writeFile(filePath, args.content, "utf-8");
17725
18411
  return {
@@ -17738,7 +18424,7 @@ function createOpencodeMcpServer() {
17738
18424
  newString: exports_external.string().describe("The replacement text")
17739
18425
  }, async (args) => {
17740
18426
  try {
17741
- const filePath = path2.isAbsolute(args.path) ? args.path : path2.resolve(getCwd(), args.path);
18427
+ const filePath = path2.isAbsolute(args.path) ? args.path : path2.resolve(getCwd2(), args.path);
17742
18428
  const content = await fs.readFile(filePath, "utf-8");
17743
18429
  if (!content.includes(args.oldString)) {
17744
18430
  return {
@@ -17764,7 +18450,7 @@ function createOpencodeMcpServer() {
17764
18450
  }, async (args) => {
17765
18451
  try {
17766
18452
  const options = {
17767
- cwd: args.cwd || getCwd(),
18453
+ cwd: args.cwd || getCwd2(),
17768
18454
  timeout: 120000
17769
18455
  };
17770
18456
  const { stdout, stderr } = await execAsync(args.command, options);
@@ -17787,7 +18473,7 @@ function createOpencodeMcpServer() {
17787
18473
  }, async (args) => {
17788
18474
  try {
17789
18475
  const files = await glob(args.pattern, {
17790
- cwd: args.cwd || getCwd(),
18476
+ cwd: args.cwd || getCwd2(),
17791
18477
  nodir: true,
17792
18478
  ignore: ["**/node_modules/**", "**/.git/**"]
17793
18479
  });
@@ -17806,22 +18492,7 @@ function createOpencodeMcpServer() {
17806
18492
  pattern: exports_external.string().describe("Regex pattern to search for"),
17807
18493
  path: exports_external.string().optional().describe("Directory or file to search in"),
17808
18494
  include: exports_external.string().optional().describe("File pattern to include, e.g., *.ts")
17809
- }, async (args) => {
17810
- try {
17811
- const searchPath = args.path || getCwd();
17812
- const includePattern = args.include || "*";
17813
- let cmd = `grep -rn --include="${includePattern}" "${args.pattern}" "${searchPath}" 2>/dev/null || true`;
17814
- const { stdout } = await execAsync(cmd, { maxBuffer: 10 * 1024 * 1024 });
17815
- return {
17816
- content: [{ type: "text", text: stdout || "(no matches)" }]
17817
- };
17818
- } catch (error) {
17819
- return {
17820
- content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }],
17821
- isError: true
17822
- };
17823
- }
17824
- })
18495
+ }, runGrepTool)
17825
18496
  ]
17826
18497
  });
17827
18498
  }
@@ -17881,7 +18552,7 @@ function buildQueryOptions(ctx, abortController) {
17881
18552
  systemContext,
17882
18553
  claudeExecutable,
17883
18554
  passthrough,
17884
- stream: stream2,
18555
+ stream: stream3,
17885
18556
  sdkAgents,
17886
18557
  passthroughMcp,
17887
18558
  cleanEnv,
@@ -17923,7 +18594,7 @@ function buildQueryOptions(ctx, abortController) {
17923
18594
  model,
17924
18595
  pathToClaudeCodeExecutable: claudeExecutable,
17925
18596
  ...abortController ? { abortController } : {},
17926
- ...stream2 ? { includePartialMessages: true } : {},
18597
+ ...stream3 ? { includePartialMessages: true } : {},
17927
18598
  permissionMode: "bypassPermissions",
17928
18599
  allowDangerouslySkipPermissions: true,
17929
18600
  ...resolveSystemPrompt(systemContext, passthrough, settingSources, codeSystemPrompt, clientSystemPrompt, cwdNote),
@@ -18034,6 +18705,17 @@ var cherryTransforms = [
18034
18705
  }
18035
18706
  ];
18036
18707
 
18708
+ // src/proxy/transforms/codex.ts
18709
+ var codexTransforms = [
18710
+ {
18711
+ name: "codex-force-passthrough",
18712
+ adapters: ["codex"],
18713
+ onRequest(ctx) {
18714
+ return { ...ctx, passthrough: true };
18715
+ }
18716
+ }
18717
+ ];
18718
+
18037
18719
  // src/proxy/transforms/registry.ts
18038
18720
  var ADAPTER_TRANSFORMS = {
18039
18721
  opencode: openCodeTransforms,
@@ -18043,7 +18725,8 @@ var ADAPTER_TRANSFORMS = {
18043
18725
  forgecode: forgeCodeTransforms,
18044
18726
  passthrough: passthroughTransforms,
18045
18727
  cherry: cherryTransforms,
18046
- openai: openCodeTransforms
18728
+ openai: openCodeTransforms,
18729
+ codex: [...openCodeTransforms, ...codexTransforms]
18047
18730
  };
18048
18731
  function getAdapterTransforms(adapterName) {
18049
18732
  return ADAPTER_TRANSFORMS[adapterName] ?? [];
@@ -18051,7 +18734,7 @@ function getAdapterTransforms(adapterName) {
18051
18734
 
18052
18735
  // src/proxy/plugins/loader.ts
18053
18736
  import { readdirSync as readdirSync2, readFileSync as readFileSync4, existsSync as existsSync5 } from "fs";
18054
- import { join as join4, isAbsolute as isAbsolute2, extname } from "path";
18737
+ import { join as join5, isAbsolute as isAbsolute2, extname } from "path";
18055
18738
  import { pathToFileURL } from "url";
18056
18739
 
18057
18740
  // src/proxy/plugins/validation.ts
@@ -18136,7 +18819,7 @@ async function loadPlugins(pluginDir, configPath) {
18136
18819
  const loaded = [];
18137
18820
  const seenNames = new Set;
18138
18821
  for (const { filename, entry } of ordered) {
18139
- const filePath = isAbsolute2(filename) ? filename : join4(pluginDir, filename);
18822
+ const filePath = isAbsolute2(filename) ? filename : join5(pluginDir, filename);
18140
18823
  if (entry && !entry.enabled) {
18141
18824
  loaded.push({
18142
18825
  name: filename,
@@ -18518,8 +19201,8 @@ import {
18518
19201
  unlinkSync,
18519
19202
  writeFileSync as writeFileSync2
18520
19203
  } from "node:fs";
18521
- import { homedir as homedir4 } from "node:os";
18522
- import { join as join5 } from "node:path";
19204
+ import { homedir as homedir5 } from "node:os";
19205
+ import { join as join6 } from "node:path";
18523
19206
  var DEFAULT_MAX_STORED_SESSIONS = 1e4;
18524
19207
  var STALE_LOCK_THRESHOLD_MS = 30000;
18525
19208
  function getMaxStoredSessions() {
@@ -18570,11 +19253,11 @@ function getStorePath() {
18570
19253
  if (!existsSync6(dir)) {
18571
19254
  mkdirSync2(dir, { recursive: true });
18572
19255
  }
18573
- return join5(dir, "sessions.json");
19256
+ return join6(dir, "sessions.json");
18574
19257
  }
18575
19258
  function getDefaultCacheDir() {
18576
- const newDir = join5(homedir4(), ".cache", "meridian");
18577
- const oldDir = join5(homedir4(), ".cache", "opencode-claude-max-proxy");
19259
+ const newDir = join6(homedir5(), ".cache", "meridian");
19260
+ const oldDir = join6(homedir5(), ".cache", "opencode-claude-max-proxy");
18578
19261
  if (existsSync6(newDir))
18579
19262
  return newDir;
18580
19263
  if (existsSync6(oldDir)) {
@@ -18914,7 +19597,7 @@ function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sd
18914
19597
  }
18915
19598
 
18916
19599
  // src/proxy/server.ts
18917
- var exec2 = promisify2(execCallback);
19600
+ var exec2 = promisify3(execCallback);
18918
19601
  var claudeExecutable = "";
18919
19602
  var UPSTREAM_IDLE_MS = 90000;
18920
19603
  function credentialStoreForProfile(profile) {
@@ -19144,8 +19827,8 @@ function createProxyServer(config = {}) {
19144
19827
  pendingSessionStores.delete(key);
19145
19828
  };
19146
19829
  };
19147
- const pluginDir = finalConfig.pluginDir ?? join7(homedir6(), ".config", "meridian", "plugins");
19148
- const pluginConfigPath = finalConfig.pluginConfigPath ?? join7(homedir6(), ".config", "meridian", "plugins.json");
19830
+ const pluginDir = finalConfig.pluginDir ?? join8(homedir7(), ".config", "meridian", "plugins");
19831
+ const pluginConfigPath = finalConfig.pluginConfigPath ?? join8(homedir7(), ".config", "meridian", "plugins.json");
19149
19832
  let loadedPlugins = [];
19150
19833
  let pluginTransforms = [];
19151
19834
  const app = new Hono2;
@@ -19161,6 +19844,7 @@ function createProxyServer(config = {}) {
19161
19844
  app.use("/plugins", requireAuth);
19162
19845
  app.use("/settings/*", requireAuth);
19163
19846
  app.use("/settings", requireAuth);
19847
+ app.use("/design-login", requireAuth);
19164
19848
  app.use("/auth/*", requireAuth);
19165
19849
  app.get("/", (c) => {
19166
19850
  const accept = c.req.header("accept") || "";
@@ -19169,7 +19853,7 @@ function createProxyServer(config = {}) {
19169
19853
  status: "ok",
19170
19854
  service: "meridian",
19171
19855
  format: "anthropic",
19172
- endpoints: ["/v1/messages", "/messages", "/v1/chat/completions", "/v1/models", "/telemetry", "/metrics", "/health"]
19856
+ endpoints: ["/v1/messages", "/messages", "/v1/chat/completions", "/v1/responses", "/v1/models", "/v1/design/*", "/design-login", "/telemetry", "/metrics", "/health"]
19173
19857
  });
19174
19858
  }
19175
19859
  return c.html(landingHtml);
@@ -19282,7 +19966,7 @@ function createProxyServer(config = {}) {
19282
19966
  stream: body.stream ?? false,
19283
19967
  workingDirectory
19284
19968
  }), adapterBase);
19285
- const stream2 = pipelineCtx.prefersStreaming !== undefined ? pipelineCtx.prefersStreaming : body.stream ?? false;
19969
+ const stream3 = pipelineCtx.prefersStreaming !== undefined ? pipelineCtx.prefersStreaming : body.stream ?? false;
19286
19970
  const effortHeader = c.req.header("x-opencode-effort");
19287
19971
  const thinkingHeader = c.req.header("x-opencode-thinking");
19288
19972
  const taskBudgetHeader = c.req.header("x-opencode-task-budget");
@@ -19357,7 +20041,7 @@ function createProxyServer(config = {}) {
19357
20041
  const lineageType = lineageResult.type === "diverged" && !cachedSession ? "new" : lineageResult.type;
19358
20042
  const msgCount = Array.isArray(body.messages) ? body.messages.length : 0;
19359
20043
  const toolCount = body.tools?.length ?? 0;
19360
- 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}`;
20044
+ 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}`;
19361
20045
  plog(`[PROXY] ${requestLogLine} msgs=${msgSummary}`);
19362
20046
  diagnosticLog2.session(`${requestLogLine}`, requestMeta.requestId);
19363
20047
  if (lineageResult.type === "diverged" && profileSessionId && !isIndependentSession) {
@@ -19371,7 +20055,7 @@ function createProxyServer(config = {}) {
19371
20055
  }
19372
20056
  claudeLog("request.received", {
19373
20057
  model,
19374
- stream: stream2,
20058
+ stream: stream3,
19375
20059
  queueWaitMs: requestMeta.queueStartedAt - requestMeta.queueEnteredAt,
19376
20060
  messageCount: Array.isArray(body.messages) ? body.messages.length : 0,
19377
20061
  hasSystemPrompt: Boolean(body.system)
@@ -19610,7 +20294,7 @@ function createProxyServer(config = {}) {
19610
20294
  stderrLines.push(data.trimEnd());
19611
20295
  claudeLog("subprocess.stderr", { line: data.trimEnd() });
19612
20296
  };
19613
- if (!stream2) {
20297
+ if (!stream3) {
19614
20298
  const contentBlocks = [];
19615
20299
  let assistantMessages = 0;
19616
20300
  let hasStructuredOutput = false;
@@ -21534,6 +22218,107 @@ data: ${JSON.stringify({
21534
22218
  }
21535
22219
  });
21536
22220
  });
22221
+ app.post("/v1/responses", async (c) => {
22222
+ const rawBody = await c.req.json();
22223
+ const anthropicBody = translateResponsesToAnthropic(rawBody);
22224
+ if (!anthropicBody) {
22225
+ return c.json({ error: { type: "invalid_request_error", message: "input: Field required", code: null } }, 400);
22226
+ }
22227
+ if (!anthropicBody.model) {
22228
+ return c.json({ error: { type: "invalid_request_error", message: "model: Field required", code: null } }, 400);
22229
+ }
22230
+ const internalHeaders = {
22231
+ "Content-Type": "application/json",
22232
+ "x-meridian-agent": "codex"
22233
+ };
22234
+ const xApiKey = c.req.header("x-api-key");
22235
+ if (xApiKey)
22236
+ internalHeaders["x-api-key"] = xApiKey;
22237
+ const authz = c.req.header("authorization");
22238
+ if (authz)
22239
+ internalHeaders["authorization"] = authz;
22240
+ const xProfile = c.req.header("x-meridian-profile");
22241
+ if (xProfile)
22242
+ internalHeaders["x-meridian-profile"] = xProfile;
22243
+ const internalReq = new Request("http://internal/v1/messages", {
22244
+ method: "POST",
22245
+ headers: internalHeaders,
22246
+ body: JSON.stringify(anthropicBody)
22247
+ });
22248
+ const internalRes = await app.fetch(internalReq);
22249
+ if (!internalRes.ok) {
22250
+ const errBody = await internalRes.text();
22251
+ return c.json({ error: { type: "upstream_error", message: errBody, code: null } }, internalRes.status);
22252
+ }
22253
+ const responseId = `resp_${randomUUID().replace(/-/g, "")}`;
22254
+ const created = Math.floor(Date.now() / 1000);
22255
+ const model = typeof rawBody.model === "string" && rawBody.model ? rawBody.model : CANONICAL_SONNET_MODEL;
22256
+ const ctx = { responseId, model, created, reasoningRequested: reasoningRequested(rawBody) };
22257
+ if (!anthropicBody.stream) {
22258
+ const anthropicRes = await internalRes.json();
22259
+ return c.json(translateAnthropicToResponses(anthropicRes, ctx));
22260
+ }
22261
+ const encoder = new TextEncoder;
22262
+ const readable = new ReadableStream({
22263
+ async start(controller) {
22264
+ const reader = internalRes.body?.getReader();
22265
+ if (!reader) {
22266
+ controller.close();
22267
+ return;
22268
+ }
22269
+ const decoder = new TextDecoder;
22270
+ let buffer = "";
22271
+ const translate = createResponsesSseTranslator(ctx);
22272
+ try {
22273
+ while (true) {
22274
+ const { done, value } = await reader.read();
22275
+ if (done)
22276
+ break;
22277
+ buffer += decoder.decode(value, { stream: true });
22278
+ const lines = buffer.split(`
22279
+ `);
22280
+ buffer = lines.pop() ?? "";
22281
+ for (const line of lines) {
22282
+ if (!line.startsWith("data: "))
22283
+ continue;
22284
+ const dataStr = line.slice(6).trim();
22285
+ if (!dataStr)
22286
+ continue;
22287
+ let event;
22288
+ try {
22289
+ event = JSON.parse(dataStr);
22290
+ } catch {
22291
+ continue;
22292
+ }
22293
+ if (typeof event.type !== "string")
22294
+ continue;
22295
+ for (const emission of translate(event)) {
22296
+ controller.enqueue(encoder.encode(`event: ${emission.event}
22297
+ data: ${JSON.stringify(emission.data)}
22298
+
22299
+ `));
22300
+ }
22301
+ }
22302
+ }
22303
+ } catch (err) {
22304
+ const message = err instanceof Error ? err.message : String(err);
22305
+ controller.enqueue(encoder.encode(`event: response.failed
22306
+ data: ${JSON.stringify({ response: { id: responseId, status: "failed", error: { message } } })}
22307
+
22308
+ `));
22309
+ } finally {
22310
+ controller.close();
22311
+ }
22312
+ }
22313
+ });
22314
+ return new Response(readable, {
22315
+ headers: {
22316
+ "Content-Type": "text/event-stream",
22317
+ "Cache-Control": "no-cache",
22318
+ Connection: "keep-alive"
22319
+ }
22320
+ });
22321
+ });
21537
22322
  app.get("/v1/models", async (c) => {
21538
22323
  const authStatus = await getClaudeAuthStatusAsync();
21539
22324
  const isMax = authStatus?.subscriptionType === "max";
@@ -21696,6 +22481,65 @@ data: ${JSON.stringify({
21696
22481
  } : {}
21697
22482
  });
21698
22483
  });
22484
+ const designTokenStore = createFileDesignTokenStore();
22485
+ const designLogin = createDesignLogin({ store: designTokenStore });
22486
+ app.get("/design-login", (c) => c.json(designLogin.start()));
22487
+ app.post("/design-login", async (c) => {
22488
+ let body;
22489
+ try {
22490
+ body = await c.req.json();
22491
+ } catch {
22492
+ return c.json({ type: "error", error: { type: "invalid_request", message: "Request body must be JSON with a 'code' field." } }, 400);
22493
+ }
22494
+ const result = await designLogin.exchange(body);
22495
+ if (result.status === 200)
22496
+ plog(`[PROXY] Design token stored via /design-login`);
22497
+ return new Response(JSON.stringify(result.body), {
22498
+ status: result.status,
22499
+ headers: { "content-type": "application/json" }
22500
+ });
22501
+ });
22502
+ app.get("/v1/design/*", (c) => {
22503
+ c.status(200);
22504
+ c.header("content-type", "text/event-stream");
22505
+ c.header("cache-control", "no-cache");
22506
+ return stream(c, async (s) => {
22507
+ while (!s.aborted) {
22508
+ await s.write(`: keepalive
22509
+
22510
+ `);
22511
+ await s.sleep(15000);
22512
+ }
22513
+ });
22514
+ });
22515
+ app.post("/v1/design/*", async (c) => {
22516
+ const profile = resolveProfile(finalConfig.profiles, finalConfig.defaultProfile, c.req.header("x-meridian-profile") || undefined);
22517
+ const url = new URL(c.req.url);
22518
+ const upstreamUrl = `${DESIGN_UPSTREAM_ORIGIN}${url.pathname}${url.search}`;
22519
+ const designToken = await getDesignAccessToken({ store: designTokenStore });
22520
+ const authHeaders = await resolveDesignAuthHeaders({
22521
+ designToken,
22522
+ profile,
22523
+ credentialStore: credentialStoreForProfile(profile),
22524
+ ensureFresh: ensureFreshToken
22525
+ });
22526
+ const body = await c.req.arrayBuffer();
22527
+ const forwardHeaders = buildDesignForwardHeaders((name) => c.req.header(name), authHeaders);
22528
+ let upstreamRes;
22529
+ try {
22530
+ upstreamRes = await fetch(upstreamUrl, { method: "POST", headers: forwardHeaders, body });
22531
+ } catch (err) {
22532
+ return c.json({ type: "error", error: { type: "upstream_error", message: err instanceof Error ? err.message : String(err) } }, 502);
22533
+ }
22534
+ if (isDesignAuthFailure(upstreamRes.status)) {
22535
+ 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);
22536
+ }
22537
+ plog(`[PROXY] DESIGN upstream=${upstreamRes.status}`);
22538
+ return new Response(upstreamRes.body, {
22539
+ status: upstreamRes.status,
22540
+ headers: filterUpstreamResponseHeaders(upstreamRes.headers.entries())
22541
+ });
22542
+ });
21699
22543
  app.all("*", (c) => {
21700
22544
  plog(`[PROXY] UNHANDLED ${c.req.method} ${c.req.url}`);
21701
22545
  return c.json({ error: { type: "not_found", message: `Endpoint not supported: ${c.req.method} ${new URL(c.req.url).pathname}` } }, 404);