opencode-see-image 0.1.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.
Files changed (3) hide show
  1. package/README.md +35 -3
  2. package/index.ts +47 -19
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -22,13 +22,23 @@ That's it — the plugin self-contains both the tool and the triggering instruct
22
22
 
23
23
  ## Prerequisites
24
24
 
25
- You need a connected vision-capable provider. The defaults assume **opencode-go** with **MiniMax M3**:
25
+ You need a connected vision-capable provider. The plugin auto-detects whichever you have connected — **either of these works**:
26
26
 
27
+ ### Option A — Free (OpenCode Zen)
28
+ 1. Run `/connect` in opencode
29
+ 2. Select **opencode** (OpenCode Zen)
30
+ 3. Paste your API key from [opencode.ai/auth](https://opencode.ai/auth)
31
+
32
+ The plugin falls back to **big-pickle** (free, vision-capable, ~20s). No subscription needed.
33
+
34
+ ### Option B — Paid, fast (OpenCode Go)
27
35
  1. Run `/connect` in opencode
28
36
  2. Select **opencode-go**
29
37
  3. Paste your API key from [opencode.ai/auth](https://opencode.ai/auth)
30
38
 
31
- If you already have opencode-go connected, you're done.
39
+ The plugin prefers **minimax-m3** via opencode-go (~3s) when available.
40
+
41
+ **Resolution order:** explicit `SEE_IMAGE_API_KEY` env → configured `SEE_IMAGE_PROVIDER` → `opencode-go` (MiniMax M3) → `opencode` (big-pickle, free).
32
42
 
33
43
  ## How it works
34
44
 
@@ -81,7 +91,15 @@ To use a different opencode-go model (e.g. Kimi K2.7):
81
91
  export SEE_IMAGE_MODEL="kimi-k2.7-code"
82
92
  ```
83
93
 
84
- ### Verified vision-capable models on opencode-go
94
+ ### Verified vision-capable models
95
+
96
+ **Free (OpenCode Zen):**
97
+
98
+ | Model | Speed | Notes |
99
+ |---|---|---|
100
+ | `big-pickle` | ~20s | Free. Accurate. Default fallback when only Zen is connected. |
101
+
102
+ **Paid (OpenCode Go):**
85
103
 
86
104
  | Model | Speed | Notes |
87
105
  |---|---|---|
@@ -102,6 +120,20 @@ When opencode rejects an image attachment, the model only receives a bare filena
102
120
 
103
121
  Pass an absolute `filePath` to skip the search.
104
122
 
123
+ ## Install via your agent (copy-paste this)
124
+
125
+ Paste this prompt to your opencode agent — it'll install the plugin for you:
126
+
127
+ ```
128
+ Install the opencode-see-image plugin so I can send you screenshots. Do this:
129
+
130
+ 1. Edit ~/.config/opencode/opencode.jsonc (create it if missing). Preserve any existing fields and the $schema. Add "opencode-see-image" to the "plugin" array. If "plugin" doesn't exist, add it as ["opencode-see-image"].
131
+ 2. Check that a vision-capable provider is connected by looking for ~/.local/share/opencode/auth.json with either an "opencode-go" entry (paid, fast) OR an "opencode" entry (free). If neither is present, tell me to run /connect and select either opencode-go or opencode (key from opencode.ai/auth).
132
+ 3. Tell me to quit and restart opencode for the plugin to load.
133
+
134
+ After I restart and attach a screenshot, you should call the see_image tool to view it.
135
+ ```
136
+
105
137
  ## License
106
138
 
107
139
  MIT
package/index.ts CHANGED
@@ -28,26 +28,53 @@ const EXT_MEDIA: Record<string, string> = {
28
28
  }
29
29
 
30
30
  // ─── Auth ───────────────────────────────────────────────────────────────────
31
- function readApiKey(): string {
32
- // 1. Explicit env var wins.
33
- if (process.env.SEE_IMAGE_API_KEY) return process.env.SEE_IMAGE_API_KEY
31
+ // Resolves a usable API key + the endpoint + model to use. Falls back through
32
+ // a chain so users with any connected opencode subscription (paid opencode-go
33
+ // or free opencode Zen) get a working default with zero config.
34
+ function resolveAuth(): { key: string; endpoint: string; model: string } {
35
+ // 1. Explicit env vars win outright.
36
+ if (process.env.SEE_IMAGE_API_KEY) {
37
+ return { key: process.env.SEE_IMAGE_API_KEY, endpoint: ENDPOINT, model: MODEL }
38
+ }
34
39
 
35
- // 2. Read from opencode's auth store (~/.local/share/opencode/auth.json).
40
+ // 2. Walk opencode's auth store and try the configured provider first,
41
+ // then a curated fallback chain (paid → free).
36
42
  const authPath = path.join(os.homedir(), ".local/share/opencode/auth.json")
43
+ let auth: any = {}
37
44
  try {
38
- const auth = JSON.parse(fs.readFileSync(authPath, "utf8"))
39
- const entry = auth[PROVIDER_ID]
40
- if (entry && (entry.key || entry.access)) {
41
- return entry.key || entry.access
42
- }
45
+ auth = JSON.parse(fs.readFileSync(authPath, "utf8"))
43
46
  } catch {
44
- // fall through to error
47
+ // ignore handled by the empty-auth path below
48
+ }
49
+
50
+ // Each candidate: [providerId, endpoint, defaultModel]
51
+ const candidates: Array<[string, string, string]> = [
52
+ [PROVIDER_ID, ENDPOINT, MODEL],
53
+ // Free fallback: OpenCode Zen's big-pickle supports vision at no cost.
54
+ ["opencode", "https://opencode.ai/zen/v1/messages", "big-pickle"],
55
+ // Paid fallbacks on opencode-go:
56
+ ["opencode-go", "https://opencode.ai/zen/go/v1/messages", "minimax-m3"],
57
+ ]
58
+
59
+ const tried: string[] = []
60
+ for (const [pid, ep, mdl] of candidates) {
61
+ const entry = auth[pid]
62
+ const k = entry && (entry.key || entry.access)
63
+ if (k) {
64
+ // If the user pinned PROVIDER_ID but not MODEL, honor the candidate's
65
+ // default model only when provider matches the pinned one; otherwise
66
+ // keep the configured MODEL (may be set via env).
67
+ const useModel =
68
+ pid === PROVIDER_ID || !process.env.SEE_IMAGE_MODEL ? mdl : MODEL
69
+ return { key: k, endpoint: ep, model: useModel }
70
+ }
71
+ tried.push(pid)
45
72
  }
46
73
 
47
74
  throw new Error(
48
- `see_image: no API key. Either run /connect for "${PROVIDER_ID}" in opencode, ` +
49
- `or set SEE_IMAGE_API_KEY, or set SEE_IMAGE_PROVIDER to a connected provider ID. ` +
50
- `(Looked in ${authPath} for key "${PROVIDER_ID}".)`,
75
+ `see_image: no API key. Connect a provider in opencode via /connect — ` +
76
+ `either "opencode-go" (paid, fast MiniMax M3) or "opencode" (free, big-pickle). ` +
77
+ `Or set SEE_IMAGE_API_KEY explicitly. (Checked providers: ${tried.join(", ") || "none"} in ${authPath}.)`,
51
78
  )
52
79
  }
53
80
 
@@ -136,8 +163,10 @@ const seeImageTool = tool({
136
163
  ? args.question
137
164
  : "Describe this image in detail. If it is a screenshot, describe the UI, text content, and layout precisely. This description will be used by another model to answer the user, so be thorough and accurate."
138
165
 
166
+ const { key, endpoint, model } = resolveAuth()
167
+
139
168
  const body = {
140
- model: MODEL,
169
+ model,
141
170
  max_tokens: 2048,
142
171
  messages: [
143
172
  {
@@ -153,8 +182,7 @@ const seeImageTool = tool({
153
182
  ],
154
183
  }
155
184
 
156
- const key = readApiKey()
157
- const res = await fetch(ENDPOINT, {
185
+ const res = await fetch(endpoint, {
158
186
  method: "POST",
159
187
  headers: {
160
188
  "x-api-key": key,
@@ -168,7 +196,7 @@ const seeImageTool = tool({
168
196
  if (!res.ok) {
169
197
  const errText = await res.text()
170
198
  throw new Error(
171
- `see_image: vision call to "${MODEL}" failed: HTTP ${res.status} — ${errText.slice(0, 300)}`,
199
+ `see_image: vision call to "${model}" @ ${endpoint} failed: HTTP ${res.status} — ${errText.slice(0, 300)}`,
172
200
  )
173
201
  }
174
202
 
@@ -183,13 +211,13 @@ const seeImageTool = tool({
183
211
 
184
212
  if (!text) {
185
213
  throw new Error(
186
- `see_image: model "${MODEL}" returned no text. Response: ${JSON.stringify(data).slice(0, 300)}`,
214
+ `see_image: model "${model}" returned no text. Response: ${JSON.stringify(data).slice(0, 300)}`,
187
215
  )
188
216
  }
189
217
 
190
218
  context.metadata({
191
219
  title: `see_image: ${path.basename(fullPath)}`,
192
- metadata: { model: MODEL, provider: PROVIDER_ID, file: fullPath },
220
+ metadata: { model, endpoint, file: fullPath },
193
221
  })
194
222
 
195
223
  return text
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-see-image",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Give non-vision opencode models the ability to see images/screenshots by routing them to a vision-capable model (MiniMax M3 via opencode-go by default).",
5
5
  "type": "module",
6
6
  "main": "index.ts",