opencode-cliproxyapi 0.1.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 +23 -0
- package/LICENSE +21 -0
- package/README.md +194 -0
- package/dist/catalog.d.ts +13 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +176 -0
- package/package.json +55 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
|
6
|
+
and this project follows [Semantic Versioning](https://semver.org/).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-07-25
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Dynamic model discovery from CLIProxyAPI's `/v1/models` endpoint.
|
|
15
|
+
- OpenCode provider configuration for OpenAI-compatible chat completions.
|
|
16
|
+
- Custom server URL and API key support through environment variables or
|
|
17
|
+
plugin options.
|
|
18
|
+
- Automatic model names and capability hints in OpenCode's model picker.
|
|
19
|
+
- Local plugin and npm package installation flows.
|
|
20
|
+
|
|
21
|
+
[Unreleased]: https://github.com/yourcasualdev/opencode-cliproxyapi/compare/v0.1.0...HEAD
|
|
22
|
+
[0.1.0]: https://github.com/yourcasualdev/opencode-cliproxyapi/releases/tag/v0.1.0
|
|
23
|
+
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 İbrahim BABAL
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# OpenCode CLIProxyAPI
|
|
2
|
+
|
|
3
|
+
[](https://github.com/yourcasualdev/opencode-cliproxyapi/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/opencode-cliproxyapi)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
Use every model exposed by [CLIProxyAPI](https://github.com/router-for-me/CLIProxyAPI)
|
|
8
|
+
directly in [OpenCode](https://opencode.ai/).
|
|
9
|
+
|
|
10
|
+
The plugin discovers CLIProxyAPI's live `/v1/models` catalog whenever OpenCode
|
|
11
|
+
starts. Available models appear in the normal `/models` picker under
|
|
12
|
+
**CLIProxyAPI**, with no hard-coded model list to maintain.
|
|
13
|
+
|
|
14
|
+
## Quick start
|
|
15
|
+
|
|
16
|
+
You need OpenCode, a running CLIProxyAPI server, and one of its API keys.
|
|
17
|
+
|
|
18
|
+
### 1. Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
opencode plugin opencode-cliproxyapi --global
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### 2. Set your connection
|
|
25
|
+
|
|
26
|
+
macOS or Linux:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
export CLIPROXY_BASE_URL="http://your-server:8317"
|
|
30
|
+
export CLIPROXY_API_KEY="your-cli-proxy-api-key"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Windows PowerShell:
|
|
34
|
+
|
|
35
|
+
```powershell
|
|
36
|
+
$env:CLIPROXY_BASE_URL = "http://your-server:8317"
|
|
37
|
+
$env:CLIPROXY_API_KEY = "your-cli-proxy-api-key"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The URL may include `/v1`, but it is not required. If the URL is omitted, the
|
|
41
|
+
plugin uses `http://localhost:8317/v1`.
|
|
42
|
+
|
|
43
|
+
These variables must be available to the process that starts OpenCode. Add them
|
|
44
|
+
to your shell profile if you want them set in every new terminal.
|
|
45
|
+
|
|
46
|
+
### 3. Verify
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
opencode models cliproxyapi
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
You should see models reported by your server:
|
|
53
|
+
|
|
54
|
+
```text
|
|
55
|
+
cliproxyapi/claude-sonnet-4-6
|
|
56
|
+
cliproxyapi/gpt-5.6-terra
|
|
57
|
+
cliproxyapi/gemini-3.1-pro-low
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 4. Select a model
|
|
61
|
+
|
|
62
|
+
Start OpenCode and run `/models`:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
opencode
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Choose **CLIProxyAPI**, select a model, and use OpenCode normally. Restart
|
|
69
|
+
OpenCode whenever the model catalog on CLIProxyAPI changes.
|
|
70
|
+
|
|
71
|
+
> This release uses environment variables instead of OpenCode's `/connect`
|
|
72
|
+
> screen. Model selection itself uses the standard `/models` experience.
|
|
73
|
+
|
|
74
|
+
## Configuration
|
|
75
|
+
|
|
76
|
+
The environment variables are the simplest and safest configuration:
|
|
77
|
+
|
|
78
|
+
| Variable | Required | Default |
|
|
79
|
+
| --- | --- | --- |
|
|
80
|
+
| `CLIPROXY_API_KEY` | Yes, when the server requires authentication | None |
|
|
81
|
+
| `CLIPROXY_BASE_URL` | No | `http://localhost:8317/v1` |
|
|
82
|
+
|
|
83
|
+
Plugin options can be set in `opencode.json` when more control is needed:
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"$schema": "https://opencode.ai/config.json",
|
|
88
|
+
"plugin": [
|
|
89
|
+
[
|
|
90
|
+
"opencode-cliproxyapi",
|
|
91
|
+
{
|
|
92
|
+
"baseURL": "http://your-server:8317",
|
|
93
|
+
"providerName": "My CLIProxyAPI"
|
|
94
|
+
}
|
|
95
|
+
]
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Keep the API key in `CLIPROXY_API_KEY`; do not commit it to
|
|
101
|
+
`opencode.json`.
|
|
102
|
+
|
|
103
|
+
| Plugin option | Default | Purpose |
|
|
104
|
+
| --- | --- | --- |
|
|
105
|
+
| `baseURL` | `CLIPROXY_BASE_URL` or `http://localhost:8317/v1` | CLIProxyAPI URL |
|
|
106
|
+
| `apiKey` | `CLIPROXY_API_KEY` | API key; prefer the environment variable |
|
|
107
|
+
| `providerID` | `cliproxyapi` | ID used in `provider/model` names |
|
|
108
|
+
| `providerName` | `CLIProxyAPI` | Name displayed in the model picker |
|
|
109
|
+
| `protocol` | `chat` | `chat` uses `/chat/completions`; `responses` uses `/responses` |
|
|
110
|
+
| `discoveryTimeoutMs` | `10000` | Startup model-discovery timeout |
|
|
111
|
+
|
|
112
|
+
Existing `provider.cliproxyapi` settings are preserved, so individual models
|
|
113
|
+
can be customized:
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"provider": {
|
|
118
|
+
"cliproxyapi": {
|
|
119
|
+
"models": {
|
|
120
|
+
"gpt-5.6-terra": {
|
|
121
|
+
"name": "Terra",
|
|
122
|
+
"limit": {
|
|
123
|
+
"context": 200000,
|
|
124
|
+
"output": 65536
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Troubleshooting
|
|
134
|
+
|
|
135
|
+
### `Missing API key`
|
|
136
|
+
|
|
137
|
+
Set `CLIPROXY_API_KEY` in the same terminal that starts OpenCode:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
export CLIPROXY_API_KEY="your-cli-proxy-api-key"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### No CLIProxyAPI models appear
|
|
144
|
+
|
|
145
|
+
First check the API directly. If your base URL does not include `/v1`:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
curl -H "Authorization: Bearer $CLIPROXY_API_KEY" \
|
|
149
|
+
"${CLIPROXY_BASE_URL%/}/v1/models"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
If it already ends in `/v1`:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
curl -H "Authorization: Bearer $CLIPROXY_API_KEY" \
|
|
156
|
+
"${CLIPROXY_BASE_URL%/}/models"
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Then restart OpenCode and run:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
opencode models cliproxyapi
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### It works in one terminal but not another
|
|
166
|
+
|
|
167
|
+
The second terminal or desktop launcher may not have the environment
|
|
168
|
+
variables. Start OpenCode from the terminal where they are set, or configure
|
|
169
|
+
the launcher to provide them.
|
|
170
|
+
|
|
171
|
+
## Development
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
git clone https://github.com/yourcasualdev/opencode-cliproxyapi.git
|
|
175
|
+
cd opencode-cliproxyapi
|
|
176
|
+
bun install
|
|
177
|
+
bun run check
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
The repository's `opencode.json` loads the local build for integration testing:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
bun run build
|
|
184
|
+
export CLIPROXY_BASE_URL="http://your-server:8317"
|
|
185
|
+
export CLIPROXY_API_KEY="your-cli-proxy-api-key"
|
|
186
|
+
opencode models cliproxyapi
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines and
|
|
190
|
+
[SECURITY.md](SECURITY.md) for private vulnerability reporting.
|
|
191
|
+
|
|
192
|
+
## License
|
|
193
|
+
|
|
194
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type CatalogModel = {
|
|
2
|
+
id: string;
|
|
3
|
+
ownedBy?: string;
|
|
4
|
+
};
|
|
5
|
+
export declare function normalizeBaseURL(value: string): string;
|
|
6
|
+
export declare function parseCatalog(input: unknown): CatalogModel[];
|
|
7
|
+
export declare function discoverModels(input: {
|
|
8
|
+
baseURL: string;
|
|
9
|
+
apiKey?: string;
|
|
10
|
+
timeoutMs: number;
|
|
11
|
+
fetcher?: typeof fetch;
|
|
12
|
+
}): Promise<CatalogModel[]>;
|
|
13
|
+
//# sourceMappingURL=catalog.d.ts.map
|
|
@@ -0,0 +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;AAMD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,UAK7C;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,EAAE,CAuB3D;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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Plugin } from "@opencode-ai/plugin";
|
|
2
|
+
export declare const CLIProxyAPIPlugin: Plugin;
|
|
3
|
+
declare const _default: {
|
|
4
|
+
id: string;
|
|
5
|
+
server: Plugin;
|
|
6
|
+
};
|
|
7
|
+
export default _default;
|
|
8
|
+
export { discoverModels, normalizeBaseURL, parseCatalog } from "./catalog.js";
|
|
9
|
+
export type { CatalogModel } from "./catalog.js";
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAU,MAAM,EAA+B,MAAM,qBAAqB,CAAA;AAoBtF,eAAO,MAAM,iBAAiB,EAAE,MAmD/B,CAAA;;;;;AAED,wBAGwB;AACxB,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAC7E,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/catalog.ts
|
|
3
|
+
function normalizeBaseURL(value) {
|
|
4
|
+
const url = new URL(value);
|
|
5
|
+
const pathname = url.pathname.replace(/\/+$/, "");
|
|
6
|
+
url.pathname = pathname.endsWith("/v1") ? pathname : `${pathname}/v1`;
|
|
7
|
+
return url.toString().replace(/\/$/, "");
|
|
8
|
+
}
|
|
9
|
+
function parseCatalog(input) {
|
|
10
|
+
if (!isRecord(input))
|
|
11
|
+
throw new Error("CLIProxyAPI returned a non-object model catalog");
|
|
12
|
+
const response = input;
|
|
13
|
+
if (!Array.isArray(response.data))
|
|
14
|
+
throw new Error("CLIProxyAPI model catalog is missing the data array");
|
|
15
|
+
const models = response.data.map((item) => {
|
|
16
|
+
if (!isRecord(item) || typeof item.id !== "string" || item.id.trim() === "")
|
|
17
|
+
return;
|
|
18
|
+
return {
|
|
19
|
+
id: item.id,
|
|
20
|
+
...typeof item.owned_by === "string" ? { ownedBy: item.owned_by } : {}
|
|
21
|
+
};
|
|
22
|
+
}).filter((item) => item !== undefined);
|
|
23
|
+
if (models.length === 0)
|
|
24
|
+
throw new Error("CLIProxyAPI returned no usable models");
|
|
25
|
+
const unique = new Map;
|
|
26
|
+
for (const model of models) {
|
|
27
|
+
if (!unique.has(model.id))
|
|
28
|
+
unique.set(model.id, model);
|
|
29
|
+
}
|
|
30
|
+
return [...unique.values()];
|
|
31
|
+
}
|
|
32
|
+
async function discoverModels(input) {
|
|
33
|
+
const response = await (input.fetcher ?? fetch)(`${input.baseURL}/models`, {
|
|
34
|
+
headers: input.apiKey ? { Authorization: `Bearer ${input.apiKey}` } : undefined,
|
|
35
|
+
signal: AbortSignal.timeout(input.timeoutMs)
|
|
36
|
+
});
|
|
37
|
+
if (!response.ok) {
|
|
38
|
+
const detail = (await response.text()).trim().slice(0, 300);
|
|
39
|
+
throw new Error(`CLIProxyAPI model discovery failed with HTTP ${response.status}${detail ? `: ${detail}` : ""}`);
|
|
40
|
+
}
|
|
41
|
+
return parseCatalog(await response.json());
|
|
42
|
+
}
|
|
43
|
+
function isRecord(value) {
|
|
44
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/index.ts
|
|
48
|
+
var DEFAULT_BASE_URL = "http://localhost:8317/v1";
|
|
49
|
+
var DEFAULT_PROVIDER_ID = "cliproxyapi";
|
|
50
|
+
var DEFAULT_PROVIDER_NAME = "CLIProxyAPI";
|
|
51
|
+
var DEFAULT_DISCOVERY_TIMEOUT_MS = 1e4;
|
|
52
|
+
var CLIProxyAPIPlugin = async ({ client }, rawOptions) => {
|
|
53
|
+
const options = readOptions(rawOptions);
|
|
54
|
+
return {
|
|
55
|
+
config: async (config) => {
|
|
56
|
+
const providerID = options.providerID ?? DEFAULT_PROVIDER_ID;
|
|
57
|
+
const existing = config.provider?.[providerID];
|
|
58
|
+
const baseURL = normalizeBaseURL(options.baseURL ?? process.env.CLIPROXY_BASE_URL ?? stringOption(existing?.options?.baseURL) ?? DEFAULT_BASE_URL);
|
|
59
|
+
const apiKey = options.apiKey ?? process.env.CLIPROXY_API_KEY ?? stringOption(existing?.options?.apiKey);
|
|
60
|
+
try {
|
|
61
|
+
const catalog = await discoverModels({
|
|
62
|
+
baseURL,
|
|
63
|
+
apiKey,
|
|
64
|
+
timeoutMs: options.discoveryTimeoutMs ?? DEFAULT_DISCOVERY_TIMEOUT_MS
|
|
65
|
+
});
|
|
66
|
+
addProvider(config, {
|
|
67
|
+
providerID,
|
|
68
|
+
providerName: options.providerName ?? DEFAULT_PROVIDER_NAME,
|
|
69
|
+
baseURL,
|
|
70
|
+
apiKey,
|
|
71
|
+
protocol: options.protocol ?? "chat",
|
|
72
|
+
catalog
|
|
73
|
+
});
|
|
74
|
+
await client.app.log({
|
|
75
|
+
body: {
|
|
76
|
+
service: "opencode-cliproxyapi",
|
|
77
|
+
level: "info",
|
|
78
|
+
message: `Discovered ${catalog.length} CLIProxyAPI models from ${baseURL}`
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
} catch (error) {
|
|
82
|
+
await client.app.log({
|
|
83
|
+
body: {
|
|
84
|
+
service: "opencode-cliproxyapi",
|
|
85
|
+
level: "error",
|
|
86
|
+
message: error instanceof Error ? error.message : String(error)
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
var src_default = {
|
|
95
|
+
id: "opencode-cliproxyapi",
|
|
96
|
+
server: CLIProxyAPIPlugin
|
|
97
|
+
};
|
|
98
|
+
function addProvider(config, input) {
|
|
99
|
+
const existing = config.provider?.[input.providerID];
|
|
100
|
+
const discovered = Object.fromEntries(input.catalog.map((model) => [
|
|
101
|
+
model.id,
|
|
102
|
+
{
|
|
103
|
+
name: displayName(model.id),
|
|
104
|
+
tool_call: !isImageModel(model.id),
|
|
105
|
+
reasoning: isReasoningModel(model.id),
|
|
106
|
+
attachment: supportsAttachments(model.id),
|
|
107
|
+
...isImageModel(model.id) ? {
|
|
108
|
+
modalities: {
|
|
109
|
+
input: ["text", "image"],
|
|
110
|
+
output: ["image"]
|
|
111
|
+
}
|
|
112
|
+
} : {}
|
|
113
|
+
}
|
|
114
|
+
]));
|
|
115
|
+
config.provider = {
|
|
116
|
+
...config.provider,
|
|
117
|
+
[input.providerID]: {
|
|
118
|
+
...existing,
|
|
119
|
+
name: existing?.name ?? input.providerName,
|
|
120
|
+
npm: existing?.npm ?? (input.protocol === "responses" ? "@ai-sdk/openai" : "@ai-sdk/openai-compatible"),
|
|
121
|
+
env: [...new Set([...existing?.env ?? [], "CLIPROXY_API_KEY"])],
|
|
122
|
+
options: {
|
|
123
|
+
...existing?.options,
|
|
124
|
+
baseURL: input.baseURL,
|
|
125
|
+
...input.apiKey ? { apiKey: input.apiKey } : {}
|
|
126
|
+
},
|
|
127
|
+
models: {
|
|
128
|
+
...discovered,
|
|
129
|
+
...existing?.models
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function readOptions(input) {
|
|
135
|
+
if (!input)
|
|
136
|
+
return {};
|
|
137
|
+
return {
|
|
138
|
+
baseURL: stringOption(input.baseURL),
|
|
139
|
+
apiKey: stringOption(input.apiKey),
|
|
140
|
+
providerID: stringOption(input.providerID),
|
|
141
|
+
providerName: stringOption(input.providerName),
|
|
142
|
+
protocol: input.protocol === "responses" ? "responses" : input.protocol === "chat" ? "chat" : undefined,
|
|
143
|
+
discoveryTimeoutMs: typeof input.discoveryTimeoutMs === "number" && input.discoveryTimeoutMs > 0 ? input.discoveryTimeoutMs : undefined
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function stringOption(value) {
|
|
147
|
+
return typeof value === "string" && value.trim() !== "" ? value : undefined;
|
|
148
|
+
}
|
|
149
|
+
function displayName(modelID) {
|
|
150
|
+
return modelID.split("-").map((part) => {
|
|
151
|
+
const lower = part.toLowerCase();
|
|
152
|
+
if (lower === "gpt")
|
|
153
|
+
return "GPT";
|
|
154
|
+
if (lower === "oss")
|
|
155
|
+
return "OSS";
|
|
156
|
+
if (lower === "codex")
|
|
157
|
+
return "Codex";
|
|
158
|
+
return part.charAt(0).toUpperCase() + part.slice(1);
|
|
159
|
+
}).join(" ");
|
|
160
|
+
}
|
|
161
|
+
function isImageModel(modelID) {
|
|
162
|
+
return /(?:^|-)image(?:-|$)/i.test(modelID);
|
|
163
|
+
}
|
|
164
|
+
function isReasoningModel(modelID) {
|
|
165
|
+
return /thinking|reasoning|codex|gpt-(?:5|oss)/i.test(modelID);
|
|
166
|
+
}
|
|
167
|
+
function supportsAttachments(modelID) {
|
|
168
|
+
return /^(?:claude|gemini|gpt)/i.test(modelID);
|
|
169
|
+
}
|
|
170
|
+
export {
|
|
171
|
+
parseCatalog,
|
|
172
|
+
normalizeBaseURL,
|
|
173
|
+
discoverModels,
|
|
174
|
+
src_default as default,
|
|
175
|
+
CLIProxyAPIPlugin
|
|
176
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/package.json",
|
|
3
|
+
"name": "opencode-cliproxyapi",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "Discover and use every model exposed by CLIProxyAPI in OpenCode.",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"author": "İbrahim BABAL",
|
|
9
|
+
"homepage": "https://github.com/yourcasualdev/opencode-cliproxyapi#readme",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/yourcasualdev/opencode-cliproxyapi.git"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/yourcasualdev/opencode-cliproxyapi/issues"
|
|
16
|
+
},
|
|
17
|
+
"main": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"import": "./dist/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"README.md",
|
|
28
|
+
"CHANGELOG.md",
|
|
29
|
+
"LICENSE"
|
|
30
|
+
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "bun build ./src/index.ts --outdir ./dist --target bun --format esm --external @opencode-ai/plugin && tsc --emitDeclarationOnly",
|
|
33
|
+
"typecheck": "tsc --noEmit",
|
|
34
|
+
"test": "bun test ./src",
|
|
35
|
+
"check": "bun run typecheck && bun run test && bun run build",
|
|
36
|
+
"prepublishOnly": "bun run check"
|
|
37
|
+
},
|
|
38
|
+
"keywords": [
|
|
39
|
+
"opencode",
|
|
40
|
+
"cliproxyapi",
|
|
41
|
+
"llm",
|
|
42
|
+
"plugin"
|
|
43
|
+
],
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@opencode-ai/plugin": "1.18.5",
|
|
46
|
+
"@types/bun": "^1.3.5",
|
|
47
|
+
"typescript": "^5.9.3"
|
|
48
|
+
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"bun": ">=1.3.0"
|
|
51
|
+
},
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public"
|
|
54
|
+
}
|
|
55
|
+
}
|