@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 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
- ## Default and catalogue
25
+ ## Model selection and catalogue
26
26
 
27
- Import `aiCatalogue`, `listAiModels`, `getAiModel`, `aiAccessLabels` and
28
- `DEFAULT_AI_MODEL` from `@jskit-ai/connectors-catalog/shared/ai`.
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,615 model records.
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, with
53
- `opencode/big-pickle` first. `includeDeprecated: true` exposes retained upstream
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 seven active public choices are Big Pickle, MiMo-V2.5 Free,
67
- Ling3.0 FlashFin Free, Nemotron 3 Ultra Free, Nemotron 3.5 Lightning Free,
68
- Muse Spark 1.2 Contributor Free and Muse Spark 1.3 Contributor Free.
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 Big Pickle uses OpenAI-compatible chat;
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
- This is enough to select the no-setup default:
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": "none" },
101
- "settings": { "model": "opencode/big-pickle" }
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
- For an administrator-funded model, select `authentication.method: "api-key"`
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
- For the no-setup default: choose AI, keep Big Pickle selected and save. No
163
- provider-side action is needed. For API-key routes, follow the provider's current
164
- dashboard documentation linked in `aiCatalogue.providers[].documentationUrl`:
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/default classification, strict
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. The default is the snapshot's Zen Big Pickle no-account route; administrator keys resolve through Env, and individual keys through an app-supplied user/provider-scoped reference 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.61",
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.61",
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.244"
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, big-pickle, zen, zai, environment, api-key, ownership, cli
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. The default Big Pickle
16
- route needs no account, user key or environment setup.
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.key === DEFAULT_AI_MODEL ? -1 : b.key === DEFAULT_AI_MODEL ? 1 :
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. Big Pickle starts without an account or key.",
38
- accountModes: ["shared", "per-user"], authenticationMethods: ["none", "api-key"], scopes: [],
39
- authenticationMethodsForSettings: (settings = {}) => getAiModel(settings.model ?? DEFAULT_AI_MODEL)?.access === "free-no-setup"
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", defaultTo: DEFAULT_AI_MODEL,
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 ?? DEFAULT_AI_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
- "Big Pickle and other choices labelled no account or key need no registration. Save and use this model through your application's AI library.",
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, DEFAULT_AI_MODEL, getAiModel, listAiModels };
60
+ export { aiCatalogue, aiAccessLabels, aiDefinition, getAiModel, listAiModels };
@@ -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 publish its value." }
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 publishing the optional frontend key; keep the backend key server-side."
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 published app." },
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 publishes this browser key; never publish the server key.",
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
  ]
@@ -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 publish 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.",
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 published application. Copy it from your GA4 web stream." }],
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: [
@@ -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 publishes its value in image URLs. A private sk_ key cannot be used for this image flow.",
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 published application's image URLs.",
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.",
@@ -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 publish this value; backend verification does not verify its restrictions." }],
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 publish a secret sk. token in browser code.",
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.",
@@ -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 publish."
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 publish this key in frontend code. New users require an explicit listener or recording seat choice; check your Attention plan before enabling provisioning.",
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, DEFAULT_AI_MODEL, getAiModel, listAiModels } from "../src/shared/ai.js";
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, 7615);
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 and uses Big Pickle as the explicit default", () => {
53
- assert.equal(listAiModels()[0].key, DEFAULT_AI_MODEL);
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
- "big-pickle", "ling-3.0-flash-fin-free", "mimo-v2.5-free", "muse-spark-1.2-contributor-free",
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 JSON defaults agree with the form and preserve explicit free model choices", () => {
67
- const config = validate(configuration());
68
- assert.deepEqual(config.integrations.suggestions.settings, { model: "opencode/big-pickle" });
69
- assert.deepEqual(config.integrations.suggestions.authentication, { method: "none" });
70
- config.integrations.suggestions.settings.model = "opencode/mimo-v2.5-free";
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: "big-pickle",
95
- modelLimits: getAiModel(DEFAULT_AI_MODEL).limit,
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(), authorize }).resolve(request);
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://opencode.ai/zen/v1", id: "big-pickle" });
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
  });