@khalilgharbaoui/opencode-claude-code-plugin 0.49.0 → 0.50.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/dist/index.d.ts +36 -1
- package/dist/index.js +317 -190
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/claude-code-plugin/SKILL.md +3 -0
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/claude-code-language-model.ts
|
|
2
|
-
import { join as
|
|
2
|
+
import { join as join13 } from "path";
|
|
3
3
|
|
|
4
4
|
// src/ids.ts
|
|
5
5
|
import { randomInt } from "crypto";
|
|
@@ -3514,33 +3514,97 @@ function clearCompression(sessionKey2) {
|
|
|
3514
3514
|
}
|
|
3515
3515
|
|
|
3516
3516
|
// src/mcp-bridge.ts
|
|
3517
|
-
import * as
|
|
3518
|
-
import * as
|
|
3519
|
-
import * as
|
|
3517
|
+
import * as fs5 from "fs";
|
|
3518
|
+
import * as path6 from "path";
|
|
3519
|
+
import * as os3 from "os";
|
|
3520
3520
|
import * as crypto2 from "crypto";
|
|
3521
3521
|
import {
|
|
3522
3522
|
parse as parseJsonc,
|
|
3523
3523
|
printParseErrorCode
|
|
3524
3524
|
} from "jsonc-parser";
|
|
3525
|
+
|
|
3526
|
+
// src/mcp-auth.ts
|
|
3527
|
+
import * as fs4 from "fs";
|
|
3528
|
+
import * as os2 from "os";
|
|
3529
|
+
import * as path5 from "path";
|
|
3530
|
+
var EXPIRY_SKEW_SECONDS = 30;
|
|
3531
|
+
function opencodeStateDir() {
|
|
3532
|
+
const dataHome = process.env.XDG_DATA_HOME || path5.join(os2.homedir(), ".local", "share");
|
|
3533
|
+
return path5.join(dataHome, "opencode");
|
|
3534
|
+
}
|
|
3535
|
+
function readMcpAuthStore(stateDir) {
|
|
3536
|
+
const file = path5.join(stateDir, "mcp-auth.json");
|
|
3537
|
+
try {
|
|
3538
|
+
const stat = fs4.lstatSync(file);
|
|
3539
|
+
if (!stat.isFile()) {
|
|
3540
|
+
log.debug("ignoring opencode MCP auth store that is not a regular file", { path: file });
|
|
3541
|
+
return {};
|
|
3542
|
+
}
|
|
3543
|
+
if (typeof stat.uid === "number" && typeof process.getuid === "function" && stat.uid !== process.getuid()) {
|
|
3544
|
+
log.debug("ignoring opencode MCP auth store owned by another user", { path: file });
|
|
3545
|
+
return {};
|
|
3546
|
+
}
|
|
3547
|
+
const parsed = JSON.parse(fs4.readFileSync(file, "utf8"));
|
|
3548
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
3549
|
+
log.debug("opencode MCP auth store has an unexpected shape", { path: file });
|
|
3550
|
+
return {};
|
|
3551
|
+
}
|
|
3552
|
+
return parsed;
|
|
3553
|
+
} catch {
|
|
3554
|
+
return {};
|
|
3555
|
+
}
|
|
3556
|
+
}
|
|
3557
|
+
function selectBearerToken(store, serverName, serverUrl, now = Date.now()) {
|
|
3558
|
+
const entry = Object.prototype.hasOwnProperty.call(store, serverName) ? store[serverName] : void 0;
|
|
3559
|
+
if (!entry || typeof entry !== "object") return { token: null, refusal: "no-entry" };
|
|
3560
|
+
if (!entry.serverUrl || entry.serverUrl !== serverUrl) {
|
|
3561
|
+
return { token: null, refusal: "url-mismatch" };
|
|
3562
|
+
}
|
|
3563
|
+
const accessToken = entry.tokens?.accessToken;
|
|
3564
|
+
if (typeof accessToken !== "string" || accessToken.length === 0) {
|
|
3565
|
+
return { token: null, refusal: "no-token" };
|
|
3566
|
+
}
|
|
3567
|
+
const expiresAt = entry.tokens?.expiresAt;
|
|
3568
|
+
if (typeof expiresAt === "number" && expiresAt - now / 1e3 < EXPIRY_SKEW_SECONDS) {
|
|
3569
|
+
return { token: null, refusal: "expired" };
|
|
3570
|
+
}
|
|
3571
|
+
return { token: accessToken, expiresAt: typeof expiresAt === "number" ? expiresAt : void 0 };
|
|
3572
|
+
}
|
|
3573
|
+
function buildFreshnessKey(expiresAt) {
|
|
3574
|
+
return expiresAt === void 0 ? "no-expiry" : String(expiresAt);
|
|
3575
|
+
}
|
|
3576
|
+
function hasAuthorizationHeader(headers) {
|
|
3577
|
+
return Object.keys(headers).some((key) => key.toLowerCase() === "authorization");
|
|
3578
|
+
}
|
|
3579
|
+
function injectBearerHeaders(server2, accessToken) {
|
|
3580
|
+
const existing = server2.headers && typeof server2.headers === "object" && !Array.isArray(server2.headers) ? server2.headers : {};
|
|
3581
|
+
if (hasAuthorizationHeader(existing)) return { ...server2 };
|
|
3582
|
+
return {
|
|
3583
|
+
...server2,
|
|
3584
|
+
headers: { ...existing, Authorization: `Bearer ${accessToken}` }
|
|
3585
|
+
};
|
|
3586
|
+
}
|
|
3587
|
+
|
|
3588
|
+
// src/mcp-bridge.ts
|
|
3525
3589
|
var FILE_NAMES = ["opencode.jsonc", "opencode.json", "config.json"];
|
|
3526
3590
|
var PROJECT_FILE_NAMES = ["opencode.json", "opencode.jsonc"];
|
|
3527
3591
|
function fileExists(p) {
|
|
3528
3592
|
try {
|
|
3529
|
-
return
|
|
3593
|
+
return fs5.statSync(p).isFile();
|
|
3530
3594
|
} catch {
|
|
3531
3595
|
return false;
|
|
3532
3596
|
}
|
|
3533
3597
|
}
|
|
3534
3598
|
function dirExists(p) {
|
|
3535
3599
|
try {
|
|
3536
|
-
return
|
|
3600
|
+
return fs5.statSync(p).isDirectory();
|
|
3537
3601
|
} catch {
|
|
3538
3602
|
return false;
|
|
3539
3603
|
}
|
|
3540
3604
|
}
|
|
3541
3605
|
function readAndParse(file) {
|
|
3542
3606
|
try {
|
|
3543
|
-
const raw =
|
|
3607
|
+
const raw = fs5.readFileSync(file, "utf8");
|
|
3544
3608
|
const errors = [];
|
|
3545
3609
|
const parsed = parseJsonc(raw, errors, { allowTrailingComma: true });
|
|
3546
3610
|
if (errors.length > 0) {
|
|
@@ -3576,14 +3640,14 @@ function deepMerge(target, source) {
|
|
|
3576
3640
|
}
|
|
3577
3641
|
function walkUp(opts) {
|
|
3578
3642
|
const out = [];
|
|
3579
|
-
let current =
|
|
3643
|
+
let current = path6.resolve(opts.start);
|
|
3580
3644
|
while (true) {
|
|
3581
3645
|
for (const target of opts.targets) {
|
|
3582
|
-
const candidate =
|
|
3646
|
+
const candidate = path6.join(current, target);
|
|
3583
3647
|
if (opts.predicate(candidate)) out.push(candidate);
|
|
3584
3648
|
}
|
|
3585
|
-
if (opts.stop && current ===
|
|
3586
|
-
const parent =
|
|
3649
|
+
if (opts.stop && current === path6.resolve(opts.stop)) break;
|
|
3650
|
+
const parent = path6.dirname(current);
|
|
3587
3651
|
if (parent === current) break;
|
|
3588
3652
|
current = parent;
|
|
3589
3653
|
}
|
|
@@ -3591,28 +3655,28 @@ function walkUp(opts) {
|
|
|
3591
3655
|
}
|
|
3592
3656
|
function detectWorktree(cwd) {
|
|
3593
3657
|
const override = process.env.OPENCODE_WORKTREE;
|
|
3594
|
-
if (override) return
|
|
3595
|
-
let current =
|
|
3658
|
+
if (override) return path6.resolve(override);
|
|
3659
|
+
let current = path6.resolve(cwd);
|
|
3596
3660
|
while (true) {
|
|
3597
|
-
const gitPath =
|
|
3661
|
+
const gitPath = path6.join(current, ".git");
|
|
3598
3662
|
try {
|
|
3599
|
-
if (
|
|
3663
|
+
if (fs5.existsSync(gitPath)) return current;
|
|
3600
3664
|
} catch {
|
|
3601
3665
|
}
|
|
3602
|
-
const parent =
|
|
3666
|
+
const parent = path6.dirname(current);
|
|
3603
3667
|
if (parent === current) return void 0;
|
|
3604
3668
|
current = parent;
|
|
3605
3669
|
}
|
|
3606
3670
|
}
|
|
3607
3671
|
function globalConfigDir() {
|
|
3608
|
-
const xdg = process.env.XDG_CONFIG_HOME ??
|
|
3609
|
-
return
|
|
3672
|
+
const xdg = process.env.XDG_CONFIG_HOME ?? path6.join(os3.homedir(), ".config");
|
|
3673
|
+
return path6.join(xdg, "opencode");
|
|
3610
3674
|
}
|
|
3611
3675
|
function loadGlobalConfig(hostApi = "v1") {
|
|
3612
3676
|
const dir = globalConfigDir();
|
|
3613
3677
|
let merged = {};
|
|
3614
3678
|
for (const name of FILE_NAMES.slice().reverse()) {
|
|
3615
|
-
const file =
|
|
3679
|
+
const file = path6.join(dir, name);
|
|
3616
3680
|
if (!fileExists(file)) continue;
|
|
3617
3681
|
const parsed = readAndParse(file);
|
|
3618
3682
|
if (parsed) merged = mergeConfigLayer(merged, parsed, hostApi);
|
|
@@ -3622,7 +3686,7 @@ function loadGlobalConfig(hostApi = "v1") {
|
|
|
3622
3686
|
function loadProjectFilesInDir(dir, hostApi = "v1") {
|
|
3623
3687
|
let merged = {};
|
|
3624
3688
|
for (const name of PROJECT_FILE_NAMES) {
|
|
3625
|
-
const file =
|
|
3689
|
+
const file = path6.join(dir, name);
|
|
3626
3690
|
if (!fileExists(file)) continue;
|
|
3627
3691
|
const parsed = readAndParse(file);
|
|
3628
3692
|
if (parsed) merged = mergeConfigLayer(merged, parsed, hostApi);
|
|
@@ -3633,7 +3697,7 @@ function dotOpencodeDirs(cwd, worktree) {
|
|
|
3633
3697
|
const dirs = [];
|
|
3634
3698
|
const seen = /* @__PURE__ */ new Set();
|
|
3635
3699
|
const push = (p) => {
|
|
3636
|
-
const abs =
|
|
3700
|
+
const abs = path6.resolve(p);
|
|
3637
3701
|
if (!seen.has(abs) && dirExists(abs)) {
|
|
3638
3702
|
seen.add(abs);
|
|
3639
3703
|
dirs.push(abs);
|
|
@@ -3647,9 +3711,9 @@ function dotOpencodeDirs(cwd, worktree) {
|
|
|
3647
3711
|
})) {
|
|
3648
3712
|
push(dir);
|
|
3649
3713
|
}
|
|
3650
|
-
const home =
|
|
3714
|
+
const home = os3.homedir();
|
|
3651
3715
|
if (home) {
|
|
3652
|
-
const homeDot =
|
|
3716
|
+
const homeDot = path6.join(home, ".opencode");
|
|
3653
3717
|
if (dirExists(homeDot)) push(homeDot);
|
|
3654
3718
|
}
|
|
3655
3719
|
const envDir = process.env.OPENCODE_CONFIG_DIR;
|
|
@@ -3667,7 +3731,33 @@ function substituteEnvPlaceholders(source) {
|
|
|
3667
3731
|
}
|
|
3668
3732
|
return out;
|
|
3669
3733
|
}
|
|
3670
|
-
|
|
3734
|
+
var warnedExpiredBearer = /* @__PURE__ */ new Set();
|
|
3735
|
+
function withBearerToken(name, spec, url, auth) {
|
|
3736
|
+
if (!auth) return spec;
|
|
3737
|
+
const lookup = selectBearerToken(auth.store, name, url);
|
|
3738
|
+
if (lookup.token === null) {
|
|
3739
|
+
if (lookup.refusal === "expired" && !warnedExpiredBearer.has(name)) {
|
|
3740
|
+
warnedExpiredBearer.add(name);
|
|
3741
|
+
log.warn(
|
|
3742
|
+
"opencode's stored OAuth token for this MCP server has expired, so the bridged server carries no credential; re-authenticate it in opencode",
|
|
3743
|
+
{ server: name }
|
|
3744
|
+
);
|
|
3745
|
+
} else {
|
|
3746
|
+
log.debug("no opencode OAuth token for bridged MCP server", {
|
|
3747
|
+
server: name,
|
|
3748
|
+
reason: lookup.refusal
|
|
3749
|
+
});
|
|
3750
|
+
}
|
|
3751
|
+
return spec;
|
|
3752
|
+
}
|
|
3753
|
+
log.info("bridged MCP server carries opencode's stored OAuth token", { server: name });
|
|
3754
|
+
return injectBearerHeaders(spec, lookup.token);
|
|
3755
|
+
}
|
|
3756
|
+
function bearerAuthContext(options) {
|
|
3757
|
+
if (!options?.oauthTokens) return void 0;
|
|
3758
|
+
return { store: readMcpAuthStore(options.stateDir ?? opencodeStateDir()) };
|
|
3759
|
+
}
|
|
3760
|
+
function translateServer(name, spec, auth) {
|
|
3671
3761
|
if (spec.enabled === false) return null;
|
|
3672
3762
|
const type = spec.type;
|
|
3673
3763
|
if (type === "local") {
|
|
@@ -3693,7 +3783,7 @@ function translateServer(name, spec) {
|
|
|
3693
3783
|
log.warn("skipping remote MCP server with no url", { name });
|
|
3694
3784
|
return null;
|
|
3695
3785
|
}
|
|
3696
|
-
|
|
3786
|
+
let out = {
|
|
3697
3787
|
type: "http",
|
|
3698
3788
|
url: spec.url
|
|
3699
3789
|
};
|
|
@@ -3702,7 +3792,7 @@ function translateServer(name, spec) {
|
|
|
3702
3792
|
spec.headers
|
|
3703
3793
|
);
|
|
3704
3794
|
}
|
|
3705
|
-
return out;
|
|
3795
|
+
return withBearerToken(name, out, spec.url, auth);
|
|
3706
3796
|
}
|
|
3707
3797
|
log.warn("skipping MCP server with unknown type", {
|
|
3708
3798
|
name,
|
|
@@ -3753,18 +3843,19 @@ function mergeMcp(target, source) {
|
|
|
3753
3843
|
return out;
|
|
3754
3844
|
}
|
|
3755
3845
|
var MCP_PENDING_STATUS = "pending";
|
|
3756
|
-
function bridgeOpencodeMcp(cwd, runtimeStatus, excludeServers, hostApi = "v1") {
|
|
3846
|
+
function bridgeOpencodeMcp(cwd, runtimeStatus, excludeServers, hostApi = "v1", auth) {
|
|
3757
3847
|
const {
|
|
3758
3848
|
servers: merged,
|
|
3759
3849
|
enabledServerNames: allEnabledServerNames,
|
|
3760
3850
|
hash
|
|
3761
|
-
} = mergeOpencodeMcp(cwd, runtimeStatus, hostApi);
|
|
3851
|
+
} = mergeOpencodeMcp(cwd, runtimeStatus, hostApi, auth);
|
|
3852
|
+
const authContext = bearerAuthContext(auth);
|
|
3762
3853
|
const servers = {};
|
|
3763
3854
|
const bridgedServerNames = [];
|
|
3764
3855
|
for (const [name, spec] of Object.entries(merged)) {
|
|
3765
3856
|
if (!spec || typeof spec !== "object") continue;
|
|
3766
3857
|
if (excludeServers?.has(name)) continue;
|
|
3767
|
-
const translated = translateServer(name, spec);
|
|
3858
|
+
const translated = translateServer(name, spec, authContext);
|
|
3768
3859
|
if (translated) {
|
|
3769
3860
|
servers[name] = translated;
|
|
3770
3861
|
bridgedServerNames.push(name);
|
|
@@ -3796,7 +3887,7 @@ function opencodeConfigLayers(cwd, hostApi = "v1") {
|
|
|
3796
3887
|
const projectDirs = [];
|
|
3797
3888
|
const seenProjectDirs = /* @__PURE__ */ new Set();
|
|
3798
3889
|
for (const f of projectFiles) {
|
|
3799
|
-
const d =
|
|
3890
|
+
const d = path6.dirname(f);
|
|
3800
3891
|
if (!seenProjectDirs.has(d)) {
|
|
3801
3892
|
seenProjectDirs.add(d);
|
|
3802
3893
|
projectDirs.push(d);
|
|
@@ -3822,7 +3913,7 @@ function loadMergedOpencodeConfig(cwd, hostApi = "v1") {
|
|
|
3822
3913
|
for (const layer of opencodeConfigLayers(cwd, hostApi)) merged = mergeConfigLayer(merged, layer, hostApi);
|
|
3823
3914
|
return merged;
|
|
3824
3915
|
}
|
|
3825
|
-
function mergeOpencodeMcp(cwd, runtimeStatus, hostApi = "v1") {
|
|
3916
|
+
function mergeOpencodeMcp(cwd, runtimeStatus, hostApi = "v1", auth) {
|
|
3826
3917
|
let merged = {};
|
|
3827
3918
|
for (const layer of opencodeConfigLayers(cwd, hostApi)) {
|
|
3828
3919
|
const servers = extractMcpBlock(layer);
|
|
@@ -3846,7 +3937,19 @@ function mergeOpencodeMcp(cwd, runtimeStatus, hostApi = "v1") {
|
|
|
3846
3937
|
enabledServerNames.push(name);
|
|
3847
3938
|
}
|
|
3848
3939
|
const mergedBody = JSON.stringify({ mcpServers: merged }, null, 2);
|
|
3849
|
-
const
|
|
3940
|
+
const hasher = crypto2.createHash("sha256").update(mergedBody);
|
|
3941
|
+
const authContext = bearerAuthContext(auth);
|
|
3942
|
+
if (authContext) {
|
|
3943
|
+
for (const [name, spec] of Object.entries(merged)) {
|
|
3944
|
+
if (!spec || typeof spec !== "object") continue;
|
|
3945
|
+
const server2 = spec;
|
|
3946
|
+
if (server2.type !== "remote" || typeof server2.url !== "string") continue;
|
|
3947
|
+
const lookup = selectBearerToken(authContext.store, name, server2.url);
|
|
3948
|
+
if (lookup.token === null) continue;
|
|
3949
|
+
hasher.update(`\0bearer:${name}:${buildFreshnessKey(lookup.expiresAt)}`);
|
|
3950
|
+
}
|
|
3951
|
+
}
|
|
3952
|
+
const hash = hasher.digest("hex").slice(0, 12);
|
|
3850
3953
|
return { servers: merged, enabledServerNames, hash };
|
|
3851
3954
|
}
|
|
3852
3955
|
function finishBridge(input) {
|
|
@@ -3863,13 +3966,13 @@ function finishBridge(input) {
|
|
|
3863
3966
|
}
|
|
3864
3967
|
const body = JSON.stringify({ mcpServers: servers }, null, 2);
|
|
3865
3968
|
const bodyDigest = crypto2.createHash("sha256").update(body).digest("hex").slice(0, 12);
|
|
3866
|
-
const outPath =
|
|
3969
|
+
const outPath = path6.join(
|
|
3867
3970
|
pluginTmpDir(),
|
|
3868
3971
|
`mcp-${bodyDigest}.json`
|
|
3869
3972
|
);
|
|
3870
3973
|
try {
|
|
3871
3974
|
if (!fileExists(outPath)) {
|
|
3872
|
-
|
|
3975
|
+
fs5.writeFileSync(outPath, body, { encoding: "utf8", mode: 384 });
|
|
3873
3976
|
}
|
|
3874
3977
|
} catch (e) {
|
|
3875
3978
|
log.warn("failed to write bridged MCP config", {
|
|
@@ -4536,17 +4639,17 @@ function tailIsReplyOnly(history, from) {
|
|
|
4536
4639
|
}
|
|
4537
4640
|
|
|
4538
4641
|
// src/session-resume-store.ts
|
|
4539
|
-
import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as
|
|
4540
|
-
import { homedir as
|
|
4541
|
-
import { join as
|
|
4642
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
4643
|
+
import { homedir as homedir5 } from "os";
|
|
4644
|
+
import { join as join6 } from "path";
|
|
4542
4645
|
var MAX_RESUME_RECORDS = 256;
|
|
4543
4646
|
var RESUME_RECORD_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
4544
4647
|
var records = null;
|
|
4545
4648
|
var pathOverride = null;
|
|
4546
4649
|
function resumeStorePath() {
|
|
4547
4650
|
if (pathOverride) return pathOverride;
|
|
4548
|
-
const stateRoot = process.env.XDG_STATE_HOME ||
|
|
4549
|
-
return
|
|
4651
|
+
const stateRoot = process.env.XDG_STATE_HOME || join6(homedir5(), ".local", "state");
|
|
4652
|
+
return join6(stateRoot, "opencode-claude-code-plugin", "claude-sessions.json");
|
|
4550
4653
|
}
|
|
4551
4654
|
function isDigest(value) {
|
|
4552
4655
|
const entry = value;
|
|
@@ -4560,7 +4663,7 @@ function load() {
|
|
|
4560
4663
|
if (records) return records;
|
|
4561
4664
|
records = /* @__PURE__ */ new Map();
|
|
4562
4665
|
try {
|
|
4563
|
-
const parsed = JSON.parse(
|
|
4666
|
+
const parsed = JSON.parse(readFileSync3(resumeStorePath(), "utf8"));
|
|
4564
4667
|
const entries = parsed && typeof parsed === "object" ? parsed.sessions : null;
|
|
4565
4668
|
if (entries && typeof entries === "object") {
|
|
4566
4669
|
const cutoff = Date.now() - RESUME_RECORD_MAX_AGE_MS;
|
|
@@ -4575,7 +4678,7 @@ function save(update) {
|
|
|
4575
4678
|
const file = resumeStorePath();
|
|
4576
4679
|
const merged = /* @__PURE__ */ new Map();
|
|
4577
4680
|
try {
|
|
4578
|
-
const onDisk = JSON.parse(
|
|
4681
|
+
const onDisk = JSON.parse(readFileSync3(file, "utf8"))?.sessions;
|
|
4579
4682
|
if (onDisk && typeof onDisk === "object") {
|
|
4580
4683
|
for (const [key, record] of Object.entries(onDisk)) {
|
|
4581
4684
|
if (isRecord2(record)) merged.set(key, record);
|
|
@@ -4588,7 +4691,7 @@ function save(update) {
|
|
|
4588
4691
|
const kept = [...merged.entries()].filter(([, record]) => record.updatedAt >= cutoff).sort((a, b) => a[1].updatedAt - b[1].updatedAt).slice(-MAX_RESUME_RECORDS);
|
|
4589
4692
|
records = new Map(kept);
|
|
4590
4693
|
try {
|
|
4591
|
-
mkdirSync3(
|
|
4694
|
+
mkdirSync3(join6(file, ".."), { recursive: true, mode: 448 });
|
|
4592
4695
|
const tmp = `${file}.${process.pid}.tmp`;
|
|
4593
4696
|
writeFileSync3(tmp, JSON.stringify({ version: 1, sessions: Object.fromEntries(kept) }), {
|
|
4594
4697
|
mode: 384
|
|
@@ -6037,7 +6140,7 @@ async function handleBtwCommand(client, input, options = {}) {
|
|
|
6037
6140
|
// src/diagnostic-bundle.ts
|
|
6038
6141
|
import { createHash as createHash4, randomBytes as randomBytes2 } from "crypto";
|
|
6039
6142
|
import { openSync, readSync, closeSync, fstatSync, realpathSync } from "fs";
|
|
6040
|
-
import { homedir as
|
|
6143
|
+
import { homedir as homedir6 } from "os";
|
|
6041
6144
|
|
|
6042
6145
|
// src/log-messages.ts
|
|
6043
6146
|
var PLUGIN_LOG_MESSAGES = /* @__PURE__ */ new Set([
|
|
@@ -6133,6 +6236,7 @@ var PLUGIN_LOG_MESSAGES = /* @__PURE__ */ new Set([
|
|
|
6133
6236
|
"no proxyTools entry was recognised; nothing will be proxied this turn",
|
|
6134
6237
|
"no stdout after envelope write; respawning claude process to resume conversation",
|
|
6135
6238
|
"opencode 2 event subscription ended; deleted sessions keep their claude process until idle eviction",
|
|
6239
|
+
"opencode's stored OAuth token for this MCP server has expired, so the bridged server carries no credential; re-authenticate it in opencode",
|
|
6136
6240
|
"pending proxy call arrived after stream close; rejecting",
|
|
6137
6241
|
'permissionMode "plan" is enforced: claude cannot edit files or run commands, and --dangerously-skip-permissions is deliberately not passed so it stays that way. Headless Claude Code is not offered an ExitPlanMode tool, so nothing releases plan mode mid-session; approving a plan in chat does not unlock writes. Leaving plan mode means changing the config and restarting opencode.',
|
|
6138
6242
|
`permissionPreset "read-only": this claude CLI has no --permission-prompts (2.1.263+), so permission requests are denied by the plugin's own can_use_tool handler instead of by the CLI. Same outcome, one layer fewer.`,
|
|
@@ -6398,7 +6502,7 @@ function createIdHasher(salt = randomBytes2(16).toString("hex")) {
|
|
|
6398
6502
|
return (raw) => createHash4("sha256").update(salt).update("\0").update(raw).digest("hex").slice(0, 8);
|
|
6399
6503
|
}
|
|
6400
6504
|
function createRedactionContext(options = {}) {
|
|
6401
|
-
const home = options.home ??
|
|
6505
|
+
const home = options.home ?? homedir6();
|
|
6402
6506
|
const homes = /* @__PURE__ */ new Set();
|
|
6403
6507
|
if (home) homes.add(home);
|
|
6404
6508
|
if (home) {
|
|
@@ -6583,8 +6687,8 @@ function redactLogLine(raw, context) {
|
|
|
6583
6687
|
if (!parsed.data || Object.keys(parsed.data).length === 0) return head;
|
|
6584
6688
|
return `${head} ${JSON.stringify(sanitizeLogData(parsed.data, context))}`;
|
|
6585
6689
|
}
|
|
6586
|
-
function readLogTail(
|
|
6587
|
-
const fd = openSync(
|
|
6690
|
+
function readLogTail(path15, maxBytes = BUNDLE_TAIL_BYTES) {
|
|
6691
|
+
const fd = openSync(path15, "r");
|
|
6588
6692
|
try {
|
|
6589
6693
|
const size = fstatSync(fd).size;
|
|
6590
6694
|
const length = Math.min(size, maxBytes);
|
|
@@ -6789,14 +6893,14 @@ async function fetchPlanUsage(cliPath, options = {}) {
|
|
|
6789
6893
|
}
|
|
6790
6894
|
|
|
6791
6895
|
// src/stale-build.ts
|
|
6792
|
-
import * as
|
|
6793
|
-
import * as
|
|
6896
|
+
import * as fs7 from "fs";
|
|
6897
|
+
import * as path8 from "path";
|
|
6794
6898
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6795
6899
|
|
|
6796
6900
|
// src/startup-diagnostics.ts
|
|
6797
6901
|
import { execFile as execFile3 } from "child_process";
|
|
6798
|
-
import * as
|
|
6799
|
-
import * as
|
|
6902
|
+
import * as fs6 from "fs";
|
|
6903
|
+
import * as path7 from "path";
|
|
6800
6904
|
import { promisify as promisify2 } from "util";
|
|
6801
6905
|
import { fileURLToPath } from "url";
|
|
6802
6906
|
|
|
@@ -6831,8 +6935,8 @@ var cachedPluginVersion;
|
|
|
6831
6935
|
function pluginVersion() {
|
|
6832
6936
|
if (cachedPluginVersion) return cachedPluginVersion;
|
|
6833
6937
|
try {
|
|
6834
|
-
const here =
|
|
6835
|
-
const raw =
|
|
6938
|
+
const here = path7.dirname(fileURLToPath(import.meta.url));
|
|
6939
|
+
const raw = fs6.readFileSync(path7.join(here, "..", "package.json"), "utf8");
|
|
6836
6940
|
const version = JSON.parse(raw).version;
|
|
6837
6941
|
cachedPluginVersion = typeof version === "string" ? version : "unknown";
|
|
6838
6942
|
} catch {
|
|
@@ -6856,7 +6960,7 @@ var opencodeVersionProbe;
|
|
|
6856
6960
|
function detectOpencodeVersion(execPath = process.execPath) {
|
|
6857
6961
|
if (opencodeVersionProbe) return opencodeVersionProbe;
|
|
6858
6962
|
opencodeVersionProbe = (async () => {
|
|
6859
|
-
if (!
|
|
6963
|
+
if (!path7.basename(execPath).toLowerCase().includes("opencode")) {
|
|
6860
6964
|
log.debug("skipping opencode version probe: execPath is not opencode", { execPath });
|
|
6861
6965
|
return void 0;
|
|
6862
6966
|
}
|
|
@@ -7085,10 +7189,10 @@ function defaultEntryPath() {
|
|
|
7085
7189
|
}
|
|
7086
7190
|
function readOnDiskBuild(entryPath) {
|
|
7087
7191
|
try {
|
|
7088
|
-
const raw =
|
|
7192
|
+
const raw = fs7.readFileSync(path8.join(path8.dirname(entryPath), "..", "package.json"), "utf8");
|
|
7089
7193
|
const version = JSON.parse(raw).version;
|
|
7090
7194
|
if (typeof version !== "string" || version.length === 0) return void 0;
|
|
7091
|
-
const stat =
|
|
7195
|
+
const stat = fs7.statSync(entryPath);
|
|
7092
7196
|
return { version, mtimeMs: stat.mtimeMs, size: stat.size };
|
|
7093
7197
|
} catch {
|
|
7094
7198
|
return void 0;
|
|
@@ -7099,7 +7203,7 @@ function recordLoadedBuild() {
|
|
|
7099
7203
|
let mtimeMs;
|
|
7100
7204
|
let size;
|
|
7101
7205
|
try {
|
|
7102
|
-
const stat =
|
|
7206
|
+
const stat = fs7.statSync(entryPath);
|
|
7103
7207
|
mtimeMs = stat.mtimeMs;
|
|
7104
7208
|
size = stat.size;
|
|
7105
7209
|
} catch {
|
|
@@ -7537,7 +7641,7 @@ Could not build the report: ${message}`;
|
|
|
7537
7641
|
|
|
7538
7642
|
// src/agent-models.ts
|
|
7539
7643
|
import { readFile, readdir } from "fs/promises";
|
|
7540
|
-
import
|
|
7644
|
+
import path9 from "path";
|
|
7541
7645
|
|
|
7542
7646
|
// src/models.ts
|
|
7543
7647
|
var PROVIDER_ID = "claude-code";
|
|
@@ -8103,13 +8207,13 @@ async function readAgentMarkdownRecords(directories) {
|
|
|
8103
8207
|
const name = entry.slice(0, -3);
|
|
8104
8208
|
if (records2[name]) continue;
|
|
8105
8209
|
try {
|
|
8106
|
-
const text = await readFile(
|
|
8210
|
+
const text = await readFile(path9.join(directory, entry), "utf8");
|
|
8107
8211
|
records2[name] = parseAgentFrontmatter(text);
|
|
8108
8212
|
const prompt = agentMarkdownBody(text);
|
|
8109
8213
|
if (prompt) records2[name].prompt = prompt;
|
|
8110
8214
|
} catch (err) {
|
|
8111
8215
|
log.debug("failed to read agent markdown", {
|
|
8112
|
-
file:
|
|
8216
|
+
file: path9.join(directory, entry),
|
|
8113
8217
|
error: String(err)
|
|
8114
8218
|
});
|
|
8115
8219
|
}
|
|
@@ -8121,12 +8225,12 @@ function agentDirectories(home, projectDirectory) {
|
|
|
8121
8225
|
const directories = [];
|
|
8122
8226
|
if (projectDirectory) {
|
|
8123
8227
|
for (const name of AGENT_DIR_NAMES) {
|
|
8124
|
-
directories.push(
|
|
8228
|
+
directories.push(path9.join(projectDirectory, ".opencode", name));
|
|
8125
8229
|
}
|
|
8126
8230
|
}
|
|
8127
8231
|
if (home) {
|
|
8128
8232
|
for (const name of AGENT_DIR_NAMES) {
|
|
8129
|
-
directories.push(
|
|
8233
|
+
directories.push(path9.join(home, ".config", "opencode", name));
|
|
8130
8234
|
}
|
|
8131
8235
|
}
|
|
8132
8236
|
return directories;
|
|
@@ -8442,6 +8546,17 @@ function clipWithMarker(text, max) {
|
|
|
8442
8546
|
return `${text.slice(0, max)}
|
|
8443
8547
|
\u2026[truncated ${text.length - max} chars]`;
|
|
8444
8548
|
}
|
|
8549
|
+
var MAX_REPLAY_MESSAGE_CHARS = 2e3;
|
|
8550
|
+
var MAX_REPLAY_LAST_REPLY_CHARS = 12e3;
|
|
8551
|
+
var MAX_REPLAY_HEAD_CHARS = 1e3;
|
|
8552
|
+
function clipMiddle(text, max) {
|
|
8553
|
+
if (text.length <= max) return text;
|
|
8554
|
+
const head = Math.min(Math.floor(max / 4), MAX_REPLAY_HEAD_CHARS);
|
|
8555
|
+
const tail = max - head;
|
|
8556
|
+
return `${text.slice(0, head)}
|
|
8557
|
+
\u2026[${text.length - max} chars omitted]\u2026
|
|
8558
|
+
${text.slice(-tail)}`;
|
|
8559
|
+
}
|
|
8445
8560
|
function renderToolInput(input) {
|
|
8446
8561
|
let raw;
|
|
8447
8562
|
try {
|
|
@@ -8510,13 +8625,20 @@ function compactConversationHistory(prompt, opts = {}) {
|
|
|
8510
8625
|
return null;
|
|
8511
8626
|
}
|
|
8512
8627
|
const historyParts = [];
|
|
8628
|
+
let lastAssistant = -1;
|
|
8629
|
+
for (let i = conversationMessages.length - 2; i >= 0; i--) {
|
|
8630
|
+
if (conversationMessages[i].role === "assistant") {
|
|
8631
|
+
lastAssistant = i;
|
|
8632
|
+
break;
|
|
8633
|
+
}
|
|
8634
|
+
}
|
|
8513
8635
|
for (let i = 0; i < conversationMessages.length - 1; i++) {
|
|
8514
8636
|
const msg = conversationMessages[i];
|
|
8515
8637
|
const role = msg.role === "user" ? "User" : msg.role === "assistant" ? "Assistant" : "Tool";
|
|
8516
8638
|
const { text } = renderMessageContentForCompaction(msg);
|
|
8517
8639
|
if (text.trim()) {
|
|
8518
|
-
const
|
|
8519
|
-
historyParts.push(`${role}: ${
|
|
8640
|
+
const max = i === lastAssistant ? MAX_REPLAY_LAST_REPLY_CHARS : MAX_REPLAY_MESSAGE_CHARS;
|
|
8641
|
+
historyParts.push(`${role}: ${clipMiddle(text, max)}`);
|
|
8520
8642
|
}
|
|
8521
8643
|
}
|
|
8522
8644
|
if (historyParts.length === 0) {
|
|
@@ -8726,14 +8848,14 @@ function getTrailingUserMessages(prompt, opts = {}) {
|
|
|
8726
8848
|
|
|
8727
8849
|
// src/skill-bridge.ts
|
|
8728
8850
|
import * as crypto3 from "crypto";
|
|
8729
|
-
import * as
|
|
8730
|
-
import * as
|
|
8731
|
-
import * as
|
|
8851
|
+
import * as fs8 from "fs";
|
|
8852
|
+
import * as os4 from "os";
|
|
8853
|
+
import * as path10 from "path";
|
|
8732
8854
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
8733
8855
|
function bundledSkillsDir() {
|
|
8734
8856
|
try {
|
|
8735
8857
|
const here = fileURLToPath3(import.meta.url);
|
|
8736
|
-
const dir =
|
|
8858
|
+
const dir = path10.resolve(path10.dirname(here), "..", "skills");
|
|
8737
8859
|
return dirExists2(dir) ? dir : null;
|
|
8738
8860
|
} catch {
|
|
8739
8861
|
return null;
|
|
@@ -8741,16 +8863,16 @@ function bundledSkillsDir() {
|
|
|
8741
8863
|
}
|
|
8742
8864
|
function dirExists2(p) {
|
|
8743
8865
|
try {
|
|
8744
|
-
return
|
|
8866
|
+
return fs8.statSync(p).isDirectory();
|
|
8745
8867
|
} catch {
|
|
8746
8868
|
return false;
|
|
8747
8869
|
}
|
|
8748
8870
|
}
|
|
8749
8871
|
function realPath(p) {
|
|
8750
8872
|
try {
|
|
8751
|
-
return
|
|
8873
|
+
return fs8.realpathSync(p);
|
|
8752
8874
|
} catch {
|
|
8753
|
-
return
|
|
8875
|
+
return path10.resolve(p);
|
|
8754
8876
|
}
|
|
8755
8877
|
}
|
|
8756
8878
|
function envEnabled(name) {
|
|
@@ -8771,10 +8893,10 @@ function declaredSkillName(source) {
|
|
|
8771
8893
|
return SAFE_SKILL_NAME.test(name) ? name : null;
|
|
8772
8894
|
}
|
|
8773
8895
|
function readSkill(root, entry) {
|
|
8774
|
-
const dir =
|
|
8896
|
+
const dir = path10.join(root, entry);
|
|
8775
8897
|
let source;
|
|
8776
8898
|
try {
|
|
8777
|
-
source =
|
|
8899
|
+
source = fs8.readFileSync(path10.join(dir, "SKILL.md"), "utf8");
|
|
8778
8900
|
} catch {
|
|
8779
8901
|
return null;
|
|
8780
8902
|
}
|
|
@@ -8792,47 +8914,47 @@ function skillRoots(cwd) {
|
|
|
8792
8914
|
const roots = [];
|
|
8793
8915
|
const seen = /* @__PURE__ */ new Set();
|
|
8794
8916
|
const push = (p) => {
|
|
8795
|
-
const abs =
|
|
8917
|
+
const abs = path10.resolve(p);
|
|
8796
8918
|
if (seen.has(abs)) return;
|
|
8797
8919
|
seen.add(abs);
|
|
8798
8920
|
if (dirExists2(abs)) roots.push(abs);
|
|
8799
8921
|
};
|
|
8800
|
-
const home =
|
|
8801
|
-
const homeDir = home ?
|
|
8802
|
-
let current =
|
|
8922
|
+
const home = os4.homedir();
|
|
8923
|
+
const homeDir = home ? path10.resolve(home) : null;
|
|
8924
|
+
let current = path10.resolve(cwd);
|
|
8803
8925
|
while (true) {
|
|
8804
|
-
push(
|
|
8926
|
+
push(path10.join(current, ".opencode", "skills"));
|
|
8805
8927
|
if (current !== homeDir) {
|
|
8806
|
-
push(
|
|
8807
|
-
push(
|
|
8928
|
+
push(path10.join(current, ".claude", "skills"));
|
|
8929
|
+
push(path10.join(current, ".agents", "skills"));
|
|
8808
8930
|
}
|
|
8809
|
-
const parent =
|
|
8931
|
+
const parent = path10.dirname(current);
|
|
8810
8932
|
if (parent === current) break;
|
|
8811
8933
|
current = parent;
|
|
8812
8934
|
}
|
|
8813
|
-
if (homeDir) push(
|
|
8935
|
+
if (homeDir) push(path10.join(homeDir, ".opencode", "skills"));
|
|
8814
8936
|
const envDir = process.env.OPENCODE_CONFIG_DIR;
|
|
8815
8937
|
if (envDir) {
|
|
8816
|
-
push(
|
|
8817
|
-
push(
|
|
8938
|
+
push(path10.join(envDir, "skills"));
|
|
8939
|
+
push(path10.join(envDir, "skill"));
|
|
8818
8940
|
}
|
|
8819
|
-
const xdg = process.env.XDG_CONFIG_HOME ?? (homeDir ?
|
|
8941
|
+
const xdg = process.env.XDG_CONFIG_HOME ?? (homeDir ? path10.join(homeDir, ".config") : null);
|
|
8820
8942
|
if (xdg) {
|
|
8821
|
-
push(
|
|
8822
|
-
push(
|
|
8943
|
+
push(path10.join(xdg, "opencode", "skills"));
|
|
8944
|
+
push(path10.join(xdg, "opencode", "skill"));
|
|
8823
8945
|
}
|
|
8824
8946
|
if (homeDir && !envEnabled("OPENCODE_DISABLE_EXTERNAL_SKILLS")) {
|
|
8825
8947
|
if (!envEnabled("OPENCODE_DISABLE_CLAUDE_CODE_SKILLS")) {
|
|
8826
|
-
push(
|
|
8948
|
+
push(path10.join(homeDir, ".claude", "skills"));
|
|
8827
8949
|
}
|
|
8828
|
-
push(
|
|
8950
|
+
push(path10.join(homeDir, ".agents", "skills"));
|
|
8829
8951
|
}
|
|
8830
8952
|
return roots;
|
|
8831
8953
|
}
|
|
8832
8954
|
function collectSkills(root, claimed, found) {
|
|
8833
8955
|
let entries;
|
|
8834
8956
|
try {
|
|
8835
|
-
entries =
|
|
8957
|
+
entries = fs8.readdirSync(root, { withFileTypes: true });
|
|
8836
8958
|
} catch {
|
|
8837
8959
|
return;
|
|
8838
8960
|
}
|
|
@@ -8872,23 +8994,23 @@ function discoverBundledSkills() {
|
|
|
8872
8994
|
}
|
|
8873
8995
|
function claudeConfigDir(configDir) {
|
|
8874
8996
|
const explicit = configDir ?? process.env.CLAUDE_CONFIG_DIR;
|
|
8875
|
-
if (explicit) return
|
|
8876
|
-
const home =
|
|
8877
|
-
return home ?
|
|
8997
|
+
if (explicit) return path10.resolve(expandHome(explicit));
|
|
8998
|
+
const home = os4.homedir();
|
|
8999
|
+
return home ? path10.resolve(home, ".claude") : null;
|
|
8878
9000
|
}
|
|
8879
9001
|
function installedPluginSkillRoots(configDir, cwd) {
|
|
8880
9002
|
if (!configDir) return [];
|
|
8881
9003
|
let parsed;
|
|
8882
9004
|
try {
|
|
8883
9005
|
parsed = JSON.parse(
|
|
8884
|
-
|
|
9006
|
+
fs8.readFileSync(path10.join(configDir, "plugins", "installed_plugins.json"), "utf8")
|
|
8885
9007
|
);
|
|
8886
9008
|
} catch {
|
|
8887
9009
|
return [];
|
|
8888
9010
|
}
|
|
8889
9011
|
const plugins = parsed?.plugins;
|
|
8890
9012
|
if (!plugins || typeof plugins !== "object") return [];
|
|
8891
|
-
const workspace =
|
|
9013
|
+
const workspace = path10.resolve(cwd);
|
|
8892
9014
|
const roots = [];
|
|
8893
9015
|
for (const installs of Object.values(plugins)) {
|
|
8894
9016
|
if (!Array.isArray(installs)) continue;
|
|
@@ -8896,28 +9018,28 @@ function installedPluginSkillRoots(configDir, cwd) {
|
|
|
8896
9018
|
const entry = install;
|
|
8897
9019
|
if (typeof entry?.installPath !== "string" || !entry.installPath) continue;
|
|
8898
9020
|
if (entry.scope !== "user" && !coversWorkspace(entry.projectPath, workspace)) continue;
|
|
8899
|
-
roots.push(
|
|
9021
|
+
roots.push(path10.join(entry.installPath, "skills"));
|
|
8900
9022
|
}
|
|
8901
9023
|
}
|
|
8902
9024
|
return roots;
|
|
8903
9025
|
}
|
|
8904
9026
|
function coversWorkspace(projectPath, workspace) {
|
|
8905
9027
|
if (typeof projectPath !== "string" || !projectPath) return false;
|
|
8906
|
-
const root =
|
|
9028
|
+
const root = path10.resolve(projectPath);
|
|
8907
9029
|
if (root === workspace) return true;
|
|
8908
|
-
const rel =
|
|
8909
|
-
return rel !== "" && !rel.startsWith("..") && !
|
|
9030
|
+
const rel = path10.relative(root, workspace);
|
|
9031
|
+
return rel !== "" && !rel.startsWith("..") && !path10.isAbsolute(rel);
|
|
8910
9032
|
}
|
|
8911
9033
|
function discoverNativeClaudeSkills(opts) {
|
|
8912
9034
|
const found = [];
|
|
8913
9035
|
const scanned = /* @__PURE__ */ new Set();
|
|
8914
9036
|
const scan = (root, scope) => {
|
|
8915
|
-
const abs =
|
|
9037
|
+
const abs = path10.resolve(root);
|
|
8916
9038
|
if (scanned.has(abs)) return;
|
|
8917
9039
|
scanned.add(abs);
|
|
8918
9040
|
let entries;
|
|
8919
9041
|
try {
|
|
8920
|
-
entries =
|
|
9042
|
+
entries = fs8.readdirSync(abs, { withFileTypes: true });
|
|
8921
9043
|
} catch {
|
|
8922
9044
|
return;
|
|
8923
9045
|
}
|
|
@@ -8929,13 +9051,13 @@ function discoverNativeClaudeSkills(opts) {
|
|
|
8929
9051
|
}
|
|
8930
9052
|
};
|
|
8931
9053
|
const configDir = claudeConfigDir(opts.configDir);
|
|
8932
|
-
if (configDir) scan(
|
|
8933
|
-
const home =
|
|
8934
|
-
const homeDir = home ?
|
|
8935
|
-
let current =
|
|
9054
|
+
if (configDir) scan(path10.join(configDir, "skills"), "user");
|
|
9055
|
+
const home = os4.homedir();
|
|
9056
|
+
const homeDir = home ? path10.resolve(home) : null;
|
|
9057
|
+
let current = path10.resolve(opts.cwd);
|
|
8936
9058
|
while (true) {
|
|
8937
|
-
if (current !== homeDir) scan(
|
|
8938
|
-
const parent =
|
|
9059
|
+
if (current !== homeDir) scan(path10.join(current, ".claude", "skills"), "project");
|
|
9060
|
+
const parent = path10.dirname(current);
|
|
8939
9061
|
if (parent === current) break;
|
|
8940
9062
|
current = parent;
|
|
8941
9063
|
}
|
|
@@ -8969,10 +9091,10 @@ function dropNativelyLoadedSkills(skills, native) {
|
|
|
8969
9091
|
}
|
|
8970
9092
|
function readBundledSkillInfos() {
|
|
8971
9093
|
return discoverBundledSkills().flatMap((skill) => {
|
|
8972
|
-
const file =
|
|
9094
|
+
const file = path10.join(skill.dir, "SKILL.md");
|
|
8973
9095
|
let text;
|
|
8974
9096
|
try {
|
|
8975
|
-
text =
|
|
9097
|
+
text = fs8.readFileSync(file, "utf8");
|
|
8976
9098
|
} catch {
|
|
8977
9099
|
return [];
|
|
8978
9100
|
}
|
|
@@ -8996,15 +9118,15 @@ function readBundledSkillInfos() {
|
|
|
8996
9118
|
}
|
|
8997
9119
|
function linkSkill(source, target) {
|
|
8998
9120
|
try {
|
|
8999
|
-
|
|
9121
|
+
fs8.symlinkSync(source, target, process.platform === "win32" ? "junction" : "dir");
|
|
9000
9122
|
return;
|
|
9001
9123
|
} catch {
|
|
9002
|
-
|
|
9124
|
+
fs8.cpSync(source, target, { recursive: true, dereference: true });
|
|
9003
9125
|
}
|
|
9004
9126
|
}
|
|
9005
9127
|
function fileExists2(p) {
|
|
9006
9128
|
try {
|
|
9007
|
-
return
|
|
9129
|
+
return fs8.statSync(p).isFile();
|
|
9008
9130
|
} catch {
|
|
9009
9131
|
return false;
|
|
9010
9132
|
}
|
|
@@ -9013,14 +9135,14 @@ function buildSkillPluginDir(skills) {
|
|
|
9013
9135
|
if (skills.length === 0) return null;
|
|
9014
9136
|
const fingerprint = skills.map((s) => `${s.name}\0${s.dir}`).join("\n");
|
|
9015
9137
|
const hash = crypto3.createHash("sha256").update(fingerprint).digest("hex").slice(0, 12);
|
|
9016
|
-
const root =
|
|
9017
|
-
const manifest =
|
|
9138
|
+
const root = path10.join(pluginTmpDir(), `skills-${hash}`);
|
|
9139
|
+
const manifest = path10.join(root, ".claude-plugin", "plugin.json");
|
|
9018
9140
|
if (fileExists2(manifest)) return root;
|
|
9019
9141
|
try {
|
|
9020
|
-
|
|
9021
|
-
|
|
9022
|
-
|
|
9023
|
-
|
|
9142
|
+
fs8.rmSync(root, { recursive: true, force: true });
|
|
9143
|
+
fs8.mkdirSync(path10.join(root, ".claude-plugin"), { recursive: true });
|
|
9144
|
+
fs8.mkdirSync(path10.join(root, "skills"), { recursive: true });
|
|
9145
|
+
fs8.writeFileSync(
|
|
9024
9146
|
manifest,
|
|
9025
9147
|
JSON.stringify(
|
|
9026
9148
|
{
|
|
@@ -9033,7 +9155,7 @@ function buildSkillPluginDir(skills) {
|
|
|
9033
9155
|
{ encoding: "utf8", mode: 384 }
|
|
9034
9156
|
);
|
|
9035
9157
|
for (const skill of skills) {
|
|
9036
|
-
linkSkill(skill.dir,
|
|
9158
|
+
linkSkill(skill.dir, path10.join(root, "skills", skill.name));
|
|
9037
9159
|
}
|
|
9038
9160
|
} catch (err) {
|
|
9039
9161
|
log.warn("failed to stage opencode skill plugin dir", {
|
|
@@ -9049,7 +9171,7 @@ function registerBundledSkillPath(config) {
|
|
|
9049
9171
|
if (!dir) return false;
|
|
9050
9172
|
config.skills ??= {};
|
|
9051
9173
|
const paths = config.skills.paths ??= [];
|
|
9052
|
-
if (paths.some((p) =>
|
|
9174
|
+
if (paths.some((p) => path10.resolve(p) === dir)) return false;
|
|
9053
9175
|
paths.push(dir);
|
|
9054
9176
|
return true;
|
|
9055
9177
|
}
|
|
@@ -9099,36 +9221,36 @@ import { EventEmitter as EventEmitter4 } from "events";
|
|
|
9099
9221
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
9100
9222
|
import { existsSync as existsSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
9101
9223
|
import { unlink as unlink2 } from "fs/promises";
|
|
9102
|
-
import { join as
|
|
9224
|
+
import { join as join11 } from "path";
|
|
9103
9225
|
|
|
9104
9226
|
// src/claude-session-bun.ts
|
|
9105
|
-
import * as
|
|
9106
|
-
import * as
|
|
9107
|
-
import * as
|
|
9227
|
+
import * as os5 from "os";
|
|
9228
|
+
import * as fs9 from "fs";
|
|
9229
|
+
import * as path11 from "path";
|
|
9108
9230
|
import { execFileSync } from "child_process";
|
|
9109
9231
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
9110
9232
|
import { StringDecoder } from "string_decoder";
|
|
9111
9233
|
function resolveClaude(cmd = "claude") {
|
|
9112
|
-
if (
|
|
9234
|
+
if (path11.isAbsolute(cmd) && fs9.existsSync(cmd)) return cmd;
|
|
9113
9235
|
const viaBun = Bun.which(cmd);
|
|
9114
9236
|
if (viaBun) return viaBun;
|
|
9115
|
-
const isWin =
|
|
9237
|
+
const isWin = os5.platform() === "win32";
|
|
9116
9238
|
try {
|
|
9117
9239
|
const out = execFileSync(isWin ? "where" : "which", [cmd], {
|
|
9118
9240
|
encoding: "utf8",
|
|
9119
9241
|
stdio: ["ignore", "pipe", "ignore"]
|
|
9120
9242
|
});
|
|
9121
|
-
const first = out.split(/\r?\n/).map((l) => l.trim()).filter(Boolean).find((p) =>
|
|
9243
|
+
const first = out.split(/\r?\n/).map((l) => l.trim()).filter(Boolean).find((p) => fs9.existsSync(p));
|
|
9122
9244
|
if (first) return first;
|
|
9123
9245
|
} catch {
|
|
9124
9246
|
}
|
|
9125
9247
|
throw new Error(`Could not resolve command on PATH: ${cmd}`);
|
|
9126
9248
|
}
|
|
9127
9249
|
function encodeCwd(cwd) {
|
|
9128
|
-
const resolved =
|
|
9250
|
+
const resolved = path11.resolve(cwd);
|
|
9129
9251
|
let real = resolved;
|
|
9130
9252
|
try {
|
|
9131
|
-
real =
|
|
9253
|
+
real = fs9.realpathSync.native(resolved);
|
|
9132
9254
|
} catch {
|
|
9133
9255
|
}
|
|
9134
9256
|
return real.replace(/[^a-zA-Z0-9]/g, "-");
|
|
@@ -9136,7 +9258,7 @@ function encodeCwd(cwd) {
|
|
|
9136
9258
|
var bunPtySpawner = (argv, opts) => {
|
|
9137
9259
|
const [command, ...args] = argv;
|
|
9138
9260
|
const resolved = resolveClaude(command ?? "claude");
|
|
9139
|
-
if (
|
|
9261
|
+
if (os5.platform() === "win32" && isBatchFile(resolved)) {
|
|
9140
9262
|
throw new Error(
|
|
9141
9263
|
`The interactive transport cannot run a Windows batch shim (${resolved}). Point \`cliPath\` at claude.exe, or use the default headless transport.`
|
|
9142
9264
|
);
|
|
@@ -9244,10 +9366,10 @@ function classifyScreen(text) {
|
|
|
9244
9366
|
return null;
|
|
9245
9367
|
}
|
|
9246
9368
|
function interactiveTranscriptPath(opts) {
|
|
9247
|
-
return
|
|
9369
|
+
return path11.join(
|
|
9248
9370
|
resolveConfigDir(opts.configDir),
|
|
9249
9371
|
"projects",
|
|
9250
|
-
encodeCwd(
|
|
9372
|
+
encodeCwd(path11.resolve(opts.cwd)),
|
|
9251
9373
|
`${opts.sessionId}.jsonl`
|
|
9252
9374
|
);
|
|
9253
9375
|
}
|
|
@@ -9425,7 +9547,7 @@ var TranscriptCursor = class {
|
|
|
9425
9547
|
const filePath = this.resolvePath();
|
|
9426
9548
|
let stat;
|
|
9427
9549
|
try {
|
|
9428
|
-
stat =
|
|
9550
|
+
stat = fs9.statSync(filePath);
|
|
9429
9551
|
} catch {
|
|
9430
9552
|
return;
|
|
9431
9553
|
}
|
|
@@ -9441,13 +9563,13 @@ var TranscriptCursor = class {
|
|
|
9441
9563
|
if (stat.size <= this.offset) return;
|
|
9442
9564
|
let fd = null;
|
|
9443
9565
|
try {
|
|
9444
|
-
fd =
|
|
9566
|
+
fd = fs9.openSync(filePath, "r");
|
|
9445
9567
|
const buffer = Buffer.allocUnsafe(
|
|
9446
9568
|
Math.min(TRANSCRIPT_CHUNK_BYTES, stat.size - this.offset)
|
|
9447
9569
|
);
|
|
9448
9570
|
while (this.offset < stat.size) {
|
|
9449
9571
|
const want = Math.min(buffer.length, stat.size - this.offset);
|
|
9450
|
-
const read =
|
|
9572
|
+
const read = fs9.readSync(fd, buffer, 0, want, this.offset);
|
|
9451
9573
|
this.stats.reads++;
|
|
9452
9574
|
if (read <= 0) break;
|
|
9453
9575
|
this.offset += read;
|
|
@@ -9458,7 +9580,7 @@ var TranscriptCursor = class {
|
|
|
9458
9580
|
} finally {
|
|
9459
9581
|
if (fd !== null) {
|
|
9460
9582
|
try {
|
|
9461
|
-
|
|
9583
|
+
fs9.closeSync(fd);
|
|
9462
9584
|
} catch {
|
|
9463
9585
|
}
|
|
9464
9586
|
}
|
|
@@ -9488,12 +9610,12 @@ function isOwnPromptRecord(rec, normalized) {
|
|
|
9488
9610
|
}
|
|
9489
9611
|
function resolveConfigDir(configDir) {
|
|
9490
9612
|
const value = configDir ?? process.env.CLAUDE_CONFIG_DIR;
|
|
9491
|
-
if (!value) return
|
|
9492
|
-
if (value === "~") return
|
|
9613
|
+
if (!value) return path11.join(os5.homedir(), ".claude");
|
|
9614
|
+
if (value === "~") return os5.homedir();
|
|
9493
9615
|
if (value.startsWith("~/") || value.startsWith("~\\")) {
|
|
9494
|
-
return
|
|
9616
|
+
return path11.join(os5.homedir(), value.slice(2));
|
|
9495
9617
|
}
|
|
9496
|
-
return
|
|
9618
|
+
return path11.resolve(value);
|
|
9497
9619
|
}
|
|
9498
9620
|
function fatalScreenMessage(kind, configDir, cwd) {
|
|
9499
9621
|
if (kind === "trust") {
|
|
@@ -9557,7 +9679,7 @@ var ClaudeSession = class {
|
|
|
9557
9679
|
abandonTurn = false;
|
|
9558
9680
|
o;
|
|
9559
9681
|
constructor(opts = {}) {
|
|
9560
|
-
this.cwd =
|
|
9682
|
+
this.cwd = path11.resolve(opts.cwd ?? process.cwd());
|
|
9561
9683
|
this.configDir = resolveConfigDir(opts.configDir);
|
|
9562
9684
|
this.explicitConfigDir = opts.configDir ? this.configDir : void 0;
|
|
9563
9685
|
this.signal = opts.signal;
|
|
@@ -9569,7 +9691,7 @@ var ClaudeSession = class {
|
|
|
9569
9691
|
this.onExit = opts.onExit;
|
|
9570
9692
|
this.spawnPty = opts.spawnPty ?? bunPtySpawner;
|
|
9571
9693
|
this.sessionId = opts.resumeSessionId ?? randomUUID4();
|
|
9572
|
-
this.jsonlPath =
|
|
9694
|
+
this.jsonlPath = path11.join(
|
|
9573
9695
|
this.configDir,
|
|
9574
9696
|
"projects",
|
|
9575
9697
|
encodeCwd(this.cwd),
|
|
@@ -10280,7 +10402,7 @@ ${text2}`
|
|
|
10280
10402
|
}
|
|
10281
10403
|
function stageImage(data, extension) {
|
|
10282
10404
|
try {
|
|
10283
|
-
const file =
|
|
10405
|
+
const file = join11(pluginTmpDir(), `attachment-${randomUUID5()}.${extension}`);
|
|
10284
10406
|
writeFileSync5(file, data, { mode: 384, flag: "wx" });
|
|
10285
10407
|
return file;
|
|
10286
10408
|
} catch (err) {
|
|
@@ -10765,7 +10887,7 @@ function spawnInteractiveProcess(opts) {
|
|
|
10765
10887
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
10766
10888
|
import { constants as fsConstants } from "fs";
|
|
10767
10889
|
import { chmod as chmod2, copyFile as copyFile2, lstat as lstat2, mkdir as mkdir2 } from "fs/promises";
|
|
10768
|
-
import
|
|
10890
|
+
import path12 from "path";
|
|
10769
10891
|
var transcriptPath = interactiveTranscriptPath;
|
|
10770
10892
|
function configDirForAccount(account) {
|
|
10771
10893
|
const normalized = normalizeAccountName(account || DEFAULT_ACCOUNT);
|
|
@@ -10793,7 +10915,7 @@ async function carryTranscriptToAccount(input) {
|
|
|
10793
10915
|
const carriedId = renamed ? (input.newSessionId ?? randomUUID6)() : sessionId;
|
|
10794
10916
|
const target = renamed ? transcriptPath({ configDir: targetConfigDir, cwd, sessionId: carriedId }) : targetOwn;
|
|
10795
10917
|
try {
|
|
10796
|
-
await mkdir2(
|
|
10918
|
+
await mkdir2(path12.dirname(target), { recursive: true, mode: 448 });
|
|
10797
10919
|
await copyFile2(live.file, target, fsConstants.COPYFILE_EXCL);
|
|
10798
10920
|
await chmod2(target, 384);
|
|
10799
10921
|
} catch (err) {
|
|
@@ -10813,7 +10935,7 @@ function dedupe2(dirs) {
|
|
|
10813
10935
|
const seen = /* @__PURE__ */ new Set();
|
|
10814
10936
|
const out = [];
|
|
10815
10937
|
for (const dir of dirs) {
|
|
10816
|
-
const resolved =
|
|
10938
|
+
const resolved = path12.resolve(dir);
|
|
10817
10939
|
if (seen.has(resolved)) continue;
|
|
10818
10940
|
seen.add(resolved);
|
|
10819
10941
|
out.push(resolved);
|
|
@@ -10822,13 +10944,13 @@ function dedupe2(dirs) {
|
|
|
10822
10944
|
}
|
|
10823
10945
|
|
|
10824
10946
|
// src/prompts.ts
|
|
10825
|
-
import { readFileSync as
|
|
10826
|
-
import { homedir as
|
|
10947
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
10948
|
+
import { homedir as homedir9 } from "os";
|
|
10827
10949
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
10828
|
-
import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute3, join as
|
|
10829
|
-
function readPromptFileIfPresent(
|
|
10950
|
+
import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute3, join as join12 } from "path";
|
|
10951
|
+
function readPromptFileIfPresent(path15) {
|
|
10830
10952
|
try {
|
|
10831
|
-
const content =
|
|
10953
|
+
const content = readFileSync7(path15, "utf8").trim();
|
|
10832
10954
|
return content || void 0;
|
|
10833
10955
|
} catch {
|
|
10834
10956
|
return void 0;
|
|
@@ -10837,7 +10959,7 @@ function readPromptFileIfPresent(path14) {
|
|
|
10837
10959
|
function nearestWorkspaceAgentsPrompt(cwd) {
|
|
10838
10960
|
let dir = cwd;
|
|
10839
10961
|
while (true) {
|
|
10840
|
-
const content = readPromptFileIfPresent(
|
|
10962
|
+
const content = readPromptFileIfPresent(join12(dir, "AGENTS.md"));
|
|
10841
10963
|
if (content) return content;
|
|
10842
10964
|
const parent = dirname6(dir);
|
|
10843
10965
|
if (parent === dir) return void 0;
|
|
@@ -10933,9 +11055,9 @@ function extractSystemMessages(prompt) {
|
|
|
10933
11055
|
}
|
|
10934
11056
|
var CLAUDE_NATIVE_INSTRUCTION_FILES = /* @__PURE__ */ new Set(["CLAUDE.md", "CLAUDE.local.md"]);
|
|
10935
11057
|
function userAuthoredInstructions(forwarded, opts = {}) {
|
|
10936
|
-
const readFile2 = opts.readFile ?? ((
|
|
11058
|
+
const readFile2 = opts.readFile ?? ((path15) => {
|
|
10937
11059
|
try {
|
|
10938
|
-
return
|
|
11060
|
+
return readFileSync7(path15, "utf8");
|
|
10939
11061
|
} catch {
|
|
10940
11062
|
return null;
|
|
10941
11063
|
}
|
|
@@ -10947,16 +11069,16 @@ function userAuthoredInstructions(forwarded, opts = {}) {
|
|
|
10947
11069
|
}
|
|
10948
11070
|
const seen = /* @__PURE__ */ new Set();
|
|
10949
11071
|
for (const match of forwarded.matchAll(/^Instructions from: (.+)$/gm)) {
|
|
10950
|
-
const
|
|
10951
|
-
if (seen.has(
|
|
10952
|
-
if (CLAUDE_NATIVE_INSTRUCTION_FILES.has(basename2(
|
|
10953
|
-
seen.add(
|
|
10954
|
-
const file = readFile2(
|
|
11072
|
+
const path15 = match[1].trim();
|
|
11073
|
+
if (seen.has(path15) || !isAbsolute3(path15)) continue;
|
|
11074
|
+
if (CLAUDE_NATIVE_INSTRUCTION_FILES.has(basename2(path15))) continue;
|
|
11075
|
+
seen.add(path15);
|
|
11076
|
+
const file = readFile2(path15);
|
|
10955
11077
|
if (!file?.trim()) continue;
|
|
10956
11078
|
const start = match.index + match[0].length + 1;
|
|
10957
11079
|
const content = forwarded.startsWith(file, start) ? file : forwarded.startsWith(file.trimEnd(), start) ? file.trimEnd() : null;
|
|
10958
11080
|
if (content === null) continue;
|
|
10959
|
-
out.push({ text: `Instructions from: ${
|
|
11081
|
+
out.push({ text: `Instructions from: ${path15}
|
|
10960
11082
|
${content}`, content });
|
|
10961
11083
|
}
|
|
10962
11084
|
return out;
|
|
@@ -10976,8 +11098,8 @@ ${options.compressionSummary.trim()}`
|
|
|
10976
11098
|
for (const s of extraSystemContent) {
|
|
10977
11099
|
if (s.trim()) parts.push(s.trim());
|
|
10978
11100
|
}
|
|
10979
|
-
const configRoot = process.env.XDG_CONFIG_HOME ??
|
|
10980
|
-
const globalAgents = readPromptFileIfPresent(
|
|
11101
|
+
const configRoot = process.env.XDG_CONFIG_HOME ?? join12(homedir9(), ".config");
|
|
11102
|
+
const globalAgents = readPromptFileIfPresent(join12(configRoot, "opencode", "AGENTS.md"));
|
|
10981
11103
|
const workspaceAgents = nearestWorkspaceAgentsPrompt(cwd);
|
|
10982
11104
|
const forwarded = extraSystemContent.join("\n\n");
|
|
10983
11105
|
const pushGlobal = !!globalAgents && !forwarded.includes(globalAgents);
|
|
@@ -10993,10 +11115,10 @@ ${options.compressionSummary.trim()}`
|
|
|
10993
11115
|
if (includeMultiStepHint) parts.push(MULTI_STEP_TASK_HINT);
|
|
10994
11116
|
const content = parts.join("\n\n");
|
|
10995
11117
|
if (!content) return void 0;
|
|
10996
|
-
const
|
|
11118
|
+
const path15 = join12(pluginTmpDir(), `opencode-cc-sys-${randomUUID7()}.md`);
|
|
10997
11119
|
try {
|
|
10998
|
-
writeFileSync6(
|
|
10999
|
-
return
|
|
11120
|
+
writeFileSync6(path15, content, { encoding: "utf8", mode: 384 });
|
|
11121
|
+
return path15;
|
|
11000
11122
|
} catch (err) {
|
|
11001
11123
|
log.warn("failed to write system prompt file", { error: String(err) });
|
|
11002
11124
|
return void 0;
|
|
@@ -11423,7 +11545,9 @@ function effectiveMcpConfig(config, cwd, proxyConfigPath, runtimeStatus, exclude
|
|
|
11423
11545
|
let bridgedHash = null;
|
|
11424
11546
|
let allEnabledServerNames = [];
|
|
11425
11547
|
if (config.bridgeOpencodeMcp !== false) {
|
|
11426
|
-
const bridged = bridgeOpencodeMcp(cwd, runtimeStatus, excludeServers, config.hostApi
|
|
11548
|
+
const bridged = bridgeOpencodeMcp(cwd, runtimeStatus, excludeServers, config.hostApi, {
|
|
11549
|
+
oauthTokens: config.bridgeMcpOauthTokens === true
|
|
11550
|
+
});
|
|
11427
11551
|
if (bridged) {
|
|
11428
11552
|
if (bridged.path) paths.push(bridged.path);
|
|
11429
11553
|
bridgedHash = bridged.hash;
|
|
@@ -14127,7 +14251,7 @@ var ClaudeCodeLanguageModel = class {
|
|
|
14127
14251
|
let replayReason = "no-session-for-key";
|
|
14128
14252
|
if (this.config.resumeAfterRestart !== false && includeHistoryContext && !compactionMode && !forkFromClaudeSessionId && failoverAnswer?.kind !== "switch") {
|
|
14129
14253
|
const configDir = resolveConfigDir(this.config.configDir);
|
|
14130
|
-
const transcriptPath2 = (id) =>
|
|
14254
|
+
const transcriptPath2 = (id) => join13(configDir, "projects", encodeCwd(cwd), `${id}.jsonl`);
|
|
14131
14255
|
const resumePoint = findResumePoint({
|
|
14132
14256
|
sessionKey: sk,
|
|
14133
14257
|
prompt: options.prompt,
|
|
@@ -15383,8 +15507,8 @@ var ClaudeCodeLanguageModel = class {
|
|
|
15383
15507
|
};
|
|
15384
15508
|
|
|
15385
15509
|
// src/account-tier.ts
|
|
15386
|
-
import { readFileSync as
|
|
15387
|
-
import
|
|
15510
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
15511
|
+
import path13 from "path";
|
|
15388
15512
|
var ORGANIZATION_TYPE_LABELS = {
|
|
15389
15513
|
claude_max: "Max",
|
|
15390
15514
|
claude_pro: "Pro",
|
|
@@ -15413,7 +15537,7 @@ function readAccountTier(account) {
|
|
|
15413
15537
|
return label;
|
|
15414
15538
|
}
|
|
15415
15539
|
function readTierFromConfigDir(configDir, allowLegacyFallback) {
|
|
15416
|
-
const candidates = [
|
|
15540
|
+
const candidates = [path13.join(configDir, ".claude.json")];
|
|
15417
15541
|
if (allowLegacyFallback) candidates.push(expandHome("~/.claude.json"));
|
|
15418
15542
|
for (const candidate of candidates) {
|
|
15419
15543
|
const oauth = readOauthAccount(candidate);
|
|
@@ -15426,7 +15550,7 @@ function readTierFromConfigDir(configDir, allowLegacyFallback) {
|
|
|
15426
15550
|
function readOauthAccount(file) {
|
|
15427
15551
|
let raw;
|
|
15428
15552
|
try {
|
|
15429
|
-
raw =
|
|
15553
|
+
raw = readFileSync8(file, "utf8");
|
|
15430
15554
|
} catch {
|
|
15431
15555
|
return void 0;
|
|
15432
15556
|
}
|
|
@@ -15455,13 +15579,13 @@ function tierLabel(oauthAccount) {
|
|
|
15455
15579
|
import {
|
|
15456
15580
|
existsSync as existsSync5,
|
|
15457
15581
|
mkdirSync as mkdirSync5,
|
|
15458
|
-
readFileSync as
|
|
15582
|
+
readFileSync as readFileSync9,
|
|
15459
15583
|
realpathSync as realpathSync4,
|
|
15460
15584
|
rmSync as rmSync3,
|
|
15461
15585
|
writeFileSync as writeFileSync7
|
|
15462
15586
|
} from "fs";
|
|
15463
|
-
import { homedir as
|
|
15464
|
-
import { dirname as dirname7, join as
|
|
15587
|
+
import { homedir as homedir10 } from "os";
|
|
15588
|
+
import { dirname as dirname7, join as join14, resolve as resolve5 } from "path";
|
|
15465
15589
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
15466
15590
|
var STALE_PACKAGE_NAME = "opencode-claude-code-plugin";
|
|
15467
15591
|
var SUSPECT_DESCRIPTION_TOKEN = "Claude Code";
|
|
@@ -15469,20 +15593,20 @@ var alreadyRan = false;
|
|
|
15469
15593
|
function candidateCacheRoots() {
|
|
15470
15594
|
const xdg = process.env.XDG_CACHE_HOME;
|
|
15471
15595
|
return [
|
|
15472
|
-
xdg ?
|
|
15473
|
-
|
|
15474
|
-
|
|
15596
|
+
xdg ? join14(xdg, "opencode") : null,
|
|
15597
|
+
join14(homedir10(), ".cache", "opencode"),
|
|
15598
|
+
join14(homedir10(), "Library", "Caches", "opencode")
|
|
15475
15599
|
].filter((p) => Boolean(p));
|
|
15476
15600
|
}
|
|
15477
15601
|
function userOpencodeJsonPath() {
|
|
15478
|
-
const xdgConfig = process.env.XDG_CONFIG_HOME ??
|
|
15479
|
-
return
|
|
15602
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME ?? join14(homedir10(), ".config");
|
|
15603
|
+
return join14(xdgConfig, "opencode", "opencode.json");
|
|
15480
15604
|
}
|
|
15481
15605
|
function userIntendsToUseUnscoped() {
|
|
15482
15606
|
const cfg = userOpencodeJsonPath();
|
|
15483
15607
|
if (!existsSync5(cfg)) return false;
|
|
15484
15608
|
try {
|
|
15485
|
-
const json = JSON.parse(
|
|
15609
|
+
const json = JSON.parse(readFileSync9(cfg, "utf8"));
|
|
15486
15610
|
const plugins = json.plugin;
|
|
15487
15611
|
if (!Array.isArray(plugins)) return false;
|
|
15488
15612
|
return plugins.some(
|
|
@@ -15503,19 +15627,19 @@ function ourLoadedDir() {
|
|
|
15503
15627
|
function ourVersion(dir) {
|
|
15504
15628
|
if (!dir) return null;
|
|
15505
15629
|
try {
|
|
15506
|
-
const pkg = JSON.parse(
|
|
15630
|
+
const pkg = JSON.parse(readFileSync9(join14(dir, "package.json"), "utf8"));
|
|
15507
15631
|
return typeof pkg.version === "string" && pkg.version ? pkg.version : null;
|
|
15508
15632
|
} catch {
|
|
15509
15633
|
return null;
|
|
15510
15634
|
}
|
|
15511
15635
|
}
|
|
15512
15636
|
function markerPath() {
|
|
15513
|
-
const stateRoot = process.env.XDG_STATE_HOME ??
|
|
15514
|
-
return
|
|
15637
|
+
const stateRoot = process.env.XDG_STATE_HOME ?? join14(homedir10(), ".local", "state");
|
|
15638
|
+
return join14(stateRoot, "opencode-claude-code-plugin", "cleanup-stale.json");
|
|
15515
15639
|
}
|
|
15516
15640
|
function markerVersion() {
|
|
15517
15641
|
try {
|
|
15518
|
-
const marker = JSON.parse(
|
|
15642
|
+
const marker = JSON.parse(readFileSync9(markerPath(), "utf8"));
|
|
15519
15643
|
return typeof marker.version === "string" ? marker.version : null;
|
|
15520
15644
|
} catch {
|
|
15521
15645
|
return null;
|
|
@@ -15560,7 +15684,7 @@ function cleanupStaleUnscopedInstall() {
|
|
|
15560
15684
|
}
|
|
15561
15685
|
function cleanupOne(cacheRoot, ourDir) {
|
|
15562
15686
|
if (!existsSync5(cacheRoot)) return;
|
|
15563
|
-
const stalePath =
|
|
15687
|
+
const stalePath = join14(cacheRoot, "node_modules", STALE_PACKAGE_NAME);
|
|
15564
15688
|
if (!existsSync5(stalePath)) return;
|
|
15565
15689
|
let realStalePath = stalePath;
|
|
15566
15690
|
try {
|
|
@@ -15568,11 +15692,11 @@ function cleanupOne(cacheRoot, ourDir) {
|
|
|
15568
15692
|
} catch {
|
|
15569
15693
|
}
|
|
15570
15694
|
if (ourDir && realStalePath === ourDir) return;
|
|
15571
|
-
const pkgJsonPath =
|
|
15695
|
+
const pkgJsonPath = join14(stalePath, "package.json");
|
|
15572
15696
|
if (!existsSync5(pkgJsonPath)) return;
|
|
15573
15697
|
let pkg = {};
|
|
15574
15698
|
try {
|
|
15575
|
-
pkg = JSON.parse(
|
|
15699
|
+
pkg = JSON.parse(readFileSync9(pkgJsonPath, "utf8"));
|
|
15576
15700
|
} catch {
|
|
15577
15701
|
return;
|
|
15578
15702
|
}
|
|
@@ -15588,10 +15712,10 @@ function cleanupOne(cacheRoot, ourDir) {
|
|
|
15588
15712
|
});
|
|
15589
15713
|
return;
|
|
15590
15714
|
}
|
|
15591
|
-
const cachePkgJson =
|
|
15715
|
+
const cachePkgJson = join14(cacheRoot, "package.json");
|
|
15592
15716
|
if (!existsSync5(cachePkgJson)) return;
|
|
15593
15717
|
try {
|
|
15594
|
-
const cfg = JSON.parse(
|
|
15718
|
+
const cfg = JSON.parse(readFileSync9(cachePkgJson, "utf8"));
|
|
15595
15719
|
if (cfg?.dependencies?.[STALE_PACKAGE_NAME]) {
|
|
15596
15720
|
delete cfg.dependencies[STALE_PACKAGE_NAME];
|
|
15597
15721
|
writeFileSync7(cachePkgJson, JSON.stringify(cfg, null, 2) + "\n");
|
|
@@ -15671,7 +15795,7 @@ function registerOpencodeLogSink(client) {
|
|
|
15671
15795
|
}
|
|
15672
15796
|
|
|
15673
15797
|
// src/v2-client.ts
|
|
15674
|
-
import
|
|
15798
|
+
import path14 from "path";
|
|
15675
15799
|
function formatAgentTypeList(agents) {
|
|
15676
15800
|
const lines = agents.flatMap((agent) => {
|
|
15677
15801
|
if (agent.hidden === true || agent.mode === "primary") return [];
|
|
@@ -15700,7 +15824,7 @@ function toV1Session(info) {
|
|
|
15700
15824
|
if (!info || typeof info !== "object") return void 0;
|
|
15701
15825
|
const session = info;
|
|
15702
15826
|
const root = session.location?.directory;
|
|
15703
|
-
const directory = typeof root === "string" ? typeof session.subpath === "string" && session.subpath.length > 0 ?
|
|
15827
|
+
const directory = typeof root === "string" ? typeof session.subpath === "string" && session.subpath.length > 0 ? path14.resolve(root, session.subpath) : root : void 0;
|
|
15704
15828
|
return { ...info, directory, parentID: session.parentID };
|
|
15705
15829
|
}
|
|
15706
15830
|
function toV1Messages(messages) {
|
|
@@ -16235,6 +16359,9 @@ function createClaudeCode(settings = {}) {
|
|
|
16235
16359
|
mcpConfig: settings.mcpConfig,
|
|
16236
16360
|
strictMcpConfig: settings.strictMcpConfig,
|
|
16237
16361
|
bridgeOpencodeMcp: settings.bridgeOpencodeMcp ?? true,
|
|
16362
|
+
// No `?? true` twin: reading opencode's credential store is opt-in, so
|
|
16363
|
+
// the absent value has to stay falsy.
|
|
16364
|
+
bridgeMcpOauthTokens: settings.bridgeMcpOauthTokens,
|
|
16238
16365
|
controlRequestBehavior: preset?.controlRequestBehavior ?? settings.controlRequestBehavior ?? "allow",
|
|
16239
16366
|
controlRequestToolBehaviors: preset ? void 0 : settings.controlRequestToolBehaviors,
|
|
16240
16367
|
controlRequestDenyMessage: settings.controlRequestDenyMessage,
|