@darknesskiller/opencode-cliproxyapi 0.3.0 → 0.4.0
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 +35 -2
- package/README.md +22 -0
- package/dist/catalog.d.ts +5 -1
- package/dist/catalog.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +141 -42
- package/dist/plugin-v2.d.ts +6 -0
- package/dist/plugin-v2.d.ts.map +1 -1
- package/dist/shared.d.ts +22 -5
- package/dist/shared.d.ts.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,38 @@ 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.
|
|
8
|
+
## [0.4.0] - 2026-11-13
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Discovered models now carry token limits parsed from the CLIProxyAPI
|
|
13
|
+
codex-client catalog, resolving the V1/V2 alignment gap. Context window
|
|
14
|
+
comes from `context_window`, falling back to `max_context_window` and
|
|
15
|
+
then 128,000; output comes from `max_tokens`, falling back to 32,000,
|
|
16
|
+
OpenCode V1's effective default (`0` breaks Anthropic-routed models and
|
|
17
|
+
V2 has no default of its own). Both plugin versions resolve limits
|
|
18
|
+
through the same shared code.
|
|
19
|
+
- User config now wins per field over discovered models in both versions:
|
|
20
|
+
non-empty strings for `name`/`package`/`provider.npm`, booleans for the
|
|
21
|
+
capability flags, and `limit` merged key-by-key (raising and lowering
|
|
22
|
+
both win; `0` keeps the default policy). `variants` and other nested
|
|
23
|
+
objects keep whole-object override semantics, models configured in user
|
|
24
|
+
config alone are preserved, and invalid values no longer erase what
|
|
25
|
+
discovery found.
|
|
26
|
+
|
|
27
|
+
## [0.3.1] - 2026-11-13
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- Image attachments now reach discovered Claude, Gemini, and GPT models.
|
|
32
|
+
OpenCode only forwards image parts to models that declare image input, and
|
|
33
|
+
the plugin previously declared it only for `*-image-*` generation models, so
|
|
34
|
+
other models received a `Cannot read "<file>"` placeholder instead. V1 now
|
|
35
|
+
sets `modalities: { input: ["text", "image"], output: ["text"] }` and V2
|
|
36
|
+
sets the matching `capabilities.input`, both from one shared policy.
|
|
37
|
+
Text-only `gpt-oss-*` models are no longer marked as attachment-capable.
|
|
38
|
+
|
|
39
|
+
## [0.3.0] - 2026-10-06
|
|
9
40
|
|
|
10
41
|
### Added
|
|
11
42
|
|
|
@@ -78,7 +109,9 @@ and this project follows [Semantic Versioning](https://semver.org/).
|
|
|
78
109
|
- Automatic model names and capability hints in OpenCode's model picker.
|
|
79
110
|
- Local plugin and npm package installation flows.
|
|
80
111
|
|
|
81
|
-
[Unreleased]: https://github.com/DarknessKiller/opencode-cliproxyapi/compare/v0.
|
|
112
|
+
[Unreleased]: https://github.com/DarknessKiller/opencode-cliproxyapi/compare/v0.4.0...HEAD
|
|
113
|
+
[0.4.0]: https://github.com/DarknessKiller/opencode-cliproxyapi/compare/v0.3.1...v0.4.0
|
|
114
|
+
[0.3.0]: https://github.com/DarknessKiller/opencode-cliproxyapi/compare/v0.2.1...v0.3.0
|
|
82
115
|
[0.2.1]: https://github.com/DarknessKiller/opencode-cliproxyapi/compare/v0.2.0...v0.2.1
|
|
83
116
|
[0.2.0]: https://github.com/DarknessKiller/opencode-cliproxyapi/compare/v0.1.2...v0.2.0
|
|
84
117
|
[0.1.2]: https://github.com/DarknessKiller/opencode-cliproxyapi/compare/v0.1.1...v0.1.2
|
package/README.md
CHANGED
|
@@ -240,6 +240,28 @@ Then restart OpenCode and run:
|
|
|
240
240
|
opencode models cliproxyapi
|
|
241
241
|
```
|
|
242
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
|
+
|
|
243
265
|
### Environment configuration works in one terminal but not another
|
|
244
266
|
|
|
245
267
|
Move the connection to the recommended global OpenCode config, or add the
|
package/dist/catalog.d.ts
CHANGED
|
@@ -7,8 +7,12 @@ export type ModelProtocolCatalog = Record<string, {
|
|
|
7
7
|
models: Record<string, string>;
|
|
8
8
|
}>;
|
|
9
9
|
export type ReasoningEffortLevels = {
|
|
10
|
-
levels
|
|
10
|
+
levels?: string[];
|
|
11
11
|
defaultLevel?: string;
|
|
12
|
+
limit?: {
|
|
13
|
+
context?: number;
|
|
14
|
+
output?: number;
|
|
15
|
+
};
|
|
12
16
|
};
|
|
13
17
|
export type ReasoningEffortCatalog = Record<string, ReasoningEffortLevels>;
|
|
14
18
|
export declare function normalizeBaseURL(value: string): string;
|
package/dist/catalog.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG,MAAM,CACvC,MAAM,EACN;IACE,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC/B,CACF,CAAA;
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG,MAAM,CACvC,MAAM,EACN;IACE,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC/B,CACF,CAAA;AAID,MAAM,MAAM,qBAAqB,GAAG;IAClC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,KAAK,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAC9C,CAAA;AAGD,MAAM,MAAM,sBAAsB,GAAG,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAA;AAW1E,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,UAK7C;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,EAAE,CAuB3D;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,sBAAsB,CAiClF;AAED,wBAAsB,wBAAwB,CAAC,KAAK,EAAE;IACpD,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,KAAK,CAAA;CACvB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAiBlC;AAED,wBAAsB,cAAc,CAAC,KAAK,EAAE;IAC1C,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,KAAK,CAAA;CACvB,2BAcA;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,oBAAoB,CAsB9E;AAED,wBAAsB,sBAAsB,CAAC,KAAK,EAAE;IAClD,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,KAAK,CAAA;CACvB,iCAaA"}
|
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,EAAgB,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAU,MAAM,EAAgB,MAAM,qBAAqB,CAAA;AA6BvE,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
|
@@ -38,15 +38,19 @@ function parseReasoningEffortCatalog(input) {
|
|
|
38
38
|
if (!isRecord(item) || typeof item.slug !== "string" || item.slug.trim() === "")
|
|
39
39
|
continue;
|
|
40
40
|
const rawLevels = item.supported_reasoning_levels;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
const levels = Array.isArray(rawLevels) ? rawLevels.map((entry) => isRecord(entry) && typeof entry.effort === "string" ? entry.effort : undefined).filter((level) => level !== undefined) : undefined;
|
|
42
|
+
const limit = {};
|
|
43
|
+
const context = [item.context_window, item.max_context_window].find(isPositiveNumber);
|
|
44
|
+
const output = isPositiveNumber(item.max_tokens) ? item.max_tokens : undefined;
|
|
45
|
+
if (context !== undefined)
|
|
46
|
+
limit.context = context;
|
|
47
|
+
if (output !== undefined)
|
|
48
|
+
limit.output = output;
|
|
49
|
+
const defaultLevel = levels && typeof item.default_reasoning_level === "string" ? item.default_reasoning_level : undefined;
|
|
47
50
|
catalog[item.slug] = {
|
|
48
|
-
levels: [...new Set(levels)],
|
|
49
|
-
...defaultLevel ? { defaultLevel } : {}
|
|
51
|
+
...levels ? { levels: [...new Set(levels)] } : {},
|
|
52
|
+
...defaultLevel ? { defaultLevel } : {},
|
|
53
|
+
...Object.keys(limit).length > 0 ? { limit } : {}
|
|
50
54
|
};
|
|
51
55
|
}
|
|
52
56
|
return catalog;
|
|
@@ -99,6 +103,9 @@ async function discoverModelProtocols(input) {
|
|
|
99
103
|
}
|
|
100
104
|
return parseModelProtocolCatalog(await response.json());
|
|
101
105
|
}
|
|
106
|
+
function isPositiveNumber(value) {
|
|
107
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
108
|
+
}
|
|
102
109
|
function isRecord(value) {
|
|
103
110
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
104
111
|
}
|
|
@@ -109,6 +116,8 @@ var DEFAULT_PROVIDER_ID = "cliproxyapi";
|
|
|
109
116
|
var DEFAULT_PROVIDER_NAME = "CLIProxyAPI";
|
|
110
117
|
var DEFAULT_DISCOVERY_TIMEOUT_MS = 1e4;
|
|
111
118
|
var DEFAULT_MODEL_METADATA_URL = "https://models.dev/api.json";
|
|
119
|
+
var DEFAULT_CONTEXT_LIMIT = 128000;
|
|
120
|
+
var DEFAULT_OUTPUT_LIMIT = 32000;
|
|
112
121
|
var V1_CHAT_PACKAGE = "@ai-sdk/openai-compatible";
|
|
113
122
|
var V1_RESPONSES_PACKAGE = "@ai-sdk/openai";
|
|
114
123
|
var V1_ANTHROPIC_PACKAGE = "@ai-sdk/anthropic";
|
|
@@ -131,6 +140,35 @@ function readOptions(input) {
|
|
|
131
140
|
function stringOption(value) {
|
|
132
141
|
return typeof value === "string" && value.trim() !== "" ? value : undefined;
|
|
133
142
|
}
|
|
143
|
+
function resolveModelLimits(model, effortCatalog) {
|
|
144
|
+
const limit = effortCatalog[model.id]?.limit;
|
|
145
|
+
return {
|
|
146
|
+
context: limit?.context ?? DEFAULT_CONTEXT_LIMIT,
|
|
147
|
+
output: limit?.output ?? DEFAULT_OUTPUT_LIMIT
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function isProperString(value) {
|
|
151
|
+
return typeof value === "string" && value.trim() !== "";
|
|
152
|
+
}
|
|
153
|
+
function isProperBoolean(value) {
|
|
154
|
+
return typeof value === "boolean";
|
|
155
|
+
}
|
|
156
|
+
function isProperContextLimit(value) {
|
|
157
|
+
return Number.isInteger(value) && value > 0;
|
|
158
|
+
}
|
|
159
|
+
function isProperOutputLimit(value) {
|
|
160
|
+
return Number.isInteger(value) && value > 0;
|
|
161
|
+
}
|
|
162
|
+
function properLimitOverrides(userLimit) {
|
|
163
|
+
const input = typeof userLimit === "object" && userLimit !== null ? userLimit : {};
|
|
164
|
+
return {
|
|
165
|
+
...isProperContextLimit(input.context) ? { context: input.context } : {},
|
|
166
|
+
...isProperOutputLimit(input.output) ? { output: input.output } : {}
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function definedKeys(value) {
|
|
170
|
+
return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined));
|
|
171
|
+
}
|
|
134
172
|
function displayName(modelID) {
|
|
135
173
|
return modelID.split("-").map((part) => {
|
|
136
174
|
const lower = part.toLowerCase();
|
|
@@ -147,12 +185,19 @@ function isImageModel(modelID) {
|
|
|
147
185
|
return /(?:^|-)image(?:-|$)/i.test(modelID);
|
|
148
186
|
}
|
|
149
187
|
function isReasoningModel(modelID, efforts) {
|
|
150
|
-
if (efforts?.levels
|
|
188
|
+
if (efforts?.levels?.length)
|
|
151
189
|
return efforts.levels.some((level) => level !== "none");
|
|
152
190
|
return /thinking|reasoning|codex|gpt-(?:5|oss)/i.test(modelID);
|
|
153
191
|
}
|
|
154
192
|
function supportsAttachments(modelID) {
|
|
155
|
-
return /^(?:claude|gemini|gpt)/i.test(modelID);
|
|
193
|
+
return /^(?:claude|gemini|gpt)/i.test(modelID) && !/^gpt-oss(?:-|$)/i.test(modelID);
|
|
194
|
+
}
|
|
195
|
+
function modelModalities(modelID) {
|
|
196
|
+
const imageOutput = isImageModel(modelID);
|
|
197
|
+
return {
|
|
198
|
+
input: imageOutput || supportsAttachments(modelID) ? ["text", "image"] : ["text"],
|
|
199
|
+
output: imageOutput ? ["image"] : ["text"]
|
|
200
|
+
};
|
|
156
201
|
}
|
|
157
202
|
function resolveModelNpm(catalog, providerID, modelID) {
|
|
158
203
|
const provider = catalog[providerID];
|
|
@@ -167,7 +212,7 @@ function supportedEffortLevels(model, input) {
|
|
|
167
212
|
return;
|
|
168
213
|
if (isAnthropicRouted(model, input.protocolCatalog))
|
|
169
214
|
return;
|
|
170
|
-
const levels = efforts.levels
|
|
215
|
+
const levels = efforts.levels?.filter((level) => level !== "none") ?? [];
|
|
171
216
|
return levels.length > 0 ? levels : undefined;
|
|
172
217
|
}
|
|
173
218
|
async function discoverCatalog(input) {
|
|
@@ -202,10 +247,9 @@ function discoverEffortLevels(input) {
|
|
|
202
247
|
}
|
|
203
248
|
|
|
204
249
|
// src/plugin-v2.ts
|
|
205
|
-
var DEFAULT_CONTEXT_LIMIT = 128000;
|
|
206
|
-
var DEFAULT_OUTPUT_LIMIT = 4096;
|
|
207
250
|
function toV2Model(model, input) {
|
|
208
251
|
const image = isImageModel(model.id);
|
|
252
|
+
const modalities = modelModalities(model.id);
|
|
209
253
|
const levels = supportedEffortLevels(model, input.discovery);
|
|
210
254
|
const anthropic = isAnthropicRouted(model, input.discovery.protocolCatalog);
|
|
211
255
|
return {
|
|
@@ -216,8 +260,8 @@ function toV2Model(model, input) {
|
|
|
216
260
|
...anthropic ? { package: V2_ANTHROPIC_PACKAGE } : {},
|
|
217
261
|
capabilities: {
|
|
218
262
|
tools: !image,
|
|
219
|
-
input:
|
|
220
|
-
output:
|
|
263
|
+
input: modalities.input,
|
|
264
|
+
output: modalities.output
|
|
221
265
|
},
|
|
222
266
|
variants: (levels ?? []).map((level) => ({
|
|
223
267
|
id: level,
|
|
@@ -227,12 +271,39 @@ function toV2Model(model, input) {
|
|
|
227
271
|
cost: [{ input: 0, output: 0, cache: { read: 0, write: 0 } }],
|
|
228
272
|
status: "active",
|
|
229
273
|
enabled: true,
|
|
230
|
-
limit:
|
|
274
|
+
limit: resolveModelLimits(model, input.discovery.effortCatalog)
|
|
231
275
|
};
|
|
232
276
|
}
|
|
233
277
|
function toV2Models(input) {
|
|
234
278
|
return input.discovery.catalog.map((model) => toV2Model(model, { providerID: input.providerID, discovery: input.discovery }));
|
|
235
279
|
}
|
|
280
|
+
function mergeV2Models(discovered, userModels) {
|
|
281
|
+
const merged = discovered.map((base) => {
|
|
282
|
+
const userModel = userModels?.get(base.id);
|
|
283
|
+
return isRecord2(userModel) ? mergeV2Model(base, userModel) : base;
|
|
284
|
+
});
|
|
285
|
+
if (userModels) {
|
|
286
|
+
const discoveredIDs = new Set(discovered.map((model) => model.id));
|
|
287
|
+
for (const [id, userModel] of userModels) {
|
|
288
|
+
if (!discoveredIDs.has(id) && isRecord2(userModel))
|
|
289
|
+
merged.push(userModel);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return merged;
|
|
293
|
+
}
|
|
294
|
+
function mergeV2Model(base, userModel) {
|
|
295
|
+
return {
|
|
296
|
+
...base,
|
|
297
|
+
...definedKeys(userModel),
|
|
298
|
+
name: isProperString(userModel.name) ? userModel.name : base.name,
|
|
299
|
+
package: isProperString(userModel.package) ? userModel.package : base.package,
|
|
300
|
+
enabled: isProperBoolean(userModel.enabled) ? userModel.enabled : base.enabled,
|
|
301
|
+
limit: { ...base.limit, ...properLimitOverrides(userModel.limit) }
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
function isRecord2(value) {
|
|
305
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
306
|
+
}
|
|
236
307
|
async function setup(ctx) {
|
|
237
308
|
const options = readOptions(ctx.options);
|
|
238
309
|
const providerID = options.providerID ?? DEFAULT_PROVIDER_ID;
|
|
@@ -244,13 +315,14 @@ async function setup(ctx) {
|
|
|
244
315
|
for (const error of discovery.errors) {
|
|
245
316
|
console.warn(`[cliproxyapi] ${error}`);
|
|
246
317
|
}
|
|
247
|
-
const
|
|
318
|
+
const discovered = toV2Models({ providerID, discovery });
|
|
248
319
|
const providerPackage = options.protocol === "responses" ? V2_RESPONSES_PACKAGE : V2_CHAT_PACKAGE;
|
|
249
320
|
await ctx.provider.transform((editor) => {
|
|
250
321
|
const settings = { baseURL };
|
|
251
322
|
if (apiKey)
|
|
252
323
|
settings.apiKey = apiKey;
|
|
253
324
|
const existing = editor.get(providerID);
|
|
325
|
+
const models = mergeV2Models(discovered, existing?.models);
|
|
254
326
|
if (existing) {
|
|
255
327
|
editor.update(providerID, (provider) => {
|
|
256
328
|
provider.name = provider.name || options.providerName || DEFAULT_PROVIDER_NAME;
|
|
@@ -343,17 +415,13 @@ function addProvider(config, input) {
|
|
|
343
415
|
tool_call: !isImageModel(model.id),
|
|
344
416
|
reasoning: isReasoningModel(model.id, input.effortCatalog[model.id]),
|
|
345
417
|
attachment: supportsAttachments(model.id),
|
|
418
|
+
limit: resolveModelLimits(model, input.effortCatalog),
|
|
346
419
|
...model.ownedBy && resolveModelNpm(input.protocolCatalog, model.ownedBy, model.id) === V1_ANTHROPIC_PACKAGE ? {
|
|
347
420
|
provider: {
|
|
348
421
|
npm: V1_ANTHROPIC_PACKAGE
|
|
349
422
|
}
|
|
350
423
|
} : {},
|
|
351
|
-
...
|
|
352
|
-
modalities: {
|
|
353
|
-
input: ["text", "image"],
|
|
354
|
-
output: ["image"]
|
|
355
|
-
}
|
|
356
|
-
} : {},
|
|
424
|
+
...imageModalities(model.id),
|
|
357
425
|
...reasoningEffortConfig(model, input)
|
|
358
426
|
}
|
|
359
427
|
]));
|
|
@@ -373,6 +441,12 @@ function addProvider(config, input) {
|
|
|
373
441
|
}
|
|
374
442
|
};
|
|
375
443
|
}
|
|
444
|
+
function imageModalities(modelID) {
|
|
445
|
+
const modalities = modelModalities(modelID);
|
|
446
|
+
if (!modalities.input.includes("image") && !modalities.output.includes("image"))
|
|
447
|
+
return {};
|
|
448
|
+
return { modalities };
|
|
449
|
+
}
|
|
376
450
|
function reasoningEffortConfig(model, input) {
|
|
377
451
|
const efforts = input.effortCatalog[model.id];
|
|
378
452
|
if (!efforts)
|
|
@@ -380,12 +454,12 @@ function reasoningEffortConfig(model, input) {
|
|
|
380
454
|
const levels = supportedEffortLevels(model, input);
|
|
381
455
|
if (!levels)
|
|
382
456
|
return {};
|
|
383
|
-
const defaultLevel = efforts.defaultLevel !== undefined && efforts.levels
|
|
457
|
+
const defaultLevel = efforts.defaultLevel !== undefined && efforts.levels?.includes(efforts.defaultLevel) ? efforts.defaultLevel : undefined;
|
|
384
458
|
return {
|
|
385
459
|
...defaultLevel ? { options: { reasoningEffort: defaultLevel } } : {},
|
|
386
460
|
variants: {
|
|
387
|
-
...Object.fromEntries(efforts.levels.map((level) => [level, { reasoningEffort: level }])),
|
|
388
|
-
...disabledVariants(efforts.levels)
|
|
461
|
+
...Object.fromEntries((efforts.levels ?? []).map((level) => [level, { reasoningEffort: level }])),
|
|
462
|
+
...disabledVariants(efforts.levels ?? [])
|
|
389
463
|
}
|
|
390
464
|
};
|
|
391
465
|
}
|
|
@@ -399,25 +473,50 @@ function disabledVariants(supported) {
|
|
|
399
473
|
}
|
|
400
474
|
function mergeModels(discovered, existing) {
|
|
401
475
|
const merged = { ...discovered };
|
|
402
|
-
for (const [modelID,
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
476
|
+
for (const [modelID, userModel] of Object.entries(existing ?? {})) {
|
|
477
|
+
const base = merged[modelID];
|
|
478
|
+
if (!base) {
|
|
479
|
+
if (isRecord3(userModel))
|
|
480
|
+
merged[modelID] = { ...userModel };
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
if (isRecord3(userModel))
|
|
484
|
+
merged[modelID] = mergeDiscoveredModel(base, userModel);
|
|
410
485
|
}
|
|
411
486
|
return merged;
|
|
412
487
|
}
|
|
488
|
+
function mergeDiscoveredModel(base, userModel) {
|
|
489
|
+
const limits = { ...base.limit, ...properLimitOverrides(userModel.limit) };
|
|
490
|
+
const merged = {
|
|
491
|
+
...base,
|
|
492
|
+
...definedKeys(userModel),
|
|
493
|
+
name: isProperString(userModel.name) ? userModel.name : base.name,
|
|
494
|
+
tool_call: isProperBoolean(userModel.tool_call) ? userModel.tool_call : base.tool_call,
|
|
495
|
+
reasoning: isProperBoolean(userModel.reasoning) ? userModel.reasoning : base.reasoning,
|
|
496
|
+
attachment: isProperBoolean(userModel.attachment) ? userModel.attachment : base.attachment,
|
|
497
|
+
limit: limits
|
|
498
|
+
};
|
|
499
|
+
const userProvider = userModel.provider;
|
|
500
|
+
if (typeof userProvider === "object" && userProvider !== null) {
|
|
501
|
+
const { npm, ...rest } = userProvider;
|
|
502
|
+
const providerNpm = isProperString(npm) ? npm : base.provider?.npm;
|
|
503
|
+
const provider = providerNpm ? { npm: providerNpm, ...rest } : rest;
|
|
504
|
+
if (Object.keys(provider ?? {}).length > 0)
|
|
505
|
+
merged.provider = provider;
|
|
506
|
+
}
|
|
507
|
+
return merged;
|
|
508
|
+
}
|
|
509
|
+
function isRecord3(value) {
|
|
510
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
511
|
+
}
|
|
413
512
|
export {
|
|
414
|
-
|
|
415
|
-
parseModelProtocolCatalog,
|
|
416
|
-
parseCatalog,
|
|
417
|
-
normalizeBaseURL,
|
|
418
|
-
discoverReasoningEfforts,
|
|
419
|
-
discoverModels,
|
|
420
|
-
discoverModelProtocols,
|
|
513
|
+
CLIProxyAPIPlugin,
|
|
421
514
|
src_default as default,
|
|
422
|
-
|
|
515
|
+
discoverModelProtocols,
|
|
516
|
+
discoverModels,
|
|
517
|
+
discoverReasoningEfforts,
|
|
518
|
+
normalizeBaseURL,
|
|
519
|
+
parseCatalog,
|
|
520
|
+
parseModelProtocolCatalog,
|
|
521
|
+
parseReasoningEffortCatalog
|
|
423
522
|
};
|
package/dist/plugin-v2.d.ts
CHANGED
|
@@ -55,6 +55,12 @@ export declare function toV2Models(input: {
|
|
|
55
55
|
providerID: string;
|
|
56
56
|
discovery: CatalogDiscovery;
|
|
57
57
|
}): V2ModelInfo[];
|
|
58
|
+
/**
|
|
59
|
+
* Overlays the user's configured models on the discovered list. Discovery
|
|
60
|
+
* provides the defaults; a user value wins only where it is a value V2 can
|
|
61
|
+
* use, and a model the user configured alone is preserved verbatim.
|
|
62
|
+
*/
|
|
63
|
+
export declare function mergeV2Models(discovered: V2ModelInfo[], userModels?: ReadonlyMap<string, unknown>): V2ModelInfo[];
|
|
58
64
|
/**
|
|
59
65
|
* V2 setup. Registers the provider from the discovered catalog and returns a
|
|
60
66
|
* no-op cleanup (V2 keeps no timers or subscriptions for this plugin).
|
package/dist/plugin-v2.d.ts.map
CHANGED
|
@@ -1 +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,
|
|
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,EAqBL,KAAK,gBAAgB,EAEtB,MAAM,aAAa,CAAA;AAEpB,KAAK,OAAO,GAAG,MAAM,CAAC,OAAO,CAAA;AAE7B,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;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,WAAW,EAAE,EACzB,UAAU,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,GACxC,WAAW,EAAE,CAgBf;AAkBD;;;GAGG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CA+CtE;AAED,eAAO,MAAM,YAAY;;;CAGxB,CAAA"}
|
package/dist/shared.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { PluginOptions } from "@opencode-ai/plugin";
|
|
2
|
-
import { type CatalogModel, type ModelProtocolCatalog, type ReasoningEffortCatalog } from "./catalog.js";
|
|
2
|
+
import { type CatalogModel, type ModelProtocolCatalog, type ReasoningEffortCatalog, type ReasoningEffortLevels } from "./catalog.js";
|
|
3
3
|
export declare const DEFAULT_BASE_URL = "http://localhost:8317/v1";
|
|
4
4
|
export declare const DEFAULT_PROVIDER_ID = "cliproxyapi";
|
|
5
5
|
export declare const DEFAULT_PROVIDER_NAME = "CLIProxyAPI";
|
|
6
6
|
export declare const DEFAULT_DISCOVERY_TIMEOUT_MS = 10000;
|
|
7
7
|
export declare const DEFAULT_MODEL_METADATA_URL = "https://models.dev/api.json";
|
|
8
|
+
export declare const DEFAULT_CONTEXT_LIMIT = 128000;
|
|
9
|
+
export declare const DEFAULT_OUTPUT_LIMIT = 32000;
|
|
8
10
|
export declare const V1_CHAT_PACKAGE = "@ai-sdk/openai-compatible";
|
|
9
11
|
export declare const V1_RESPONSES_PACKAGE = "@ai-sdk/openai";
|
|
10
12
|
export declare const V1_ANTHROPIC_PACKAGE = "@ai-sdk/anthropic";
|
|
@@ -28,13 +30,28 @@ export type CatalogDiscovery = {
|
|
|
28
30
|
};
|
|
29
31
|
export declare function readOptions(input?: PluginOptions): ConnectorOptions;
|
|
30
32
|
export declare function stringOption(value: unknown): string | undefined;
|
|
33
|
+
export declare function resolveModelLimits(model: CatalogModel, effortCatalog: ReasoningEffortCatalog): {
|
|
34
|
+
context: number;
|
|
35
|
+
output: number;
|
|
36
|
+
};
|
|
37
|
+
export declare function isProperString(value: unknown): value is string;
|
|
38
|
+
export declare function isProperBoolean(value: unknown): value is boolean;
|
|
39
|
+
export declare function isProperContextLimit(value: unknown): value is number;
|
|
40
|
+
export declare function isProperOutputLimit(value: unknown): value is number;
|
|
41
|
+
export declare function properLimitOverrides(userLimit: unknown): {
|
|
42
|
+
context?: number;
|
|
43
|
+
output?: number;
|
|
44
|
+
};
|
|
45
|
+
export declare function definedKeys<T extends object>(value: T): Partial<T>;
|
|
31
46
|
export declare function displayName(modelID: string): string;
|
|
32
47
|
export declare function isImageModel(modelID: string): boolean;
|
|
33
|
-
export declare function isReasoningModel(modelID: string, efforts?:
|
|
34
|
-
levels: string[];
|
|
35
|
-
defaultLevel?: string;
|
|
36
|
-
}): boolean;
|
|
48
|
+
export declare function isReasoningModel(modelID: string, efforts?: ReasoningEffortLevels): boolean;
|
|
37
49
|
export declare function supportsAttachments(modelID: string): boolean;
|
|
50
|
+
export type ModelModalities = {
|
|
51
|
+
input: Array<"text" | "image">;
|
|
52
|
+
output: Array<"text" | "image">;
|
|
53
|
+
};
|
|
54
|
+
export declare function modelModalities(modelID: string): ModelModalities;
|
|
38
55
|
export declare function resolveModelNpm(catalog: ModelProtocolCatalog, providerID: string, modelID: string): string | undefined;
|
|
39
56
|
export declare function isAnthropicRouted(model: CatalogModel, protocolCatalog: ModelProtocolCatalog): boolean;
|
|
40
57
|
export declare function supportedEffortLevels(model: CatalogModel, input: {
|
package/dist/shared.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../src/shared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAIL,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,
|
|
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.4.0",
|
|
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",
|