@rynfar/meridian 1.53.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.
- package/README.md +43 -0
- package/dist/{profileCli-s1h4vh8w.js → cli-jhm27q0x.js} +346 -31
- package/dist/{cli-027as0fx.js → cli-mqht6rs9.js} +450 -57
- package/dist/cli.js +3 -3
- package/dist/grepTool.d.ts +23 -0
- package/dist/grepTool.d.ts.map +1 -0
- package/dist/mcpTools.d.ts.map +1 -1
- package/dist/profileCli-d9tcp916.js +35 -0
- package/dist/proxy/design.d.ts +82 -0
- package/dist/proxy/design.d.ts.map +1 -0
- package/dist/proxy/profileCli.d.ts +48 -0
- package/dist/proxy/profileCli.d.ts.map +1 -0
- package/dist/proxy/server.d.ts.map +1 -1
- package/dist/server.js +2 -2
- package/package.json +1 -1
- package/dist/cli-vjeftz4z.js +0 -329
|
@@ -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-
|
|
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
|
|
1486
|
-
import { homedir as
|
|
1490
|
+
import { join as join7 } from "node:path";
|
|
1491
|
+
import { homedir as homedir6 } from "node:os";
|
|
1487
1492
|
function getConfigPath2() {
|
|
1488
|
-
const dir =
|
|
1493
|
+
const dir = join7(homedir6(), ".config", "meridian");
|
|
1489
1494
|
if (!existsSync7(dir))
|
|
1490
1495
|
mkdirSync3(dir, { recursive: true });
|
|
1491
|
-
return
|
|
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(
|
|
3925
|
-
if (
|
|
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(
|
|
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
|
|
4196
|
-
import { join as
|
|
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
|
|
4656
|
+
import { promisify as promisify3 } from "util";
|
|
4546
4657
|
import { randomUUID } from "crypto";
|
|
4547
4658
|
|
|
4548
4659
|
// src/proxy/passthroughTools.ts
|
|
@@ -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")
|
|
@@ -12307,9 +12629,9 @@ var cherryAdapter = {
|
|
|
12307
12629
|
|
|
12308
12630
|
// src/proxy/adapterInstances.ts
|
|
12309
12631
|
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "node:fs";
|
|
12310
|
-
import { join as
|
|
12311
|
-
import { homedir as
|
|
12312
|
-
var CONFIG_FILE =
|
|
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");
|
|
12313
12635
|
function parseAdapterInstances(raw2) {
|
|
12314
12636
|
if (!raw2)
|
|
12315
12637
|
return {};
|
|
@@ -12478,7 +12800,7 @@ import { createSdkMcpServer as createSdkMcpServer2, tool } from "@anthropic-ai/c
|
|
|
12478
12800
|
import * as fs from "node:fs/promises";
|
|
12479
12801
|
import * as path2 from "node:path";
|
|
12480
12802
|
import { exec } from "node:child_process";
|
|
12481
|
-
import { promisify } from "node:util";
|
|
12803
|
+
import { promisify as promisify2 } from "node:util";
|
|
12482
12804
|
|
|
12483
12805
|
// node_modules/@isaacs/balanced-match/dist/esm/index.js
|
|
12484
12806
|
var balanced = (a, b, str) => {
|
|
@@ -17999,7 +18321,7 @@ function globIterate(pattern, options = {}) {
|
|
|
17999
18321
|
return new Glob(pattern, options).iterate();
|
|
18000
18322
|
}
|
|
18001
18323
|
var streamSync = globStreamSync;
|
|
18002
|
-
var
|
|
18324
|
+
var stream2 = Object.assign(globStream, { sync: globStreamSync });
|
|
18003
18325
|
var iterateSync = globIterateSync;
|
|
18004
18326
|
var iterate = Object.assign(globIterate, {
|
|
18005
18327
|
sync: globIterateSync
|
|
@@ -18013,7 +18335,7 @@ var glob = Object.assign(glob_, {
|
|
|
18013
18335
|
globSync,
|
|
18014
18336
|
sync,
|
|
18015
18337
|
globStream,
|
|
18016
|
-
stream,
|
|
18338
|
+
stream: stream2,
|
|
18017
18339
|
globStreamSync,
|
|
18018
18340
|
streamSync,
|
|
18019
18341
|
globIterate,
|
|
@@ -18027,9 +18349,35 @@ var glob = Object.assign(glob_, {
|
|
|
18027
18349
|
});
|
|
18028
18350
|
glob.glob = glob;
|
|
18029
18351
|
|
|
18030
|
-
// src/
|
|
18031
|
-
|
|
18352
|
+
// src/grepTool.ts
|
|
18353
|
+
import { execFile } from "node:child_process";
|
|
18354
|
+
import { promisify } from "node:util";
|
|
18355
|
+
var execFileAsync = promisify(execFile);
|
|
18032
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();
|
|
18033
18381
|
function createOpencodeMcpServer() {
|
|
18034
18382
|
return createSdkMcpServer2({
|
|
18035
18383
|
name: "opencode",
|
|
@@ -18040,7 +18388,7 @@ function createOpencodeMcpServer() {
|
|
|
18040
18388
|
encoding: exports_external.string().optional().describe("File encoding, defaults to utf-8")
|
|
18041
18389
|
}, async (args) => {
|
|
18042
18390
|
try {
|
|
18043
|
-
const filePath = path2.isAbsolute(args.path) ? args.path : path2.resolve(
|
|
18391
|
+
const filePath = path2.isAbsolute(args.path) ? args.path : path2.resolve(getCwd2(), args.path);
|
|
18044
18392
|
const content = await fs.readFile(filePath, args.encoding || "utf-8");
|
|
18045
18393
|
return {
|
|
18046
18394
|
content: [{ type: "text", text: content }]
|
|
@@ -18057,7 +18405,7 @@ function createOpencodeMcpServer() {
|
|
|
18057
18405
|
content: exports_external.string().describe("Content to write")
|
|
18058
18406
|
}, async (args) => {
|
|
18059
18407
|
try {
|
|
18060
|
-
const filePath = path2.isAbsolute(args.path) ? args.path : path2.resolve(
|
|
18408
|
+
const filePath = path2.isAbsolute(args.path) ? args.path : path2.resolve(getCwd2(), args.path);
|
|
18061
18409
|
await fs.mkdir(path2.dirname(filePath), { recursive: true });
|
|
18062
18410
|
await fs.writeFile(filePath, args.content, "utf-8");
|
|
18063
18411
|
return {
|
|
@@ -18076,7 +18424,7 @@ function createOpencodeMcpServer() {
|
|
|
18076
18424
|
newString: exports_external.string().describe("The replacement text")
|
|
18077
18425
|
}, async (args) => {
|
|
18078
18426
|
try {
|
|
18079
|
-
const filePath = path2.isAbsolute(args.path) ? args.path : path2.resolve(
|
|
18427
|
+
const filePath = path2.isAbsolute(args.path) ? args.path : path2.resolve(getCwd2(), args.path);
|
|
18080
18428
|
const content = await fs.readFile(filePath, "utf-8");
|
|
18081
18429
|
if (!content.includes(args.oldString)) {
|
|
18082
18430
|
return {
|
|
@@ -18102,7 +18450,7 @@ function createOpencodeMcpServer() {
|
|
|
18102
18450
|
}, async (args) => {
|
|
18103
18451
|
try {
|
|
18104
18452
|
const options = {
|
|
18105
|
-
cwd: args.cwd ||
|
|
18453
|
+
cwd: args.cwd || getCwd2(),
|
|
18106
18454
|
timeout: 120000
|
|
18107
18455
|
};
|
|
18108
18456
|
const { stdout, stderr } = await execAsync(args.command, options);
|
|
@@ -18125,7 +18473,7 @@ function createOpencodeMcpServer() {
|
|
|
18125
18473
|
}, async (args) => {
|
|
18126
18474
|
try {
|
|
18127
18475
|
const files = await glob(args.pattern, {
|
|
18128
|
-
cwd: args.cwd ||
|
|
18476
|
+
cwd: args.cwd || getCwd2(),
|
|
18129
18477
|
nodir: true,
|
|
18130
18478
|
ignore: ["**/node_modules/**", "**/.git/**"]
|
|
18131
18479
|
});
|
|
@@ -18144,22 +18492,7 @@ function createOpencodeMcpServer() {
|
|
|
18144
18492
|
pattern: exports_external.string().describe("Regex pattern to search for"),
|
|
18145
18493
|
path: exports_external.string().optional().describe("Directory or file to search in"),
|
|
18146
18494
|
include: exports_external.string().optional().describe("File pattern to include, e.g., *.ts")
|
|
18147
|
-
},
|
|
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
|
-
})
|
|
18495
|
+
}, runGrepTool)
|
|
18163
18496
|
]
|
|
18164
18497
|
});
|
|
18165
18498
|
}
|
|
@@ -18219,7 +18552,7 @@ function buildQueryOptions(ctx, abortController) {
|
|
|
18219
18552
|
systemContext,
|
|
18220
18553
|
claudeExecutable,
|
|
18221
18554
|
passthrough,
|
|
18222
|
-
stream:
|
|
18555
|
+
stream: stream3,
|
|
18223
18556
|
sdkAgents,
|
|
18224
18557
|
passthroughMcp,
|
|
18225
18558
|
cleanEnv,
|
|
@@ -18261,7 +18594,7 @@ function buildQueryOptions(ctx, abortController) {
|
|
|
18261
18594
|
model,
|
|
18262
18595
|
pathToClaudeCodeExecutable: claudeExecutable,
|
|
18263
18596
|
...abortController ? { abortController } : {},
|
|
18264
|
-
...
|
|
18597
|
+
...stream3 ? { includePartialMessages: true } : {},
|
|
18265
18598
|
permissionMode: "bypassPermissions",
|
|
18266
18599
|
allowDangerouslySkipPermissions: true,
|
|
18267
18600
|
...resolveSystemPrompt(systemContext, passthrough, settingSources, codeSystemPrompt, clientSystemPrompt, cwdNote),
|
|
@@ -18401,7 +18734,7 @@ function getAdapterTransforms(adapterName) {
|
|
|
18401
18734
|
|
|
18402
18735
|
// src/proxy/plugins/loader.ts
|
|
18403
18736
|
import { readdirSync as readdirSync2, readFileSync as readFileSync4, existsSync as existsSync5 } from "fs";
|
|
18404
|
-
import { join as
|
|
18737
|
+
import { join as join5, isAbsolute as isAbsolute2, extname } from "path";
|
|
18405
18738
|
import { pathToFileURL } from "url";
|
|
18406
18739
|
|
|
18407
18740
|
// src/proxy/plugins/validation.ts
|
|
@@ -18486,7 +18819,7 @@ async function loadPlugins(pluginDir, configPath) {
|
|
|
18486
18819
|
const loaded = [];
|
|
18487
18820
|
const seenNames = new Set;
|
|
18488
18821
|
for (const { filename, entry } of ordered) {
|
|
18489
|
-
const filePath = isAbsolute2(filename) ? filename :
|
|
18822
|
+
const filePath = isAbsolute2(filename) ? filename : join5(pluginDir, filename);
|
|
18490
18823
|
if (entry && !entry.enabled) {
|
|
18491
18824
|
loaded.push({
|
|
18492
18825
|
name: filename,
|
|
@@ -18868,8 +19201,8 @@ import {
|
|
|
18868
19201
|
unlinkSync,
|
|
18869
19202
|
writeFileSync as writeFileSync2
|
|
18870
19203
|
} from "node:fs";
|
|
18871
|
-
import { homedir as
|
|
18872
|
-
import { join as
|
|
19204
|
+
import { homedir as homedir5 } from "node:os";
|
|
19205
|
+
import { join as join6 } from "node:path";
|
|
18873
19206
|
var DEFAULT_MAX_STORED_SESSIONS = 1e4;
|
|
18874
19207
|
var STALE_LOCK_THRESHOLD_MS = 30000;
|
|
18875
19208
|
function getMaxStoredSessions() {
|
|
@@ -18920,11 +19253,11 @@ function getStorePath() {
|
|
|
18920
19253
|
if (!existsSync6(dir)) {
|
|
18921
19254
|
mkdirSync2(dir, { recursive: true });
|
|
18922
19255
|
}
|
|
18923
|
-
return
|
|
19256
|
+
return join6(dir, "sessions.json");
|
|
18924
19257
|
}
|
|
18925
19258
|
function getDefaultCacheDir() {
|
|
18926
|
-
const newDir =
|
|
18927
|
-
const oldDir =
|
|
19259
|
+
const newDir = join6(homedir5(), ".cache", "meridian");
|
|
19260
|
+
const oldDir = join6(homedir5(), ".cache", "opencode-claude-max-proxy");
|
|
18928
19261
|
if (existsSync6(newDir))
|
|
18929
19262
|
return newDir;
|
|
18930
19263
|
if (existsSync6(oldDir)) {
|
|
@@ -19264,7 +19597,7 @@ function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sd
|
|
|
19264
19597
|
}
|
|
19265
19598
|
|
|
19266
19599
|
// src/proxy/server.ts
|
|
19267
|
-
var exec2 =
|
|
19600
|
+
var exec2 = promisify3(execCallback);
|
|
19268
19601
|
var claudeExecutable = "";
|
|
19269
19602
|
var UPSTREAM_IDLE_MS = 90000;
|
|
19270
19603
|
function credentialStoreForProfile(profile) {
|
|
@@ -19494,8 +19827,8 @@ function createProxyServer(config = {}) {
|
|
|
19494
19827
|
pendingSessionStores.delete(key);
|
|
19495
19828
|
};
|
|
19496
19829
|
};
|
|
19497
|
-
const pluginDir = finalConfig.pluginDir ??
|
|
19498
|
-
const pluginConfigPath = finalConfig.pluginConfigPath ??
|
|
19830
|
+
const pluginDir = finalConfig.pluginDir ?? join8(homedir7(), ".config", "meridian", "plugins");
|
|
19831
|
+
const pluginConfigPath = finalConfig.pluginConfigPath ?? join8(homedir7(), ".config", "meridian", "plugins.json");
|
|
19499
19832
|
let loadedPlugins = [];
|
|
19500
19833
|
let pluginTransforms = [];
|
|
19501
19834
|
const app = new Hono2;
|
|
@@ -19511,6 +19844,7 @@ function createProxyServer(config = {}) {
|
|
|
19511
19844
|
app.use("/plugins", requireAuth);
|
|
19512
19845
|
app.use("/settings/*", requireAuth);
|
|
19513
19846
|
app.use("/settings", requireAuth);
|
|
19847
|
+
app.use("/design-login", requireAuth);
|
|
19514
19848
|
app.use("/auth/*", requireAuth);
|
|
19515
19849
|
app.get("/", (c) => {
|
|
19516
19850
|
const accept = c.req.header("accept") || "";
|
|
@@ -19519,7 +19853,7 @@ function createProxyServer(config = {}) {
|
|
|
19519
19853
|
status: "ok",
|
|
19520
19854
|
service: "meridian",
|
|
19521
19855
|
format: "anthropic",
|
|
19522
|
-
endpoints: ["/v1/messages", "/messages", "/v1/chat/completions", "/v1/responses", "/v1/models", "/telemetry", "/metrics", "/health"]
|
|
19856
|
+
endpoints: ["/v1/messages", "/messages", "/v1/chat/completions", "/v1/responses", "/v1/models", "/v1/design/*", "/design-login", "/telemetry", "/metrics", "/health"]
|
|
19523
19857
|
});
|
|
19524
19858
|
}
|
|
19525
19859
|
return c.html(landingHtml);
|
|
@@ -19632,7 +19966,7 @@ function createProxyServer(config = {}) {
|
|
|
19632
19966
|
stream: body.stream ?? false,
|
|
19633
19967
|
workingDirectory
|
|
19634
19968
|
}), adapterBase);
|
|
19635
|
-
const
|
|
19969
|
+
const stream3 = pipelineCtx.prefersStreaming !== undefined ? pipelineCtx.prefersStreaming : body.stream ?? false;
|
|
19636
19970
|
const effortHeader = c.req.header("x-opencode-effort");
|
|
19637
19971
|
const thinkingHeader = c.req.header("x-opencode-thinking");
|
|
19638
19972
|
const taskBudgetHeader = c.req.header("x-opencode-task-budget");
|
|
@@ -19707,7 +20041,7 @@ function createProxyServer(config = {}) {
|
|
|
19707
20041
|
const lineageType = lineageResult.type === "diverged" && !cachedSession ? "new" : lineageResult.type;
|
|
19708
20042
|
const msgCount = Array.isArray(body.messages) ? body.messages.length : 0;
|
|
19709
20043
|
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=${
|
|
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}`;
|
|
19711
20045
|
plog(`[PROXY] ${requestLogLine} msgs=${msgSummary}`);
|
|
19712
20046
|
diagnosticLog2.session(`${requestLogLine}`, requestMeta.requestId);
|
|
19713
20047
|
if (lineageResult.type === "diverged" && profileSessionId && !isIndependentSession) {
|
|
@@ -19721,7 +20055,7 @@ function createProxyServer(config = {}) {
|
|
|
19721
20055
|
}
|
|
19722
20056
|
claudeLog("request.received", {
|
|
19723
20057
|
model,
|
|
19724
|
-
stream:
|
|
20058
|
+
stream: stream3,
|
|
19725
20059
|
queueWaitMs: requestMeta.queueStartedAt - requestMeta.queueEnteredAt,
|
|
19726
20060
|
messageCount: Array.isArray(body.messages) ? body.messages.length : 0,
|
|
19727
20061
|
hasSystemPrompt: Boolean(body.system)
|
|
@@ -19960,7 +20294,7 @@ function createProxyServer(config = {}) {
|
|
|
19960
20294
|
stderrLines.push(data.trimEnd());
|
|
19961
20295
|
claudeLog("subprocess.stderr", { line: data.trimEnd() });
|
|
19962
20296
|
};
|
|
19963
|
-
if (!
|
|
20297
|
+
if (!stream3) {
|
|
19964
20298
|
const contentBlocks = [];
|
|
19965
20299
|
let assistantMessages = 0;
|
|
19966
20300
|
let hasStructuredOutput = false;
|
|
@@ -22147,6 +22481,65 @@ data: ${JSON.stringify({ response: { id: responseId, status: "failed", error: {
|
|
|
22147
22481
|
} : {}
|
|
22148
22482
|
});
|
|
22149
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
|
+
});
|
|
22150
22543
|
app.all("*", (c) => {
|
|
22151
22544
|
plog(`[PROXY] UNHANDLED ${c.req.method} ${c.req.url}`);
|
|
22152
22545
|
return c.json({ error: { type: "not_found", message: `Endpoint not supported: ${c.req.method} ${new URL(c.req.url).pathname}` } }, 404);
|