@every-app/sdk 0.3.0-rc.1 → 0.3.0-rc.10

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 (163) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1 -1
  3. package/dist/cloudflare/catalogSettings.generated.d.ts +81 -0
  4. package/dist/cloudflare/catalogSettings.generated.d.ts.map +1 -0
  5. package/dist/cloudflare/catalogSettings.generated.js +174 -0
  6. package/dist/cloudflare/index.d.ts +3 -2
  7. package/dist/cloudflare/index.d.ts.map +1 -1
  8. package/dist/cloudflare/index.js +4 -1
  9. package/dist/cloudflare/sentinel.d.ts +60 -2
  10. package/dist/cloudflare/sentinel.d.ts.map +1 -1
  11. package/dist/cloudflare/sentinel.js +37 -2
  12. package/dist/cloudflare/worker.d.ts +357 -63
  13. package/dist/cloudflare/worker.d.ts.map +1 -1
  14. package/dist/cloudflare/worker.js +363 -104
  15. package/dist/dev/index.d.ts +1 -1
  16. package/dist/dev/index.d.ts.map +1 -1
  17. package/dist/dev/index.js +1 -1
  18. package/dist/dev/keypair.d.ts +2 -0
  19. package/dist/dev/keypair.d.ts.map +1 -1
  20. package/dist/dev/keypair.js +2 -0
  21. package/dist/index.d.ts +6 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +5 -0
  24. package/dist/internal/aiDefault.d.ts +204 -0
  25. package/dist/internal/aiDefault.d.ts.map +1 -0
  26. package/dist/internal/aiDefault.js +334 -0
  27. package/dist/internal/aiProviders.d.ts +7 -2
  28. package/dist/internal/aiProviders.d.ts.map +1 -1
  29. package/dist/internal/aiProviders.js +17 -4
  30. package/dist/internal/appSettings.d.ts +82 -4
  31. package/dist/internal/appSettings.d.ts.map +1 -1
  32. package/dist/internal/appSettings.js +204 -29
  33. package/dist/internal/audit.d.ts +108 -0
  34. package/dist/internal/audit.d.ts.map +1 -0
  35. package/dist/internal/audit.js +235 -0
  36. package/dist/internal/capabilities.d.ts +2268 -438
  37. package/dist/internal/capabilities.d.ts.map +1 -1
  38. package/dist/internal/capabilities.js +128 -539
  39. package/dist/internal/catalog/commerce.d.ts +394 -0
  40. package/dist/internal/catalog/commerce.d.ts.map +1 -0
  41. package/dist/internal/catalog/commerce.js +477 -0
  42. package/dist/internal/catalog/comms.d.ts +375 -0
  43. package/dist/internal/catalog/comms.d.ts.map +1 -0
  44. package/dist/internal/catalog/comms.js +520 -0
  45. package/dist/internal/catalog/index.d.ts +1973 -0
  46. package/dist/internal/catalog/index.d.ts.map +1 -0
  47. package/dist/internal/catalog/index.js +19 -0
  48. package/dist/internal/catalog/inference.d.ts +393 -0
  49. package/dist/internal/catalog/inference.d.ts.map +1 -0
  50. package/dist/internal/catalog/inference.js +460 -0
  51. package/dist/internal/catalog/infra.d.ts +423 -0
  52. package/dist/internal/catalog/infra.d.ts.map +1 -0
  53. package/dist/internal/catalog/infra.js +543 -0
  54. package/dist/internal/catalog/search.d.ts +400 -0
  55. package/dist/internal/catalog/search.d.ts.map +1 -0
  56. package/dist/internal/catalog/search.js +463 -0
  57. package/dist/internal/devProviders.d.ts +2 -2
  58. package/dist/internal/devProviders.d.ts.map +1 -1
  59. package/dist/internal/devProviders.js +19 -1
  60. package/dist/internal/events.d.ts +13 -2
  61. package/dist/internal/events.d.ts.map +1 -1
  62. package/dist/internal/events.js +47 -4
  63. package/dist/internal/gatewaySettings.d.ts +3 -2
  64. package/dist/internal/gatewaySettings.d.ts.map +1 -1
  65. package/dist/internal/gatewaySettings.js +12 -6
  66. package/dist/internal/identity.d.ts +41 -4
  67. package/dist/internal/identity.d.ts.map +1 -1
  68. package/dist/internal/identity.js +48 -10
  69. package/dist/internal/index.d.ts +6 -1
  70. package/dist/internal/index.d.ts.map +1 -1
  71. package/dist/internal/index.js +6 -1
  72. package/dist/internal/members.d.ts +3 -2
  73. package/dist/internal/members.d.ts.map +1 -1
  74. package/dist/internal/oauthClient.d.ts +10 -12
  75. package/dist/internal/oauthClient.d.ts.map +1 -1
  76. package/dist/internal/oauthClient.js +20 -81
  77. package/dist/internal/oauthPresets.d.ts +33 -0
  78. package/dist/internal/oauthPresets.d.ts.map +1 -0
  79. package/dist/internal/oauthPresets.js +1064 -0
  80. package/dist/internal/oauthScopes.d.ts +56 -1
  81. package/dist/internal/oauthScopes.d.ts.map +1 -1
  82. package/dist/internal/oauthScopes.js +98 -4
  83. package/dist/internal/oauthSpec.d.ts +102 -0
  84. package/dist/internal/oauthSpec.d.ts.map +1 -0
  85. package/dist/internal/oauthSpec.js +789 -0
  86. package/dist/internal/protocol.d.ts +64 -1
  87. package/dist/internal/protocol.d.ts.map +1 -1
  88. package/dist/internal/protocol.js +51 -1
  89. package/dist/internal/providerRegistry.d.ts +77 -6
  90. package/dist/internal/providerRegistry.d.ts.map +1 -1
  91. package/dist/internal/providerRegistry.js +178 -8
  92. package/dist/internal/providerSpec.d.ts +18 -7
  93. package/dist/internal/providerSpec.d.ts.map +1 -1
  94. package/dist/internal/providerSpec.js +442 -34
  95. package/dist/internal/signInProviders.d.ts +104 -0
  96. package/dist/internal/signInProviders.d.ts.map +1 -0
  97. package/dist/internal/signInProviders.js +245 -0
  98. package/dist/manifest.d.ts +238 -0
  99. package/dist/manifest.d.ts.map +1 -0
  100. package/dist/manifest.js +30 -0
  101. package/dist/server/ai.d.ts +111 -0
  102. package/dist/server/ai.d.ts.map +1 -0
  103. package/dist/server/ai.js +145 -0
  104. package/dist/server/aiBinding.d.ts +68 -0
  105. package/dist/server/aiBinding.d.ts.map +1 -0
  106. package/dist/server/aiBinding.js +294 -0
  107. package/dist/server/attribution.d.ts +9 -5
  108. package/dist/server/attribution.d.ts.map +1 -1
  109. package/dist/server/attribution.js +12 -4
  110. package/dist/server/authorizeUrl.d.ts +1 -1
  111. package/dist/server/authorizeUrl.d.ts.map +1 -1
  112. package/dist/server/authorizeUrl.js +2 -6
  113. package/dist/server/connectUrl.d.ts +21 -0
  114. package/dist/server/connectUrl.d.ts.map +1 -0
  115. package/dist/server/connectUrl.js +37 -0
  116. package/dist/server/context.d.ts +68 -0
  117. package/dist/server/context.d.ts.map +1 -0
  118. package/dist/server/context.js +117 -0
  119. package/dist/server/emailBinding.d.ts +72 -0
  120. package/dist/server/emailBinding.d.ts.map +1 -0
  121. package/dist/server/emailBinding.js +124 -0
  122. package/dist/server/emit.d.ts +1 -1
  123. package/dist/server/emit.d.ts.map +1 -1
  124. package/dist/server/emit.js +7 -7
  125. package/dist/server/env.d.ts +44 -0
  126. package/dist/server/env.d.ts.map +1 -0
  127. package/dist/server/env.js +1 -0
  128. package/dist/server/everyApp.d.ts +27 -7
  129. package/dist/server/everyApp.d.ts.map +1 -1
  130. package/dist/server/everyApp.js +106 -5
  131. package/dist/server/fromMcpTools.d.ts +89 -0
  132. package/dist/server/fromMcpTools.d.ts.map +1 -0
  133. package/dist/server/fromMcpTools.js +192 -0
  134. package/dist/server/gatewayFetch.d.ts +42 -18
  135. package/dist/server/gatewayFetch.d.ts.map +1 -1
  136. package/dist/server/gatewayFetch.js +138 -19
  137. package/dist/server/gatewayUrl.d.ts +13 -0
  138. package/dist/server/gatewayUrl.d.ts.map +1 -0
  139. package/dist/server/gatewayUrl.js +34 -0
  140. package/dist/server/index.d.ts +15 -4
  141. package/dist/server/index.d.ts.map +1 -1
  142. package/dist/server/index.js +12 -2
  143. package/dist/server/integrationFetch.d.ts +70 -39
  144. package/dist/server/integrationFetch.d.ts.map +1 -1
  145. package/dist/server/integrationFetch.js +244 -82
  146. package/dist/server/listMembers.d.ts +1 -1
  147. package/dist/server/listMembers.d.ts.map +1 -1
  148. package/dist/server/listMembers.js +8 -11
  149. package/dist/server/refusal.d.ts +55 -0
  150. package/dist/server/refusal.d.ts.map +1 -0
  151. package/dist/server/refusal.js +193 -0
  152. package/dist/server/sendEmail.d.ts +1 -1
  153. package/dist/server/sendEmail.d.ts.map +1 -1
  154. package/dist/server/sendEmail.js +7 -7
  155. package/dist/server/toolsEndpoint.d.ts.map +1 -1
  156. package/dist/server/toolsEndpoint.js +11 -5
  157. package/dist/tanstack-start/index.d.ts +9 -0
  158. package/dist/tanstack-start/index.d.ts.map +1 -0
  159. package/dist/tanstack-start/index.js +29 -0
  160. package/dist/testing/index.d.ts +93 -0
  161. package/dist/testing/index.d.ts.map +1 -0
  162. package/dist/testing/index.js +156 -0
  163. package/package.json +35 -14
@@ -0,0 +1,111 @@
1
+ /**
2
+ * `ai()`: the Gateway's default chat model for an app that needs "a model"
3
+ * rather than one provider (docs/design/2026-10-model-providers.md). The
4
+ * manifest says `ai: { chat: true }` (or `{ chat: { why } }`); the Gateway's
5
+ * operator decides which provider and model that is (`everyapp ai
6
+ * default`, else OpenRouter → Workers AI → OpenAI → …, the first with its
7
+ * key set), and the app never names either: it sends OpenAI-shaped chat
8
+ * completions (and embeddings) to `/api/ai/default/v1/…` with `model:
9
+ * "default"` or no model at all, and the Gateway fills the model in.
10
+ *
11
+ * An OpenAI client takes `baseURL` and `fetch` as they are:
12
+ *
13
+ * const { baseURL, fetch, model } = ai({ env, for: request });
14
+ * const client = new OpenAI({ baseURL, fetch, apiKey: "gateway-managed" });
15
+ * await client.chat.completions.create({ model, messages });
16
+ *
17
+ * In local development with no Gateway, the developer's own key in
18
+ * `.dev.vars` decides the provider by the same order (createGatewayFetch's
19
+ * fallback). An app that needs one provider in particular keeps listing it
20
+ * under `providers` and calling `/api/ai/<provider>/…`.
21
+ */
22
+ import { AI_DEFAULT_MODEL } from "../internal/index.js";
23
+ import { type GatewayCallFor } from "./attribution.js";
24
+ import { type CreateGatewayFetchOptions } from "./gatewayFetch.js";
25
+ import type { SetupInfo } from "./refusal.js";
26
+ export interface AiClient {
27
+ /** `http://every-app-gateway.internal/api/ai/default/v1`: an OpenAI client's base URL. */
28
+ baseURL: string;
29
+ /** The fetch that reaches the Gateway (or, in dev, the provider directly). */
30
+ fetch: typeof fetch;
31
+ /** The model name that means "whatever the Gateway's default is": `default`. */
32
+ model: typeof AI_DEFAULT_MODEL;
33
+ /** The default route's chat completions URL, for a plain `fetch`. */
34
+ chatCompletionsUrl: string;
35
+ /** The default route's embeddings URL, for a plain `fetch`. */
36
+ embeddingsUrl: string;
37
+ }
38
+ /** The base URL of the default route, as the SDK and the docs name it. */
39
+ export declare const AI_DEFAULT_BASE_URL = "http://every-app-gateway.internal/api/ai/default/v1";
40
+ /**
41
+ * The Gateway's default chat model as a client: a base URL, a fetch and
42
+ * the model name to send. Throws `ConfigurationError` where
43
+ * `createGatewayFetch` does (no binding and no dev key) and
44
+ * `AttributionError` where a shared app's call says nobody's.
45
+ */
46
+ export declare function ai(options: CreateGatewayFetchOptions & {
47
+ optional: true;
48
+ }): AiClient | null;
49
+ export declare function ai(options: CreateGatewayFetchOptions): AiClient;
50
+ /** Whether model calls to a provider (or the default) would go through. */
51
+ export interface AiStatus extends SetupInfo {
52
+ /** The id asked about: `default`, or a provider's (`openrouter`, `workers-ai`, …). */
53
+ provider: string;
54
+ /** The manifest asks for it: `ai.chat` for `default`, else `providers`. */
55
+ requested: boolean;
56
+ /** The Gateway (in local dev, `.dev.vars`) holds every setting it needs. */
57
+ configured: boolean;
58
+ /** Calls would go through. */
59
+ ready: boolean;
60
+ /**
61
+ * For `default`: the provider and model calls go to now, or null while
62
+ * none is configured. Null for a named provider.
63
+ */
64
+ resolved: {
65
+ provider: string;
66
+ label: string;
67
+ model: string;
68
+ } | null;
69
+ }
70
+ /**
71
+ * Whether the app can use a model right now, without calling one: gate
72
+ * an AI feature and show `setup` ("A chat model isn't set up on this
73
+ * Gateway: it needs OPENROUTER_API_KEY. An admin runs: pnpm everyapp
74
+ * configure inbox") up front instead of failing mid-task. `provider`
75
+ * defaults to `default`, the Gateway's default model (`ai: { chat: true }`);
76
+ * name one the manifest lists under `providers` to ask about it instead.
77
+ * The Gateway owns the wording: show `setup` and `command` as they are.
78
+ *
79
+ * Over the binding it sends `GET /api/ai/<provider>`; in local dev it
80
+ * answers from `.dev.vars` by the same rule `createGatewayFetch()` uses.
81
+ */
82
+ export declare function aiStatus({ env, provider, for: explicitTarget, }: {
83
+ env: object;
84
+ provider?: string;
85
+ /**
86
+ * Whom the status is for: the default resolves on that organization's
87
+ * choice (`everyapp ai default`). Left out, the request `everyApp()` is
88
+ * serving or the organization `withOrganization()` named, as on every
89
+ * call.
90
+ */
91
+ for?: GatewayCallFor;
92
+ }): Promise<AiStatus>;
93
+ /**
94
+ * A provider's root behind the Gateway, `http://every-app-gateway.internal/api/ai/<provider>`:
95
+ * join the provider's own paths onto it (`/api/v1/chat/completions` for
96
+ * OpenRouter) and send them with `createGatewayFetch()`. For an OpenAI
97
+ * client's `baseURL`, use {@link aiBaseUrl}.
98
+ */
99
+ export declare function providerBaseUrl(provider: string): string;
100
+ /**
101
+ * The OpenAI-compatible base URL of `provider` behind the Gateway, for an
102
+ * OpenAI client's `baseURL` (with `fetch: createGatewayFetch(...)`):
103
+ * `default` (the default) is {@link AI_DEFAULT_BASE_URL},
104
+ * `…/api/ai/default/v1`; a catalog provider's is its root and the prefix
105
+ * of its chat route (`…/api/ai/openrouter/api/v1`, `…/api/ai/workers-ai/v1`,
106
+ * `…/api/ai/openai/v1`). Throws `ConfigurationError` for a provider with
107
+ * no OpenAI-compatible chat route (Cohere): call {@link providerBaseUrl}
108
+ * with its own paths instead.
109
+ */
110
+ export declare function aiBaseUrl(provider?: string): string;
111
+ //# sourceMappingURL=ai.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai.d.ts","sourceRoot":"","sources":["../../src/server/ai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EACL,gBAAgB,EAOjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAIL,KAAK,yBAAyB,EAC/B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,MAAM,WAAW,QAAQ;IACvB,0FAA0F;IAC1F,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB,gFAAgF;IAChF,KAAK,EAAE,OAAO,gBAAgB,CAAC;IAC/B,qEAAqE;IACrE,kBAAkB,EAAE,MAAM,CAAC;IAC3B,+DAA+D;IAC/D,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,0EAA0E;AAC1E,eAAO,MAAM,mBAAmB,wDAAuD,CAAC;AAExF;;;;;GAKG;AACH,wBAAgB,EAAE,CAChB,OAAO,EAAE,yBAAyB,GAAG;IAAE,QAAQ,EAAE,IAAI,CAAA;CAAE,GACtD,QAAQ,GAAG,IAAI,CAAC;AACnB,wBAAgB,EAAE,CAAC,OAAO,EAAE,yBAAyB,GAAG,QAAQ,CAAC;AAajE,2EAA2E;AAC3E,MAAM,WAAW,QAAS,SAAQ,SAAS;IACzC,sFAAsF;IACtF,QAAQ,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,SAAS,EAAE,OAAO,CAAC;IACnB,4EAA4E;IAC5E,UAAU,EAAE,OAAO,CAAC;IACpB,8BAA8B;IAC9B,KAAK,EAAE,OAAO,CAAC;IACf;;;OAGG;IACH,QAAQ,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACrE;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,QAAQ,CAAC,EAC7B,GAAG,EACH,QAA8B,EAC9B,GAAG,EAAE,cAAc,GACpB,EAAE;IACD,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,GAAG,CAAC,EAAE,cAAc,CAAC;CACtB,GAAG,OAAO,CAAC,QAAQ,CAAC,CA+EpB;AAID;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,QAAQ,GAAE,MAA4B,GAAG,MAAM,CAaxE"}
@@ -0,0 +1,145 @@
1
+ /**
2
+ * `ai()`: the Gateway's default chat model for an app that needs "a model"
3
+ * rather than one provider (docs/design/2026-10-model-providers.md). The
4
+ * manifest says `ai: { chat: true }` (or `{ chat: { why } }`); the Gateway's
5
+ * operator decides which provider and model that is (`everyapp ai
6
+ * default`, else OpenRouter → Workers AI → OpenAI → …, the first with its
7
+ * key set), and the app never names either: it sends OpenAI-shaped chat
8
+ * completions (and embeddings) to `/api/ai/default/v1/…` with `model:
9
+ * "default"` or no model at all, and the Gateway fills the model in.
10
+ *
11
+ * An OpenAI client takes `baseURL` and `fetch` as they are:
12
+ *
13
+ * const { baseURL, fetch, model } = ai({ env, for: request });
14
+ * const client = new OpenAI({ baseURL, fetch, apiKey: "gateway-managed" });
15
+ * await client.chat.completions.create({ model, messages });
16
+ *
17
+ * In local development with no Gateway, the developer's own key in
18
+ * `.dev.vars` decides the provider by the same order (createGatewayFetch's
19
+ * fallback). An app that needs one provider in particular keeps listing it
20
+ * under `providers` and calling `/api/ai/<provider>/…`.
21
+ */
22
+ import { AI_DEFAULT_MODEL, AI_DEFAULT_PROVIDER, CATALOG, ConfigurationError, aiProviderSettings, devAiProvider, resolveAiDefault, } from "../internal/index.js";
23
+ import { applyAttribution, attributionFor, } from "./attribution.js";
24
+ import { BINDING_ORIGIN, createGatewayFetch, gatewayBinding, } from "./gatewayFetch.js";
25
+ /** The base URL of the default route, as the SDK and the docs name it. */
26
+ export const AI_DEFAULT_BASE_URL = `${BINDING_ORIGIN}/api/ai/${AI_DEFAULT_PROVIDER}/v1`;
27
+ export function ai(options) {
28
+ const fetch = createGatewayFetch(options);
29
+ if (!fetch)
30
+ return null;
31
+ return {
32
+ baseURL: AI_DEFAULT_BASE_URL,
33
+ fetch,
34
+ model: AI_DEFAULT_MODEL,
35
+ chatCompletionsUrl: `${AI_DEFAULT_BASE_URL}/chat/completions`,
36
+ embeddingsUrl: `${AI_DEFAULT_BASE_URL}/embeddings`,
37
+ };
38
+ }
39
+ /**
40
+ * Whether the app can use a model right now, without calling one: gate
41
+ * an AI feature and show `setup` ("A chat model isn't set up on this
42
+ * Gateway: it needs OPENROUTER_API_KEY. An admin runs: pnpm everyapp
43
+ * configure inbox") up front instead of failing mid-task. `provider`
44
+ * defaults to `default`, the Gateway's default model (`ai: { chat: true }`);
45
+ * name one the manifest lists under `providers` to ask about it instead.
46
+ * The Gateway owns the wording: show `setup` and `command` as they are.
47
+ *
48
+ * Over the binding it sends `GET /api/ai/<provider>`; in local dev it
49
+ * answers from `.dev.vars` by the same rule `createGatewayFetch()` uses.
50
+ */
51
+ export async function aiStatus({ env, provider = AI_DEFAULT_PROVIDER, for: explicitTarget, }) {
52
+ const target = attributionFor(env, explicitTarget, "aiStatus");
53
+ const values = env;
54
+ const binding = gatewayBinding(env);
55
+ const unavailable = {
56
+ provider,
57
+ requested: false,
58
+ configured: false,
59
+ ready: false,
60
+ resolved: null,
61
+ settings: [],
62
+ setup: null,
63
+ command: null,
64
+ };
65
+ if (binding && typeof binding.fetch === "function") {
66
+ const headers = new Headers();
67
+ if (target !== undefined)
68
+ applyAttribution(headers, target);
69
+ const response = await binding.fetch(`${BINDING_ORIGIN}/api/ai/${encodeURIComponent(provider)}`, { headers });
70
+ if (!response.ok)
71
+ return unavailable;
72
+ const body = (await response.json());
73
+ return {
74
+ ...unavailable,
75
+ ...body,
76
+ provider,
77
+ settings: Array.isArray(body.settings) ? body.settings : [],
78
+ setup: typeof body.setup === "string" ? body.setup : null,
79
+ command: typeof body.command === "string" ? body.command : null,
80
+ resolved: body.resolved ?? null,
81
+ };
82
+ }
83
+ if (!values["EVERYAPP_DEV"])
84
+ return unavailable;
85
+ const isSet = (name) => typeof values[name] === "string" && values[name].trim() !== "";
86
+ const dev = (label, settings, configured, resolved, also = "") => ({
87
+ provider,
88
+ requested: true,
89
+ configured,
90
+ ready: configured,
91
+ resolved,
92
+ settings: configured ? [] : [...settings],
93
+ setup: configured
94
+ ? null
95
+ : `${label} isn't set up for local development: set ${settings.join(" and ")}${also} in .dev.vars.`,
96
+ command: null,
97
+ });
98
+ if (provider === AI_DEFAULT_PROVIDER) {
99
+ const resolution = resolveAiDefault({ provider: null, isSet });
100
+ return dev("A chat model", resolution.settings, resolution.configured, resolution.configured
101
+ ? {
102
+ provider: resolution.provider,
103
+ label: resolution.label,
104
+ model: resolution.model,
105
+ }
106
+ : null, " (or another model provider's key)");
107
+ }
108
+ const spec = devAiProvider(provider);
109
+ if (!spec)
110
+ return unavailable;
111
+ const settings = aiProviderSettings(spec);
112
+ return dev(CATALOG.get(provider)?.label ?? provider, settings, settings.every(isSet), null);
113
+ }
114
+ const CHAT_COMPLETIONS = "/chat/completions";
115
+ /**
116
+ * A provider's root behind the Gateway, `http://every-app-gateway.internal/api/ai/<provider>`:
117
+ * join the provider's own paths onto it (`/api/v1/chat/completions` for
118
+ * OpenRouter) and send them with `createGatewayFetch()`. For an OpenAI
119
+ * client's `baseURL`, use {@link aiBaseUrl}.
120
+ */
121
+ export function providerBaseUrl(provider) {
122
+ return `${BINDING_ORIGIN}/api/ai/${encodeURIComponent(provider)}`;
123
+ }
124
+ /**
125
+ * The OpenAI-compatible base URL of `provider` behind the Gateway, for an
126
+ * OpenAI client's `baseURL` (with `fetch: createGatewayFetch(...)`):
127
+ * `default` (the default) is {@link AI_DEFAULT_BASE_URL},
128
+ * `…/api/ai/default/v1`; a catalog provider's is its root and the prefix
129
+ * of its chat route (`…/api/ai/openrouter/api/v1`, `…/api/ai/workers-ai/v1`,
130
+ * `…/api/ai/openai/v1`). Throws `ConfigurationError` for a provider with
131
+ * no OpenAI-compatible chat route (Cohere): call {@link providerBaseUrl}
132
+ * with its own paths instead.
133
+ */
134
+ export function aiBaseUrl(provider = AI_DEFAULT_PROVIDER) {
135
+ if (provider === AI_DEFAULT_PROVIDER)
136
+ return AI_DEFAULT_BASE_URL;
137
+ const capability = CATALOG.get(provider);
138
+ const chat = capability?.mode === "ai" && "chat" in capability
139
+ ? capability.chat
140
+ : undefined;
141
+ if (!chat?.path.endsWith(CHAT_COMPLETIONS)) {
142
+ throw new ConfigurationError(`${provider} has no OpenAI-compatible chat route in the catalog: join its own paths onto providerBaseUrl("${provider}") instead.`);
143
+ }
144
+ return `${providerBaseUrl(provider)}${chat.path.slice(0, -CHAT_COMPLETIONS.length)}`;
145
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * `gatewayAiBinding(env)`: the Gateway's AI providers as a Workers AI
3
+ * binding, for code written against `env.AI.run(model, inputs, options)`.
4
+ *
5
+ * An app on Every App needs no `ai` binding of its own to call models: the
6
+ * Gateway holds the keys, counts the calls and keeps the app to `ai.models`.
7
+ * Code that already calls `env.AI.run()` (upstream code a port keeps, the
8
+ * Agents SDK, `workers-ai-provider`) gets this object in its place:
9
+ *
10
+ * const AI = gatewayAiBinding(env, { for: request });
11
+ * await AI.run("@cf/meta/llama-3.1-8b-instruct-fast", { messages });
12
+ *
13
+ * Each `run()` becomes an OpenAI-compatible call on the provider's route
14
+ * through the Gateway (`createGatewayFetch`): text generation (`messages`
15
+ * or `prompt`) on its chat completions route, text embeddings (`text`) on
16
+ * its embeddings route, answered in Workers AI's shapes (`{ response,
17
+ * tool_calls, usage }`, `{ shape, data }`, or a stream of `data:
18
+ * {"response": …}` events with `stream: true`). In local development
19
+ * without a Gateway, the developer's own key in `.dev.vars` is used, as
20
+ * `createGatewayFetch` does (WORKERS_AI_API_TOKEN and CLOUDFLARE_ACCOUNT_ID
21
+ * for Workers AI).
22
+ */
23
+ import { AI_DEFAULT_PROVIDER, type AiProviderId } from "../internal/index.js";
24
+ import type { GatewayCallFor } from "./attribution.js";
25
+ export interface GatewayAiBindingOptions {
26
+ /**
27
+ * Whom the calls are for: the inbound request, or `{ background: true,
28
+ * organizationId }` (createGatewayFetch's `for`).
29
+ */
30
+ for?: GatewayCallFor;
31
+ /**
32
+ * The Gateway provider the calls go to: an `ai` entry of the catalog
33
+ * with OpenAI-compatible routes (`workers-ai`, `openrouter`, `openai`,
34
+ * …), or `default`, the operator's model (`ai: { chat: true }`).
35
+ * Default: `workers-ai`, the provider `env.AI` reaches, so `@cf/…` model
36
+ * ids work as they are. The manifest lists the provider under
37
+ * `providers` (or says `ai.chat` for `default`).
38
+ */
39
+ provider?: AiProviderId | typeof AI_DEFAULT_PROVIDER;
40
+ /**
41
+ * The model id to send for a model the code names, e.g.
42
+ * `{ "@cf/meta/llama-3.1-8b-instruct-fast": "meta-llama/llama-3.1-8b-instruct" }`
43
+ * with `provider: "openrouter"`. A model not listed is sent as it is,
44
+ * except that on `default` an `@cf/…` id becomes `default` (the
45
+ * operator's model).
46
+ */
47
+ models?: Readonly<Record<string, string>>;
48
+ }
49
+ /**
50
+ * A Workers AI binding (`Ai`) over the Gateway: pass it where code takes
51
+ * `env.AI`. `run()` covers text generation (messages or prompt; tools;
52
+ * `stream: true`; `returnRawResponse`, which returns the provider's
53
+ * OpenAI-shaped `Response`) and text embeddings; the binding's other
54
+ * methods (`gateway()`, `autorag()`, `models()`, `toMarkdown()`, …) and
55
+ * other tasks (images, audio, batches, websockets) throw
56
+ * `ConfigurationError`. `options.gateway` is ignored: the Gateway's
57
+ * operator decides whether calls go through Cloudflare AI Gateway
58
+ * (`everyapp ai gateway on`).
59
+ *
60
+ * Errors are thrown as a binding throws them: `GatewayRefusalError` when
61
+ * the Gateway refused (`ai_provider_not_configured`, `model_not_allowed`,
62
+ * …), `ConfigurationError` where createGatewayFetch throws one (no binding
63
+ * and no dev key), and an `Error` naming the status when the provider
64
+ * answered with an error. Nothing is checked when the binding is made, so
65
+ * making one per request costs nothing.
66
+ */
67
+ export declare function gatewayAiBinding(env: object, options?: GatewayAiBindingOptions): Ai;
68
+ //# sourceMappingURL=aiBinding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aiBinding.d.ts","sourceRoot":"","sources":["../../src/server/aiBinding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAEL,mBAAmB,EAInB,KAAK,YAAY,EAClB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAIvD,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,YAAY,GAAG,OAAO,mBAAmB,CAAC;IACrD;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC3C;AAUD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,uBAA4B,GACpC,EAAE,CAoEJ"}
@@ -0,0 +1,294 @@
1
+ /**
2
+ * `gatewayAiBinding(env)`: the Gateway's AI providers as a Workers AI
3
+ * binding, for code written against `env.AI.run(model, inputs, options)`.
4
+ *
5
+ * An app on Every App needs no `ai` binding of its own to call models: the
6
+ * Gateway holds the keys, counts the calls and keeps the app to `ai.models`.
7
+ * Code that already calls `env.AI.run()` (upstream code a port keeps, the
8
+ * Agents SDK, `workers-ai-provider`) gets this object in its place:
9
+ *
10
+ * const AI = gatewayAiBinding(env, { for: request });
11
+ * await AI.run("@cf/meta/llama-3.1-8b-instruct-fast", { messages });
12
+ *
13
+ * Each `run()` becomes an OpenAI-compatible call on the provider's route
14
+ * through the Gateway (`createGatewayFetch`): text generation (`messages`
15
+ * or `prompt`) on its chat completions route, text embeddings (`text`) on
16
+ * its embeddings route, answered in Workers AI's shapes (`{ response,
17
+ * tool_calls, usage }`, `{ shape, data }`, or a stream of `data:
18
+ * {"response": …}` events with `stream: true`). In local development
19
+ * without a Gateway, the developer's own key in `.dev.vars` is used, as
20
+ * `createGatewayFetch` does (WORKERS_AI_API_TOKEN and CLOUDFLARE_ACCOUNT_ID
21
+ * for Workers AI).
22
+ */
23
+ import { AI_DEFAULT_MODEL, AI_DEFAULT_PROVIDER, AI_DEFAULT_ROUTES, ConfigurationError, devAiProvider, } from "../internal/index.js";
24
+ import { BINDING_ORIGIN, createGatewayFetch } from "./gatewayFetch.js";
25
+ import { GatewayRefusalError, gatewayRefusal } from "./refusal.js";
26
+ /**
27
+ * A Workers AI binding (`Ai`) over the Gateway: pass it where code takes
28
+ * `env.AI`. `run()` covers text generation (messages or prompt; tools;
29
+ * `stream: true`; `returnRawResponse`, which returns the provider's
30
+ * OpenAI-shaped `Response`) and text embeddings; the binding's other
31
+ * methods (`gateway()`, `autorag()`, `models()`, `toMarkdown()`, …) and
32
+ * other tasks (images, audio, batches, websockets) throw
33
+ * `ConfigurationError`. `options.gateway` is ignored: the Gateway's
34
+ * operator decides whether calls go through Cloudflare AI Gateway
35
+ * (`everyapp ai gateway on`).
36
+ *
37
+ * Errors are thrown as a binding throws them: `GatewayRefusalError` when
38
+ * the Gateway refused (`ai_provider_not_configured`, `model_not_allowed`,
39
+ * …), `ConfigurationError` where createGatewayFetch throws one (no binding
40
+ * and no dev key), and an `Error` naming the status when the provider
41
+ * answered with an error. Nothing is checked when the binding is made, so
42
+ * making one per request costs nothing.
43
+ */
44
+ export function gatewayAiBinding(env, options = {}) {
45
+ const provider = options.provider ?? "workers-ai";
46
+ async function run(model, inputs, runOptions = {}) {
47
+ if (runOptions.websocket) {
48
+ throw unsupported("run() with websocket: true");
49
+ }
50
+ if (!inputs || typeof inputs !== "object") {
51
+ throw unsupported("run() without an inputs object");
52
+ }
53
+ if (Array.isArray(inputs["requests"])) {
54
+ throw unsupported("run() with a batch of requests");
55
+ }
56
+ const embedding = !("messages" in inputs) && !("prompt" in inputs) && "text" in inputs;
57
+ if (!embedding && !("messages" in inputs) && !("prompt" in inputs)) {
58
+ throw unsupported("run() for anything but text generation (messages or prompt) and text embeddings (text)");
59
+ }
60
+ const url = `${BINDING_ORIGIN}/api/ai/${provider}${routeOf(provider, embedding ? "embeddings" : "chat")}`;
61
+ const sent = modelToSend(provider, model, options.models);
62
+ const body = embedding
63
+ ? { model: sent, input: inputs["text"] }
64
+ : chatBody(sent, inputs);
65
+ const gatewayFetch = createGatewayFetch({
66
+ env,
67
+ for: options.for,
68
+ });
69
+ const response = await gatewayFetch(url, {
70
+ method: "POST",
71
+ headers: { "content-type": "application/json" },
72
+ body: JSON.stringify(body),
73
+ ...(runOptions.signal ? { signal: runOptions.signal } : {}),
74
+ });
75
+ if (runOptions.returnRawResponse)
76
+ return response;
77
+ await throwIfFailed(response, provider);
78
+ if (embedding)
79
+ return embeddingOutput(await response.json());
80
+ if (inputs["stream"] === true) {
81
+ if (!response.body)
82
+ return new ReadableStream();
83
+ return response.body.pipeThrough(toWorkersAiStream());
84
+ }
85
+ return textOutput(await response.json());
86
+ }
87
+ const notHere = (method) => () => {
88
+ throw unsupported(`env.AI.${method}()`);
89
+ };
90
+ const binding = {
91
+ aiGatewayLogId: null,
92
+ run,
93
+ gateway: notHere("gateway"),
94
+ autorag: notHere("autorag"),
95
+ aiSearch: notHere("aiSearch"),
96
+ websearch: notHere("websearch"),
97
+ models: notHere("models"),
98
+ toMarkdown: notHere("toMarkdown"),
99
+ };
100
+ // `Ai` is the app's own Workers types' class (it changes with each
101
+ // version); the object above is its working subset, the rest throwing.
102
+ return binding;
103
+ }
104
+ function unsupported(what) {
105
+ return new ConfigurationError(`${what} isn't available through the Every App gateway; call the provider's routes with createGatewayFetch() instead.`);
106
+ }
107
+ /** The provider's path for a chat or an embeddings call, under `/api/ai/<provider>`. */
108
+ function routeOf(provider, kind) {
109
+ if (provider === AI_DEFAULT_PROVIDER)
110
+ return AI_DEFAULT_ROUTES[kind];
111
+ const spec = devAiProvider(provider);
112
+ if (!spec) {
113
+ throw new ConfigurationError(`gatewayAiBinding: ${JSON.stringify(provider)} is not an AI provider of the catalog.`);
114
+ }
115
+ const route = spec[kind];
116
+ if (!route) {
117
+ throw new ConfigurationError(`gatewayAiBinding: ${provider} has no OpenAI-compatible ${kind === "chat" ? "chat completions" : "embeddings"} route.`);
118
+ }
119
+ return route.path;
120
+ }
121
+ function modelToSend(provider, model, models) {
122
+ const mapped = models && Object.hasOwn(models, model) ? models[model] : null;
123
+ if (mapped)
124
+ return mapped;
125
+ if (provider === AI_DEFAULT_PROVIDER && model.startsWith("@cf/")) {
126
+ return AI_DEFAULT_MODEL;
127
+ }
128
+ return model;
129
+ }
130
+ /**
131
+ * Workers AI's text generation inputs as a chat completions body: `prompt`
132
+ * as one user message, legacy tools (`{ name, description, parameters }`)
133
+ * as OpenAI's, everything else (max_tokens, temperature, response_format,
134
+ * stream, …) as it is. Never adds `stream_options` (Workers AI refuses it
135
+ * on a call that doesn't stream).
136
+ */
137
+ function chatBody(model, inputs) {
138
+ const { prompt, messages, tools, raw: _raw, lora: _lora, ...rest } = inputs;
139
+ const body = {
140
+ ...rest,
141
+ model,
142
+ messages: messages ??
143
+ (typeof prompt === "string" ? [{ role: "user", content: prompt }] : []),
144
+ };
145
+ if (Array.isArray(tools))
146
+ body["tools"] = tools.map(openAiTool);
147
+ return body;
148
+ }
149
+ function openAiTool(tool) {
150
+ if (!isRecord(tool) || "function" in tool)
151
+ return tool;
152
+ const { name, description, parameters } = tool;
153
+ return {
154
+ type: "function",
155
+ function: {
156
+ name,
157
+ ...(description === undefined ? {} : { description }),
158
+ ...(parameters === undefined ? {} : { parameters }),
159
+ },
160
+ };
161
+ }
162
+ async function throwIfFailed(response, provider) {
163
+ if (response.ok)
164
+ return;
165
+ const refusal = await gatewayRefusal(response.clone());
166
+ if (refusal)
167
+ throw new GatewayRefusalError(refusal);
168
+ const text = await response.text().catch(() => "");
169
+ throw new Error(`AI (${provider}) answered ${response.status}${text ? `: ${text.slice(0, 500)}` : ""}`);
170
+ }
171
+ /** A chat completion as Workers AI answers text generation. */
172
+ function textOutput(body) {
173
+ const choice = firstChoice(body);
174
+ const message = isRecord(choice?.["message"]) ? choice["message"] : {};
175
+ const toolCalls = workersAiToolCalls(message["tool_calls"]);
176
+ const usage = isRecord(body) ? body["usage"] : undefined;
177
+ return {
178
+ // Workers AI may send an all-digit reply as a JSON number.
179
+ response: textOf(message["content"]),
180
+ ...(toolCalls ? { tool_calls: toolCalls } : {}),
181
+ ...(isRecord(usage) ? { usage } : {}),
182
+ };
183
+ }
184
+ /**
185
+ * OpenAI tool calls as both forms Workers AI answers with: the legacy
186
+ * `{ name, arguments }` (arguments parsed) and the OpenAI-shaped
187
+ * `{ id, type, function }`.
188
+ */
189
+ function workersAiToolCalls(calls) {
190
+ if (!Array.isArray(calls) || calls.length === 0)
191
+ return undefined;
192
+ return calls.filter(isRecord).map((call) => {
193
+ const fn = isRecord(call["function"]) ? call["function"] : {};
194
+ const args = fn["arguments"];
195
+ return {
196
+ ...(call["id"] === undefined ? {} : { id: call["id"] }),
197
+ type: "function",
198
+ function: { name: fn["name"], arguments: args },
199
+ name: fn["name"],
200
+ arguments: typeof args === "string" ? parsedOr(args) : args,
201
+ };
202
+ });
203
+ }
204
+ /** Embeddings as Workers AI answers them: `{ shape: [rows, dimensions], data }`. */
205
+ function embeddingOutput(body) {
206
+ const rows = isRecord(body) && Array.isArray(body["data"]) ? body["data"] : [];
207
+ const data = rows
208
+ .filter(isRecord)
209
+ .sort((a, b) => Number(a["index"] ?? 0) - Number(b["index"] ?? 0))
210
+ .map((row) => Array.isArray(row["embedding"]) ? row["embedding"] : []);
211
+ return { shape: [data.length, data[0]?.length ?? 0], data };
212
+ }
213
+ /**
214
+ * An OpenAI chat completions event stream as Workers AI streams text:
215
+ * one `data: {"response": "<token>"}` event per chunk with text (tool call
216
+ * deltas and usage alongside), then `data: [DONE]`.
217
+ */
218
+ function toWorkersAiStream() {
219
+ const decoder = new TextDecoder();
220
+ const encoder = new TextEncoder();
221
+ let pending = "";
222
+ const convert = (line) => {
223
+ const match = /^data:[ ]?(.*?)\r?$/s.exec(line);
224
+ if (!match)
225
+ return "";
226
+ const data = match[1].trim();
227
+ if (data === "[DONE]")
228
+ return "data: [DONE]\n\n";
229
+ let chunk;
230
+ try {
231
+ chunk = JSON.parse(data);
232
+ }
233
+ catch {
234
+ return "";
235
+ }
236
+ const choice = firstChoice(chunk);
237
+ const delta = isRecord(choice?.["delta"]) ? choice["delta"] : {};
238
+ const usage = isRecord(chunk) ? chunk["usage"] : undefined;
239
+ const toolCalls = Array.isArray(delta["tool_calls"])
240
+ ? delta["tool_calls"]
241
+ : undefined;
242
+ const event = {
243
+ response: textOf(delta["content"]),
244
+ ...(toolCalls ? { tool_calls: toolCalls } : {}),
245
+ ...(isRecord(usage) ? { usage } : {}),
246
+ };
247
+ if (!event.response && !toolCalls && !isRecord(usage))
248
+ return "";
249
+ return `data: ${JSON.stringify(event)}\n\n`;
250
+ };
251
+ return new TransformStream({
252
+ transform(bytes, controller) {
253
+ pending += decoder.decode(bytes, { stream: true });
254
+ const end = pending.lastIndexOf("\n");
255
+ if (end === -1)
256
+ return;
257
+ const lines = pending.slice(0, end).split("\n");
258
+ pending = pending.slice(end + 1);
259
+ const out = lines.map(convert).join("");
260
+ if (out)
261
+ controller.enqueue(encoder.encode(out));
262
+ },
263
+ flush(controller) {
264
+ pending += decoder.decode();
265
+ const out = convert(pending);
266
+ if (out)
267
+ controller.enqueue(encoder.encode(out));
268
+ },
269
+ });
270
+ }
271
+ function firstChoice(body) {
272
+ if (!isRecord(body) || !Array.isArray(body["choices"]))
273
+ return undefined;
274
+ const choice = body["choices"][0];
275
+ return isRecord(choice) ? choice : undefined;
276
+ }
277
+ function textOf(content) {
278
+ if (typeof content === "string")
279
+ return content;
280
+ if (typeof content === "number")
281
+ return String(content);
282
+ return "";
283
+ }
284
+ function parsedOr(text) {
285
+ try {
286
+ return JSON.parse(text);
287
+ }
288
+ catch {
289
+ return text;
290
+ }
291
+ }
292
+ function isRecord(value) {
293
+ return typeof value === "object" && value !== null && !Array.isArray(value);
294
+ }
@@ -1,6 +1,8 @@
1
1
  /**
2
2
  * Whom a call through the gateway is for, which decides the organization
3
- * it counts against:
3
+ * it counts against. Left out, it comes from the request context: the
4
+ * request `everyApp()` is serving (its person, while the identity is good),
5
+ * or the organization `withOrganization()` named. Given explicitly:
4
6
  *
5
7
  * - the inbound `Request`: its person, by the identity the gateway put on
6
8
  * it (forwarded as is);
@@ -24,11 +26,13 @@ export declare class AttributionError extends Error {
24
26
  constructor(message: string);
25
27
  }
26
28
  /**
27
- * Check `target` before any call is made: a shared app's Worker must pass
28
- * the request or name the organization, and anything else passed must be
29
- * one of the two shapes.
29
+ * Whom the call is for, checked before any call is made: `target` as the
30
+ * app passed it, else the request context's answer
31
+ * ({@link attributionFromContext}; a `RequestContextError` outside every
32
+ * context). A shared app's Worker must end up with the request or an
33
+ * organization, and anything passed must be one of the two shapes.
30
34
  */
31
- export declare function checkAttribution(env: object, target: GatewayCallFor | undefined, what: string): void;
35
+ export declare function attributionFor(env: object, target: GatewayCallFor | undefined, what: string): GatewayCallFor | undefined;
32
36
  /**
33
37
  * Put the call's attribution on its headers: the request's identity, or the
34
38
  * background work's organization, and never one the app set itself.
@@ -1 +1 @@
1
- {"version":3,"file":"attribution.d.ts","sourceRoot":"","sources":["../../src/server/attribution.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,cAAc,GACtB,OAAO,GACP;IAAE,UAAU,EAAE,IAAI,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAElD;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,IAAI,0BAA0B;gBAC3B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,cAAc,GAAG,SAAS,EAClC,IAAI,EAAE,MAAM,GACX,IAAI,CAsBN;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,cAAc,GAAG,SAAS,GACjC,IAAI,CAUN;AASD,0EAA0E;AAC1E,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,OAAO,CAO1D"}
1
+ {"version":3,"file":"attribution.d.ts","sourceRoot":"","sources":["../../src/server/attribution.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,cAAc,GACtB,OAAO,GACP;IAAE,UAAU,EAAE,IAAI,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAElD;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,IAAI,0BAA0B;gBAC3B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,cAAc,GAAG,SAAS,EAClC,IAAI,EAAE,MAAM,GACX,cAAc,GAAG,SAAS,CAI5B;AA8BD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,cAAc,GAAG,SAAS,GACjC,IAAI,CAUN;AASD,0EAA0E;AAC1E,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,OAAO,CAO1D"}