opencode-claude-max-proxy 1.12.0 → 1.12.2

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 CHANGED
@@ -15,30 +15,22 @@ We avoid that limitation by forwarding tool calls instead of executing them. Whe
15
15
  From Claude’s perspective, tool usage proceeds normally. From OpenCode’s perspective, it is interacting with the Anthropic API. Execution remains distributed according to the configured agents, allowing different models to handle different roles without being constrained by the SDK.
16
16
 
17
17
  ```
18
- ┌──────────┐ ┌───────────────┐ ┌──────────────┐
19
- │ │ │ │ │
20
- OpenCode │ ─────────────► │ Proxy │ ───────────► │ Claude Max │
21
- │ │ (localhost) │ │ (Agent SDK) │
22
- │ │ │ │ │ │
23
- │ │ │ │ ◄─────────── │ tool_use │
24
- │ │ ◄──────────── │ │ │
25
- │ │ │ Intercept │ └──────────────┘
26
- │ │ │ (stop turn) │
27
- │ │ │ │
28
- │ │ ▼
29
- │ │ │ ┌──────────────┐
30
- │ │ │ │ OpenCode │
31
- │ │ │ agent │
32
- │ │ │ │ system │
33
- │ │ │ └──────────────┘
34
- │ │ │ │
35
- │ │ │ ▼
36
- │ │ │ ◄────────────
37
- │ │ │ Resume turn
38
- │ │ │
39
- │ │ ◄───────────── │
40
- │ │ response │
41
- └──────────┘ └───────────────┘
18
+ OpenCode ──► Proxy (localhost) ──► Claude Max (Agent SDK)
19
+
20
+ tool_use response
21
+
22
+ Proxy intercepts ◄─────────┘
23
+ (stop turn)
24
+
25
+
26
+ OpenCode agent system
27
+ (routes to GPT-5.4, Gemini, etc.)
28
+
29
+
30
+ Proxy resumes SDK ──► Claude continues
31
+
32
+
33
+ OpenCode ◄── final response
42
34
  ```
43
35
 
44
36
  ## How Passthrough Works
@@ -54,6 +46,14 @@ The Claude Agent SDK exposes a `PreToolUse` hook that fires before any tool exec
54
46
 
55
47
  ## Quick Start
56
48
 
49
+ ### One-Liner Install
50
+
51
+ ```bash
52
+ curl -fsSL https://raw.githubusercontent.com/ianjwhite99/opencode-with-claude/main/install.sh | bash
53
+ ```
54
+
55
+ Installs everything (Claude CLI, OpenCode, proxy) and gives you an `oc` command. See [opencode-with-claude](https://github.com/ianjwhite99/opencode-with-claude) for details.
56
+
57
57
  ### Prerequisites
58
58
 
59
59
  1. **Claude Max subscription** — [Subscribe here](https://claude.ai/settings/billing)
@@ -361,3 +361,5 @@ MIT
361
361
  ## Credits
362
362
 
363
363
  Built with the [Claude Agent SDK](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk) by Anthropic.
364
+
365
+ [opencode-with-claude](https://github.com/ianjwhite99/opencode-with-claude) installer by [@ianjwhite99](https://github.com/ianjwhite99). Multimodal support based on work by [@juanferreiramorel](https://github.com/juanferreiramorel). Per-terminal proxy idea by [@calebdw](https://github.com/calebdw). README cleanup by [@skipships](https://github.com/skipships).
@@ -1,3 +1,4 @@
1
+ import { createRequire } from "node:module";
1
2
  var __defProp = Object.defineProperty;
2
3
  var __returnValue = (v) => v;
3
4
  function __exportSetter(name, newValue) {
@@ -12,6 +13,7 @@ var __export = (target, all) => {
12
13
  set: __exportSetter.bind(all, name)
13
14
  });
14
15
  };
16
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
15
17
 
16
18
  // node_modules/hono/dist/compose.js
17
19
  var compose = (middleware, onError, onNotFound) => {
@@ -2239,10 +2241,11 @@ var claudeLog = (event, extra) => {
2239
2241
  };
2240
2242
 
2241
2243
  // src/proxy/server.ts
2242
- import { execSync } from "child_process";
2244
+ import { exec as execCallback } from "child_process";
2243
2245
  import { existsSync as existsSync2 } from "fs";
2244
2246
  import { fileURLToPath as fileURLToPath3 } from "url";
2245
- import { join as join2, dirname as dirname3 } from "path";
2247
+ import { join as join2, dirname as dirname2 } from "path";
2248
+ import { promisify as promisify2 } from "util";
2246
2249
 
2247
2250
  // src/mcpTools.ts
2248
2251
  import { createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk";
@@ -12071,10 +12074,53 @@ function stripMcpPrefix(toolName) {
12071
12074
  }
12072
12075
 
12073
12076
  // src/proxy/sessionStore.ts
12074
- import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync } from "fs";
12075
- import { join } from "path";
12076
- import { homedir } from "os";
12077
+ import {
12078
+ closeSync,
12079
+ existsSync,
12080
+ mkdirSync,
12081
+ openSync,
12082
+ readFileSync,
12083
+ renameSync,
12084
+ statSync,
12085
+ unlinkSync,
12086
+ writeFileSync
12087
+ } from "node:fs";
12088
+ import { homedir } from "node:os";
12089
+ import { join } from "node:path";
12077
12090
  var SESSION_TTL_MS = 24 * 60 * 60 * 1000;
12091
+ var STALE_LOCK_THRESHOLD_MS = 30000;
12092
+ function acquireLock(lockPath) {
12093
+ try {
12094
+ const fd = openSync(lockPath, "wx");
12095
+ closeSync(fd);
12096
+ return true;
12097
+ } catch (e) {
12098
+ const err = e;
12099
+ if (err.code !== "EEXIST") {
12100
+ console.error("[sessionStore] lock acquire failed:", err.message);
12101
+ return false;
12102
+ }
12103
+ try {
12104
+ const stat = statSync(lockPath);
12105
+ if (Date.now() - stat.mtimeMs > STALE_LOCK_THRESHOLD_MS) {
12106
+ unlinkSync(lockPath);
12107
+ const fd = openSync(lockPath, "wx");
12108
+ closeSync(fd);
12109
+ return true;
12110
+ }
12111
+ } catch (staleError) {
12112
+ console.error("[sessionStore] stale lock recovery failed:", staleError.message);
12113
+ }
12114
+ return false;
12115
+ }
12116
+ }
12117
+ function releaseLock(lockPath) {
12118
+ try {
12119
+ unlinkSync(lockPath);
12120
+ } catch (e) {
12121
+ console.error("[sessionStore] lock release failed:", e.message);
12122
+ }
12123
+ }
12078
12124
  function getStorePath() {
12079
12125
  const dir = process.env.CLAUDE_PROXY_SESSION_DIR || join(homedir(), ".cache", "opencode-claude-max-proxy");
12080
12126
  if (!existsSync(dir)) {
@@ -12097,20 +12143,24 @@ function readStore() {
12097
12143
  }
12098
12144
  }
12099
12145
  return pruned;
12100
- } catch {
12146
+ } catch (e) {
12147
+ console.error("[sessionStore] read failed:", e.message);
12101
12148
  return {};
12102
12149
  }
12103
12150
  }
12104
12151
  function writeStore(store) {
12105
12152
  const path3 = getStorePath();
12106
- const tmp = path3 + ".tmp";
12153
+ const tmp = `${path3}.tmp`;
12107
12154
  try {
12108
12155
  writeFileSync(tmp, JSON.stringify(store, null, 2));
12109
12156
  renameSync(tmp, path3);
12110
- } catch {
12157
+ } catch (e) {
12158
+ console.error("[sessionStore] write failed:", e.message);
12111
12159
  try {
12112
12160
  writeFileSync(path3, JSON.stringify(store, null, 2));
12113
- } catch {}
12161
+ } catch (directWriteError) {
12162
+ console.error("[sessionStore] write failed:", directWriteError.message);
12163
+ }
12114
12164
  }
12115
12165
  }
12116
12166
  function lookupSharedSession(key) {
@@ -12123,29 +12173,152 @@ function lookupSharedSession(key) {
12123
12173
  return session;
12124
12174
  }
12125
12175
  function storeSharedSession(key, claudeSessionId, messageCount) {
12126
- const store = readStore();
12127
- const existing = store[key];
12128
- store[key] = {
12129
- claudeSessionId,
12130
- createdAt: existing?.createdAt || Date.now(),
12131
- lastUsedAt: Date.now(),
12132
- messageCount: messageCount ?? existing?.messageCount ?? 0
12133
- };
12134
- writeStore(store);
12176
+ const path3 = getStorePath();
12177
+ const lockPath = `${path3}.lock`;
12178
+ const hasLock = acquireLock(lockPath);
12179
+ if (!hasLock) {
12180
+ console.warn("[sessionStore] could not acquire lock, proceeding without");
12181
+ }
12182
+ try {
12183
+ const store = readStore();
12184
+ const existing = store[key];
12185
+ store[key] = {
12186
+ claudeSessionId,
12187
+ createdAt: existing?.createdAt || Date.now(),
12188
+ lastUsedAt: Date.now(),
12189
+ messageCount: messageCount ?? existing?.messageCount ?? 0
12190
+ };
12191
+ writeStore(store);
12192
+ } finally {
12193
+ if (hasLock) {
12194
+ releaseLock(lockPath);
12195
+ }
12196
+ }
12135
12197
  }
12136
12198
  function clearSharedSessions() {
12137
12199
  const path3 = getStorePath();
12138
12200
  try {
12139
12201
  writeFileSync(path3, "{}");
12140
- } catch {}
12202
+ } catch (e) {
12203
+ console.error("[sessionStore] clear failed:", e.message);
12204
+ }
12205
+ }
12206
+
12207
+ // src/utils/lruMap.ts
12208
+ class LRUMap {
12209
+ maxSize;
12210
+ onEvict;
12211
+ map = new Map;
12212
+ constructor(maxSize, onEvict) {
12213
+ this.maxSize = maxSize;
12214
+ this.onEvict = onEvict;
12215
+ }
12216
+ get size() {
12217
+ return this.map.size;
12218
+ }
12219
+ get(key) {
12220
+ const value = this.map.get(key);
12221
+ if (value === undefined)
12222
+ return;
12223
+ this.map.delete(key);
12224
+ this.map.set(key, value);
12225
+ return value;
12226
+ }
12227
+ set(key, value) {
12228
+ if (this.map.has(key)) {
12229
+ this.map.delete(key);
12230
+ } else if (this.map.size >= this.maxSize) {
12231
+ this.evictOldest();
12232
+ }
12233
+ this.map.set(key, value);
12234
+ return this;
12235
+ }
12236
+ has(key) {
12237
+ return this.map.has(key);
12238
+ }
12239
+ delete(key) {
12240
+ return this.map.delete(key);
12241
+ }
12242
+ clear() {
12243
+ this.map.clear();
12244
+ }
12245
+ entries() {
12246
+ return this.map.entries();
12247
+ }
12248
+ keys() {
12249
+ return this.map.keys();
12250
+ }
12251
+ values() {
12252
+ return this.map.values();
12253
+ }
12254
+ forEach(callbackfn) {
12255
+ this.map.forEach((value, key) => callbackfn(value, key, this));
12256
+ }
12257
+ [Symbol.iterator]() {
12258
+ return this.map[Symbol.iterator]();
12259
+ }
12260
+ evictOldest() {
12261
+ const oldestKey = this.map.keys().next().value;
12262
+ if (oldestKey === undefined)
12263
+ return;
12264
+ const oldestValue = this.map.get(oldestKey);
12265
+ if (oldestValue === undefined)
12266
+ return;
12267
+ this.map.delete(oldestKey);
12268
+ this.onEvict?.(oldestKey, oldestValue);
12269
+ }
12141
12270
  }
12142
12271
 
12143
12272
  // src/proxy/server.ts
12144
- var sessionCache = new Map;
12145
- var fingerprintCache = new Map;
12273
+ var DEFAULT_MAX_SESSIONS = 1000;
12274
+ function getMaxSessionsLimit() {
12275
+ const raw2 = process.env.CLAUDE_PROXY_MAX_SESSIONS;
12276
+ if (!raw2)
12277
+ return DEFAULT_MAX_SESSIONS;
12278
+ const parsed = Number.parseInt(raw2, 10);
12279
+ if (!Number.isFinite(parsed) || parsed <= 0) {
12280
+ console.warn(`[PROXY] Invalid CLAUDE_PROXY_MAX_SESSIONS value "${raw2}"; using default ${DEFAULT_MAX_SESSIONS}`);
12281
+ return DEFAULT_MAX_SESSIONS;
12282
+ }
12283
+ return parsed;
12284
+ }
12285
+ function removeFingerprintEntriesByClaudeSessionId(claudeSessionId) {
12286
+ for (const [key, state] of fingerprintCache.entries()) {
12287
+ if (state.claudeSessionId === claudeSessionId) {
12288
+ fingerprintCache.delete(key);
12289
+ }
12290
+ }
12291
+ }
12292
+ function removeSessionEntriesByClaudeSessionId(claudeSessionId) {
12293
+ for (const [key, state] of sessionCache.entries()) {
12294
+ if (state.claudeSessionId === claudeSessionId) {
12295
+ sessionCache.delete(key);
12296
+ }
12297
+ }
12298
+ }
12299
+ function createSessionCache(maxSize) {
12300
+ return new LRUMap(maxSize, (_key, evictedState) => {
12301
+ removeFingerprintEntriesByClaudeSessionId(evictedState.claudeSessionId);
12302
+ });
12303
+ }
12304
+ function createFingerprintCache(maxSize) {
12305
+ return new LRUMap(maxSize, (_key, evictedState) => {
12306
+ removeSessionEntriesByClaudeSessionId(evictedState.claudeSessionId);
12307
+ });
12308
+ }
12309
+ var activeMaxSessions = getMaxSessionsLimit();
12310
+ var sessionCache = createSessionCache(activeMaxSessions);
12311
+ var fingerprintCache = createFingerprintCache(activeMaxSessions);
12146
12312
  function clearSessionCache() {
12147
- sessionCache.clear();
12148
- fingerprintCache.clear();
12313
+ const configuredLimit = getMaxSessionsLimit();
12314
+ if (configuredLimit !== activeMaxSessions) {
12315
+ activeMaxSessions = configuredLimit;
12316
+ sessionCache = createSessionCache(activeMaxSessions);
12317
+ fingerprintCache = createFingerprintCache(activeMaxSessions);
12318
+ } else {
12319
+ sessionCache.clear();
12320
+ fingerprintCache.clear();
12321
+ }
12149
12322
  try {
12150
12323
  clearSharedSessions();
12151
12324
  } catch {}
@@ -12332,21 +12505,40 @@ var ALLOWED_MCP_TOOLS = [
12332
12505
  `mcp__${MCP_SERVER_NAME}__glob`,
12333
12506
  `mcp__${MCP_SERVER_NAME}__grep`
12334
12507
  ];
12335
- function resolveClaudeExecutable() {
12336
- try {
12337
- const sdkPath = fileURLToPath3(import.meta.resolve("@anthropic-ai/claude-agent-sdk"));
12338
- const sdkCliJs = join2(dirname3(sdkPath), "cli.js");
12339
- if (existsSync2(sdkCliJs))
12340
- return sdkCliJs;
12341
- } catch {}
12508
+ var exec2 = promisify2(execCallback);
12509
+ var cachedClaudePath = null;
12510
+ var cachedClaudePathPromise = null;
12511
+ var claudeExecutable = "";
12512
+ async function resolveClaudeExecutableAsync() {
12513
+ if (cachedClaudePath)
12514
+ return cachedClaudePath;
12515
+ if (cachedClaudePathPromise)
12516
+ return cachedClaudePathPromise;
12517
+ cachedClaudePathPromise = (async () => {
12518
+ try {
12519
+ const sdkPath = fileURLToPath3(import.meta.resolve("@anthropic-ai/claude-agent-sdk"));
12520
+ const sdkCliJs = join2(dirname2(sdkPath), "cli.js");
12521
+ if (existsSync2(sdkCliJs)) {
12522
+ cachedClaudePath = sdkCliJs;
12523
+ return sdkCliJs;
12524
+ }
12525
+ } catch {}
12526
+ try {
12527
+ const { stdout } = await exec2("which claude");
12528
+ const claudePath = stdout.trim();
12529
+ if (claudePath && existsSync2(claudePath)) {
12530
+ cachedClaudePath = claudePath;
12531
+ return claudePath;
12532
+ }
12533
+ } catch {}
12534
+ throw new Error("Could not find Claude Code executable. Install via: npm install -g @anthropic-ai/claude-code");
12535
+ })();
12342
12536
  try {
12343
- const claudePath = execSync("which claude", { encoding: "utf-8" }).trim();
12344
- if (claudePath && existsSync2(claudePath))
12345
- return claudePath;
12346
- } catch {}
12347
- throw new Error("Could not find Claude Code executable. Install via: npm install -g @anthropic-ai/claude-code");
12537
+ return await cachedClaudePathPromise;
12538
+ } finally {
12539
+ cachedClaudePathPromise = null;
12540
+ }
12348
12541
  }
12349
- var claudeExecutable = resolveClaudeExecutable();
12350
12542
  function mapModelToClaudeModel(model) {
12351
12543
  if (model.includes("opus"))
12352
12544
  return "opus";
@@ -12482,13 +12674,6 @@ IMPORTANT: When using the task/Task tool, the subagent_type parameter must be on
12482
12674
  }
12483
12675
  }
12484
12676
  } else {
12485
- if (systemContext) {
12486
- structured.push({
12487
- type: "user",
12488
- message: { role: "user", content: systemContext },
12489
- parent_tool_use_id: null
12490
- });
12491
- }
12492
12677
  for (const m of messagesToConvert) {
12493
12678
  if (m.role === "user") {
12494
12679
  structured.push({
@@ -12556,9 +12741,7 @@ IMPORTANT: When using the task/Task tool, the subagent_type parameter must be on
12556
12741
  }).join(`
12557
12742
 
12558
12743
  `) || "";
12559
- prompt = !isResume && systemContext ? `${systemContext}
12560
-
12561
- ${conversationParts}` : conversationParts;
12744
+ prompt = conversationParts;
12562
12745
  }
12563
12746
  const passthrough = Boolean(process.env.CLAUDE_PROXY_PASSTHROUGH);
12564
12747
  const capturedToolUses = [];
@@ -12603,6 +12786,9 @@ ${conversationParts}` : conversationParts;
12603
12786
  let currentSessionId;
12604
12787
  claudeLog("upstream.start", { mode: "non_stream", model });
12605
12788
  try {
12789
+ if (!claudeExecutable) {
12790
+ claudeExecutable = await resolveClaudeExecutableAsync();
12791
+ }
12606
12792
  const response = query({
12607
12793
  prompt,
12608
12794
  options: {
@@ -12612,6 +12798,9 @@ ${conversationParts}` : conversationParts;
12612
12798
  pathToClaudeCodeExecutable: claudeExecutable,
12613
12799
  permissionMode: "bypassPermissions",
12614
12800
  allowDangerouslySkipPermissions: true,
12801
+ ...systemContext ? {
12802
+ systemPrompt: { type: "preset", preset: "claude_code", append: systemContext }
12803
+ } : {},
12615
12804
  ...passthrough ? {
12616
12805
  disallowedTools: [...BLOCKED_BUILTIN_TOOLS, ...CLAUDE_CODE_ONLY_TOOLS],
12617
12806
  ...passthroughMcp ? {
@@ -12759,6 +12948,9 @@ ${conversationParts}` : conversationParts;
12759
12948
  includePartialMessages: true,
12760
12949
  permissionMode: "bypassPermissions",
12761
12950
  allowDangerouslySkipPermissions: true,
12951
+ ...systemContext ? {
12952
+ systemPrompt: { type: "preset", preset: "claude_code", append: systemContext }
12953
+ } : {},
12762
12954
  ...passthrough ? {
12763
12955
  disallowedTools: [...BLOCKED_BUILTIN_TOOLS, ...CLAUDE_CODE_ONLY_TOOLS],
12764
12956
  ...passthroughMcp ? {
@@ -12799,6 +12991,7 @@ ${conversationParts}` : conversationParts;
12799
12991
  }
12800
12992
  }, 15000);
12801
12993
  const skipBlockIndices = new Set;
12994
+ const streamedToolUseIds = new Set;
12802
12995
  let messageStartEmitted = false;
12803
12996
  try {
12804
12997
  for await (const message of response) {
@@ -12836,6 +13029,8 @@ ${conversationParts}` : conversationParts;
12836
13029
  if (block?.type === "tool_use" && typeof block.name === "string") {
12837
13030
  if (passthrough && block.name.startsWith(PASSTHROUGH_MCP_PREFIX)) {
12838
13031
  block.name = stripMcpPrefix(block.name);
13032
+ if (block.id)
13033
+ streamedToolUseIds.add(block.id);
12839
13034
  } else if (block.name.startsWith("mcp__")) {
12840
13035
  if (eventIndex !== undefined)
12841
13036
  skipBlockIndices.add(eventIndex);
@@ -12883,9 +13078,10 @@ data: ${JSON.stringify(event)}
12883
13078
  storeSession(opencodeSessionId, body.messages || [], currentSessionId);
12884
13079
  }
12885
13080
  if (!streamClosed) {
12886
- if (passthrough && capturedToolUses.length > 0 && messageStartEmitted) {
12887
- for (let i = 0;i < capturedToolUses.length; i++) {
12888
- const tu = capturedToolUses[i];
13081
+ const unseenToolUses = capturedToolUses.filter((tu) => !streamedToolUseIds.has(tu.id));
13082
+ if (passthrough && unseenToolUses.length > 0 && messageStartEmitted) {
13083
+ for (let i = 0;i < unseenToolUses.length; i++) {
13084
+ const tu = unseenToolUses[i];
12889
13085
  const blockIndex = eventsForwarded + i;
12890
13086
  safeEnqueue(encoder.encode(`event: content_block_start
12891
13087
  data: ${JSON.stringify({
@@ -13028,10 +13224,10 @@ data: ${JSON.stringify({
13028
13224
  };
13029
13225
  app.post("/v1/messages", (c) => handleWithQueue(c, "/v1/messages"));
13030
13226
  app.post("/messages", (c) => handleWithQueue(c, "/messages"));
13031
- app.get("/health", (c) => {
13227
+ app.get("/health", async (c) => {
13032
13228
  try {
13033
- const authJson = execSync("claude auth status", { encoding: "utf-8", timeout: 5000 });
13034
- const auth = JSON.parse(authJson);
13229
+ const { stdout } = await exec2("claude auth status", { timeout: 5000 });
13230
+ const auth = JSON.parse(stdout);
13035
13231
  if (!auth.loggedIn) {
13036
13232
  return c.json({
13037
13233
  status: "unhealthy",
@@ -13063,6 +13259,7 @@ data: ${JSON.stringify({
13063
13259
  return { app, config: finalConfig };
13064
13260
  }
13065
13261
  async function startProxyServer(config = {}) {
13262
+ claudeExecutable = await resolveClaudeExecutableAsync();
13066
13263
  const { app, config: finalConfig } = createProxyServer(config);
13067
13264
  const server = serve({
13068
13265
  fetch: app.fetch,
@@ -13095,4 +13292,4 @@ Or use a different port:`);
13095
13292
  return server;
13096
13293
  }
13097
13294
 
13098
- export { clearSessionCache, createProxyServer, startProxyServer };
13295
+ export { __require, getMaxSessionsLimit, clearSessionCache, createProxyServer, startProxyServer };
package/dist/cli.js CHANGED
@@ -1,10 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ __require,
3
4
  startProxyServer
4
- } from "./cli-x0mnz7nm.js";
5
+ } from "./cli-m99nmtqk.js";
5
6
 
6
7
  // bin/cli.ts
7
- import { execSync } from "child_process";
8
+ import { exec as execCallback } from "child_process";
9
+ import { promisify } from "util";
10
+ var exec = promisify(execCallback);
8
11
  process.on("uncaughtException", (err) => {
9
12
  console.error(`[PROXY] Uncaught exception (recovered): ${err.message}`);
10
13
  });
@@ -14,17 +17,25 @@ process.on("unhandledRejection", (reason) => {
14
17
  var port = parseInt(process.env.CLAUDE_PROXY_PORT || "3456", 10);
15
18
  var host = process.env.CLAUDE_PROXY_HOST || "127.0.0.1";
16
19
  var idleTimeoutSeconds = parseInt(process.env.CLAUDE_PROXY_IDLE_TIMEOUT_SECONDS || "120", 10);
17
- try {
18
- const authJson = execSync("claude auth status", { encoding: "utf-8", timeout: 5000 });
19
- const auth = JSON.parse(authJson);
20
- if (!auth.loggedIn) {
21
- console.error("\x1B[31m✗ Not logged in to Claude.\x1B[0m Run: claude login");
22
- process.exit(1);
20
+ async function runCli(start = startProxyServer, runExec = exec) {
21
+ try {
22
+ const { stdout } = await runExec("claude auth status", { timeout: 5000 });
23
+ const auth = JSON.parse(stdout);
24
+ if (!auth.loggedIn) {
25
+ console.error("\x1B[31m✗ Not logged in to Claude.\x1B[0m Run: claude login");
26
+ process.exit(1);
27
+ }
28
+ if (auth.subscriptionType !== "max") {
29
+ console.error(`\x1B[33m⚠ Claude subscription: ${auth.subscriptionType || "unknown"} (Max recommended)\x1B[0m`);
30
+ }
31
+ } catch {
32
+ console.error("\x1B[33m⚠ Could not verify Claude auth status. If requests fail, run: claude login\x1B[0m");
23
33
  }
24
- if (auth.subscriptionType !== "max") {
25
- console.error(`\x1B[33m⚠ Claude subscription: ${auth.subscriptionType || "unknown"} (Max recommended)\x1B[0m`);
26
- }
27
- } catch {
28
- console.error("\x1B[33m⚠ Could not verify Claude auth status. If requests fail, run: claude login\x1B[0m");
34
+ await start({ port, host, idleTimeoutSeconds });
35
+ }
36
+ if (__require.main == __require.module) {
37
+ await runCli();
29
38
  }
30
- await startProxyServer({ port, host, idleTimeoutSeconds });
39
+ export {
40
+ runCli
41
+ };
package/dist/server.js CHANGED
@@ -1,10 +1,12 @@
1
1
  import {
2
2
  clearSessionCache,
3
3
  createProxyServer,
4
+ getMaxSessionsLimit,
4
5
  startProxyServer
5
- } from "./cli-x0mnz7nm.js";
6
+ } from "./cli-m99nmtqk.js";
6
7
  export {
7
8
  startProxyServer,
9
+ getMaxSessionsLimit,
8
10
  createProxyServer,
9
11
  clearSessionCache
10
12
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-claude-max-proxy",
3
- "version": "1.12.0",
3
+ "version": "1.12.2",
4
4
  "description": "Use your Claude Max subscription with OpenCode via proxy server",
5
5
  "type": "module",
6
6
  "main": "./dist/server.js",