opencode-plugboard 0.21.0 → 0.21.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/README.md CHANGED
@@ -43,7 +43,7 @@ export PLUGBOARD_KEY_MY_APP=choose-a-long-random-string
43
43
  {
44
44
  "plugins": [
45
45
  {
46
- "package": "opencode-plugboard@0.21.0",
46
+ "package": "opencode-plugboard@0.21.1",
47
47
  "options": {
48
48
  "keys": { "my-app": "{env:PLUGBOARD_KEY_MY_APP}" },
49
49
  "models": ["anthropic/claude-sonnet-4-6", "github-copilot/*", "opencode-go/glm-5.3-flash"],
@@ -390,7 +390,7 @@ A server shared by a Discord bot, n8n, Open WebUI and a coding agent, with most
390
390
  {
391
391
  "plugins": [
392
392
  {
393
- "package": "opencode-plugboard@0.21.0",
393
+ "package": "opencode-plugboard@0.21.1",
394
394
  "options": {
395
395
  // Reachable from the LAN. Non-loopback hosts refuse to start without keys.
396
396
  "listen": { "host": "10.0.1.50", "port": 4010 },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-plugboard",
3
- "version": "0.21.0",
3
+ "version": "0.21.1",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Use the ChatGPT, Claude, Copilot and OpenCode Go logins in your OpenCode server from any app: an OpenCode V2 plugin serving OpenAI, Anthropic and Gemini compatible APIs.",
@@ -240,16 +240,25 @@ export function matchesSchema(value: unknown, schema: Readonly<Record<string, un
240
240
  * the schema asks for (one code fence is stripped first). Undefined for any other text.
241
241
  */
242
242
  export function textFallback(text: string, schema: Readonly<Record<string, unknown>>): string | undefined {
243
- const candidate = unfence(text.trim())
244
- let value: unknown
245
- try {
246
- value = JSON.parse(candidate)
247
- } catch {
248
- return undefined
243
+ // The whole text (fenced or not), else the one fenced block inside prose: Claude, told not to call
244
+ // tools, skips `respond` and answers "Here is the JSON: ```json {...}```" (found by the live suite).
245
+ const fenced = [...text.matchAll(FENCE_ANYWHERE)].map((match) => match[1]!.trim())
246
+ const candidates = [unfence(text.trim()), ...(fenced.length === 1 ? fenced : [])]
247
+ for (const candidate of candidates) {
248
+ let value: unknown
249
+ try {
250
+ value = JSON.parse(candidate)
251
+ } catch {
252
+ continue
253
+ }
254
+ if (matchesSchema(value, schema)) return candidate
249
255
  }
250
- return matchesSchema(value, schema) ? candidate : undefined
256
+ return undefined
251
257
  }
252
258
 
259
+ /** Every fenced code block (```json or a bare ```) anywhere in the text. */
260
+ const FENCE_ANYWHERE = /```[ \t]*(?:json)?[ \t]*\r?\n([\s\S]*?)\r?\n?[ \t]*```/gi
261
+
253
262
  /** One markdown code fence (```json or a bare ```) around the whole text. */
254
263
  const FENCED = /^```[ \t]*(?:json)?[ \t]*\r?\n([\s\S]*?)\r?\n?[ \t]*```$/i
255
264