@darknesskiller/opencode-cliproxyapi 0.2.1 → 0.3.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/CHANGELOG.md +29 -1
- package/README.md +66 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +199 -98
- package/dist/plugin-v2.d.ts +68 -0
- package/dist/plugin-v2.d.ts.map +1 -0
- package/dist/shared.d.ts +55 -0
- package/dist/shared.d.ts.map +1 -0
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,33 @@ All notable changes to this project are documented here.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
|
6
6
|
and this project follows [Semantic Versioning](https://semver.org/).
|
|
7
7
|
|
|
8
|
+
## [0.3.1] - Unreleased
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Image attachments now reach discovered Claude, Gemini, and GPT models.
|
|
13
|
+
OpenCode only forwards image parts to models that declare image input, and
|
|
14
|
+
the plugin previously declared it only for `*-image-*` generation models, so
|
|
15
|
+
other models received a `Cannot read "<file>"` placeholder instead. V1 now
|
|
16
|
+
sets `modalities: { input: ["text", "image"], output: ["text"] }` and V2
|
|
17
|
+
sets the matching `capabilities.input`, both from one shared policy.
|
|
18
|
+
Text-only `gpt-oss-*` models are no longer marked as attachment-capable.
|
|
19
|
+
|
|
20
|
+
## [0.3.0] - 2026-10-05
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- OpenCode v2 support. The package now default-exports a definition object with
|
|
25
|
+
`id`, `setup`, and `server`: OpenCode v2 loads it through `setup(ctx)` and
|
|
26
|
+
registers the provider with `ctx.provider.transform` (discovered catalog,
|
|
27
|
+
`@opencode/ai/providers/*` driver package, `baseURL`/`apiKey` settings),
|
|
28
|
+
while OpenCode v1 (1.18.29 and newer) keeps calling `server(ctx)`.
|
|
29
|
+
Anthropic-endpoint models are routed through
|
|
30
|
+
`@opencode/ai/providers/anthropic` via the per-model `package` field, and
|
|
31
|
+
v2 variants list exactly the effort levels the server reported.
|
|
32
|
+
- `index.ts` at the repository root so a local checkout can be used as the
|
|
33
|
+
plugin path without a build step.
|
|
34
|
+
|
|
8
35
|
## [0.2.1] - 2026-09-09
|
|
9
36
|
|
|
10
37
|
### Fixed
|
|
@@ -63,7 +90,8 @@ and this project follows [Semantic Versioning](https://semver.org/).
|
|
|
63
90
|
- Automatic model names and capability hints in OpenCode's model picker.
|
|
64
91
|
- Local plugin and npm package installation flows.
|
|
65
92
|
|
|
66
|
-
[Unreleased]: https://github.com/DarknessKiller/opencode-cliproxyapi/compare/v0.
|
|
93
|
+
[Unreleased]: https://github.com/DarknessKiller/opencode-cliproxyapi/compare/v0.3.0...HEAD
|
|
94
|
+
[0.3.0]: https://github.com/DarknessKiller/opencode-cliproxyapi/compare/v0.2.1...v0.3.0
|
|
67
95
|
[0.2.1]: https://github.com/DarknessKiller/opencode-cliproxyapi/compare/v0.2.0...v0.2.1
|
|
68
96
|
[0.2.0]: https://github.com/DarknessKiller/opencode-cliproxyapi/compare/v0.1.2...v0.2.0
|
|
69
97
|
[0.1.2]: https://github.com/DarknessKiller/opencode-cliproxyapi/compare/v0.1.1...v0.1.2
|
package/README.md
CHANGED
|
@@ -173,6 +173,50 @@ can be customized:
|
|
|
173
173
|
}
|
|
174
174
|
```
|
|
175
175
|
|
|
176
|
+
### OpenCode v2
|
|
177
|
+
|
|
178
|
+
OpenCode v2 replaced the `plugin` array with `plugins` and no longer runs v1
|
|
179
|
+
plugin code. This package ships a dual entrypoint: v2 calls `setup(ctx)` and
|
|
180
|
+
v1 (1.18.29 and newer) calls `server(ctx)`, so one configuration works on both.
|
|
181
|
+
|
|
182
|
+
```jsonc
|
|
183
|
+
// OpenCode v2 (~/.config/opencode/opencode.jsonc)
|
|
184
|
+
{
|
|
185
|
+
"$schema": "https://opencode.ai/config.json",
|
|
186
|
+
"plugins": [
|
|
187
|
+
{
|
|
188
|
+
"package": "@darknesskiller/opencode-cliproxyapi",
|
|
189
|
+
"options": {
|
|
190
|
+
"baseURL": "http://your-server:8317",
|
|
191
|
+
"apiKey": "your-cli-proxy-api-key"
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
]
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
On v2 the plugin registers the provider itself: it publishes the discovered
|
|
199
|
+
catalog through `ctx.provider.transform`, selects
|
|
200
|
+
`@opencode/ai/providers/openai-compatible` (or `@opencode/ai/providers/openai`
|
|
201
|
+
with `protocol: "responses"`), and routes Anthropic-endpoint models through
|
|
202
|
+
`@opencode/ai/providers/anthropic`. Every option in the table above works
|
|
203
|
+
unchanged inside the plugin entry's `options`.
|
|
204
|
+
|
|
205
|
+
To run a checkout instead of the published package, point `package` at the
|
|
206
|
+
repository directory (OpenCode resolves its root `index.ts`) and use the local
|
|
207
|
+
path:
|
|
208
|
+
|
|
209
|
+
```jsonc
|
|
210
|
+
{
|
|
211
|
+
"plugins": [
|
|
212
|
+
{
|
|
213
|
+
"package": "/path/to/opencode-cliproxyapi",
|
|
214
|
+
"options": { "baseURL": "http://your-server:8317" }
|
|
215
|
+
}
|
|
216
|
+
]
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
176
220
|
## Troubleshooting
|
|
177
221
|
|
|
178
222
|
### `Missing API key`
|
|
@@ -196,6 +240,28 @@ Then restart OpenCode and run:
|
|
|
196
240
|
opencode models cliproxyapi
|
|
197
241
|
```
|
|
198
242
|
|
|
243
|
+
### The model says it cannot read an attached image
|
|
244
|
+
|
|
245
|
+
CLIProxyAPI's `/v1/models` only reports model ids, so image input is inferred
|
|
246
|
+
from the model family: discovered models whose id starts with `claude`,
|
|
247
|
+
`gemini`, or `gpt` (except text-only `gpt-oss-*`) accept images, and
|
|
248
|
+
`*-image-*` models are treated as image generators. New releases in those
|
|
249
|
+
families are covered automatically. On OpenCode v1, declare the modalities of
|
|
250
|
+
a vision model outside these families under the provider's `models` in
|
|
251
|
+
`opencode.json`; user entries override the discovered metadata:
|
|
252
|
+
|
|
253
|
+
```jsonc
|
|
254
|
+
{
|
|
255
|
+
"provider": {
|
|
256
|
+
"cliproxyapi": {
|
|
257
|
+
"models": {
|
|
258
|
+
"your-vision-model": { "attachment": true, "modalities": { "input": ["text", "image"], "output": ["text"] } }
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
199
265
|
### Environment configuration works in one terminal but not another
|
|
200
266
|
|
|
201
267
|
Move the connection to the recommended global OpenCode config, or add the
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import type { Plugin } from "@opencode-ai/plugin";
|
|
2
|
+
import { setup } from "./plugin-v2.js";
|
|
2
3
|
export declare const CLIProxyAPIPlugin: Plugin;
|
|
4
|
+
/**
|
|
5
|
+
* Dual V1/V2 entry point. OpenCode V2 calls `setup(ctx)`; V1 (1.18.29 and
|
|
6
|
+
* newer) calls `server(ctx)` for the classic hooks object.
|
|
7
|
+
*/
|
|
3
8
|
declare const _default: {
|
|
4
9
|
id: string;
|
|
5
10
|
server: Plugin;
|
|
11
|
+
setup: typeof setup;
|
|
6
12
|
};
|
|
7
13
|
export default _default;
|
|
8
14
|
export { discoverModelProtocols, discoverModels, discoverReasoningEfforts, normalizeBaseURL, parseCatalog, parseModelProtocolCatalog, parseReasoningEffortCatalog, } from "./catalog.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAU,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAU,MAAM,EAAgB,MAAM,qBAAqB,CAAA;AAwBvE,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAWtC,eAAO,MAAM,iBAAiB,EAAE,MAiE/B,CAAA;AAED;;;GAGG;;;;;;AACH,wBAIkD;AAClD,OAAO,EACL,sBAAsB,EACtB,cAAc,EACd,wBAAwB,EACxB,gBAAgB,EAChB,YAAY,EACZ,yBAAyB,EACzB,2BAA2B,GAC5B,MAAM,cAAc,CAAA;AACrB,YAAY,EAAE,YAAY,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -103,12 +103,185 @@ function isRecord(value) {
|
|
|
103
103
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
// src/
|
|
106
|
+
// src/shared.ts
|
|
107
107
|
var DEFAULT_BASE_URL = "http://localhost:8317/v1";
|
|
108
108
|
var DEFAULT_PROVIDER_ID = "cliproxyapi";
|
|
109
109
|
var DEFAULT_PROVIDER_NAME = "CLIProxyAPI";
|
|
110
110
|
var DEFAULT_DISCOVERY_TIMEOUT_MS = 1e4;
|
|
111
111
|
var DEFAULT_MODEL_METADATA_URL = "https://models.dev/api.json";
|
|
112
|
+
var V1_CHAT_PACKAGE = "@ai-sdk/openai-compatible";
|
|
113
|
+
var V1_RESPONSES_PACKAGE = "@ai-sdk/openai";
|
|
114
|
+
var V1_ANTHROPIC_PACKAGE = "@ai-sdk/anthropic";
|
|
115
|
+
var V2_CHAT_PACKAGE = "@opencode/ai/providers/openai-compatible";
|
|
116
|
+
var V2_RESPONSES_PACKAGE = "@opencode/ai/providers/openai";
|
|
117
|
+
var V2_ANTHROPIC_PACKAGE = "@opencode/ai/providers/anthropic";
|
|
118
|
+
function readOptions(input) {
|
|
119
|
+
if (!input)
|
|
120
|
+
return {};
|
|
121
|
+
return {
|
|
122
|
+
baseURL: stringOption(input.baseURL),
|
|
123
|
+
apiKey: stringOption(input.apiKey),
|
|
124
|
+
providerID: stringOption(input.providerID),
|
|
125
|
+
providerName: stringOption(input.providerName),
|
|
126
|
+
protocol: input.protocol === "responses" ? "responses" : input.protocol === "chat" ? "chat" : undefined,
|
|
127
|
+
modelMetadataURL: input.modelMetadataURL === false ? false : stringOption(input.modelMetadataURL),
|
|
128
|
+
discoveryTimeoutMs: typeof input.discoveryTimeoutMs === "number" && input.discoveryTimeoutMs > 0 ? input.discoveryTimeoutMs : undefined
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function stringOption(value) {
|
|
132
|
+
return typeof value === "string" && value.trim() !== "" ? value : undefined;
|
|
133
|
+
}
|
|
134
|
+
function displayName(modelID) {
|
|
135
|
+
return modelID.split("-").map((part) => {
|
|
136
|
+
const lower = part.toLowerCase();
|
|
137
|
+
if (lower === "gpt")
|
|
138
|
+
return "GPT";
|
|
139
|
+
if (lower === "oss")
|
|
140
|
+
return "OSS";
|
|
141
|
+
if (lower === "codex")
|
|
142
|
+
return "Codex";
|
|
143
|
+
return part.charAt(0).toUpperCase() + part.slice(1);
|
|
144
|
+
}).join(" ");
|
|
145
|
+
}
|
|
146
|
+
function isImageModel(modelID) {
|
|
147
|
+
return /(?:^|-)image(?:-|$)/i.test(modelID);
|
|
148
|
+
}
|
|
149
|
+
function isReasoningModel(modelID, efforts) {
|
|
150
|
+
if (efforts?.levels.length)
|
|
151
|
+
return efforts.levels.some((level) => level !== "none");
|
|
152
|
+
return /thinking|reasoning|codex|gpt-(?:5|oss)/i.test(modelID);
|
|
153
|
+
}
|
|
154
|
+
function supportsAttachments(modelID) {
|
|
155
|
+
return /^(?:claude|gemini|gpt)/i.test(modelID) && !/^gpt-oss(?:-|$)/i.test(modelID);
|
|
156
|
+
}
|
|
157
|
+
function modelModalities(modelID) {
|
|
158
|
+
const imageOutput = isImageModel(modelID);
|
|
159
|
+
return {
|
|
160
|
+
input: imageOutput || supportsAttachments(modelID) ? ["text", "image"] : ["text"],
|
|
161
|
+
output: imageOutput ? ["image"] : ["text"]
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function resolveModelNpm(catalog, providerID, modelID) {
|
|
165
|
+
const provider = catalog[providerID];
|
|
166
|
+
return provider?.models[modelID] ?? provider?.npm;
|
|
167
|
+
}
|
|
168
|
+
function isAnthropicRouted(model, protocolCatalog) {
|
|
169
|
+
return model.ownedBy !== undefined && resolveModelNpm(protocolCatalog, model.ownedBy, model.id) === V1_ANTHROPIC_PACKAGE;
|
|
170
|
+
}
|
|
171
|
+
function supportedEffortLevels(model, input) {
|
|
172
|
+
const efforts = input.effortCatalog[model.id];
|
|
173
|
+
if (!efforts)
|
|
174
|
+
return;
|
|
175
|
+
if (isAnthropicRouted(model, input.protocolCatalog))
|
|
176
|
+
return;
|
|
177
|
+
const levels = efforts.levels.filter((level) => level !== "none");
|
|
178
|
+
return levels.length > 0 ? levels : undefined;
|
|
179
|
+
}
|
|
180
|
+
async function discoverCatalog(input) {
|
|
181
|
+
const [catalog, protocolDiscovery, effortDiscovery] = await Promise.all([
|
|
182
|
+
discoverModels({ baseURL: input.baseURL, apiKey: input.apiKey, timeoutMs: input.timeoutMs }),
|
|
183
|
+
discoverProtocols({ url: input.modelMetadataURL, timeoutMs: input.timeoutMs }),
|
|
184
|
+
discoverEffortLevels({ baseURL: input.baseURL, apiKey: input.apiKey, timeoutMs: input.timeoutMs })
|
|
185
|
+
]);
|
|
186
|
+
return {
|
|
187
|
+
catalog,
|
|
188
|
+
protocolCatalog: protocolDiscovery.catalog,
|
|
189
|
+
effortCatalog: effortDiscovery.catalog,
|
|
190
|
+
errors: [protocolDiscovery.error, effortDiscovery.error].filter((error) => error !== undefined)
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function discoverProtocols(input) {
|
|
194
|
+
if (input.url === false)
|
|
195
|
+
return Promise.resolve({ catalog: {} });
|
|
196
|
+
return discoverModelProtocols({
|
|
197
|
+
url: input.url,
|
|
198
|
+
timeoutMs: input.timeoutMs
|
|
199
|
+
}).then((catalog) => ({ catalog })).catch((error) => ({
|
|
200
|
+
catalog: {},
|
|
201
|
+
error: error instanceof Error ? error.message : String(error)
|
|
202
|
+
}));
|
|
203
|
+
}
|
|
204
|
+
function discoverEffortLevels(input) {
|
|
205
|
+
return discoverReasoningEfforts(input).then((catalog) => ({ catalog })).catch((error) => ({
|
|
206
|
+
catalog: {},
|
|
207
|
+
error: error instanceof Error ? error.message : String(error)
|
|
208
|
+
}));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// src/plugin-v2.ts
|
|
212
|
+
var DEFAULT_CONTEXT_LIMIT = 128000;
|
|
213
|
+
var DEFAULT_OUTPUT_LIMIT = 4096;
|
|
214
|
+
function toV2Model(model, input) {
|
|
215
|
+
const image = isImageModel(model.id);
|
|
216
|
+
const modalities = modelModalities(model.id);
|
|
217
|
+
const levels = supportedEffortLevels(model, input.discovery);
|
|
218
|
+
const anthropic = isAnthropicRouted(model, input.discovery.protocolCatalog);
|
|
219
|
+
return {
|
|
220
|
+
id: model.id,
|
|
221
|
+
modelID: model.id,
|
|
222
|
+
providerID: input.providerID,
|
|
223
|
+
name: displayName(model.id),
|
|
224
|
+
...anthropic ? { package: V2_ANTHROPIC_PACKAGE } : {},
|
|
225
|
+
capabilities: {
|
|
226
|
+
tools: !image,
|
|
227
|
+
input: modalities.input,
|
|
228
|
+
output: modalities.output
|
|
229
|
+
},
|
|
230
|
+
variants: (levels ?? []).map((level) => ({
|
|
231
|
+
id: level,
|
|
232
|
+
settings: { reasoningEffort: level }
|
|
233
|
+
})),
|
|
234
|
+
time: { released: 0 },
|
|
235
|
+
cost: [{ input: 0, output: 0, cache: { read: 0, write: 0 } }],
|
|
236
|
+
status: "active",
|
|
237
|
+
enabled: true,
|
|
238
|
+
limit: { context: DEFAULT_CONTEXT_LIMIT, output: DEFAULT_OUTPUT_LIMIT }
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function toV2Models(input) {
|
|
242
|
+
return input.discovery.catalog.map((model) => toV2Model(model, { providerID: input.providerID, discovery: input.discovery }));
|
|
243
|
+
}
|
|
244
|
+
async function setup(ctx) {
|
|
245
|
+
const options = readOptions(ctx.options);
|
|
246
|
+
const providerID = options.providerID ?? DEFAULT_PROVIDER_ID;
|
|
247
|
+
const baseURL = normalizeBaseURL(options.baseURL ?? process.env.CLIPROXY_BASE_URL ?? DEFAULT_BASE_URL);
|
|
248
|
+
const apiKey = options.apiKey ?? process.env.CLIPROXY_API_KEY;
|
|
249
|
+
const timeoutMs = options.discoveryTimeoutMs ?? DEFAULT_DISCOVERY_TIMEOUT_MS;
|
|
250
|
+
const modelMetadataURL = options.modelMetadataURL ?? DEFAULT_MODEL_METADATA_URL;
|
|
251
|
+
const discovery = await discoverCatalog({ baseURL, apiKey, modelMetadataURL, timeoutMs });
|
|
252
|
+
for (const error of discovery.errors) {
|
|
253
|
+
console.warn(`[cliproxyapi] ${error}`);
|
|
254
|
+
}
|
|
255
|
+
const models = toV2Models({ providerID, discovery });
|
|
256
|
+
const providerPackage = options.protocol === "responses" ? V2_RESPONSES_PACKAGE : V2_CHAT_PACKAGE;
|
|
257
|
+
await ctx.provider.transform((editor) => {
|
|
258
|
+
const settings = { baseURL };
|
|
259
|
+
if (apiKey)
|
|
260
|
+
settings.apiKey = apiKey;
|
|
261
|
+
const existing = editor.get(providerID);
|
|
262
|
+
if (existing) {
|
|
263
|
+
editor.update(providerID, (provider) => {
|
|
264
|
+
provider.name = provider.name || options.providerName || DEFAULT_PROVIDER_NAME;
|
|
265
|
+
provider.activation = "enabled";
|
|
266
|
+
provider.package = providerPackage;
|
|
267
|
+
provider.settings = { ...provider.settings, ...settings };
|
|
268
|
+
});
|
|
269
|
+
editor.models.set(providerID, models);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
const info = {
|
|
273
|
+
id: providerID,
|
|
274
|
+
name: options.providerName ?? DEFAULT_PROVIDER_NAME,
|
|
275
|
+
activation: "enabled",
|
|
276
|
+
package: providerPackage,
|
|
277
|
+
settings
|
|
278
|
+
};
|
|
279
|
+
editor.add({ info, models });
|
|
280
|
+
});
|
|
281
|
+
return async () => {};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// src/index.ts
|
|
112
285
|
var CLIProxyAPIPlugin = async ({ client }, rawOptions) => {
|
|
113
286
|
const options = readOptions(rawOptions);
|
|
114
287
|
return {
|
|
@@ -119,24 +292,13 @@ var CLIProxyAPIPlugin = async ({ client }, rawOptions) => {
|
|
|
119
292
|
const apiKey = options.apiKey ?? process.env.CLIPROXY_API_KEY ?? stringOption(existing?.options?.apiKey);
|
|
120
293
|
try {
|
|
121
294
|
const timeoutMs = options.discoveryTimeoutMs ?? DEFAULT_DISCOVERY_TIMEOUT_MS;
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
url: options.modelMetadataURL ?? DEFAULT_MODEL_METADATA_URL,
|
|
130
|
-
timeoutMs
|
|
131
|
-
}),
|
|
132
|
-
discoverEffortLevels({
|
|
133
|
-
baseURL,
|
|
134
|
-
apiKey,
|
|
135
|
-
timeoutMs
|
|
136
|
-
})
|
|
137
|
-
]);
|
|
138
|
-
const discoveryErrors = [protocolDiscovery.error, effortDiscovery.error].filter((error) => error !== undefined);
|
|
139
|
-
for (const message of discoveryErrors) {
|
|
295
|
+
const discovery = await discoverCatalog({
|
|
296
|
+
baseURL,
|
|
297
|
+
apiKey,
|
|
298
|
+
modelMetadataURL: options.modelMetadataURL ?? DEFAULT_MODEL_METADATA_URL,
|
|
299
|
+
timeoutMs
|
|
300
|
+
});
|
|
301
|
+
for (const message of discovery.errors) {
|
|
140
302
|
await client.app.log({
|
|
141
303
|
body: {
|
|
142
304
|
service: "opencode-cliproxyapi",
|
|
@@ -151,15 +313,15 @@ var CLIProxyAPIPlugin = async ({ client }, rawOptions) => {
|
|
|
151
313
|
baseURL,
|
|
152
314
|
apiKey,
|
|
153
315
|
protocol: options.protocol ?? "chat",
|
|
154
|
-
catalog,
|
|
155
|
-
protocolCatalog:
|
|
156
|
-
effortCatalog:
|
|
316
|
+
catalog: discovery.catalog,
|
|
317
|
+
protocolCatalog: discovery.protocolCatalog,
|
|
318
|
+
effortCatalog: discovery.effortCatalog
|
|
157
319
|
});
|
|
158
320
|
await client.app.log({
|
|
159
321
|
body: {
|
|
160
322
|
service: "opencode-cliproxyapi",
|
|
161
323
|
level: "info",
|
|
162
|
-
message: `Discovered ${catalog.length} CLIProxyAPI models from ${baseURL}`
|
|
324
|
+
message: `Discovered ${discovery.catalog.length} CLIProxyAPI models from ${baseURL}`
|
|
163
325
|
}
|
|
164
326
|
});
|
|
165
327
|
} catch (error) {
|
|
@@ -177,7 +339,8 @@ var CLIProxyAPIPlugin = async ({ client }, rawOptions) => {
|
|
|
177
339
|
};
|
|
178
340
|
var src_default = {
|
|
179
341
|
id: "opencode-cliproxyapi",
|
|
180
|
-
server: CLIProxyAPIPlugin
|
|
342
|
+
server: CLIProxyAPIPlugin,
|
|
343
|
+
setup
|
|
181
344
|
};
|
|
182
345
|
function addProvider(config, input) {
|
|
183
346
|
const existing = config.provider?.[input.providerID];
|
|
@@ -188,17 +351,12 @@ function addProvider(config, input) {
|
|
|
188
351
|
tool_call: !isImageModel(model.id),
|
|
189
352
|
reasoning: isReasoningModel(model.id, input.effortCatalog[model.id]),
|
|
190
353
|
attachment: supportsAttachments(model.id),
|
|
191
|
-
...model.ownedBy && resolveModelNpm(input.protocolCatalog, model.ownedBy, model.id) ===
|
|
354
|
+
...model.ownedBy && resolveModelNpm(input.protocolCatalog, model.ownedBy, model.id) === V1_ANTHROPIC_PACKAGE ? {
|
|
192
355
|
provider: {
|
|
193
|
-
npm:
|
|
194
|
-
}
|
|
195
|
-
} : {},
|
|
196
|
-
...isImageModel(model.id) ? {
|
|
197
|
-
modalities: {
|
|
198
|
-
input: ["text", "image"],
|
|
199
|
-
output: ["image"]
|
|
356
|
+
npm: V1_ANTHROPIC_PACKAGE
|
|
200
357
|
}
|
|
201
358
|
} : {},
|
|
359
|
+
...imageModalities(model.id),
|
|
202
360
|
...reasoningEffortConfig(model, input)
|
|
203
361
|
}
|
|
204
362
|
]));
|
|
@@ -207,7 +365,7 @@ function addProvider(config, input) {
|
|
|
207
365
|
[input.providerID]: {
|
|
208
366
|
...existing,
|
|
209
367
|
name: existing?.name ?? input.providerName,
|
|
210
|
-
npm: existing?.npm ?? (input.protocol === "responses" ?
|
|
368
|
+
npm: existing?.npm ?? (input.protocol === "responses" ? V1_RESPONSES_PACKAGE : V1_CHAT_PACKAGE),
|
|
211
369
|
env: [...new Set([...existing?.env ?? [], "CLIPROXY_API_KEY"])],
|
|
212
370
|
options: {
|
|
213
371
|
...existing?.options,
|
|
@@ -218,15 +376,18 @@ function addProvider(config, input) {
|
|
|
218
376
|
}
|
|
219
377
|
};
|
|
220
378
|
}
|
|
379
|
+
function imageModalities(modelID) {
|
|
380
|
+
const modalities = modelModalities(modelID);
|
|
381
|
+
if (!modalities.input.includes("image") && !modalities.output.includes("image"))
|
|
382
|
+
return {};
|
|
383
|
+
return { modalities };
|
|
384
|
+
}
|
|
221
385
|
function reasoningEffortConfig(model, input) {
|
|
222
386
|
const efforts = input.effortCatalog[model.id];
|
|
223
387
|
if (!efforts)
|
|
224
388
|
return {};
|
|
225
|
-
const
|
|
226
|
-
if (
|
|
227
|
-
return {};
|
|
228
|
-
const levels = efforts.levels.filter((level) => level !== "none");
|
|
229
|
-
if (levels.length === 0)
|
|
389
|
+
const levels = supportedEffortLevels(model, input);
|
|
390
|
+
if (!levels)
|
|
230
391
|
return {};
|
|
231
392
|
const defaultLevel = efforts.defaultLevel !== undefined && efforts.levels.includes(efforts.defaultLevel) ? efforts.defaultLevel : undefined;
|
|
232
393
|
return {
|
|
@@ -245,10 +406,6 @@ function disabledVariants(supported) {
|
|
|
245
406
|
{ disabled: true }
|
|
246
407
|
]));
|
|
247
408
|
}
|
|
248
|
-
function resolveModelNpm(catalog, providerID, modelID) {
|
|
249
|
-
const provider = catalog[providerID];
|
|
250
|
-
return provider?.models[modelID] ?? provider?.npm;
|
|
251
|
-
}
|
|
252
409
|
function mergeModels(discovered, existing) {
|
|
253
410
|
const merged = { ...discovered };
|
|
254
411
|
for (const [modelID, model] of Object.entries(existing ?? {})) {
|
|
@@ -262,62 +419,6 @@ function mergeModels(discovered, existing) {
|
|
|
262
419
|
}
|
|
263
420
|
return merged;
|
|
264
421
|
}
|
|
265
|
-
function readOptions(input) {
|
|
266
|
-
if (!input)
|
|
267
|
-
return {};
|
|
268
|
-
return {
|
|
269
|
-
baseURL: stringOption(input.baseURL),
|
|
270
|
-
apiKey: stringOption(input.apiKey),
|
|
271
|
-
providerID: stringOption(input.providerID),
|
|
272
|
-
providerName: stringOption(input.providerName),
|
|
273
|
-
protocol: input.protocol === "responses" ? "responses" : input.protocol === "chat" ? "chat" : undefined,
|
|
274
|
-
modelMetadataURL: input.modelMetadataURL === false ? false : stringOption(input.modelMetadataURL),
|
|
275
|
-
discoveryTimeoutMs: typeof input.discoveryTimeoutMs === "number" && input.discoveryTimeoutMs > 0 ? input.discoveryTimeoutMs : undefined
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
function discoverProtocols(input) {
|
|
279
|
-
if (input.url === false)
|
|
280
|
-
return Promise.resolve({ catalog: {} });
|
|
281
|
-
return discoverModelProtocols({
|
|
282
|
-
url: input.url,
|
|
283
|
-
timeoutMs: input.timeoutMs
|
|
284
|
-
}).then((catalog) => ({ catalog })).catch((error) => ({
|
|
285
|
-
catalog: {},
|
|
286
|
-
error: error instanceof Error ? error.message : String(error)
|
|
287
|
-
}));
|
|
288
|
-
}
|
|
289
|
-
function discoverEffortLevels(input) {
|
|
290
|
-
return discoverReasoningEfforts(input).then((catalog) => ({ catalog })).catch((error) => ({
|
|
291
|
-
catalog: {},
|
|
292
|
-
error: error instanceof Error ? error.message : String(error)
|
|
293
|
-
}));
|
|
294
|
-
}
|
|
295
|
-
function stringOption(value) {
|
|
296
|
-
return typeof value === "string" && value.trim() !== "" ? value : undefined;
|
|
297
|
-
}
|
|
298
|
-
function displayName(modelID) {
|
|
299
|
-
return modelID.split("-").map((part) => {
|
|
300
|
-
const lower = part.toLowerCase();
|
|
301
|
-
if (lower === "gpt")
|
|
302
|
-
return "GPT";
|
|
303
|
-
if (lower === "oss")
|
|
304
|
-
return "OSS";
|
|
305
|
-
if (lower === "codex")
|
|
306
|
-
return "Codex";
|
|
307
|
-
return part.charAt(0).toUpperCase() + part.slice(1);
|
|
308
|
-
}).join(" ");
|
|
309
|
-
}
|
|
310
|
-
function isImageModel(modelID) {
|
|
311
|
-
return /(?:^|-)image(?:-|$)/i.test(modelID);
|
|
312
|
-
}
|
|
313
|
-
function isReasoningModel(modelID, efforts) {
|
|
314
|
-
if (efforts?.levels.length)
|
|
315
|
-
return efforts.levels.some((level) => level !== "none");
|
|
316
|
-
return /thinking|reasoning|codex|gpt-(?:5|oss)/i.test(modelID);
|
|
317
|
-
}
|
|
318
|
-
function supportsAttachments(modelID) {
|
|
319
|
-
return /^(?:claude|gemini|gpt)/i.test(modelID);
|
|
320
|
-
}
|
|
321
422
|
export {
|
|
322
423
|
CLIProxyAPIPlugin,
|
|
323
424
|
src_default as default,
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode V2 entrypoint for CLIProxyAPI.
|
|
3
|
+
*
|
|
4
|
+
* V2 plugins default-export a definition with an `id` and a `setup(ctx)`
|
|
5
|
+
* function; V1 plugin functions do not run on V2. This module ports the
|
|
6
|
+
* discovery pipeline to the V2 provider API: it resolves the connector
|
|
7
|
+
* options, runs the same catalog/protocol/effort discovery as V1, and
|
|
8
|
+
* publishes the resulting provider through `ctx.provider.transform`.
|
|
9
|
+
*
|
|
10
|
+
* All `@opencode/plugin` imports are type-only, so the built plugin has no
|
|
11
|
+
* runtime dependency on the V2 SDK.
|
|
12
|
+
*/
|
|
13
|
+
import type { Plugin } from "@opencode/plugin";
|
|
14
|
+
import { type CatalogDiscovery } from "./shared.js";
|
|
15
|
+
type Context = Plugin.Context;
|
|
16
|
+
export interface V2ModelVariant {
|
|
17
|
+
id: string;
|
|
18
|
+
settings?: Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
export interface V2ModelInfo {
|
|
21
|
+
id: string;
|
|
22
|
+
modelID: string;
|
|
23
|
+
providerID: string;
|
|
24
|
+
name: string;
|
|
25
|
+
package?: string;
|
|
26
|
+
capabilities: {
|
|
27
|
+
tools: boolean;
|
|
28
|
+
input: string[];
|
|
29
|
+
output: string[];
|
|
30
|
+
};
|
|
31
|
+
variants: V2ModelVariant[];
|
|
32
|
+
time: {
|
|
33
|
+
released: number;
|
|
34
|
+
};
|
|
35
|
+
cost: Array<{
|
|
36
|
+
input: number;
|
|
37
|
+
output: number;
|
|
38
|
+
cache: {
|
|
39
|
+
read: number;
|
|
40
|
+
write: number;
|
|
41
|
+
};
|
|
42
|
+
}>;
|
|
43
|
+
status: "active";
|
|
44
|
+
enabled: boolean;
|
|
45
|
+
limit: {
|
|
46
|
+
context: number;
|
|
47
|
+
output: number;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export declare function toV2Model(model: CatalogDiscovery["catalog"][number], input: {
|
|
51
|
+
providerID: string;
|
|
52
|
+
discovery: CatalogDiscovery;
|
|
53
|
+
}): V2ModelInfo;
|
|
54
|
+
export declare function toV2Models(input: {
|
|
55
|
+
providerID: string;
|
|
56
|
+
discovery: CatalogDiscovery;
|
|
57
|
+
}): V2ModelInfo[];
|
|
58
|
+
/**
|
|
59
|
+
* V2 setup. Registers the provider from the discovered catalog and returns a
|
|
60
|
+
* no-op cleanup (V2 keeps no timers or subscriptions for this plugin).
|
|
61
|
+
*/
|
|
62
|
+
export declare function setup(ctx: Context): Promise<() => Promise<void>>;
|
|
63
|
+
export declare const v2Definition: {
|
|
64
|
+
id: string;
|
|
65
|
+
setup: typeof setup;
|
|
66
|
+
};
|
|
67
|
+
export {};
|
|
68
|
+
//# sourceMappingURL=plugin-v2.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-v2.d.ts","sourceRoot":"","sources":["../src/plugin-v2.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAS,MAAM,EAAY,MAAM,kBAAkB,CAAA;AAE/D,OAAO,EAgBL,KAAK,gBAAgB,EAEtB,MAAM,aAAa,CAAA;AAEpB,KAAK,OAAO,GAAG,MAAM,CAAC,OAAO,CAAA;AAK7B,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE;QACZ,KAAK,EAAE,OAAO,CAAA;QACd,KAAK,EAAE,MAAM,EAAE,CAAA;QACf,MAAM,EAAE,MAAM,EAAE,CAAA;KACjB,CAAA;IACD,QAAQ,EAAE,cAAc,EAAE,CAAA;IAC1B,IAAI,EAAE;QACJ,QAAQ,EAAE,MAAM,CAAA;KACjB,CAAA;IACD,IAAI,EAAE,KAAK,CAAC;QACV,KAAK,EAAE,MAAM,CAAA;QACb,MAAM,EAAE,MAAM,CAAA;QACd,KAAK,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAA;KACvC,CAAC,CAAA;IACF,MAAM,EAAE,QAAQ,CAAA;IAChB,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE;QACL,OAAO,EAAE,MAAM,CAAA;QACf,MAAM,EAAE,MAAM,CAAA;KACf,CAAA;CACF;AAED,wBAAgB,SAAS,CACvB,KAAK,EAAE,gBAAgB,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,EAC1C,KAAK,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,gBAAgB,CAAA;CAAE,GACzD,WAAW,CA6Bb;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE;IAChC,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,gBAAgB,CAAA;CAC5B,GAAG,WAAW,EAAE,CAIhB;AAED;;;GAGG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CA8CtE;AAED,eAAO,MAAM,YAAY;;;CAGxB,CAAA"}
|
package/dist/shared.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { PluginOptions } from "@opencode-ai/plugin";
|
|
2
|
+
import { type CatalogModel, type ModelProtocolCatalog, type ReasoningEffortCatalog } from "./catalog.js";
|
|
3
|
+
export declare const DEFAULT_BASE_URL = "http://localhost:8317/v1";
|
|
4
|
+
export declare const DEFAULT_PROVIDER_ID = "cliproxyapi";
|
|
5
|
+
export declare const DEFAULT_PROVIDER_NAME = "CLIProxyAPI";
|
|
6
|
+
export declare const DEFAULT_DISCOVERY_TIMEOUT_MS = 10000;
|
|
7
|
+
export declare const DEFAULT_MODEL_METADATA_URL = "https://models.dev/api.json";
|
|
8
|
+
export declare const V1_CHAT_PACKAGE = "@ai-sdk/openai-compatible";
|
|
9
|
+
export declare const V1_RESPONSES_PACKAGE = "@ai-sdk/openai";
|
|
10
|
+
export declare const V1_ANTHROPIC_PACKAGE = "@ai-sdk/anthropic";
|
|
11
|
+
export declare const V2_CHAT_PACKAGE = "@opencode/ai/providers/openai-compatible";
|
|
12
|
+
export declare const V2_RESPONSES_PACKAGE = "@opencode/ai/providers/openai";
|
|
13
|
+
export declare const V2_ANTHROPIC_PACKAGE = "@opencode/ai/providers/anthropic";
|
|
14
|
+
export type ConnectorOptions = {
|
|
15
|
+
baseURL?: string;
|
|
16
|
+
apiKey?: string;
|
|
17
|
+
providerID?: string;
|
|
18
|
+
providerName?: string;
|
|
19
|
+
protocol?: "chat" | "responses";
|
|
20
|
+
modelMetadataURL?: string | false;
|
|
21
|
+
discoveryTimeoutMs?: number;
|
|
22
|
+
};
|
|
23
|
+
export type CatalogDiscovery = {
|
|
24
|
+
catalog: CatalogModel[];
|
|
25
|
+
protocolCatalog: ModelProtocolCatalog;
|
|
26
|
+
effortCatalog: ReasoningEffortCatalog;
|
|
27
|
+
errors: string[];
|
|
28
|
+
};
|
|
29
|
+
export declare function readOptions(input?: PluginOptions): ConnectorOptions;
|
|
30
|
+
export declare function stringOption(value: unknown): string | undefined;
|
|
31
|
+
export declare function displayName(modelID: string): string;
|
|
32
|
+
export declare function isImageModel(modelID: string): boolean;
|
|
33
|
+
export declare function isReasoningModel(modelID: string, efforts?: {
|
|
34
|
+
levels: string[];
|
|
35
|
+
defaultLevel?: string;
|
|
36
|
+
}): boolean;
|
|
37
|
+
export declare function supportsAttachments(modelID: string): boolean;
|
|
38
|
+
export type ModelModalities = {
|
|
39
|
+
input: Array<"text" | "image">;
|
|
40
|
+
output: Array<"text" | "image">;
|
|
41
|
+
};
|
|
42
|
+
export declare function modelModalities(modelID: string): ModelModalities;
|
|
43
|
+
export declare function resolveModelNpm(catalog: ModelProtocolCatalog, providerID: string, modelID: string): string | undefined;
|
|
44
|
+
export declare function isAnthropicRouted(model: CatalogModel, protocolCatalog: ModelProtocolCatalog): boolean;
|
|
45
|
+
export declare function supportedEffortLevels(model: CatalogModel, input: {
|
|
46
|
+
protocolCatalog: ModelProtocolCatalog;
|
|
47
|
+
effortCatalog: ReasoningEffortCatalog;
|
|
48
|
+
}): string[] | undefined;
|
|
49
|
+
export declare function discoverCatalog(input: {
|
|
50
|
+
baseURL: string;
|
|
51
|
+
apiKey?: string;
|
|
52
|
+
modelMetadataURL: string | false;
|
|
53
|
+
timeoutMs: number;
|
|
54
|
+
}): Promise<CatalogDiscovery>;
|
|
55
|
+
//# sourceMappingURL=shared.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../src/shared.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@darknesskiller/opencode-cliproxyapi",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.1",
|
|
5
5
|
"description": "Fork of opencode-cliproxyapi that discovers CLIProxyAPI models and auto-configures reasoning-effort variants in OpenCode.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "MIT",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"LICENSE"
|
|
30
30
|
],
|
|
31
31
|
"scripts": {
|
|
32
|
-
"build": "bun build ./src/index.ts --outdir ./dist --target bun --format esm --external @opencode-ai/plugin && tsc --emitDeclarationOnly",
|
|
32
|
+
"build": "bun build ./src/index.ts --outdir ./dist --target bun --format esm --external @opencode-ai/plugin --external @opencode/plugin && tsc --emitDeclarationOnly",
|
|
33
33
|
"typecheck": "tsc --noEmit",
|
|
34
34
|
"test": "bun test ./src",
|
|
35
35
|
"check": "bun run typecheck && bun run test && bun run build",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
],
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@opencode-ai/plugin": "1.18.29",
|
|
46
|
+
"@opencode/plugin": "^2.0.18",
|
|
46
47
|
"@types/bun": "^1.3.5",
|
|
47
48
|
"typescript": "^5.9.3"
|
|
48
49
|
},
|