@khalilgharbaoui/opencode-claude-code-plugin 0.2.0 → 0.2.1
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 +0 -6
- package/dist/index.js +127 -36
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -90,12 +90,6 @@ type OpenCodeHooks = {
|
|
|
90
90
|
id: string;
|
|
91
91
|
models?: (provider: OpenCodeProvider) => Promise<Record<string, OpenCodeModel>>;
|
|
92
92
|
};
|
|
93
|
-
/**
|
|
94
|
-
* Called for every bus event opencode publishes. We use this to react to
|
|
95
|
-
* `global.disposed` (fired when opencode invalidates its config — e.g.
|
|
96
|
-
* after a UI MCP toggle or `updateGlobal`) and evict cached claude
|
|
97
|
-
* subprocesses so the next turn picks up the fresh config.
|
|
98
|
-
*/
|
|
99
93
|
event?: (input: {
|
|
100
94
|
event: OpenCodeEvent;
|
|
101
95
|
}) => Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -742,15 +742,6 @@ function deleteActiveProcess(key) {
|
|
|
742
742
|
activeProcesses.delete(key);
|
|
743
743
|
}
|
|
744
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
|
-
}
|
|
754
745
|
function getClaudeSessionId(key) {
|
|
755
746
|
return claudeSessions.get(key);
|
|
756
747
|
}
|
|
@@ -1045,12 +1036,12 @@ async function createProxyMcpServer(tools = DEFAULT_PROXY_TOOLS) {
|
|
|
1045
1036
|
hasInput: input != null
|
|
1046
1037
|
});
|
|
1047
1038
|
const result = await new Promise(
|
|
1048
|
-
(
|
|
1039
|
+
(resolve3, reject) => {
|
|
1049
1040
|
const entry = {
|
|
1050
1041
|
id: callId,
|
|
1051
1042
|
toolName,
|
|
1052
1043
|
input,
|
|
1053
|
-
resolve:
|
|
1044
|
+
resolve: resolve3,
|
|
1054
1045
|
reject
|
|
1055
1046
|
};
|
|
1056
1047
|
pending.set(callId, entry);
|
|
@@ -1107,11 +1098,11 @@ async function createProxyMcpServer(tools = DEFAULT_PROXY_TOOLS) {
|
|
|
1107
1098
|
}
|
|
1108
1099
|
}
|
|
1109
1100
|
});
|
|
1110
|
-
await new Promise((
|
|
1101
|
+
await new Promise((resolve3, reject) => {
|
|
1111
1102
|
server2.once("error", reject);
|
|
1112
1103
|
server2.listen(0, "127.0.0.1", () => {
|
|
1113
1104
|
server2.off("error", reject);
|
|
1114
|
-
|
|
1105
|
+
resolve3();
|
|
1115
1106
|
});
|
|
1116
1107
|
});
|
|
1117
1108
|
const addr = server2.address();
|
|
@@ -1158,8 +1149,8 @@ async function createProxyMcpServer(tools = DEFAULT_PROXY_TOOLS) {
|
|
|
1158
1149
|
entry.reject(new Error("proxy MCP server closed"));
|
|
1159
1150
|
}
|
|
1160
1151
|
pending.clear();
|
|
1161
|
-
await new Promise((
|
|
1162
|
-
server2.close(() =>
|
|
1152
|
+
await new Promise((resolve3) => {
|
|
1153
|
+
server2.close(() => resolve3());
|
|
1163
1154
|
});
|
|
1164
1155
|
}
|
|
1165
1156
|
};
|
|
@@ -1183,10 +1174,10 @@ function disallowedToolFlags(tools) {
|
|
|
1183
1174
|
return out;
|
|
1184
1175
|
}
|
|
1185
1176
|
function readBody(req) {
|
|
1186
|
-
return new Promise((
|
|
1177
|
+
return new Promise((resolve3, reject) => {
|
|
1187
1178
|
const chunks = [];
|
|
1188
1179
|
req.on("data", (chunk) => chunks.push(chunk));
|
|
1189
|
-
req.on("end", () =>
|
|
1180
|
+
req.on("end", () => resolve3(Buffer.concat(chunks).toString("utf8")));
|
|
1190
1181
|
req.on("error", reject);
|
|
1191
1182
|
});
|
|
1192
1183
|
}
|
|
@@ -1684,7 +1675,7 @@ var ClaudeCodeLanguageModel = class {
|
|
|
1684
1675
|
let thinkingText = "";
|
|
1685
1676
|
let resultMeta = {};
|
|
1686
1677
|
const toolCalls = [];
|
|
1687
|
-
const result = await new Promise((
|
|
1678
|
+
const result = await new Promise((resolve3, reject) => {
|
|
1688
1679
|
rl.on("line", (line) => {
|
|
1689
1680
|
if (!line.trim()) return;
|
|
1690
1681
|
try {
|
|
@@ -1775,7 +1766,7 @@ ${plan}
|
|
|
1775
1766
|
durationMs: msg.duration_ms,
|
|
1776
1767
|
usage: msg.usage
|
|
1777
1768
|
};
|
|
1778
|
-
|
|
1769
|
+
resolve3({
|
|
1779
1770
|
...resultMeta,
|
|
1780
1771
|
text: responseText,
|
|
1781
1772
|
thinking: thinkingText,
|
|
@@ -1786,7 +1777,7 @@ ${plan}
|
|
|
1786
1777
|
}
|
|
1787
1778
|
});
|
|
1788
1779
|
rl.on("close", () => {
|
|
1789
|
-
|
|
1780
|
+
resolve3({
|
|
1790
1781
|
...resultMeta,
|
|
1791
1782
|
text: responseText,
|
|
1792
1783
|
thinking: thinkingText,
|
|
@@ -2901,6 +2892,117 @@ function titleizeAccount(account) {
|
|
|
2901
2892
|
return normalizeAccountName(account).split("-").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
2902
2893
|
}
|
|
2903
2894
|
|
|
2895
|
+
// src/cleanup-stale.ts
|
|
2896
|
+
import {
|
|
2897
|
+
existsSync as existsSync2,
|
|
2898
|
+
readFileSync as readFileSync2,
|
|
2899
|
+
realpathSync,
|
|
2900
|
+
rmSync,
|
|
2901
|
+
writeFileSync as writeFileSync3
|
|
2902
|
+
} from "fs";
|
|
2903
|
+
import { homedir as homedir2 } from "os";
|
|
2904
|
+
import { join as join3, resolve as resolve2 } from "path";
|
|
2905
|
+
import { fileURLToPath } from "url";
|
|
2906
|
+
var STALE_PACKAGE_NAME = "opencode-claude-code-plugin";
|
|
2907
|
+
var SUSPECT_DESCRIPTION_TOKEN = "Claude Code";
|
|
2908
|
+
var alreadyRan = false;
|
|
2909
|
+
function candidateCacheRoots() {
|
|
2910
|
+
const xdg = process.env.XDG_CACHE_HOME;
|
|
2911
|
+
return [
|
|
2912
|
+
xdg ? join3(xdg, "opencode") : null,
|
|
2913
|
+
join3(homedir2(), ".cache", "opencode"),
|
|
2914
|
+
join3(homedir2(), "Library", "Caches", "opencode")
|
|
2915
|
+
].filter((p) => Boolean(p));
|
|
2916
|
+
}
|
|
2917
|
+
function userOpencodeJsonPath() {
|
|
2918
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME ?? join3(homedir2(), ".config");
|
|
2919
|
+
return join3(xdgConfig, "opencode", "opencode.json");
|
|
2920
|
+
}
|
|
2921
|
+
function userIntendsToUseUnscoped() {
|
|
2922
|
+
const cfg = userOpencodeJsonPath();
|
|
2923
|
+
if (!existsSync2(cfg)) return false;
|
|
2924
|
+
try {
|
|
2925
|
+
const json = JSON.parse(readFileSync2(cfg, "utf8"));
|
|
2926
|
+
const plugins = json.plugin;
|
|
2927
|
+
if (!Array.isArray(plugins)) return false;
|
|
2928
|
+
return plugins.some(
|
|
2929
|
+
(entry) => typeof entry === "string" && /^opencode-claude-code-plugin(@[^/]+)?$/.test(entry)
|
|
2930
|
+
);
|
|
2931
|
+
} catch {
|
|
2932
|
+
return false;
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2935
|
+
function ourLoadedDir() {
|
|
2936
|
+
try {
|
|
2937
|
+
const filePath = fileURLToPath(import.meta.url);
|
|
2938
|
+
return realpathSync(resolve2(filePath, "..", ".."));
|
|
2939
|
+
} catch {
|
|
2940
|
+
return null;
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
function cleanupStaleUnscopedInstall() {
|
|
2944
|
+
if (alreadyRan) return;
|
|
2945
|
+
alreadyRan = true;
|
|
2946
|
+
if (process.env.OPENCODE_CLAUDE_CODE_PLUGIN_NO_CLEANUP === "1") return;
|
|
2947
|
+
if (userIntendsToUseUnscoped()) return;
|
|
2948
|
+
const ourDir = ourLoadedDir();
|
|
2949
|
+
for (const cacheRoot of candidateCacheRoots()) {
|
|
2950
|
+
try {
|
|
2951
|
+
cleanupOne(cacheRoot, ourDir);
|
|
2952
|
+
} catch (err) {
|
|
2953
|
+
log.warn("cleanup-stale: error processing cache root", {
|
|
2954
|
+
cacheRoot,
|
|
2955
|
+
error: String(err)
|
|
2956
|
+
});
|
|
2957
|
+
}
|
|
2958
|
+
}
|
|
2959
|
+
}
|
|
2960
|
+
function cleanupOne(cacheRoot, ourDir) {
|
|
2961
|
+
if (!existsSync2(cacheRoot)) return;
|
|
2962
|
+
const stalePath = join3(cacheRoot, "node_modules", STALE_PACKAGE_NAME);
|
|
2963
|
+
if (!existsSync2(stalePath)) return;
|
|
2964
|
+
let realStalePath = stalePath;
|
|
2965
|
+
try {
|
|
2966
|
+
realStalePath = realpathSync(stalePath);
|
|
2967
|
+
} catch {
|
|
2968
|
+
}
|
|
2969
|
+
if (ourDir && realStalePath === ourDir) return;
|
|
2970
|
+
const pkgJsonPath = join3(stalePath, "package.json");
|
|
2971
|
+
if (!existsSync2(pkgJsonPath)) return;
|
|
2972
|
+
let pkg = {};
|
|
2973
|
+
try {
|
|
2974
|
+
pkg = JSON.parse(readFileSync2(pkgJsonPath, "utf8"));
|
|
2975
|
+
} catch {
|
|
2976
|
+
return;
|
|
2977
|
+
}
|
|
2978
|
+
if (pkg.name !== STALE_PACKAGE_NAME) return;
|
|
2979
|
+
if (!pkg.description?.includes(SUSPECT_DESCRIPTION_TOKEN)) return;
|
|
2980
|
+
log.info("cleanup-stale: removing unscoped install", { stalePath });
|
|
2981
|
+
try {
|
|
2982
|
+
rmSync(stalePath, { recursive: true, force: true });
|
|
2983
|
+
} catch (err) {
|
|
2984
|
+
log.warn("cleanup-stale: rmSync failed", {
|
|
2985
|
+
stalePath,
|
|
2986
|
+
error: String(err)
|
|
2987
|
+
});
|
|
2988
|
+
return;
|
|
2989
|
+
}
|
|
2990
|
+
const cachePkgJson = join3(cacheRoot, "package.json");
|
|
2991
|
+
if (!existsSync2(cachePkgJson)) return;
|
|
2992
|
+
try {
|
|
2993
|
+
const cfg = JSON.parse(readFileSync2(cachePkgJson, "utf8"));
|
|
2994
|
+
if (cfg?.dependencies?.[STALE_PACKAGE_NAME]) {
|
|
2995
|
+
delete cfg.dependencies[STALE_PACKAGE_NAME];
|
|
2996
|
+
writeFileSync3(cachePkgJson, JSON.stringify(cfg, null, 2) + "\n");
|
|
2997
|
+
log.info("cleanup-stale: pruned dep from cache package.json");
|
|
2998
|
+
}
|
|
2999
|
+
} catch (err) {
|
|
3000
|
+
log.warn("cleanup-stale: cache package.json update failed", {
|
|
3001
|
+
error: String(err)
|
|
3002
|
+
});
|
|
3003
|
+
}
|
|
3004
|
+
}
|
|
3005
|
+
|
|
2904
3006
|
// src/index.ts
|
|
2905
3007
|
function createClaudeCode(settings = {}) {
|
|
2906
3008
|
const cliPath = settings.cliPath ?? process.env.CLAUDE_CLI_PATH ?? "claude";
|
|
@@ -3080,18 +3182,8 @@ async function expandAccountProviders(config) {
|
|
|
3080
3182
|
}
|
|
3081
3183
|
return expandedCount > 0;
|
|
3082
3184
|
}
|
|
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
3185
|
var server = async (input) => {
|
|
3186
|
+
cleanupStaleUnscopedInstall();
|
|
3095
3187
|
if (input && typeof input === "object" && "client" in input) {
|
|
3096
3188
|
setOpencodeClient(input.client);
|
|
3097
3189
|
}
|
|
@@ -3106,11 +3198,10 @@ var server = async (input) => {
|
|
|
3106
3198
|
...await providerConfig(existing)
|
|
3107
3199
|
};
|
|
3108
3200
|
},
|
|
3109
|
-
event:
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
},
|
|
3201
|
+
// No `event` hook: MCP config drift is detected at turn start by the
|
|
3202
|
+
// hot-reload check in `claude-code-language-model.ts`, which respawns
|
|
3203
|
+
// claude safely between turns. Eviction on `global.disposed` would kill
|
|
3204
|
+
// an in-flight stream and abort the user's current turn.
|
|
3114
3205
|
provider: {
|
|
3115
3206
|
id: PROVIDER_ID2,
|
|
3116
3207
|
models: async (provider) => defaultModelsForProvider(provider.models)
|