@jskit-ai/connectors-catalog 0.1.61 → 0.1.63
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/patterns/oauth-connection/PATTERN.md +25 -0
- package/src/shared/ai-models.json +0 -1
- package/src/shared/ai.js +9 -12
- package/src/shared/algolia.js +2 -2
- package/src/shared/firebase-cloud-messaging.js +1 -1
- package/src/shared/google-maps-platform.js +1 -1
- package/src/shared/google.js +2 -2
- package/src/shared/logo-dev.js +2 -2
- package/src/shared/mapbox.js +2 -2
- package/src/shared/tokens.js +2 -2
- 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.63",
|
|
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.62",
|
|
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.245"
|
|
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
|
|
|
@@ -181,6 +181,31 @@ comma-separated permissions. Oura handles reduced callback scopes and rotated
|
|
|
181
181
|
refresh tokens. Some providers do not return a refresh token; expired access
|
|
182
182
|
then needs consent again. Provider PKCE enforcement needs provider evidence.
|
|
183
183
|
|
|
184
|
+
## Shared connection from an Env refresh token
|
|
185
|
+
|
|
186
|
+
When the application's host already holds a long-lived grant for the shared
|
|
187
|
+
account (the owner connected it once with the same OAuth client), skip consent
|
|
188
|
+
in the app: add `refreshTokenRef` to a `shared` oauth2 integration whose
|
|
189
|
+
registration uses the authorization-code flow.
|
|
190
|
+
|
|
191
|
+
```json
|
|
192
|
+
"authentication": {
|
|
193
|
+
"method": "oauth2",
|
|
194
|
+
"registrationRef": "google",
|
|
195
|
+
"refreshTokenRef": "env:GOOGLE_REFRESH_TOKEN"
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The Env value is a seed. On first use, and whenever the value or the OAuth
|
|
200
|
+
client changes, the runtime replaces the shared connection with one built from
|
|
201
|
+
that refresh token. From then on it refreshes and stores rotated tokens in the
|
|
202
|
+
application's own store as usual. `status()` reports `source: "env"` (show it as
|
|
203
|
+
"Connected from Env"). `beginAuthorization()` and `disconnect()` refuse with
|
|
204
|
+
`connector_env_managed`: "This connection comes from an Env value; unlink or
|
|
205
|
+
replace that value instead." A revoked token fails with "The refresh token in
|
|
206
|
+
Env no longer works; replace that Env value." The refresh token must come from
|
|
207
|
+
the same client as `registrationRef`; non-JSKIT apps read the variable directly.
|
|
208
|
+
|
|
184
209
|
## Twitch composition
|
|
185
210
|
|
|
186
211
|
Use `twitchProvider` from `@jskit-ai/connectors-catalog/server/twitch` in the
|
|
@@ -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/src/shared/algolia.js
CHANGED
|
@@ -12,7 +12,7 @@ const algoliaDefinition = Object.freeze({
|
|
|
12
12
|
settingsFields: [
|
|
13
13
|
{ name: "applicationId", label: "Application ID", hint: "Copy the ID from your Algolia application's API Keys page. This ID can be public." },
|
|
14
14
|
{ name: "publicApiKeyRef", environmentCredential: { label: "Public frontend API key", public: true }, label: "Public API key reference (optional)", placeholder: "env:ALGOLIA_SEARCH_KEY",
|
|
15
|
-
hint: "Optional search-only key for your frontend. Enter its reference; your app decides whether to
|
|
15
|
+
hint: "Optional search-only key for your frontend. Enter its reference; your app decides whether to expose its value." }
|
|
16
16
|
],
|
|
17
17
|
setup: {
|
|
18
18
|
url: "https://www.algolia.com/doc/guides/security/api-keys",
|
|
@@ -22,7 +22,7 @@ const algoliaDefinition = Object.freeze({
|
|
|
22
22
|
"Copy the new backend key. Enter env:ALGOLIA_API_KEY in API key reference here and save. Choose Set credential in Env for ALGOLIA_API_KEY, paste its value and save it there.",
|
|
23
23
|
"If the frontend will search Algolia directly, create a separate key with Search (search) access restricted to indices safe for those users to read. Enter env:ALGOLIA_SEARCH_KEY in Public API key reference (optional), save, then use its Set credential in Env link to store that key value.",
|
|
24
24
|
"Return here and choose Connect account (or Verify again for an existing connection). Check connection only reads the application’s saved status. No OAuth registration or callback URL is needed. Verification lists indices with the backend key; it does not validate the optional frontend key.",
|
|
25
|
-
"If access fails, check the Application ID, selected indices, permissions and key expiry. Your framework owns
|
|
25
|
+
"If access fails, check the Application ID, selected indices, permissions and key expiry. Your framework owns exposing the optional frontend key; keep the backend key server-side."
|
|
26
26
|
]
|
|
27
27
|
}
|
|
28
28
|
});
|
|
@@ -26,7 +26,7 @@ const firebaseCloudMessagingDefinition = Object.freeze({
|
|
|
26
26
|
{ name: "projectId", label: "Firebase project ID", placeholder: "my-notifications-app", hint: "The project receiving messages. Usually project_id in your JSON; cross-project access requires an IAM grant on this target project." },
|
|
27
27
|
{ name: "clientMode", label: "Client setup", items: [{ value: "server", title: "Server or native only" }, { value: "web", title: "Include web push" }],
|
|
28
28
|
hint: "Existing mobile/browser targets need server credentials. Include web push to configure browser registration too." },
|
|
29
|
-
{ name: "apiKey", label: "Firebase API key", placeholder: "AIzaSyXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", hint: "Public API key from the Firebase web app configuration. This value is visible in your
|
|
29
|
+
{ name: "apiKey", label: "Firebase API key", placeholder: "AIzaSyXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", hint: "Public API key from the Firebase web app configuration. This value is visible in your deployed app." },
|
|
30
30
|
{ name: "appId", label: "Firebase App ID", placeholder: "1:123456789012:web:abcdef1234567890", hint: "Public appId from the same Firebase project and web app." },
|
|
31
31
|
{ name: "vapidKey", label: "Web Push VAPID key", placeholder: "BEl62...", hint: "Public key from Project settings > Cloud Messaging > Web Push certificates. Never paste the private VAPID key." }
|
|
32
32
|
],
|
|
@@ -18,7 +18,7 @@ const googleMapsPlatformDefinition = Object.freeze({
|
|
|
18
18
|
"Restrict the server key to Geocoding API and the backend's outbound IPs. Enter env:GOOGLE_MAPS_SERVER_KEY in Server API key reference, Save configuration, then Set credential in Env. Save the real key as GOOGLE_MAPS_SERVER_KEY.",
|
|
19
19
|
"For places and routes, also enable Places API (New) and Routes API in Library, then add them to the server key API restrictions. Place names/address/location use billable data fields; set quotas and review current pricing. Geocoding verification does not test these additional APIs.",
|
|
20
20
|
"For browser maps, enable Maps JavaScript API. Open Google Maps Platform > Map management > Create map ID, select JavaScript and copy the ID into Map ID. The app loads Google's SDK with its separate browser key and gives the map container a height. Advanced markers require a map ID.",
|
|
21
|
-
"For browser maps, create a separate key restricted to the required browser APIs and Websites, including the actual preview and custom domains. Enter env:GOOGLE_MAPS_BROWSER_KEY in Browser API key reference and save that value separately in Env. The app
|
|
21
|
+
"For browser maps, create a separate key restricted to the required browser APIs and Websites, including the actual preview and custom domains. Enter env:GOOGLE_MAPS_BROWSER_KEY in Browser API key reference and save that value separately in Env. The app exposes this browser key; never expose the server key.",
|
|
22
22
|
"Save configuration, enter Verification address, then Connect account. This performs one potentially billable geocoding request. It verifies only the server key, not browser maps. No OAuth registration or callback is needed.",
|
|
23
23
|
"Update Env when replacing a key. Disconnect removes local connection state; delete or restrict the key in Google Cloud to revoke provider access. Check existing usage before deleting a key used by other applications."
|
|
24
24
|
]
|
package/src/shared/google.js
CHANGED
|
@@ -84,7 +84,7 @@ const googleSearchConsoleDefinition = definition("google-search-console", "Googl
|
|
|
84
84
|
"The connecting Google account needs access to the intended Search Console property. A property owner can open that property, choose Settings > Users and permissions > Add user, enter the account email, choose the appropriate role and save.",
|
|
85
85
|
"To add a property, open Search Console > property selector > Add property. Domain properties require your DNS provider's TXT record; URL-prefix properties offer verification methods such as an HTML file or tag. Follow Google's displayed instructions and Verify, or have an existing owner add this account. An OAuth grant does not verify domain ownership.",
|
|
86
86
|
"Search performance uses finalized Pacific-time dates and top rows, not a complete traffic export. URL inspection reports Google's indexed snapshot, not a live page test or request for indexing. Use the exact property identifier returned by the connection.",
|
|
87
|
-
"To submit or remove sitemaps, select Manage Search Console properties (webmasters), add the scope in Data Access and reconnect. The app must
|
|
87
|
+
"To submit or remove sitemaps, select Manage Search Console properties (webmasters), add the scope in Data Access and reconnect. The app must serve a real sitemap URL first. Submission does not create the file, guarantee indexing or change DNS; deletion removes its Search Console entry, not the hosted file.",
|
|
88
88
|
"Keep Read Search Console properties for the initial check, which lists accessible properties. Site Verification permissions alone do not authorize that check or create property access. An empty result can mean this Google account has no properties."
|
|
89
89
|
]);
|
|
90
90
|
const googleAnalyticsDefinition = Object.freeze({
|
|
@@ -98,7 +98,7 @@ const googleAnalyticsDefinition = Object.freeze({
|
|
|
98
98
|
validator: (value) => /^G-[A-Z0-9]+$/u.test(value) || "Enter the GA4 web stream Measurement ID beginning G-."
|
|
99
99
|
} }),
|
|
100
100
|
settingsFields: [{ name: "measurementId", label: "Measurement ID", required: true,
|
|
101
|
-
hint: "Public: this value will be visible in your
|
|
101
|
+
hint: "Public: this value will be visible in your deployed application. Copy it from your GA4 web stream." }],
|
|
102
102
|
setup: {
|
|
103
103
|
url: "https://support.google.com/analytics/answer/9304153",
|
|
104
104
|
steps: [
|
package/src/shared/logo-dev.js
CHANGED
|
@@ -3,11 +3,11 @@ const logoDevDefinition = Object.freeze({
|
|
|
3
3
|
accountModes: ["shared", "assistant"], authenticationMethods: ["api-key"], scopes: [],
|
|
4
4
|
runtimeKind: "public-resource", configurationOnly: true,
|
|
5
5
|
apiKeyReferenceLabel: "Publishable key reference",
|
|
6
|
-
apiKeyReferenceHint: "Reference a pk_ key. Your application
|
|
6
|
+
apiKeyReferenceHint: "Reference a pk_ key. Your application exposes its value in image URLs. A private sk_ key cannot be used for this image flow.",
|
|
7
7
|
setup: {
|
|
8
8
|
url: "https://www.logo.dev/docs/platform/api-keys",
|
|
9
9
|
steps: [
|
|
10
|
-
"Sign into https://www.logo.dev/dashboard/api-keys and copy the publishable key beginning pk_. Do not copy the private sk_ key: this integration displays images, and its pk_ value will be visible in the
|
|
10
|
+
"Sign into https://www.logo.dev/dashboard/api-keys and copy the publishable key beginning pk_. Do not copy the private sk_ key: this integration displays images, and its pk_ value will be visible in the deployed application's image URLs.",
|
|
11
11
|
"Enter env:LOGO_DEV_PUBLISHABLE_KEY in Publishable key reference, then Save configuration. Choose Set credential in Env and store the actual pk_ value as LOGO_DEV_PUBLISHABLE_KEY. Return after saving Env. Your application must resolve this reference and expose only this publishable value to its image code.",
|
|
12
12
|
"In Logo.dev API Keys, enable Allowed Domains Only, enter allowed domains one per line, then Save Changes. Include the application's actual hosting/custom domain and intended preview domains. example.com matches that domain; *.example.com matches its subdomains. Add localhost explicitly if needed for local development.",
|
|
13
13
|
"Before enabling restrictions, ensure image requests send an origin referrer. Use referrerpolicy=origin on the image or an equivalent application policy; no-referrer and same-origin policies block cross-site referrers. Unlisted domains or missing referrers cause image failures.",
|
package/src/shared/mapbox.js
CHANGED
|
@@ -14,7 +14,7 @@ const mapboxDefinition = Object.freeze({
|
|
|
14
14
|
}),
|
|
15
15
|
settingsFields: [{ name: "usage", label: "Mapbox usage", credentialScope: value => value,
|
|
16
16
|
items: [{ title: "Backend geocoding and optional browser maps", value: "backend" }, { title: "Browser maps only", value: "browser" }] }, { name: "publicTokenRef", environmentCredential: { label: "Public browser token", public: true }, label: "Public browser token reference", placeholder: "env:MAPBOX_PUBLIC_TOKEN",
|
|
17
|
-
hint: "Required for browser-only mode; optional with backend geocoding. Separate pk. token for Mapbox in your browser. The application may
|
|
17
|
+
hint: "Required for browser-only mode; optional with backend geocoding. Separate pk. token for Mapbox in your browser. The application may expose this value; backend verification does not verify its restrictions." }],
|
|
18
18
|
setup: {
|
|
19
19
|
url: "https://docs.mapbox.com/accounts/guides/tokens/",
|
|
20
20
|
stepsForSettings(settings) { return settings?.usage === "browser" ? [
|
|
@@ -26,7 +26,7 @@ const mapboxDefinition = Object.freeze({
|
|
|
26
26
|
] : this.steps; },
|
|
27
27
|
steps: [
|
|
28
28
|
"Sign into Mapbox Developer Console → Access Tokens → Create a token. Name it for this application and environment. Select only the scopes your APIs need; geocoding can use a public pk. token. Copy a secret sk. token immediately: it is shown only once.",
|
|
29
|
-
"For backend geocoding, enter env:MAPBOX_BACKEND_TOKEN in Backend access token reference. Use a token without browser URL restrictions: backend requests have no browser referrer. Never
|
|
29
|
+
"For backend geocoding, enter env:MAPBOX_BACKEND_TOKEN in Backend access token reference. Use a token without browser URL restrictions: backend requests have no browser referrer. Never expose a secret sk. token in browser code.",
|
|
30
30
|
"For browser maps, create a separate public pk. token with the SDK’s map scopes, including styles:read and fonts:read. Enter env:MAPBOX_PUBLIC_TOKEN in Public browser token reference. The application must deliberately load this public value; this connector does not render a map.",
|
|
31
31
|
"Restrict the browser token to the deployed application URLs, adding preview, custom domains and localhost when needed. Create a new token rather than using the default public token, which cannot be URL-restricted. Wildcards are unsupported; blocking the Referer header can cause 403 errors.",
|
|
32
32
|
"Save configuration, then use Set credential in Env for MAPBOX_BACKEND_TOKEN and Open Env for MAPBOX_PUBLIC_TOKEN if supplied. Store token values there, not in reference fields. Connect account verifies only the backend token’s validity; it does not test geocoding entitlement or the optional browser token. No OAuth app or callback URL is needed.",
|
package/src/shared/tokens.js
CHANGED
|
@@ -655,7 +655,7 @@ const lexwareDefinition = definition("lexware", "Lexware", "Read accounting cont
|
|
|
655
655
|
"Draft invoices require an existing contact UUID, invoice and service/delivery dates with timezones, and custom lines with quantity, unit, net EUR amount and tax rate. Choose those accounting values in the app; the connector does not infer tax. Review and finalize drafts in Lexware. No email or payment is sent.",
|
|
656
656
|
"An interrupted invoice creation may already have succeeded. Check the voucher list before repeating it. Respect the provider limit of two requests per second across endpoints. The app must authorize each contact/document and approve writes before calling the connector.",
|
|
657
657
|
"This adapter does not create contacts/articles/vouchers, download PDFs, finalize invoices, upload receipts or receive accounting events. For example, it can prepare a draft booking invoice for an existing customer, but receipt intake and signed event processing need native app implementation.",
|
|
658
|
-
"No OAuth registration or callback URL is needed. Verification reads contacts. Keys expire after at most 24 months: create a replacement and update Env before expiry. Public API does not mean the key is safe to
|
|
658
|
+
"No OAuth registration or callback URL is needed. Verification reads contacts. Keys expire after at most 24 months: create a replacement and update Env before expiry. Public API does not mean the key is safe to expose."
|
|
659
659
|
]);
|
|
660
660
|
const sevdeskDefinition = Object.freeze({
|
|
661
661
|
...definition("sevdesk", "Sevdesk", "Read sevdesk contact organizations and people with offset pagination.",
|
|
@@ -683,7 +683,7 @@ const attentionDefinition = definition("attention", "Attention", "Read conversat
|
|
|
683
683
|
"Sign into app.attention.tech as an organization administrator. Open your profile avatar at the top left, Settings, then Organization > API Keys.",
|
|
684
684
|
"Choose + Create API Key at the top right and give it a name identifying this application. Copy the value in API Key Created before closing; it is shown once.",
|
|
685
685
|
"Enter env:ATTENTION_API_KEY in API key reference here and save. Choose Set credential in Env, paste the key as the ATTENTION_API_KEY value and save it there. Return here and choose Connect account (or Verify again when already connected). Check connection only reloads status.",
|
|
686
|
-
"An organization key can expose broad read/write and user-management access. Your application must restrict each operation to authorized users; never
|
|
686
|
+
"An organization key can expose broad read/write and user-management access. Your application must restrict each operation to authorized users; never expose this key in frontend code. New users require an explicit listener or recording seat choice; check your Attention plan before enabling provisioning.",
|
|
687
687
|
"No OAuth registration or callback URL is needed. Verification reads a conversation page without changing recordings or CRM data.",
|
|
688
688
|
"To replace access, create a new key and update Env, then verify before opening the old key's three-dot menu and choosing Delete. Deletion immediately stops every application still using that old key. Disconnect here removes the local grant only; revoke the key in Attention to stop provider access."
|
|
689
689
|
]);
|
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
|
});
|