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 +26 -24
- package/dist/{cli-x0mnz7nm.js → cli-m99nmtqk.js} +250 -53
- package/dist/cli.js +25 -14
- package/dist/server.js +3 -1
- package/package.json +1 -1
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
|
-
|
|
21
|
-
│
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
│
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
│
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
│
|
|
32
|
-
|
|
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 {
|
|
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
|
|
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 {
|
|
12075
|
-
|
|
12076
|
-
|
|
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
|
|
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
|
|
12127
|
-
const
|
|
12128
|
-
|
|
12129
|
-
|
|
12130
|
-
|
|
12131
|
-
|
|
12132
|
-
|
|
12133
|
-
|
|
12134
|
-
|
|
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
|
|
12145
|
-
|
|
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
|
-
|
|
12148
|
-
|
|
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
|
-
|
|
12336
|
-
|
|
12337
|
-
|
|
12338
|
-
|
|
12339
|
-
|
|
12340
|
-
|
|
12341
|
-
|
|
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
|
-
|
|
12344
|
-
|
|
12345
|
-
|
|
12346
|
-
}
|
|
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 =
|
|
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
|
-
|
|
12887
|
-
|
|
12888
|
-
|
|
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
|
|
13034
|
-
const auth = JSON.parse(
|
|
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-
|
|
5
|
+
} from "./cli-m99nmtqk.js";
|
|
5
6
|
|
|
6
7
|
// bin/cli.ts
|
|
7
|
-
import {
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
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-
|
|
6
|
+
} from "./cli-m99nmtqk.js";
|
|
6
7
|
export {
|
|
7
8
|
startProxyServer,
|
|
9
|
+
getMaxSessionsLimit,
|
|
8
10
|
createProxyServer,
|
|
9
11
|
clearSessionCache
|
|
10
12
|
};
|