@every-app/sdk 0.3.0-rc.13 → 0.3.0-rc.15
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/README.md +15 -14
- package/dist/cloudflare/catalogSettings.generated.d.ts +17 -7
- package/dist/cloudflare/catalogSettings.generated.d.ts.map +1 -1
- package/dist/cloudflare/catalogSettings.generated.js +40 -12
- package/dist/cloudflare/worker.d.ts +27 -7
- package/dist/cloudflare/worker.d.ts.map +1 -1
- package/dist/cloudflare/worker.js +13 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/internal/appSettings.d.ts +124 -79
- package/dist/internal/appSettings.d.ts.map +1 -1
- package/dist/internal/appSettings.js +240 -125
- package/dist/internal/capabilities.d.ts +5 -29
- package/dist/internal/capabilities.d.ts.map +1 -1
- package/dist/internal/capabilities.js +4 -24
- package/dist/internal/devProviders.d.ts +1 -19
- package/dist/internal/devProviders.d.ts.map +1 -1
- package/dist/internal/devProviders.js +7 -1
- package/dist/internal/oauthPresets.d.ts +351 -14
- package/dist/internal/oauthPresets.d.ts.map +1 -1
- package/dist/internal/oauthPresets.js +155 -22
- package/dist/internal/oauthSpec.d.ts +2 -46
- package/dist/internal/oauthSpec.d.ts.map +1 -1
- package/dist/internal/oauthSpec.js +0 -57
- package/dist/internal/remoteMcp.d.ts.map +1 -1
- package/dist/internal/remoteMcp.js +4 -3
- package/dist/internal/tools.d.ts +3 -1
- package/dist/internal/tools.d.ts.map +1 -1
- package/dist/manifest.d.ts +49 -13
- package/dist/manifest.d.ts.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/integrationFetch.d.ts +7 -7
- package/dist/server/integrationFetch.d.ts.map +1 -1
- package/dist/server/integrationFetch.js +4 -2
- package/dist/server/tools.d.ts +5 -2
- package/dist/server/tools.d.ts.map +1 -1
- package/dist/server/tools.js +5 -2
- package/package.json +1 -1
- package/dist/internal/oauthBuiltIns.d.ts +0 -101
- package/dist/internal/oauthBuiltIns.d.ts.map +0 -1
- package/dist/internal/oauthBuiltIns.js +0 -122
|
@@ -8,17 +8,20 @@
|
|
|
8
8
|
* (`providers`) and a capability (`capabilities`) are the Gateway's: their
|
|
9
9
|
* keys and clients live on the Gateway Worker, set once for every app
|
|
10
10
|
* with `everyapp secret set <NAME>`. The app's own secrets (`secrets`)
|
|
11
|
-
* live on its Worker, set from its directory.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
11
|
+
* live on its Worker, set from its directory. Everything the manifest
|
|
12
|
+
* lists at its top level is required: the app doesn't open until it is
|
|
13
|
+
* set. The rest is grouped into features (`features`): each a named set of
|
|
14
|
+
* secrets, settings and the Gateway capabilities and providers it needs,
|
|
15
|
+
* with numbered setup steps. A required feature's values are required; an
|
|
16
|
+
* optional feature is all or nothing, never blocking: off while none of
|
|
17
|
+
* its own values is set, incomplete while some are, ready once all are.
|
|
18
|
+
* Each declaration may say `why` it is needed, where it comes from
|
|
19
|
+
* (`docs`, an https URL) and, for an app's own value, `how` to get it;
|
|
20
|
+
* for providers and capabilities the registries here supply a default. An
|
|
21
|
+
* app's plain settings (`settings`: a host, a project id, a region) are
|
|
22
|
+
* held like its secrets, on its Worker, but are values rather than keys:
|
|
23
|
+
* each says what it takes (a URL, a number, one of a list) and may give a
|
|
24
|
+
* default.
|
|
22
25
|
*
|
|
23
26
|
* Everything in this module is pure and tenant-neutral: a manifest ships
|
|
24
27
|
* in a release, and what is set is asked of the caller (`isSet`), which
|
|
@@ -28,18 +31,8 @@
|
|
|
28
31
|
import { CATALOG, capabilitySettings, } from "./capabilities.js";
|
|
29
32
|
import { aiDefaultSetupHint, aiRequiredSettings, resolveAiDefault, } from "./aiDefault.js";
|
|
30
33
|
import { isInstallOwnedGatewaySetting, isSettableGatewaySetting, } from "./gatewaySettings.js";
|
|
34
|
+
import { oauthSetupGuide } from "./oauthPresets.js";
|
|
31
35
|
import { hasRemoteMcpBlock, remoteMcpBlockProblem } from "./remoteMcp.js";
|
|
32
|
-
/** The need an entry declares; required unless it says otherwise. */
|
|
33
|
-
export function needOf(entry) {
|
|
34
|
-
if (!entry)
|
|
35
|
-
return { kind: "required" };
|
|
36
|
-
if (typeof entry.anyOf === "string" && entry.anyOf.trim()) {
|
|
37
|
-
return { kind: "anyOf", group: entry.anyOf.trim() };
|
|
38
|
-
}
|
|
39
|
-
if (entry.optional === true)
|
|
40
|
-
return { kind: "optional" };
|
|
41
|
-
return { kind: "required" };
|
|
42
|
-
}
|
|
43
36
|
function needFields(entry) {
|
|
44
37
|
if (!entry || typeof entry !== "object")
|
|
45
38
|
return {};
|
|
@@ -47,11 +40,66 @@ function needFields(entry) {
|
|
|
47
40
|
return {
|
|
48
41
|
...(typeof record["why"] === "string" ? { why: record["why"] } : {}),
|
|
49
42
|
...(typeof record["docs"] === "string" ? { docs: record["docs"] } : {}),
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/** `how`, `generate` and `file` as a stored entry has them. */
|
|
46
|
+
function ownValueFields(entry) {
|
|
47
|
+
if (!entry || typeof entry !== "object")
|
|
48
|
+
return {};
|
|
49
|
+
const record = entry;
|
|
50
|
+
return {
|
|
51
|
+
...(typeof record["how"] === "string" ? { how: record["how"] } : {}),
|
|
52
|
+
...(record["generate"] === true ? { generate: true } : {}),
|
|
53
|
+
...(record["file"] === true ? { file: true } : {}),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const stringList = (value) => Array.isArray(value)
|
|
57
|
+
? value.filter((each) => typeof each === "string")
|
|
58
|
+
: [];
|
|
59
|
+
/** The features the manifest declares, in order, each read whatever a stored row holds. */
|
|
60
|
+
export function featureEntries(manifest) {
|
|
61
|
+
const features = manifest.features;
|
|
62
|
+
if (!features || typeof features !== "object")
|
|
63
|
+
return [];
|
|
64
|
+
return Object.entries(features).flatMap(([id, raw]) => {
|
|
65
|
+
if (!raw || typeof raw !== "object")
|
|
66
|
+
return [];
|
|
67
|
+
const record = raw;
|
|
68
|
+
return [
|
|
69
|
+
{
|
|
70
|
+
id,
|
|
71
|
+
name: typeof record["name"] === "string" && record["name"].trim()
|
|
72
|
+
? record["name"]
|
|
73
|
+
: id,
|
|
74
|
+
required: record["required"] === true,
|
|
75
|
+
...(typeof record["why"] === "string" ? { why: record["why"] } : {}),
|
|
76
|
+
steps: stringList(record["steps"]),
|
|
77
|
+
capabilities: stringList(record["capabilities"]),
|
|
78
|
+
providers: stringList(record["providers"]),
|
|
79
|
+
},
|
|
80
|
+
];
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/** One feature's raw `secrets` or `settings` list, with the feature id. */
|
|
84
|
+
function featureLists(manifest, key) {
|
|
85
|
+
const features = manifest.features;
|
|
86
|
+
if (!features || typeof features !== "object")
|
|
87
|
+
return [];
|
|
88
|
+
return Object.entries(features).flatMap(([feature, raw]) => {
|
|
89
|
+
const list = raw && typeof raw === "object"
|
|
90
|
+
? raw[key]
|
|
91
|
+
: undefined;
|
|
92
|
+
return Array.isArray(list) ? [{ feature, list }] : [];
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/** The need of a capability or provider: optional when an optional feature lists it, else required. */
|
|
96
|
+
function gatewayNeedOf(features, kind, id) {
|
|
97
|
+
const owner = features.find((feature) => feature[kind].includes(id));
|
|
98
|
+
if (!owner)
|
|
99
|
+
return { need: { kind: "required" } };
|
|
100
|
+
return {
|
|
101
|
+
need: { kind: owner.required ? "required" : "optional" },
|
|
102
|
+
feature: owner.id,
|
|
55
103
|
};
|
|
56
104
|
}
|
|
57
105
|
/** The providers the manifest lists, each as an entry whatever form it took. */
|
|
@@ -69,50 +117,68 @@ export function providerEntries(manifest) {
|
|
|
69
117
|
export function providerIds(manifest) {
|
|
70
118
|
return providerEntries(manifest).map((entry) => entry.id);
|
|
71
119
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
120
|
+
function secretEntryOf(entry, feature) {
|
|
121
|
+
const tag = feature === undefined ? {} : { feature };
|
|
122
|
+
if (typeof entry === "string")
|
|
123
|
+
return [{ name: entry, ...tag }];
|
|
124
|
+
if (entry && typeof entry === "object") {
|
|
125
|
+
const name = entry.name;
|
|
126
|
+
if (typeof name === "string") {
|
|
127
|
+
return [{ name, ...needFields(entry), ...ownValueFields(entry), ...tag }];
|
|
79
128
|
}
|
|
80
|
-
|
|
81
|
-
|
|
129
|
+
}
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* The app's own secrets the manifest lists, each as an entry whatever form
|
|
134
|
+
* it took: the top-level ones, then each feature's (tagged with it).
|
|
135
|
+
*/
|
|
136
|
+
export function secretEntries(manifest) {
|
|
137
|
+
return [
|
|
138
|
+
...(manifest.secrets ?? []).flatMap((entry) => secretEntryOf(entry)),
|
|
139
|
+
...featureLists(manifest, "secrets").flatMap(({ feature, list }) => list.flatMap((entry) => secretEntryOf(entry, feature))),
|
|
140
|
+
];
|
|
82
141
|
}
|
|
83
142
|
/** The names of the app's own secrets, in order. */
|
|
84
143
|
export function secretNames(manifest) {
|
|
85
144
|
return secretEntries(manifest).map((entry) => entry.name);
|
|
86
145
|
}
|
|
87
|
-
|
|
146
|
+
function settingEntryOf(entry, feature) {
|
|
147
|
+
if (!entry || typeof entry !== "object")
|
|
148
|
+
return [];
|
|
149
|
+
const record = entry;
|
|
150
|
+
if (typeof record["name"] !== "string")
|
|
151
|
+
return [];
|
|
152
|
+
const type = record["type"];
|
|
153
|
+
const values = record["values"];
|
|
154
|
+
return [
|
|
155
|
+
{
|
|
156
|
+
name: record["name"],
|
|
157
|
+
...needFields(entry),
|
|
158
|
+
...(typeof record["how"] === "string" ? { how: record["how"] } : {}),
|
|
159
|
+
...(type === "url" ||
|
|
160
|
+
type === "number" ||
|
|
161
|
+
type === "boolean" ||
|
|
162
|
+
type === "text"
|
|
163
|
+
? { type }
|
|
164
|
+
: {}),
|
|
165
|
+
...(Array.isArray(values) &&
|
|
166
|
+
values.every((value) => typeof value === "string")
|
|
167
|
+
? { values: values }
|
|
168
|
+
: {}),
|
|
169
|
+
...(typeof record["default"] === "string"
|
|
170
|
+
? { default: record["default"] }
|
|
171
|
+
: {}),
|
|
172
|
+
...(feature === undefined ? {} : { feature }),
|
|
173
|
+
},
|
|
174
|
+
];
|
|
175
|
+
}
|
|
176
|
+
/** The app's plain settings the manifest lists, each as an entry: the top-level ones, then each feature's. */
|
|
88
177
|
export function settingEntries(manifest) {
|
|
89
|
-
return
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
});
|
|
178
|
+
return [
|
|
179
|
+
...(manifest.settings ?? []).flatMap((entry) => settingEntryOf(entry)),
|
|
180
|
+
...featureLists(manifest, "settings").flatMap(({ feature, list }) => list.flatMap((entry) => settingEntryOf(entry, feature))),
|
|
181
|
+
];
|
|
116
182
|
}
|
|
117
183
|
/** The names of the app's plain settings, in order. */
|
|
118
184
|
export function settingNames(manifest) {
|
|
@@ -256,19 +322,59 @@ function providerHint(capability, names) {
|
|
|
256
322
|
];
|
|
257
323
|
return lines.length > 0 ? { hint: lines.join(" ") } : {};
|
|
258
324
|
}
|
|
259
|
-
function withNeed(setting, entry, defaults = {}) {
|
|
325
|
+
function withNeed(setting, entry, need, defaults = {}) {
|
|
260
326
|
const docs = entry?.docs ?? defaults.docs;
|
|
261
|
-
// The registry's guide for a Gateway setting; the manifest's for the app's own secret.
|
|
262
|
-
const setup = setting.holder === "gateway" ? defaults.setup : entry?.setup;
|
|
263
327
|
return {
|
|
264
328
|
...setting,
|
|
265
|
-
need:
|
|
329
|
+
need: need.need,
|
|
330
|
+
...(need.feature === undefined ? {} : { feature: need.feature }),
|
|
266
331
|
...(entry?.why ? { why: entry.why } : {}),
|
|
267
332
|
...(docs ? { docs } : {}),
|
|
268
|
-
...(setup ? { setup } : {}),
|
|
333
|
+
...(defaults.setup ? { setup: defaults.setup } : {}),
|
|
269
334
|
...(defaults.hint ? { hint: defaults.hint } : {}),
|
|
270
335
|
};
|
|
271
336
|
}
|
|
337
|
+
/**
|
|
338
|
+
* A stored manifest from before features (deployed before 0.3.0-rc.14) marks a
|
|
339
|
+
* value `optional: true`. The strict schema refuses that key now, but a stored
|
|
340
|
+
* row keeps it until the app is redeployed, so it still reads as optional:
|
|
341
|
+
* updating the Gateway never puts a working app into Needs setup.
|
|
342
|
+
*/
|
|
343
|
+
function storedOptional(need, raw) {
|
|
344
|
+
if (need.feature !== undefined || need.need.kind === "optional")
|
|
345
|
+
return need;
|
|
346
|
+
const optional = raw !== null &&
|
|
347
|
+
typeof raw === "object" &&
|
|
348
|
+
raw["optional"] === true;
|
|
349
|
+
return optional ? { need: { kind: "optional" } } : need;
|
|
350
|
+
}
|
|
351
|
+
/** The raw entry named `name` in a stored list (`secrets`, `settings`, `providers`). */
|
|
352
|
+
function rawEntry(list, name, key) {
|
|
353
|
+
if (!Array.isArray(list))
|
|
354
|
+
return undefined;
|
|
355
|
+
return list.find((entry) => entry !== null &&
|
|
356
|
+
typeof entry === "object" &&
|
|
357
|
+
entry[key] === name);
|
|
358
|
+
}
|
|
359
|
+
/** An app's own value's need: its feature's (required, or optional), or required at the top level. */
|
|
360
|
+
function ownNeedOf(features, feature) {
|
|
361
|
+
if (feature === undefined)
|
|
362
|
+
return { need: { kind: "required" } };
|
|
363
|
+
const owner = features.find((each) => each.id === feature);
|
|
364
|
+
return {
|
|
365
|
+
need: { kind: owner?.required ? "required" : "optional" },
|
|
366
|
+
feature,
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
/** `how`, `generate` and `file` onto an app's own setting, when its entry says them. */
|
|
370
|
+
function withOwnValue(setting, entry) {
|
|
371
|
+
return {
|
|
372
|
+
...setting,
|
|
373
|
+
...(entry.how ? { how: entry.how } : {}),
|
|
374
|
+
...(entry.generate ? { generate: true } : {}),
|
|
375
|
+
...(entry.file ? { file: true } : {}),
|
|
376
|
+
};
|
|
377
|
+
}
|
|
272
378
|
/**
|
|
273
379
|
* Every setting the manifest declares, in manifest order: its AI
|
|
274
380
|
* providers, its capabilities, then the app's own secrets. A provider or capability the
|
|
@@ -279,6 +385,7 @@ function withNeed(setting, entry, defaults = {}) {
|
|
|
279
385
|
*/
|
|
280
386
|
export function appSettings(manifest, lookup = CATALOG, options = {}) {
|
|
281
387
|
const settings = [];
|
|
388
|
+
const features = featureEntries(manifest);
|
|
282
389
|
const chat = manifest.ai?.chat;
|
|
283
390
|
if (chat === true || (chat && typeof chat === "object")) {
|
|
284
391
|
// The default resolves on what is set: with nothing set, the first of
|
|
@@ -302,24 +409,25 @@ export function appSettings(manifest, lookup = CATALOG, options = {}) {
|
|
|
302
409
|
alternatives: resolution.candidates
|
|
303
410
|
.map((candidate) => settable(candidate.settings))
|
|
304
411
|
.filter((names) => names.length > 0),
|
|
305
|
-
}, chat === true ? undefined : chat, {
|
|
412
|
+
}, chat === true ? undefined : needFields(chat), storedOptional({ need: { kind: "required" } }, chat), {
|
|
306
413
|
...(provider?.mode === "ai" ? { docs: provider.docs } : {}),
|
|
307
414
|
setup: "setup/ai.md",
|
|
308
415
|
hint: aiDefaultSetupHint(resolution),
|
|
309
416
|
}));
|
|
310
417
|
}
|
|
311
|
-
// The default's settings: an optional pin
|
|
312
|
-
//
|
|
313
|
-
//
|
|
314
|
-
// serves the default.
|
|
418
|
+
// The default's settings: an optional pin (a provider an optional
|
|
419
|
+
// feature lists) that needs only those is the same row (one key, set
|
|
420
|
+
// once), so it isn't listed twice. A required pin stays: it needs that
|
|
421
|
+
// provider's key even while another provider serves the default.
|
|
315
422
|
const defaultNames = settings.find((setting) => setting.kind === "ai-default")?.names;
|
|
316
423
|
for (const entry of providerEntries(manifest)) {
|
|
317
424
|
const capability = lookup.get(entry.id);
|
|
318
425
|
if (capability?.mode !== "ai")
|
|
319
426
|
continue;
|
|
320
427
|
const names = providerNames(capability);
|
|
428
|
+
const need = storedOptional(gatewayNeedOf(features, "providers", entry.id), rawEntry(manifest.providers, entry.id, "id"));
|
|
321
429
|
if (defaultNames &&
|
|
322
|
-
|
|
430
|
+
need.need.kind === "optional" &&
|
|
323
431
|
names.length > 0 &&
|
|
324
432
|
names.every((name) => defaultNames.includes(name))) {
|
|
325
433
|
continue;
|
|
@@ -331,7 +439,7 @@ export function appSettings(manifest, lookup = CATALOG, options = {}) {
|
|
|
331
439
|
holder: "gateway",
|
|
332
440
|
names,
|
|
333
441
|
alsoReads: [],
|
|
334
|
-
}, entry, {
|
|
442
|
+
}, entry, need, {
|
|
335
443
|
docs: capability.docs,
|
|
336
444
|
setup: capability.setup,
|
|
337
445
|
...providerHint(capability, names),
|
|
@@ -350,9 +458,11 @@ export function appSettings(manifest, lookup = CATALOG, options = {}) {
|
|
|
350
458
|
holder: "gateway",
|
|
351
459
|
names,
|
|
352
460
|
alsoReads,
|
|
353
|
-
}, needFields(entry), {
|
|
461
|
+
}, needFields(entry), storedOptional(gatewayNeedOf(features, "capabilities", id), entry), {
|
|
354
462
|
docs: capabilityDocs(capability),
|
|
355
|
-
setup: capability.
|
|
463
|
+
setup: capability.mode === "oauth_client"
|
|
464
|
+
? oauthSetupGuide(id)
|
|
465
|
+
: capability.setup,
|
|
356
466
|
...providerHint(capability, names),
|
|
357
467
|
}));
|
|
358
468
|
}
|
|
@@ -376,27 +486,30 @@ export function appSettings(manifest, lookup = CATALOG, options = {}) {
|
|
|
376
486
|
});
|
|
377
487
|
}
|
|
378
488
|
for (const entry of secretEntries(manifest)) {
|
|
379
|
-
settings.push(withNeed({
|
|
489
|
+
settings.push(withOwnValue(withNeed({
|
|
380
490
|
kind: "secret",
|
|
381
491
|
id: entry.name,
|
|
382
492
|
label: entry.name,
|
|
383
493
|
holder: "app",
|
|
384
494
|
names: [entry.name],
|
|
385
495
|
alsoReads: [],
|
|
386
|
-
}, entry));
|
|
496
|
+
}, entry, storedOptional(ownNeedOf(features, entry.feature), rawEntry(manifest.secrets, entry.name, "name"))), entry));
|
|
387
497
|
}
|
|
388
498
|
for (const entry of settingEntries(manifest)) {
|
|
389
|
-
|
|
390
|
-
const need = entry.default !== undefined ? { ...entry, optional: true } : entry;
|
|
499
|
+
const own = storedOptional(ownNeedOf(features, entry.feature), rawEntry(manifest.settings, entry.name, "name"));
|
|
391
500
|
settings.push({
|
|
392
|
-
...withNeed({
|
|
501
|
+
...withOwnValue(withNeed({
|
|
393
502
|
kind: "setting",
|
|
394
503
|
id: entry.name,
|
|
395
504
|
label: entry.name,
|
|
396
505
|
holder: "app",
|
|
397
506
|
names: [entry.name],
|
|
398
507
|
alsoReads: [],
|
|
399
|
-
},
|
|
508
|
+
}, entry,
|
|
509
|
+
// A setting with a default is never unset for long: the deploy writes it.
|
|
510
|
+
entry.default !== undefined
|
|
511
|
+
? { ...own, need: { kind: "optional" } }
|
|
512
|
+
: own), entry),
|
|
400
513
|
value: settingValueRule(entry),
|
|
401
514
|
});
|
|
402
515
|
}
|
|
@@ -433,12 +546,24 @@ function settingIsSet(setting, isSet) {
|
|
|
433
546
|
}
|
|
434
547
|
return unknown ? null : false;
|
|
435
548
|
}
|
|
549
|
+
/**
|
|
550
|
+
* A feature's state from its settings (see {@link FeatureState}). Whether
|
|
551
|
+
* it is started is read from its own values that have no default (a
|
|
552
|
+
* default is written by the deploy, so it says nothing), or, for a
|
|
553
|
+
* feature of Gateway settings alone, from those.
|
|
554
|
+
*/
|
|
555
|
+
function featureState(settings) {
|
|
556
|
+
if (settings.every((setting) => setting.set !== false))
|
|
557
|
+
return "ready";
|
|
558
|
+
const own = settings.filter((setting) => setting.holder === "app" && setting.value?.default === undefined);
|
|
559
|
+
const started = (own.length > 0 ? own : settings).some((setting) => setting.set === true);
|
|
560
|
+
return started ? "incomplete" : "off";
|
|
561
|
+
}
|
|
436
562
|
/**
|
|
437
563
|
* Every setting against what is set. `isSet` answers per holder and name:
|
|
438
564
|
* true, false, or null when it can't tell (the registry never recorded
|
|
439
|
-
* the app Worker's secret names), which never blocks.
|
|
440
|
-
*
|
|
441
|
-
* member blocks.
|
|
565
|
+
* the app Worker's secret names), which never blocks. A required setting
|
|
566
|
+
* that is unset blocks; an optional one (an optional feature's) never does.
|
|
442
567
|
*/
|
|
443
568
|
export function evaluateAppSettings(settings, isSet, options = {}) {
|
|
444
569
|
const evaluated = settings.map((setting) => {
|
|
@@ -449,47 +574,30 @@ export function evaluateAppSettings(settings, isSet, options = {}) {
|
|
|
449
574
|
blocking: false,
|
|
450
575
|
};
|
|
451
576
|
});
|
|
452
|
-
const groups = [];
|
|
453
|
-
for (const setting of evaluated) {
|
|
454
|
-
if (setting.need.kind !== "anyOf")
|
|
455
|
-
continue;
|
|
456
|
-
const group = setting.need.group;
|
|
457
|
-
let found = groups.find((each) => each.group === group);
|
|
458
|
-
if (!found) {
|
|
459
|
-
found = { group, members: [], met: false };
|
|
460
|
-
groups.push(found);
|
|
461
|
-
}
|
|
462
|
-
found.members.push(setting);
|
|
463
|
-
}
|
|
464
|
-
for (const group of groups) {
|
|
465
|
-
group.met = group.members.some((member) => member.set === true);
|
|
466
|
-
const unknown = group.members.some((member) => member.set === null);
|
|
467
|
-
if (!group.met && !unknown) {
|
|
468
|
-
for (const member of group.members)
|
|
469
|
-
member.blocking = true;
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
577
|
for (const setting of evaluated) {
|
|
473
578
|
if (setting.need.kind === "required" && setting.set === false) {
|
|
474
579
|
setting.blocking = true;
|
|
475
580
|
}
|
|
476
581
|
}
|
|
477
582
|
const unmet = evaluated.filter((setting) => setting.blocking);
|
|
478
|
-
|
|
583
|
+
const features = (options.features ?? []).map((feature) => {
|
|
584
|
+
const own = evaluated.filter((setting) => setting.feature === feature.id);
|
|
585
|
+
return { ...feature, settings: own, state: featureState(own) };
|
|
586
|
+
});
|
|
587
|
+
return { ready: unmet.length === 0, settings: evaluated, unmet, features };
|
|
479
588
|
}
|
|
480
589
|
/**
|
|
481
590
|
* What to run for an app that needs setup: `everyapp configure <app>`,
|
|
482
591
|
* which reads the app's manifest (from the registry, so it runs from
|
|
483
|
-
* anywhere), prompts for each unmet setting
|
|
484
|
-
*
|
|
592
|
+
* anywhere), prompts for each unmet setting, feature by feature, and sets
|
|
593
|
+
* them where they are held. Null when
|
|
485
594
|
* the app is ready. `cli` is how the operator runs everyapp (`pnpm
|
|
486
595
|
* everyapp` from a checkout). Scripts that set one name at a time use
|
|
487
596
|
* `everyapp secret set NAME [--app <id>]` instead.
|
|
488
597
|
*
|
|
489
598
|
* A provider app (`provider`: the gateway's own, with no Worker and no
|
|
490
599
|
* directory; `everyapp mcp on <id>`, an MCP app) has nothing to configure: its
|
|
491
|
-
* command is the `secret set` of its Gateway key
|
|
492
|
-
* unmet any-of group), as `mcp list` says it.
|
|
600
|
+
* command is the `secret set` of its Gateway key, as `mcp list` says it.
|
|
493
601
|
*/
|
|
494
602
|
export function setupCommand(report, { cli, appId, provider = false, }) {
|
|
495
603
|
if (report.unmet.length === 0)
|
|
@@ -506,15 +614,8 @@ export function setupCommand(report, { cli, appId, provider = false, }) {
|
|
|
506
614
|
if (!names.includes(name))
|
|
507
615
|
names.push(name);
|
|
508
616
|
};
|
|
509
|
-
for (const setting of report.unmet)
|
|
510
|
-
|
|
511
|
-
add(setting);
|
|
512
|
-
}
|
|
513
|
-
for (const group of report.groups) {
|
|
514
|
-
const first = group.members.find((member) => member.blocking);
|
|
515
|
-
if (first)
|
|
516
|
-
add(first);
|
|
517
|
-
}
|
|
617
|
+
for (const setting of report.unmet)
|
|
618
|
+
add(setting);
|
|
518
619
|
return { command: `${cli} secret set ${names.join(" ")}` };
|
|
519
620
|
}
|
|
520
621
|
export function appSetupOf(report, options) {
|
|
@@ -523,10 +624,9 @@ export function appSetupOf(report, options) {
|
|
|
523
624
|
ready: report.ready,
|
|
524
625
|
settings: report.settings.map(strip),
|
|
525
626
|
unmet: report.unmet.map(strip),
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
members: group.members.map((member) => member.id),
|
|
627
|
+
features: report.features.map((feature) => ({
|
|
628
|
+
...feature,
|
|
629
|
+
settings: feature.settings.map((setting) => setting.id),
|
|
530
630
|
})),
|
|
531
631
|
command: options.withCommand === false ? null : setupCommand(report, options),
|
|
532
632
|
};
|
|
@@ -540,3 +640,18 @@ export function apiKeyCapabilityOf(id, lookup = CATALOG) {
|
|
|
540
640
|
const capability = lookup.get(id);
|
|
541
641
|
return capability?.mode === "api_key" ? capability : undefined;
|
|
542
642
|
}
|
|
643
|
+
/**
|
|
644
|
+
* A fresh value for a secret the manifest marks `generate: true`: 32
|
|
645
|
+
* bytes from the platform's cryptographic random source, base64url
|
|
646
|
+
* without padding (43 characters).
|
|
647
|
+
*/
|
|
648
|
+
export function generatedSecret() {
|
|
649
|
+
const bytes = crypto.getRandomValues(new Uint8Array(32));
|
|
650
|
+
let binary = "";
|
|
651
|
+
for (const byte of bytes)
|
|
652
|
+
binary += String.fromCharCode(byte);
|
|
653
|
+
return btoa(binary)
|
|
654
|
+
.replace(/\+/g, "-")
|
|
655
|
+
.replace(/\//g, "_")
|
|
656
|
+
.replace(/=+$/, "");
|
|
657
|
+
}
|
|
@@ -16,9 +16,10 @@
|
|
|
16
16
|
* proxy's rules: inference routes only, the model allowlist, the usage
|
|
17
17
|
* tap. Never a capability: its key would reach the account's credits
|
|
18
18
|
* and keys on the same host.
|
|
19
|
-
* - `oauth_client`: an OAuth provider
|
|
20
|
-
*
|
|
21
|
-
*
|
|
19
|
+
* - `oauth_client`: an OAuth provider whose client the gateway holds and
|
|
20
|
+
* whose flow it runs. None is in the catalog: each is a registered row,
|
|
21
|
+
* written from a preset (./oauthPresets.ts: Google, Microsoft, Slack,
|
|
22
|
+
* GitHub and the rest) or an operator's spec. A person connects in an app,
|
|
22
23
|
* for exactly the provider scopes the app's manifest entry lists
|
|
23
24
|
* (`scopes`); the gateway keeps the tokens and puts the person's token
|
|
24
25
|
* on the app's calls to the provider's `api` hosts. Each provider is a
|
|
@@ -396,8 +397,6 @@ export interface OAuthClientCapability {
|
|
|
396
397
|
};
|
|
397
398
|
/** Where the operator registers the client (the spec's `docs`). */
|
|
398
399
|
console: string;
|
|
399
|
-
/** The everyapp skill's setup guide for the client, when one exists. */
|
|
400
|
-
setup?: string;
|
|
401
400
|
/** The only hosts the held token is ever sent to, over https. */
|
|
402
401
|
api: readonly string[];
|
|
403
402
|
flow: OAuthFlow;
|
|
@@ -2631,24 +2630,6 @@ export declare const CAPABILITIES: {
|
|
|
2631
2630
|
readonly settings: readonly [];
|
|
2632
2631
|
readonly optionalSettings: readonly [];
|
|
2633
2632
|
};
|
|
2634
|
-
readonly slack: OAuthClientCapability & {
|
|
2635
|
-
secrets: {
|
|
2636
|
-
clientId: "SLACK_CLIENT_ID";
|
|
2637
|
-
clientSecret: "SLACK_CLIENT_SECRET";
|
|
2638
|
-
};
|
|
2639
|
-
};
|
|
2640
|
-
readonly "google-workspace": OAuthClientCapability & {
|
|
2641
|
-
secrets: {
|
|
2642
|
-
clientId: "GOOGLE_WORKSPACE_CLIENT_ID";
|
|
2643
|
-
clientSecret: "GOOGLE_WORKSPACE_CLIENT_SECRET";
|
|
2644
|
-
};
|
|
2645
|
-
};
|
|
2646
|
-
readonly "microsoft-365": OAuthClientCapability & {
|
|
2647
|
-
secrets: {
|
|
2648
|
-
clientId: "MICROSOFT_365_CLIENT_ID";
|
|
2649
|
-
clientSecret: "MICROSOFT_365_CLIENT_SECRET";
|
|
2650
|
-
};
|
|
2651
|
-
};
|
|
2652
2633
|
};
|
|
2653
2634
|
export type CapabilityId = keyof typeof CAPABILITIES;
|
|
2654
2635
|
type CapabilityIdOf<Mode extends Capability["mode"]> = {
|
|
@@ -2660,8 +2641,6 @@ export type ApiKeyCapabilityId = CapabilityIdOf<"api_key">;
|
|
|
2660
2641
|
export type AiProviderId = CapabilityIdOf<"ai">;
|
|
2661
2642
|
/** Capabilities the gateway performs itself, each with its SDK helper. */
|
|
2662
2643
|
export type ServiceCapabilityId = CapabilityIdOf<"service">;
|
|
2663
|
-
/** The built-in OAuth providers whose APIs apps reach with `integrationFetch()`. */
|
|
2664
|
-
export type OAuthClientCapabilityId = CapabilityIdOf<"oauth_client">;
|
|
2665
2644
|
/** The Gateway settings an `auth` block names: the key, and a Basic username held as a setting. */
|
|
2666
2645
|
type AuthSettingsOf<A> = (A extends {
|
|
2667
2646
|
secret: infer S extends string;
|
|
@@ -2674,9 +2653,6 @@ type SettingsOf<C> = C extends {
|
|
|
2674
2653
|
settings: readonly (infer S extends string)[];
|
|
2675
2654
|
optionalSettings: readonly (infer O extends string)[];
|
|
2676
2655
|
} ? S | O : C extends {
|
|
2677
|
-
secrets: infer S;
|
|
2678
|
-
flow: unknown;
|
|
2679
|
-
} ? S[keyof S] & string : C extends {
|
|
2680
2656
|
auth: infer A;
|
|
2681
2657
|
} ? AuthSettingsOf<A> | (C extends {
|
|
2682
2658
|
alsoNeeds: readonly (infer N extends string)[];
|
|
@@ -2745,7 +2721,7 @@ export declare const INTEGRATION_PROXY_PREFIX = "/api/integrations";
|
|
|
2745
2721
|
* `/api/integrations/<id>/<host><path>?<query>`. The host is in the path so
|
|
2746
2722
|
* a provider with several API hosts needs no other channel.
|
|
2747
2723
|
*/
|
|
2748
|
-
export declare function integrationProxyPath(id: ApiKeyCapabilityId |
|
|
2724
|
+
export declare function integrationProxyPath(id: ApiKeyCapabilityId | (string & {}), upstream: URL): string;
|
|
2749
2725
|
/** The AppGateway path of a service capability's route: `/api/integrations/<id>/<route>`. */
|
|
2750
2726
|
export declare function serviceRoutePath(id: ServiceCapabilityId, route: string): string;
|
|
2751
2727
|
/** What an AppGateway capability path names. */
|