@rynfar/meridian 1.53.0 → 1.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,22 +1,347 @@
1
- import {
2
- resolveClaudeExecutableSync
3
- } from "./cli-vjeftz4z.js";
4
1
  import {
5
2
  setSetting
6
3
  } from "./cli-340h1chz.js";
7
4
  import {
8
5
  createPlatformCredentialStore
9
6
  } from "./cli-aq5zz92m.js";
10
- import"./cli-p9swy5t3.js";
7
+ import {
8
+ __esm
9
+ } from "./cli-p9swy5t3.js";
10
+
11
+ // src/env.ts
12
+ function env(suffix) {
13
+ return process.env[`MERIDIAN_${suffix}`] ?? process.env[`CLAUDE_PROXY_${suffix}`];
14
+ }
15
+ function envBool(suffix) {
16
+ const val = env(suffix);
17
+ return val === "1" || val === "true" || val === "yes";
18
+ }
19
+ function resolvePassthrough(defaultValue) {
20
+ const val = env("PASSTHROUGH");
21
+ if (val === "1" || val === "true" || val === "yes")
22
+ return true;
23
+ if (val === "0" || val === "false" || val === "no")
24
+ return false;
25
+ return defaultValue;
26
+ }
27
+ function envInt(suffix, defaultValue) {
28
+ const val = env(suffix);
29
+ if (!val)
30
+ return defaultValue;
31
+ const parsed = parseInt(val, 10);
32
+ return Number.isFinite(parsed) ? parsed : defaultValue;
33
+ }
34
+ var init_env = () => {};
11
35
 
12
36
  // src/proxy/profileCli.ts
13
37
  import { execFileSync, spawnSync } from "node:child_process";
14
38
  import { createHash, randomBytes } from "node:crypto";
15
- import { mkdirSync, readFileSync, rmSync, writeFileSync, existsSync } from "node:fs";
39
+ import { mkdirSync, readFileSync, rmSync, writeFileSync, existsSync as existsSync2 } from "node:fs";
16
40
  import { homedir } from "node:os";
17
- import { join } from "node:path";
18
- var PROFILES_DIR = join(homedir(), ".config", "meridian", "profiles");
19
- var CONFIG_FILE = join(homedir(), ".config", "meridian", "profiles.json");
41
+ import { join as join2 } from "node:path";
42
+
43
+ // src/proxy/models.ts
44
+ init_env();
45
+ import { exec as execCallback, execFile as execFileCallback } from "child_process";
46
+ import { existsSync, statSync } from "fs";
47
+ import { fileURLToPath } from "url";
48
+ import { join, dirname } from "path";
49
+ import { promisify } from "util";
50
+ var exec = promisify(execCallback);
51
+ var execFile = promisify(execFileCallback);
52
+ var STUB_SIZE_THRESHOLD = 4096;
53
+ var CANONICAL_FABLE_MODEL = "claude-fable-5";
54
+ var CANONICAL_OPUS_MODEL = "claude-opus-4-8";
55
+ var CANONICAL_SONNET_MODEL = "claude-sonnet-5";
56
+ var CANONICAL_HAIKU_MODEL = "claude-haiku-4-5";
57
+ function resolveSdkModelDefaults(env2 = process.env) {
58
+ return {
59
+ ANTHROPIC_DEFAULT_FABLE_MODEL: env2.MERIDIAN_DEFAULT_FABLE_MODEL ?? CANONICAL_FABLE_MODEL,
60
+ ANTHROPIC_DEFAULT_OPUS_MODEL: env2.MERIDIAN_DEFAULT_OPUS_MODEL ?? CANONICAL_OPUS_MODEL,
61
+ ANTHROPIC_DEFAULT_SONNET_MODEL: env2.MERIDIAN_DEFAULT_SONNET_MODEL ?? CANONICAL_SONNET_MODEL,
62
+ ANTHROPIC_DEFAULT_HAIKU_MODEL: env2.MERIDIAN_DEFAULT_HAIKU_MODEL ?? CANONICAL_HAIKU_MODEL
63
+ };
64
+ }
65
+ function explicitModelPin(requestedModel) {
66
+ const base = requestedModel.trim().toLowerCase().replace(/\[1m\]$/, "");
67
+ const match = /^claude-(sonnet|opus|haiku|fable|mythos)-\d[\w.-]*$/.exec(base);
68
+ if (!match)
69
+ return;
70
+ const tier = match[1] === "mythos" ? "FABLE" : match[1].toUpperCase();
71
+ return { [`ANTHROPIC_DEFAULT_${tier}_MODEL`]: base };
72
+ }
73
+ var AUTH_STATUS_CACHE_TTL_MS = 60000;
74
+ var AUTH_STATUS_FAILURE_TTL_MS = 5000;
75
+ var cachedAuthStatus = null;
76
+ var lastKnownGoodAuthStatus = null;
77
+ var cachedAuthStatusAt = 0;
78
+ var cachedAuthStatusIsFailure = false;
79
+ var cachedAuthStatusPromise = null;
80
+ function supports1mContext(model) {
81
+ const override = env("1M_CONTEXT_SUPPORT");
82
+ if (override === "0" || override === "false" || override === "no")
83
+ return false;
84
+ if (model.includes("4-5") || model.includes("4.5"))
85
+ return false;
86
+ return true;
87
+ }
88
+ function mapModelToClaudeModel(model, subscriptionType, agentMode) {
89
+ if (model.includes("haiku"))
90
+ return "haiku";
91
+ const use1m = supports1mContext(model);
92
+ const isSubagent = agentMode === "subagent";
93
+ if (model.includes("fable") || model.includes("mythos")) {
94
+ if (use1m && !isSubagent && !isExtendedContextKnownUnavailable())
95
+ return "fable[1m]";
96
+ return "fable";
97
+ }
98
+ if (model.includes("opus")) {
99
+ if (use1m && !isSubagent && !isExtendedContextKnownUnavailable())
100
+ return "opus[1m]";
101
+ return "opus";
102
+ }
103
+ const sonnetOverride = process.env.MERIDIAN_SONNET_MODEL ?? process.env.CLAUDE_PROXY_SONNET_MODEL;
104
+ if (sonnetOverride === "sonnet[1m]") {
105
+ if (!use1m || isSubagent || isExtendedContextKnownUnavailable())
106
+ return "sonnet";
107
+ return "sonnet[1m]";
108
+ }
109
+ return "sonnet";
110
+ }
111
+ var EXTRA_USAGE_RETRY_MS = 60 * 60 * 1000;
112
+ var extraUsageUnavailableAt = 0;
113
+ function recordExtendedContextUnavailable() {
114
+ extraUsageUnavailableAt = Date.now();
115
+ }
116
+ function isExtendedContextKnownUnavailable() {
117
+ return extraUsageUnavailableAt > 0 && Date.now() - extraUsageUnavailableAt < EXTRA_USAGE_RETRY_MS;
118
+ }
119
+ function stripExtendedContext(model) {
120
+ if (model === "opus[1m]")
121
+ return "opus";
122
+ if (model === "sonnet[1m]")
123
+ return "sonnet";
124
+ if (model === "fable[1m]")
125
+ return "fable";
126
+ return model;
127
+ }
128
+ function hasExtendedContext(model) {
129
+ return model.endsWith("[1m]");
130
+ }
131
+ var profileAuthCaches = new Map;
132
+ function getAuthCacheInfo(profileId) {
133
+ if (!profileId) {
134
+ return { lastCheckedAt: cachedAuthStatusAt, lastSuccessAt: cachedAuthStatusIsFailure ? 0 : cachedAuthStatusAt, isFailure: cachedAuthStatusIsFailure };
135
+ }
136
+ const cache = profileAuthCaches.get(profileId);
137
+ if (!cache)
138
+ return { lastCheckedAt: 0, lastSuccessAt: 0, isFailure: false };
139
+ return { lastCheckedAt: cache.at, lastSuccessAt: cache.lastSuccessAt, isFailure: cache.isFailure };
140
+ }
141
+ function getAuthCache(key) {
142
+ let cache = profileAuthCaches.get(key);
143
+ if (!cache) {
144
+ cache = { status: null, lastKnownGood: null, at: 0, isFailure: false, promise: null, lastSuccessAt: 0 };
145
+ profileAuthCaches.set(key, cache);
146
+ }
147
+ return cache;
148
+ }
149
+ async function getClaudeAuthStatusAsync(profileId, envOverrides) {
150
+ const isDefault = !profileId;
151
+ const cache = isDefault ? null : getAuthCache(profileId);
152
+ const c_status = cache ? cache.status : cachedAuthStatus;
153
+ const c_lastKnownGood = cache ? cache.lastKnownGood : lastKnownGoodAuthStatus;
154
+ const c_at = cache ? cache.at : cachedAuthStatusAt;
155
+ const c_isFailure = cache ? cache.isFailure : cachedAuthStatusIsFailure;
156
+ let c_promise = cache ? cache.promise : cachedAuthStatusPromise;
157
+ const ttl = c_isFailure ? AUTH_STATUS_FAILURE_TTL_MS : AUTH_STATUS_CACHE_TTL_MS;
158
+ if (c_at > 0 && Date.now() - c_at < ttl) {
159
+ return c_status ?? c_lastKnownGood;
160
+ }
161
+ if (c_promise)
162
+ return c_promise;
163
+ c_promise = (async () => {
164
+ try {
165
+ const claudePath = await resolveClaudeExecutableAsync();
166
+ const { stdout } = await execFile(claudePath, ["auth", "status"], {
167
+ timeout: 5000,
168
+ ...envOverrides ? { env: { ...process.env, ...envOverrides } } : {}
169
+ });
170
+ const parsed = JSON.parse(stdout);
171
+ if (cache) {
172
+ cache.status = parsed;
173
+ cache.lastKnownGood = parsed;
174
+ cache.at = Date.now();
175
+ cache.isFailure = false;
176
+ cache.lastSuccessAt = Date.now();
177
+ } else {
178
+ cachedAuthStatus = parsed;
179
+ lastKnownGoodAuthStatus = parsed;
180
+ cachedAuthStatusAt = Date.now();
181
+ cachedAuthStatusIsFailure = false;
182
+ }
183
+ return parsed;
184
+ } catch {
185
+ if (cache) {
186
+ cache.isFailure = true;
187
+ cache.at = Date.now();
188
+ cache.status = null;
189
+ return cache.lastKnownGood;
190
+ } else {
191
+ cachedAuthStatusIsFailure = true;
192
+ cachedAuthStatusAt = Date.now();
193
+ cachedAuthStatus = null;
194
+ return lastKnownGoodAuthStatus;
195
+ }
196
+ }
197
+ })();
198
+ if (cache)
199
+ cache.promise = c_promise;
200
+ else
201
+ cachedAuthStatusPromise = c_promise;
202
+ try {
203
+ return await c_promise;
204
+ } finally {
205
+ if (cache)
206
+ cache.promise = null;
207
+ else
208
+ cachedAuthStatusPromise = null;
209
+ }
210
+ }
211
+ var cachedClaudeInfo = null;
212
+ var cachedClaudePathPromise = null;
213
+ var DEFAULT_DEPS = {
214
+ existsSync,
215
+ statSync: (p) => statSync(p),
216
+ exec,
217
+ resolvePackage: (specifier) => fileURLToPath(import.meta.resolve(specifier)),
218
+ envGet: (name) => process.env[name],
219
+ platform: process.platform,
220
+ arch: process.arch,
221
+ isBun: typeof process.versions.bun !== "undefined"
222
+ };
223
+ function tryEnvOverride(deps) {
224
+ const explicit = deps.envGet("MERIDIAN_CLAUDE_PATH");
225
+ if (!explicit)
226
+ return null;
227
+ return deps.existsSync(explicit) ? explicit : null;
228
+ }
229
+ function tryBundledBinary(deps) {
230
+ try {
231
+ const pkgPath = deps.resolvePackage("@anthropic-ai/claude-code/package.json");
232
+ const bundled = join(dirname(pkgPath), "bin", "claude.exe");
233
+ if (!deps.existsSync(bundled))
234
+ return null;
235
+ const size = deps.statSync(bundled).size;
236
+ if (size <= STUB_SIZE_THRESHOLD)
237
+ return null;
238
+ return bundled;
239
+ } catch {
240
+ return null;
241
+ }
242
+ }
243
+ function tryPlatformPackage(deps) {
244
+ const binName = deps.platform === "win32" ? "claude.exe" : "claude";
245
+ const candidates = [`@anthropic-ai/claude-code-${deps.platform}-${deps.arch}`];
246
+ if (deps.platform === "linux") {
247
+ candidates.push(`@anthropic-ai/claude-code-${deps.platform}-${deps.arch}-musl`);
248
+ }
249
+ for (const pkg of candidates) {
250
+ try {
251
+ const pkgJson = deps.resolvePackage(`${pkg}/package.json`);
252
+ const candidate = join(dirname(pkgJson), binName);
253
+ if (deps.existsSync(candidate))
254
+ return candidate;
255
+ } catch {}
256
+ }
257
+ return null;
258
+ }
259
+ async function tryPathLookup(deps) {
260
+ const cmd = deps.platform === "win32" ? "where claude" : "which claude";
261
+ try {
262
+ const { stdout } = await deps.exec(cmd);
263
+ const candidates = stdout.split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
264
+ for (const candidate of candidates) {
265
+ if (deps.platform === "win32" && candidate.startsWith("/"))
266
+ continue;
267
+ if (deps.existsSync(candidate))
268
+ return candidate;
269
+ }
270
+ } catch {}
271
+ return null;
272
+ }
273
+ function tryLegacySdkCliJs(deps) {
274
+ if (!deps.isBun)
275
+ return null;
276
+ try {
277
+ const sdkPath = deps.resolvePackage("@anthropic-ai/claude-agent-sdk");
278
+ const cliJs = join(dirname(sdkPath), "cli.js");
279
+ return deps.existsSync(cliJs) ? cliJs : null;
280
+ } catch {
281
+ return null;
282
+ }
283
+ }
284
+ async function resolveClaudeExecutableWithSource(deps = DEFAULT_DEPS) {
285
+ const env2 = tryEnvOverride(deps);
286
+ if (env2)
287
+ return { path: env2, source: "env" };
288
+ const bundled = tryBundledBinary(deps);
289
+ if (bundled)
290
+ return { path: bundled, source: "bundled" };
291
+ const platformPkg = tryPlatformPackage(deps);
292
+ if (platformPkg)
293
+ return { path: platformPkg, source: "platform-package" };
294
+ const pathLookup = await tryPathLookup(deps);
295
+ if (pathLookup)
296
+ return { path: pathLookup, source: "path-lookup" };
297
+ const legacy = tryLegacySdkCliJs(deps);
298
+ if (legacy)
299
+ return { path: legacy, source: "legacy-cli-js" };
300
+ return null;
301
+ }
302
+ function resolveClaudeExecutableSync(deps = DEFAULT_DEPS) {
303
+ const env2 = tryEnvOverride(deps);
304
+ if (env2)
305
+ return { path: env2, source: "env" };
306
+ const bundled = tryBundledBinary(deps);
307
+ if (bundled)
308
+ return { path: bundled, source: "bundled" };
309
+ const platformPkg = tryPlatformPackage(deps);
310
+ if (platformPkg)
311
+ return { path: platformPkg, source: "platform-package" };
312
+ return null;
313
+ }
314
+ function getResolvedClaudeExecutableInfo() {
315
+ return cachedClaudeInfo;
316
+ }
317
+ async function resolveClaudeExecutableAsync() {
318
+ if (cachedClaudeInfo)
319
+ return cachedClaudeInfo.path;
320
+ if (cachedClaudePathPromise)
321
+ return cachedClaudePathPromise;
322
+ cachedClaudePathPromise = (async () => {
323
+ const resolved = await resolveClaudeExecutableWithSource();
324
+ if (resolved) {
325
+ cachedClaudeInfo = resolved;
326
+ return resolved.path;
327
+ }
328
+ throw new Error("Could not find Claude Code executable. Install via: npm install -g @anthropic-ai/claude-code, " + "or set MERIDIAN_CLAUDE_PATH=/path/to/claude to point at an existing binary.");
329
+ })();
330
+ try {
331
+ return await cachedClaudePathPromise;
332
+ } finally {
333
+ cachedClaudePathPromise = null;
334
+ }
335
+ }
336
+ function isClosedControllerError(error) {
337
+ if (!(error instanceof Error))
338
+ return false;
339
+ return error.message.includes("Controller is already closed");
340
+ }
341
+
342
+ // src/proxy/profileCli.ts
343
+ var PROFILES_DIR = join2(homedir(), ".config", "meridian", "profiles");
344
+ var CONFIG_FILE = join2(homedir(), ".config", "meridian", "profiles.json");
20
345
  var OAUTH_AUTHORIZE_URL = "https://claude.com/cai/oauth/authorize";
21
346
  var OAUTH_TOKEN_URL = "https://platform.claude.com/v1/oauth/token";
22
347
  var OAUTH_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
@@ -33,18 +358,19 @@ function ensureProfilesDir() {
33
358
  mkdirSync(PROFILES_DIR, { recursive: true });
34
359
  }
35
360
  function getProfileDir(id) {
36
- return join(PROFILES_DIR, id);
361
+ return join2(PROFILES_DIR, id);
37
362
  }
38
363
  function buildAuthLoginEnv(configDir, _options = {}, baseEnv = process.env) {
39
- const env = { ...baseEnv };
364
+ const env2 = { ...baseEnv };
40
365
  if (configDir)
41
- env.CLAUDE_CONFIG_DIR = configDir;
42
- return env;
366
+ env2.CLAUDE_CONFIG_DIR = configDir;
367
+ return env2;
43
368
  }
44
369
  function base64Url(bytes) {
45
370
  return bytes.toString("base64url");
46
371
  }
47
- function createManualOAuthSession() {
372
+ function createManualOAuthSession(scopes) {
373
+ const scopeList = scopes ?? OAUTH_SCOPES;
48
374
  const codeVerifier = base64Url(randomBytes(32));
49
375
  const state = base64Url(randomBytes(32));
50
376
  const codeChallenge = createHash("sha256").update(codeVerifier).digest("base64url");
@@ -53,7 +379,7 @@ function createManualOAuthSession() {
53
379
  url.searchParams.set("client_id", OAUTH_CLIENT_ID);
54
380
  url.searchParams.set("response_type", "code");
55
381
  url.searchParams.set("redirect_uri", OAUTH_REDIRECT_URI);
56
- url.searchParams.set("scope", OAUTH_SCOPES.join(" "));
382
+ url.searchParams.set("scope", scopeList.join(" "));
57
383
  url.searchParams.set("code_challenge", codeChallenge);
58
384
  url.searchParams.set("code_challenge_method", "S256");
59
385
  url.searchParams.set("state", state);
@@ -78,7 +404,7 @@ function parseAuthorizationCodeInput(input) {
78
404
  return code ? { code, state } : null;
79
405
  }
80
406
  function loadProfileConfig() {
81
- if (!existsSync(CONFIG_FILE))
407
+ if (!existsSync2(CONFIG_FILE))
82
408
  return [];
83
409
  try {
84
410
  return JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
@@ -186,7 +512,7 @@ async function profileAdd(id, options = {}) {
186
512
  console.error(` Run: meridian profile list`);
187
513
  process.exit(1);
188
514
  }
189
- const defaultClaudeDir = join(homedir(), ".claude");
515
+ const defaultClaudeDir = join2(homedir(), ".claude");
190
516
  const alreadyImported = profiles.some((p) => p.claudeConfigDir === defaultClaudeDir);
191
517
  if (!alreadyImported) {
192
518
  const defaultAuth = getAuthStatus(defaultClaudeDir);
@@ -317,7 +643,7 @@ function dirsToRemoveOnProfileRemove(profile, profilesDir) {
317
643
  dirs.push(profile.claudeConfigDir);
318
644
  }
319
645
  if (profile.oauthToken || profile.type === "oauth-token") {
320
- const isolationDir = join(profilesDir, profile.id);
646
+ const isolationDir = join2(profilesDir, profile.id);
321
647
  if (!dirs.includes(isolationDir))
322
648
  dirs.push(isolationDir);
323
649
  }
@@ -339,7 +665,7 @@ function profileRemove(id) {
339
665
  profiles.splice(idx, 1);
340
666
  saveProfileConfig(profiles);
341
667
  for (const dir of dirsToRemove) {
342
- if (existsSync(dir))
668
+ if (existsSync2(dir))
343
669
  rmSync(dir, { recursive: true, force: true });
344
670
  }
345
671
  console.log(`\x1B[32m✓ Profile "${id}" removed.\x1B[0m`);
@@ -498,16 +824,5 @@ Examples:
498
824
  meridian profile switch work # Switch to work account
499
825
  meridian profile list # Show all profiles`);
500
826
  }
501
- export {
502
- profileSwitch,
503
- profileRemove,
504
- profileLogin,
505
- profileList,
506
- profileHelp,
507
- profileAddOauthToken,
508
- profileAdd,
509
- parseAuthorizationCodeInput,
510
- dirsToRemoveOnProfileRemove,
511
- createManualOAuthSession,
512
- buildAuthLoginEnv
513
- };
827
+
828
+ export { env, envBool, resolvePassthrough, envInt, init_env, CANONICAL_SONNET_MODEL, resolveSdkModelDefaults, explicitModelPin, mapModelToClaudeModel, recordExtendedContextUnavailable, stripExtendedContext, hasExtendedContext, getAuthCacheInfo, getClaudeAuthStatusAsync, getResolvedClaudeExecutableInfo, resolveClaudeExecutableAsync, isClosedControllerError, OAUTH_TOKEN_URL, OAUTH_CLIENT_ID, OAUTH_REDIRECT_URI, buildAuthLoginEnv, createManualOAuthSession, parseAuthorizationCodeInput, profileAdd, profileAddOauthToken, profileList, dirsToRemoveOnProfileRemove, profileRemove, profileSwitch, profileLogin, profileHelp };