Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# Changelog

## 1.0.1 — Direct tool access

### Fixes

- Restores `lightpanda` as a directly callable tool in OpenCode V2 by explicitly setting `codemode: false`. Previously, the V2 migration left it behind `execute`, unlike the built-in WebFetch tool.
- Keeps the existing tool name, inputs, permission action, and fetch behavior unchanged.

### Tests

- Adds a regression test requiring direct tool exposure.
- Tightens the OpenCode V2 smoke test to require a direct `lightpanda` call rather than accepting a nested Code Mode invocation.
- Gives the smoke test a distinct plugin ID so a globally installed copy cannot shadow the checkout under test.

## 1.0.0 — OpenCode V2 migration

This is the first published release for **OpenCode V2**. The major version marks a breaking plugin API migration, not a change to the Lightpanda tool's name or inputs.
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ A Lightpanda browser plugin/tool for OpenCode V2. It is like WebFetch tool on st
| `1.x` | OpenCode V2, 2.0.18 or newer |
| `0.2.2` | OpenCode V1 |

See [Migrating from 0.x](#migrating-from-0x) and the [1.0.0 release notes](CHANGELOG.md).
See [Migrating from 0.x](#migrating-from-0x) and the [release notes](CHANGELOG.md).

## Features

Expand All @@ -29,7 +29,7 @@ See [Migrating from 0.x](#migrating-from-0x) and the [1.0.0 release notes](CHANG
Install Lightpanda, then add the plugin globally:

```sh
opencode plugin add opencode-lightpanda@1.0.0
opencode plugin add opencode-lightpanda@1.0.1
```

To update an existing installation:
Expand All @@ -45,7 +45,7 @@ If OpenCode cannot find Lightpanda, start it with `LIGHTPANDA_BIN` set to the ex
## Migrating from 0.x

1. Upgrade to [OpenCode V2](https://opencode.ai/v2/docs/migrate-v1) (2.0.18 or newer) before upgrading this plugin. The 1.x plugin uses the new `@opencode/plugin` API; it cannot run in OpenCode V1.
2. Install `opencode-lightpanda@1.0.0` with the command above. For a configuration-managed installation, replace any pinned 0.x package entry with the V2 package:
2. Install `opencode-lightpanda@1.0.1` with the command above. For a configuration-managed installation, replace any pinned 0.x package entry with the V2 package:

```json
{
Expand Down Expand Up @@ -100,7 +100,7 @@ Load the checkout directly by adding it to `~/.config/opencode/opencode.json`:

## Behavior

- Adds a distinct `lightpanda` tool with its own permission action.
- Adds a directly callable `lightpanda` tool with its own permission action; no Code Mode `execute` wrapper is required.
- Returns an error for non-2xx responses and responses over 5 MB.
- Reports concise TLS/DNS fetch failures from Lightpanda's JSON diagnostics, with a bounded stderr fallback.
- Blocks private-network requests, including subresources initiated by page JavaScript.
Expand Down
1 change: 1 addition & 0 deletions lightpanda.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ test("registers the V2 lightpanda tool and permission action", () => {
expect(tools).toHaveLength(1)
expect(lightpanda.name).toBe("lightpanda")
expect(lightpanda.options?.permission).toBe("lightpanda")
expect(lightpanda.options?.codemode).toBe(false)
expect(lightpanda.input).toMatchObject({
type: "object",
required: ["url"],
Expand Down
2 changes: 1 addition & 1 deletion lightpanda.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ For web searches, use html.duckduckgo.com instead of Google because Google block
required: ["url"],
additionalProperties: false,
},
options: { permission: "lightpanda" },
options: { permission: "lightpanda", codemode: false },
execute: fetchPage,
})
})
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "opencode-lightpanda",
"version": "1.0.0",
"version": "1.0.1",
"description": "Lightpanda-powered web fetching for OpenCode V2",
"type": "module",
"main": "lightpanda.ts",
Expand Down
4 changes: 4 additions & 0 deletions test/fixtures/smoke-plugin/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import plugin from "../../../index"

// Keep a globally installed copy from shadowing the checkout under test.
export default { ...plugin, id: "opencode-lightpanda-smoke" }
15 changes: 6 additions & 9 deletions test/smoke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ try {
join(project, "opencode.json"),
JSON.stringify({
$schema: "https://opencode.ai/config.json",
plugins: [resolve(import.meta.dir, "..")],
plugins: [resolve(import.meta.dir, "fixtures/smoke-plugin")],
permissions: [{ action: "lightpanda", resource: "*", effect: "allow" }],
}),
)
Expand Down Expand Up @@ -48,20 +48,17 @@ try {
throw new Error(`OpenCode exited with ${exitCode}: ${error ?? stderr.trim()}`)
}

const call = events.find((event) => {
if (event.type !== "tool_use" || event.part?.state?.status !== "completed") return false
return event.part.tool === "lightpanda" || event.part.state.metadata?.metadata?.toolCalls?.some(
(tool) => tool.tool === "lightpanda" && tool.status === "completed",
)
})
const call = events.find((event) =>
event.type === "tool_use" && event.part?.tool === "lightpanda" && event.part.state?.status === "completed",
)
if (!call) {
const tools = events.filter((event) => event.type === "tool_use").map((event) => ({
tool: event.part?.tool,
status: event.part?.state?.status,
nested: event.part?.state?.metadata?.metadata?.toolCalls,
}))
const logs = stderr.split("\n").filter((line) => line.includes("loading plugin") || line.includes("failed to load plugin"))
throw new Error(`OpenCode did not call lightpanda (session ${events[0]?.sessionID}; tools: ${JSON.stringify(tools)}; logs: ${logs.join("\n")})`)
throw new Error(`OpenCode did not call lightpanda directly (session ${events[0]?.sessionID}; tools: ${JSON.stringify(tools)}; logs: ${logs.join("\n")})`)
}

const args = JSON.parse(call.part!.state!.output ?? "null") as unknown
Expand All @@ -72,7 +69,7 @@ try {
throw new Error("Lightpanda did not receive the requested format and timeout")
}

console.log("OpenCode V2 loaded and executed lightpanda")
console.log("OpenCode V2 loaded and directly executed lightpanda")
} finally {
try {
if (sessionID) {
Expand Down
Loading