@rynfar/meridian 1.24.1 → 1.25.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +128 -184
- package/dist/cli-a05ws7rb.js +18 -0
- package/dist/cli-m9pfb7h9.js +203 -0
- package/dist/cli-rtab0qa6.js +67 -0
- package/dist/{cli-bjpad5x9.js → cli-s6f9jefk.js} +177 -124
- package/dist/cli.js +56 -3
- package/dist/proxy/adapter.d.ts +1 -1
- package/dist/proxy/errors.d.ts +16 -0
- package/dist/proxy/errors.d.ts.map +1 -1
- package/dist/proxy/fileChanges.d.ts.map +1 -1
- package/dist/proxy/models.d.ts +17 -1
- package/dist/proxy/models.d.ts.map +1 -1
- package/dist/proxy/server.d.ts.map +1 -1
- package/dist/proxy/setup.d.ts +42 -0
- package/dist/proxy/setup.d.ts.map +1 -0
- package/dist/proxy/tokenRefresh.d.ts +51 -0
- package/dist/proxy/tokenRefresh.d.ts.map +1 -0
- package/dist/server.js +4 -1
- package/dist/setup-5x116vbs.js +13 -0
- package/dist/telemetry/dashboard.d.ts +1 -1
- package/dist/telemetry/dashboard.d.ts.map +1 -1
- package/dist/telemetry/routes.d.ts.map +1 -1
- package/dist/tokenRefresh-ywwpe8k2.js +11 -0
- package/package.json +76 -75
- package/plugin/meridian.ts +54 -0
|
@@ -1,19 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
set: __exportSetter.bind(all, name)
|
|
14
|
-
});
|
|
15
|
-
};
|
|
16
|
-
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
1
|
+
import {
|
|
2
|
+
checkPluginConfigured
|
|
3
|
+
} from "./cli-rtab0qa6.js";
|
|
4
|
+
import {
|
|
5
|
+
claudeLog,
|
|
6
|
+
refreshOAuthToken,
|
|
7
|
+
withClaudeLogContext
|
|
8
|
+
} from "./cli-m9pfb7h9.js";
|
|
9
|
+
import {
|
|
10
|
+
__export,
|
|
11
|
+
__require
|
|
12
|
+
} from "./cli-a05ws7rb.js";
|
|
17
13
|
|
|
18
14
|
// node_modules/hono/dist/compose.js
|
|
19
15
|
var compose = (middleware, onError, onNotFound) => {
|
|
@@ -2179,68 +2175,6 @@ var DEFAULT_PROXY_CONFIG = {
|
|
|
2179
2175
|
silent: false
|
|
2180
2176
|
};
|
|
2181
2177
|
|
|
2182
|
-
// src/logger.ts
|
|
2183
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2184
|
-
var contextStore = new AsyncLocalStorage;
|
|
2185
|
-
var shouldLog = () => process.env["OPENCODE_CLAUDE_PROVIDER_DEBUG"];
|
|
2186
|
-
var shouldLogStreamDebug = () => process.env["OPENCODE_CLAUDE_PROVIDER_STREAM_DEBUG"];
|
|
2187
|
-
var isVerboseStreamEvent = (event) => {
|
|
2188
|
-
return event.startsWith("stream.") || event === "response.empty_stream";
|
|
2189
|
-
};
|
|
2190
|
-
var REDACTED_KEYS = new Set([
|
|
2191
|
-
"authorization",
|
|
2192
|
-
"cookie",
|
|
2193
|
-
"x-api-key",
|
|
2194
|
-
"apiKey",
|
|
2195
|
-
"apikey",
|
|
2196
|
-
"prompt",
|
|
2197
|
-
"messages",
|
|
2198
|
-
"content"
|
|
2199
|
-
]);
|
|
2200
|
-
var sanitize = (value) => {
|
|
2201
|
-
if (value === null || value === undefined)
|
|
2202
|
-
return value;
|
|
2203
|
-
if (typeof value === "string") {
|
|
2204
|
-
if (value.length > 512) {
|
|
2205
|
-
return `${value.slice(0, 512)}... [truncated=${value.length}]`;
|
|
2206
|
-
}
|
|
2207
|
-
return value;
|
|
2208
|
-
}
|
|
2209
|
-
if (Array.isArray(value)) {
|
|
2210
|
-
return value.map(sanitize);
|
|
2211
|
-
}
|
|
2212
|
-
if (typeof value === "object") {
|
|
2213
|
-
const out = {};
|
|
2214
|
-
for (const [k, v] of Object.entries(value)) {
|
|
2215
|
-
if (REDACTED_KEYS.has(k)) {
|
|
2216
|
-
if (typeof v === "string") {
|
|
2217
|
-
out[k] = `[redacted len=${v.length}]`;
|
|
2218
|
-
} else if (Array.isArray(v)) {
|
|
2219
|
-
out[k] = `[redacted array len=${v.length}]`;
|
|
2220
|
-
} else {
|
|
2221
|
-
out[k] = "[redacted]";
|
|
2222
|
-
}
|
|
2223
|
-
} else {
|
|
2224
|
-
out[k] = sanitize(v);
|
|
2225
|
-
}
|
|
2226
|
-
}
|
|
2227
|
-
return out;
|
|
2228
|
-
}
|
|
2229
|
-
return value;
|
|
2230
|
-
};
|
|
2231
|
-
var withClaudeLogContext = (context, fn) => {
|
|
2232
|
-
return contextStore.run(context, fn);
|
|
2233
|
-
};
|
|
2234
|
-
var claudeLog = (event, extra) => {
|
|
2235
|
-
if (!shouldLog())
|
|
2236
|
-
return;
|
|
2237
|
-
if (isVerboseStreamEvent(event) && !shouldLogStreamDebug())
|
|
2238
|
-
return;
|
|
2239
|
-
const context = contextStore.getStore() || {};
|
|
2240
|
-
const payload = sanitize({ ts: new Date().toISOString(), event, ...context, ...extra || {} });
|
|
2241
|
-
console.debug(`[opencode-claude-code-provider] ${JSON.stringify(payload)}`);
|
|
2242
|
-
};
|
|
2243
|
-
|
|
2244
2178
|
// src/proxy/server.ts
|
|
2245
2179
|
import { exec as execCallback2 } from "child_process";
|
|
2246
2180
|
import { promisify as promisify3 } from "util";
|
|
@@ -6455,6 +6389,11 @@ class DiagnosticLogStore {
|
|
|
6455
6389
|
}
|
|
6456
6390
|
}
|
|
6457
6391
|
var diagnosticLog = new DiagnosticLogStore;
|
|
6392
|
+
// src/telemetry/routes.ts
|
|
6393
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
6394
|
+
import { resolve, dirname } from "node:path";
|
|
6395
|
+
import { fileURLToPath } from "node:url";
|
|
6396
|
+
|
|
6458
6397
|
// src/telemetry/dashboard.ts
|
|
6459
6398
|
var dashboardHtml = `<!DOCTYPE html>
|
|
6460
6399
|
<html lang="en">
|
|
@@ -6462,6 +6401,7 @@ var dashboardHtml = `<!DOCTYPE html>
|
|
|
6462
6401
|
<meta charset="utf-8">
|
|
6463
6402
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6464
6403
|
<title>Meridian — Telemetry</title>
|
|
6404
|
+
<link rel="icon" type="image/svg+xml" href="/telemetry/icon.svg">
|
|
6465
6405
|
<style>
|
|
6466
6406
|
:root {
|
|
6467
6407
|
--bg: #0d1117; --surface: #161b22; --border: #30363d;
|
|
@@ -6783,11 +6723,21 @@ timer = setInterval(refresh, 5000);
|
|
|
6783
6723
|
</html>`;
|
|
6784
6724
|
|
|
6785
6725
|
// src/telemetry/routes.ts
|
|
6726
|
+
var _iconPath = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "assets", "icon.svg");
|
|
6727
|
+
var _iconSvg = existsSync(_iconPath) ? readFileSync(_iconPath, "utf-8") : null;
|
|
6786
6728
|
function createTelemetryRoutes() {
|
|
6787
6729
|
const routes = new Hono2;
|
|
6788
6730
|
routes.get("/", (c) => {
|
|
6789
6731
|
return c.html(dashboardHtml);
|
|
6790
6732
|
});
|
|
6733
|
+
routes.get("/icon.svg", (c) => {
|
|
6734
|
+
if (!_iconSvg)
|
|
6735
|
+
return c.notFound();
|
|
6736
|
+
return c.body(_iconSvg, 200, {
|
|
6737
|
+
"Content-Type": "image/svg+xml",
|
|
6738
|
+
"Cache-Control": "public, max-age=3600"
|
|
6739
|
+
});
|
|
6740
|
+
});
|
|
6791
6741
|
routes.get("/requests", (c) => {
|
|
6792
6742
|
const limit = Number.parseInt(c.req.query("limit") || "50", 10);
|
|
6793
6743
|
const since = c.req.query("since") ? Number.parseInt(c.req.query("since"), 10) : undefined;
|
|
@@ -6940,6 +6890,13 @@ refresh();setInterval(refresh,10000);
|
|
|
6940
6890
|
// src/proxy/errors.ts
|
|
6941
6891
|
function classifyError(errMsg) {
|
|
6942
6892
|
const lower = errMsg.toLowerCase();
|
|
6893
|
+
if (lower.includes("oauth token has expired") || lower.includes("not logged in")) {
|
|
6894
|
+
return {
|
|
6895
|
+
status: 401,
|
|
6896
|
+
type: "authentication_error",
|
|
6897
|
+
message: "Claude OAuth token has expired and could not be refreshed automatically. Run 'claude login' in your terminal to re-authenticate."
|
|
6898
|
+
};
|
|
6899
|
+
}
|
|
6943
6900
|
if (lower.includes("401") || lower.includes("authentication") || lower.includes("invalid auth") || lower.includes("credentials")) {
|
|
6944
6901
|
return {
|
|
6945
6902
|
status: 401,
|
|
@@ -6948,10 +6905,11 @@ function classifyError(errMsg) {
|
|
|
6948
6905
|
};
|
|
6949
6906
|
}
|
|
6950
6907
|
if (lower.includes("429") || lower.includes("rate limit") || lower.includes("too many requests")) {
|
|
6908
|
+
const hint = lower.includes("1m") || lower.includes("context") ? " If you're frequently hitting this, set MERIDIAN_SONNET_MODEL=sonnet to use the 200k model instead." : "";
|
|
6951
6909
|
return {
|
|
6952
6910
|
status: 429,
|
|
6953
6911
|
type: "rate_limit_error",
|
|
6954
|
-
message:
|
|
6912
|
+
message: `Claude Max rate limit reached. Wait a moment and try again.${hint}`
|
|
6955
6913
|
};
|
|
6956
6914
|
}
|
|
6957
6915
|
if (lower.includes("402") || lower.includes("billing") || lower.includes("subscription") || lower.includes("payment")) {
|
|
@@ -7014,6 +6972,10 @@ function classifyError(errMsg) {
|
|
|
7014
6972
|
message: errMsg || "Unknown error"
|
|
7015
6973
|
};
|
|
7016
6974
|
}
|
|
6975
|
+
function isExpiredTokenError(errMsg) {
|
|
6976
|
+
const lower = errMsg.toLowerCase();
|
|
6977
|
+
return lower.includes("oauth token has expired") || lower.includes("not logged in");
|
|
6978
|
+
}
|
|
7017
6979
|
function isStaleSessionError(error) {
|
|
7018
6980
|
if (!(error instanceof Error))
|
|
7019
6981
|
return false;
|
|
@@ -7023,12 +6985,16 @@ function isRateLimitError(errMsg) {
|
|
|
7023
6985
|
const lower = errMsg.toLowerCase();
|
|
7024
6986
|
return lower.includes("429") || lower.includes("rate limit") || lower.includes("too many requests");
|
|
7025
6987
|
}
|
|
6988
|
+
function isExtraUsageRequiredError(errMsg) {
|
|
6989
|
+
const lower = errMsg.toLowerCase();
|
|
6990
|
+
return lower.includes("extra usage") && lower.includes("1m");
|
|
6991
|
+
}
|
|
7026
6992
|
|
|
7027
6993
|
// src/proxy/models.ts
|
|
7028
6994
|
import { exec as execCallback } from "child_process";
|
|
7029
|
-
import { existsSync } from "fs";
|
|
7030
|
-
import { fileURLToPath } from "url";
|
|
7031
|
-
import { join, dirname } from "path";
|
|
6995
|
+
import { existsSync as existsSync2 } from "fs";
|
|
6996
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6997
|
+
import { join, dirname as dirname2 } from "path";
|
|
7032
6998
|
import { promisify } from "util";
|
|
7033
6999
|
var exec = promisify(execCallback);
|
|
7034
7000
|
var AUTH_STATUS_CACHE_TTL_MS = 60000;
|
|
@@ -7043,19 +7009,35 @@ function supports1mContext(model) {
|
|
|
7043
7009
|
return false;
|
|
7044
7010
|
return true;
|
|
7045
7011
|
}
|
|
7046
|
-
function mapModelToClaudeModel(model, subscriptionType) {
|
|
7012
|
+
function mapModelToClaudeModel(model, subscriptionType, agentMode) {
|
|
7047
7013
|
if (model.includes("haiku"))
|
|
7048
7014
|
return "haiku";
|
|
7049
7015
|
const use1m = supports1mContext(model);
|
|
7050
|
-
|
|
7051
|
-
|
|
7016
|
+
const isSubagent = agentMode === "subagent";
|
|
7017
|
+
if (model.includes("opus")) {
|
|
7018
|
+
if (use1m && !isSubagent && !isExtendedContextKnownUnavailable())
|
|
7019
|
+
return "opus[1m]";
|
|
7020
|
+
return "opus";
|
|
7021
|
+
}
|
|
7052
7022
|
const sonnetOverride = process.env.MERIDIAN_SONNET_MODEL ?? process.env.CLAUDE_PROXY_SONNET_MODEL;
|
|
7053
7023
|
if (sonnetOverride === "sonnet" || sonnetOverride === "sonnet[1m]")
|
|
7054
7024
|
return sonnetOverride;
|
|
7055
7025
|
if (!use1m)
|
|
7056
7026
|
return "sonnet";
|
|
7027
|
+
if (isSubagent)
|
|
7028
|
+
return "sonnet";
|
|
7029
|
+
if (isExtendedContextKnownUnavailable())
|
|
7030
|
+
return "sonnet";
|
|
7057
7031
|
return subscriptionType === "max" ? "sonnet[1m]" : "sonnet";
|
|
7058
7032
|
}
|
|
7033
|
+
var EXTRA_USAGE_RETRY_MS = 60 * 60 * 1000;
|
|
7034
|
+
var extraUsageUnavailableAt = 0;
|
|
7035
|
+
function recordExtendedContextUnavailable() {
|
|
7036
|
+
extraUsageUnavailableAt = Date.now();
|
|
7037
|
+
}
|
|
7038
|
+
function isExtendedContextKnownUnavailable() {
|
|
7039
|
+
return extraUsageUnavailableAt > 0 && Date.now() - extraUsageUnavailableAt < EXTRA_USAGE_RETRY_MS;
|
|
7040
|
+
}
|
|
7059
7041
|
function stripExtendedContext(model) {
|
|
7060
7042
|
if (model === "opus[1m]")
|
|
7061
7043
|
return "opus";
|
|
@@ -7106,9 +7088,9 @@ async function resolveClaudeExecutableAsync() {
|
|
|
7106
7088
|
const runningUnderBun = typeof process.versions.bun !== "undefined";
|
|
7107
7089
|
if (runningUnderBun) {
|
|
7108
7090
|
try {
|
|
7109
|
-
const sdkPath =
|
|
7110
|
-
const sdkCliJs = join(
|
|
7111
|
-
if (
|
|
7091
|
+
const sdkPath = fileURLToPath2(import.meta.resolve("@anthropic-ai/claude-agent-sdk"));
|
|
7092
|
+
const sdkCliJs = join(dirname2(sdkPath), "cli.js");
|
|
7093
|
+
if (existsSync2(sdkCliJs)) {
|
|
7112
7094
|
cachedClaudePath = sdkCliJs;
|
|
7113
7095
|
return sdkCliJs;
|
|
7114
7096
|
}
|
|
@@ -7117,16 +7099,16 @@ async function resolveClaudeExecutableAsync() {
|
|
|
7117
7099
|
try {
|
|
7118
7100
|
const { stdout } = await exec("which claude");
|
|
7119
7101
|
const claudePath = stdout.trim();
|
|
7120
|
-
if (claudePath &&
|
|
7102
|
+
if (claudePath && existsSync2(claudePath)) {
|
|
7121
7103
|
cachedClaudePath = claudePath;
|
|
7122
7104
|
return claudePath;
|
|
7123
7105
|
}
|
|
7124
7106
|
} catch {}
|
|
7125
7107
|
if (!runningUnderBun) {
|
|
7126
7108
|
try {
|
|
7127
|
-
const sdkPath =
|
|
7128
|
-
const sdkCliJs = join(
|
|
7129
|
-
if (
|
|
7109
|
+
const sdkPath = fileURLToPath2(import.meta.resolve("@anthropic-ai/claude-agent-sdk"));
|
|
7110
|
+
const sdkCliJs = join(dirname2(sdkPath), "cli.js");
|
|
7111
|
+
if (existsSync2(sdkCliJs)) {
|
|
7130
7112
|
cachedClaudePath = sdkCliJs;
|
|
7131
7113
|
return sdkCliJs;
|
|
7132
7114
|
}
|
|
@@ -7218,6 +7200,17 @@ function createFileChangeHook(changes, mcpPrefix) {
|
|
|
7218
7200
|
}]
|
|
7219
7201
|
};
|
|
7220
7202
|
}
|
|
7203
|
+
function isLikelyFilePath(s) {
|
|
7204
|
+
if (/[()[\]]/.test(s))
|
|
7205
|
+
return false;
|
|
7206
|
+
if (/^-?\d+$/.test(s))
|
|
7207
|
+
return false;
|
|
7208
|
+
if (/^[{}]$/.test(s))
|
|
7209
|
+
return false;
|
|
7210
|
+
if (!/[\w/.]/.test(s))
|
|
7211
|
+
return false;
|
|
7212
|
+
return true;
|
|
7213
|
+
}
|
|
7221
7214
|
function extractFileChangesFromBash(command) {
|
|
7222
7215
|
const changes = [];
|
|
7223
7216
|
const seen = new Set;
|
|
@@ -7232,10 +7225,12 @@ function extractFileChangesFromBash(command) {
|
|
|
7232
7225
|
changes.push({ operation, path });
|
|
7233
7226
|
}
|
|
7234
7227
|
};
|
|
7235
|
-
const redirectRegex = /(?<![0-9])>{1,2}\s*['"]?([^\s'";&|)]+)['"]?/g;
|
|
7228
|
+
const redirectRegex = /(?<![0-9=])>{1,2}\s*['"]?([^\s'";&|)]+)['"]?/g;
|
|
7236
7229
|
let match2;
|
|
7237
7230
|
while ((match2 = redirectRegex.exec(command)) !== null) {
|
|
7238
|
-
|
|
7231
|
+
if (isLikelyFilePath(match2[1])) {
|
|
7232
|
+
addChange("wrote", match2[1]);
|
|
7233
|
+
}
|
|
7239
7234
|
}
|
|
7240
7235
|
const teeRegex = /\btee\s+(?:-[a-zA-Z]\s+)*['"]?([^\s'";&|)]+)['"]?/g;
|
|
7241
7236
|
while ((match2 = teeRegex.exec(command)) !== null) {
|
|
@@ -9203,7 +9198,7 @@ minimatch.escape = escape;
|
|
|
9203
9198
|
minimatch.unescape = unescape;
|
|
9204
9199
|
|
|
9205
9200
|
// node_modules/glob/dist/esm/glob.js
|
|
9206
|
-
import { fileURLToPath as
|
|
9201
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
9207
9202
|
|
|
9208
9203
|
// node_modules/lru-cache/dist/esm/index.js
|
|
9209
9204
|
var defaultPerf = typeof performance === "object" && performance && typeof performance.now === "function" ? performance : Date;
|
|
@@ -10366,7 +10361,7 @@ class LRUCache {
|
|
|
10366
10361
|
|
|
10367
10362
|
// node_modules/path-scurry/dist/esm/index.js
|
|
10368
10363
|
import { posix, win32 } from "node:path";
|
|
10369
|
-
import { fileURLToPath as
|
|
10364
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
10370
10365
|
import { lstatSync, readdir as readdirCB, readdirSync, readlinkSync, realpathSync as rps } from "fs";
|
|
10371
10366
|
import * as actualFS from "node:fs";
|
|
10372
10367
|
import { lstat, readdir, readlink, realpath } from "node:fs/promises";
|
|
@@ -10913,10 +10908,10 @@ class Minipass extends EventEmitter {
|
|
|
10913
10908
|
return this[ENCODING] ? buf.join("") : Buffer.concat(buf, buf.dataLength);
|
|
10914
10909
|
}
|
|
10915
10910
|
async promise() {
|
|
10916
|
-
return new Promise((
|
|
10911
|
+
return new Promise((resolve2, reject) => {
|
|
10917
10912
|
this.on(DESTROYED, () => reject(new Error("stream destroyed")));
|
|
10918
10913
|
this.on("error", (er) => reject(er));
|
|
10919
|
-
this.on("end", () =>
|
|
10914
|
+
this.on("end", () => resolve2());
|
|
10920
10915
|
});
|
|
10921
10916
|
}
|
|
10922
10917
|
[Symbol.asyncIterator]() {
|
|
@@ -10935,7 +10930,7 @@ class Minipass extends EventEmitter {
|
|
|
10935
10930
|
return Promise.resolve({ done: false, value: res });
|
|
10936
10931
|
if (this[EOF])
|
|
10937
10932
|
return stop();
|
|
10938
|
-
let
|
|
10933
|
+
let resolve2;
|
|
10939
10934
|
let reject;
|
|
10940
10935
|
const onerr = (er) => {
|
|
10941
10936
|
this.off("data", ondata);
|
|
@@ -10949,19 +10944,19 @@ class Minipass extends EventEmitter {
|
|
|
10949
10944
|
this.off("end", onend);
|
|
10950
10945
|
this.off(DESTROYED, ondestroy);
|
|
10951
10946
|
this.pause();
|
|
10952
|
-
|
|
10947
|
+
resolve2({ value, done: !!this[EOF] });
|
|
10953
10948
|
};
|
|
10954
10949
|
const onend = () => {
|
|
10955
10950
|
this.off("error", onerr);
|
|
10956
10951
|
this.off("data", ondata);
|
|
10957
10952
|
this.off(DESTROYED, ondestroy);
|
|
10958
10953
|
stop();
|
|
10959
|
-
|
|
10954
|
+
resolve2({ done: true, value: undefined });
|
|
10960
10955
|
};
|
|
10961
10956
|
const ondestroy = () => onerr(new Error("stream destroyed"));
|
|
10962
10957
|
return new Promise((res2, rej) => {
|
|
10963
10958
|
reject = rej;
|
|
10964
|
-
|
|
10959
|
+
resolve2 = res2;
|
|
10965
10960
|
this.once(DESTROYED, ondestroy);
|
|
10966
10961
|
this.once("error", onerr);
|
|
10967
10962
|
this.once("end", onend);
|
|
@@ -11661,8 +11656,8 @@ class PathBase {
|
|
|
11661
11656
|
if (this.#asyncReaddirInFlight) {
|
|
11662
11657
|
await this.#asyncReaddirInFlight;
|
|
11663
11658
|
} else {
|
|
11664
|
-
let
|
|
11665
|
-
this.#asyncReaddirInFlight = new Promise((res) =>
|
|
11659
|
+
let resolve2 = () => {};
|
|
11660
|
+
this.#asyncReaddirInFlight = new Promise((res) => resolve2 = res);
|
|
11666
11661
|
try {
|
|
11667
11662
|
for (const e of await this.#fs.promises.readdir(fullpath, {
|
|
11668
11663
|
withFileTypes: true
|
|
@@ -11675,7 +11670,7 @@ class PathBase {
|
|
|
11675
11670
|
children.provisional = 0;
|
|
11676
11671
|
}
|
|
11677
11672
|
this.#asyncReaddirInFlight = undefined;
|
|
11678
|
-
|
|
11673
|
+
resolve2();
|
|
11679
11674
|
}
|
|
11680
11675
|
return children.slice(0, children.provisional);
|
|
11681
11676
|
}
|
|
@@ -11821,7 +11816,7 @@ class PathScurryBase {
|
|
|
11821
11816
|
constructor(cwd = process.cwd(), pathImpl, sep2, { nocase, childrenCacheSize = 16 * 1024, fs = defaultFS } = {}) {
|
|
11822
11817
|
this.#fs = fsFromOption(fs);
|
|
11823
11818
|
if (cwd instanceof URL || cwd.startsWith("file://")) {
|
|
11824
|
-
cwd =
|
|
11819
|
+
cwd = fileURLToPath3(cwd);
|
|
11825
11820
|
}
|
|
11826
11821
|
const cwdPath = pathImpl.resolve(cwd);
|
|
11827
11822
|
this.roots = Object.create(null);
|
|
@@ -13125,7 +13120,7 @@ class Glob {
|
|
|
13125
13120
|
if (!opts.cwd) {
|
|
13126
13121
|
this.cwd = "";
|
|
13127
13122
|
} else if (opts.cwd instanceof URL || opts.cwd.startsWith("file://")) {
|
|
13128
|
-
opts.cwd =
|
|
13123
|
+
opts.cwd = fileURLToPath4(opts.cwd);
|
|
13129
13124
|
}
|
|
13130
13125
|
this.cwd = opts.cwd || "";
|
|
13131
13126
|
this.root = opts.root;
|
|
@@ -13679,10 +13674,10 @@ class LRUMap {
|
|
|
13679
13674
|
// src/proxy/sessionStore.ts
|
|
13680
13675
|
import {
|
|
13681
13676
|
closeSync,
|
|
13682
|
-
existsSync as
|
|
13677
|
+
existsSync as existsSync3,
|
|
13683
13678
|
mkdirSync,
|
|
13684
13679
|
openSync,
|
|
13685
|
-
readFileSync,
|
|
13680
|
+
readFileSync as readFileSync2,
|
|
13686
13681
|
renameSync,
|
|
13687
13682
|
statSync,
|
|
13688
13683
|
unlinkSync,
|
|
@@ -13737,7 +13732,7 @@ var sessionDirOverride = null;
|
|
|
13737
13732
|
var skipLocking = false;
|
|
13738
13733
|
function getStorePath() {
|
|
13739
13734
|
const dir = sessionDirOverride || process.env.MERIDIAN_SESSION_DIR || process.env.CLAUDE_PROXY_SESSION_DIR || getDefaultCacheDir();
|
|
13740
|
-
if (!
|
|
13735
|
+
if (!existsSync3(dir)) {
|
|
13741
13736
|
mkdirSync(dir, { recursive: true });
|
|
13742
13737
|
}
|
|
13743
13738
|
return join2(dir, "sessions.json");
|
|
@@ -13745,9 +13740,9 @@ function getStorePath() {
|
|
|
13745
13740
|
function getDefaultCacheDir() {
|
|
13746
13741
|
const newDir = join2(homedir(), ".cache", "meridian");
|
|
13747
13742
|
const oldDir = join2(homedir(), ".cache", "opencode-claude-max-proxy");
|
|
13748
|
-
if (
|
|
13743
|
+
if (existsSync3(newDir))
|
|
13749
13744
|
return newDir;
|
|
13750
|
-
if (
|
|
13745
|
+
if (existsSync3(oldDir)) {
|
|
13751
13746
|
try {
|
|
13752
13747
|
const { symlinkSync } = __require("fs");
|
|
13753
13748
|
symlinkSync(oldDir, newDir);
|
|
@@ -13760,10 +13755,10 @@ function getDefaultCacheDir() {
|
|
|
13760
13755
|
}
|
|
13761
13756
|
function readStore() {
|
|
13762
13757
|
const path3 = getStorePath();
|
|
13763
|
-
if (!
|
|
13758
|
+
if (!existsSync3(path3))
|
|
13764
13759
|
return {};
|
|
13765
13760
|
try {
|
|
13766
|
-
const data =
|
|
13761
|
+
const data = readFileSync2(path3, "utf-8");
|
|
13767
13762
|
return JSON.parse(data);
|
|
13768
13763
|
} catch (e) {
|
|
13769
13764
|
console.error("[sessionStore] read failed:", e.message);
|
|
@@ -14112,8 +14107,8 @@ function createProxyServer(config = {}) {
|
|
|
14112
14107
|
activeSessions++;
|
|
14113
14108
|
return;
|
|
14114
14109
|
}
|
|
14115
|
-
return new Promise((
|
|
14116
|
-
sessionQueue.push({ resolve:
|
|
14110
|
+
return new Promise((resolve3) => {
|
|
14111
|
+
sessionQueue.push({ resolve: resolve3 });
|
|
14117
14112
|
});
|
|
14118
14113
|
}
|
|
14119
14114
|
function releaseSession() {
|
|
@@ -14149,11 +14144,15 @@ function createProxyServer(config = {}) {
|
|
|
14149
14144
|
return textPrompt;
|
|
14150
14145
|
};
|
|
14151
14146
|
const body = await c.req.json();
|
|
14147
|
+
if (!Array.isArray(body.messages)) {
|
|
14148
|
+
return c.json({ type: "error", error: { type: "invalid_request_error", message: "messages: Field required" } }, 400);
|
|
14149
|
+
}
|
|
14152
14150
|
const authStatus = await getClaudeAuthStatusAsync();
|
|
14153
|
-
|
|
14151
|
+
const agentMode = c.req.header("x-opencode-agent-mode") ?? null;
|
|
14152
|
+
let model = mapModelToClaudeModel(body.model || "sonnet", authStatus?.subscriptionType, agentMode);
|
|
14154
14153
|
const adapter = detectAdapter(c);
|
|
14155
14154
|
const adapterStreamPref = adapter.prefersStreaming?.(body);
|
|
14156
|
-
const stream2 = adapterStreamPref !== undefined ? adapterStreamPref : body.stream ??
|
|
14155
|
+
const stream2 = adapterStreamPref !== undefined ? adapterStreamPref : body.stream ?? false;
|
|
14157
14156
|
const workingDirectory = (process.env.MERIDIAN_WORKDIR ?? process.env.CLAUDE_PROXY_WORKDIR) || adapter.extractWorkingDirectory(body) || process.cwd();
|
|
14158
14157
|
const {
|
|
14159
14158
|
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS,
|
|
@@ -14184,7 +14183,7 @@ function createProxyServer(config = {}) {
|
|
|
14184
14183
|
}).join(" → ");
|
|
14185
14184
|
const lineageType = lineageResult.type === "diverged" && !cachedSession ? "new" : lineageResult.type;
|
|
14186
14185
|
const msgCount = Array.isArray(body.messages) ? body.messages.length : 0;
|
|
14187
|
-
const requestLogLine = `${requestMeta.requestId} model=${model} stream=${stream2} tools=${body.tools?.length ?? 0} lineage=${lineageType} session=${resumeSessionId?.slice(0, 8) || "new"}${isUndo && undoRollbackUuid ? ` rollback=${undoRollbackUuid.slice(0, 8)}` : ""} active=${activeSessions}/${MAX_CONCURRENT_SESSIONS} msgCount=${msgCount}`;
|
|
14186
|
+
const requestLogLine = `${requestMeta.requestId} model=${model} stream=${stream2} tools=${body.tools?.length ?? 0} 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}`;
|
|
14188
14187
|
console.error(`[PROXY] ${requestLogLine} msgs=${msgSummary}`);
|
|
14189
14188
|
diagnosticLog.session(`${requestLogLine}`, requestMeta.requestId);
|
|
14190
14189
|
claudeLog("request.received", {
|
|
@@ -14352,6 +14351,7 @@ function createProxyServer(config = {}) {
|
|
|
14352
14351
|
const RATE_LIMIT_BASE_DELAY_MS = 1000;
|
|
14353
14352
|
const response = async function* () {
|
|
14354
14353
|
let rateLimitRetries = 0;
|
|
14354
|
+
let tokenRefreshed = false;
|
|
14355
14355
|
while (true) {
|
|
14356
14356
|
let didYieldContent = false;
|
|
14357
14357
|
try {
|
|
@@ -14373,7 +14373,7 @@ function createProxyServer(config = {}) {
|
|
|
14373
14373
|
adapter,
|
|
14374
14374
|
onStderr
|
|
14375
14375
|
}))) {
|
|
14376
|
-
if (event.type === "assistant") {
|
|
14376
|
+
if (event.type === "assistant" && !event.error) {
|
|
14377
14377
|
didYieldContent = true;
|
|
14378
14378
|
}
|
|
14379
14379
|
yield event;
|
|
@@ -14414,6 +14414,28 @@ function createProxyServer(config = {}) {
|
|
|
14414
14414
|
}));
|
|
14415
14415
|
return;
|
|
14416
14416
|
}
|
|
14417
|
+
if (isExtraUsageRequiredError(errMsg) && hasExtendedContext(model)) {
|
|
14418
|
+
const from = model;
|
|
14419
|
+
model = stripExtendedContext(model);
|
|
14420
|
+
recordExtendedContextUnavailable();
|
|
14421
|
+
claudeLog("upstream.context_fallback", {
|
|
14422
|
+
mode: "non_stream",
|
|
14423
|
+
from,
|
|
14424
|
+
to: model,
|
|
14425
|
+
reason: "extra_usage_required"
|
|
14426
|
+
});
|
|
14427
|
+
console.error(`[PROXY] ${requestMeta.requestId} extra usage required for [1m], falling back to ${model} (skipping [1m] for 1h)`);
|
|
14428
|
+
continue;
|
|
14429
|
+
}
|
|
14430
|
+
if (isExpiredTokenError(errMsg) && !tokenRefreshed) {
|
|
14431
|
+
tokenRefreshed = true;
|
|
14432
|
+
const refreshed = await refreshOAuthToken();
|
|
14433
|
+
if (refreshed) {
|
|
14434
|
+
claudeLog("token_refresh.retrying", { mode: "non_stream" });
|
|
14435
|
+
console.error(`[PROXY] ${requestMeta.requestId} OAuth token expired — refreshed, retrying`);
|
|
14436
|
+
continue;
|
|
14437
|
+
}
|
|
14438
|
+
}
|
|
14417
14439
|
if (isRateLimitError(errMsg)) {
|
|
14418
14440
|
if (hasExtendedContext(model)) {
|
|
14419
14441
|
const from = model;
|
|
@@ -14622,6 +14644,7 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
14622
14644
|
const RATE_LIMIT_BASE_DELAY_MS = 1000;
|
|
14623
14645
|
const response = async function* () {
|
|
14624
14646
|
let rateLimitRetries = 0;
|
|
14647
|
+
let tokenRefreshed = false;
|
|
14625
14648
|
while (true) {
|
|
14626
14649
|
let didYieldClientEvent = false;
|
|
14627
14650
|
try {
|
|
@@ -14684,6 +14707,28 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
14684
14707
|
}));
|
|
14685
14708
|
return;
|
|
14686
14709
|
}
|
|
14710
|
+
if (isExtraUsageRequiredError(errMsg) && hasExtendedContext(model)) {
|
|
14711
|
+
const from = model;
|
|
14712
|
+
model = stripExtendedContext(model);
|
|
14713
|
+
recordExtendedContextUnavailable();
|
|
14714
|
+
claudeLog("upstream.context_fallback", {
|
|
14715
|
+
mode: "stream",
|
|
14716
|
+
from,
|
|
14717
|
+
to: model,
|
|
14718
|
+
reason: "extra_usage_required"
|
|
14719
|
+
});
|
|
14720
|
+
console.error(`[PROXY] ${requestMeta.requestId} extra usage required for [1m], falling back to ${model} (skipping [1m] for 1h)`);
|
|
14721
|
+
continue;
|
|
14722
|
+
}
|
|
14723
|
+
if (isExpiredTokenError(errMsg) && !tokenRefreshed) {
|
|
14724
|
+
tokenRefreshed = true;
|
|
14725
|
+
const refreshed = await refreshOAuthToken();
|
|
14726
|
+
if (refreshed) {
|
|
14727
|
+
claudeLog("token_refresh.retrying", { mode: "stream" });
|
|
14728
|
+
console.error(`[PROXY] ${requestMeta.requestId} OAuth token expired — refreshed, retrying`);
|
|
14729
|
+
continue;
|
|
14730
|
+
}
|
|
14731
|
+
}
|
|
14687
14732
|
if (isRateLimitError(errMsg)) {
|
|
14688
14733
|
if (hasExtendedContext(model)) {
|
|
14689
14734
|
const from = model;
|
|
@@ -15117,7 +15162,8 @@ data: ${JSON.stringify({
|
|
|
15117
15162
|
email: auth.email,
|
|
15118
15163
|
subscriptionType: auth.subscriptionType
|
|
15119
15164
|
},
|
|
15120
|
-
mode: process.env.MERIDIAN_PASSTHROUGH ?? process.env.CLAUDE_PROXY_PASSTHROUGH ? "passthrough" : "internal"
|
|
15165
|
+
mode: process.env.MERIDIAN_PASSTHROUGH ?? process.env.CLAUDE_PROXY_PASSTHROUGH ? "passthrough" : "internal",
|
|
15166
|
+
plugin: { opencode: checkPluginConfigured() ? "configured" : "not-configured" }
|
|
15121
15167
|
});
|
|
15122
15168
|
} catch {
|
|
15123
15169
|
return c.json({
|
|
@@ -15127,6 +15173,13 @@ data: ${JSON.stringify({
|
|
|
15127
15173
|
});
|
|
15128
15174
|
}
|
|
15129
15175
|
});
|
|
15176
|
+
app.post("/auth/refresh", async (c) => {
|
|
15177
|
+
const success = await refreshOAuthToken();
|
|
15178
|
+
if (success) {
|
|
15179
|
+
return c.json({ success: true, message: "OAuth token refreshed successfully" });
|
|
15180
|
+
}
|
|
15181
|
+
return c.json({ success: false, message: "Token refresh failed. If the problem persists, run 'claude login'." }, 500);
|
|
15182
|
+
});
|
|
15130
15183
|
app.all("*", (c) => {
|
|
15131
15184
|
console.error(`[PROXY] UNHANDLED ${c.req.method} ${c.req.url}`);
|
|
15132
15185
|
return c.json({ error: { type: "not_found", message: `Endpoint not supported: ${c.req.method} ${new URL(c.req.url).pathname}` } }, 404);
|
|
@@ -15170,11 +15223,11 @@ Or use a different port:`);
|
|
|
15170
15223
|
server,
|
|
15171
15224
|
config: finalConfig,
|
|
15172
15225
|
async close() {
|
|
15173
|
-
await new Promise((
|
|
15174
|
-
server.close((err) => err ? reject(err) :
|
|
15226
|
+
await new Promise((resolve3, reject) => {
|
|
15227
|
+
server.close((err) => err ? reject(err) : resolve3());
|
|
15175
15228
|
});
|
|
15176
15229
|
}
|
|
15177
15230
|
};
|
|
15178
15231
|
}
|
|
15179
15232
|
|
|
15180
|
-
export {
|
|
15233
|
+
export { computeLineageHash, hashMessage, computeMessageHashes, getMaxSessionsLimit, clearSessionCache, createProxyServer, startProxyServer };
|