opencode-blablador 0.0.0-stage → 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/LICENSE +21 -0
- package/README.md +135 -2
- package/dist/catalog.d.ts +20 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +24 -0
- package/dist/catalog.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +160 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 the opencode-blablador authors
|
|
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
CHANGED
|
@@ -1,3 +1,136 @@
|
|
|
1
|
-
#
|
|
1
|
+
# opencode-blablador
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Blablador provider for [OpenCode](https://opencode.ai) v2. It registers the Helmholtz / JSC
|
|
4
|
+
Blablador gateway with the models, context ceilings, and reasoning levels the service actually
|
|
5
|
+
supports.
|
|
6
|
+
|
|
7
|
+
- **All chat aliases**, with a verified 21-model fallback catalog and live `/v1/models` discovery.
|
|
8
|
+
- **Measured context windows**: 1,048,576 / 262,144 / 131,072 tokens instead of a blanket 98k cap.
|
|
9
|
+
- **16,384-token output ceiling**, matching the gateway limit that otherwise causes HTTP 400s.
|
|
10
|
+
- **Per-family reasoning variants** that translate OpenCode's levels into each backend's
|
|
11
|
+
`reasoning_effort` vocabulary.
|
|
12
|
+
- **Env or `/connect` authentication**, with no plaintext token in the config file.
|
|
13
|
+
- Uses OpenCode's native `@opencode/ai/providers/openai-compatible` driver for streaming and tools.
|
|
14
|
+
|
|
15
|
+
## Requirements
|
|
16
|
+
|
|
17
|
+
- OpenCode v2 with `@opencode/plugin` 2.1.4 or newer.
|
|
18
|
+
- Node.js 22 or newer.
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
opencode plugin add opencode-blablador
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Or add it to `opencode.jsonc`:
|
|
27
|
+
|
|
28
|
+
```jsonc
|
|
29
|
+
{
|
|
30
|
+
"plugins": ["opencode-blablador"]
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Authenticate
|
|
35
|
+
|
|
36
|
+
Either store a token through OpenCode:
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
/connect -> Blablador -> API key
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Or export it before starting OpenCode:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
export BLABLADOR_API_TOKEN="your-jsc-blablador-token"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The provider is `auto`-activated: it appears once OpenCode can see an env or `/connect`
|
|
49
|
+
connection. Discovery and requests use the stored `/connect` key when one exists and the env var
|
|
50
|
+
otherwise; keys are trimmed, because a pasted token with surrounding whitespace is what the gateway
|
|
51
|
+
401s on. On first load the plugin queries `/v1/models` with that credential. If discovery fails or
|
|
52
|
+
returns fewer than five chat models, the bundled catalog is used and a warning is logged. Switching
|
|
53
|
+
the credential with `/connect` re-runs discovery without a restart.
|
|
54
|
+
|
|
55
|
+
## Use
|
|
56
|
+
|
|
57
|
+
Select **Blablador** in the model picker. Models that reason expose variants such as `low`,
|
|
58
|
+
`medium`, `high`, and `xhigh`. Picking no variant leaves the field unset, so the backend default
|
|
59
|
+
applies.
|
|
60
|
+
|
|
61
|
+
The output cap is 16,384 tokens, including reasoning tokens.
|
|
62
|
+
|
|
63
|
+
## Context ceilings
|
|
64
|
+
|
|
65
|
+
These numbers are the ceilings the gateway actually enforces.
|
|
66
|
+
|
|
67
|
+
| Context | Models |
|
|
68
|
+
| --- | --- |
|
|
69
|
+
| **1,048,576** | `alias-huge`, `alias-kimi-k3-1m`, `alias-glm-5.3-flash`, `alias-deepseek-v41-flash-booster`, `alias-mimo-v2.6-flash`, `alias-mimo-v2.6-pro`, `alias-muse-spark`, `deepseek-v4.1-flash-at-hzdr` |
|
|
70
|
+
| **262,144** | `alias-code`, `alias-large`, `alias-qwen3.8-27b`, `alias-qwen3.8-flash-next`, `alias-qwen38-27b`, `alias-qwen38-quick`, `alias-aleph-kolibri-1`, `alias-apertus`, `alias-apertus-70b`, `alias-ocr`, `Swift-Qwen3.8-27b` |
|
|
71
|
+
| **131,072** | `alias-fast`, `alias-eve` |
|
|
72
|
+
|
|
73
|
+
## Reasoning levels
|
|
74
|
+
|
|
75
|
+
Blablador backends accept different `reasoning_effort` values. Sending a value a backend does not
|
|
76
|
+
know returns HTTP 400, so the plugin maps OpenCode's levels per family:
|
|
77
|
+
|
|
78
|
+
| Family | Models | Backend vocabulary |
|
|
79
|
+
| --- | --- | --- |
|
|
80
|
+
| Qwen | `alias-code`, `alias-large`, `alias-qwen*`, `Swift-Qwen3.8-27b` | `none`, `low`, `medium`, `xhigh` |
|
|
81
|
+
| gpt-oss | `alias-fast` | `low`, `medium`, `high` |
|
|
82
|
+
| Kimi | `alias-kimi-k3-1m` | `none`, `low`, `high`, `max` |
|
|
83
|
+
| DeepSeek | `alias-deepseek-*`, `deepseek-v4.1-flash-at-hzdr` | `none`, `low`, `high`, `xhigh`, `max` |
|
|
84
|
+
| Muse | `alias-muse-spark` | `low`, `medium`, `high`, `xhigh` |
|
|
85
|
+
| Generic | unlisted aliases | `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max` |
|
|
86
|
+
| None | Apertus, Eve, OCR | the field is never sent |
|
|
87
|
+
|
|
88
|
+
The mapping preserves intent where definitions differ: `high` becomes `xhigh` on Qwen, while
|
|
89
|
+
`xhigh` and `max` clamp to `high` on gpt-oss.
|
|
90
|
+
|
|
91
|
+
If JSC adds a family with its own vocabulary, add a pattern to `FAMILY` and an entry to `EFFORT`
|
|
92
|
+
in blablador-core, the shared catalog this plugin depends on; until then it uses the generic map.
|
|
93
|
+
|
|
94
|
+
## Live catalog
|
|
95
|
+
|
|
96
|
+
`GET /v1/models` carries no capability metadata and JSC says aliases come and go without notice.
|
|
97
|
+
At startup the plugin:
|
|
98
|
+
|
|
99
|
+
1. Keeps `alias-*` chat models plus the two verified unaliased chat models.
|
|
100
|
+
2. Drops embeddings, rerankers, image/video/voice endpoints, and other non-chat models.
|
|
101
|
+
3. Fills missing roots and context windows from the bundled catalog.
|
|
102
|
+
4. Replaces the fallback list only if at least five chat models remain.
|
|
103
|
+
|
|
104
|
+
Discovery runs at startup and again whenever the Blablador credential changes. The output cap
|
|
105
|
+
shrinks below 16,384 for any model whose live window is smaller than the gateway ceiling, so the
|
|
106
|
+
prompt always keeps room inside the window.
|
|
107
|
+
|
|
108
|
+
## Limitations
|
|
109
|
+
|
|
110
|
+
- The live catalog is fetched at startup, not on every model-picker open.
|
|
111
|
+
- Vision support is a fixed allowlist because `/v1/models` does not report modalities.
|
|
112
|
+
- Blablador returns no prompt-cache accounting, so the plugin does not configure caching.
|
|
113
|
+
- Blablador is a shared research service; rate limits and alias availability are controlled by JSC.
|
|
114
|
+
|
|
115
|
+
## Development
|
|
116
|
+
|
|
117
|
+
The catalog rules (fallback list, family/effort maps, live merge, token trimming) live in
|
|
118
|
+
blablador-core; this package is the OpenCode adapter around them. It lives in a monorepo, so install from the
|
|
119
|
+
repo root — the workspace links the core, no symlinks:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
npm install # at the monorepo root
|
|
123
|
+
npm run typecheck
|
|
124
|
+
npm test # builds dist/ and runs the host tests (catalog rules are tested in the core)
|
|
125
|
+
npm run test:live # loads dist/ in an isolated real OpenCode and checks its catalog against /v1/models,
|
|
126
|
+
# runs one #high completion (the effort word end-to-end) and one red-PNG image via -f;
|
|
127
|
+
# needs BLABLADOR_API_TOKEN and the opencode binary, skips without them
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
To point a local OpenCode at the built plugin, `"plugins": ["./dist"]` in this repo's
|
|
131
|
+
`opencode.jsonc` does it — a path plugin entry must be a **directory** (a `.ts` file path is
|
|
132
|
+
ignored with a warning), and relative paths resolve against the config file's directory.
|
|
133
|
+
|
|
134
|
+
## License
|
|
135
|
+
|
|
136
|
+
MIT
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode adapter over blablador-core.
|
|
3
|
+
*
|
|
4
|
+
* The catalog, family/effort maps, live merge, token normalization and output cap all live in the
|
|
5
|
+
* shared core so pi-blablador and this plugin cannot drift. This file re-exports that surface and
|
|
6
|
+
* adds the only OpenCode-specific piece: turning a model's effort map into `variants`.
|
|
7
|
+
*/
|
|
8
|
+
export { BASE_URL, DEFAULT_CONTEXT, EFFORT, EXTRA_CHAT, FALLBACK_MODELS, MAX_OUTPUT, MIN_CATALOG, NOT_CHAT, TOKEN_ENV, VISION, displayName, familyOf, fetchLiveModels, mergeLiveModels, outputCap, resolveToken, type CatalogModel, type EffortMap, type Level, type RemoteModel, } from "blablador-core";
|
|
9
|
+
import { type CatalogModel, type Level } from "blablador-core";
|
|
10
|
+
export interface ModelVariant {
|
|
11
|
+
readonly id: Level;
|
|
12
|
+
readonly reasoningEffort: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* OpenCode variants for a model. IDs are the user-facing levels; settings carry the value the
|
|
16
|
+
* family's backend accepts, which the native OpenAI-compatible driver lowers to `reasoning_effort`.
|
|
17
|
+
* A family with no reasoning (`EFFORT[family] === null`) advertises no variants.
|
|
18
|
+
*/
|
|
19
|
+
export declare function variantsFor(model: Pick<CatalogModel, "id" | "root">): ModelVariant[];
|
|
20
|
+
//# sourceMappingURL=catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,QAAQ,EACR,eAAe,EACf,MAAM,EACN,UAAU,EACV,eAAe,EACf,UAAU,EACV,WAAW,EACX,QAAQ,EACR,SAAS,EACT,MAAM,EACN,WAAW,EACX,QAAQ,EACR,eAAe,EACf,eAAe,EACf,SAAS,EACT,YAAY,EACZ,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,KAAK,EACV,KAAK,WAAW,GACjB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAoB,KAAK,YAAY,EAAE,KAAK,KAAK,EAAkB,MAAM,gBAAgB,CAAC;AAEjG,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,GAAG,MAAM,CAAC,GAAG,YAAY,EAAE,CAOpF"}
|
package/dist/catalog.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode adapter over blablador-core.
|
|
3
|
+
*
|
|
4
|
+
* The catalog, family/effort maps, live merge, token normalization and output cap all live in the
|
|
5
|
+
* shared core so pi-blablador and this plugin cannot drift. This file re-exports that surface and
|
|
6
|
+
* adds the only OpenCode-specific piece: turning a model's effort map into `variants`.
|
|
7
|
+
*/
|
|
8
|
+
export { BASE_URL, DEFAULT_CONTEXT, EFFORT, EXTRA_CHAT, FALLBACK_MODELS, MAX_OUTPUT, MIN_CATALOG, NOT_CHAT, TOKEN_ENV, VISION, displayName, familyOf, fetchLiveModels, mergeLiveModels, outputCap, resolveToken, } from "blablador-core";
|
|
9
|
+
import { EFFORT, familyOf } from "blablador-core";
|
|
10
|
+
/**
|
|
11
|
+
* OpenCode variants for a model. IDs are the user-facing levels; settings carry the value the
|
|
12
|
+
* family's backend accepts, which the native OpenAI-compatible driver lowers to `reasoning_effort`.
|
|
13
|
+
* A family with no reasoning (`EFFORT[family] === null`) advertises no variants.
|
|
14
|
+
*/
|
|
15
|
+
export function variantsFor(model) {
|
|
16
|
+
const effort = EFFORT[familyOf(model.id, model.root)];
|
|
17
|
+
if (!effort)
|
|
18
|
+
return [];
|
|
19
|
+
return Object.entries(effort).map(([id, reasoningEffort]) => ({
|
|
20
|
+
id,
|
|
21
|
+
reasoningEffort,
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog.js","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,QAAQ,EACR,eAAe,EACf,MAAM,EACN,UAAU,EACV,eAAe,EACf,UAAU,EACV,WAAW,EACX,QAAQ,EACR,SAAS,EACT,MAAM,EACN,WAAW,EACX,QAAQ,EACR,eAAe,EACf,eAAe,EACf,SAAS,EACT,YAAY,GAKb,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAiD,MAAM,gBAAgB,CAAC;AAOjG;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,KAAwC;IAClE,MAAM,MAAM,GAAiC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IACpF,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IACvB,OAAQ,MAAM,CAAC,OAAO,CAAC,MAAM,CAA4B,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,eAAe,CAAC,EAAE,EAAE,CAAC,CAAC;QACxF,EAAE;QACF,eAAe;KAChB,CAAC,CAAC,CAAC;AACN,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,MAAM,EAAY,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { Integration, Model, Plugin, Provider } from "@opencode/plugin";
|
|
2
|
+
import { BASE_URL, EFFORT, FALLBACK_MODELS, TOKEN_ENV, VISION, displayName, familyOf, fetchLiveModels, outputCap, resolveToken, variantsFor, } from "./catalog.js";
|
|
3
|
+
const PACKAGE = "@opencode/ai/providers/openai-compatible";
|
|
4
|
+
const PROVIDER_ID = Provider.ID.make("blablador");
|
|
5
|
+
const INTEGRATION_ID = Integration.ID.make("blablador");
|
|
6
|
+
const FETCH_TIMEOUT_MS = 10_000;
|
|
7
|
+
function modelInfo(model, all) {
|
|
8
|
+
const base = Model.Info.default(PROVIDER_ID, Model.ID.make(model.id));
|
|
9
|
+
return {
|
|
10
|
+
...base,
|
|
11
|
+
name: displayName(model, all),
|
|
12
|
+
capabilities: {
|
|
13
|
+
...base.capabilities,
|
|
14
|
+
input: VISION.has(model.id) ? ["text", "image"] : ["text"],
|
|
15
|
+
},
|
|
16
|
+
// outputCap keeps room for the prompt when a live window is smaller than the gateway ceiling.
|
|
17
|
+
limit: { context: model.contextWindow, output: outputCap(model.contextWindow) },
|
|
18
|
+
variants: variantsFor(model).map((variant) => ({
|
|
19
|
+
id: Model.VariantID.make(variant.id),
|
|
20
|
+
settings: { reasoningEffort: variant.reasoningEffort },
|
|
21
|
+
})),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export default Plugin.define({
|
|
25
|
+
id: "opencode-blablador",
|
|
26
|
+
async setup(ctx) {
|
|
27
|
+
// Auth entry points: /connect stores a key, or the integration reads the env var directly.
|
|
28
|
+
await ctx.integration.transform((integrations) => {
|
|
29
|
+
integrations.update(INTEGRATION_ID, (integration) => {
|
|
30
|
+
integration.name = "Blablador";
|
|
31
|
+
});
|
|
32
|
+
integrations.method.update({
|
|
33
|
+
integrationID: INTEGRATION_ID,
|
|
34
|
+
method: { type: "env", names: [TOKEN_ENV] },
|
|
35
|
+
});
|
|
36
|
+
integrations.method.update({
|
|
37
|
+
integrationID: INTEGRATION_ID,
|
|
38
|
+
method: { type: "key", label: "Blablador API token" },
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
// The active catalog. The provider transform reads it on every rebuild, so a refresh plus
|
|
42
|
+
// ctx.provider.reload() republishes models without a restart.
|
|
43
|
+
const catalog = {
|
|
44
|
+
models: FALLBACK_MODELS,
|
|
45
|
+
roots: new Map(FALLBACK_MODELS.map((model) => [model.id, model.root])),
|
|
46
|
+
};
|
|
47
|
+
// Discovery must use the same credential the model resolver uses for inference: the active
|
|
48
|
+
// connection first (a stored /connect key wins over the env var), env as the fallback.
|
|
49
|
+
// resolveToken trims: a pasted key with surrounding whitespace is what the gateway 401s on.
|
|
50
|
+
async function currentToken() {
|
|
51
|
+
const connection = await ctx.integration.connection
|
|
52
|
+
.active(INTEGRATION_ID)
|
|
53
|
+
.catch(() => undefined);
|
|
54
|
+
const credential = connection
|
|
55
|
+
? await ctx.integration.connection.resolve(connection).catch(() => undefined)
|
|
56
|
+
: undefined;
|
|
57
|
+
return resolveToken(credential, process.env[TOKEN_ENV]);
|
|
58
|
+
}
|
|
59
|
+
async function discover() {
|
|
60
|
+
const token = await currentToken();
|
|
61
|
+
if (!token)
|
|
62
|
+
return;
|
|
63
|
+
try {
|
|
64
|
+
const models = await fetchLiveModels(token, AbortSignal.timeout(FETCH_TIMEOUT_MS));
|
|
65
|
+
catalog.models = models;
|
|
66
|
+
catalog.roots = new Map(models.map((model) => [model.id, model.root]));
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
70
|
+
console.warn(`blablador: keeping the ${catalog.models.length}-model catalog; ${message}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
await discover();
|
|
74
|
+
await ctx.provider.transform((providers) => {
|
|
75
|
+
providers.add({
|
|
76
|
+
info: {
|
|
77
|
+
...Provider.Info.empty(PROVIDER_ID),
|
|
78
|
+
name: "Blablador",
|
|
79
|
+
activation: "auto",
|
|
80
|
+
package: PACKAGE,
|
|
81
|
+
settings: { baseURL: BASE_URL },
|
|
82
|
+
integrationID: INTEGRATION_ID,
|
|
83
|
+
},
|
|
84
|
+
models: catalog.models.map((model) => modelInfo(model, catalog.models)),
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
// V2 has no chat.params. These hooks build every request shape, so mapping here covers the
|
|
88
|
+
// agent loop plus compaction, title generation, and synthetic requests.
|
|
89
|
+
const sanitize = (event) => {
|
|
90
|
+
const options = event.options;
|
|
91
|
+
const requested = options.reasoningEffort ?? options.reasoning_effort;
|
|
92
|
+
delete options.reasoningEffort;
|
|
93
|
+
delete options.reasoning_effort;
|
|
94
|
+
if (requested === undefined)
|
|
95
|
+
return;
|
|
96
|
+
const rule = EFFORT[familyOf(event.model.id, catalog.roots.get(event.model.id) ?? null)];
|
|
97
|
+
if (!rule)
|
|
98
|
+
return;
|
|
99
|
+
const mapped = typeof requested === "string" ? rule[requested] : undefined;
|
|
100
|
+
options.reasoningEffort = mapped ?? "low";
|
|
101
|
+
};
|
|
102
|
+
// A stored key with stray whitespace 401s every chat even when discovery is fine, because
|
|
103
|
+
// inference auth is OpenCode's own. Trim it on the wire: the clean-key path only reads a
|
|
104
|
+
// header, and the value is rewritten only when trimming actually changes it.
|
|
105
|
+
const trimAuth = (event) => {
|
|
106
|
+
const value = event.request.headers.get("authorization");
|
|
107
|
+
if (!value)
|
|
108
|
+
return;
|
|
109
|
+
const bearer = /^Bearer (.*)$/.exec(value);
|
|
110
|
+
const raw = bearer?.[1];
|
|
111
|
+
if (raw === undefined)
|
|
112
|
+
return;
|
|
113
|
+
const token = raw.trim();
|
|
114
|
+
if (token === raw)
|
|
115
|
+
return;
|
|
116
|
+
event.request.headers.set("authorization", `Bearer ${token}`);
|
|
117
|
+
};
|
|
118
|
+
const registrations = await Promise.all([
|
|
119
|
+
ctx.session.hook("context", sanitize, { providerID: PROVIDER_ID }),
|
|
120
|
+
ctx.session.hook("compaction", sanitize, { providerID: PROVIDER_ID }),
|
|
121
|
+
ctx.session.hook("generate", sanitize, { providerID: PROVIDER_ID }),
|
|
122
|
+
ctx.session.hook("title", sanitize, { providerID: PROVIDER_ID }),
|
|
123
|
+
ctx.session.hook("http.request", trimAuth, { providerID: PROVIDER_ID }),
|
|
124
|
+
]);
|
|
125
|
+
// /connect or a credential switch changes the key and the reachable aliases: re-discover and
|
|
126
|
+
// reload instead of telling the user to restart. Switched is what `credential create` emits
|
|
127
|
+
// when a new key becomes active; a bad key just keeps the fallback with a warning.
|
|
128
|
+
const watch = new AbortController();
|
|
129
|
+
let refreshing = false;
|
|
130
|
+
void (async () => {
|
|
131
|
+
try {
|
|
132
|
+
for await (const event of ctx.event.subscribe({ signal: watch.signal })) {
|
|
133
|
+
if (event.type !== "credential.switched")
|
|
134
|
+
continue;
|
|
135
|
+
if (event.data.integrationID !== INTEGRATION_ID)
|
|
136
|
+
continue;
|
|
137
|
+
if (refreshing)
|
|
138
|
+
continue;
|
|
139
|
+
refreshing = true;
|
|
140
|
+
try {
|
|
141
|
+
await discover();
|
|
142
|
+
await ctx.provider.reload();
|
|
143
|
+
}
|
|
144
|
+
finally {
|
|
145
|
+
refreshing = false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
if (!watch.signal.aborted)
|
|
151
|
+
console.warn("blablador: credential watch stopped", error);
|
|
152
|
+
}
|
|
153
|
+
})();
|
|
154
|
+
return async () => {
|
|
155
|
+
watch.abort();
|
|
156
|
+
await Promise.all(registrations.map((registration) => registration.dispose()));
|
|
157
|
+
};
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAGxE,OAAO,EACL,QAAQ,EACR,MAAM,EACN,eAAe,EACf,SAAS,EACT,MAAM,EACN,WAAW,EACX,QAAQ,EACR,eAAe,EACf,SAAS,EACT,YAAY,EACZ,WAAW,GAGZ,MAAM,cAAc,CAAC;AAEtB,MAAM,OAAO,GAAG,0CAA0C,CAAC;AAC3D,MAAM,WAAW,GAAG,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AAClD,MAAM,cAAc,GAAG,WAAW,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AACxD,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEhC,SAAS,SAAS,CAAC,KAAmB,EAAE,GAA4B;IAClE,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IACtE,OAAO;QACL,GAAG,IAAI;QACP,IAAI,EAAE,WAAW,CAAC,KAAK,EAAE,GAAG,CAAC;QAC7B,YAAY,EAAE;YACZ,GAAG,IAAI,CAAC,YAAY;YACpB,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;SAC3D;QACD,8FAA8F;QAC9F,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE;QAC/E,QAAQ,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YAC7C,EAAE,EAAE,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACpC,QAAQ,EAAE,EAAE,eAAe,EAAE,OAAO,CAAC,eAAe,EAAE;SACvD,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC;AAED,eAAe,MAAM,CAAC,MAAM,CAAC;IAC3B,EAAE,EAAE,oBAAoB;IACxB,KAAK,CAAC,KAAK,CAAC,GAAG;QACb,2FAA2F;QAC3F,MAAM,GAAG,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,YAAY,EAAE,EAAE;YAC/C,YAAY,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,WAAW,EAAE,EAAE;gBAClD,WAAW,CAAC,IAAI,GAAG,WAAW,CAAC;YACjC,CAAC,CAAC,CAAC;YACH,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC;gBACzB,aAAa,EAAE,cAAc;gBAC7B,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,SAAS,CAAC,EAAE;aAC5C,CAAC,CAAC;YACH,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC;gBACzB,aAAa,EAAE,cAAc;gBAC7B,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,qBAAqB,EAAE;aACtD,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,0FAA0F;QAC1F,8DAA8D;QAC9D,MAAM,OAAO,GAA2E;YACtF,MAAM,EAAE,eAAe;YACvB,KAAK,EAAE,IAAI,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;SACvE,CAAC;QAEF,2FAA2F;QAC3F,uFAAuF;QACvF,4FAA4F;QAC5F,KAAK,UAAU,YAAY;YACzB,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,WAAW,CAAC,UAAU;iBAChD,MAAM,CAAC,cAAc,CAAC;iBACtB,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;YAC1B,MAAM,UAAU,GAAiC,UAAU;gBACzD,CAAC,CAAC,MAAM,GAAG,CAAC,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;gBAC7E,CAAC,CAAC,SAAS,CAAC;YACd,OAAO,YAAY,CACjB,UAAwD,EACxD,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CACvB,CAAC;QACJ,CAAC;QAED,KAAK,UAAU,QAAQ;YACrB,MAAM,KAAK,GAAG,MAAM,YAAY,EAAE,CAAC;YACnC,IAAI,CAAC,KAAK;gBAAE,OAAO;YACnB,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC;gBACnF,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC;gBACxB,OAAO,CAAC,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACzE,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBACvE,OAAO,CAAC,IAAI,CACV,0BAA0B,OAAO,CAAC,MAAM,CAAC,MAAM,mBAAmB,OAAO,EAAE,CAC5E,CAAC;YACJ,CAAC;QACH,CAAC;QAED,MAAM,QAAQ,EAAE,CAAC;QAEjB,MAAM,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,SAAS,EAAE,EAAE;YACzC,SAAS,CAAC,GAAG,CAAC;gBACZ,IAAI,EAAE;oBACJ,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC;oBACnC,IAAI,EAAE,WAAW;oBACjB,UAAU,EAAE,MAAM;oBAClB,OAAO,EAAE,OAAO;oBAChB,QAAQ,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE;oBAC/B,aAAa,EAAE,cAAc;iBAC9B;gBACD,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;aACxE,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,2FAA2F;QAC3F,wEAAwE;QACxE,MAAM,QAAQ,GAAG,CAAC,KAAqB,EAAE,EAAE;YACzC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;YAC9B,MAAM,SAAS,GAAG,OAAO,CAAC,eAAe,IAAI,OAAO,CAAC,gBAAgB,CAAC;YACtE,OAAO,OAAO,CAAC,eAAe,CAAC;YAC/B,OAAO,OAAO,CAAC,gBAAgB,CAAC;YAChC,IAAI,SAAS,KAAK,SAAS;gBAAE,OAAO;YAEpC,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;YACzF,IAAI,CAAC,IAAI;gBAAE,OAAO;YAClB,MAAM,MAAM,GAAG,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAkB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YACpF,OAAO,CAAC,eAAe,GAAG,MAAM,IAAI,KAAK,CAAC;QAC5C,CAAC,CAAC;QAEF,0FAA0F;QAC1F,yFAAyF;QACzF,6EAA6E;QAC7E,MAAM,QAAQ,GAAG,CAAC,KAAyB,EAAE,EAAE;YAC7C,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;YACzD,IAAI,CAAC,KAAK;gBAAE,OAAO;YACnB,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC3C,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;YACxB,IAAI,GAAG,KAAK,SAAS;gBAAE,OAAO;YAC9B,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;YACzB,IAAI,KAAK,KAAK,GAAG;gBAAE,OAAO;YAC1B,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,KAAK,EAAE,CAAC,CAAC;QAChE,CAAC,CAAC;QAEF,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACtC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC;YAClE,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC;YACrE,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC;YACnE,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC;YAChE,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,cAAc,EAAE,QAAQ,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC;SACxE,CAAC,CAAC;QAEH,6FAA6F;QAC7F,4FAA4F;QAC5F,mFAAmF;QACnF,MAAM,KAAK,GAAG,IAAI,eAAe,EAAE,CAAC;QACpC,IAAI,UAAU,GAAG,KAAK,CAAC;QACvB,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,IAAI,CAAC;gBACH,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC;oBACxE,IAAI,KAAK,CAAC,IAAI,KAAK,qBAAqB;wBAAE,SAAS;oBACnD,IAAI,KAAK,CAAC,IAAI,CAAC,aAAa,KAAK,cAAc;wBAAE,SAAS;oBAC1D,IAAI,UAAU;wBAAE,SAAS;oBACzB,UAAU,GAAG,IAAI,CAAC;oBAClB,IAAI,CAAC;wBACH,MAAM,QAAQ,EAAE,CAAC;wBACjB,MAAM,GAAG,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;oBAC9B,CAAC;4BAAS,CAAC;wBACT,UAAU,GAAG,KAAK,CAAC;oBACrB,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO;oBAAE,OAAO,CAAC,IAAI,CAAC,qCAAqC,EAAE,KAAK,CAAC,CAAC;YACxF,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;QAEL,OAAO,KAAK,IAAI,EAAE;YAChB,KAAK,CAAC,KAAK,EAAE,CAAC;YACd,MAAM,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACjF,CAAC,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,49 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-blablador",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Blablador (Helmholtz / JSC) provider plugin for OpenCode: measured context windows, live catalog, and reasoning effort mapping",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"opencode",
|
|
9
|
+
"opencode-plugin",
|
|
10
|
+
"blablador",
|
|
11
|
+
"helmholtz",
|
|
12
|
+
"jsc",
|
|
13
|
+
"openai-compatible"
|
|
14
|
+
],
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"main": "./dist/index.js",
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"import": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=22"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsc -p tsconfig.json",
|
|
33
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
34
|
+
"test": "npm run build && node --test test/*.test.mjs",
|
|
35
|
+
"test:live": "npm run build && node test/live.mjs",
|
|
36
|
+
"prepublishOnly": "npm run typecheck && npm test"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"@opencode/plugin": ">=2.1.4"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"blablador-core": "0.1.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@opencode/plugin": "2.1.4",
|
|
46
|
+
"@types/node": "^24.12.2",
|
|
47
|
+
"typescript": "^7.0.2"
|
|
48
|
+
}
|
|
49
|
+
}
|