@wervt/nuxt 0.3.2 → 0.3.4
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 +2 -0
- package/dist/dev.d.ts +4 -4
- package/dist/dev.js +33 -11
- package/dist/env-types.js +13 -0
- package/dist/icons.js +30 -0
- package/dist/loading.js +30 -0
- package/dist/module.d.ts +0 -2
- package/dist/module.js +42 -2
- package/dist/node_modules/.pnpm/nitro@3.0.260903-beta/node_modules/nitro/dist/runtime/internal/task.js +1 -2
- package/dist/node_modules/.pnpm/nitro@3.0.260903-beta/node_modules/nitro/dist/runtime/task.js +1 -2
- package/dist/og-image.js +38 -0
- package/dist/preview.d.ts +1 -3
- package/dist/preview.js +1 -2
- package/dist/provision.d.ts +31 -4
- package/dist/provision.js +82 -21
- package/dist/route-tools.js +1 -2
- package/dist/runtime/app/plugins/analytics.client.d.ts +0 -2
- package/dist/runtime/app/plugins/analytics.client.js +1 -2
- package/dist/runtime/app/plugins/feedback.client.d.ts +0 -2
- package/dist/runtime/app/plugins/feedback.client.js +1 -2
- package/dist/runtime/app/plugins/update.client.d.ts +0 -2
- package/dist/runtime/app/plugins/update.client.js +1 -2
- package/dist/runtime/app/plugins/user.client.d.ts +0 -2
- package/dist/runtime/app/plugins/user.client.js +1 -2
- package/dist/runtime/app/plugins/webmcp.client.d.ts +0 -2
- package/dist/runtime/app/plugins/webmcp.client.js +1 -2
- package/dist/runtime/app/preview-error.d.ts +26 -0
- package/dist/runtime/app/preview-error.js +69 -0
- package/dist/runtime/app/snapshot.d.ts +15 -0
- package/dist/runtime/app/snapshot.js +63 -0
- package/dist/runtime/app/useShape.d.ts +8 -3
- package/dist/runtime/app/useShape.js +10 -55
- package/dist/runtime/app/useWervtFeedback.d.ts +1 -2
- package/dist/runtime/app/useWervtFeedback.js +1 -2
- package/dist/runtime/app/useWervtTrack.d.ts +1 -3
- package/dist/runtime/app/useWervtTrack.js +1 -2
- package/dist/runtime/app/useWervtUpdate.d.ts +1 -3
- package/dist/runtime/app/useWervtUpdate.js +2 -3
- package/dist/runtime/app/useWervtUser.d.ts +1 -3
- package/dist/runtime/app/useWervtUser.js +1 -2
- package/dist/runtime/email/components.d.ts +1 -3
- package/dist/runtime/email/components.js +1 -2
- package/dist/runtime/email/index.d.ts +2 -1
- package/dist/runtime/email/index.js +2 -1
- package/dist/runtime/email/invite.d.ts +73 -0
- package/dist/runtime/email/invite.js +59 -0
- package/dist/runtime/email/render.d.ts +1 -3
- package/dist/runtime/email/render.js +1 -2
- package/dist/runtime/server/access.d.ts +4 -5
- package/dist/runtime/server/access.js +4 -4
- package/dist/runtime/server/agent-token-plugin.d.ts +11 -0
- package/dist/runtime/server/agent-token-plugin.js +9 -0
- package/dist/runtime/server/anthropic.d.ts +19 -0
- package/dist/runtime/server/anthropic.js +52 -0
- package/dist/runtime/server/boot.d.ts +0 -2
- package/dist/runtime/server/boot.js +1 -2
- package/dist/runtime/server/db.d.ts +1 -3
- package/dist/runtime/server/db.js +1 -2
- package/dist/runtime/server/dev-auth.d.ts +0 -2
- package/dist/runtime/server/dev-auth.js +1 -2
- package/dist/runtime/server/email-preview.d.ts +0 -2
- package/dist/runtime/server/email-preview.js +1 -2
- package/dist/runtime/server/identity.d.ts +18 -0
- package/dist/runtime/server/identity.js +36 -0
- package/dist/runtime/server/index.d.ts +4 -3
- package/dist/runtime/server/index.js +4 -3
- package/dist/runtime/server/logger.d.ts +0 -2
- package/dist/runtime/server/logger.js +1 -2
- package/dist/runtime/server/mail.d.ts +1 -3
- package/dist/runtime/server/mail.js +1 -2
- package/dist/runtime/server/mcp-auth-server.d.ts +0 -2
- package/dist/runtime/server/mcp-auth-server.js +1 -2
- package/dist/runtime/server/mcp-auth.d.ts +1 -3
- package/dist/runtime/server/mcp-auth.js +1 -2
- package/dist/runtime/server/mcp-claims.d.ts +1 -3
- package/dist/runtime/server/mcp-claims.js +1 -2
- package/dist/runtime/server/mcp-metadata.d.ts +0 -2
- package/dist/runtime/server/mcp-metadata.js +1 -2
- package/dist/runtime/server/mcp.d.ts +0 -2
- package/dist/runtime/server/mcp.js +1 -2
- package/dist/runtime/server/preview-errors-plugin.d.ts +13 -0
- package/dist/runtime/server/preview-errors-plugin.js +19 -0
- package/dist/runtime/server/seed.d.ts +1 -3
- package/dist/runtime/server/seed.js +1 -2
- package/dist/runtime/server/shape.d.ts +1 -3
- package/dist/runtime/server/shape.js +1 -2
- package/dist/runtime/server/storage-plugin.d.ts +1 -3
- package/dist/runtime/server/storage-plugin.js +1 -2
- package/dist/runtime/server/task-handlers.d.ts +2 -3
- package/dist/runtime/server/task-handlers.js +1 -2
- package/dist/runtime/server/task-list-nitro2.d.ts +0 -2
- package/dist/runtime/server/task-list-nitro2.js +1 -2
- package/dist/runtime/server/task-list-nitro3.d.ts +0 -2
- package/dist/runtime/server/task-list-nitro3.js +1 -2
- package/dist/runtime/server/task-run-nitro2.d.ts +0 -2
- package/dist/runtime/server/task-run-nitro2.js +1 -2
- package/dist/runtime/server/task-run-nitro3.d.ts +0 -2
- package/dist/runtime/server/task-run-nitro3.js +1 -2
- package/dist/runtime/server/tasks.d.ts +1 -3
- package/dist/runtime/server/tasks.js +1 -2
- package/dist/runtime/server/tool-handlers.d.ts +1 -3
- package/dist/runtime/server/tool-handlers.js +1 -2
- package/dist/runtime/server/tool-list.js +1 -2
- package/dist/runtime/server/tool-run.js +1 -2
- package/dist/runtime/server/tools.d.ts +1 -2
- package/dist/runtime/server/tools.js +1 -2
- package/dist/runtime/server/user-route.d.ts +0 -2
- package/dist/runtime/server/user-route.js +1 -2
- package/dist/runtime/server/user.d.ts +9 -5
- package/dist/runtime/server/user.js +29 -5
- package/dist/runtime/server/validate.d.ts +1 -2
- package/dist/runtime/server/validate.js +1 -2
- package/dist/runtime/shared/agent-token.d.ts +7 -0
- package/dist/runtime/shared/agent-token.js +16 -0
- package/dist/runtime/shared/mark.d.ts +1 -0
- package/dist/runtime/shared/mark.js +3 -0
- package/dist/runtime/shared/preview-errors.d.ts +10 -0
- package/dist/runtime/shared/preview-errors.js +14 -0
- package/dist/runtime/shared/shape.d.ts +1 -2
- package/dist/runtime/shared/shape.js +1 -2
- package/dist/runtime/shared/user.d.ts +9 -3
- package/dist/runtime/shared/user.js +5 -3
- package/dist/seed.js +1 -2
- package/dist/shapes.js +1 -2
- package/dist/storage.d.ts +50 -4
- package/dist/storage.js +224 -5
- package/dist/studio-preview.js +34 -0
- package/package.json +17 -8
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Component } from "vue";
|
|
2
|
-
//#region src/runtime/email/render.d.ts
|
|
3
2
|
export interface RenderedEmail {
|
|
4
3
|
subject: string;
|
|
5
4
|
html: string;
|
|
@@ -13,5 +12,4 @@ export type EmailProps<T> = T extends (new () => {
|
|
|
13
12
|
* Renders an email template (a component with `<WervtEmail>` at its root) to HTML, a plain-text
|
|
14
13
|
* part and its subject.
|
|
15
14
|
*/
|
|
16
|
-
export declare function renderEmail<T extends Component>(template: T, props?: EmailProps<T>): Promise<RenderedEmail>;
|
|
17
|
-
//#endregion
|
|
15
|
+
export declare function renderEmail<T extends Component>(template: T, props?: EmailProps<T>): Promise<RenderedEmail>;
|
|
@@ -2,7 +2,7 @@ import { SUBJECT_KEY } from "./components.js";
|
|
|
2
2
|
import { createSSRApp } from "vue";
|
|
3
3
|
import { convert } from "html-to-text";
|
|
4
4
|
import { renderToString } from "vue/server-renderer";
|
|
5
|
-
|
|
5
|
+
// src/runtime/email/render.ts
|
|
6
6
|
const DOCTYPE = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">";
|
|
7
7
|
/**
|
|
8
8
|
* Renders an email template (a component with `<WervtEmail>` at its root) to HTML, a plain-text
|
|
@@ -64,5 +64,4 @@ function toText(html) {
|
|
|
64
64
|
]
|
|
65
65
|
}).trim();
|
|
66
66
|
}
|
|
67
|
-
//#endregion
|
|
68
67
|
export { renderEmail };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { RequestEvent } from "nuxt/server";
|
|
2
|
-
//#region src/runtime/server/access.d.ts
|
|
3
2
|
export interface WervtInvite {
|
|
4
3
|
email: string;
|
|
5
4
|
/** Who invited them. */
|
|
@@ -24,11 +23,11 @@ export interface WervtAccess {
|
|
|
24
23
|
/**
|
|
25
24
|
* Invites on an invite-only app (`wervt access <app> --invite`), as the user of this request: the
|
|
26
25
|
* runtime vouches for them, and only someone with access can invite. Everyone with access may
|
|
27
|
-
* invite
|
|
26
|
+
* invite, or only the owner, members and admins with `wervt access <app> --invite-by owners`;
|
|
27
|
+
* restrict it further in the route if the app has its own roles.
|
|
28
28
|
*
|
|
29
|
-
* Throws with the platform's status: 401 without a signed-in user, 403 when they
|
|
29
|
+
* Throws with the platform's status: 401 without a signed-in user, 403 when they may not invite,
|
|
30
30
|
* 409 when the app isn't invite-only, 429 past the limits (25 invites per person and day, 500 per
|
|
31
31
|
* app).
|
|
32
32
|
*/
|
|
33
|
-
export declare function useWervtAccess(event: RequestEvent): WervtAccess;
|
|
34
|
-
//#endregion
|
|
33
|
+
export declare function useWervtAccess(event: RequestEvent): WervtAccess;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { DEV_LOGIN_COOKIE, devUser } from "../shared/user.js";
|
|
2
2
|
import { createError, getCookie } from "nuxt/server";
|
|
3
|
-
|
|
3
|
+
// src/runtime/server/access.ts
|
|
4
4
|
const devInvites = [];
|
|
5
5
|
/**
|
|
6
6
|
* Invites on an invite-only app (`wervt access <app> --invite`), as the user of this request: the
|
|
7
7
|
* runtime vouches for them, and only someone with access can invite. Everyone with access may
|
|
8
|
-
* invite
|
|
8
|
+
* invite, or only the owner, members and admins with `wervt access <app> --invite-by owners`;
|
|
9
|
+
* restrict it further in the route if the app has its own roles.
|
|
9
10
|
*
|
|
10
|
-
* Throws with the platform's status: 401 without a signed-in user, 403 when they
|
|
11
|
+
* Throws with the platform's status: 401 without a signed-in user, 403 when they may not invite,
|
|
11
12
|
* 409 when the app isn't invite-only, 429 past the limits (25 invites per person and day, 500 per
|
|
12
13
|
* app).
|
|
13
14
|
*/
|
|
@@ -83,5 +84,4 @@ function devAccess(event) {
|
|
|
83
84
|
}
|
|
84
85
|
};
|
|
85
86
|
}
|
|
86
|
-
//#endregion
|
|
87
87
|
export { useWervtAccess };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface EnrichContext {
|
|
2
|
+
event: Record<string, unknown>;
|
|
3
|
+
headers?: Record<string, string>;
|
|
4
|
+
}
|
|
5
|
+
interface NitroAppHooks {
|
|
6
|
+
hooks: {
|
|
7
|
+
hook(name: 'evlog:enrich', handler: (ctx: EnrichContext) => void): unknown;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export default function wervtAgentToken(nitroApp: NitroAppHooks): void;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { parseAgentToken } from "../shared/agent-token.js";
|
|
2
|
+
// src/runtime/server/agent-token-plugin.ts
|
|
3
|
+
function wervtAgentToken(nitroApp) {
|
|
4
|
+
nitroApp.hooks.hook("evlog:enrich", (ctx) => {
|
|
5
|
+
const agentToken = parseAgentToken(ctx.headers?.["x-wervt-agent-token"]);
|
|
6
|
+
if (agentToken) ctx.event.agentToken = agentToken;
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
export { wervtAgentToken as default };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
2
|
+
export interface UseAnthropicOptions {
|
|
3
|
+
/**
|
|
4
|
+
* The key for `nuxt dev`, where there is no workload identity. Default: `ANTHROPIC_API_KEY`.
|
|
5
|
+
* Never used on wervt.
|
|
6
|
+
*/
|
|
7
|
+
devApiKey?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* An Anthropic client that authenticates as this app through its wervt identity: no API key, only
|
|
11
|
+
* IDs from the Anthropic Console in the app's env (`ANTHROPIC_FEDERATION_RULE_ID`,
|
|
12
|
+
* `ANTHROPIC_ORGANIZATION_ID`, optionally `ANTHROPIC_SERVICE_ACCOUNT_ID` and
|
|
13
|
+
* `ANTHROPIC_WORKSPACE_ID`). The SDK trades an identity token for an Anthropic token and renews it
|
|
14
|
+
* before it runs out.
|
|
15
|
+
*
|
|
16
|
+
* Needs `wervt identity enable <app>` and a federation rule for the app's subject
|
|
17
|
+
* (`wervt identity status <app>`). In `nuxt dev` it uses `options.devApiKey` instead.
|
|
18
|
+
*/
|
|
19
|
+
export declare function useAnthropic(options?: UseAnthropicOptions): Anthropic;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { useWorkloadIdentity } from "./identity.js";
|
|
2
|
+
import { createError } from "nuxt/server";
|
|
3
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
4
|
+
import { oidcFederationProvider } from "@anthropic-ai/sdk/lib/credentials/oidc-federation";
|
|
5
|
+
// src/runtime/server/anthropic.ts
|
|
6
|
+
const BASE_URL = "https://api.anthropic.com";
|
|
7
|
+
let client;
|
|
8
|
+
/**
|
|
9
|
+
* An Anthropic client that authenticates as this app through its wervt identity: no API key, only
|
|
10
|
+
* IDs from the Anthropic Console in the app's env (`ANTHROPIC_FEDERATION_RULE_ID`,
|
|
11
|
+
* `ANTHROPIC_ORGANIZATION_ID`, optionally `ANTHROPIC_SERVICE_ACCOUNT_ID` and
|
|
12
|
+
* `ANTHROPIC_WORKSPACE_ID`). The SDK trades an identity token for an Anthropic token and renews it
|
|
13
|
+
* before it runs out.
|
|
14
|
+
*
|
|
15
|
+
* Needs `wervt identity enable <app>` and a federation rule for the app's subject
|
|
16
|
+
* (`wervt identity status <app>`). In `nuxt dev` it uses `options.devApiKey` instead.
|
|
17
|
+
*/
|
|
18
|
+
function useAnthropic(options = {}) {
|
|
19
|
+
return client ??= create(options);
|
|
20
|
+
}
|
|
21
|
+
function create(options) {
|
|
22
|
+
if (!process.env.WERVT_RUNTIME_URL) {
|
|
23
|
+
const apiKey = options.devApiKey ?? process.env.ANTHROPIC_API_KEY;
|
|
24
|
+
if (!apiKey) throw createError({
|
|
25
|
+
status: 503,
|
|
26
|
+
message: "useAnthropic() in `nuxt dev` needs an API key (ANTHROPIC_API_KEY or devApiKey)."
|
|
27
|
+
});
|
|
28
|
+
return new Anthropic({ apiKey });
|
|
29
|
+
}
|
|
30
|
+
const federationRuleId = process.env.ANTHROPIC_FEDERATION_RULE_ID;
|
|
31
|
+
const organizationId = process.env.ANTHROPIC_ORGANIZATION_ID;
|
|
32
|
+
if (!federationRuleId || !organizationId) throw createError({
|
|
33
|
+
status: 503,
|
|
34
|
+
message: "useAnthropic() needs ANTHROPIC_FEDERATION_RULE_ID and ANTHROPIC_ORGANIZATION_ID (wervt env <app> --set …)."
|
|
35
|
+
});
|
|
36
|
+
const identity = useWorkloadIdentity();
|
|
37
|
+
return new Anthropic({
|
|
38
|
+
apiKey: null,
|
|
39
|
+
authToken: null,
|
|
40
|
+
baseURL: BASE_URL,
|
|
41
|
+
credentials: oidcFederationProvider({
|
|
42
|
+
identityTokenProvider: () => identity.token(BASE_URL),
|
|
43
|
+
federationRuleId,
|
|
44
|
+
organizationId,
|
|
45
|
+
serviceAccountId: process.env.ANTHROPIC_SERVICE_ACCOUNT_ID || void 0,
|
|
46
|
+
workspaceId: process.env.ANTHROPIC_WORKSPACE_ID || void 0,
|
|
47
|
+
baseURL: BASE_URL,
|
|
48
|
+
fetch: (...args) => fetch(...args)
|
|
49
|
+
})
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
export { useAnthropic };
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/runtime/server/boot.d.ts
|
|
2
1
|
declare const _default: import("nuxt/server").EventHandler<{
|
|
3
2
|
error: string;
|
|
4
3
|
ok?: undefined;
|
|
@@ -6,5 +5,4 @@ declare const _default: import("nuxt/server").EventHandler<{
|
|
|
6
5
|
error?: undefined;
|
|
7
6
|
ok: boolean;
|
|
8
7
|
}>;
|
|
9
|
-
//#endregion
|
|
10
8
|
export { _default as default };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineEventHandler } from "nuxt/server";
|
|
2
|
-
|
|
2
|
+
// src/runtime/server/boot.ts
|
|
3
3
|
var boot_default = defineEventHandler((event) => {
|
|
4
4
|
event.res.headers.set("cache-control", "no-store");
|
|
5
5
|
if (event.req.headers.get("x-wervt-task") !== "1") {
|
|
@@ -8,5 +8,4 @@ var boot_default = defineEventHandler((event) => {
|
|
|
8
8
|
}
|
|
9
9
|
return { ok: true };
|
|
10
10
|
});
|
|
11
|
-
//#endregion
|
|
12
11
|
export { boot_default as default };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { PostgresJsDatabase } from "drizzle-orm/postgres-js";
|
|
2
2
|
import { AnyRelations, EmptyRelations } from "drizzle-orm";
|
|
3
|
-
//#region src/runtime/server/db.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* Drizzle client for the app's database. In production the wervt runtime injects
|
|
6
5
|
* DATABASE_URL for the app's own role, whose search_path is its own schema, so
|
|
@@ -15,5 +14,4 @@ import { AnyRelations, EmptyRelations } from "drizzle-orm";
|
|
|
15
14
|
*/
|
|
16
15
|
export declare function useDb<TRelations extends AnyRelations = EmptyRelations>(options?: {
|
|
17
16
|
relations?: TRelations;
|
|
18
|
-
}): PostgresJsDatabase<TRelations>;
|
|
19
|
-
//#endregion
|
|
17
|
+
}): PostgresJsDatabase<TRelations>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { drizzle } from "drizzle-orm/postgres-js";
|
|
2
2
|
import postgres from "postgres";
|
|
3
|
-
|
|
3
|
+
// src/runtime/server/db.ts
|
|
4
4
|
let client;
|
|
5
5
|
const clients = /* @__PURE__ */ new Map();
|
|
6
6
|
/**
|
|
@@ -32,5 +32,4 @@ function useDb(options) {
|
|
|
32
32
|
}
|
|
33
33
|
return db;
|
|
34
34
|
}
|
|
35
|
-
//#endregion
|
|
36
35
|
export { useDb };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DEV_LOGIN_COOKIE } from "../shared/user.js";
|
|
2
2
|
import { defineEventHandler, deleteCookie, getQuery, sendRedirect, setCookie } from "nuxt/server";
|
|
3
|
-
|
|
3
|
+
// src/runtime/server/dev-auth.ts
|
|
4
4
|
var dev_auth_default = defineEventHandler((event) => {
|
|
5
5
|
const { redirect } = getQuery(event);
|
|
6
6
|
const target = typeof redirect === "string" && redirect.startsWith("/") ? redirect : "/";
|
|
@@ -11,5 +11,4 @@ var dev_auth_default = defineEventHandler((event) => {
|
|
|
11
11
|
else deleteCookie(event, DEV_LOGIN_COOKIE, { path: "/" });
|
|
12
12
|
return sendRedirect(event, target);
|
|
13
13
|
});
|
|
14
|
-
//#endregion
|
|
15
14
|
export { dev_auth_default as default };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { renderEmail } from "../email/render.js";
|
|
2
2
|
import { createError, defineEventHandler, getQuery } from "nuxt/server";
|
|
3
3
|
import { templates } from "#wervt/emails";
|
|
4
|
-
|
|
4
|
+
// src/runtime/server/email-preview.ts
|
|
5
5
|
const list = templates;
|
|
6
6
|
const page = (body) => new Response(`<!doctype html><meta charset="utf-8"><title>Emails</title><body style="font:15px/1.6 system-ui;margin:40px">${body}</body>`, { headers: { "content-type": "text/html; charset=utf-8" } });
|
|
7
7
|
const escape = (value) => value.replace(/[&<>"]/g, (c) => ({
|
|
@@ -26,5 +26,4 @@ var email_preview_default = defineEventHandler(async (event) => {
|
|
|
26
26
|
if ("text" in getQuery(event)) return new Response(`Subject: ${mail.subject}\n\n${mail.text}\n`, { headers: { "content-type": "text/plain; charset=utf-8" } });
|
|
27
27
|
return new Response(mail.html, { headers: { "content-type": "text/html; charset=utf-8" } });
|
|
28
28
|
});
|
|
29
|
-
//#endregion
|
|
30
29
|
export { email_preview_default as default };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface WorkloadIdentity {
|
|
2
|
+
/**
|
|
3
|
+
* A fresh JWT that says "I am this wervt app", for `audience` (a provider on wervt's list, e.g.
|
|
4
|
+
* `https://api.anthropic.com`). Valid for 5 minutes and single-use: trade it for a provider
|
|
5
|
+
* token right away. Each call signs a new one; providers' SDKs cache what they get for it.
|
|
6
|
+
*/
|
|
7
|
+
token(audience: string): Promise<string>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The app's workload identity: tokens signed by wervt's issuer, so the app can authenticate to an
|
|
11
|
+
* AI provider (Anthropic's Workload Identity Federation) without an API key. Its owner turns it
|
|
12
|
+
* on with `wervt identity enable <app>`; `useAnthropic()` (`@wervt/nuxt/anthropic`) uses it.
|
|
13
|
+
*
|
|
14
|
+
* Throws with the platform's status: 403 when the app has no identity, 400 for an audience wervt
|
|
15
|
+
* doesn't sign for, 429 past 10 tokens a minute, 503 when the issuer is off. In `nuxt dev` there
|
|
16
|
+
* is no issuer, so it always throws.
|
|
17
|
+
*/
|
|
18
|
+
export declare function useWorkloadIdentity(): WorkloadIdentity;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { createError } from "nuxt/server";
|
|
2
|
+
// src/runtime/server/identity.ts
|
|
3
|
+
/**
|
|
4
|
+
* The app's workload identity: tokens signed by wervt's issuer, so the app can authenticate to an
|
|
5
|
+
* AI provider (Anthropic's Workload Identity Federation) without an API key. Its owner turns it
|
|
6
|
+
* on with `wervt identity enable <app>`; `useAnthropic()` (`@wervt/nuxt/anthropic`) uses it.
|
|
7
|
+
*
|
|
8
|
+
* Throws with the platform's status: 403 when the app has no identity, 400 for an audience wervt
|
|
9
|
+
* doesn't sign for, 429 past 10 tokens a minute, 503 when the issuer is off. In `nuxt dev` there
|
|
10
|
+
* is no issuer, so it always throws.
|
|
11
|
+
*/
|
|
12
|
+
function useWorkloadIdentity() {
|
|
13
|
+
return { async token(audience) {
|
|
14
|
+
const runtime = process.env.WERVT_RUNTIME_URL;
|
|
15
|
+
if (!runtime) throw createError({
|
|
16
|
+
status: 503,
|
|
17
|
+
message: "Workload identity only exists on wervt, not in `nuxt dev`."
|
|
18
|
+
});
|
|
19
|
+
const res = await fetch(new URL("/_wervt/identity-token", runtime), {
|
|
20
|
+
method: "POST",
|
|
21
|
+
headers: {
|
|
22
|
+
"content-type": "application/json",
|
|
23
|
+
"x-wervt-app": process.env.WERVT_APP ?? "",
|
|
24
|
+
"x-wervt-token": process.env.WERVT_TOKEN ?? ""
|
|
25
|
+
},
|
|
26
|
+
body: JSON.stringify({ audience })
|
|
27
|
+
});
|
|
28
|
+
const body = await res.json().catch(() => ({}));
|
|
29
|
+
if (!res.ok || !body.token) throw createError({
|
|
30
|
+
status: res.status,
|
|
31
|
+
message: body.error ?? `identity token failed (${res.status})`
|
|
32
|
+
});
|
|
33
|
+
return body.token;
|
|
34
|
+
} };
|
|
35
|
+
}
|
|
36
|
+
export { useWorkloadIdentity };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { WervtUser } from "../shared/user.js";
|
|
1
|
+
import { WervtRole, WervtUser } from "../shared/user.js";
|
|
2
2
|
import { EmailProps, RenderedEmail, renderEmail } from "../email/render.js";
|
|
3
3
|
import { WervtAccess, WervtInvite, useWervtAccess } from "./access.js";
|
|
4
4
|
import { useDb } from "./db.js";
|
|
5
|
+
import { WorkloadIdentity, useWorkloadIdentity } from "./identity.js";
|
|
5
6
|
import { createEvlogError, log, useLogger } from "./logger.js";
|
|
6
7
|
import { SendMailOptions, sendMail } from "./mail.js";
|
|
7
8
|
import { McpTokenContext } from "./mcp-auth.js";
|
|
@@ -11,5 +12,5 @@ import { ShapeOptions, ShapeRow, proxyShape } from "./shape.js";
|
|
|
11
12
|
import { Task, TaskEvent, TaskResult, defineTask } from "./tasks.js";
|
|
12
13
|
import { SafeResult, useSafeValidatedBody, useSafeValidatedParams, useSafeValidatedQuery, useValidatedBody, useValidatedParams, useValidatedQuery, vh } from "./validate.js";
|
|
13
14
|
import { Tool, ToolRoute, defineTool, defineToolRoute } from "./tools.js";
|
|
14
|
-
import { useWervtUser } from "./user.js";
|
|
15
|
-
export { type EmailProps, type McpTokenContext, type RenderedEmail, type SafeResult, type SendMailOptions, type ShapeOptions, type ShapeResponse, type ShapeRow, type Task, type TaskEvent, type TaskResult, type Tool, type ToolRoute, type WervtAccess, type WervtInvite, type WervtUser, createEvlogError, defineTask, defineTool, defineToolRoute, log, proxyShape, renderEmail, seedTable, sendMail, useDb, useLogger, useSafeValidatedBody, useSafeValidatedParams, useSafeValidatedQuery, useValidatedBody, useValidatedParams, useValidatedQuery, useWervtAccess, useWervtUser, vh };
|
|
15
|
+
import { requireWervtRole, useWervtUser } from "./user.js";
|
|
16
|
+
export { type EmailProps, type McpTokenContext, type RenderedEmail, type SafeResult, type SendMailOptions, type ShapeOptions, type ShapeResponse, type ShapeRow, type Task, type TaskEvent, type TaskResult, type Tool, type ToolRoute, type WervtAccess, type WervtInvite, type WervtRole, type WervtUser, type WorkloadIdentity, createEvlogError, defineTask, defineTool, defineToolRoute, log, proxyShape, renderEmail, requireWervtRole, seedTable, sendMail, useDb, useLogger, useSafeValidatedBody, useSafeValidatedParams, useSafeValidatedQuery, useValidatedBody, useValidatedParams, useValidatedQuery, useWervtAccess, useWervtUser, useWorkloadIdentity, vh };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { renderEmail } from "../email/render.js";
|
|
1
2
|
import { useWervtAccess } from "./access.js";
|
|
3
|
+
import { useWorkloadIdentity } from "./identity.js";
|
|
2
4
|
import { useDb } from "./db.js";
|
|
3
|
-
import { renderEmail } from "../email/render.js";
|
|
4
5
|
import { createEvlogError, log, useLogger } from "./logger.js";
|
|
5
6
|
import { sendMail } from "./mail.js";
|
|
6
7
|
import { seedTable } from "./seed.js";
|
|
@@ -8,5 +9,5 @@ import { proxyShape } from "./shape.js";
|
|
|
8
9
|
import { defineTask } from "./tasks.js";
|
|
9
10
|
import { useSafeValidatedBody, useSafeValidatedParams, useSafeValidatedQuery, useValidatedBody, useValidatedParams, useValidatedQuery, vh } from "./validate.js";
|
|
10
11
|
import { defineTool, defineToolRoute } from "./tools.js";
|
|
11
|
-
import { useWervtUser } from "./user.js";
|
|
12
|
-
export { createEvlogError, defineTask, defineTool, defineToolRoute, log, proxyShape, renderEmail, seedTable, sendMail, useDb, useLogger, useSafeValidatedBody, useSafeValidatedParams, useSafeValidatedQuery, useValidatedBody, useValidatedParams, useValidatedQuery, useWervtAccess, useWervtUser, vh };
|
|
12
|
+
import { requireWervtRole, useWervtUser } from "./user.js";
|
|
13
|
+
export { createEvlogError, defineTask, defineTool, defineToolRoute, log, proxyShape, renderEmail, requireWervtRole, seedTable, sendMail, useDb, useLogger, useSafeValidatedBody, useSafeValidatedParams, useSafeValidatedQuery, useValidatedBody, useValidatedParams, useValidatedQuery, useWervtAccess, useWervtUser, useWorkloadIdentity, vh };
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { RequestEvent } from "nuxt/server";
|
|
2
2
|
import { createEvlogError, log } from "evlog";
|
|
3
|
-
//#region src/runtime/server/logger.d.ts
|
|
4
3
|
/** The request's wide event: `useLogger(event).set({ user: { id } })` adds fields to it. */
|
|
5
4
|
export declare function useLogger<T extends object = Record<string, unknown>>(event: RequestEvent, service?: string): import("evlog").AuditableLogger<T>;
|
|
6
|
-
//#endregion
|
|
7
5
|
export { createEvlogError, log };
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { createEvlogError, log, useLogger as useLogger$1 } from "evlog";
|
|
2
|
-
|
|
2
|
+
// src/runtime/server/logger.ts
|
|
3
3
|
/** The request's wide event: `useLogger(event).set({ user: { id } })` adds fields to it. */
|
|
4
4
|
function useLogger(event, service) {
|
|
5
5
|
return useLogger$1(event, service);
|
|
6
6
|
}
|
|
7
|
-
//#endregion
|
|
8
7
|
export { createEvlogError, log, useLogger };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { EmailProps } from "../email/render.js";
|
|
2
2
|
import { Component } from "vue";
|
|
3
|
-
//#region src/runtime/server/mail.d.ts
|
|
4
3
|
export interface SendMailOptions<T extends Component> {
|
|
5
4
|
/** One recipient. */
|
|
6
5
|
to: string;
|
|
@@ -19,5 +18,4 @@ export interface SendMailOptions<T extends Component> {
|
|
|
19
18
|
*/
|
|
20
19
|
export declare function sendMail<T extends Component>(options: SendMailOptions<T>): Promise<{
|
|
21
20
|
id: string;
|
|
22
|
-
}>;
|
|
23
|
-
//#endregion
|
|
21
|
+
}>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { renderEmail } from "../email/render.js";
|
|
2
2
|
import { createError } from "nuxt/server";
|
|
3
|
-
|
|
3
|
+
// src/runtime/server/mail.ts
|
|
4
4
|
/**
|
|
5
5
|
* Renders a template and sends it through wervt. Throws on failure, with the status the platform
|
|
6
6
|
* answered: 400 (invalid mail), 429 (the app's or the recipient's hourly limit), 502/503.
|
|
@@ -39,5 +39,4 @@ async function sendMail(options) {
|
|
|
39
39
|
});
|
|
40
40
|
return { id: body.id ?? "" };
|
|
41
41
|
}
|
|
42
|
-
//#endregion
|
|
43
42
|
export { sendMail };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CORS, fetchAuth, mcpEndpoint } from "./mcp-auth.js";
|
|
2
2
|
import { defineEventHandler } from "nuxt/server";
|
|
3
|
-
|
|
3
|
+
// src/runtime/server/mcp-auth-server.ts
|
|
4
4
|
var mcp_auth_server_default = defineEventHandler(async (event) => {
|
|
5
5
|
const endpoint = mcpEndpoint(event);
|
|
6
6
|
if (event.req.method === "OPTIONS") return new Response(null, {
|
|
@@ -17,5 +17,4 @@ var mcp_auth_server_default = defineEventHandler(async (event) => {
|
|
|
17
17
|
});
|
|
18
18
|
return Response.json(await res.json(), { headers: CORS });
|
|
19
19
|
});
|
|
20
|
-
//#endregion
|
|
21
20
|
export { mcp_auth_server_default as default };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { WervtUser } from "../shared/user.js";
|
|
2
2
|
import { RequestEvent } from "nuxt/server";
|
|
3
|
-
//#region src/runtime/server/mcp-auth.d.ts
|
|
4
3
|
/** What `wervt:mcp:token` handlers get: set `user` to accept the token. */
|
|
5
4
|
export interface McpTokenContext {
|
|
6
5
|
token: string;
|
|
@@ -57,5 +56,4 @@ export declare function verifyAccessToken(token: string, endpoint: McpEndpoint):
|
|
|
57
56
|
/** The user a bearer token on `/mcp` stands for: a wervt access token, or the app's own. */
|
|
58
57
|
export declare function authenticateMcp(token: string, event: RequestEvent, endpoint: McpEndpoint): Promise<WervtUser | undefined>;
|
|
59
58
|
/** Makes `useWervtUser(event)` return the token's user while a tool runs over MCP. */
|
|
60
|
-
export declare function setMcpUser(event: RequestEvent, user: WervtUser): void;
|
|
61
|
-
//#endregion
|
|
59
|
+
export declare function setMcpUser(event: RequestEvent, user: WervtUser): void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { userFromClaims } from "./mcp-claims.js";
|
|
2
2
|
import { getRequestURL, useServerHooks } from "nuxt/server";
|
|
3
3
|
import { createRemoteJWKSet, customFetch, errors, jwtVerify } from "jose";
|
|
4
|
-
|
|
4
|
+
// src/runtime/server/mcp-auth.ts
|
|
5
5
|
const trimSlash = (url) => url.replace(/\/+$/, "");
|
|
6
6
|
/**
|
|
7
7
|
* Where this app's `/mcp` and wervt's login live, from the runtime's `WERVT_APP_URL` and
|
|
@@ -95,5 +95,4 @@ async function authenticateMcp(token, event, endpoint) {
|
|
|
95
95
|
function setMcpUser(event, user) {
|
|
96
96
|
event.context.wervtMcpUser = user;
|
|
97
97
|
}
|
|
98
|
-
//#endregion
|
|
99
98
|
export { CORS, authenticateMcp, fetchAuth, mcpEndpoint, protectedResourceMetadata, setMcpUser, verifyAccessToken };
|
|
@@ -1,5 +1,3 @@
|
|
|
1
1
|
import { WervtUser } from "../shared/user.js";
|
|
2
|
-
//#region src/runtime/server/mcp-claims.d.ts
|
|
3
2
|
/** The wervt user an access token's claims name, or undefined when they're incomplete. */
|
|
4
|
-
export declare function userFromClaims(claims: Record<string, unknown>): WervtUser | undefined;
|
|
5
|
-
//#endregion
|
|
3
|
+
export declare function userFromClaims(claims: Record<string, unknown>): WervtUser | undefined;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/runtime/server/mcp-claims.ts
|
|
2
2
|
/** The wervt user an access token's claims name, or undefined when they're incomplete. */
|
|
3
3
|
function userFromClaims(claims) {
|
|
4
4
|
const { sub, email, name, picture, accounts } = claims;
|
|
@@ -14,5 +14,4 @@ function userFromClaims(claims) {
|
|
|
14
14
|
function isAccounts(value) {
|
|
15
15
|
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.values(value).every((id) => typeof id === "string");
|
|
16
16
|
}
|
|
17
|
-
//#endregion
|
|
18
17
|
export { userFromClaims };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CORS, mcpEndpoint, protectedResourceMetadata } from "./mcp-auth.js";
|
|
2
2
|
import { defineEventHandler, getRequestURL } from "nuxt/server";
|
|
3
|
-
|
|
3
|
+
// src/runtime/server/mcp-metadata.ts
|
|
4
4
|
var mcp_metadata_default = defineEventHandler((event) => {
|
|
5
5
|
const endpoint = mcpEndpoint(event);
|
|
6
6
|
if (event.req.method === "OPTIONS") return new Response(null, {
|
|
@@ -11,5 +11,4 @@ var mcp_metadata_default = defineEventHandler((event) => {
|
|
|
11
11
|
const mcp = getRequestURL(event).pathname.endsWith("/mcp");
|
|
12
12
|
return Response.json(protectedResourceMetadata(endpoint, mcp ? endpoint.resource : endpoint.app), { headers: CORS });
|
|
13
13
|
});
|
|
14
|
-
//#endregion
|
|
15
14
|
export { mcp_metadata_default as default };
|
|
@@ -6,7 +6,7 @@ import { allTools, toolInputSchema } from "./tool-handlers.js";
|
|
|
6
6
|
import { defineEventHandler } from "nuxt/server";
|
|
7
7
|
import { server } from "#wervt/mcp";
|
|
8
8
|
import { defineMcpHandler } from "h3-mcp";
|
|
9
|
-
|
|
9
|
+
// src/runtime/server/mcp.ts
|
|
10
10
|
const MAX_BODY = 4194304;
|
|
11
11
|
const text = (value) => [{
|
|
12
12
|
type: "text",
|
|
@@ -104,5 +104,4 @@ var mcp_default = defineEventHandler(async (event) => {
|
|
|
104
104
|
});
|
|
105
105
|
return (await handler)(event);
|
|
106
106
|
});
|
|
107
|
-
//#endregion
|
|
108
107
|
export { mcp_default as default };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
interface ErrorContext {
|
|
2
|
+
event?: {
|
|
3
|
+
req?: Request;
|
|
4
|
+
path?: string;
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
interface NitroAppHooks {
|
|
8
|
+
hooks: {
|
|
9
|
+
hook(name: 'error', handler: (error: unknown, context: ErrorContext) => void): unknown;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export default function wervtPreviewErrors(nitroApp: NitroAppHooks): void;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { recordPreviewError, trimStack } from "../shared/preview-errors.js";
|
|
2
|
+
// src/runtime/server/preview-errors-plugin.ts
|
|
3
|
+
function wervtPreviewErrors(nitroApp) {
|
|
4
|
+
const file = process.env.WERVT_PREVIEW_ERRORS;
|
|
5
|
+
if (!file) return;
|
|
6
|
+
nitroApp.hooks.hook("error", (error, { event }) => {
|
|
7
|
+
const status = error ?? {};
|
|
8
|
+
if ((status.status ?? status.statusCode ?? 500) < 500) return;
|
|
9
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
10
|
+
recordPreviewError(file, {
|
|
11
|
+
at: Date.now(),
|
|
12
|
+
source: "server",
|
|
13
|
+
message: `${err.name}: ${err.message}`,
|
|
14
|
+
stack: trimStack(err.stack),
|
|
15
|
+
url: event?.req ? new URL(event.req.url).pathname : event?.path
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
export { wervtPreviewErrors as default };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { InferInsertModel } from "drizzle-orm";
|
|
2
2
|
import { PgTable } from "drizzle-orm/pg-core";
|
|
3
|
-
//#region src/runtime/server/seed.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* Inserts `rows` into `table` only while it is empty, so a seed task can run again without
|
|
6
5
|
* duplicating data or touching what users created. Returns how many rows it inserted.
|
|
@@ -14,5 +13,4 @@ import { PgTable } from "drizzle-orm/pg-core";
|
|
|
14
13
|
* },
|
|
15
14
|
* })
|
|
16
15
|
*/
|
|
17
|
-
export declare function seedTable<T extends PgTable>(table: T, rows: InferInsertModel<T>[]): Promise<number>;
|
|
18
|
-
//#endregion
|
|
16
|
+
export declare function seedTable<T extends PgTable>(table: T, rows: InferInsertModel<T>[]): Promise<number>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useDb } from "./db.js";
|
|
2
2
|
import { sql } from "drizzle-orm";
|
|
3
|
-
|
|
3
|
+
// src/runtime/server/seed.ts
|
|
4
4
|
/**
|
|
5
5
|
* Inserts `rows` into `table` only while it is empty, so a seed task can run again without
|
|
6
6
|
* duplicating data or touching what users created. Returns how many rows it inserted.
|
|
@@ -23,5 +23,4 @@ async function seedTable(table, rows) {
|
|
|
23
23
|
await db.insert(target).values(rows);
|
|
24
24
|
return rows.length;
|
|
25
25
|
}
|
|
26
|
-
//#endregion
|
|
27
26
|
export { seedTable };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { ShapeResponse } from "../shared/shape.js";
|
|
2
2
|
import { RequestEvent } from "nuxt/server";
|
|
3
3
|
import { InferSelectModel, Table } from "drizzle-orm";
|
|
4
|
-
//#region src/runtime/server/shape.d.ts
|
|
5
4
|
export interface ShapeOptions<T extends Table | string = Table | string> {
|
|
6
5
|
/**
|
|
7
6
|
* The Drizzle table (`todos` from `server/db/schema`), or its name without schema. With the
|
|
@@ -22,5 +21,4 @@ export type ShapeRow<T extends Table | string> = T extends Table ? InferSelectMo
|
|
|
22
21
|
* In production the request goes through the wervt runtime gateway, which pins the
|
|
23
22
|
* table to this app's schema. In dev it goes to Electric directly (ELECTRIC_URL).
|
|
24
23
|
*/
|
|
25
|
-
export declare function proxyShape<T extends Table | string>(event: RequestEvent, { table: tableOrName, where, params, columns }: ShapeOptions<T>): Promise<ShapeResponse<ShapeRow<T>>>;
|
|
26
|
-
//#endregion
|
|
24
|
+
export declare function proxyShape<T extends Table | string>(event: RequestEvent, { table: tableOrName, where, params, columns }: ShapeOptions<T>): Promise<ShapeResponse<ShapeRow<T>>>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getTableName, isTable } from "drizzle-orm";
|
|
2
|
-
|
|
2
|
+
// src/runtime/server/shape.ts
|
|
3
3
|
const CLIENT_PARAMS = [
|
|
4
4
|
"offset",
|
|
5
5
|
"handle",
|
|
@@ -53,5 +53,4 @@ function devTable(table) {
|
|
|
53
53
|
function quoteIdentifier(name) {
|
|
54
54
|
return /^[a-z_][a-z0-9_]*$/.test(name) ? name : `"${name.replaceAll("\"", "\"\"")}"`;
|
|
55
55
|
}
|
|
56
|
-
//#endregion
|
|
57
56
|
export { proxyShape };
|