@khalilgharbaoui/opencode-claude-code-plugin 0.1.5 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -0
- package/dist/index.d.ts +84 -9
- package/dist/index.js +472 -165
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -102,7 +102,7 @@ var CLAUDE_INTERNAL_TOOLS = /* @__PURE__ */ new Set([
|
|
|
102
102
|
"Agent",
|
|
103
103
|
"AskFollowupQuestion"
|
|
104
104
|
]);
|
|
105
|
-
function mapTool(name, input) {
|
|
105
|
+
function mapTool(name, input, opts) {
|
|
106
106
|
if (CLAUDE_INTERNAL_TOOLS.has(name)) {
|
|
107
107
|
log.debug("skipping Claude CLI internal tool", { name });
|
|
108
108
|
return { name, input, executed: true, skip: true };
|
|
@@ -115,8 +115,13 @@ function mapTool(name, input) {
|
|
|
115
115
|
}
|
|
116
116
|
if (name === "WebSearch" || name === "web_search") {
|
|
117
117
|
const mappedInput = input?.query ? { query: input.query } : input;
|
|
118
|
-
|
|
119
|
-
|
|
118
|
+
const route = opts?.webSearch;
|
|
119
|
+
if (route && route !== "claude" && route !== "disabled") {
|
|
120
|
+
log.debug("routing WebSearch to opencode tool", { target: route, mappedInput });
|
|
121
|
+
return { name: route, input: mappedInput, executed: false };
|
|
122
|
+
}
|
|
123
|
+
log.debug("WebSearch executed by Claude CLI", { mappedInput });
|
|
124
|
+
return { name: "WebSearch", input: mappedInput, executed: true };
|
|
120
125
|
}
|
|
121
126
|
if (name === "TaskOutput") {
|
|
122
127
|
if (!input) return { name: "bash", executed: false };
|
|
@@ -377,7 +382,8 @@ import * as fs from "fs";
|
|
|
377
382
|
import * as path from "path";
|
|
378
383
|
import * as os from "os";
|
|
379
384
|
import * as crypto from "crypto";
|
|
380
|
-
var
|
|
385
|
+
var FILE_NAMES = ["opencode.jsonc", "opencode.json", "config.json"];
|
|
386
|
+
var PROJECT_FILE_NAMES = ["opencode.json", "opencode.jsonc"];
|
|
381
387
|
function fileExists(p) {
|
|
382
388
|
try {
|
|
383
389
|
return fs.statSync(p).isFile();
|
|
@@ -385,35 +391,12 @@ function fileExists(p) {
|
|
|
385
391
|
return false;
|
|
386
392
|
}
|
|
387
393
|
}
|
|
388
|
-
function
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
394
|
+
function dirExists(p) {
|
|
395
|
+
try {
|
|
396
|
+
return fs.statSync(p).isDirectory();
|
|
397
|
+
} catch {
|
|
398
|
+
return false;
|
|
392
399
|
}
|
|
393
|
-
return null;
|
|
394
|
-
}
|
|
395
|
-
function walkUpForConfig(startDir) {
|
|
396
|
-
const closestFirst = [];
|
|
397
|
-
let dir = path.resolve(startDir);
|
|
398
|
-
while (true) {
|
|
399
|
-
const hit = findConfigInDir(dir);
|
|
400
|
-
if (hit) closestFirst.push(hit);
|
|
401
|
-
const dotdir = path.join(dir, ".opencode");
|
|
402
|
-
const dothit = findConfigInDir(dotdir);
|
|
403
|
-
if (dothit) closestFirst.push(dothit);
|
|
404
|
-
const parent = path.dirname(dir);
|
|
405
|
-
if (parent === dir) break;
|
|
406
|
-
dir = parent;
|
|
407
|
-
}
|
|
408
|
-
return closestFirst.reverse();
|
|
409
|
-
}
|
|
410
|
-
function globalConfigs() {
|
|
411
|
-
const out = [];
|
|
412
|
-
const xdg = process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config");
|
|
413
|
-
const dir = path.join(xdg, "opencode");
|
|
414
|
-
const hit = findConfigInDir(dir);
|
|
415
|
-
if (hit) out.push(hit);
|
|
416
|
-
return out;
|
|
417
400
|
}
|
|
418
401
|
function stripJsonComments(text) {
|
|
419
402
|
let out = "";
|
|
@@ -454,26 +437,120 @@ function stripJsonComments(text) {
|
|
|
454
437
|
}
|
|
455
438
|
return out;
|
|
456
439
|
}
|
|
457
|
-
function
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
440
|
+
function readAndParse(file) {
|
|
441
|
+
try {
|
|
442
|
+
const raw = fs.readFileSync(file, "utf8");
|
|
443
|
+
return JSON.parse(stripJsonComments(raw));
|
|
444
|
+
} catch (e) {
|
|
445
|
+
log.warn("failed to parse opencode config", {
|
|
446
|
+
file,
|
|
447
|
+
error: e instanceof Error ? e.message : String(e)
|
|
448
|
+
});
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
function isPlainObject(x) {
|
|
453
|
+
return typeof x === "object" && x !== null && !Array.isArray(x);
|
|
454
|
+
}
|
|
455
|
+
function deepMerge(target, source) {
|
|
456
|
+
const out = { ...target };
|
|
457
|
+
for (const [k, v] of Object.entries(source)) {
|
|
458
|
+
if (v === void 0) continue;
|
|
459
|
+
const existing = out[k];
|
|
460
|
+
if (isPlainObject(existing) && isPlainObject(v)) {
|
|
461
|
+
out[k] = deepMerge(existing, v);
|
|
462
|
+
} else {
|
|
463
|
+
out[k] = v;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
return out;
|
|
467
|
+
}
|
|
468
|
+
function walkUp(opts) {
|
|
469
|
+
const out = [];
|
|
470
|
+
let current = path.resolve(opts.start);
|
|
471
|
+
while (true) {
|
|
472
|
+
for (const target of opts.targets) {
|
|
473
|
+
const candidate = path.join(current, target);
|
|
474
|
+
if (opts.predicate(candidate)) out.push(candidate);
|
|
475
|
+
}
|
|
476
|
+
if (opts.stop && current === path.resolve(opts.stop)) break;
|
|
477
|
+
const parent = path.dirname(current);
|
|
478
|
+
if (parent === current) break;
|
|
479
|
+
current = parent;
|
|
480
|
+
}
|
|
481
|
+
return out;
|
|
482
|
+
}
|
|
483
|
+
function detectWorktree(cwd) {
|
|
484
|
+
const override = process.env.OPENCODE_WORKTREE;
|
|
485
|
+
if (override) return path.resolve(override);
|
|
486
|
+
let current = path.resolve(cwd);
|
|
487
|
+
while (true) {
|
|
488
|
+
const gitPath = path.join(current, ".git");
|
|
489
|
+
try {
|
|
490
|
+
if (fs.existsSync(gitPath)) return current;
|
|
491
|
+
} catch {
|
|
492
|
+
}
|
|
493
|
+
const parent = path.dirname(current);
|
|
494
|
+
if (parent === current) return void 0;
|
|
495
|
+
current = parent;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
function globalConfigDir() {
|
|
499
|
+
const xdg = process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config");
|
|
500
|
+
return path.join(xdg, "opencode");
|
|
501
|
+
}
|
|
502
|
+
function loadGlobalConfig() {
|
|
503
|
+
const dir = globalConfigDir();
|
|
504
|
+
let merged = {};
|
|
505
|
+
for (const name of FILE_NAMES.slice().reverse()) {
|
|
506
|
+
const file = path.join(dir, name);
|
|
507
|
+
if (!fileExists(file)) continue;
|
|
508
|
+
const parsed = readAndParse(file);
|
|
509
|
+
if (parsed) merged = deepMerge(merged, parsed);
|
|
510
|
+
}
|
|
511
|
+
return merged;
|
|
512
|
+
}
|
|
513
|
+
function loadProjectFilesInDir(dir) {
|
|
514
|
+
let merged = {};
|
|
515
|
+
for (const name of PROJECT_FILE_NAMES) {
|
|
516
|
+
const file = path.join(dir, name);
|
|
517
|
+
if (!fileExists(file)) continue;
|
|
518
|
+
const parsed = readAndParse(file);
|
|
519
|
+
if (parsed) merged = deepMerge(merged, parsed);
|
|
520
|
+
}
|
|
521
|
+
return merged;
|
|
522
|
+
}
|
|
523
|
+
function dotOpencodeDirs(cwd, worktree) {
|
|
524
|
+
const dirs = [];
|
|
525
|
+
const seen = /* @__PURE__ */ new Set();
|
|
526
|
+
const push = (p) => {
|
|
527
|
+
const abs = path.resolve(p);
|
|
528
|
+
if (!seen.has(abs) && dirExists(abs)) {
|
|
529
|
+
seen.add(abs);
|
|
530
|
+
dirs.push(abs);
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
for (const dir of walkUp({
|
|
534
|
+
start: cwd,
|
|
535
|
+
stop: worktree,
|
|
536
|
+
targets: [".opencode"],
|
|
537
|
+
predicate: dirExists
|
|
538
|
+
})) {
|
|
539
|
+
push(dir);
|
|
540
|
+
}
|
|
541
|
+
const home = os.homedir();
|
|
542
|
+
if (home) {
|
|
543
|
+
const homeDot = path.join(home, ".opencode");
|
|
544
|
+
if (dirExists(homeDot)) push(homeDot);
|
|
545
|
+
}
|
|
546
|
+
const envDir = process.env.OPENCODE_CONFIG_DIR;
|
|
547
|
+
if (envDir && dirExists(envDir)) push(envDir);
|
|
548
|
+
return dirs;
|
|
472
549
|
}
|
|
473
550
|
function translateServer(name, spec) {
|
|
474
|
-
if (!spec || typeof spec !== "object") return null;
|
|
475
551
|
if (spec.enabled === false) return null;
|
|
476
|
-
|
|
552
|
+
const type = spec.type;
|
|
553
|
+
if (type === "local") {
|
|
477
554
|
const cmd = spec.command;
|
|
478
555
|
if (!Array.isArray(cmd) || cmd.length === 0) {
|
|
479
556
|
log.warn("skipping local MCP server with no command", { name });
|
|
@@ -489,8 +566,8 @@ function translateServer(name, spec) {
|
|
|
489
566
|
}
|
|
490
567
|
return out;
|
|
491
568
|
}
|
|
492
|
-
if (
|
|
493
|
-
if (
|
|
569
|
+
if (type === "remote") {
|
|
570
|
+
if (typeof spec.url !== "string" || !spec.url) {
|
|
494
571
|
log.warn("skipping remote MCP server with no url", { name });
|
|
495
572
|
return null;
|
|
496
573
|
}
|
|
@@ -505,36 +582,73 @@ function translateServer(name, spec) {
|
|
|
505
582
|
}
|
|
506
583
|
log.warn("skipping MCP server with unknown type", {
|
|
507
584
|
name,
|
|
508
|
-
type:
|
|
585
|
+
type: type ?? null
|
|
509
586
|
});
|
|
510
587
|
return null;
|
|
511
588
|
}
|
|
512
|
-
function
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
589
|
+
function extractMcpBlock(config) {
|
|
590
|
+
const mcp = config.mcp;
|
|
591
|
+
if (!mcp || typeof mcp !== "object" || Array.isArray(mcp)) return {};
|
|
592
|
+
return mcp;
|
|
593
|
+
}
|
|
594
|
+
function mergeMcp(target, source) {
|
|
595
|
+
const out = { ...target };
|
|
596
|
+
for (const [name, spec] of Object.entries(source)) {
|
|
597
|
+
if (!spec || typeof spec !== "object") continue;
|
|
598
|
+
const existing = out[name];
|
|
599
|
+
if (existing && typeof existing === "object") {
|
|
600
|
+
out[name] = deepMerge(
|
|
601
|
+
existing,
|
|
602
|
+
spec
|
|
603
|
+
);
|
|
604
|
+
} else {
|
|
605
|
+
out[name] = spec;
|
|
606
|
+
}
|
|
522
607
|
}
|
|
608
|
+
return out;
|
|
523
609
|
}
|
|
524
|
-
function bridgeOpencodeMcp(cwd) {
|
|
525
|
-
const
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
const
|
|
531
|
-
if (
|
|
532
|
-
|
|
533
|
-
|
|
610
|
+
function bridgeOpencodeMcp(cwd, runtimeStatus) {
|
|
611
|
+
const worktree = detectWorktree(cwd);
|
|
612
|
+
let merged = {};
|
|
613
|
+
merged = mergeMcp(merged, extractMcpBlock(loadGlobalConfig()));
|
|
614
|
+
const explicitConfig = process.env.OPENCODE_CONFIG;
|
|
615
|
+
if (explicitConfig && fileExists(explicitConfig)) {
|
|
616
|
+
const parsed = readAndParse(explicitConfig);
|
|
617
|
+
if (parsed) merged = mergeMcp(merged, extractMcpBlock(parsed));
|
|
618
|
+
}
|
|
619
|
+
const projectFiles = walkUp({
|
|
620
|
+
start: cwd,
|
|
621
|
+
stop: worktree,
|
|
622
|
+
targets: PROJECT_FILE_NAMES,
|
|
623
|
+
predicate: fileExists
|
|
624
|
+
});
|
|
625
|
+
const projectDirs = [];
|
|
626
|
+
const seenProjectDirs = /* @__PURE__ */ new Set();
|
|
627
|
+
for (const f of projectFiles) {
|
|
628
|
+
const d = path.dirname(f);
|
|
629
|
+
if (!seenProjectDirs.has(d)) {
|
|
630
|
+
seenProjectDirs.add(d);
|
|
631
|
+
projectDirs.push(d);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
for (const dir of projectDirs.slice().reverse()) {
|
|
635
|
+
merged = mergeMcp(merged, extractMcpBlock(loadProjectFilesInDir(dir)));
|
|
636
|
+
}
|
|
637
|
+
for (const dir of dotOpencodeDirs(cwd, worktree)) {
|
|
638
|
+
merged = mergeMcp(merged, extractMcpBlock(loadProjectFilesInDir(dir)));
|
|
639
|
+
}
|
|
640
|
+
if (runtimeStatus) {
|
|
641
|
+
for (const name of Object.keys(merged)) {
|
|
642
|
+
const status = runtimeStatus[name];
|
|
643
|
+
if (status === void 0) continue;
|
|
644
|
+
const existing = merged[name];
|
|
645
|
+
const base = existing && typeof existing === "object" ? existing : {};
|
|
646
|
+
merged[name] = { ...base, enabled: status === "connected" };
|
|
534
647
|
}
|
|
535
648
|
}
|
|
536
649
|
const servers = {};
|
|
537
650
|
for (const [name, spec] of Object.entries(merged)) {
|
|
651
|
+
if (!spec || typeof spec !== "object") continue;
|
|
538
652
|
const translated = translateServer(name, spec);
|
|
539
653
|
if (translated) servers[name] = translated;
|
|
540
654
|
}
|
|
@@ -556,11 +670,41 @@ function bridgeOpencodeMcp(cwd) {
|
|
|
556
670
|
return null;
|
|
557
671
|
}
|
|
558
672
|
log.info("bridged opencode MCP config", {
|
|
559
|
-
sources: files,
|
|
560
673
|
target: outPath,
|
|
674
|
+
hash,
|
|
561
675
|
servers: Object.keys(servers)
|
|
562
676
|
});
|
|
563
|
-
return outPath;
|
|
677
|
+
return { path: outPath, hash };
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// src/runtime-status.ts
|
|
681
|
+
var opencodeClient = null;
|
|
682
|
+
function setOpencodeClient(client) {
|
|
683
|
+
if (client && typeof client === "object") {
|
|
684
|
+
opencodeClient = client;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
async function getRuntimeMcpStatus() {
|
|
688
|
+
const client = opencodeClient;
|
|
689
|
+
if (!client?.mcp?.status) return void 0;
|
|
690
|
+
try {
|
|
691
|
+
const res = await client.mcp.status();
|
|
692
|
+
const data = res.data;
|
|
693
|
+
if (!data || typeof data !== "object") return void 0;
|
|
694
|
+
const out = {};
|
|
695
|
+
for (const [name, entry] of Object.entries(data)) {
|
|
696
|
+
if (entry && typeof entry === "object") {
|
|
697
|
+
const status = entry.status;
|
|
698
|
+
if (typeof status === "string") out[name] = status;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
return out;
|
|
702
|
+
} catch (err) {
|
|
703
|
+
log.warn("failed to fetch opencode MCP runtime status", {
|
|
704
|
+
error: err instanceof Error ? err.message : String(err)
|
|
705
|
+
});
|
|
706
|
+
return void 0;
|
|
707
|
+
}
|
|
564
708
|
}
|
|
565
709
|
|
|
566
710
|
// src/session-manager.ts
|
|
@@ -598,6 +742,15 @@ function deleteActiveProcess(key) {
|
|
|
598
742
|
activeProcesses.delete(key);
|
|
599
743
|
}
|
|
600
744
|
}
|
|
745
|
+
function evictAllSessions(reason) {
|
|
746
|
+
const count = activeProcesses.size;
|
|
747
|
+
if (count === 0) return 0;
|
|
748
|
+
log.info("evicting all claude processes", { reason, count });
|
|
749
|
+
for (const key of Array.from(activeProcesses.keys())) {
|
|
750
|
+
deleteActiveProcess(key);
|
|
751
|
+
}
|
|
752
|
+
return count;
|
|
753
|
+
}
|
|
601
754
|
function getClaudeSessionId(key) {
|
|
602
755
|
return claudeSessions.get(key);
|
|
603
756
|
}
|
|
@@ -607,7 +760,7 @@ function setClaudeSessionId(key, sessionId) {
|
|
|
607
760
|
function deleteClaudeSessionId(key) {
|
|
608
761
|
claudeSessions.delete(key);
|
|
609
762
|
}
|
|
610
|
-
function spawnClaudeProcess(cliPath, cliArgs, cwd, sessionKey2, proxyServer) {
|
|
763
|
+
function spawnClaudeProcess(cliPath, cliArgs, cwd, sessionKey2, proxyServer, mcpHash) {
|
|
611
764
|
evictIfNeeded();
|
|
612
765
|
log.info("spawning new claude process", { cliPath, cliArgs, cwd, sessionKey: sessionKey2 });
|
|
613
766
|
const proc = spawn(cliPath, cliArgs, {
|
|
@@ -624,8 +777,16 @@ function spawnClaudeProcess(cliPath, cliArgs, cwd, sessionKey2, proxyServer) {
|
|
|
624
777
|
rl.on("close", () => {
|
|
625
778
|
lineEmitter.emit("close");
|
|
626
779
|
});
|
|
627
|
-
const ap = {
|
|
780
|
+
const ap = {
|
|
781
|
+
proc,
|
|
782
|
+
lineEmitter,
|
|
783
|
+
proxyServer: proxyServer ?? null,
|
|
784
|
+
mcpHash
|
|
785
|
+
};
|
|
628
786
|
activeProcesses.set(sessionKey2, ap);
|
|
787
|
+
proc.on("error", (err) => {
|
|
788
|
+
log.error("claude process error", { sessionKey: sessionKey2, error: err.message });
|
|
789
|
+
});
|
|
629
790
|
proc.on("exit", (code, signal) => {
|
|
630
791
|
log.info("claude process exited", { code, signal, sessionKey: sessionKey2 });
|
|
631
792
|
void proxyServer?.close();
|
|
@@ -1139,18 +1300,27 @@ var ClaudeCodeLanguageModel = class {
|
|
|
1139
1300
|
return "no-tools";
|
|
1140
1301
|
}
|
|
1141
1302
|
/**
|
|
1142
|
-
* Build the combined `--mcp-config` list
|
|
1143
|
-
*
|
|
1144
|
-
*
|
|
1303
|
+
* Build the combined `--mcp-config` list and return both the list and the
|
|
1304
|
+
* hash of the bridged opencode MCP block (or null when bridging is off /
|
|
1305
|
+
* yields nothing). The hash is used to detect mid-session config changes
|
|
1306
|
+
* and respawn the underlying claude process.
|
|
1307
|
+
*
|
|
1308
|
+
* `runtimeStatus` is a snapshot of opencode's `client.mcp.status()`. When
|
|
1309
|
+
* provided it overlays opencode's UI-toggled state on top of disk config
|
|
1310
|
+
* so `/mcps` toggles propagate without a config file write.
|
|
1145
1311
|
*/
|
|
1146
|
-
effectiveMcpConfig(cwd, proxyConfigPath) {
|
|
1147
|
-
const
|
|
1312
|
+
effectiveMcpConfig(cwd, proxyConfigPath, runtimeStatus) {
|
|
1313
|
+
const paths = Array.isArray(this.config.mcpConfig) ? this.config.mcpConfig.slice() : this.config.mcpConfig ? [this.config.mcpConfig] : [];
|
|
1314
|
+
let bridgedHash = null;
|
|
1148
1315
|
if (this.config.bridgeOpencodeMcp !== false) {
|
|
1149
|
-
const bridged = bridgeOpencodeMcp(cwd);
|
|
1150
|
-
if (bridged)
|
|
1316
|
+
const bridged = bridgeOpencodeMcp(cwd, runtimeStatus);
|
|
1317
|
+
if (bridged) {
|
|
1318
|
+
paths.push(bridged.path);
|
|
1319
|
+
bridgedHash = bridged.hash;
|
|
1320
|
+
}
|
|
1151
1321
|
}
|
|
1152
|
-
if (proxyConfigPath)
|
|
1153
|
-
return
|
|
1322
|
+
if (proxyConfigPath) paths.push(proxyConfigPath);
|
|
1323
|
+
return { paths, bridgedHash };
|
|
1154
1324
|
}
|
|
1155
1325
|
/** Resolve ProxyToolDef[] for the configured proxyTools names. */
|
|
1156
1326
|
resolvedProxyTools() {
|
|
@@ -1484,14 +1654,16 @@ var ClaudeCodeLanguageModel = class {
|
|
|
1484
1654
|
includeHistoryContext,
|
|
1485
1655
|
reasoningEffort
|
|
1486
1656
|
);
|
|
1657
|
+
const runtimeStatus = await getRuntimeMcpStatus();
|
|
1487
1658
|
const cliArgs = buildCliArgs({
|
|
1488
1659
|
sessionKey: sk,
|
|
1489
1660
|
skipPermissions: this.config.skipPermissions !== false,
|
|
1490
1661
|
includeSessionId: false,
|
|
1491
1662
|
model: this.modelId,
|
|
1492
1663
|
permissionMode: this.config.permissionMode,
|
|
1493
|
-
mcpConfig: this.effectiveMcpConfig(cwd),
|
|
1494
|
-
strictMcpConfig: this.config.strictMcpConfig
|
|
1664
|
+
mcpConfig: this.effectiveMcpConfig(cwd, void 0, runtimeStatus).paths,
|
|
1665
|
+
strictMcpConfig: this.config.strictMcpConfig,
|
|
1666
|
+
disallowedTools: this.config.webSearch === "disabled" ? ["WebSearch"] : void 0
|
|
1495
1667
|
});
|
|
1496
1668
|
log.info("doGenerate starting", {
|
|
1497
1669
|
cwd,
|
|
@@ -1661,7 +1833,7 @@ ${plan}
|
|
|
1661
1833
|
input: mappedInput,
|
|
1662
1834
|
executed,
|
|
1663
1835
|
skip
|
|
1664
|
-
} = mapTool(tc.name, tc.args);
|
|
1836
|
+
} = mapTool(tc.name, tc.args, { webSearch: this.config.webSearch });
|
|
1665
1837
|
if (skip) continue;
|
|
1666
1838
|
content.push({
|
|
1667
1839
|
type: "tool-call",
|
|
@@ -1762,6 +1934,7 @@ ${plan}
|
|
|
1762
1934
|
const self = this;
|
|
1763
1935
|
const pendingProxyCall = getPendingProxyCall(sk);
|
|
1764
1936
|
const pendingProxyResult = pendingProxyCall ? this.extractPendingProxyResult(options.prompt, pendingProxyCall.toolCallId) : null;
|
|
1937
|
+
const runtimeStatus = await getRuntimeMcpStatus();
|
|
1765
1938
|
log.info("doStream starting", {
|
|
1766
1939
|
cwd,
|
|
1767
1940
|
model: this.modelId,
|
|
@@ -1777,25 +1950,55 @@ ${plan}
|
|
|
1777
1950
|
let proc;
|
|
1778
1951
|
let lineEmitter;
|
|
1779
1952
|
let proxyServer = activeProcess?.proxyServer ?? null;
|
|
1953
|
+
if (activeProcess && self.config.hotReloadMcp !== false && self.config.bridgeOpencodeMcp !== false) {
|
|
1954
|
+
const probe = self.effectiveMcpConfig(cwd, void 0, runtimeStatus);
|
|
1955
|
+
const previousHash = activeProcess.mcpHash ?? null;
|
|
1956
|
+
if (previousHash !== probe.bridgedHash) {
|
|
1957
|
+
log.info("opencode MCP config changed, respawning claude", {
|
|
1958
|
+
sk,
|
|
1959
|
+
previousHash,
|
|
1960
|
+
currentHash: probe.bridgedHash
|
|
1961
|
+
});
|
|
1962
|
+
deleteActiveProcess(sk);
|
|
1963
|
+
activeProcess = void 0;
|
|
1964
|
+
proxyServer = null;
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1780
1967
|
const setup = async () => {
|
|
1781
1968
|
if (!proxyServer && resolvedProxy) {
|
|
1782
1969
|
proxyServer = await self.ensureProxyServer(resolvedProxy, sk);
|
|
1783
1970
|
}
|
|
1971
|
+
const proxyDisallowed = resolvedProxy ? disallowedToolFlags(resolvedProxy) : [];
|
|
1972
|
+
const extraDisallowed = [];
|
|
1973
|
+
if (self.config.webSearch === "disabled") extraDisallowed.push("WebSearch");
|
|
1974
|
+
const allDisallowed = [...proxyDisallowed, ...extraDisallowed];
|
|
1975
|
+
const mcp = self.effectiveMcpConfig(
|
|
1976
|
+
cwd,
|
|
1977
|
+
proxyServer?.configPath(),
|
|
1978
|
+
runtimeStatus
|
|
1979
|
+
);
|
|
1784
1980
|
const cliArgs = buildCliArgs({
|
|
1785
1981
|
sessionKey: sk,
|
|
1786
1982
|
skipPermissions,
|
|
1787
1983
|
model: self.modelId,
|
|
1788
1984
|
permissionMode: self.config.permissionMode,
|
|
1789
|
-
mcpConfig:
|
|
1985
|
+
mcpConfig: mcp.paths,
|
|
1790
1986
|
strictMcpConfig: self.config.strictMcpConfig,
|
|
1791
|
-
disallowedTools:
|
|
1987
|
+
disallowedTools: allDisallowed.length > 0 ? allDisallowed : void 0
|
|
1792
1988
|
});
|
|
1793
1989
|
if (activeProcess) {
|
|
1794
1990
|
proc = activeProcess.proc;
|
|
1795
1991
|
lineEmitter = activeProcess.lineEmitter;
|
|
1796
1992
|
log.debug("reusing active process", { sk });
|
|
1797
1993
|
} else {
|
|
1798
|
-
const ap = spawnClaudeProcess(
|
|
1994
|
+
const ap = spawnClaudeProcess(
|
|
1995
|
+
cliPath,
|
|
1996
|
+
cliArgs,
|
|
1997
|
+
cwd,
|
|
1998
|
+
sk,
|
|
1999
|
+
proxyServer,
|
|
2000
|
+
mcp.bridgedHash
|
|
2001
|
+
);
|
|
1799
2002
|
proc = ap.proc;
|
|
1800
2003
|
lineEmitter = ap.lineEmitter;
|
|
1801
2004
|
activeProcess = ap;
|
|
@@ -1868,10 +2071,7 @@ ${plan}
|
|
|
1868
2071
|
}
|
|
1869
2072
|
});
|
|
1870
2073
|
controllerClosed = true;
|
|
1871
|
-
|
|
1872
|
-
lineEmitter.off("close", closeHandler);
|
|
1873
|
-
pendingProxyUnsubscribe?.();
|
|
1874
|
-
pendingProxyUnsubscribe = null;
|
|
2074
|
+
cleanupTurn();
|
|
1875
2075
|
try {
|
|
1876
2076
|
controller.close();
|
|
1877
2077
|
} catch {
|
|
@@ -1930,7 +2130,11 @@ ${plan}
|
|
|
1930
2130
|
inputJson: ""
|
|
1931
2131
|
});
|
|
1932
2132
|
if (block.name !== "AskUserQuestion" && block.name !== "ask_user_question" && block.name !== "ExitPlanMode" && !block.name.startsWith(PROXY_TOOL_PREFIX)) {
|
|
1933
|
-
const { name: mappedName, skip, executed } = mapTool(
|
|
2133
|
+
const { name: mappedName, skip, executed } = mapTool(
|
|
2134
|
+
block.name,
|
|
2135
|
+
void 0,
|
|
2136
|
+
{ webSearch: self.config.webSearch }
|
|
2137
|
+
);
|
|
1934
2138
|
if (!skip) {
|
|
1935
2139
|
controller.enqueue({
|
|
1936
2140
|
type: "tool-input-start",
|
|
@@ -2047,7 +2251,7 @@ ${plan}
|
|
|
2047
2251
|
input: mappedInput,
|
|
2048
2252
|
executed,
|
|
2049
2253
|
skip
|
|
2050
|
-
} = mapTool(tc.name, parsedInput);
|
|
2254
|
+
} = mapTool(tc.name, parsedInput, { webSearch: self.config.webSearch });
|
|
2051
2255
|
if (!skip) {
|
|
2052
2256
|
toolCallsById.set(tc.id, {
|
|
2053
2257
|
id: tc.id,
|
|
@@ -2167,7 +2371,7 @@ ${plan}
|
|
|
2167
2371
|
input: mappedInput,
|
|
2168
2372
|
executed,
|
|
2169
2373
|
skip
|
|
2170
|
-
} = mapTool(block.name, parsedInput);
|
|
2374
|
+
} = mapTool(block.name, parsedInput, { webSearch: self.config.webSearch });
|
|
2171
2375
|
if (!skip) {
|
|
2172
2376
|
if (!executed) skipResultForIds.add(block.id);
|
|
2173
2377
|
controller.enqueue({
|
|
@@ -2287,8 +2491,7 @@ ${plan}
|
|
|
2287
2491
|
}
|
|
2288
2492
|
});
|
|
2289
2493
|
controllerClosed = true;
|
|
2290
|
-
|
|
2291
|
-
lineEmitter.off("close", closeHandler);
|
|
2494
|
+
cleanupTurn();
|
|
2292
2495
|
try {
|
|
2293
2496
|
controller.close();
|
|
2294
2497
|
} catch {
|
|
@@ -2303,12 +2506,8 @@ ${plan}
|
|
|
2303
2506
|
const closeHandler = () => {
|
|
2304
2507
|
log.debug("readline closed");
|
|
2305
2508
|
if (controllerClosed) return;
|
|
2306
|
-
clearFallbackTimer();
|
|
2307
2509
|
controllerClosed = true;
|
|
2308
|
-
|
|
2309
|
-
lineEmitter.off("close", closeHandler);
|
|
2310
|
-
pendingProxyUnsubscribe?.();
|
|
2311
|
-
pendingProxyUnsubscribe = null;
|
|
2510
|
+
cleanupTurn();
|
|
2312
2511
|
endTextBlock();
|
|
2313
2512
|
controller.enqueue({
|
|
2314
2513
|
type: "finish",
|
|
@@ -2323,6 +2522,28 @@ ${plan}
|
|
|
2323
2522
|
} catch {
|
|
2324
2523
|
}
|
|
2325
2524
|
};
|
|
2525
|
+
let cleanedUp = false;
|
|
2526
|
+
const cleanupTurn = () => {
|
|
2527
|
+
if (cleanedUp) return;
|
|
2528
|
+
cleanedUp = true;
|
|
2529
|
+
clearFallbackTimer();
|
|
2530
|
+
lineEmitter.off("line", lineHandler);
|
|
2531
|
+
lineEmitter.off("close", closeHandler);
|
|
2532
|
+
pendingProxyUnsubscribe?.();
|
|
2533
|
+
pendingProxyUnsubscribe = null;
|
|
2534
|
+
proc.off("error", procErrorHandler);
|
|
2535
|
+
};
|
|
2536
|
+
const procErrorHandler = (err) => {
|
|
2537
|
+
log.error("process error", { error: err.message });
|
|
2538
|
+
if (controllerClosed) return;
|
|
2539
|
+
controllerClosed = true;
|
|
2540
|
+
cleanupTurn();
|
|
2541
|
+
controller.enqueue({ type: "error", error: err });
|
|
2542
|
+
try {
|
|
2543
|
+
controller.close();
|
|
2544
|
+
} catch {
|
|
2545
|
+
}
|
|
2546
|
+
};
|
|
2326
2547
|
lineEmitter.on("line", lineHandler);
|
|
2327
2548
|
lineEmitter.on("close", closeHandler);
|
|
2328
2549
|
pendingProxyUnsubscribe = onPendingProxyCall(sk, (call) => {
|
|
@@ -2333,19 +2554,7 @@ ${plan}
|
|
|
2333
2554
|
});
|
|
2334
2555
|
finishWithToolCall(call);
|
|
2335
2556
|
});
|
|
2336
|
-
proc.on("error",
|
|
2337
|
-
log.error("process error", { error: err.message });
|
|
2338
|
-
clearFallbackTimer();
|
|
2339
|
-
if (controllerClosed) return;
|
|
2340
|
-
controllerClosed = true;
|
|
2341
|
-
pendingProxyUnsubscribe?.();
|
|
2342
|
-
pendingProxyUnsubscribe = null;
|
|
2343
|
-
controller.enqueue({ type: "error", error: err });
|
|
2344
|
-
try {
|
|
2345
|
-
controller.close();
|
|
2346
|
-
} catch {
|
|
2347
|
-
}
|
|
2348
|
-
});
|
|
2557
|
+
proc.on("error", procErrorHandler);
|
|
2349
2558
|
if (options.abortSignal) {
|
|
2350
2559
|
options.abortSignal.addEventListener("abort", () => {
|
|
2351
2560
|
if (turnCompleted || controllerClosed) return;
|
|
@@ -2355,10 +2564,7 @@ ${plan}
|
|
|
2355
2564
|
{ cwd }
|
|
2356
2565
|
);
|
|
2357
2566
|
controllerClosed = true;
|
|
2358
|
-
|
|
2359
|
-
lineEmitter.off("close", closeHandler);
|
|
2360
|
-
pendingProxyUnsubscribe?.();
|
|
2361
|
-
pendingProxyUnsubscribe = null;
|
|
2567
|
+
cleanupTurn();
|
|
2362
2568
|
try {
|
|
2363
2569
|
controller.close();
|
|
2364
2570
|
} catch {
|
|
@@ -2457,10 +2663,43 @@ function defineModel(opts) {
|
|
|
2457
2663
|
var haikuCost = { input: 1e-6, output: 5e-6, cacheRead: 1e-7, cacheWrite: 125e-8 };
|
|
2458
2664
|
var sonnetCost = { input: 3e-6, output: 15e-6, cacheRead: 3e-7, cacheWrite: 375e-8 };
|
|
2459
2665
|
var opusCost = { input: 15e-6, output: 75e-6, cacheRead: 15e-7, cacheWrite: 1875e-8 };
|
|
2666
|
+
function toConfigModel(model) {
|
|
2667
|
+
const inputMods = [];
|
|
2668
|
+
const outputMods = [];
|
|
2669
|
+
for (const [k, v] of Object.entries(model.capabilities.input)) {
|
|
2670
|
+
if (v) inputMods.push(k);
|
|
2671
|
+
}
|
|
2672
|
+
for (const [k, v] of Object.entries(model.capabilities.output)) {
|
|
2673
|
+
if (v) outputMods.push(k);
|
|
2674
|
+
}
|
|
2675
|
+
return {
|
|
2676
|
+
id: model.api.id,
|
|
2677
|
+
name: model.name,
|
|
2678
|
+
status: model.status,
|
|
2679
|
+
family: model.family ?? "",
|
|
2680
|
+
release_date: model.release_date,
|
|
2681
|
+
temperature: model.capabilities.temperature,
|
|
2682
|
+
reasoning: model.capabilities.reasoning,
|
|
2683
|
+
attachment: model.capabilities.attachment,
|
|
2684
|
+
tool_call: model.capabilities.toolcall,
|
|
2685
|
+
modalities: { input: inputMods, output: outputMods },
|
|
2686
|
+
interleaved: model.capabilities.interleaved,
|
|
2687
|
+
cost: {
|
|
2688
|
+
input: model.cost.input,
|
|
2689
|
+
output: model.cost.output,
|
|
2690
|
+
cache_read: model.cost.cache.read,
|
|
2691
|
+
cache_write: model.cost.cache.write
|
|
2692
|
+
},
|
|
2693
|
+
limit: model.limit,
|
|
2694
|
+
options: model.options,
|
|
2695
|
+
headers: model.headers,
|
|
2696
|
+
variants: model.variants
|
|
2697
|
+
};
|
|
2698
|
+
}
|
|
2460
2699
|
var defaultModels = {
|
|
2461
2700
|
"claude-haiku-4-5": defineModel({
|
|
2462
2701
|
id: "claude-haiku-4-5",
|
|
2463
|
-
name: "Claude
|
|
2702
|
+
name: "Claude Haiku 4.5",
|
|
2464
2703
|
family: "haiku",
|
|
2465
2704
|
reasoning: false,
|
|
2466
2705
|
context: 2e5,
|
|
@@ -2470,7 +2709,7 @@ var defaultModels = {
|
|
|
2470
2709
|
}),
|
|
2471
2710
|
"claude-sonnet-4-5": defineModel({
|
|
2472
2711
|
id: "claude-sonnet-4-5",
|
|
2473
|
-
name: "Claude
|
|
2712
|
+
name: "Claude Sonnet 4.5",
|
|
2474
2713
|
family: "sonnet",
|
|
2475
2714
|
reasoning: true,
|
|
2476
2715
|
context: 1e6,
|
|
@@ -2480,7 +2719,7 @@ var defaultModels = {
|
|
|
2480
2719
|
}),
|
|
2481
2720
|
"claude-sonnet-4-6": defineModel({
|
|
2482
2721
|
id: "claude-sonnet-4-6",
|
|
2483
|
-
name: "Claude
|
|
2722
|
+
name: "Claude Sonnet 4.6",
|
|
2484
2723
|
family: "sonnet",
|
|
2485
2724
|
reasoning: true,
|
|
2486
2725
|
context: 1e6,
|
|
@@ -2490,7 +2729,7 @@ var defaultModels = {
|
|
|
2490
2729
|
}),
|
|
2491
2730
|
"claude-opus-4-5": defineModel({
|
|
2492
2731
|
id: "claude-opus-4-5",
|
|
2493
|
-
name: "Claude
|
|
2732
|
+
name: "Claude Opus 4.5",
|
|
2494
2733
|
family: "opus",
|
|
2495
2734
|
reasoning: true,
|
|
2496
2735
|
context: 1e6,
|
|
@@ -2500,7 +2739,7 @@ var defaultModels = {
|
|
|
2500
2739
|
}),
|
|
2501
2740
|
"claude-opus-4-6": defineModel({
|
|
2502
2741
|
id: "claude-opus-4-6",
|
|
2503
|
-
name: "Claude
|
|
2742
|
+
name: "Claude Opus 4.6",
|
|
2504
2743
|
family: "opus",
|
|
2505
2744
|
reasoning: true,
|
|
2506
2745
|
context: 1e6,
|
|
@@ -2510,7 +2749,7 @@ var defaultModels = {
|
|
|
2510
2749
|
}),
|
|
2511
2750
|
"claude-opus-4-7": defineModel({
|
|
2512
2751
|
id: "claude-opus-4-7",
|
|
2513
|
-
name: "Claude
|
|
2752
|
+
name: "Claude Opus 4.7",
|
|
2514
2753
|
family: "opus",
|
|
2515
2754
|
reasoning: true,
|
|
2516
2755
|
context: 1e6,
|
|
@@ -2569,7 +2808,15 @@ async function ensureAccountRuntime(account, baseCliPath) {
|
|
|
2569
2808
|
if (!configDir) return { cliPath: baseCliPath };
|
|
2570
2809
|
const expandedConfigDir = expandHome(configDir);
|
|
2571
2810
|
await mkdir(expandedConfigDir, { recursive: true });
|
|
2572
|
-
|
|
2811
|
+
try {
|
|
2812
|
+
await ensureSharedCapabilities(expandedConfigDir);
|
|
2813
|
+
} catch (err) {
|
|
2814
|
+
log.warn("failed to symlink shared capabilities; continuing anyway", {
|
|
2815
|
+
account,
|
|
2816
|
+
configDir: expandedConfigDir,
|
|
2817
|
+
error: String(err)
|
|
2818
|
+
});
|
|
2819
|
+
}
|
|
2573
2820
|
const cliPath = await writeAccountWrapper(
|
|
2574
2821
|
normalizeAccountName(account),
|
|
2575
2822
|
baseCliPath,
|
|
@@ -2675,7 +2922,9 @@ function createClaudeCode(settings = {}) {
|
|
|
2675
2922
|
controlRequestBehavior: settings.controlRequestBehavior ?? "allow",
|
|
2676
2923
|
controlRequestToolBehaviors: settings.controlRequestToolBehaviors,
|
|
2677
2924
|
controlRequestDenyMessage: settings.controlRequestDenyMessage,
|
|
2678
|
-
proxyTools
|
|
2925
|
+
proxyTools,
|
|
2926
|
+
webSearch: settings.webSearch,
|
|
2927
|
+
hotReloadMcp: settings.hotReloadMcp ?? true
|
|
2679
2928
|
});
|
|
2680
2929
|
};
|
|
2681
2930
|
const provider = function(modelId) {
|
|
@@ -2742,6 +2991,31 @@ function defaultModelsForProvider(providerModels, providerID = PROVIDER_ID2, mod
|
|
|
2742
2991
|
}
|
|
2743
2992
|
return models;
|
|
2744
2993
|
}
|
|
2994
|
+
function configModelsForProvider(providerModels, providerID, modelSuffix) {
|
|
2995
|
+
const models = {};
|
|
2996
|
+
for (const [id, model] of Object.entries(defaultModels)) {
|
|
2997
|
+
const modelId = modelSuffix ? `${id}@${modelSuffix}` : id;
|
|
2998
|
+
const existing = providerModels[id] ?? providerModels[modelId];
|
|
2999
|
+
const full = {
|
|
3000
|
+
...model,
|
|
3001
|
+
id: modelId,
|
|
3002
|
+
providerID,
|
|
3003
|
+
api: {
|
|
3004
|
+
...model.api,
|
|
3005
|
+
id: modelId,
|
|
3006
|
+
npm: existing?.api?.npm ?? model.api.npm,
|
|
3007
|
+
url: existing?.api?.url ?? model.api.url
|
|
3008
|
+
}
|
|
3009
|
+
};
|
|
3010
|
+
models[modelId] = toConfigModel(full);
|
|
3011
|
+
}
|
|
3012
|
+
for (const [id, model] of Object.entries(providerModels)) {
|
|
3013
|
+
if (!(id in models)) {
|
|
3014
|
+
models[id] = toConfigModel({ ...model, providerID });
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
return models;
|
|
3018
|
+
}
|
|
2745
3019
|
async function providerConfig(existing, providerID = PROVIDER_ID2, optionDefaults = {}, displayName) {
|
|
2746
3020
|
const mergedOptions = {
|
|
2747
3021
|
cliPath: "claude",
|
|
@@ -2769,47 +3043,80 @@ async function expandAccountProviders(config) {
|
|
|
2769
3043
|
if (!accounts) return false;
|
|
2770
3044
|
config.provider ??= {};
|
|
2771
3045
|
const seedOptions = cleanProviderOptions(seed?.options);
|
|
3046
|
+
let expandedCount = 0;
|
|
2772
3047
|
for (const account of accounts) {
|
|
2773
3048
|
const providerID = accountProviderId(account);
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
3049
|
+
try {
|
|
3050
|
+
const existing = config.provider[providerID];
|
|
3051
|
+
const modelSuffix = accountModelSuffix(account);
|
|
3052
|
+
config.provider[providerID] = {
|
|
3053
|
+
...existing,
|
|
3054
|
+
...await providerConfig(
|
|
3055
|
+
existing,
|
|
3056
|
+
providerID,
|
|
3057
|
+
{
|
|
3058
|
+
...seedOptions,
|
|
3059
|
+
account
|
|
3060
|
+
},
|
|
3061
|
+
accountDisplayName(account)
|
|
3062
|
+
),
|
|
3063
|
+
models: configModelsForProvider(
|
|
3064
|
+
existing?.models ?? seed?.models ?? {},
|
|
3065
|
+
providerID,
|
|
3066
|
+
modelSuffix
|
|
3067
|
+
)
|
|
3068
|
+
};
|
|
3069
|
+
expandedCount++;
|
|
3070
|
+
} catch (err) {
|
|
3071
|
+
log.error("failed to expand account provider", {
|
|
3072
|
+
account,
|
|
2789
3073
|
providerID,
|
|
2790
|
-
|
|
2791
|
-
)
|
|
2792
|
-
}
|
|
3074
|
+
error: String(err)
|
|
3075
|
+
});
|
|
3076
|
+
}
|
|
2793
3077
|
}
|
|
2794
|
-
|
|
2795
|
-
|
|
3078
|
+
if (expandedCount > 0) {
|
|
3079
|
+
delete config.provider[PROVIDER_ID2];
|
|
3080
|
+
}
|
|
3081
|
+
return expandedCount > 0;
|
|
2796
3082
|
}
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
3083
|
+
function readEventType(ev) {
|
|
3084
|
+
if (!ev || typeof ev !== "object") return void 0;
|
|
3085
|
+
const e = ev;
|
|
3086
|
+
if (typeof e.type === "string") return e.type;
|
|
3087
|
+
const payload = e.payload;
|
|
3088
|
+
if (payload && typeof payload === "object") {
|
|
3089
|
+
const t = payload.type;
|
|
3090
|
+
if (typeof t === "string") return t;
|
|
3091
|
+
}
|
|
3092
|
+
return void 0;
|
|
3093
|
+
}
|
|
3094
|
+
var server = async (input) => {
|
|
3095
|
+
if (input && typeof input === "object" && "client" in input) {
|
|
3096
|
+
setOpencodeClient(input.client);
|
|
2811
3097
|
}
|
|
2812
|
-
|
|
3098
|
+
return {
|
|
3099
|
+
config: async (config) => {
|
|
3100
|
+
config.provider ??= {};
|
|
3101
|
+
const expanded = await expandAccountProviders(config);
|
|
3102
|
+
if (expanded) return;
|
|
3103
|
+
const existing = config.provider[PROVIDER_ID2];
|
|
3104
|
+
config.provider[PROVIDER_ID2] = {
|
|
3105
|
+
...existing,
|
|
3106
|
+
...await providerConfig(existing)
|
|
3107
|
+
};
|
|
3108
|
+
},
|
|
3109
|
+
event: async ({ event }) => {
|
|
3110
|
+
if (readEventType(event) === "global.disposed") {
|
|
3111
|
+
evictAllSessions("global.disposed");
|
|
3112
|
+
}
|
|
3113
|
+
},
|
|
3114
|
+
provider: {
|
|
3115
|
+
id: PROVIDER_ID2,
|
|
3116
|
+
models: async (provider) => defaultModelsForProvider(provider.models)
|
|
3117
|
+
}
|
|
3118
|
+
};
|
|
3119
|
+
};
|
|
2813
3120
|
var index_default = {
|
|
2814
3121
|
id: "@khalilgharbaoui/opencode-claude-code-plugin",
|
|
2815
3122
|
server
|