@every-app/sdk 0.3.0-rc.5 → 0.3.0-rc.7
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/dist/cloudflare/catalogSettings.generated.d.ts +1 -16
- package/dist/cloudflare/catalogSettings.generated.d.ts.map +1 -1
- package/dist/cloudflare/catalogSettings.generated.js +0 -49
- package/dist/cloudflare/worker.d.ts +14 -0
- package/dist/cloudflare/worker.d.ts.map +1 -1
- package/dist/cloudflare/worker.js +19 -6
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- 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 +0 -6
- package/dist/internal/gatewaySettings.d.ts.map +1 -1
- package/dist/internal/gatewaySettings.js +1 -14
- 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 +25 -4
- package/dist/manifest.d.ts.map +1 -1
- package/dist/server/ai.d.ts +45 -0
- package/dist/server/ai.d.ts.map +1 -1
- package/dist/server/ai.js +77 -1
- package/dist/server/aiBinding.d.ts.map +1 -1
- package/dist/server/aiBinding.js +3 -3
- 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.js +1 -1
- package/dist/server/emit.js +1 -1
- package/dist/server/gatewayFetch.d.ts.map +1 -1
- package/dist/server/gatewayFetch.js +20 -7
- package/dist/server/index.d.ts +3 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +3 -2
- package/dist/server/integrationFetch.d.ts +4 -31
- package/dist/server/integrationFetch.d.ts.map +1 -1
- package/dist/server/integrationFetch.js +44 -42
- 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/package.json +1 -1
|
@@ -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
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aiProviders.d.ts","sourceRoot":"","sources":["../../src/internal/aiProviders.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAGL,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,OAAO,EACZ,KAAK,UAAU,EACf,KAAK,kBAAkB,EACxB,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"aiProviders.d.ts","sourceRoot":"","sources":["../../src/internal/aiProviders.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAGL,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,OAAO,EACZ,KAAK,UAAU,EACf,KAAK,kBAAkB,EACxB,MAAM,mBAAmB,CAAC;AAG3B,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,+GAA+G;IAC/G,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,uFAAuF;IACvF,MAAM,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,IAAI,EAAE,UAAU,CAAC;IACjB,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,0FAA0F;IAC1F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uFAAuF;IACvF,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,wEAAwE;IACxE,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,KAAK,MAAM,CAAC;IAClE,8EAA8E;IAC9E,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,KAAK,QAAQ,CAAC;CACpE;AAED,MAAM,MAAM,aAAa,GAAG,YAAY,CAAC;AAEzC,qDAAqD;AACrD,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,oBAAoB,GAC/B,iBAAiB,CAoBnB;AAED,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CACrC,MAAM,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAUG,CAAC;AAE9C,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM;AACZ,kHAAkH;AAClH,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACzC,iBAAiB,GAAG,SAAS,CAS/B;AAED,gGAAgG;AAChG,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,EAAE,CAEpE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,kBAAkB,GAC3B,MAAM,GAAG,IAAI,CAcf;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,UAAU,EAAE,oBAAoB,EAChC,QAAQ,EAAE,kBAAkB,GAC3B,MAAM,GAAG,IAAI,CAIf;AAED,6DAA6D;AAC7D,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,MAAM,SAAK,GACV,GAAG,GAAG,IAAI,CAOZ;AAWD;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,QAAQ,GACjB,QAAQ,CAqBV"}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* rewrite the gateway has (Workers AI's numeric tokens).
|
|
9
9
|
*/
|
|
10
10
|
import { CAPABILITIES, CAPABILITY_IDS, } from "./capabilities.js";
|
|
11
|
+
import { resolveDevProvider } from "./devProviders.js";
|
|
11
12
|
/** The fallback's view of one `ai` catalog entry. */
|
|
12
13
|
export function devAiProviderSpec(capability) {
|
|
13
14
|
const { basePath, normalize } = capability;
|
|
@@ -38,9 +39,17 @@ export const DEV_AI_PROVIDERS = Object.fromEntries(CAPABILITY_IDS.flatMap((id) =
|
|
|
38
39
|
? [[id, devAiProviderSpec(capability)]]
|
|
39
40
|
: [];
|
|
40
41
|
}));
|
|
41
|
-
export function devAiProvider(name
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
export function devAiProvider(name,
|
|
43
|
+
/** The dev Worker's bindings: a registered `ai` spec under `.everyapp/providers/<id>.json` resolves from them. */
|
|
44
|
+
values) {
|
|
45
|
+
if (Object.hasOwn(DEV_AI_PROVIDERS, name)) {
|
|
46
|
+
return DEV_AI_PROVIDERS[name];
|
|
47
|
+
}
|
|
48
|
+
if (!values)
|
|
49
|
+
return undefined;
|
|
50
|
+
const resolved = resolveDevProvider(values, name);
|
|
51
|
+
return resolved.found && resolved.capability.mode === "ai"
|
|
52
|
+
? devAiProviderSpec(resolved.capability)
|
|
44
53
|
: undefined;
|
|
45
54
|
}
|
|
46
55
|
/** Every setting a provider needs, its key first: the names `.dev.vars` and the gateway use. */
|
|
@@ -15,7 +15,10 @@
|
|
|
15
15
|
* member is set; only for an app that picks among the set members at run
|
|
16
16
|
* time). Each may say `why` it is needed and where it comes from
|
|
17
17
|
* (`docs`, an https URL); for providers and capabilities the registries
|
|
18
|
-
* here supply a default.
|
|
18
|
+
* here supply a default. An app's plain settings (`settings`: a host, a
|
|
19
|
+
* project id, a region) are held like its secrets, on its Worker, but are
|
|
20
|
+
* values rather than keys: each says what it takes (a URL, a number, one
|
|
21
|
+
* of a list) and may give a default.
|
|
19
22
|
*
|
|
20
23
|
* Everything in this module is pure and tenant-neutral: a manifest ships
|
|
21
24
|
* in a release, and what is set is asked of the caller (`isSet`), which
|
|
@@ -23,7 +26,7 @@
|
|
|
23
26
|
* the app Worker's secret names.
|
|
24
27
|
*/
|
|
25
28
|
import { type ApiKeyCapability, type CapabilityLookup } from "./capabilities.js";
|
|
26
|
-
import { type
|
|
29
|
+
import { type IsSet } from "./aiDefault.js";
|
|
27
30
|
/** A provider as `providers` lists it: its id, or an entry with a need. */
|
|
28
31
|
export interface ProviderEntry extends SettingNeedFields {
|
|
29
32
|
id: string;
|
|
@@ -32,6 +35,32 @@ export interface ProviderEntry extends SettingNeedFields {
|
|
|
32
35
|
export interface SecretEntry extends SettingNeedFields {
|
|
33
36
|
name: string;
|
|
34
37
|
}
|
|
38
|
+
/** What a plain setting's value is: text by default, or a URL, a number, a switch. */
|
|
39
|
+
export type SettingValueType = "text" | "url" | "number" | "boolean";
|
|
40
|
+
/**
|
|
41
|
+
* An app's plain setting as `settings` lists it: a value that isn't a
|
|
42
|
+
* secret (a PostHog host, a project id, a region) and differs per
|
|
43
|
+
* install, set like a secret with `everyapp configure <app>` and read
|
|
44
|
+
* from `env` as a string.
|
|
45
|
+
*/
|
|
46
|
+
export interface SettingEntry {
|
|
47
|
+
name: string;
|
|
48
|
+
why?: string;
|
|
49
|
+
docs?: string;
|
|
50
|
+
optional?: boolean;
|
|
51
|
+
/** What it takes; `text` when not given. */
|
|
52
|
+
type?: SettingValueType;
|
|
53
|
+
/** The values it may take, for a `text` setting that is one of a list. */
|
|
54
|
+
values?: readonly string[];
|
|
55
|
+
/** What the deploy sets it to while nobody has set it. */
|
|
56
|
+
default?: string;
|
|
57
|
+
}
|
|
58
|
+
/** What a plain setting's value must be: what `configure` checks and every surface says. */
|
|
59
|
+
export interface SettingValueRule {
|
|
60
|
+
type: SettingValueType;
|
|
61
|
+
values?: string[];
|
|
62
|
+
default?: string;
|
|
63
|
+
}
|
|
35
64
|
/** What any declaration may say about its need. */
|
|
36
65
|
export interface SettingNeedFields {
|
|
37
66
|
/** Why the app needs it, for whoever sets it (200 characters at most). */
|
|
@@ -61,6 +90,7 @@ export interface SettingsManifestLike {
|
|
|
61
90
|
readonly [id: string]: unknown;
|
|
62
91
|
};
|
|
63
92
|
readonly secrets?: ReadonlyArray<string | SecretEntry>;
|
|
93
|
+
readonly settings?: ReadonlyArray<SettingEntry>;
|
|
64
94
|
readonly ai?: {
|
|
65
95
|
readonly routes?: readonly string[];
|
|
66
96
|
/** The app needs the Gateway's default chat model (`/api/ai/default`): `true`, or an entry with its need. */
|
|
@@ -70,7 +100,9 @@ export interface SettingsManifestLike {
|
|
|
70
100
|
readonly remote?: unknown;
|
|
71
101
|
readonly name?: string;
|
|
72
102
|
}
|
|
73
|
-
export type SettingKind = "provider" | "capability" | "secret"
|
|
103
|
+
export type SettingKind = "provider" | "capability" | "secret"
|
|
104
|
+
/** An app's plain setting (`settings`): a value on its Worker, not a key. */
|
|
105
|
+
| "setting" | "ai-routes"
|
|
74
106
|
/** The Gateway's default model provider (`ai.chat`): one key of any provider in the order, or the operator's choice. */
|
|
75
107
|
| "ai-default"
|
|
76
108
|
/** A remote MCP app's credential (./remoteMcp.ts): one Gateway setting named by the row. */
|
|
@@ -87,8 +119,8 @@ export type SettingNeed = {
|
|
|
87
119
|
};
|
|
88
120
|
/** What `appSettings` may be told about the Gateway, for the default model provider. */
|
|
89
121
|
export interface AppSettingsOptions {
|
|
90
|
-
/** The operator's stored default (`everyapp ai default`), or none. */
|
|
91
|
-
aiDefault?:
|
|
122
|
+
/** The operator's stored default provider (`everyapp ai default`), or none. */
|
|
123
|
+
aiDefault?: string | null;
|
|
92
124
|
/** What is set, to put the configured provider first; absent, the order decides. */
|
|
93
125
|
isSet?: IsSet;
|
|
94
126
|
}
|
|
@@ -129,6 +161,12 @@ export interface AppSetting {
|
|
|
129
161
|
* has no guide, so this is what an agent reads for it.
|
|
130
162
|
*/
|
|
131
163
|
hint?: string;
|
|
164
|
+
/**
|
|
165
|
+
* For an app's plain setting (`kind: "setting"`): what its value must
|
|
166
|
+
* be, and its default. A setting without one is a credential, never
|
|
167
|
+
* shown.
|
|
168
|
+
*/
|
|
169
|
+
value?: SettingValueRule;
|
|
132
170
|
}
|
|
133
171
|
/** A setting with whether it is set, and whether that keeps the app closed. */
|
|
134
172
|
export interface EvaluatedSetting extends AppSetting {
|
|
@@ -166,6 +204,28 @@ export declare function providerIds(manifest: Pick<SettingsManifestLike, "provid
|
|
|
166
204
|
export declare function secretEntries(manifest: Pick<SettingsManifestLike, "secrets">): SecretEntry[];
|
|
167
205
|
/** The names of the app's own secrets, in order. */
|
|
168
206
|
export declare function secretNames(manifest: Pick<SettingsManifestLike, "secrets">): string[];
|
|
207
|
+
/** The app's plain settings the manifest lists, each as an entry. */
|
|
208
|
+
export declare function settingEntries(manifest: Pick<SettingsManifestLike, "settings">): SettingEntry[];
|
|
209
|
+
/** The names of the app's plain settings, in order. */
|
|
210
|
+
export declare function settingNames(manifest: Pick<SettingsManifestLike, "settings">): string[];
|
|
211
|
+
/**
|
|
212
|
+
* Every name the manifest puts on the app's own Workers: its secrets,
|
|
213
|
+
* then its plain settings. What `secret set` routes to the app and what
|
|
214
|
+
* `apps.secret_names` records.
|
|
215
|
+
*/
|
|
216
|
+
export declare function appWorkerNames(manifest: Pick<SettingsManifestLike, "secrets" | "settings">): string[];
|
|
217
|
+
/** A plain setting's rule, from its entry. */
|
|
218
|
+
export declare function settingValueRule(entry: SettingEntry): SettingValueRule;
|
|
219
|
+
/**
|
|
220
|
+
* What a plain setting takes, completing "Takes …": "an http or https
|
|
221
|
+
* URL", "one of us or eu", with ", default <value>" when it has one.
|
|
222
|
+
*/
|
|
223
|
+
export declare function settingValueTakes(rule: SettingValueRule): string;
|
|
224
|
+
/**
|
|
225
|
+
* Why `value` won't do for a plain setting (one sentence), or null when
|
|
226
|
+
* it will: never empty, and of its type, or one of its values exactly.
|
|
227
|
+
*/
|
|
228
|
+
export declare function settingValueProblem(rule: Pick<SettingValueRule, "type" | "values">, value: string): string | null;
|
|
169
229
|
/**
|
|
170
230
|
* Every setting the manifest declares, in manifest order: its AI
|
|
171
231
|
* providers, its capabilities, the AI proxy's extra routes when `ai.routes`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"appSettings.d.ts","sourceRoot":"","sources":["../../src/internal/appSettings.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"appSettings.d.ts","sourceRoot":"","sources":["../../src/internal/appSettings.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,7 +15,10 @@
|
|
|
15
15
|
* member is set; only for an app that picks among the set members at run
|
|
16
16
|
* time). Each may say `why` it is needed and where it comes from
|
|
17
17
|
* (`docs`, an https URL); for providers and capabilities the registries
|
|
18
|
-
* here supply a default.
|
|
18
|
+
* here supply a default. An app's plain settings (`settings`: a host, a
|
|
19
|
+
* project id, a region) are held like its secrets, on its Worker, but are
|
|
20
|
+
* values rather than keys: each says what it takes (a URL, a number, one
|
|
21
|
+
* of a list) and may give a default.
|
|
19
22
|
*
|
|
20
23
|
* Everything in this module is pure and tenant-neutral: a manifest ships
|
|
21
24
|
* in a release, and what is set is asked of the caller (`isSet`), which
|
|
@@ -81,6 +84,115 @@ export function secretEntries(manifest) {
|
|
|
81
84
|
export function secretNames(manifest) {
|
|
82
85
|
return secretEntries(manifest).map((entry) => entry.name);
|
|
83
86
|
}
|
|
87
|
+
/** The app's plain settings the manifest lists, each as an entry. */
|
|
88
|
+
export function settingEntries(manifest) {
|
|
89
|
+
return (manifest.settings ?? []).flatMap((entry) => {
|
|
90
|
+
if (!entry || typeof entry !== "object" || typeof entry.name !== "string") {
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
const record = entry;
|
|
94
|
+
const type = record["type"];
|
|
95
|
+
const values = record["values"];
|
|
96
|
+
return [
|
|
97
|
+
{
|
|
98
|
+
name: entry.name,
|
|
99
|
+
...needFields(entry),
|
|
100
|
+
...(type === "url" ||
|
|
101
|
+
type === "number" ||
|
|
102
|
+
type === "boolean" ||
|
|
103
|
+
type === "text"
|
|
104
|
+
? { type }
|
|
105
|
+
: {}),
|
|
106
|
+
...(Array.isArray(values) &&
|
|
107
|
+
values.every((value) => typeof value === "string")
|
|
108
|
+
? { values: values }
|
|
109
|
+
: {}),
|
|
110
|
+
...(typeof record["default"] === "string"
|
|
111
|
+
? { default: record["default"] }
|
|
112
|
+
: {}),
|
|
113
|
+
},
|
|
114
|
+
];
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
/** The names of the app's plain settings, in order. */
|
|
118
|
+
export function settingNames(manifest) {
|
|
119
|
+
return settingEntries(manifest).map((entry) => entry.name);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Every name the manifest puts on the app's own Workers: its secrets,
|
|
123
|
+
* then its plain settings. What `secret set` routes to the app and what
|
|
124
|
+
* `apps.secret_names` records.
|
|
125
|
+
*/
|
|
126
|
+
export function appWorkerNames(manifest) {
|
|
127
|
+
return [...secretNames(manifest), ...settingNames(manifest)];
|
|
128
|
+
}
|
|
129
|
+
/** A plain setting's rule, from its entry. */
|
|
130
|
+
export function settingValueRule(entry) {
|
|
131
|
+
return {
|
|
132
|
+
type: entry.type ?? "text",
|
|
133
|
+
...(entry.values?.length ? { values: [...entry.values] } : {}),
|
|
134
|
+
...(entry.default !== undefined ? { default: entry.default } : {}),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/** `a, b or c`. */
|
|
138
|
+
function either(values) {
|
|
139
|
+
return values.length < 2
|
|
140
|
+
? (values[0] ?? "")
|
|
141
|
+
: `${values.slice(0, -1).join(", ")} or ${values.at(-1)}`;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* What a plain setting takes, completing "Takes …": "an http or https
|
|
145
|
+
* URL", "one of us or eu", with ", default <value>" when it has one.
|
|
146
|
+
*/
|
|
147
|
+
export function settingValueTakes(rule) {
|
|
148
|
+
const what = rule.values?.length
|
|
149
|
+
? `${rule.values.length > 2 ? "one of " : ""}${either(rule.values)}`
|
|
150
|
+
: {
|
|
151
|
+
text: "any text",
|
|
152
|
+
url: "an http or https URL",
|
|
153
|
+
number: "a number",
|
|
154
|
+
boolean: "true or false",
|
|
155
|
+
}[rule.type];
|
|
156
|
+
return rule.default !== undefined ? `${what}, default ${rule.default}` : what;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Why `value` won't do for a plain setting (one sentence), or null when
|
|
160
|
+
* it will: never empty, and of its type, or one of its values exactly.
|
|
161
|
+
*/
|
|
162
|
+
export function settingValueProblem(rule, value) {
|
|
163
|
+
const trimmed = value.trim();
|
|
164
|
+
if (!trimmed)
|
|
165
|
+
return "Enter a value.";
|
|
166
|
+
if (rule.values?.length) {
|
|
167
|
+
return rule.values.includes(trimmed)
|
|
168
|
+
? null
|
|
169
|
+
: `Not ${rule.values.length > 2 ? "one of " : ""}${either(rule.values)}.`;
|
|
170
|
+
}
|
|
171
|
+
switch (rule.type) {
|
|
172
|
+
case "text":
|
|
173
|
+
return null;
|
|
174
|
+
case "url": {
|
|
175
|
+
let url = null;
|
|
176
|
+
try {
|
|
177
|
+
url = new URL(trimmed);
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
url = null;
|
|
181
|
+
}
|
|
182
|
+
return url && (url.protocol === "http:" || url.protocol === "https:")
|
|
183
|
+
? null
|
|
184
|
+
: `"${trimmed}" isn't an http or https URL.`;
|
|
185
|
+
}
|
|
186
|
+
case "number":
|
|
187
|
+
return /^-?\d+(\.\d+)?$/.test(trimmed)
|
|
188
|
+
? null
|
|
189
|
+
: `"${trimmed}" isn't a number.`;
|
|
190
|
+
case "boolean":
|
|
191
|
+
return trimmed === "true" || trimmed === "false"
|
|
192
|
+
? null
|
|
193
|
+
: "Not true or false.";
|
|
194
|
+
}
|
|
195
|
+
}
|
|
84
196
|
/**
|
|
85
197
|
* The Gateway settings an AI provider needs, the install's left out (set
|
|
86
198
|
* already). A catalog provider's names are settable by definition; a
|
|
@@ -103,26 +215,21 @@ function capabilityNames(capability) {
|
|
|
103
215
|
names: settable(capability.settings),
|
|
104
216
|
alsoReads: settable(capability.optionalSettings),
|
|
105
217
|
};
|
|
106
|
-
case "oauth_client":
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return capability.defaultOrigin
|
|
117
|
-
? { names, alsoReads: host }
|
|
118
|
-
: { names: [...names, ...host], alsoReads: [] };
|
|
119
|
-
}
|
|
218
|
+
case "oauth_client":
|
|
219
|
+
return {
|
|
220
|
+
names: [
|
|
221
|
+
capability.secrets.clientId,
|
|
222
|
+
...(capability.secrets.clientSecret
|
|
223
|
+
? [capability.secrets.clientSecret]
|
|
224
|
+
: []),
|
|
225
|
+
],
|
|
226
|
+
alsoReads: [],
|
|
227
|
+
};
|
|
120
228
|
}
|
|
121
229
|
}
|
|
122
230
|
/**
|
|
123
231
|
* Where a capability's key or client comes from, when the registry knows:
|
|
124
|
-
* an API-key provider's access page, or an OAuth client's console
|
|
125
|
-
* on the default instance for a provider with no fixed host).
|
|
232
|
+
* an API-key provider's access page, or an OAuth client's console.
|
|
126
233
|
*/
|
|
127
234
|
function capabilityDocs(capability) {
|
|
128
235
|
switch (capability.mode) {
|
|
@@ -130,11 +237,7 @@ function capabilityDocs(capability) {
|
|
|
130
237
|
case "ai":
|
|
131
238
|
return capability.docs;
|
|
132
239
|
case "oauth_client":
|
|
133
|
-
|
|
134
|
-
return capability.console;
|
|
135
|
-
return capability.defaultOrigin
|
|
136
|
-
? `${capability.defaultOrigin}${capability.console}`
|
|
137
|
-
: undefined;
|
|
240
|
+
return capability.console;
|
|
138
241
|
case "service":
|
|
139
242
|
return undefined;
|
|
140
243
|
}
|
|
@@ -184,7 +287,7 @@ export function appSettings(manifest, lookup = CATALOG, options = {}) {
|
|
|
184
287
|
// ask for; with one configured, that provider's. `isSet` here only
|
|
185
288
|
// orders the candidates; evaluateAppSettings decides what is set.
|
|
186
289
|
const resolution = resolveAiDefault({
|
|
187
|
-
|
|
290
|
+
provider: options.aiDefault ?? null,
|
|
188
291
|
isSet: options.isSet ?? (() => false),
|
|
189
292
|
lookup,
|
|
190
293
|
});
|
|
@@ -286,6 +389,21 @@ export function appSettings(manifest, lookup = CATALOG, options = {}) {
|
|
|
286
389
|
alsoReads: [],
|
|
287
390
|
}, entry));
|
|
288
391
|
}
|
|
392
|
+
for (const entry of settingEntries(manifest)) {
|
|
393
|
+
// A setting with a default is never unset for long: the deploy writes it.
|
|
394
|
+
const need = entry.default !== undefined ? { ...entry, optional: true } : entry;
|
|
395
|
+
settings.push({
|
|
396
|
+
...withNeed({
|
|
397
|
+
kind: "setting",
|
|
398
|
+
id: entry.name,
|
|
399
|
+
label: entry.name,
|
|
400
|
+
holder: "app",
|
|
401
|
+
names: [entry.name],
|
|
402
|
+
alsoReads: [],
|
|
403
|
+
}, need),
|
|
404
|
+
value: settingValueRule(entry),
|
|
405
|
+
});
|
|
406
|
+
}
|
|
289
407
|
return settings;
|
|
290
408
|
}
|
|
291
409
|
/** Whether each name of `setting` is set: all → true, one unset → false, else unknown. */
|