@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.
Files changed (42) hide show
  1. package/README.md +15 -14
  2. package/dist/cloudflare/catalogSettings.generated.d.ts +17 -7
  3. package/dist/cloudflare/catalogSettings.generated.d.ts.map +1 -1
  4. package/dist/cloudflare/catalogSettings.generated.js +40 -12
  5. package/dist/cloudflare/worker.d.ts +27 -7
  6. package/dist/cloudflare/worker.d.ts.map +1 -1
  7. package/dist/cloudflare/worker.js +13 -3
  8. package/dist/index.d.ts +1 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/internal/appSettings.d.ts +124 -79
  11. package/dist/internal/appSettings.d.ts.map +1 -1
  12. package/dist/internal/appSettings.js +240 -125
  13. package/dist/internal/capabilities.d.ts +5 -29
  14. package/dist/internal/capabilities.d.ts.map +1 -1
  15. package/dist/internal/capabilities.js +4 -24
  16. package/dist/internal/devProviders.d.ts +1 -19
  17. package/dist/internal/devProviders.d.ts.map +1 -1
  18. package/dist/internal/devProviders.js +7 -1
  19. package/dist/internal/oauthPresets.d.ts +351 -14
  20. package/dist/internal/oauthPresets.d.ts.map +1 -1
  21. package/dist/internal/oauthPresets.js +155 -22
  22. package/dist/internal/oauthSpec.d.ts +2 -46
  23. package/dist/internal/oauthSpec.d.ts.map +1 -1
  24. package/dist/internal/oauthSpec.js +0 -57
  25. package/dist/internal/remoteMcp.d.ts.map +1 -1
  26. package/dist/internal/remoteMcp.js +4 -3
  27. package/dist/internal/tools.d.ts +3 -1
  28. package/dist/internal/tools.d.ts.map +1 -1
  29. package/dist/manifest.d.ts +49 -13
  30. package/dist/manifest.d.ts.map +1 -1
  31. package/dist/server/index.d.ts +1 -1
  32. package/dist/server/index.d.ts.map +1 -1
  33. package/dist/server/integrationFetch.d.ts +7 -7
  34. package/dist/server/integrationFetch.d.ts.map +1 -1
  35. package/dist/server/integrationFetch.js +4 -2
  36. package/dist/server/tools.d.ts +5 -2
  37. package/dist/server/tools.d.ts.map +1 -1
  38. package/dist/server/tools.js +5 -2
  39. package/package.json +1 -1
  40. package/dist/internal/oauthBuiltIns.d.ts +0 -101
  41. package/dist/internal/oauthBuiltIns.d.ts.map +0 -1
  42. 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. Each declaration carries a
12
- * need: required (the default; the app doesn't open until it is set),
13
- * optional (the app opens, and the feature says what is missing where it
14
- * is used), or a member of a named any-of group (the app opens once any
15
- * member is set; only for an app that picks among the set members at run
16
- * time). Each may say `why` it is needed and where it comes from
17
- * (`docs`, an https URL); for providers and capabilities the registries
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.
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
- ...(typeof record["optional"] === "boolean"
51
- ? { optional: record["optional"] }
52
- : {}),
53
- ...(typeof record["setup"] === "string" ? { setup: record["setup"] } : {}),
54
- ...(typeof record["anyOf"] === "string" ? { anyOf: record["anyOf"] } : {}),
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
- /** The app's own secrets the manifest lists, each as an entry whatever form it took. */
73
- export function secretEntries(manifest) {
74
- return (manifest.secrets ?? []).flatMap((entry) => {
75
- if (typeof entry === "string")
76
- return [{ name: entry }];
77
- if (entry && typeof entry === "object" && typeof entry.name === "string") {
78
- return [{ name: entry.name, ...needFields(entry) }];
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
- return [];
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
- /** The app's plain settings the manifest lists, each as an entry. */
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 (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
- });
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: needOf(entry),
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 that needs only those is the
312
- // same row (one key, set once), so it isn't listed twice. A required
313
- // pin stays: it needs that provider's key even while another provider
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
- needOf(entry).kind === "optional" &&
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.setup,
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
- // A setting with a default is never unset for long: the deploy writes it.
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
- }, need),
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. An any-of group is
440
- * met once any member is set; while none is and none is unknown, every
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
- return { ready: unmet.length === 0, settings: evaluated, groups, unmet };
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 (one member of an any-of group
484
- * is enough: it asks which) and sets them where they are held. Null when
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 (the first member of an
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
- if (setting.need.kind === "required")
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
- groups: report.groups.map((group) => ({
527
- group: group.group,
528
- met: group.met,
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 (Google, Microsoft, Slack built
20
- * in; GitHub and others as presets, ./oauthPresets.ts) whose client the
21
- * gateway holds and whose flow it runs. A person connects in an app,
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 | OAuthClientCapabilityId | (string & {}), upstream: URL): string;
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. */