@every-app/sdk 0.1.14 → 0.3.0-rc.1
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 +608 -2
- package/dist/cloudflare/index.d.ts +21 -1
- package/dist/cloudflare/index.d.ts.map +1 -1
- package/dist/cloudflare/index.js +21 -1
- package/dist/cloudflare/sentinel.d.ts +69 -0
- package/dist/cloudflare/sentinel.d.ts.map +1 -0
- package/dist/cloudflare/sentinel.js +50 -0
- package/dist/cloudflare/worker.d.ts +511 -0
- package/dist/cloudflare/worker.d.ts.map +1 -0
- package/dist/cloudflare/worker.js +738 -0
- package/dist/dev/index.d.ts +8 -0
- package/dist/dev/index.d.ts.map +1 -0
- package/dist/dev/index.js +7 -0
- package/dist/dev/keypair.d.ts +24 -0
- package/dist/dev/keypair.d.ts.map +1 -0
- package/dist/dev/keypair.js +60 -0
- package/dist/internal/aiProviders.d.ts +67 -0
- package/dist/internal/aiProviders.d.ts.map +1 -0
- package/dist/internal/aiProviders.js +220 -0
- package/dist/internal/appSettings.d.ts +237 -0
- package/dist/internal/appSettings.d.ts.map +1 -0
- package/dist/internal/appSettings.js +374 -0
- package/dist/internal/capabilities.d.ts +1344 -0
- package/dist/internal/capabilities.d.ts.map +1 -0
- package/dist/internal/capabilities.js +1521 -0
- package/dist/internal/devProviders.d.ts +49 -0
- package/dist/internal/devProviders.d.ts.map +1 -0
- package/dist/internal/devProviders.js +62 -0
- package/dist/internal/email.d.ts +280 -0
- package/dist/internal/email.d.ts.map +1 -0
- package/dist/internal/email.js +613 -0
- package/dist/internal/events.d.ts +182 -0
- package/dist/internal/events.d.ts.map +1 -0
- package/dist/internal/events.js +324 -0
- package/dist/internal/gatewaySettings.d.ts +82 -0
- package/dist/internal/gatewaySettings.d.ts.map +1 -0
- package/dist/internal/gatewaySettings.js +209 -0
- package/dist/internal/identity.d.ts +88 -0
- package/dist/internal/identity.d.ts.map +1 -0
- package/dist/internal/identity.js +231 -0
- package/dist/internal/index.d.ts +19 -0
- package/dist/internal/index.d.ts.map +1 -0
- package/dist/internal/index.js +18 -0
- package/dist/internal/members.d.ts +35 -0
- package/dist/internal/members.d.ts.map +1 -0
- package/dist/internal/members.js +7 -0
- package/dist/internal/oauthClient.d.ts +63 -0
- package/dist/internal/oauthClient.d.ts.map +1 -0
- package/dist/internal/oauthClient.js +194 -0
- package/dist/internal/oauthScopes.d.ts +117 -0
- package/dist/internal/oauthScopes.d.ts.map +1 -0
- package/dist/internal/oauthScopes.js +416 -0
- package/dist/internal/organizationName.d.ts +18 -0
- package/dist/internal/organizationName.d.ts.map +1 -0
- package/dist/internal/organizationName.js +24 -0
- package/dist/internal/protocol.d.ts +195 -0
- package/dist/internal/protocol.d.ts.map +1 -0
- package/dist/internal/protocol.js +250 -0
- package/dist/internal/providerApps.d.ts +75 -0
- package/dist/internal/providerApps.d.ts.map +1 -0
- package/dist/internal/providerApps.js +59 -0
- package/dist/internal/providerRegistry.d.ts +147 -0
- package/dist/internal/providerRegistry.d.ts.map +1 -0
- package/dist/internal/providerRegistry.js +262 -0
- package/dist/internal/providerSpec.d.ts +55 -0
- package/dist/internal/providerSpec.d.ts.map +1 -0
- package/dist/internal/providerSpec.js +746 -0
- package/dist/internal/remoteMcp.d.ts +188 -0
- package/dist/internal/remoteMcp.d.ts.map +1 -0
- package/dist/internal/remoteMcp.js +543 -0
- package/dist/internal/tools.d.ts +115 -0
- package/dist/internal/tools.d.ts.map +1 -0
- package/dist/internal/tools.js +80 -0
- package/dist/server/attribution.d.ts +39 -0
- package/dist/server/attribution.d.ts.map +1 -0
- package/dist/server/attribution.js +67 -0
- package/dist/server/authorizeUrl.d.ts +43 -0
- package/dist/server/authorizeUrl.d.ts.map +1 -0
- package/dist/server/authorizeUrl.js +110 -0
- package/dist/server/emit.d.ts +63 -0
- package/dist/server/emit.d.ts.map +1 -0
- package/dist/server/emit.js +101 -0
- package/dist/server/everyApp.d.ts +81 -0
- package/dist/server/everyApp.d.ts.map +1 -0
- package/dist/server/everyApp.js +185 -0
- package/dist/server/gatewayFetch.d.ts +37 -0
- package/dist/server/gatewayFetch.d.ts.map +1 -0
- package/dist/server/gatewayFetch.js +115 -0
- package/dist/server/index.d.ts +18 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +15 -0
- package/dist/server/integrationFetch.d.ts +131 -0
- package/dist/server/integrationFetch.d.ts.map +1 -0
- package/dist/server/integrationFetch.js +526 -0
- package/dist/server/listMembers.d.ts +32 -0
- package/dist/server/listMembers.d.ts.map +1 -0
- package/dist/server/listMembers.js +74 -0
- package/dist/server/nodeServer.d.ts +48 -0
- package/dist/server/nodeServer.d.ts.map +1 -0
- package/dist/server/nodeServer.js +45 -0
- package/dist/server/sendEmail.d.ts +39 -0
- package/dist/server/sendEmail.d.ts.map +1 -0
- package/dist/server/sendEmail.js +114 -0
- package/dist/server/tools.d.ts +184 -0
- package/dist/server/tools.d.ts.map +1 -0
- package/dist/server/tools.js +347 -0
- package/dist/server/toolsEndpoint.d.ts +32 -0
- package/dist/server/toolsEndpoint.d.ts.map +1 -0
- package/dist/server/toolsEndpoint.js +231 -0
- package/package.json +21 -31
- package/dist/cloudflare/getLocalD1Url.d.ts +0 -2
- package/dist/cloudflare/getLocalD1Url.d.ts.map +0 -1
- package/dist/cloudflare/getLocalD1Url.js +0 -40
- package/dist/cloudflare/lazyInit.d.ts +0 -22
- package/dist/cloudflare/lazyInit.d.ts.map +0 -1
- package/dist/cloudflare/lazyInit.js +0 -65
- package/dist/cloudflare/server/gateway.d.ts +0 -29
- package/dist/cloudflare/server/gateway.d.ts.map +0 -1
- package/dist/cloudflare/server/gateway.js +0 -69
- package/dist/cloudflare/server/index.d.ts +0 -3
- package/dist/cloudflare/server/index.d.ts.map +0 -1
- package/dist/cloudflare/server/index.js +0 -2
- package/dist/core/authenticatedFetch.d.ts +0 -9
- package/dist/core/authenticatedFetch.d.ts.map +0 -1
- package/dist/core/authenticatedFetch.js +0 -31
- package/dist/core/index.d.ts +0 -4
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js +0 -2
- package/dist/core/sessionManager.d.ts +0 -58
- package/dist/core/sessionManager.d.ts.map +0 -1
- package/dist/core/sessionManager.js +0 -357
- package/dist/shared/bypassGatewayLocalOnly.d.ts +0 -15
- package/dist/shared/bypassGatewayLocalOnly.d.ts.map +0 -1
- package/dist/shared/bypassGatewayLocalOnly.js +0 -45
- package/dist/shared/parseMessagePayload.d.ts +0 -3
- package/dist/shared/parseMessagePayload.d.ts.map +0 -1
- package/dist/shared/parseMessagePayload.js +0 -18
- package/dist/tanstack/EmbeddedAppProvider.d.ts +0 -30
- package/dist/tanstack/EmbeddedAppProvider.d.ts.map +0 -1
- package/dist/tanstack/EmbeddedAppProvider.js +0 -57
- package/dist/tanstack/GatewayRequiredError.d.ts +0 -20
- package/dist/tanstack/GatewayRequiredError.d.ts.map +0 -1
- package/dist/tanstack/GatewayRequiredError.js +0 -91
- package/dist/tanstack/_internal/useEveryAppSession.d.ts +0 -18
- package/dist/tanstack/_internal/useEveryAppSession.d.ts.map +0 -1
- package/dist/tanstack/_internal/useEveryAppSession.js +0 -47
- package/dist/tanstack/index.d.ts +0 -4
- package/dist/tanstack/index.d.ts.map +0 -1
- package/dist/tanstack/index.js +0 -3
- package/dist/tanstack/server/authConfig.d.ts +0 -3
- package/dist/tanstack/server/authConfig.d.ts.map +0 -1
- package/dist/tanstack/server/authConfig.js +0 -13
- package/dist/tanstack/server/authenticateRequest.d.ts +0 -31
- package/dist/tanstack/server/authenticateRequest.d.ts.map +0 -1
- package/dist/tanstack/server/authenticateRequest.js +0 -154
- package/dist/tanstack/server/index.d.ts +0 -4
- package/dist/tanstack/server/index.d.ts.map +0 -1
- package/dist/tanstack/server/index.js +0 -2
- package/dist/tanstack/server/types.d.ts +0 -5
- package/dist/tanstack/server/types.d.ts.map +0 -1
- package/dist/tanstack/server/types.js +0 -1
- package/dist/tanstack/useEveryAppRouter.d.ts +0 -7
- package/dist/tanstack/useEveryAppRouter.d.ts.map +0 -1
- package/dist/tanstack/useEveryAppRouter.js +0 -66
- package/dist/tanstack/useSessionTokenClientMiddleware.d.ts +0 -2
- package/dist/tanstack/useSessionTokenClientMiddleware.d.ts.map +0 -1
- package/dist/tanstack/useSessionTokenClientMiddleware.js +0 -29
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nodeServerHandler()` — serve a Node HTTP framework (Express, NestJS, or
|
|
3
|
+
* anything on `node:http`) from an Every App Worker, through workerd's
|
|
4
|
+
* `cloudflare:node` bridge:
|
|
5
|
+
*
|
|
6
|
+
* export default everyApp(
|
|
7
|
+
* nodeServerHandler({
|
|
8
|
+
* boot: async () => {
|
|
9
|
+
* const app = await NestFactory.create(AppModule);
|
|
10
|
+
* await app.listen(0);
|
|
11
|
+
* return { port: app.getHttpServer().address().port };
|
|
12
|
+
* },
|
|
13
|
+
* }),
|
|
14
|
+
* );
|
|
15
|
+
*
|
|
16
|
+
* `everyApp()` verifies the identity before the Node app sees the request,
|
|
17
|
+
* which still carries it as `x-everyapp-identity`. The bridge needs
|
|
18
|
+
* `nodejs_compat` and a compatibility date from 2025-09-01, which every
|
|
19
|
+
* `everyAppWorker()` has. See "Node frameworks" in the README for the rules
|
|
20
|
+
* such a Worker must follow (minify it, no work at module scope).
|
|
21
|
+
*/
|
|
22
|
+
import type { EveryAppHandler } from "./everyApp.js";
|
|
23
|
+
/** A booted Node server: the port its `http.Server` listens on. */
|
|
24
|
+
export interface NodeServer {
|
|
25
|
+
port: number;
|
|
26
|
+
}
|
|
27
|
+
export interface NodeServerHandlerOptions<TEnv> {
|
|
28
|
+
/**
|
|
29
|
+
* Start the server and return the port it listens on, e.g. `await
|
|
30
|
+
* app.listen(0)` and then `app.getHttpServer().address().port`. Port 0
|
|
31
|
+
* lets workerd pick one; a fixed port is still held by the server of the
|
|
32
|
+
* module graph a Vite reload replaced, in the same isolate. Runs once per
|
|
33
|
+
* isolate, on its first request rather than at module scope; requests
|
|
34
|
+
* that arrive meanwhile wait for it, and a failed boot is retried on the
|
|
35
|
+
* next request.
|
|
36
|
+
*/
|
|
37
|
+
boot(env: TEnv): Promise<NodeServer>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A handler for `everyApp()` that hands each request to the Node server
|
|
41
|
+
* `boot` started. On the way back it drops `Set-Cookie` (the gateway owns
|
|
42
|
+
* the session, and strips cookies from what reaches the app), and passes a
|
|
43
|
+
* body Node already encoded (`content-encoding`, e.g. from `compression()`)
|
|
44
|
+
* through as is: the bridge would let workerd encode it again, and clients
|
|
45
|
+
* would get unreadable bytes.
|
|
46
|
+
*/
|
|
47
|
+
export declare function nodeServerHandler<TEnv = Record<string, unknown>>(options: NodeServerHandlerOptions<TEnv>): EveryAppHandler<TEnv>;
|
|
48
|
+
//# sourceMappingURL=nodeServer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nodeServer.d.ts","sourceRoot":"","sources":["../../src/server/nodeServer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,mEAAmE;AACnE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,wBAAwB,CAAC,IAAI;IAC5C;;;;;;;;OAQG;IACH,IAAI,CAAC,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACtC;AAKD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9D,OAAO,EAAE,wBAAwB,CAAC,IAAI,CAAC,GACtC,eAAe,CAAC,IAAI,CAAC,CAiBvB"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** Statuses whose responses can't carry a body. */
|
|
2
|
+
const NULL_BODY_STATUSES = new Set([101, 204, 205, 304]);
|
|
3
|
+
/**
|
|
4
|
+
* A handler for `everyApp()` that hands each request to the Node server
|
|
5
|
+
* `boot` started. On the way back it drops `Set-Cookie` (the gateway owns
|
|
6
|
+
* the session, and strips cookies from what reaches the app), and passes a
|
|
7
|
+
* body Node already encoded (`content-encoding`, e.g. from `compression()`)
|
|
8
|
+
* through as is: the bridge would let workerd encode it again, and clients
|
|
9
|
+
* would get unreadable bytes.
|
|
10
|
+
*/
|
|
11
|
+
export function nodeServerHandler(options) {
|
|
12
|
+
let booting;
|
|
13
|
+
const server = (env) => {
|
|
14
|
+
if (!booting) {
|
|
15
|
+
const attempt = boot(options, env);
|
|
16
|
+
booting = attempt;
|
|
17
|
+
attempt.catch(() => {
|
|
18
|
+
if (booting === attempt)
|
|
19
|
+
booting = undefined;
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return booting;
|
|
23
|
+
};
|
|
24
|
+
return async (request, env) => {
|
|
25
|
+
const { port, handleAsNodeRequest } = await server(env);
|
|
26
|
+
return fromNode(await handleAsNodeRequest(port, request));
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
async function boot(options, env) {
|
|
30
|
+
// Imported here rather than at the top, so that loading the SDK needs no
|
|
31
|
+
// bridge (Node test runners, Workers on an older compatibility date).
|
|
32
|
+
const { handleAsNodeRequest } = await import("cloudflare:node");
|
|
33
|
+
const { port } = await options.boot(env);
|
|
34
|
+
return { port, handleAsNodeRequest };
|
|
35
|
+
}
|
|
36
|
+
function fromNode(response) {
|
|
37
|
+
const headers = new Headers(response.headers);
|
|
38
|
+
headers.delete("set-cookie");
|
|
39
|
+
return new Response(NULL_BODY_STATUSES.has(response.status) ? null : response.body, {
|
|
40
|
+
status: response.status,
|
|
41
|
+
statusText: response.statusText,
|
|
42
|
+
headers,
|
|
43
|
+
...(headers.has("content-encoding") ? { encodeBody: "manual" } : {}),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type SendEmailMessage, type SendEmailResult } from "../internal/index.js";
|
|
2
|
+
import { type GatewayCallFor } from "./attribution.js";
|
|
3
|
+
export interface SendEmailOptions {
|
|
4
|
+
/**
|
|
5
|
+
* The Worker env: the EVERY_APP_GATEWAY binding when deployed; in local
|
|
6
|
+
* dev EVERYAPP_DEV, EVERYAPP_EMAIL_SENDERS in `.dev.vars` for the senders
|
|
7
|
+
* a `from` may name, and optionally EMAIL_REST_API_TOKEN,
|
|
8
|
+
* CLOUDFLARE_ACCOUNT_ID and EMAIL_FROM there to really send.
|
|
9
|
+
*/
|
|
10
|
+
env: object;
|
|
11
|
+
/**
|
|
12
|
+
* Whom the mail is for: the inbound request (its user), or
|
|
13
|
+
* `{ background: true, organizationId }` for work no person started (an
|
|
14
|
+
* `email()` handler, a cron, a Workflow). An organization's own app may
|
|
15
|
+
* omit it for background work; a shared app's Worker must pass one or
|
|
16
|
+
* the other ({@link AttributionError}).
|
|
17
|
+
*/
|
|
18
|
+
for?: GatewayCallFor;
|
|
19
|
+
message: SendEmailMessage;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Send an email through the gateway's `email` capability: its sender
|
|
23
|
+
* (EMAIL_FROM) unless `message.from` names an address the installation's
|
|
24
|
+
* senders allow (`everyapp email set --sender`; in dev, `.dev.vars`'
|
|
25
|
+
* EVERYAPP_EMAIL_SENDERS), within the gateway's daily budgets. The app
|
|
26
|
+
* lists `capabilities: { email: { access: "write" } }`.
|
|
27
|
+
*
|
|
28
|
+
* Throws {@link GatewayRefusalError} when the gateway won't send
|
|
29
|
+
* (`email_sender_not_allowed`, `email_budget_exhausted`,
|
|
30
|
+
* `integration_not_configured`, ...), with the code and a message to show.
|
|
31
|
+
*
|
|
32
|
+
* In local dev (`everyapp dev`, no binding) the message is checked the way
|
|
33
|
+
* the gateway checks it, then printed and answered `{ status: "logged" }`;
|
|
34
|
+
* with EMAIL_REST_API_TOKEN, CLOUDFLARE_ACCOUNT_ID and EMAIL_FROM in
|
|
35
|
+
* `.dev.vars` it goes out for real through Email Sending's REST API, from
|
|
36
|
+
* the developer's own account.
|
|
37
|
+
*/
|
|
38
|
+
export declare function sendEmail({ env, for: target, message, }: SendEmailOptions): Promise<SendEmailResult>;
|
|
39
|
+
//# sourceMappingURL=sendEmail.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sendEmail.d.ts","sourceRoot":"","sources":["../../src/server/sendEmail.ts"],"names":[],"mappings":"AAAA,OAAO,EAeL,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAI1B,MAAM,WAAW,gBAAgB;IAC/B;;;;;OAKG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,gBAAgB,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,SAAS,CAAC,EAC9B,GAAG,EACH,GAAG,EAAE,MAAM,EACX,OAAO,GACR,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CAoB7C"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { ConfigurationError, EMAIL_SENDERS_ENV, emailDomain, emailProviderRefusal, emailRestBody, emailRestUrl, emailSenderRefusal, parseEmailList, parseEmailSender, parseSendEmailMessage, readEmailRestResponse, senderAllowed, serviceRoutePath, } from "../internal/index.js";
|
|
2
|
+
import { applyAttribution, checkAttribution, } from "./attribution.js";
|
|
3
|
+
import { BINDING_ORIGIN } from "./gatewayFetch.js";
|
|
4
|
+
import { GatewayRefusalError, gatewayRefusal } from "./integrationFetch.js";
|
|
5
|
+
/**
|
|
6
|
+
* Send an email through the gateway's `email` capability: its sender
|
|
7
|
+
* (EMAIL_FROM) unless `message.from` names an address the installation's
|
|
8
|
+
* senders allow (`everyapp email set --sender`; in dev, `.dev.vars`'
|
|
9
|
+
* EVERYAPP_EMAIL_SENDERS), within the gateway's daily budgets. The app
|
|
10
|
+
* lists `capabilities: { email: { access: "write" } }`.
|
|
11
|
+
*
|
|
12
|
+
* Throws {@link GatewayRefusalError} when the gateway won't send
|
|
13
|
+
* (`email_sender_not_allowed`, `email_budget_exhausted`,
|
|
14
|
+
* `integration_not_configured`, ...), with the code and a message to show.
|
|
15
|
+
*
|
|
16
|
+
* In local dev (`everyapp dev`, no binding) the message is checked the way
|
|
17
|
+
* the gateway checks it, then printed and answered `{ status: "logged" }`;
|
|
18
|
+
* with EMAIL_REST_API_TOKEN, CLOUDFLARE_ACCOUNT_ID and EMAIL_FROM in
|
|
19
|
+
* `.dev.vars` it goes out for real through Email Sending's REST API, from
|
|
20
|
+
* the developer's own account.
|
|
21
|
+
*/
|
|
22
|
+
export async function sendEmail({ env, for: target, message, }) {
|
|
23
|
+
checkAttribution(env, target, "sendEmail");
|
|
24
|
+
const values = env;
|
|
25
|
+
const binding = values["EVERY_APP_GATEWAY"];
|
|
26
|
+
if (binding) {
|
|
27
|
+
if (typeof binding.fetch !== "function") {
|
|
28
|
+
throw new ConfigurationError("EVERY_APP_GATEWAY is configured but is not a valid service binding.");
|
|
29
|
+
}
|
|
30
|
+
return sendThroughGateway(binding, target, message);
|
|
31
|
+
}
|
|
32
|
+
if (!values["EVERYAPP_DEV"]) {
|
|
33
|
+
throw new ConfigurationError("Email is unavailable: deploy with the EVERY_APP_GATEWAY binding (everyapp deploy), or run under everyapp dev.");
|
|
34
|
+
}
|
|
35
|
+
return sendInDev(values, message);
|
|
36
|
+
}
|
|
37
|
+
async function sendThroughGateway(binding, target, message) {
|
|
38
|
+
const headers = new Headers({ "content-type": "application/json" });
|
|
39
|
+
applyAttribution(headers, target);
|
|
40
|
+
const response = await binding.fetch(new Request(`${BINDING_ORIGIN}${serviceRoutePath("email", "send")}`, {
|
|
41
|
+
method: "POST",
|
|
42
|
+
headers,
|
|
43
|
+
body: JSON.stringify(message),
|
|
44
|
+
}));
|
|
45
|
+
const refusal = await gatewayRefusal(response);
|
|
46
|
+
if (refusal)
|
|
47
|
+
throw new GatewayRefusalError(refusal);
|
|
48
|
+
if (!response.ok) {
|
|
49
|
+
throw new GatewayRefusalError({
|
|
50
|
+
status: response.status,
|
|
51
|
+
code: "email_send_failed",
|
|
52
|
+
message: `The Every App gateway answered ${response.status} to the send.`,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return (await response.json());
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* What the gateway would do, with no gateway: the same checks and sender
|
|
59
|
+
* rule, then the developer's own Email Sending account or the console.
|
|
60
|
+
*/
|
|
61
|
+
async function sendInDev(values, input) {
|
|
62
|
+
const parsed = parseSendEmailMessage(input);
|
|
63
|
+
if (!parsed.ok) {
|
|
64
|
+
throw new GatewayRefusalError({
|
|
65
|
+
status: 400,
|
|
66
|
+
code: "email_invalid_message",
|
|
67
|
+
message: parsed.problem,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
const message = parsed.message;
|
|
71
|
+
if (message.from &&
|
|
72
|
+
!senderAllowed(message.from.address, parseEmailList(values[EMAIL_SENDERS_ENV]))) {
|
|
73
|
+
throw new GatewayRefusalError({
|
|
74
|
+
status: 403,
|
|
75
|
+
code: "email_sender_not_allowed",
|
|
76
|
+
message: emailSenderRefusal(message.from.address, "dev"),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
const token = stringValue(values["EMAIL_REST_API_TOKEN"]);
|
|
80
|
+
const accountId = stringValue(values["CLOUDFLARE_ACCOUNT_ID"]);
|
|
81
|
+
const configuredFrom = stringValue(values["EMAIL_FROM"]);
|
|
82
|
+
// The gateway needs a name for its sender; a developer's address alone will do.
|
|
83
|
+
const sender = configuredFrom
|
|
84
|
+
? (parseEmailSender(configuredFrom, stringValue(values["EMAIL_FROM_NAME"])) ?? { address: configuredFrom })
|
|
85
|
+
: null;
|
|
86
|
+
if (token && accountId && sender) {
|
|
87
|
+
return sendWithRest(message, message.from ?? sender, token, accountId);
|
|
88
|
+
}
|
|
89
|
+
const recipients = message.to.length + message.cc.length + message.bcc.length;
|
|
90
|
+
console.info(`[everyapp dev] email to ${recipients} recipient${recipients === 1 ? "" : "s"} subject ${JSON.stringify(message.subject)} (not sent: set EMAIL_REST_API_TOKEN, CLOUDFLARE_ACCOUNT_ID and EMAIL_FROM in .dev.vars to send)`);
|
|
91
|
+
return { status: "logged", messageId: `dev-${crypto.randomUUID()}` };
|
|
92
|
+
}
|
|
93
|
+
async function sendWithRest(message, from, token, accountId) {
|
|
94
|
+
const response = await fetch(emailRestUrl(accountId), {
|
|
95
|
+
method: "POST",
|
|
96
|
+
headers: {
|
|
97
|
+
authorization: `Bearer ${token}`,
|
|
98
|
+
"content-type": "application/json",
|
|
99
|
+
},
|
|
100
|
+
body: JSON.stringify(emailRestBody({ ...message, from })),
|
|
101
|
+
});
|
|
102
|
+
const body = await response.json().catch(() => null);
|
|
103
|
+
const result = readEmailRestResponse(response.status, body);
|
|
104
|
+
if (!result.ok) {
|
|
105
|
+
throw new GatewayRefusalError(emailProviderRefusal(result.providerCode, emailDomain(from.address)));
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
status: result.status,
|
|
109
|
+
...(result.messageId ? { messageId: result.messageId } : {}),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function stringValue(value) {
|
|
113
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
114
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `defineTools()` — agent tools an app offers to MCP clients through the
|
|
3
|
+
* Every App gateway.
|
|
4
|
+
*
|
|
5
|
+
* export const tools = defineTools(
|
|
6
|
+
* {
|
|
7
|
+
* list_todos: {
|
|
8
|
+
* description: "List the user's todos.",
|
|
9
|
+
* risk: "read",
|
|
10
|
+
* input: z.object({}).strict(),
|
|
11
|
+
* handler: async (_input, { user }) => listTodos(user.id),
|
|
12
|
+
* },
|
|
13
|
+
* },
|
|
14
|
+
* { instructions: "Never guess todo ids; call list_todos first." },
|
|
15
|
+
* );
|
|
16
|
+
* export default everyApp(handler, { tools });
|
|
17
|
+
*
|
|
18
|
+
* Apps never speak MCP. The gateway snapshots this catalog at deploy, serves
|
|
19
|
+
* MCP clients from the snapshot, and calls a tool over the internal tools
|
|
20
|
+
* contract (../internal/tools.ts) with a verified `chan: "mcp"` identity.
|
|
21
|
+
*
|
|
22
|
+
* `input` and `output` accept any validator implementing Standard Schema and
|
|
23
|
+
* Standard JSON Schema (zod >= 4.2 does both), or a plain
|
|
24
|
+
* `{ jsonSchema, validate }` pair. The SDK itself takes no dependency.
|
|
25
|
+
*/
|
|
26
|
+
import { type EveryAppUser, type ToolCallResultV1, type ToolDescriptorV1, type ToolIssueV1, type ToolRisk } from "../internal/index.js";
|
|
27
|
+
import type { ExecutionContextLike } from "./everyApp.js";
|
|
28
|
+
/** Reported with every catalog; must match package.json. */
|
|
29
|
+
export declare const SDK_VERSION = "0.3.0";
|
|
30
|
+
/**
|
|
31
|
+
* Throw from a tool handler to return an error the model can read and act on
|
|
32
|
+
* ("No todo with that id"). Any other error is logged here and reaches the
|
|
33
|
+
* client only as "Internal error in <app> (ref <call id>)".
|
|
34
|
+
*/
|
|
35
|
+
export declare class ToolError extends Error {
|
|
36
|
+
constructor(message: string);
|
|
37
|
+
}
|
|
38
|
+
type StandardPathSegment = PropertyKey | {
|
|
39
|
+
readonly key: PropertyKey;
|
|
40
|
+
};
|
|
41
|
+
interface StandardIssue {
|
|
42
|
+
readonly message: string;
|
|
43
|
+
readonly path?: ReadonlyArray<StandardPathSegment> | undefined;
|
|
44
|
+
}
|
|
45
|
+
type StandardResult<Output> = {
|
|
46
|
+
readonly value: Output;
|
|
47
|
+
readonly issues?: undefined;
|
|
48
|
+
} | {
|
|
49
|
+
readonly issues: ReadonlyArray<StandardIssue>;
|
|
50
|
+
};
|
|
51
|
+
interface StandardSchemaProps<Input, Output> {
|
|
52
|
+
readonly version: 1;
|
|
53
|
+
readonly vendor: string;
|
|
54
|
+
readonly validate: (value: unknown, options?: {
|
|
55
|
+
readonly libraryOptions?: Record<string, unknown>;
|
|
56
|
+
}) => StandardResult<Output> | Promise<StandardResult<Output>>;
|
|
57
|
+
readonly types?: {
|
|
58
|
+
readonly input: Input;
|
|
59
|
+
readonly output: Output;
|
|
60
|
+
} | undefined;
|
|
61
|
+
}
|
|
62
|
+
interface StandardJsonSchemaConverter {
|
|
63
|
+
readonly input: (options: {
|
|
64
|
+
readonly target: string;
|
|
65
|
+
}) => Record<string, unknown>;
|
|
66
|
+
readonly output: (options: {
|
|
67
|
+
readonly target: string;
|
|
68
|
+
}) => Record<string, unknown>;
|
|
69
|
+
}
|
|
70
|
+
/** A Standard Schema validator that also converts to JSON Schema (zod >= 4.2). */
|
|
71
|
+
export interface StandardSchemaWithJSON<Input = unknown, Output = Input> {
|
|
72
|
+
readonly "~standard": StandardSchemaProps<Input, Output> & {
|
|
73
|
+
readonly jsonSchema: StandardJsonSchemaConverter;
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/** For validators without Standard JSON Schema: supply the JSON Schema too. */
|
|
77
|
+
export interface PlainToolSchema<Output = unknown> {
|
|
78
|
+
jsonSchema: Record<string, unknown>;
|
|
79
|
+
validate(value: unknown): StandardResult<Output> | Promise<StandardResult<Output>>;
|
|
80
|
+
}
|
|
81
|
+
export type ToolSchema<Output = unknown> = StandardSchemaWithJSON<unknown, Output> | PlainToolSchema<Output>;
|
|
82
|
+
/** What a tool handler receives besides its validated input. */
|
|
83
|
+
export interface ToolContext<TEnv = Cloudflare.Env> {
|
|
84
|
+
/** The verified caller. `channel` is "mcp"; `toolCall` names this call. */
|
|
85
|
+
user: EveryAppUser;
|
|
86
|
+
env: TEnv;
|
|
87
|
+
ctx: ExecutionContextLike;
|
|
88
|
+
request: Request;
|
|
89
|
+
/** Unique id of this call; also the ref in masked error messages. */
|
|
90
|
+
callId: string;
|
|
91
|
+
}
|
|
92
|
+
export interface ToolDefinition<TInput = unknown> {
|
|
93
|
+
/** Short human-readable name clients may show. */
|
|
94
|
+
title?: string;
|
|
95
|
+
/** What the tool does and when to use it. The model reads this. */
|
|
96
|
+
description: string;
|
|
97
|
+
/**
|
|
98
|
+
* `read` changes nothing. `write` creates or changes data. `destructive`
|
|
99
|
+
* deletes or irreversibly changes data; AI clients can't see or call
|
|
100
|
+
* destructive tools until approvals exist.
|
|
101
|
+
*/
|
|
102
|
+
risk: ToolRisk;
|
|
103
|
+
/** Arguments schema; must describe an object. */
|
|
104
|
+
input: ToolSchema<TInput>;
|
|
105
|
+
/**
|
|
106
|
+
* Result schema; must describe an object. The result is validated and
|
|
107
|
+
* also sent as MCP structured content.
|
|
108
|
+
*/
|
|
109
|
+
output?: ToolSchema;
|
|
110
|
+
/** Repeating a call with the same arguments has no further effect. */
|
|
111
|
+
idempotent?: boolean;
|
|
112
|
+
/** The tool reaches systems outside this app (other APIs, the web). */
|
|
113
|
+
openWorld?: boolean;
|
|
114
|
+
/**
|
|
115
|
+
* Return a string (sent as text), a JSON-serializable value (sent as a
|
|
116
|
+
* JSON text block), or {@link toolResult} for text and data together.
|
|
117
|
+
* Throw {@link ToolError} for errors the model may see.
|
|
118
|
+
*/
|
|
119
|
+
handler: (input: TInput, context: ToolContext) => unknown;
|
|
120
|
+
}
|
|
121
|
+
declare const TOOL_RESULT: unique symbol;
|
|
122
|
+
/** What {@link toolResult} returns; a handler returns it as is. */
|
|
123
|
+
export interface ToolResult {
|
|
124
|
+
readonly [TOOL_RESULT]: true;
|
|
125
|
+
readonly text: string;
|
|
126
|
+
readonly structured: Record<string, unknown>;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Return a written result and the same result as data. The model reads
|
|
130
|
+
* `text` (a summary, a table); clients also get `structured`, checked
|
|
131
|
+
* against the tool's `output` schema. Without this, a tool with an `output`
|
|
132
|
+
* schema sends its value as JSON text.
|
|
133
|
+
*/
|
|
134
|
+
export declare function toolResult(result: {
|
|
135
|
+
text: string;
|
|
136
|
+
structured: Record<string, unknown>;
|
|
137
|
+
}): ToolResult;
|
|
138
|
+
export interface DefineToolsOptions {
|
|
139
|
+
/** Guidance for the model that spans this app's tools (at most 2 KB). */
|
|
140
|
+
instructions?: string;
|
|
141
|
+
}
|
|
142
|
+
/** Returned by {@link defineTools}; pass it to `everyApp(…, { tools })`. */
|
|
143
|
+
export interface ToolSet {
|
|
144
|
+
/** Tool names, sorted. */
|
|
145
|
+
readonly names: readonly string[];
|
|
146
|
+
}
|
|
147
|
+
export interface ToolCatalog {
|
|
148
|
+
instructions?: string;
|
|
149
|
+
tools: ToolDescriptorV1[];
|
|
150
|
+
hash: string;
|
|
151
|
+
/** Tools left out because they can't be described, and why. */
|
|
152
|
+
warnings: string[];
|
|
153
|
+
}
|
|
154
|
+
interface ToolSetInternals {
|
|
155
|
+
tool(name: string): ToolDefinition | undefined;
|
|
156
|
+
/** Descriptors and hash, computed once per isolate. */
|
|
157
|
+
catalog(): Promise<ToolCatalog>;
|
|
158
|
+
}
|
|
159
|
+
/** @internal The everyApp() side of a tool set. */
|
|
160
|
+
export declare function toolSetInternals(toolSet: ToolSet): ToolSetInternals;
|
|
161
|
+
/**
|
|
162
|
+
* Define an app's agent tools. Names are lowercase snake_case (at most 48
|
|
163
|
+
* characters); the gateway prefixes them with the app id where several apps
|
|
164
|
+
* share one MCP endpoint. Handler inputs are typed from each `input` schema.
|
|
165
|
+
*/
|
|
166
|
+
export declare function defineTools<TInputs extends Record<string, unknown>>(tools: {
|
|
167
|
+
[K in keyof TInputs]: ToolDefinition<TInputs[K]>;
|
|
168
|
+
}, options?: DefineToolsOptions): ToolSet;
|
|
169
|
+
export type ValidationOutcome<T> = {
|
|
170
|
+
ok: true;
|
|
171
|
+
value: T;
|
|
172
|
+
} | {
|
|
173
|
+
ok: false;
|
|
174
|
+
issues: ToolIssueV1[];
|
|
175
|
+
};
|
|
176
|
+
/** Run a tool schema's validator. */
|
|
177
|
+
export declare function validateWithSchema<T>(schema: ToolSchema<T>, value: unknown): Promise<ValidationOutcome<T>>;
|
|
178
|
+
/**
|
|
179
|
+
* Turn a handler's return value into a tool result. With an output schema,
|
|
180
|
+
* the value must validate and is sent as structured content as well as text.
|
|
181
|
+
*/
|
|
182
|
+
export declare function toToolResult(definition: ToolDefinition, value: unknown): Promise<ToolCallResultV1>;
|
|
183
|
+
export {};
|
|
184
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/server/tools.ts"],"names":[],"mappings":"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"}
|