@jskit-ai/connectors-catalog 0.1.60 → 0.1.62
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/docs/ai.md +26 -21
- package/package.json +3 -3
- package/patterns/ai-connections/PATTERN.md +4 -3
- package/src/shared/ai-models.json +0 -1
- package/src/shared/ai.js +9 -12
- package/test/ai.test.js +25 -17
package/docs/ai.md
CHANGED
|
@@ -22,10 +22,15 @@ model and `env:NAME` reference and use its own environment/configuration and AI
|
|
|
22
22
|
client. Framework-independent facts in this guide are usable by its coding AI;
|
|
23
23
|
PHP code and a parallel PHP package are not part of JSKIT.
|
|
24
24
|
|
|
25
|
-
##
|
|
25
|
+
## Model selection and catalogue
|
|
26
26
|
|
|
27
|
-
Import `aiCatalogue`, `listAiModels`, `getAiModel
|
|
28
|
-
|
|
27
|
+
Import `aiCatalogue`, `listAiModels`, `getAiModel` and `aiAccessLabels` from
|
|
28
|
+
`@jskit-ai/connectors-catalog/shared/ai`.
|
|
29
|
+
|
|
30
|
+
There is no default model. The connector's first authentication method is a
|
|
31
|
+
provider API key, and an integration without a model is invalid. Free
|
|
32
|
+
access without an account or key is used only for a model explicitly chosen
|
|
33
|
+
from the `free-no-setup` list.
|
|
29
34
|
|
|
30
35
|
The one-time 10 September 2026 extraction contains 213 provider records and
|
|
31
36
|
7,614 model records from models.dev, the catalogue used by OpenCode. There is
|
|
@@ -41,7 +46,8 @@ accepts these names and serves their requests with V4.1 Flash. JSKIT sends the
|
|
|
41
46
|
selected ID unchanged and does not rewrite configuration. All three Flash
|
|
42
47
|
entries accept text and images. They and `deepseek/deepseek-v4-pro` declare
|
|
43
48
|
the provider's exact 1,048,576-token context and 393,216-token output limits;
|
|
44
|
-
Pro accepts text only. The bundled catalogue now has 7,
|
|
49
|
+
Pro accepts text only. The bundled catalogue now has 7,614 model records;
|
|
50
|
+
OpenCode Zen's Big Pickle was removed from it.
|
|
45
51
|
The source hashes identify the original 7,614-record extraction, before this
|
|
46
52
|
reviewed update. Flash entries declare paid access without a fixed price because
|
|
47
53
|
the provider has peak and off-peak rates. See the
|
|
@@ -49,9 +55,8 @@ the provider has peak and off-peak rates. See the
|
|
|
49
55
|
[model details](https://api-docs.deepseek.com/quick_start/pricing/), including
|
|
50
56
|
[exact model metadata](https://api-docs.deepseek.com/api/list-models/).
|
|
51
57
|
|
|
52
|
-
`listAiModels()` hides deprecated entries and sorts free choices first
|
|
53
|
-
`
|
|
54
|
-
history; `providerId`, `access` and `configurableOnly` filter the list.
|
|
58
|
+
`listAiModels()` hides deprecated entries and sorts free choices first.
|
|
59
|
+
`includeDeprecated: true` exposes retained upstream history; `providerId`, `access` and `configurableOnly` filter the list.
|
|
55
60
|
Models retain limits, modality and cost metadata as dated information, not a
|
|
56
61
|
promise of current availability or a billing calculator. An explicit unknown
|
|
57
62
|
or deprecated model fails validation; it is never replaced automatically.
|
|
@@ -63,9 +68,9 @@ or deprecated model fails validation; it is never replaced automatically.
|
|
|
63
68
|
| `paid` | Declared positive input or output charges |
|
|
64
69
|
| `unknown` | Insufficient pricing information; check the provider |
|
|
65
70
|
|
|
66
|
-
The snapshot's
|
|
67
|
-
|
|
68
|
-
|
|
71
|
+
The snapshot's active public choices include MiMo-V2.5 Free, Ling3.0 FlashFin
|
|
72
|
+
Free, Nemotron 3 Ultra Free, Nemotron 3.5 Lightning Free, Muse Spark 1.2
|
|
73
|
+
Contributor Free and Muse Spark 1.3 Contributor Free.
|
|
69
74
|
This is a source-derived eligibility list, **not live inference verification**.
|
|
70
75
|
Provider capacity, free offers and data-use terms can change. A failure stays
|
|
71
76
|
with the selected provider/model; the application decides how to explain it.
|
|
@@ -73,7 +78,7 @@ with the selected provider/model; the application decides how to explain it.
|
|
|
73
78
|
Zen's no-account protocol uses the fixed string `public` in its API-key
|
|
74
79
|
position. The resolver supplies it. Nobody needs to enter it, create an account
|
|
75
80
|
or create an environment variable for this mode. The Muse models use the
|
|
76
|
-
OpenAI Responses SDK route, whereas
|
|
81
|
+
OpenAI Responses SDK route, whereas MiMo uses OpenAI-compatible chat;
|
|
77
82
|
the model's `sdkPackage` preserves that distinction.
|
|
78
83
|
|
|
79
84
|
The initial configuration form and resolver support regular direct API-key
|
|
@@ -86,7 +91,7 @@ claim that a URL and API key can configure them.
|
|
|
86
91
|
|
|
87
92
|
## Portable configuration and Env
|
|
88
93
|
|
|
89
|
-
|
|
94
|
+
An administrator-funded Z.AI model:
|
|
90
95
|
|
|
91
96
|
```json
|
|
92
97
|
{
|
|
@@ -97,15 +102,14 @@ This is enough to select the no-setup default:
|
|
|
97
102
|
"provider": "ai",
|
|
98
103
|
"accountMode": "shared",
|
|
99
104
|
"scopes": [],
|
|
100
|
-
"authentication": { "method": "
|
|
101
|
-
"settings": { "model": "
|
|
105
|
+
"authentication": { "method": "api-key", "secretRef": "env:APP_AI_API_KEY" },
|
|
106
|
+
"settings": { "model": "zai/glm-4.7-flash" }
|
|
102
107
|
}
|
|
103
108
|
}
|
|
104
109
|
}
|
|
105
110
|
```
|
|
106
111
|
|
|
107
|
-
|
|
108
|
-
and `authentication.secretRef: "env:APP_AI_API_KEY"`. Store the **value** in the
|
|
112
|
+
Store the key's **value** in the
|
|
109
113
|
existing Env screen or the application's normal private environment. The host's
|
|
110
114
|
existing launch/deployment environment mechanism supplies it to the backend;
|
|
111
115
|
this integration adds no token delivery service. Do not use `VITE_` or another
|
|
@@ -159,9 +163,10 @@ There are no `vibe64-online`/`vibe64-public` OAuth registrations or universal
|
|
|
159
163
|
callbacks for this AI integration. Regular keys belong to the app administrator
|
|
160
164
|
or individual app user and are billed directly by that provider.
|
|
161
165
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
166
|
+
Choose AI, then choose a provider and model; nothing is preselected. For an
|
|
167
|
+
explicitly chosen model labelled no account or key, no provider-side action is
|
|
168
|
+
needed. For API-key routes, follow the provider's current dashboard
|
|
169
|
+
documentation linked in `aiCatalogue.providers[].documentationUrl`:
|
|
165
170
|
|
|
166
171
|
1. The account owner signs in to the provider and selects the organization or
|
|
167
172
|
project that should own usage. This is a human account/terms/billing step.
|
|
@@ -190,7 +195,7 @@ authorization merely by editing this JSON.
|
|
|
190
195
|
|
|
191
196
|
## Evidence and limits
|
|
192
197
|
|
|
193
|
-
Focused tests cover snapshot integrity, free
|
|
198
|
+
Focused tests cover snapshot integrity, free classification, no default model, strict
|
|
194
199
|
selection, authorized environment and individual-key resolution, rotation,
|
|
195
200
|
disconnect, redacted failures and SDK parameter handoff. Browser fixtures cover
|
|
196
201
|
the editor's default, searchable model change, credential ownership, persistence
|
|
@@ -199,7 +204,7 @@ account creation or generated-application execution.
|
|
|
199
204
|
|
|
200
205
|
## Existing-scope closeout — 13 September 2026
|
|
201
206
|
|
|
202
|
-
A static, attributed September 10 provider/model catalogue, searchable free/paid model configuration and a backend-only authorized credential/SDK-parameter resolver.
|
|
207
|
+
A static, attributed September 10 provider/model catalogue, searchable free/paid model configuration and a backend-only authorized credential/SDK-parameter resolver. No model is preselected and the first authentication method is a provider API key. Administrator keys resolve through Env, and individual keys through an app-supplied user/provider-scoped reference resolver.
|
|
203
208
|
|
|
204
209
|
No inference engine, streaming, tools, conversation storage, provider OAuth/login screens, account/key provisioning, live credential verification, usage metering, billing, token resale or Vibe64 AI gateway. No automatic SDK installation, catalogue updates, model replacement or paid fallback. Only the 13 explicitly supported direct-key routes are configurable; other catalogue entries are framework metadata, not implemented cloud/coding-subscription authentication. Model availability, pricing/free classification and endpoint operation are dated source information, not live proof; even no-setup choices can be unavailable or capacity-limited. Detailed provider-specific personal-key entry, verification, encrypted storage, rotation/disconnect and authorization remain application work; the resolver only consumes those bindings. The app/framework owns SDK installation, protocol selection, requests, errors and UI. Generic key-creation guidance and linked provider documentation are supplied; bespoke audited login/key-creation screens for every catalogue provider are not. To extend: deliberately refresh the static snapshot, improve selected provider-specific instructions, and wire any desired app-native key-entry/inference features. This intentionally differs from Lovable managed AI; coding-agent credentials and runtimes remain separate from direct-provider API authentication.
|
|
205
210
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/connectors-catalog",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.62",
|
|
4
4
|
"description": "Provider definitions and focused API operations for JSKIT connectors.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -123,14 +123,14 @@
|
|
|
123
123
|
"@aws-sdk/client-redshift-data": "3.1128.0",
|
|
124
124
|
"@aws-sdk/client-s3": "3.1128.0",
|
|
125
125
|
"@aws-sdk/s3-request-presigner": "3.1128.0",
|
|
126
|
-
"@jskit-ai/connectors-core": "0.1.
|
|
126
|
+
"@jskit-ai/connectors-core": "0.1.61",
|
|
127
127
|
"@modelcontextprotocol/sdk": "1.32.1",
|
|
128
128
|
"@smithy/fetch-http-handler": "5.8.0",
|
|
129
129
|
"jose": "^6.1.3",
|
|
130
130
|
"json-rest-schema": "^1.0.17"
|
|
131
131
|
},
|
|
132
132
|
"peerDependencies": {
|
|
133
|
-
"@jskit-ai/kernel": "0.1.
|
|
133
|
+
"@jskit-ai/kernel": "0.1.244"
|
|
134
134
|
},
|
|
135
135
|
"jskit": {
|
|
136
136
|
"kind": "runtime",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
id: connectors/ai-connections
|
|
3
3
|
title: App-owned AI connections and static model selection
|
|
4
4
|
summary: Resolve an administrator environment key or an individual user's grant, then use the application's chosen AI SDK directly.
|
|
5
|
-
keywords: ai, models, free,
|
|
5
|
+
keywords: ai, models, free, zen, zai, environment, api-key, ownership, cli
|
|
6
6
|
requires: @jskit-ai/connectors-core, @jskit-ai/connectors-catalog
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -12,8 +12,9 @@ requires: @jskit-ai/connectors-core, @jskit-ai/connectors-catalog
|
|
|
12
12
|
|
|
13
13
|
Use the [AI guide](https://github.com/mobily-enterprises/jskit-ai/blob/main/packages/connectors-catalog/docs/ai.md) to choose the model, account owner and
|
|
14
14
|
framework SDK before wiring requests. The shared catalogue is a deliberate
|
|
15
|
-
static extraction, not an OpenCode runtime integration.
|
|
16
|
-
|
|
15
|
+
static extraction, not an OpenCode runtime integration. There is no default
|
|
16
|
+
model: choose the provider, model and key. Only an explicitly chosen model
|
|
17
|
+
labelled no account or key needs no account or environment setup.
|
|
17
18
|
|
|
18
19
|
## Framework APIs
|
|
19
20
|
|
|
@@ -5392,7 +5392,6 @@
|
|
|
5392
5392
|
{"id":"text-embedding-ada-002","name":"text-embedding-ada-002","cost":{"input":0.1,"output":0},"limit":{"context":8192,"output":1536},"modalities":{"input":["text"],"output":["text"]},"sdkPackage":"@ai-sdk/openai","access":"paid"}
|
|
5393
5393
|
]},
|
|
5394
5394
|
{"id": "opencode", "name": "OpenCode Zen", "documentationUrl": "https://opencode.ai/docs/zen", "baseURL": "https://opencode.ai/zen/v1", "environment": ["OPENCODE_API_KEY"], "sdkPackage": "@ai-sdk/openai-compatible", "models": [
|
|
5395
|
-
{"id":"big-pickle","name":"Big Pickle","cost":{"input":0,"output":0,"cache_read":0,"cache_write":0},"limit":{"context":200000,"input":160000,"output":32000},"modalities":{"input":["text"],"output":["text"]},"sdkPackage":"@ai-sdk/openai-compatible","access":"free-no-setup"},
|
|
5396
5395
|
{"id":"claude-3-5-haiku","name":"Claude Haiku 3.5","status":"deprecated","cost":{"input":0.8,"output":4,"cache_read":0.08,"cache_write":1},"limit":{"context":200000,"output":8192},"modalities":{"input":["text","image","pdf"],"output":["text"]},"sdkPackage":"@ai-sdk/anthropic","access":"paid"},
|
|
5397
5396
|
{"id":"claude-fable-5","name":"Claude Fable 5","cost":{"input":10,"output":50,"cache_read":1,"cache_write":12.5},"limit":{"context":1000000,"output":128000},"modalities":{"input":["text","image","pdf"],"output":["text"]},"sdkPackage":"@ai-sdk/anthropic","access":"paid"},
|
|
5398
5397
|
{"id":"claude-fable-5-1","name":"Claude Fable 5.1","cost":{"input":10,"output":50,"cache_read":0.25,"cache_write":12.5},"limit":{"context":1000000,"output":128000},"modalities":{"input":["text","image","pdf"],"output":["text"]},"sdkPackage":"@ai-sdk/anthropic","access":"paid"},
|
package/src/shared/ai.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createSchema } from "json-rest-schema";
|
|
2
2
|
import snapshot from "./ai-models.json" with { type: "json" };
|
|
3
3
|
|
|
4
|
-
const DEFAULT_AI_MODEL = "opencode/big-pickle";
|
|
5
4
|
const aiAccessLabels = Object.freeze({
|
|
6
5
|
"free-no-setup": "Free · no account or key",
|
|
7
6
|
"free-with-connection": "Free · connection required",
|
|
@@ -26,38 +25,36 @@ function listAiModels({ providerId, access, includeDeprecated = false, configura
|
|
|
26
25
|
(!configurableOnly || provider.connection === "api-key"))
|
|
27
26
|
.flatMap((provider) => provider.models).filter((model) =>
|
|
28
27
|
(includeDeprecated || model.status !== "deprecated") && (!access || model.access === access))
|
|
29
|
-
.sort((a, b) => a.
|
|
30
|
-
order.indexOf(a.access) - order.indexOf(b.access) || a.key.localeCompare(b.key));
|
|
28
|
+
.sort((a, b) => order.indexOf(a.access) - order.indexOf(b.access) || a.key.localeCompare(b.key));
|
|
31
29
|
}
|
|
32
30
|
|
|
33
31
|
const modelItems = Object.freeze(listAiModels({ configurableOnly: true }).map((model) => Object.freeze({
|
|
34
32
|
value: model.key, title: `${model.name} (${model.providerId})`, props: { subtitle: aiAccessLabels[model.access] }
|
|
35
33
|
})));
|
|
36
34
|
const aiDefinition = Object.freeze({
|
|
37
|
-
id: "ai", name: "AI", description: "Choose an app AI model and whose account pays.
|
|
38
|
-
accountModes: ["shared", "per-user"], authenticationMethods: ["
|
|
39
|
-
authenticationMethodsForSettings: (settings = {}) => getAiModel(settings.model
|
|
35
|
+
id: "ai", name: "AI", description: "Choose an app AI provider, model and whose account pays. No model is preselected.",
|
|
36
|
+
accountModes: ["shared", "per-user"], authenticationMethods: ["api-key", "none"], scopes: [],
|
|
37
|
+
authenticationMethodsForSettings: (settings = {}) => getAiModel(settings.model)?.access === "free-no-setup"
|
|
40
38
|
? ["none", "api-key"] : ["api-key"],
|
|
41
39
|
authenticationLabels: { none: "Free access · no account or key", "api-key": "Use a provider API key" },
|
|
42
40
|
authenticationHint: "Your app calls the provider directly. A shared account belongs to the app administrator; individual accounts belong to each signed-in app user.",
|
|
43
|
-
settingsSchema: createSchema({ model: { type: "string",
|
|
41
|
+
settingsSchema: createSchema({ model: { type: "string", required: true,
|
|
44
42
|
enum: modelItems.map((item) => item.value) } }),
|
|
45
43
|
settingsFields: [{ name: "model", label: "AI model", searchable: true, items: modelItems,
|
|
46
44
|
credentialScope: (key) => getAiModel(key)?.providerId,
|
|
47
45
|
hint: (settings = {}) => {
|
|
48
|
-
const model = getAiModel(settings.model
|
|
49
|
-
return `${model ? `${model.name}: ${aiAccessLabels[model.access]}. ` : ""}Search by provider or model. Catalogue dated 10 September 2026; availability can change. No automatic paid fallback.`;
|
|
46
|
+
const model = getAiModel(settings.model);
|
|
47
|
+
return `${model ? `${model.name}: ${aiAccessLabels[model.access]}. ` : "Choose a provider and model. "}Search by provider or model. Catalogue dated 10 September 2026; availability can change. No automatic paid fallback.`;
|
|
50
48
|
} }],
|
|
51
49
|
apiKeyReferenceHint: "Use an environment reference for an administrator key. For individual accounts, use a private reference your app resolves for the authenticated user. Never put a key in this file.",
|
|
52
50
|
setup: {
|
|
53
51
|
url: "https://opencode.ai/docs/zen/",
|
|
54
52
|
steps: [
|
|
55
|
-
"
|
|
56
|
-
"For a connected model, create a regular API key in that provider's dashboard. Z.AI's GLM-4.7-Flash uses a regular Z.AI key; its Coding Plan is a different product.",
|
|
53
|
+
"Choose a provider and model; no model is preselected. Create a regular API key in that provider's dashboard. Z.AI's GLM-4.7-Flash uses a regular Z.AI key; its Coding Plan is a different product.",
|
|
57
54
|
"For a shared account, store the administrator's key in Env. For individual accounts, your application must provide private key entry, storage and disconnect controls for each authenticated user.",
|
|
58
55
|
"Saving selects configuration; it does not verify a provider account or install an AI library into your application. Follow the AI connection guide for the selected framework."
|
|
59
56
|
]
|
|
60
57
|
}
|
|
61
58
|
});
|
|
62
59
|
|
|
63
|
-
export { aiCatalogue, aiAccessLabels, aiDefinition,
|
|
60
|
+
export { aiCatalogue, aiAccessLabels, aiDefinition, getAiModel, listAiModels };
|
package/test/ai.test.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import test from "node:test";
|
|
3
3
|
import { readFile } from "node:fs/promises";
|
|
4
|
-
import { aiCatalogue, aiDefinition,
|
|
4
|
+
import { aiCatalogue, aiDefinition, getAiModel, listAiModels } from "../src/shared/ai.js";
|
|
5
5
|
import { createAiConnectionResolver } from "../src/server/ai.js";
|
|
6
6
|
import { createEnvironmentReferenceResolver } from "../../connectors-core/src/server/environmentReferences.js";
|
|
7
7
|
import { parseIntegrationConfiguration, validateIntegrationConfiguration } from "../../connectors-core/src/shared/configuration.js";
|
|
@@ -17,9 +17,10 @@ const validate = (config) => validateIntegrationConfiguration(config, { provider
|
|
|
17
17
|
test("AI retains the attributed snapshot with reviewed current identities and explicit deprecations", () => {
|
|
18
18
|
assert.equal(aiCatalogue.extractedAt, "2026-09-10");
|
|
19
19
|
assert.equal(aiCatalogue.providers.length, 213);
|
|
20
|
-
assert.equal(listAiModels({ includeDeprecated: true }).length,
|
|
20
|
+
assert.equal(listAiModels({ includeDeprecated: true }).length, 7614);
|
|
21
21
|
assert.match(aiCatalogue.sourceSha256, /^[a-f0-9]{64}$/u);
|
|
22
22
|
assert.equal(getAiModel("opencode/grok-code").status, "deprecated");
|
|
23
|
+
assert.equal(getAiModel("opencode/big-pickle"), undefined);
|
|
23
24
|
assert.equal(listAiModels().some((model) => model.key === "opencode/grok-code"), false);
|
|
24
25
|
assert.equal(aiCatalogue.providers.find((provider) => provider.id === "amazon-bedrock").connection, "framework");
|
|
25
26
|
});
|
|
@@ -49,10 +50,10 @@ test("DeepSeek retains provider-supported saved aliases and exact current model
|
|
|
49
50
|
assert.deepEqual(pro.modalities.input, ["text"]);
|
|
50
51
|
});
|
|
51
52
|
|
|
52
|
-
test("AI lists every active Zen public model first
|
|
53
|
-
assert.equal(listAiModels()[0].
|
|
53
|
+
test("AI lists every active Zen public model first", () => {
|
|
54
|
+
assert.equal(listAiModels()[0].access, "free-no-setup");
|
|
54
55
|
assert.deepEqual(listAiModels({ access: "free-no-setup" }).map((model) => model.id).sort(), [
|
|
55
|
-
"
|
|
56
|
+
"ling-3.0-flash-fin-free", "mimo-v2.5-free", "muse-spark-1.2-contributor-free",
|
|
56
57
|
"muse-spark-1.3-contributor-free", "nemotron-3-ultra-free", "nemotron-3.5-lightning-free"
|
|
57
58
|
].sort());
|
|
58
59
|
assert.equal(getAiModel("zai/glm-4.7-flash").access, "free-with-connection");
|
|
@@ -63,17 +64,23 @@ test("AI lists every active Zen public model first and uses Big Pickle as the ex
|
|
|
63
64
|
}
|
|
64
65
|
});
|
|
65
66
|
|
|
66
|
-
test("AI
|
|
67
|
-
|
|
68
|
-
assert.deepEqual(
|
|
69
|
-
assert.
|
|
70
|
-
|
|
67
|
+
test("AI has no default model or keyless default and preserves explicit free model choices", () => {
|
|
68
|
+
assert.equal(aiDefinition.authenticationMethods[0], "api-key");
|
|
69
|
+
assert.deepEqual(aiDefinition.authenticationMethodsForSettings({}), ["api-key"]);
|
|
70
|
+
assert.throws(() => validate(configuration()), (error) => {
|
|
71
|
+
assert.ok(error.fieldErrors["integrations.suggestions.settings.model"]);
|
|
72
|
+
assert.ok(error.fieldErrors["integrations.suggestions.authentication.method"]); return true;
|
|
73
|
+
});
|
|
74
|
+
assert.throws(() => validate(configuration({ authentication: { method: "api-key", secretRef: "env:KEY" } })), (error) => {
|
|
75
|
+
assert.deepEqual(Object.keys(error.fieldErrors), ["integrations.suggestions.settings.model"]); return true;
|
|
76
|
+
});
|
|
77
|
+
const config = validate(configuration({ settings: { model: "opencode/mimo-v2.5-free" } }));
|
|
71
78
|
assert.deepEqual(parseIntegrationConfiguration(JSON.stringify(config), { providers: [aiDefinition] }), config);
|
|
72
79
|
assert.equal(JSON.stringify(config).includes("public"), false);
|
|
73
80
|
});
|
|
74
81
|
|
|
75
82
|
test("AI refuses missing models, deprecated models, coding subscriptions and unsupported cloud setup", () => {
|
|
76
|
-
for (const model of ["opencode/removed", "opencode/grok-code", "zai-coding-plan/glm-4.7", "amazon-bedrock/anthropic.claude-v2", "constructor"]) {
|
|
83
|
+
for (const model of ["opencode/removed", "opencode/grok-code", "opencode/big-pickle", "zai-coding-plan/glm-4.7", "amazon-bedrock/anthropic.claude-v2", "constructor"]) {
|
|
77
84
|
assert.throws(() => validate(configuration({ settings: { model } })), { code: "integration_configuration_invalid" });
|
|
78
85
|
}
|
|
79
86
|
for (const model of ["zai/glm-4.7-flash", "zai/glm-4.7", "openai/gpt-5.4"]) {
|
|
@@ -87,12 +94,12 @@ test("AI refuses missing models, deprecated models, coding subscriptions and uns
|
|
|
87
94
|
}
|
|
88
95
|
});
|
|
89
96
|
|
|
90
|
-
test("AI resolves no-account SDK parameters without environment, network or an OpenCode runtime", async (t) => {
|
|
97
|
+
test("AI resolves explicit no-account SDK parameters without environment, network or an OpenCode runtime", async (t) => {
|
|
91
98
|
t.mock.method(globalThis, "fetch", () => { throw new Error("Network is forbidden in this resolver."); });
|
|
92
|
-
const resolver = createAiConnectionResolver({ configuration: configuration(), authorize,
|
|
99
|
+
const resolver = createAiConnectionResolver({ configuration: configuration({ settings: { model: "opencode/mimo-v2.5-free" } }), authorize,
|
|
93
100
|
resolveReference: () => { throw new Error("Free mode must not resolve credentials."); } });
|
|
94
|
-
assert.deepEqual(await resolver.resolve(request), { providerId: "opencode", model: "
|
|
95
|
-
modelLimits: getAiModel(
|
|
101
|
+
assert.deepEqual(await resolver.resolve(request), { providerId: "opencode", model: "mimo-v2.5-free",
|
|
102
|
+
modelLimits: getAiModel("opencode/mimo-v2.5-free").limit,
|
|
96
103
|
sdkPackage: "@ai-sdk/openai-compatible", baseURL: "https://opencode.ai/zen/v1", apiKey: "public", access: "free-no-setup" });
|
|
97
104
|
assert.equal(globalThis.fetch.mock.callCount(), 0);
|
|
98
105
|
assert.equal(Object.hasOwn(resolver, "invoke"), false);
|
|
@@ -153,10 +160,11 @@ test("AI denies unauthorized subjects before looking up credentials and redacts
|
|
|
153
160
|
});
|
|
154
161
|
|
|
155
162
|
test("AI can hand parameters to an app-owned SDK without making that SDK a dependency", async () => {
|
|
156
|
-
const result = await createAiConnectionResolver({ configuration: configuration(
|
|
163
|
+
const result = await createAiConnectionResolver({ configuration: configuration({ settings: { model: "zai/glm-4.7-flash" },
|
|
164
|
+
authentication: { method: "api-key", secretRef: "env:KEY" } }), authorize, resolveReference: () => "test-key" }).resolve(request);
|
|
157
165
|
// A controlled SDK factory fixture; no generated application or inference call.
|
|
158
166
|
const createProvider = (options) => ({ model: (id) => ({ endpoint: options.baseURL, id }) });
|
|
159
|
-
assert.deepEqual(createProvider(result).model(result.model), { endpoint: "https://
|
|
167
|
+
assert.deepEqual(createProvider(result).model(result.model), { endpoint: "https://api.z.ai/api/paas/v4", id: "glm-4.7-flash" });
|
|
160
168
|
const pkg = JSON.parse(await readFile(new URL("../package.json", import.meta.url), "utf8"));
|
|
161
169
|
assert.equal(Object.keys(pkg.dependencies).some((name) => name.startsWith("@ai-sdk/") || name.includes("opencode")), false);
|
|
162
170
|
});
|