@every-app/sdk 0.3.0-rc.4 → 0.3.0-rc.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/cloudflare/catalogSettings.generated.d.ts +80 -0
- package/dist/cloudflare/catalogSettings.generated.d.ts.map +1 -0
- package/dist/cloudflare/catalogSettings.generated.js +172 -0
- package/dist/cloudflare/index.d.ts +1 -0
- package/dist/cloudflare/index.d.ts.map +1 -1
- package/dist/cloudflare/index.js +3 -0
- package/dist/cloudflare/worker.d.ts +29 -98
- package/dist/cloudflare/worker.d.ts.map +1 -1
- package/dist/cloudflare/worker.js +45 -132
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/internal/aiDefault.d.ts +70 -7
- package/dist/internal/aiDefault.d.ts.map +1 -1
- package/dist/internal/aiDefault.js +145 -15
- package/dist/internal/aiProviders.d.ts +3 -1
- package/dist/internal/aiProviders.d.ts.map +1 -1
- package/dist/internal/aiProviders.js +12 -3
- package/dist/internal/appSettings.d.ts +65 -5
- package/dist/internal/appSettings.d.ts.map +1 -1
- package/dist/internal/appSettings.js +141 -23
- package/dist/internal/capabilities.d.ts +52 -433
- package/dist/internal/capabilities.d.ts.map +1 -1
- package/dist/internal/capabilities.js +25 -558
- package/dist/internal/catalog/index.d.ts +6 -6
- package/dist/internal/catalog/inference.d.ts +6 -6
- package/dist/internal/catalog/inference.d.ts.map +1 -1
- package/dist/internal/catalog/inference.js +9 -6
- package/dist/internal/events.d.ts.map +1 -1
- package/dist/internal/events.js +1 -0
- package/dist/internal/gatewaySettings.d.ts +1 -1
- package/dist/internal/gatewaySettings.d.ts.map +1 -1
- package/dist/internal/gatewaySettings.js +1 -3
- package/dist/internal/identity.d.ts +12 -1
- package/dist/internal/identity.d.ts.map +1 -1
- package/dist/internal/identity.js +11 -1
- package/dist/internal/oauthClient.d.ts +10 -12
- package/dist/internal/oauthClient.d.ts.map +1 -1
- package/dist/internal/oauthClient.js +20 -81
- package/dist/internal/oauthPresets.d.ts +7 -3
- package/dist/internal/oauthPresets.d.ts.map +1 -1
- package/dist/internal/oauthPresets.js +533 -3
- package/dist/internal/oauthSpec.d.ts +44 -1
- package/dist/internal/oauthSpec.d.ts.map +1 -1
- package/dist/internal/oauthSpec.js +147 -34
- package/dist/internal/protocol.d.ts +14 -0
- package/dist/internal/protocol.d.ts.map +1 -1
- package/dist/internal/protocol.js +7 -0
- package/dist/internal/providerRegistry.d.ts +61 -1
- package/dist/internal/providerRegistry.d.ts.map +1 -1
- package/dist/internal/providerRegistry.js +127 -6
- package/dist/internal/providerSpec.d.ts +14 -7
- package/dist/internal/providerSpec.d.ts.map +1 -1
- package/dist/internal/providerSpec.js +306 -7
- package/dist/manifest.d.ts +226 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +30 -0
- package/dist/server/ai.d.ts +66 -0
- package/dist/server/ai.d.ts.map +1 -1
- package/dist/server/ai.js +113 -8
- package/dist/server/aiBinding.d.ts +68 -0
- package/dist/server/aiBinding.d.ts.map +1 -0
- package/dist/server/aiBinding.js +294 -0
- package/dist/server/authorizeUrl.d.ts +1 -1
- package/dist/server/authorizeUrl.d.ts.map +1 -1
- package/dist/server/authorizeUrl.js +2 -6
- package/dist/server/emailBinding.d.ts +72 -0
- package/dist/server/emailBinding.d.ts.map +1 -0
- package/dist/server/emailBinding.js +124 -0
- package/dist/server/emit.js +1 -1
- package/dist/server/env.d.ts +41 -0
- package/dist/server/env.d.ts.map +1 -0
- package/dist/server/env.js +1 -0
- package/dist/server/gatewayFetch.d.ts +17 -13
- package/dist/server/gatewayFetch.d.ts.map +1 -1
- package/dist/server/gatewayFetch.js +24 -14
- package/dist/server/gatewayUrl.d.ts +13 -0
- package/dist/server/gatewayUrl.d.ts.map +1 -0
- package/dist/server/gatewayUrl.js +34 -0
- package/dist/server/index.d.ts +10 -3
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +8 -2
- package/dist/server/integrationFetch.d.ts +16 -32
- package/dist/server/integrationFetch.d.ts.map +1 -1
- package/dist/server/integrationFetch.js +47 -65
- package/dist/server/listMembers.js +1 -1
- package/dist/server/refusal.d.ts +55 -0
- package/dist/server/refusal.d.ts.map +1 -0
- package/dist/server/refusal.js +193 -0
- package/dist/server/sendEmail.js +1 -1
- package/dist/tanstack-start/index.d.ts +9 -0
- package/dist/tanstack-start/index.d.ts.map +1 -0
- package/dist/tanstack-start/index.js +29 -0
- package/dist/testing/index.d.ts +93 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +156 -0
- package/package.json +34 -12
|
@@ -3,6 +3,10 @@ import fs from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { parseEnv } from "node:util";
|
|
5
5
|
import { DEV_ISSUER, DEV_ORG_ID, DEV_PUBLIC_KEY_PEM } from "../dev/keypair.js";
|
|
6
|
+
// The catalog's settings, generated from ../internal/capabilities.ts, which
|
|
7
|
+
// this file must not import (see ./index.ts: the Vite plugin hides whatever
|
|
8
|
+
// cloudflare.config.ts imports from the Worker).
|
|
9
|
+
import { DEV_GATEWAY_KEYS } from "./catalogSettings.generated.js";
|
|
6
10
|
import { sentinelDeployTarget, } from "./sentinel.js";
|
|
7
11
|
/** The names of the manifest's `secrets`, whichever form each took. */
|
|
8
12
|
function manifestSecretNames(manifest) {
|
|
@@ -12,131 +16,6 @@ function manifestSecretNames(manifest) {
|
|
|
12
16
|
function manifestProviderIds(manifest) {
|
|
13
17
|
return (manifest.providers ?? []).map((entry) => typeof entry === "string" ? entry : entry.id);
|
|
14
18
|
}
|
|
15
|
-
/**
|
|
16
|
-
* The gateway secrets holding the keys of the AI providers and capabilities
|
|
17
|
-
* an app may ask for, with the settings a provider needs besides its key
|
|
18
|
-
* (Workers AI's account, a Basic username held as a setting, a host
|
|
19
|
-
* setting). With no gateway in development, the SDK reaches them with the
|
|
20
|
-
* developer's own values under the same names. A copy of the catalog's
|
|
21
|
-
* settings (`capabilitySettings()` over every entry of
|
|
22
|
-
* ../internal/capabilities.ts), which the Worker loads and this file must
|
|
23
|
-
* not import (see ./index.ts: the Vite plugin hides whatever
|
|
24
|
-
* cloudflare.config.ts imports from the Worker). worker.test.ts keeps the
|
|
25
|
-
* copy in step: a provider added to the catalog is added here.
|
|
26
|
-
*/
|
|
27
|
-
const DEV_GATEWAY_KEYS = {
|
|
28
|
-
providers: {
|
|
29
|
-
openai: ["OPENAI_API_KEY"],
|
|
30
|
-
openrouter: ["OPENROUTER_API_KEY"],
|
|
31
|
-
"workers-ai": ["WORKERS_AI_API_TOKEN", "CLOUDFLARE_ACCOUNT_ID"],
|
|
32
|
-
// Not Cloudflare AI Gateway (the proxy entry): dev goes to the
|
|
33
|
-
// providers directly, so its settings are the Gateway's alone.
|
|
34
|
-
// The AI providers of ../internal/catalog/inference.ts.
|
|
35
|
-
anthropic: ["ANTHROPIC_API_KEY"],
|
|
36
|
-
gemini: ["GEMINI_API_KEY"],
|
|
37
|
-
mistral: ["MISTRAL_API_KEY"],
|
|
38
|
-
groq: ["GROQ_API_KEY"],
|
|
39
|
-
cohere: ["COHERE_API_KEY"],
|
|
40
|
-
},
|
|
41
|
-
capabilities: {
|
|
42
|
-
dataforseo: ["DATAFORSEO_API_KEY"],
|
|
43
|
-
context: ["CONTEXT_API_KEY"],
|
|
44
|
-
// The sender, and the developer's own Email Sending account: set all
|
|
45
|
-
// but the name and sendEmail() really sends; otherwise it prints.
|
|
46
|
-
// Resend's key and EMAIL_PROVIDER pick the provider integrationStatus()
|
|
47
|
-
// names, as the gateway would.
|
|
48
|
-
// TODO(integration): the last two added by the email-providers lane
|
|
49
|
-
// (this file is outside its ownership list).
|
|
50
|
-
email: [
|
|
51
|
-
"EMAIL_FROM",
|
|
52
|
-
"EMAIL_FROM_NAME",
|
|
53
|
-
"EMAIL_REST_API_TOKEN",
|
|
54
|
-
"CLOUDFLARE_ACCOUNT_ID",
|
|
55
|
-
"RESEND_API_KEY",
|
|
56
|
-
"EMAIL_PROVIDER",
|
|
57
|
-
],
|
|
58
|
-
// The organization's members: nothing to set; listMembers() answers
|
|
59
|
-
// from the dev identity.
|
|
60
|
-
members: [],
|
|
61
|
-
// OAuth clients: the id, the secret, and the instance a provider with
|
|
62
|
-
// no fixed host is on.
|
|
63
|
-
linkedin: ["LINKEDIN_CLIENT_ID", "LINKEDIN_CLIENT_SECRET"],
|
|
64
|
-
facebook: ["FACEBOOK_APP_ID", "FACEBOOK_APP_SECRET"],
|
|
65
|
-
instagram: ["INSTAGRAM_APP_ID", "INSTAGRAM_APP_SECRET"],
|
|
66
|
-
threads: ["THREADS_APP_ID", "THREADS_APP_SECRET"],
|
|
67
|
-
youtube: ["YOUTUBE_CLIENT_ID", "YOUTUBE_CLIENT_SECRET"],
|
|
68
|
-
tiktok: ["TIKTOK_CLIENT_ID", "TIKTOK_CLIENT_SECRET"],
|
|
69
|
-
"tiktok-business": [
|
|
70
|
-
"TIKTOK_BUSINESS_CLIENT_ID",
|
|
71
|
-
"TIKTOK_BUSINESS_CLIENT_SECRET",
|
|
72
|
-
],
|
|
73
|
-
reddit: ["REDDIT_CLIENT_ID", "REDDIT_CLIENT_SECRET"],
|
|
74
|
-
pinterest: ["PINTEREST_CLIENT_ID", "PINTEREST_CLIENT_SECRET"],
|
|
75
|
-
discord: ["DISCORD_CLIENT_ID", "DISCORD_CLIENT_SECRET"],
|
|
76
|
-
slack: ["SLACK_CLIENT_ID", "SLACK_CLIENT_SECRET"],
|
|
77
|
-
mastodon: ["MASTODON_CLIENT_ID", "MASTODON_CLIENT_SECRET", "MASTODON_URL"],
|
|
78
|
-
kick: ["KICK_CLIENT_ID", "KICK_CLIENT_SECRET"],
|
|
79
|
-
twitch: ["TWITCH_CLIENT_ID", "TWITCH_CLIENT_SECRET"],
|
|
80
|
-
tumblr: ["TUMBLR_CLIENT_ID", "TUMBLR_CLIENT_SECRET"],
|
|
81
|
-
dribbble: ["DRIBBBLE_CLIENT_ID", "DRIBBBLE_CLIENT_SECRET"],
|
|
82
|
-
"google-workspace": [
|
|
83
|
-
"GOOGLE_WORKSPACE_CLIENT_ID",
|
|
84
|
-
"GOOGLE_WORKSPACE_CLIENT_SECRET",
|
|
85
|
-
],
|
|
86
|
-
"microsoft-365": ["MICROSOFT_365_CLIENT_ID", "MICROSOFT_365_CLIENT_SECRET"],
|
|
87
|
-
// The API-key providers of ../internal/catalog/*.ts, in catalog order:
|
|
88
|
-
// the key, then a Basic username held as a setting, then the host
|
|
89
|
-
// setting of a provider whose host is the operator's.
|
|
90
|
-
stripe: ["STRIPE_API_KEY"],
|
|
91
|
-
lemonsqueezy: ["LEMONSQUEEZY_API_KEY"],
|
|
92
|
-
paddle: ["PADDLE_API_KEY"],
|
|
93
|
-
shopify: ["SHOPIFY_API_TOKEN", "SHOPIFY_HOST"],
|
|
94
|
-
plaid: ["PLAID_SECRET"],
|
|
95
|
-
attio: ["ATTIO_API_KEY"],
|
|
96
|
-
hubspot: ["HUBSPOT_API_TOKEN"],
|
|
97
|
-
pipedrive: ["PIPEDRIVE_API_TOKEN"],
|
|
98
|
-
airtable: ["AIRTABLE_API_TOKEN"],
|
|
99
|
-
notion: ["NOTION_API_TOKEN"],
|
|
100
|
-
postmark: ["POSTMARK_API_TOKEN"],
|
|
101
|
-
sendgrid: ["SENDGRID_API_KEY"],
|
|
102
|
-
twilio: ["TWILIO_API_SECRET", "TWILIO_API_KEY"],
|
|
103
|
-
intercom: ["INTERCOM_API_TOKEN"],
|
|
104
|
-
crisp: ["CRISP_API_KEY", "CRISP_API_IDENTIFIER"],
|
|
105
|
-
linear: ["LINEAR_API_KEY"],
|
|
106
|
-
github: ["GITHUB_API_TOKEN"],
|
|
107
|
-
gitlab: ["GITLAB_API_TOKEN"],
|
|
108
|
-
vercel: ["VERCEL_API_TOKEN"],
|
|
109
|
-
cloudflare: ["CLOUDFLARE_PROVIDER_API_TOKEN"],
|
|
110
|
-
posthog: ["POSTHOG_PERSONAL_API_KEY"],
|
|
111
|
-
mixpanel: [
|
|
112
|
-
"MIXPANEL_SERVICE_ACCOUNT_SECRET",
|
|
113
|
-
"MIXPANEL_SERVICE_ACCOUNT_USERNAME",
|
|
114
|
-
],
|
|
115
|
-
segment: ["SEGMENT_WRITE_KEY"],
|
|
116
|
-
supabase: ["SUPABASE_SECRET_KEY", "SUPABASE_HOST"],
|
|
117
|
-
turso: ["TURSO_API_TOKEN"],
|
|
118
|
-
"turso-db": ["TURSO_DB_AUTH_TOKEN", "TURSO_DB_HOST"],
|
|
119
|
-
neon: ["NEON_API_KEY"],
|
|
120
|
-
pinecone: ["PINECONE_API_KEY", "PINECONE_HOST"],
|
|
121
|
-
mapbox: ["MAPBOX_ACCESS_TOKEN"],
|
|
122
|
-
openweather: ["OPENWEATHER_API_KEY"],
|
|
123
|
-
voyage: ["VOYAGE_API_KEY"],
|
|
124
|
-
perplexity: ["PERPLEXITY_API_KEY"],
|
|
125
|
-
jina: ["JINA_API_KEY"],
|
|
126
|
-
replicate: ["REPLICATE_API_TOKEN"],
|
|
127
|
-
huggingface: ["HUGGINGFACE_API_TOKEN"],
|
|
128
|
-
firecrawl: ["FIRECRAWL_API_KEY"],
|
|
129
|
-
exa: ["EXA_API_KEY"],
|
|
130
|
-
tavily: ["TAVILY_API_KEY"],
|
|
131
|
-
serper: ["SERPER_API_KEY"],
|
|
132
|
-
"brave-search": ["BRAVE_SEARCH_API_KEY"],
|
|
133
|
-
apify: ["APIFY_API_TOKEN"],
|
|
134
|
-
deepgram: ["DEEPGRAM_API_KEY"],
|
|
135
|
-
elevenlabs: ["ELEVENLABS_API_KEY"],
|
|
136
|
-
assemblyai: ["ASSEMBLYAI_API_KEY"],
|
|
137
|
-
newsapi: ["NEWSAPI_API_KEY"],
|
|
138
|
-
},
|
|
139
|
-
};
|
|
140
19
|
/**
|
|
141
20
|
* Observability every Every App Worker ships with: logs (with invocation
|
|
142
21
|
* logs), sampled traces and Workers Issues, with query strings redacted so
|
|
@@ -166,6 +45,19 @@ const BUILD_ENV = "EVERYAPP_BUILD";
|
|
|
166
45
|
const RESERVED_SECRET_NAME = /^(GATEWAY_URL|CF_VERSION_METADATA|EVERYAPP_\w*|EVERY_APP_\w*|APP__\w*)$/i;
|
|
167
46
|
const GATEWAY_WORKER_NAME = "every-app-gateway";
|
|
168
47
|
const GATEWAY_ENTRYPOINT = "AppGateway";
|
|
48
|
+
/**
|
|
49
|
+
* Where `everyapp dev` serves its launcher, which it passes to the app's dev
|
|
50
|
+
* server in EVERYAPP_DEV_GATEWAY_URL; its default address otherwise. The
|
|
51
|
+
* deployed Worker gets the Gateway's URL from the deploy instead.
|
|
52
|
+
*/
|
|
53
|
+
const DEV_GATEWAY_URL_ENV = "EVERYAPP_DEV_GATEWAY_URL";
|
|
54
|
+
const DEFAULT_DEV_GATEWAY_URL = "http://localhost:8787";
|
|
55
|
+
function devGatewayUrl() {
|
|
56
|
+
return {
|
|
57
|
+
type: "text",
|
|
58
|
+
value: process.env[DEV_GATEWAY_URL_ENV] || DEFAULT_DEV_GATEWAY_URL,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
169
61
|
/**
|
|
170
62
|
* The manifest's `capabilities` as the SDK's local-dev twin of the gateway
|
|
171
63
|
* reads them: with no gateway to hold an OAuth client's calls to the scope
|
|
@@ -177,7 +69,7 @@ function devCapabilities(manifest) {
|
|
|
177
69
|
/**
|
|
178
70
|
* The binding the dev Worker gets the specs of providers registered on a
|
|
179
71
|
* gateway in (`DEV_PROVIDERS_ENV` in ../internal/devProviders.ts; a copy,
|
|
180
|
-
* kept in step by a test,
|
|
72
|
+
* kept in step by a test, since this file may not import ../internal).
|
|
181
73
|
*/
|
|
182
74
|
const DEV_PROVIDERS_ENV = "EVERYAPP_DEV_PROVIDERS";
|
|
183
75
|
/**
|
|
@@ -291,6 +183,12 @@ export function everyAppWorker(options) {
|
|
|
291
183
|
const { deploy, name } = appWorkerName(options, "everyAppWorker");
|
|
292
184
|
const env = {
|
|
293
185
|
EVERYAPP_APP_ID: { type: "text", value: manifest.id },
|
|
186
|
+
// The manifest itself, for a page that shows what the app asked for:
|
|
187
|
+
// the Worker can't import everyapp.config.ts (see ./index.ts).
|
|
188
|
+
EVERYAPP_MANIFEST: {
|
|
189
|
+
type: "json",
|
|
190
|
+
value: manifest,
|
|
191
|
+
},
|
|
294
192
|
CF_VERSION_METADATA: { type: "version-metadata" },
|
|
295
193
|
};
|
|
296
194
|
if (deploy) {
|
|
@@ -317,6 +215,7 @@ export function everyAppWorker(options) {
|
|
|
317
215
|
value: JSON.stringify([DEV_PUBLIC_KEY_PEM]),
|
|
318
216
|
};
|
|
319
217
|
env["EVERYAPP_IDENTITY_ISSUER"] = { type: "text", value: DEV_ISSUER };
|
|
218
|
+
env["GATEWAY_URL"] = devGatewayUrl();
|
|
320
219
|
// The stub gateway's organization, as the deploy's secret would be, so
|
|
321
220
|
// background work's withOrganization(env.EVERY_APP_ORG_ID, …) runs
|
|
322
221
|
// here too. Not a pin: under the dev opt-in everyApp() enforces none,
|
|
@@ -436,6 +335,7 @@ export function everyAppAuxiliaryWorker(options) {
|
|
|
436
335
|
else {
|
|
437
336
|
env["EVERYAPP_DEV"] = { type: "text", value: "1" };
|
|
438
337
|
env["EVERYAPP_DEV_CAPABILITIES"] = devCapabilities(manifest);
|
|
338
|
+
env["GATEWAY_URL"] = devGatewayUrl();
|
|
439
339
|
env["EVERY_APP_ORG_ID"] = { type: "text", value: DEV_ORG_ID };
|
|
440
340
|
addDevProviders(env, options.root);
|
|
441
341
|
}
|
|
@@ -905,18 +805,25 @@ taken) {
|
|
|
905
805
|
return { bindings, exports };
|
|
906
806
|
}
|
|
907
807
|
/**
|
|
908
|
-
* Check the names of the manifest's `secrets`, and in dev
|
|
909
|
-
* set locally
|
|
910
|
-
* and
|
|
808
|
+
* Check the names of the manifest's `secrets` and `settings`, and in dev
|
|
809
|
+
* declare the ones set locally (a setting that isn't, with its default),
|
|
810
|
+
* with the developer's own keys for the manifest's providers and
|
|
811
|
+
* capabilities.
|
|
911
812
|
*/
|
|
912
813
|
function addSecrets(env, options, deploy) {
|
|
913
|
-
const
|
|
814
|
+
const settings = options.manifest.settings ?? [];
|
|
815
|
+
const secrets = [
|
|
816
|
+
...manifestSecretNames(options.manifest),
|
|
817
|
+
...settings.map((setting) => setting.name),
|
|
818
|
+
];
|
|
819
|
+
const settingNames = new Set(settings.map((setting) => setting.name));
|
|
914
820
|
for (const secret of secrets) {
|
|
821
|
+
const field = settingNames.has(secret) ? "settings" : "secrets";
|
|
915
822
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(secret)) {
|
|
916
|
-
throw new Error(
|
|
823
|
+
throw new Error(`${field}: "${secret}" isn't a valid binding name.`);
|
|
917
824
|
}
|
|
918
825
|
if (env[secret] || RESERVED_SECRET_NAME.test(secret)) {
|
|
919
|
-
throw new Error(
|
|
826
|
+
throw new Error(`${field}: ${secret} is a name the deploy sets or a binding it builds.`);
|
|
920
827
|
}
|
|
921
828
|
}
|
|
922
829
|
if (!deploy) {
|
|
@@ -933,6 +840,12 @@ function addSecrets(env, options, deploy) {
|
|
|
933
840
|
for (const secret of locallySetSecrets(options.root ?? process.cwd(), options.ctx.mode ?? "development", [...names].filter((name) => !env[name]))) {
|
|
934
841
|
env[secret] = { type: "secret" };
|
|
935
842
|
}
|
|
843
|
+
// A setting nobody set locally reads its default, as a deployed one does.
|
|
844
|
+
for (const setting of settings) {
|
|
845
|
+
if (!env[setting.name] && setting.default !== undefined) {
|
|
846
|
+
env[setting.name] = { type: "text", value: setting.default };
|
|
847
|
+
}
|
|
848
|
+
}
|
|
936
849
|
}
|
|
937
850
|
}
|
|
938
851
|
/** The {@link DEV_GATEWAY_KEYS} of the manifest's providers and capabilities. */
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @every-app/sdk — what `everyapp.config.ts` imports: `defineEveryApp` and
|
|
3
|
+
* the manifest's types. A leaf, like the config itself (see ./manifest.ts).
|
|
4
|
+
*/
|
|
5
|
+
export { defineEveryApp, type EveryAppCapabilityEntry, type EveryAppClassResource, type EveryAppManifest, type EveryAppPublicRoute, type EveryAppQueueConsumer, type EveryAppResources, type EveryAppSecretEntry, type EveryAppSettingEntry, type EveryAppSettingNeed, type EveryAppWorkerLimits, } from "./manifest.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EACL,cAAc,EACd,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,GAC1B,MAAM,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -15,6 +15,11 @@
|
|
|
15
15
|
* and fronts whichever provider the default resolves to when
|
|
16
16
|
* `AI_GATEWAY_ID` is set (./capabilities.ts, `aig`).
|
|
17
17
|
*
|
|
18
|
+
* The models resolve the same way: the operator's model setting for the
|
|
19
|
+
* provider (`everyapp ai model <provider> --chat … --embeddings …`,
|
|
20
|
+
* `org_settings.ai_models`), else the catalog entry's seed (`chat.seedModel`,
|
|
21
|
+
* `embeddings.seedModel`). Nothing reads a seed except through here.
|
|
22
|
+
*
|
|
18
23
|
* Everything here is pure: what is set is asked of the caller (`isSet`),
|
|
19
24
|
* which reads the Gateway's env, the Worker's secret names, or `.dev.vars`.
|
|
20
25
|
*/
|
|
@@ -38,19 +43,54 @@ export declare const AI_DEFAULT_ROUTES: {
|
|
|
38
43
|
readonly embeddings: "/v1/embeddings";
|
|
39
44
|
};
|
|
40
45
|
export type AiDefaultRoute = keyof typeof AI_DEFAULT_ROUTES;
|
|
41
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* A choice as typed (`everyapp ai default openrouter/anthropic/claude-sonnet-5.5`,
|
|
48
|
+
* the Usage page's dialog, `everyapp_ai_default`): the provider, and the
|
|
49
|
+
* chat model to store for it. The provider goes to `org_settings.ai_default`,
|
|
50
|
+
* the model to that provider's `chat` in `org_settings.ai_models`.
|
|
51
|
+
*/
|
|
42
52
|
export interface AiDefaultChoice {
|
|
43
53
|
provider: string;
|
|
44
54
|
model?: string;
|
|
45
55
|
}
|
|
56
|
+
/** The operator's models for one provider; a field left out serves the seed. */
|
|
57
|
+
export interface AiModelSetting {
|
|
58
|
+
chat?: string;
|
|
59
|
+
embeddings?: string;
|
|
60
|
+
}
|
|
61
|
+
/** The operator's models by provider id (`org_settings.ai_models`, JSON). */
|
|
62
|
+
export type AiModelSettings = Readonly<Record<string, AiModelSetting>>;
|
|
63
|
+
/** A change to one provider's models: a name sets it, null puts the seed back, absent leaves it. */
|
|
64
|
+
export interface AiModelPatch {
|
|
65
|
+
chat?: string | null;
|
|
66
|
+
embeddings?: string | null;
|
|
67
|
+
}
|
|
68
|
+
/** Whether `value` can be a model id: 1 to 200 characters, no surrounding or control whitespace. */
|
|
69
|
+
export declare function isAiModelName(value: string): boolean;
|
|
46
70
|
/**
|
|
47
|
-
* `
|
|
71
|
+
* `org_settings.ai_models` as stored: a JSON object of provider id →
|
|
72
|
+
* `{ chat?, embeddings? }`. Anything else (null, malformed JSON, a field
|
|
73
|
+
* that isn't a model name) reads as no setting, so the seed serves.
|
|
74
|
+
*/
|
|
75
|
+
export declare function parseAiModelSettings(value: string | null | undefined): AiModelSettings;
|
|
76
|
+
/** The stored text of the settings, providers sorted; null when nothing is set. */
|
|
77
|
+
export declare function formatAiModelSettings(settings: AiModelSettings): string | null;
|
|
78
|
+
/** `settings` with one provider's models changed by `patch`. */
|
|
79
|
+
export declare function withAiModelSetting(settings: AiModelSettings, provider: string, patch: AiModelPatch): AiModelSettings;
|
|
80
|
+
/**
|
|
81
|
+
* `openrouter` or `openrouter/anthropic/claude-haiku-5.5` as typed:
|
|
48
82
|
* the provider id up to the first slash, the model after it (a model id
|
|
49
83
|
* may hold slashes of its own). Null for blank or malformed text.
|
|
50
84
|
*/
|
|
51
85
|
export declare function parseAiDefaultChoice(value: string | null | undefined): AiDefaultChoice | null;
|
|
52
|
-
/** The
|
|
86
|
+
/** The text of a choice: `provider` or `provider/model`. */
|
|
53
87
|
export declare function formatAiDefaultChoice(choice: AiDefaultChoice): string;
|
|
88
|
+
/**
|
|
89
|
+
* The provider id `org_settings.ai_default` holds, or null. The column
|
|
90
|
+
* holds the provider alone (its models are in `ai_models`); text after a
|
|
91
|
+
* first slash is not read.
|
|
92
|
+
*/
|
|
93
|
+
export declare function parseAiDefaultProvider(value: string | null | undefined): string | null;
|
|
54
94
|
/** Whether `name` holds a value, as the Gateway's env or a secret list says. */
|
|
55
95
|
export type IsSet = (name: string) => boolean;
|
|
56
96
|
/** An `ai` entry the default may resolve to: not the proxy, with a chat route. */
|
|
@@ -77,8 +117,15 @@ export interface AiDefaultCandidate {
|
|
|
77
117
|
/** The Gateway settings it needs; what to set when it isn't configured. */
|
|
78
118
|
settings: string[];
|
|
79
119
|
configured: boolean;
|
|
80
|
-
/** The model the Gateway fills in when the app names none. */
|
|
120
|
+
/** The chat model the Gateway fills in when the app names none: the operator's setting, else the seed. */
|
|
81
121
|
model: string;
|
|
122
|
+
/** The embeddings model, the same way; absent when the entry has no embeddings route. */
|
|
123
|
+
embeddingsModel?: string;
|
|
124
|
+
/** The catalog's seeds: what serves where the operator set no model. */
|
|
125
|
+
seed: {
|
|
126
|
+
chat: string;
|
|
127
|
+
embeddings?: string;
|
|
128
|
+
};
|
|
82
129
|
/** Through Cloudflare AI Gateway, as things are set now. */
|
|
83
130
|
viaAiGateway: boolean;
|
|
84
131
|
embeddings: boolean;
|
|
@@ -94,8 +141,15 @@ export type AiDefaultSource =
|
|
|
94
141
|
export interface AiDefaultResolution {
|
|
95
142
|
provider: string;
|
|
96
143
|
label: string;
|
|
97
|
-
/** The model the default serves: the
|
|
144
|
+
/** The chat model the default serves: the operator's setting for the provider, else the seed. */
|
|
98
145
|
model: string;
|
|
146
|
+
/** The embeddings model, the same way; absent when the provider has no embeddings route. */
|
|
147
|
+
embeddingsModel?: string;
|
|
148
|
+
/** The provider's seeds. */
|
|
149
|
+
seed: {
|
|
150
|
+
chat: string;
|
|
151
|
+
embeddings?: string;
|
|
152
|
+
};
|
|
99
153
|
source: AiDefaultSource;
|
|
100
154
|
/** Every setting the provider needs is set, so the default serves. */
|
|
101
155
|
configured: boolean;
|
|
@@ -118,9 +172,15 @@ export interface AiDefaultResolution {
|
|
|
118
172
|
* then any other `ai` entry of `lookup` with a chat route (a registered
|
|
119
173
|
* row, one day), each with its state.
|
|
120
174
|
*/
|
|
121
|
-
export declare function aiDefaultCandidates(isSet: IsSet, lookup?: CapabilityLookup): AiDefaultCandidate[];
|
|
175
|
+
export declare function aiDefaultCandidates(isSet: IsSet, lookup?: CapabilityLookup, models?: AiModelSettings): AiDefaultCandidate[];
|
|
122
176
|
/** Why `choice.provider` can't be the default, or null when it can. */
|
|
123
177
|
export declare function aiDefaultChoiceProblem(choice: AiDefaultChoice, lookup?: CapabilityLookup): string | null;
|
|
178
|
+
/**
|
|
179
|
+
* Why `patch` can't be stored for `provider`, or null when it can: the
|
|
180
|
+
* provider must be one the default can be, a model name must be a model
|
|
181
|
+
* name, and an embeddings model needs the entry's embeddings route.
|
|
182
|
+
*/
|
|
183
|
+
export declare function aiModelPatchProblem(provider: string, patch: AiModelPatch, lookup?: CapabilityLookup): string | null;
|
|
124
184
|
/**
|
|
125
185
|
* The Gateway's effective default, from the stored choice (or none) and
|
|
126
186
|
* what is set. A choice that names a provider which isn't configured
|
|
@@ -129,7 +189,10 @@ export declare function aiDefaultChoiceProblem(choice: AiDefaultChoice, lookup?:
|
|
|
129
189
|
* `problem` and the order decides.
|
|
130
190
|
*/
|
|
131
191
|
export declare function resolveAiDefault(input: {
|
|
132
|
-
|
|
192
|
+
/** The stored provider (`org_settings.ai_default`), or null for the shipped order. */
|
|
193
|
+
provider: string | null;
|
|
194
|
+
/** The operator's models by provider (`org_settings.ai_models`); none: the seeds serve. */
|
|
195
|
+
models?: AiModelSettings;
|
|
133
196
|
isSet: IsSet;
|
|
134
197
|
lookup?: CapabilityLookup;
|
|
135
198
|
}): AiDefaultResolution;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aiDefault.d.ts","sourceRoot":"","sources":["../../src/internal/aiDefault.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"aiDefault.d.ts","sourceRoot":"","sources":["../../src/internal/aiDefault.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,6 +15,11 @@
|
|
|
15
15
|
* and fronts whichever provider the default resolves to when
|
|
16
16
|
* `AI_GATEWAY_ID` is set (./capabilities.ts, `aig`).
|
|
17
17
|
*
|
|
18
|
+
* The models resolve the same way: the operator's model setting for the
|
|
19
|
+
* provider (`everyapp ai model <provider> --chat … --embeddings …`,
|
|
20
|
+
* `org_settings.ai_models`), else the catalog entry's seed (`chat.seedModel`,
|
|
21
|
+
* `embeddings.seedModel`). Nothing reads a seed except through here.
|
|
22
|
+
*
|
|
18
23
|
* Everything here is pure: what is set is asked of the caller (`isSet`),
|
|
19
24
|
* which reads the Gateway's env, the Worker's secret names, or `.dev.vars`.
|
|
20
25
|
*/
|
|
@@ -48,10 +53,85 @@ export const AI_DEFAULT_ROUTES = {
|
|
|
48
53
|
chat: "/v1/chat/completions",
|
|
49
54
|
embeddings: "/v1/embeddings",
|
|
50
55
|
};
|
|
51
|
-
/**
|
|
56
|
+
/** Whether `value` can be a model id: 1 to 200 characters, no surrounding or control whitespace. */
|
|
57
|
+
export function isAiModelName(value) {
|
|
58
|
+
return (value.length >= 1 &&
|
|
59
|
+
value.length <= 200 &&
|
|
60
|
+
value.trim() === value &&
|
|
61
|
+
!/[\u0000-\u001f\u007f]/.test(value));
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* `org_settings.ai_models` as stored: a JSON object of provider id →
|
|
65
|
+
* `{ chat?, embeddings? }`. Anything else (null, malformed JSON, a field
|
|
66
|
+
* that isn't a model name) reads as no setting, so the seed serves.
|
|
67
|
+
*/
|
|
68
|
+
export function parseAiModelSettings(value) {
|
|
69
|
+
if (typeof value !== "string" || value.trim() === "")
|
|
70
|
+
return {};
|
|
71
|
+
let parsed;
|
|
72
|
+
try {
|
|
73
|
+
parsed = JSON.parse(value);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return {};
|
|
77
|
+
}
|
|
78
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
79
|
+
return {};
|
|
80
|
+
const settings = {};
|
|
81
|
+
for (const [provider, entry] of Object.entries(parsed)) {
|
|
82
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
83
|
+
continue;
|
|
84
|
+
const setting = {};
|
|
85
|
+
for (const kind of ["chat", "embeddings"]) {
|
|
86
|
+
const model = entry[kind];
|
|
87
|
+
if (typeof model === "string" && isAiModelName(model)) {
|
|
88
|
+
setting[kind] = model;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (setting.chat !== undefined || setting.embeddings !== undefined) {
|
|
92
|
+
settings[provider] = setting;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return settings;
|
|
96
|
+
}
|
|
97
|
+
/** The stored text of the settings, providers sorted; null when nothing is set. */
|
|
98
|
+
export function formatAiModelSettings(settings) {
|
|
99
|
+
const providers = Object.keys(settings)
|
|
100
|
+
.filter((id) => settings[id]?.chat !== undefined ||
|
|
101
|
+
settings[id]?.embeddings !== undefined)
|
|
102
|
+
.sort();
|
|
103
|
+
if (providers.length === 0)
|
|
104
|
+
return null;
|
|
105
|
+
return JSON.stringify(Object.fromEntries(providers.map((id) => {
|
|
106
|
+
const { chat, embeddings } = settings[id];
|
|
107
|
+
return [
|
|
108
|
+
id,
|
|
109
|
+
{
|
|
110
|
+
...(chat === undefined ? {} : { chat }),
|
|
111
|
+
...(embeddings === undefined ? {} : { embeddings }),
|
|
112
|
+
},
|
|
113
|
+
];
|
|
114
|
+
})));
|
|
115
|
+
}
|
|
116
|
+
/** `settings` with one provider's models changed by `patch`. */
|
|
117
|
+
export function withAiModelSetting(settings, provider, patch) {
|
|
118
|
+
const next = { ...settings[provider] };
|
|
119
|
+
for (const kind of ["chat", "embeddings"]) {
|
|
120
|
+
const value = patch[kind];
|
|
121
|
+
if (value === null)
|
|
122
|
+
delete next[kind];
|
|
123
|
+
else if (value !== undefined)
|
|
124
|
+
next[kind] = value;
|
|
125
|
+
}
|
|
126
|
+
const rest = Object.fromEntries(Object.entries(settings).filter(([id]) => id !== provider));
|
|
127
|
+
return next.chat === undefined && next.embeddings === undefined
|
|
128
|
+
? rest
|
|
129
|
+
: { ...rest, [provider]: next };
|
|
130
|
+
}
|
|
131
|
+
/** A choice must name a provider id; the model is free text after the first slash. */
|
|
52
132
|
const CHOICE = /^([a-z][a-z0-9-]{1,39})(?:\/(.{1,200}))?$/;
|
|
53
133
|
/**
|
|
54
|
-
* `openrouter` or `openrouter/anthropic/claude-haiku-5.5` as
|
|
134
|
+
* `openrouter` or `openrouter/anthropic/claude-haiku-5.5` as typed:
|
|
55
135
|
* the provider id up to the first slash, the model after it (a model id
|
|
56
136
|
* may hold slashes of its own). Null for blank or malformed text.
|
|
57
137
|
*/
|
|
@@ -64,10 +144,18 @@ export function parseAiDefaultChoice(value) {
|
|
|
64
144
|
const model = match[2]?.trim();
|
|
65
145
|
return model ? { provider: match[1], model } : { provider: match[1] };
|
|
66
146
|
}
|
|
67
|
-
/** The
|
|
147
|
+
/** The text of a choice: `provider` or `provider/model`. */
|
|
68
148
|
export function formatAiDefaultChoice(choice) {
|
|
69
149
|
return choice.model ? `${choice.provider}/${choice.model}` : choice.provider;
|
|
70
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* The provider id `org_settings.ai_default` holds, or null. The column
|
|
153
|
+
* holds the provider alone (its models are in `ai_models`); text after a
|
|
154
|
+
* first slash is not read.
|
|
155
|
+
*/
|
|
156
|
+
export function parseAiDefaultProvider(value) {
|
|
157
|
+
return parseAiDefaultChoice(value)?.provider ?? null;
|
|
158
|
+
}
|
|
71
159
|
export function canBeAiDefault(capability) {
|
|
72
160
|
return (capability?.mode === "ai" &&
|
|
73
161
|
capability.kind !== "proxy" &&
|
|
@@ -96,13 +184,28 @@ export function aiGatewayConfigured(isSet) {
|
|
|
96
184
|
export function viaAiGateway(capability, isSet) {
|
|
97
185
|
return capability.aig !== undefined && aiGatewayConfigured(isSet);
|
|
98
186
|
}
|
|
99
|
-
|
|
187
|
+
/** The provider's models: the operator's setting, else the seed. */
|
|
188
|
+
function modelsOf(id, capability, models) {
|
|
189
|
+
const setting = models[id];
|
|
190
|
+
const { chat, embeddings } = capability;
|
|
191
|
+
return {
|
|
192
|
+
model: setting?.chat ?? chat.seedModel,
|
|
193
|
+
...(embeddings
|
|
194
|
+
? { embeddingsModel: setting?.embeddings ?? embeddings.seedModel }
|
|
195
|
+
: {}),
|
|
196
|
+
seed: {
|
|
197
|
+
chat: chat.seedModel,
|
|
198
|
+
...(embeddings ? { embeddings: embeddings.seedModel } : {}),
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function candidateOf(id, capability, isSet, models) {
|
|
100
203
|
return {
|
|
101
204
|
id,
|
|
102
205
|
label: capability.label,
|
|
103
206
|
settings: aiRequiredSettings(capability),
|
|
104
207
|
configured: aiProviderConfigured(capability, isSet),
|
|
105
|
-
|
|
208
|
+
...modelsOf(id, capability, models),
|
|
106
209
|
viaAiGateway: viaAiGateway(capability, isSet),
|
|
107
210
|
embeddings: capability.embeddings !== undefined,
|
|
108
211
|
};
|
|
@@ -112,15 +215,18 @@ function candidateOf(id, capability, isSet) {
|
|
|
112
215
|
* then any other `ai` entry of `lookup` with a chat route (a registered
|
|
113
216
|
* row, one day), each with its state.
|
|
114
217
|
*/
|
|
115
|
-
export function aiDefaultCandidates(isSet, lookup = CATALOG) {
|
|
218
|
+
export function aiDefaultCandidates(isSet, lookup = CATALOG, models = {}) {
|
|
116
219
|
const ids = [
|
|
117
220
|
...AI_DEFAULT_ORDER,
|
|
118
221
|
...lookup.ids().filter((id) => !AI_DEFAULT_ORDER.includes(id)),
|
|
119
222
|
];
|
|
120
223
|
return ids.flatMap((id) => {
|
|
121
224
|
const capability = lookup.get(id);
|
|
225
|
+
// A registered row whose key isn't bound to the hosts it has now reads
|
|
226
|
+
// as unset everywhere (`providers.value-bound-to-hosts`).
|
|
227
|
+
const set = lookup.bound && !lookup.bound(id) ? () => false : isSet;
|
|
122
228
|
return canBeAiDefault(capability)
|
|
123
|
-
? [candidateOf(id, capability,
|
|
229
|
+
? [candidateOf(id, capability, set, models)]
|
|
124
230
|
: [];
|
|
125
231
|
});
|
|
126
232
|
}
|
|
@@ -140,6 +246,29 @@ export function aiDefaultChoiceProblem(choice, lookup = CATALOG) {
|
|
|
140
246
|
}
|
|
141
247
|
return null;
|
|
142
248
|
}
|
|
249
|
+
/**
|
|
250
|
+
* Why `patch` can't be stored for `provider`, or null when it can: the
|
|
251
|
+
* provider must be one the default can be, a model name must be a model
|
|
252
|
+
* name, and an embeddings model needs the entry's embeddings route.
|
|
253
|
+
*/
|
|
254
|
+
export function aiModelPatchProblem(provider, patch, lookup = CATALOG) {
|
|
255
|
+
const why = aiDefaultChoiceProblem({ provider }, lookup);
|
|
256
|
+
if (why)
|
|
257
|
+
return why;
|
|
258
|
+
const capability = lookup.get(provider);
|
|
259
|
+
if (!canBeAiDefault(capability))
|
|
260
|
+
return `${provider} can't serve the default`;
|
|
261
|
+
for (const kind of ["chat", "embeddings"]) {
|
|
262
|
+
const value = patch[kind];
|
|
263
|
+
if (typeof value === "string" && !isAiModelName(value)) {
|
|
264
|
+
return `${JSON.stringify(value)} isn't a model id: 1 to 200 characters, no surrounding spaces`;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (typeof patch.embeddings === "string" && !capability.embeddings) {
|
|
268
|
+
return `${capability.label} has no embeddings route, so it serves no embeddings model`;
|
|
269
|
+
}
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
143
272
|
/**
|
|
144
273
|
* The Gateway's effective default, from the stored choice (or none) and
|
|
145
274
|
* what is set. A choice that names a provider which isn't configured
|
|
@@ -149,12 +278,13 @@ export function aiDefaultChoiceProblem(choice, lookup = CATALOG) {
|
|
|
149
278
|
*/
|
|
150
279
|
export function resolveAiDefault(input) {
|
|
151
280
|
const lookup = input.lookup ?? CATALOG;
|
|
281
|
+
const models = input.models ?? {};
|
|
152
282
|
const { isSet } = input;
|
|
153
|
-
const candidates = aiDefaultCandidates(isSet, lookup);
|
|
154
|
-
const resolve = (id, capability, source,
|
|
283
|
+
const candidates = aiDefaultCandidates(isSet, lookup, models);
|
|
284
|
+
const resolve = (id, capability, source, problem) => ({
|
|
155
285
|
provider: id,
|
|
156
286
|
label: capability.label,
|
|
157
|
-
|
|
287
|
+
...modelsOf(id, capability, models),
|
|
158
288
|
source,
|
|
159
289
|
configured: aiProviderConfigured(capability, isSet),
|
|
160
290
|
settings: aiRequiredSettings(capability),
|
|
@@ -168,11 +298,11 @@ export function resolveAiDefault(input) {
|
|
|
168
298
|
...(problem ? { problem } : {}),
|
|
169
299
|
});
|
|
170
300
|
let problem;
|
|
171
|
-
if (input.
|
|
172
|
-
const capability = lookup.get(input.
|
|
173
|
-
const why = aiDefaultChoiceProblem(input.
|
|
301
|
+
if (input.provider) {
|
|
302
|
+
const capability = lookup.get(input.provider);
|
|
303
|
+
const why = aiDefaultChoiceProblem({ provider: input.provider }, lookup);
|
|
174
304
|
if (!why && canBeAiDefault(capability)) {
|
|
175
|
-
return resolve(input.
|
|
305
|
+
return resolve(input.provider, capability, "setting");
|
|
176
306
|
}
|
|
177
307
|
problem = why ?? undefined;
|
|
178
308
|
}
|
|
@@ -184,7 +314,7 @@ export function resolveAiDefault(input) {
|
|
|
184
314
|
const capability = lookup.get(first.id);
|
|
185
315
|
if (!canBeAiDefault(capability))
|
|
186
316
|
throw new Error("unreachable");
|
|
187
|
-
return resolve(first.id, capability, configured ? "order" : "none",
|
|
317
|
+
return resolve(first.id, capability, configured ? "order" : "none", problem);
|
|
188
318
|
}
|
|
189
319
|
/**
|
|
190
320
|
* The sentence setup shows for an unconfigured default: the one key to
|
|
@@ -32,7 +32,9 @@ export type DevAiProvider = AiProviderId;
|
|
|
32
32
|
/** The fallback's view of one `ai` catalog entry. */
|
|
33
33
|
export declare function devAiProviderSpec(capability: AiProviderCapability): DevAiProviderSpec;
|
|
34
34
|
export declare const DEV_AI_PROVIDERS: Readonly<Record<DevAiProvider, DevAiProviderSpec>>;
|
|
35
|
-
export declare function devAiProvider(name: string
|
|
35
|
+
export declare function devAiProvider(name: string,
|
|
36
|
+
/** The dev Worker's bindings: a registered `ai` spec under `.everyapp/providers/<id>.json` resolves from them. */
|
|
37
|
+
values?: Readonly<Record<string, unknown>>): DevAiProviderSpec | undefined;
|
|
36
38
|
/** Every setting a provider needs, its key first: the names `.dev.vars` and the gateway use. */
|
|
37
39
|
export declare function aiProviderSettings(spec: DevAiProviderSpec): string[];
|
|
38
40
|
/**
|