@koriit/opencode-claude-bridge 0.1.0 → 0.1.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/package.json +1 -1
- package/src/inject.ts +219 -4
- package/src/mcp-inject.ts +9 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@koriit/opencode-claude-bridge",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "An OpenCode plugin that bridges enabled Claude Code plugins (commands, agents, skills, MCP, LSP) into OpenCode at runtime, namespaced so they never shadow your existing items.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/inject.ts
CHANGED
|
@@ -202,6 +202,209 @@ function modelIfMappable(model: unknown): string | undefined {
|
|
|
202
202
|
return model.includes("/") ? model : undefined
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
+
// ── Agent color sanitization ──────────────────────────────────────────────────
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* OpenCode's agent `color` field accepts exactly two forms (from ConfigAgentV1.Info):
|
|
209
|
+
* 1. A hex string matching `^#[0-9a-fA-F]{6}$`
|
|
210
|
+
* 2. One of the 7 semantic enum values: primary | secondary | accent | success | warning | error | info
|
|
211
|
+
*
|
|
212
|
+
* Claude Code agents use free-form CSS color names (e.g. "magenta", "cyan"). Writing these
|
|
213
|
+
* raw into cfg.agent causes OpenCode's schema validation to throw at config.get — outside
|
|
214
|
+
* our try/catch — crashing the entire instance.
|
|
215
|
+
*
|
|
216
|
+
* This regex matches form 1.
|
|
217
|
+
*/
|
|
218
|
+
const OPENCODE_HEX_COLOR_RE = /^#[0-9a-fA-F]{6}$/
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* The 7 semantic color tokens OpenCode accepts as an alternative to a hex code.
|
|
222
|
+
*/
|
|
223
|
+
const OPENCODE_COLOR_ENUMS = new Set(["primary", "secondary", "accent", "success", "warning", "error", "info"])
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Mapping from CSS named colors to their `#rrggbb` equivalents.
|
|
227
|
+
* This covers the named colors Claude Code agents realistically use in frontmatter.
|
|
228
|
+
* Standard CSS named-color hex values (per the CSS Color Level 4 spec).
|
|
229
|
+
*/
|
|
230
|
+
const CSS_COLOR_TO_HEX: Record<string, string> = {
|
|
231
|
+
// Common single-word CSS colors used in Claude agent frontmatter
|
|
232
|
+
aliceblue: "#f0f8ff",
|
|
233
|
+
antiquewhite: "#faebd7",
|
|
234
|
+
aqua: "#00ffff",
|
|
235
|
+
aquamarine: "#7fffd4",
|
|
236
|
+
azure: "#f0ffff",
|
|
237
|
+
beige: "#f5f5dc",
|
|
238
|
+
bisque: "#ffe4c4",
|
|
239
|
+
black: "#000000",
|
|
240
|
+
blanchedalmond: "#ffebcd",
|
|
241
|
+
blue: "#0000ff",
|
|
242
|
+
blueviolet: "#8a2be2",
|
|
243
|
+
brown: "#a52a2a",
|
|
244
|
+
burlywood: "#deb887",
|
|
245
|
+
cadetblue: "#5f9ea0",
|
|
246
|
+
chartreuse: "#7fff00",
|
|
247
|
+
chocolate: "#d2691e",
|
|
248
|
+
coral: "#ff7f50",
|
|
249
|
+
cornflowerblue: "#6495ed",
|
|
250
|
+
cornsilk: "#fff8dc",
|
|
251
|
+
crimson: "#dc143c",
|
|
252
|
+
cyan: "#00ffff",
|
|
253
|
+
darkblue: "#00008b",
|
|
254
|
+
darkcyan: "#008b8b",
|
|
255
|
+
darkgoldenrod: "#b8860b",
|
|
256
|
+
darkgray: "#a9a9a9",
|
|
257
|
+
darkgreen: "#006400",
|
|
258
|
+
darkgrey: "#a9a9a9",
|
|
259
|
+
darkkhaki: "#bdb76b",
|
|
260
|
+
darkmagenta: "#8b008b",
|
|
261
|
+
darkolivegreen: "#556b2f",
|
|
262
|
+
darkorange: "#ff8c00",
|
|
263
|
+
darkorchid: "#9932cc",
|
|
264
|
+
darkred: "#8b0000",
|
|
265
|
+
darksalmon: "#e9967a",
|
|
266
|
+
darkseagreen: "#8fbc8f",
|
|
267
|
+
darkslateblue: "#483d8b",
|
|
268
|
+
darkslategray: "#2f4f4f",
|
|
269
|
+
darkslategrey: "#2f4f4f",
|
|
270
|
+
darkturquoise: "#00ced1",
|
|
271
|
+
darkviolet: "#9400d3",
|
|
272
|
+
deeppink: "#ff1493",
|
|
273
|
+
deepskyblue: "#00bfff",
|
|
274
|
+
dimgray: "#696969",
|
|
275
|
+
dimgrey: "#696969",
|
|
276
|
+
dodgerblue: "#1e90ff",
|
|
277
|
+
firebrick: "#b22222",
|
|
278
|
+
floralwhite: "#fffaf0",
|
|
279
|
+
forestgreen: "#228b22",
|
|
280
|
+
fuchsia: "#ff00ff",
|
|
281
|
+
gainsboro: "#dcdcdc",
|
|
282
|
+
ghostwhite: "#f8f8ff",
|
|
283
|
+
gold: "#ffd700",
|
|
284
|
+
goldenrod: "#daa520",
|
|
285
|
+
gray: "#808080",
|
|
286
|
+
green: "#008000",
|
|
287
|
+
greenyellow: "#adff2f",
|
|
288
|
+
grey: "#808080",
|
|
289
|
+
honeydew: "#f0fff0",
|
|
290
|
+
hotpink: "#ff69b4",
|
|
291
|
+
indianred: "#cd5c5c",
|
|
292
|
+
indigo: "#4b0082",
|
|
293
|
+
ivory: "#fffff0",
|
|
294
|
+
khaki: "#f0e68c",
|
|
295
|
+
lavender: "#e6e6fa",
|
|
296
|
+
lavenderblush: "#fff0f5",
|
|
297
|
+
lawngreen: "#7cfc00",
|
|
298
|
+
lemonchiffon: "#fffacd",
|
|
299
|
+
lightblue: "#add8e6",
|
|
300
|
+
lightcoral: "#f08080",
|
|
301
|
+
lightcyan: "#e0ffff",
|
|
302
|
+
lightgoldenrodyellow: "#fafad2",
|
|
303
|
+
lightgray: "#d3d3d3",
|
|
304
|
+
lightgreen: "#90ee90",
|
|
305
|
+
lightgrey: "#d3d3d3",
|
|
306
|
+
lightpink: "#ffb6c1",
|
|
307
|
+
lightsalmon: "#ffa07a",
|
|
308
|
+
lightseagreen: "#20b2aa",
|
|
309
|
+
lightskyblue: "#87cefa",
|
|
310
|
+
lightslategray: "#778899",
|
|
311
|
+
lightslategrey: "#778899",
|
|
312
|
+
lightsteelblue: "#b0c4de",
|
|
313
|
+
lightyellow: "#ffffe0",
|
|
314
|
+
lime: "#00ff00",
|
|
315
|
+
limegreen: "#32cd32",
|
|
316
|
+
linen: "#faf0e6",
|
|
317
|
+
magenta: "#ff00ff",
|
|
318
|
+
maroon: "#800000",
|
|
319
|
+
mediumaquamarine: "#66cdaa",
|
|
320
|
+
mediumblue: "#0000cd",
|
|
321
|
+
mediumorchid: "#ba55d3",
|
|
322
|
+
mediumpurple: "#9370db",
|
|
323
|
+
mediumseagreen: "#3cb371",
|
|
324
|
+
mediumslateblue: "#7b68ee",
|
|
325
|
+
mediumspringgreen: "#00fa9a",
|
|
326
|
+
mediumturquoise: "#48d1cc",
|
|
327
|
+
mediumvioletred: "#c71585",
|
|
328
|
+
midnightblue: "#191970",
|
|
329
|
+
mintcream: "#f5fffa",
|
|
330
|
+
mistyrose: "#ffe4e1",
|
|
331
|
+
moccasin: "#ffe4b5",
|
|
332
|
+
navajowhite: "#ffdead",
|
|
333
|
+
navy: "#000080",
|
|
334
|
+
oldlace: "#fdf5e6",
|
|
335
|
+
olive: "#808000",
|
|
336
|
+
olivedrab: "#6b8e23",
|
|
337
|
+
orange: "#ffa500",
|
|
338
|
+
orangered: "#ff4500",
|
|
339
|
+
orchid: "#da70d6",
|
|
340
|
+
palegoldenrod: "#eee8aa",
|
|
341
|
+
palegreen: "#98fb98",
|
|
342
|
+
paleturquoise: "#afeeee",
|
|
343
|
+
palevioletred: "#db7093",
|
|
344
|
+
papayawhip: "#ffefd5",
|
|
345
|
+
peachpuff: "#ffdab9",
|
|
346
|
+
peru: "#cd853f",
|
|
347
|
+
pink: "#ffc0cb",
|
|
348
|
+
plum: "#dda0dd",
|
|
349
|
+
powderblue: "#b0e0e6",
|
|
350
|
+
purple: "#800080",
|
|
351
|
+
rebeccapurple: "#663399",
|
|
352
|
+
red: "#ff0000",
|
|
353
|
+
rosybrown: "#bc8f8f",
|
|
354
|
+
royalblue: "#4169e1",
|
|
355
|
+
saddlebrown: "#8b4513",
|
|
356
|
+
salmon: "#fa8072",
|
|
357
|
+
sandybrown: "#f4a460",
|
|
358
|
+
seagreen: "#2e8b57",
|
|
359
|
+
seashell: "#fff5ee",
|
|
360
|
+
sienna: "#a0522d",
|
|
361
|
+
silver: "#c0c0c0",
|
|
362
|
+
skyblue: "#87ceeb",
|
|
363
|
+
slateblue: "#6a5acd",
|
|
364
|
+
slategray: "#708090",
|
|
365
|
+
slategrey: "#708090",
|
|
366
|
+
snow: "#fffafa",
|
|
367
|
+
springgreen: "#00ff7f",
|
|
368
|
+
steelblue: "#4682b4",
|
|
369
|
+
tan: "#d2b48c",
|
|
370
|
+
teal: "#008080",
|
|
371
|
+
thistle: "#d8bfd8",
|
|
372
|
+
tomato: "#ff6347",
|
|
373
|
+
turquoise: "#40e0d0",
|
|
374
|
+
violet: "#ee82ee",
|
|
375
|
+
wheat: "#f5deb3",
|
|
376
|
+
white: "#ffffff",
|
|
377
|
+
whitesmoke: "#f5f5f5",
|
|
378
|
+
yellow: "#ffff00",
|
|
379
|
+
yellowgreen: "#9acd32",
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Sanitize an agent `color` value for injection into OpenCode's config.
|
|
384
|
+
*
|
|
385
|
+
* Returns the sanitized string if it can be mapped to a valid OpenCode color,
|
|
386
|
+
* or `null` if the value is unrecognizable and must be dropped. Never returns
|
|
387
|
+
* a value that would fail OpenCode's `^#[0-9a-fA-F]{6}$` / enum check.
|
|
388
|
+
*
|
|
389
|
+
* Decision table:
|
|
390
|
+
* - Already a valid hex `#rrggbb` → pass through as-is
|
|
391
|
+
* - Already one of the 7 OpenCode enum tokens → pass through as-is
|
|
392
|
+
* - A known CSS named color (case-insensitive) → map to hex
|
|
393
|
+
* - Anything else → null (caller drops + warns)
|
|
394
|
+
*/
|
|
395
|
+
export function sanitizeAgentColor(value: string): string | null {
|
|
396
|
+
// Fast-path: already valid.
|
|
397
|
+
if (OPENCODE_HEX_COLOR_RE.test(value)) return value
|
|
398
|
+
if (OPENCODE_COLOR_ENUMS.has(value)) return value
|
|
399
|
+
|
|
400
|
+
// Try CSS named color (case-insensitive lookup).
|
|
401
|
+
const hex = CSS_COLOR_TO_HEX[value.toLowerCase()]
|
|
402
|
+
if (hex !== undefined) return hex
|
|
403
|
+
|
|
404
|
+
// Unknown — must be dropped to avoid an OpenCode schema error.
|
|
405
|
+
return null
|
|
406
|
+
}
|
|
407
|
+
|
|
205
408
|
// ── Command injection for one plugin ─────────────────────────────────────────
|
|
206
409
|
|
|
207
410
|
/**
|
|
@@ -411,10 +614,12 @@ async function injectPluginAgents(
|
|
|
411
614
|
if (typeof variant === "string") entry.variant = variant
|
|
412
615
|
|
|
413
616
|
const temperature = data["temperature"]
|
|
414
|
-
|
|
617
|
+
// OpenCode uses Schema.Finite which rejects NaN, Infinity, and -Infinity.
|
|
618
|
+
if (typeof temperature === "number" && isFinite(temperature)) entry.temperature = temperature
|
|
415
619
|
|
|
416
620
|
const top_p = data["top_p"]
|
|
417
|
-
|
|
621
|
+
// Same finite constraint as temperature.
|
|
622
|
+
if (typeof top_p === "number" && isFinite(top_p)) entry.top_p = top_p
|
|
418
623
|
|
|
419
624
|
const steps = data["steps"]
|
|
420
625
|
if (typeof steps === "number" && Number.isInteger(steps) && steps > 0) entry.steps = steps
|
|
@@ -422,8 +627,18 @@ async function injectPluginAgents(
|
|
|
422
627
|
const hidden = data["hidden"]
|
|
423
628
|
if (typeof hidden === "boolean") entry.hidden = hidden
|
|
424
629
|
|
|
425
|
-
const
|
|
426
|
-
if (typeof
|
|
630
|
+
const colorRaw = data["color"]
|
|
631
|
+
if (typeof colorRaw === "string") {
|
|
632
|
+
const color = sanitizeAgentColor(colorRaw)
|
|
633
|
+
if (color !== null) {
|
|
634
|
+
entry.color = color
|
|
635
|
+
} else {
|
|
636
|
+
logger.warn(
|
|
637
|
+
`agent "${bareName}" from plugin "${plugin.id}" has unrecognized color value "${colorRaw}"; dropping color field`,
|
|
638
|
+
{ fatalInStrict: false },
|
|
639
|
+
)
|
|
640
|
+
}
|
|
641
|
+
}
|
|
427
642
|
|
|
428
643
|
cfg.agent[name] = entry
|
|
429
644
|
summary.agents++
|
package/src/mcp-inject.ts
CHANGED
|
@@ -178,7 +178,15 @@ export function mapClaudeMcpServer(
|
|
|
178
178
|
if (typeof server.oauth.clientId === "string") oauth.clientId = server.oauth.clientId
|
|
179
179
|
if (typeof server.oauth.clientSecret === "string") oauth.clientSecret = server.oauth.clientSecret
|
|
180
180
|
if (typeof server.oauth.scope === "string") oauth.scope = server.oauth.scope
|
|
181
|
-
|
|
181
|
+
// OpenCode enforces callbackPort as an integer in [1, 65535] (Schema.isBetween).
|
|
182
|
+
if (
|
|
183
|
+
typeof server.oauth.callbackPort === "number" &&
|
|
184
|
+
Number.isInteger(server.oauth.callbackPort) &&
|
|
185
|
+
server.oauth.callbackPort >= 1 &&
|
|
186
|
+
server.oauth.callbackPort <= 65535
|
|
187
|
+
) {
|
|
188
|
+
oauth.callbackPort = server.oauth.callbackPort
|
|
189
|
+
}
|
|
182
190
|
if (typeof server.oauth.redirectUri === "string") oauth.redirectUri = server.oauth.redirectUri
|
|
183
191
|
entry.oauth = oauth
|
|
184
192
|
}
|