@konduiteu/openclaw 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 schnaq GmbH
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,125 @@
1
+ # konduit for OpenClaw
2
+
3
+ European AI inference through [konduit](https://konduit.eu) as an OpenClaw
4
+ provider, with the organisation's live credit balance and rate limits on the
5
+ provider card.
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ openclaw plugins install clawhub:@konduiteu/openclaw
11
+ openclaw plugins enable konduit
12
+ ```
13
+
14
+ or from npm, which asks you to confirm the source:
15
+
16
+ ```sh
17
+ openclaw plugins install npm:@konduiteu/openclaw
18
+ ```
19
+
20
+ Set `KONDUIT_API_KEY` to a key minted in the [konduit console](https://console.konduit.eu),
21
+ or run `openclaw onboard --konduit-api-key kdt-…`. The key needs the
22
+ `usage:read` scope for the card to show a balance; a key created without any
23
+ scopes is unrestricted and works as it is. A key narrowed to `chat:write` still
24
+ completes, and the card says why it shows nothing.
25
+
26
+ If you had configured konduit by hand under `models.providers.konduit`, keep
27
+ it: OpenClaw merges your entries with the plugin's by model id, and your
28
+ `baseUrl` wins.
29
+
30
+ ## What the card shows
31
+
32
+ - **Available credit** — the organisation's `available_micro_eur`, in EUR.
33
+ Every key of an organisation sees the same wallet; it is the number that
34
+ explains a `402`.
35
+ - **Summary** — an open reservation if there is one, then the rate limits the
36
+ key is actually held to: its own, and its organisation's (or konduit's
37
+ deployment default where the organisation set none). `60 RPM · 100K TPM
38
+ (key) · 1M TPM (organisation)`. A limit that is not configured is left out.
39
+ - Errors name their cause: a rejected key, a key without `usage:read`, a
40
+ rate limit, or konduit's ledger being unavailable.
41
+
42
+ OpenClaw caches a snapshot for a minute and refreshes when you look, so the
43
+ card costs one request a minute against your key's rate limit while it is
44
+ open.
45
+
46
+ ## Models and prices
47
+
48
+ `openclaw.plugin.json` lists every chat deployment konduit still serves, with
49
+ context window, output cap and price, generated from `GET /v1/models`:
50
+
51
+ ```sh
52
+ KONDUIT_API_KEY=kdt-… npm run catalog
53
+ ```
54
+
55
+ That includes deployments konduit marks `deprecated`: the status discourages
56
+ them, it does not switch them off, and a model you already have in your config
57
+ should not vanish from the list because of a label. Only `retired` deployments
58
+ are left out. The default model is never a deprecated one.
59
+
60
+ CI checks the committed list against the live catalog. Live discovery is on
61
+ as well, so a deployment konduit adds appears before the next release — at
62
+ cost zero until the generator has written its price.
63
+
64
+ **One caveat.** OpenClaw prices models in US dollars per million tokens and
65
+ has no currency field. konduit prices in euros; the euro figures are stored
66
+ in those fields, so OpenClaw's local *session cost* shows a dollar sign over
67
+ euro-denominated numbers. The balance on the card is not affected: it comes
68
+ from konduit and is formatted as EUR.
69
+
70
+ The two Hetzner deployments are listed at zero. That is a recorded price, not
71
+ a missing one: Hetzner's Inference API is free of charge while it stays
72
+ experimental.
73
+
74
+ ## The contract
75
+
76
+ The plugin reads `GET /v1/usage`, described in the konduit repository's
77
+ `docs/openapi/gateway.yaml`. The plugin pins the API it was written against,
78
+ not a konduit commit.
79
+
80
+ Model ids are `provider/model` or `provider/model:variant`. The separator used
81
+ to be `@` and the plugin refuses to publish that spelling, because enough of
82
+ the ecosystem reads `@` as an instance selector to truncate an id in transit.
83
+
84
+ ## Develop
85
+
86
+ ```sh
87
+ npm install
88
+ npm test # vitest
89
+ npm run typecheck
90
+ npm run build # dist/, which is what OpenClaw loads
91
+ npm run validate # clawhub package validate
92
+ openclaw plugins install --link . # try it in a local OpenClaw
93
+ ```
94
+
95
+ `src/openclaw-sdk.d.ts` declares the plugin SDK subpaths that `openclaw`
96
+ ships without types. Delete it whole when a release types its own SDK;
97
+ nothing else has to change.
98
+
99
+ ## Publish
100
+
101
+ The first ClawHub publish is manual and creates the package:
102
+
103
+ ```sh
104
+ npm exec clawhub -- login
105
+ npm run validate
106
+ npm exec clawhub -- package publish . --dry-run
107
+ npm exec clawhub -- package publish .
108
+ ```
109
+
110
+ After it, set the trusted publisher once and the dispatch workflow publishes
111
+ with GitHub OIDC and no stored token:
112
+
113
+ ```sh
114
+ npm exec clawhub -- package trusted-publisher set @konduiteu/openclaw \
115
+ --repository schnaq/openclaw-konduit \
116
+ --workflow-filename clawhub-publish.yml
117
+ ```
118
+
119
+ npm publishes on a `v*` tag with provenance. Version, tag, push:
120
+
121
+ ```sh
122
+ npm version minor && git push --follow-tags
123
+ ```
124
+
125
+ MIT.
Binary file
@@ -0,0 +1,20 @@
1
+ // The manifest is the source of truth for models and auth; this module is how
2
+ // the runtime reads it. It is read with createRequire rather than imported:
3
+ // tsc would otherwise pull the root JSON under rootDir and move dist/ around
4
+ // it. import.meta.url is dist/catalog.js in production and src/catalog.ts under
5
+ // vitest; ../openclaw.plugin.json is the package root from both.
6
+ import { createRequire } from "node:module";
7
+ import { buildManifestModelProviderConfig } from "openclaw/plugin-sdk/provider-catalog-shared";
8
+ export const PROVIDER_ID = "konduit";
9
+ export const manifest = createRequire(import.meta.url)("../openclaw.plugin.json");
10
+ const catalog = manifest.modelCatalog.providers.konduit;
11
+ /** Where konduit's OpenAI-compatible API lives; a user's configured baseUrl overrides it. */
12
+ export const KONDUIT_BASE_URL = catalog.baseUrl;
13
+ /** The provider as OpenClaw's catalog wants it. A copy every call; the manifest is never handed out. */
14
+ export function buildKonduitProvider() {
15
+ return {
16
+ baseUrl: KONDUIT_BASE_URL,
17
+ api: "openai-completions",
18
+ models: structuredClone(buildManifestModelProviderConfig({ providerId: PROVIDER_ID, catalog: catalog }).models),
19
+ };
20
+ }
package/dist/index.js ADDED
@@ -0,0 +1,35 @@
1
+ // konduit as an OpenClaw provider: European inference over an OpenAI-compatible
2
+ // API, plus the two hooks that put the organisation's balance and limits on the
3
+ // provider card. Shape follows OpenClaw's bundled extensions/deepseek.
4
+ import { defineSingleProviderPluginEntry, } from "openclaw/plugin-sdk/provider-entry";
5
+ import { buildKonduitProvider, manifest, PROVIDER_ID } from "./catalog.js";
6
+ import { fetchKonduitUsage, resolveBaseUrl } from "./usage.js";
7
+ export default defineSingleProviderPluginEntry({
8
+ id: PROVIDER_ID,
9
+ name: "konduit",
10
+ description: "European AI inference through konduit, with live balance and rate limits.",
11
+ // API-key auth and the model catalog are derived from the manifest, so they
12
+ // are declared once, in openclaw.plugin.json, and the generator keeps them.
13
+ manifest: manifest,
14
+ provider: {
15
+ label: "konduit",
16
+ docsPath: "/providers/konduit",
17
+ catalog: {
18
+ buildProvider: buildKonduitProvider,
19
+ buildStaticProvider: buildKonduitProvider,
20
+ // A user who configured models.providers.konduit.baseUrl keeps it.
21
+ allowExplicitBaseUrl: true,
22
+ // GET {baseUrl}/models: a deployment konduit adds appears without a plugin
23
+ // release, at cost zero until the generator writes its price.
24
+ liveModelDiscovery: true,
25
+ discoveryMode: "strict",
26
+ },
27
+ // Both hooks are required for OpenClaw to poll this provider's usage at
28
+ // all; a plugin with only fetchUsageSnapshot is not auto-discovered.
29
+ resolveUsageAuth: async (ctx) => {
30
+ const token = ctx.resolveApiKeyFromConfigAndStore({ envDirect: [ctx.env.KONDUIT_API_KEY] });
31
+ return token ? { token } : null;
32
+ },
33
+ fetchUsageSnapshot: async (ctx) => await fetchKonduitUsage(resolveBaseUrl(ctx.config), ctx.token, ctx.timeoutMs, ctx.fetchFn),
34
+ },
35
+ });
package/dist/usage.js ADDED
@@ -0,0 +1,108 @@
1
+ // GET /v1/usage → OpenClaw's provider card. The contract is the `Usage` schema
2
+ // in schnaq/konduit's docs/openapi/gateway.yaml; this file is its only reader.
3
+ import { readProviderJsonResponse } from "openclaw/plugin-sdk/provider-http";
4
+ import { buildUsageErrorSnapshot, fetchJson, } from "openclaw/plugin-sdk/provider-usage";
5
+ import { KONDUIT_BASE_URL, PROVIDER_ID } from "./catalog.js";
6
+ export const DISPLAY_NAME = "konduit";
7
+ const MICRO_PER_UNIT = 1_000_000;
8
+ const MALFORMED = "Malformed usage response";
9
+ /**
10
+ * The base URL the usage call goes to. A user who pointed models.providers.konduit
11
+ * at another gateway must have their balance read from that gateway, never from
12
+ * the manifest's default; the manifest is the fallback only.
13
+ */
14
+ export function resolveBaseUrl(config) {
15
+ const configured = config.models?.providers?.[PROVIDER_ID]?.baseUrl?.trim();
16
+ return (configured || KONDUIT_BASE_URL).replace(/\/+$/, "");
17
+ }
18
+ // What a customer reads when the call is refused. The statuses are konduit's
19
+ // documented ones; anything else is shown as its number.
20
+ const REFUSALS = {
21
+ 401: "konduit rejected the API key",
22
+ 403: "the API key lacks the usage:read scope",
23
+ 429: "rate limited by konduit; the card refreshes later",
24
+ 503: "konduit's ledger is unavailable; retry shortly",
25
+ };
26
+ export async function fetchKonduitUsage(baseUrl, token, timeoutMs, fetchFn) {
27
+ const response = await fetchJson(`${baseUrl}/usage`, { method: "GET", headers: { Authorization: `Bearer ${token}`, Accept: "application/json" } }, timeoutMs, fetchFn);
28
+ if (!response.ok) {
29
+ await response.body?.cancel().catch(() => undefined);
30
+ return buildUsageErrorSnapshot(PROVIDER_ID, REFUSALS[response.status] ?? `HTTP ${response.status}`);
31
+ }
32
+ let data;
33
+ try {
34
+ data = await readProviderJsonResponse(response, "konduit usage");
35
+ }
36
+ catch {
37
+ return buildUsageErrorSnapshot(PROVIDER_ID, MALFORMED);
38
+ }
39
+ return snapshotFrom(data);
40
+ }
41
+ /** Maps one answer to the card. Pure, so the mapping is tested without a fetch. */
42
+ export function snapshotFrom(data) {
43
+ if (!isUsageResponse(data)) {
44
+ return buildUsageErrorSnapshot(PROVIDER_ID, MALFORMED);
45
+ }
46
+ const { balance, limits } = data;
47
+ return {
48
+ provider: PROVIDER_ID,
49
+ displayName: DISPLAY_NAME,
50
+ windows: [],
51
+ billing: [
52
+ {
53
+ type: "balance",
54
+ label: "Available credit",
55
+ amount: balance.available_micro_eur / MICRO_PER_UNIT,
56
+ unit: balance.currency,
57
+ },
58
+ ],
59
+ summary: summarise(balance, limits),
60
+ };
61
+ }
62
+ function summarise(balance, limits) {
63
+ const parts = [];
64
+ if (balance.reserved_micro_eur > 0) {
65
+ parts.push(`Reserved ${money(balance.reserved_micro_eur / MICRO_PER_UNIT, balance.currency)}`);
66
+ }
67
+ parts.push(describeLimit(limits.key, "key"), describeLimit(limits.organisation, "organisation"));
68
+ return parts.filter(Boolean).join(" · ") || "no rate limits configured";
69
+ }
70
+ // null is "no ceiling" and is left out; a limit that is there is shown compactly.
71
+ function describeLimit(limit, scope) {
72
+ const parts = [];
73
+ if (limit.requests_per_minute !== null)
74
+ parts.push(`${compact(limit.requests_per_minute)} RPM`);
75
+ if (limit.tokens_per_minute !== null)
76
+ parts.push(`${compact(limit.tokens_per_minute)} TPM`);
77
+ return parts.length > 0 ? `${parts.join(" · ")} (${scope})` : "";
78
+ }
79
+ function compact(value) {
80
+ return new Intl.NumberFormat("en", { notation: "compact", maximumFractionDigits: 1 }).format(value);
81
+ }
82
+ function money(amount, currency) {
83
+ try {
84
+ return new Intl.NumberFormat("en", { style: "currency", currency }).format(amount);
85
+ }
86
+ catch {
87
+ return `${amount.toFixed(2)} ${currency}`;
88
+ }
89
+ }
90
+ function isRecord(value) {
91
+ return typeof value === "object" && value !== null && !Array.isArray(value);
92
+ }
93
+ function isLimit(value) {
94
+ return (isRecord(value) &&
95
+ (value.requests_per_minute === null || typeof value.requests_per_minute === "number") &&
96
+ (value.tokens_per_minute === null || typeof value.tokens_per_minute === "number"));
97
+ }
98
+ function isUsageResponse(value) {
99
+ if (!isRecord(value) || !isRecord(value.balance) || !isRecord(value.limits))
100
+ return false;
101
+ const { balance, limits } = value;
102
+ return (typeof balance.currency === "string" &&
103
+ typeof balance.booked_micro_eur === "number" &&
104
+ typeof balance.reserved_micro_eur === "number" &&
105
+ typeof balance.available_micro_eur === "number" &&
106
+ isLimit(limits.key) &&
107
+ isLimit(limits.organisation));
108
+ }
@@ -0,0 +1,360 @@
1
+ {
2
+ "id": "konduit",
3
+ "name": "konduit",
4
+ "description": "European AI inference through konduit, with live balance and rate limits.",
5
+ "version": "0.1.0",
6
+ "categories": [
7
+ "models"
8
+ ],
9
+ "activation": {
10
+ "onStartup": false
11
+ },
12
+ "enabledByDefault": false,
13
+ "providers": [
14
+ "konduit"
15
+ ],
16
+ "contracts": {
17
+ "usageProviders": [
18
+ "konduit"
19
+ ]
20
+ },
21
+ "modelCatalog": {
22
+ "providers": {
23
+ "konduit": {
24
+ "baseUrl": "https://api.konduit.eu/v1",
25
+ "api": "openai-completions",
26
+ "defaultModel": "scaleway/gpt-oss-120b",
27
+ "models": [
28
+ {
29
+ "id": "hetzner/qwen3.6-35b-a3b:fp8",
30
+ "name": "Qwen3.6 35B A3B",
31
+ "reasoning": false,
32
+ "input": [
33
+ "text"
34
+ ],
35
+ "contextWindow": 262144,
36
+ "maxTokens": 4096,
37
+ "cost": {
38
+ "input": 0,
39
+ "output": 0,
40
+ "cacheRead": 0,
41
+ "cacheWrite": 0
42
+ },
43
+ "compat": {
44
+ "supportsUsageInStreaming": true,
45
+ "supportsTools": true,
46
+ "maxTokensField": "max_tokens"
47
+ }
48
+ },
49
+ {
50
+ "id": "hetzner/qwen3.8-27b",
51
+ "name": "Qwen3.8 27B",
52
+ "reasoning": false,
53
+ "input": [
54
+ "text"
55
+ ],
56
+ "contextWindow": 262144,
57
+ "maxTokens": 4096,
58
+ "cost": {
59
+ "input": 0,
60
+ "output": 0,
61
+ "cacheRead": 0,
62
+ "cacheWrite": 0
63
+ },
64
+ "compat": {
65
+ "supportsUsageInStreaming": true,
66
+ "supportsTools": true,
67
+ "maxTokensField": "max_tokens"
68
+ }
69
+ },
70
+ {
71
+ "id": "scaleway/deepseek-v4-flash-0731",
72
+ "name": "DeepSeek V4 Flash 0731",
73
+ "reasoning": false,
74
+ "input": [
75
+ "text"
76
+ ],
77
+ "contextWindow": 256000,
78
+ "maxTokens": 32768,
79
+ "cost": {
80
+ "input": 0.4,
81
+ "output": 0.8,
82
+ "cacheRead": 0,
83
+ "cacheWrite": 0
84
+ },
85
+ "compat": {
86
+ "supportsUsageInStreaming": true,
87
+ "supportsTools": true,
88
+ "maxTokensField": "max_tokens"
89
+ }
90
+ },
91
+ {
92
+ "id": "scaleway/gemma-4-26b-a4b-it",
93
+ "name": "Gemma 4 26B A4B IT",
94
+ "reasoning": false,
95
+ "input": [
96
+ "text"
97
+ ],
98
+ "contextWindow": 256000,
99
+ "maxTokens": 32768,
100
+ "cost": {
101
+ "input": 0.25,
102
+ "output": 0.5,
103
+ "cacheRead": 0,
104
+ "cacheWrite": 0
105
+ },
106
+ "compat": {
107
+ "supportsUsageInStreaming": true,
108
+ "supportsTools": true,
109
+ "maxTokensField": "max_tokens"
110
+ }
111
+ },
112
+ {
113
+ "id": "scaleway/glm-5.2",
114
+ "name": "GLM-5.2",
115
+ "reasoning": false,
116
+ "input": [
117
+ "text"
118
+ ],
119
+ "contextWindow": 256000,
120
+ "maxTokens": 16384,
121
+ "cost": {
122
+ "input": 1.8,
123
+ "output": 5.5,
124
+ "cacheRead": 0,
125
+ "cacheWrite": 0
126
+ },
127
+ "compat": {
128
+ "supportsUsageInStreaming": true,
129
+ "supportsTools": true,
130
+ "maxTokensField": "max_tokens"
131
+ }
132
+ },
133
+ {
134
+ "id": "scaleway/gpt-oss-120b",
135
+ "name": "GPT-OSS 120B",
136
+ "reasoning": false,
137
+ "input": [
138
+ "text"
139
+ ],
140
+ "contextWindow": 128000,
141
+ "maxTokens": 32768,
142
+ "cost": {
143
+ "input": 0.15,
144
+ "output": 0.6,
145
+ "cacheRead": 0,
146
+ "cacheWrite": 0
147
+ },
148
+ "compat": {
149
+ "supportsUsageInStreaming": true,
150
+ "supportsTools": true,
151
+ "maxTokensField": "max_tokens"
152
+ }
153
+ },
154
+ {
155
+ "id": "scaleway/llama-3.3-70b-instruct",
156
+ "name": "Llama 3.3 70B Instruct",
157
+ "reasoning": false,
158
+ "input": [
159
+ "text"
160
+ ],
161
+ "contextWindow": 100000,
162
+ "maxTokens": 16384,
163
+ "cost": {
164
+ "input": 0.9,
165
+ "output": 0.9,
166
+ "cacheRead": 0,
167
+ "cacheWrite": 0
168
+ },
169
+ "compat": {
170
+ "supportsUsageInStreaming": true,
171
+ "supportsTools": true,
172
+ "maxTokensField": "max_tokens"
173
+ }
174
+ },
175
+ {
176
+ "id": "scaleway/mistral-medium-3.5-128b",
177
+ "name": "Mistral Medium 3.5 128B",
178
+ "reasoning": false,
179
+ "input": [
180
+ "text"
181
+ ],
182
+ "contextWindow": 180000,
183
+ "maxTokens": 16384,
184
+ "cost": {
185
+ "input": 1.5,
186
+ "output": 7.5,
187
+ "cacheRead": 0,
188
+ "cacheWrite": 0
189
+ },
190
+ "compat": {
191
+ "supportsUsageInStreaming": true,
192
+ "supportsTools": true,
193
+ "maxTokensField": "max_tokens"
194
+ }
195
+ },
196
+ {
197
+ "id": "scaleway/mistral-small-3.2",
198
+ "name": "Mistral Small 3.2",
199
+ "reasoning": false,
200
+ "input": [
201
+ "text"
202
+ ],
203
+ "contextWindow": 128000,
204
+ "maxTokens": 4096,
205
+ "cost": {
206
+ "input": 0.15,
207
+ "output": 0.35,
208
+ "cacheRead": 0,
209
+ "cacheWrite": 0
210
+ },
211
+ "compat": {
212
+ "supportsUsageInStreaming": true,
213
+ "supportsTools": true,
214
+ "maxTokensField": "max_tokens"
215
+ }
216
+ },
217
+ {
218
+ "id": "scaleway/pixtral-12b-2409",
219
+ "name": "Pixtral 12B 2409",
220
+ "reasoning": false,
221
+ "input": [
222
+ "text"
223
+ ],
224
+ "contextWindow": 128000,
225
+ "maxTokens": 4096,
226
+ "cost": {
227
+ "input": 0.2,
228
+ "output": 0.2,
229
+ "cacheRead": 0,
230
+ "cacheWrite": 0
231
+ },
232
+ "compat": {
233
+ "supportsUsageInStreaming": true,
234
+ "supportsTools": true,
235
+ "maxTokensField": "max_tokens"
236
+ }
237
+ },
238
+ {
239
+ "id": "scaleway/qwen3-235b-a22b-instruct-2507",
240
+ "name": "Qwen3 235B A22B Instruct 2507",
241
+ "reasoning": false,
242
+ "input": [
243
+ "text"
244
+ ],
245
+ "contextWindow": 250000,
246
+ "maxTokens": 16384,
247
+ "cost": {
248
+ "input": 0.75,
249
+ "output": 2.25,
250
+ "cacheRead": 0,
251
+ "cacheWrite": 0
252
+ },
253
+ "compat": {
254
+ "supportsUsageInStreaming": true,
255
+ "supportsTools": true,
256
+ "maxTokensField": "max_tokens"
257
+ }
258
+ },
259
+ {
260
+ "id": "scaleway/qwen3-coder-30b-a3b-instruct",
261
+ "name": "Qwen3 Coder 30B A3B Instruct",
262
+ "reasoning": false,
263
+ "input": [
264
+ "text"
265
+ ],
266
+ "contextWindow": 128000,
267
+ "maxTokens": 32768,
268
+ "cost": {
269
+ "input": 0.2,
270
+ "output": 0.8,
271
+ "cacheRead": 0,
272
+ "cacheWrite": 0
273
+ },
274
+ "compat": {
275
+ "supportsUsageInStreaming": true,
276
+ "supportsTools": true,
277
+ "maxTokensField": "max_tokens"
278
+ }
279
+ },
280
+ {
281
+ "id": "scaleway/qwen3.5-397b-a17b",
282
+ "name": "Qwen3.5 397B A17B",
283
+ "reasoning": false,
284
+ "input": [
285
+ "text"
286
+ ],
287
+ "contextWindow": 250000,
288
+ "maxTokens": 16384,
289
+ "cost": {
290
+ "input": 0.6,
291
+ "output": 3.6,
292
+ "cacheRead": 0,
293
+ "cacheWrite": 0
294
+ },
295
+ "compat": {
296
+ "supportsUsageInStreaming": true,
297
+ "supportsTools": true,
298
+ "maxTokensField": "max_tokens"
299
+ }
300
+ },
301
+ {
302
+ "id": "scaleway/qwen3.6-35b-a3b",
303
+ "name": "Qwen3.6 35B A3B",
304
+ "reasoning": false,
305
+ "input": [
306
+ "text"
307
+ ],
308
+ "contextWindow": 262144,
309
+ "maxTokens": 4096,
310
+ "cost": {
311
+ "input": 0.25,
312
+ "output": 1.5,
313
+ "cacheRead": 0,
314
+ "cacheWrite": 0
315
+ },
316
+ "compat": {
317
+ "supportsUsageInStreaming": true,
318
+ "supportsTools": true,
319
+ "maxTokensField": "max_tokens"
320
+ }
321
+ }
322
+ ]
323
+ }
324
+ },
325
+ "discovery": {
326
+ "konduit": "refreshable"
327
+ }
328
+ },
329
+ "setup": {
330
+ "providers": [
331
+ {
332
+ "id": "konduit",
333
+ "envVars": [
334
+ "KONDUIT_API_KEY"
335
+ ]
336
+ }
337
+ ]
338
+ },
339
+ "providerAuthChoices": [
340
+ {
341
+ "provider": "konduit",
342
+ "method": "api-key",
343
+ "choiceId": "konduit-api-key",
344
+ "appGuidedSecret": true,
345
+ "choiceLabel": "konduit API key",
346
+ "groupId": "konduit",
347
+ "groupLabel": "konduit",
348
+ "groupHint": "API key",
349
+ "optionKey": "konduitApiKey",
350
+ "cliFlag": "--konduit-api-key",
351
+ "cliOption": "--konduit-api-key <key>",
352
+ "cliDescription": "konduit API key (kdt-…), minted in the konduit console"
353
+ }
354
+ ],
355
+ "configSchema": {
356
+ "type": "object",
357
+ "additionalProperties": false,
358
+ "properties": {}
359
+ }
360
+ }
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@konduiteu/openclaw",
3
+ "version": "0.1.0",
4
+ "description": "OpenClaw provider plugin for konduit: European inference, with live balance and rate limits on the provider card.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/schnaq/openclaw-konduit"
10
+ },
11
+ "homepage": "https://konduit.eu",
12
+ "engines": {
13
+ "node": ">=24"
14
+ },
15
+ "scripts": {
16
+ "build": "tsc -p tsconfig.json",
17
+ "prepack": "npm run build",
18
+ "typecheck": "tsc -p tsconfig.json --noEmit",
19
+ "test": "vitest run --config ./vitest.config.ts",
20
+ "catalog": "node scripts/catalog.ts",
21
+ "catalog:check": "node scripts/catalog.ts --check",
22
+ "validate": "npm run build && clawhub package validate . --out .clawhub-validation"
23
+ },
24
+ "files": [
25
+ "dist",
26
+ "openclaw.plugin.json",
27
+ "assets/icon.png",
28
+ "README.md",
29
+ "LICENSE"
30
+ ],
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "peerDependencies": {
35
+ "openclaw": ">=2026.9.4"
36
+ },
37
+ "peerDependenciesMeta": {
38
+ "openclaw": {
39
+ "optional": true
40
+ }
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^24.0.0",
44
+ "clawhub": "0.23.3",
45
+ "openclaw": "2026.9.4",
46
+ "typescript": "^5.9.0",
47
+ "vitest": "^3.2.0"
48
+ },
49
+ "openclaw": {
50
+ "extensions": [
51
+ "./dist/index.js"
52
+ ],
53
+ "compat": {
54
+ "pluginApi": ">=2026.9.4"
55
+ },
56
+ "build": {
57
+ "openclawVersion": "2026.9.4"
58
+ },
59
+ "install": {
60
+ "clawhubSpec": "clawhub:@konduiteu/openclaw",
61
+ "npmSpec": "@konduiteu/openclaw",
62
+ "defaultChoice": "clawhub",
63
+ "minHostVersion": ">=2026.9.4"
64
+ },
65
+ "release": {
66
+ "publishToClawHub": true,
67
+ "publishToNpm": true
68
+ }
69
+ }
70
+ }