@agent-native/core 0.44.3 → 0.45.0
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/dist/cli/connect.d.ts +2 -1
- package/dist/cli/connect.d.ts.map +1 -1
- package/dist/cli/connect.js +185 -5
- package/dist/cli/connect.js.map +1 -1
- package/dist/cli/index.js +27 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/plan-local.d.ts +43 -0
- package/dist/cli/plan-local.d.ts.map +1 -0
- package/dist/cli/plan-local.js +477 -0
- package/dist/cli/plan-local.js.map +1 -0
- package/dist/cli/pr-visual-recap-workflow.d.ts +1 -1
- package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.js +1 -1
- package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
- package/dist/cli/recap.d.ts +164 -0
- package/dist/cli/recap.d.ts.map +1 -1
- package/dist/cli/recap.js +657 -10
- package/dist/cli/recap.js.map +1 -1
- package/dist/cli/skills.d.ts +6 -2
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +440 -37
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/AnnotatedCodeBlock.js +18 -4
- package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -1
- package/dist/client/blocks/library/ApiEndpointBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/ApiEndpointBlock.js +11 -7
- package/dist/client/blocks/library/ApiEndpointBlock.js.map +1 -1
- package/dist/client/blocks/library/DiffBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/DiffBlock.js +90 -7
- package/dist/client/blocks/library/DiffBlock.js.map +1 -1
- package/dist/client/blocks/library/HighlightedCode.d.ts +2 -1
- package/dist/client/blocks/library/HighlightedCode.d.ts.map +1 -1
- package/dist/client/blocks/library/HighlightedCode.js +2 -2
- package/dist/client/blocks/library/HighlightedCode.js.map +1 -1
- package/dist/client/blocks/library/JsonExplorerBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/JsonExplorerBlock.js +57 -13
- package/dist/client/blocks/library/JsonExplorerBlock.js.map +1 -1
- package/dist/client/blocks/library/annotation-rail.d.ts +15 -5
- package/dist/client/blocks/library/annotation-rail.d.ts.map +1 -1
- package/dist/client/blocks/library/annotation-rail.js +35 -24
- package/dist/client/blocks/library/annotation-rail.js.map +1 -1
- package/dist/client/blocks/library/code-filename-label.d.ts +8 -0
- package/dist/client/blocks/library/code-filename-label.d.ts.map +1 -0
- package/dist/client/blocks/library/code-filename-label.js +15 -0
- package/dist/client/blocks/library/code-filename-label.js.map +1 -0
- package/dist/client/blocks/library/code.d.ts.map +1 -1
- package/dist/client/blocks/library/code.js +28 -6
- package/dist/client/blocks/library/code.js.map +1 -1
- package/dist/client/blocks/library/columns.d.ts.map +1 -1
- package/dist/client/blocks/library/columns.js +11 -1
- package/dist/client/blocks/library/columns.js.map +1 -1
- package/dist/client/blocks/library/diff.config.d.ts +1 -1
- package/dist/client/blocks/library/diff.config.js.map +1 -1
- package/dist/client/blocks/library/narrow-container.d.ts +13 -0
- package/dist/client/blocks/library/narrow-container.d.ts.map +1 -0
- package/dist/client/blocks/library/narrow-container.js +32 -0
- package/dist/client/blocks/library/narrow-container.js.map +1 -0
- package/dist/client/blocks/library/question-form.d.ts.map +1 -1
- package/dist/client/blocks/library/question-form.js +8 -9
- package/dist/client/blocks/library/question-form.js.map +1 -1
- package/dist/client/blocks/library/tabs.d.ts.map +1 -1
- package/dist/client/blocks/library/tabs.js +11 -5
- package/dist/client/blocks/library/tabs.js.map +1 -1
- package/dist/client/blocks/types.d.ts +2 -0
- package/dist/client/blocks/types.d.ts.map +1 -1
- package/dist/client/blocks/types.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +4 -1
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/db-admin/TableEditor.d.ts.map +1 -1
- package/dist/client/db-admin/TableEditor.js +3 -1
- package/dist/client/db-admin/TableEditor.js.map +1 -1
- package/dist/db/client.d.ts +19 -0
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +43 -2
- package/dist/db/client.js.map +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +9 -2
- package/dist/db/migrations.js.map +1 -1
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +8 -0
- package/dist/deploy/build.js.map +1 -1
- package/dist/extensions/html-shell.js +1 -1
- package/dist/extensions/html-shell.js.map +1 -1
- package/dist/jobs/scheduler.d.ts.map +1 -1
- package/dist/jobs/scheduler.js +5 -1
- package/dist/jobs/scheduler.js.map +1 -1
- package/dist/mcp/build-server.d.ts +1 -0
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +7 -3
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/mcp/oauth-route.d.ts.map +1 -1
- package/dist/mcp/oauth-route.js +56 -19
- package/dist/mcp/oauth-route.js.map +1 -1
- package/dist/mcp/oauth-store.d.ts +1 -0
- package/dist/mcp/oauth-store.d.ts.map +1 -1
- package/dist/mcp/oauth-store.js +9 -0
- package/dist/mcp/oauth-store.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +9 -4
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp-client/errors.js +3 -3
- package/dist/mcp-client/errors.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +3 -1
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-teams.d.ts.map +1 -1
- package/dist/server/agent-teams.js +10 -2
- package/dist/server/agent-teams.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +7 -3
- package/dist/server/auth.js.map +1 -1
- package/dist/server/recap-image-route.d.ts.map +1 -1
- package/dist/server/recap-image-route.js +3 -6
- package/dist/server/recap-image-route.js.map +1 -1
- package/dist/server/sentry.d.ts.map +1 -1
- package/dist/server/sentry.js +12 -5
- package/dist/server/sentry.js.map +1 -1
- package/dist/server/social-og-image.d.ts.map +1 -1
- package/dist/server/social-og-image.js +3 -1
- package/dist/server/social-og-image.js.map +1 -1
- package/dist/styles/blocks.css +36 -10
- package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +22 -6
- package/docs/content/plan-plugin.md +18 -1
- package/docs/content/pr-visual-recap.md +37 -10
- package/docs/content/template-plan.md +45 -1
- package/package.json +1 -1
- package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +22 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recap-image-route.d.ts","sourceRoot":"","sources":["../../src/server/recap-image-route.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"recap-image-route.d.ts","sourceRoot":"","sources":["../../src/server/recap-image-route.ts"],"names":[],"mappings":"AAsNA;;;;;GAKG;AACH,wBAAgB,uBAAuB,2FAoBtC"}
|
|
@@ -45,12 +45,9 @@ async function resolveUploadSession(event) {
|
|
|
45
45
|
const session = await getSession(event).catch(() => null);
|
|
46
46
|
if (session?.email)
|
|
47
47
|
return session;
|
|
48
|
-
// Trim once and reuse the trimmed value everywhere. Match verifyAuth's check
|
|
49
|
-
// exactly — a literal, case-sensitive `Bearer ` prefix (not `/i`, not `\s+`) —
|
|
50
|
-
// so this pre-check never accepts a header that verifyAuth would then reject
|
|
51
|
-
// (e.g. lowercase `bearer` or a tab separator).
|
|
52
48
|
const authHeader = getHeader(event, "authorization")?.trim();
|
|
53
|
-
|
|
49
|
+
const bearer = /^Bearer\s+(.+)$/i.exec(authHeader ?? "")?.[1]?.trim();
|
|
50
|
+
if (!authHeader || !bearer)
|
|
54
51
|
return null;
|
|
55
52
|
try {
|
|
56
53
|
const [{ getMcpOAuthResource }, { verifyAuth, resolveOrgIdFromDomain }] = await Promise.all([
|
|
@@ -67,7 +64,7 @@ async function resolveUploadSession(event) {
|
|
|
67
64
|
const orgId = identity.orgId ?? (await resolveOrgIdFromDomain(identity.orgDomain));
|
|
68
65
|
return {
|
|
69
66
|
email: identity.userEmail,
|
|
70
|
-
token:
|
|
67
|
+
token: bearer,
|
|
71
68
|
...(orgId ? { orgId } : {}),
|
|
72
69
|
};
|
|
73
70
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recap-image-route.js","sourceRoot":"","sources":["../../src/server/recap-image-route.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Routes for signed, content-only recap PNG images.\n *\n * POST /_agent-native/recap-image\n * Auth: `Authorization: Bearer <token>` — accepts the SAME tokens the MCP /\n * action surface accepts: a legacy `sessions` bearer (desktop/native) OR a\n * connect-minted MCP OAuth access token (the `agent-native connect` token,\n * audience-bound to this app's `{origin}/_agent-native/mcp` resource). A\n * normal browser session cookie is also accepted. Rejects unauthenticated\n * callers with 401.\n * Body: raw `image/png` bytes, or JSON `{ \"pngBase64\": \"...\" }`. Capped at\n * ~5 MB. Stores the PNG and returns `{ imageUrl: \"<origin>/_agent-native/\n * recap-image/<token>.png\" }`.\n *\n * GET /_agent-native/recap-image/<token>.png\n * ANONYMOUS (no auth) so GitHub's camo image proxy can fetch it into a\n * private-repo PR comment. Returns the stored PNG with a strict\n * `Content-Type: image/png` and a long immutable cache header. 404 on an\n * unknown/malformed token. Only ever serves opaque image bytes — no plan\n * data leaks through this route.\n */\nimport {\n defineEventHandler,\n getHeader,\n getMethod,\n readRawBody,\n setResponseHeader,\n setResponseStatus,\n type H3Event,\n} from \"h3\";\nimport { getSession, type AuthSession } from \"./auth.js\";\nimport { getAppUrl } from \"./google-oauth.js\";\nimport {\n RECAP_IMAGE_CONTENT_TYPE,\n RECAP_IMAGE_MAX_BYTES,\n getRecapImage,\n isValidRecapImageToken,\n saveRecapImage,\n} from \"./recap-image-store.js\";\n\nconst PNG_MAGIC = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);\n\n/** Long immutable cache — the bytes for a given token never change. */\nconst RECAP_IMAGE_CACHE_CONTROL =\n \"public, max-age=31536000, immutable, stale-while-revalidate=604800, stale-if-error=86400\";\n\nfunction isPngBuffer(buf: Buffer): boolean {\n return (\n buf.byteLength >= PNG_MAGIC.byteLength &&\n buf.subarray(0, 8).equals(PNG_MAGIC)\n );\n}\n\n/**\n * Resolve a session for the upload route. Reuses the SAME acceptance the MCP /\n * action surface uses:\n * 1. `getSession(event)` — browser cookie, ACCESS_TOKEN, and legacy bearer\n * (`sessions` table) tokens.\n * 2. A connect-minted MCP OAuth access token, verified through the MCP\n * surface's canonical `verifyAuth` with this app's MCP resource as the\n * expected audience and `allowDevOpen: false`. `getSession` only honors\n * this token on the `/_agent-native/actions/*` surface, so we mirror that\n * verification here for the recap-image upload route.\n */\nasync function resolveUploadSession(\n event: H3Event,\n): Promise<AuthSession | null> {\n const session = await getSession(event).catch(() => null);\n if (session?.email) return session;\n\n // Trim once and reuse the trimmed value everywhere. Match verifyAuth's check\n // exactly — a literal, case-sensitive `Bearer ` prefix (not `/i`, not `\\s+`) —\n // so this pre-check never accepts a header that verifyAuth would then reject\n // (e.g. lowercase `bearer` or a tab separator).\n const authHeader = getHeader(event, \"authorization\")?.trim();\n if (!authHeader || !/^Bearer \\S/.test(authHeader)) return null;\n\n try {\n const [{ getMcpOAuthResource }, { verifyAuth, resolveOrgIdFromDomain }] =\n await Promise.all([\n import(\"../mcp/oauth-route.js\"),\n import(\"../mcp/build-server.js\"),\n ]);\n const result = await verifyAuth(authHeader, undefined, {\n resourceUrl: getMcpOAuthResource(event),\n allowDevOpen: false,\n });\n const identity = result.authed ? result.identity : undefined;\n if (!identity?.userEmail) return null;\n const orgId =\n identity.orgId ?? (await resolveOrgIdFromDomain(identity.orgDomain));\n return {\n email: identity.userEmail,\n token: authHeader.replace(/^Bearer /, \"\").trim(),\n ...(orgId ? { orgId } : {}),\n };\n } catch (error) {\n console.error(\"[recap-image] bearer verification error:\", error);\n return null;\n }\n}\n\n/**\n * Extract PNG bytes from the request. Supports raw `image/png` bytes and JSON\n * `{ pngBase64 }`. Returns `null` on a malformed/oversized/non-PNG payload.\n */\nasync function readPngFromRequest(event: H3Event): Promise<Buffer | null> {\n const rawBody = await readRawBody(event, false).catch(() => undefined);\n if (!rawBody || rawBody.byteLength === 0) return null;\n if (rawBody.byteLength > RECAP_IMAGE_MAX_BYTES) return null;\n\n // h3 v2's `readRawBody(event, false)` resolves a bare `Uint8Array`, not a Node\n // `Buffer`. Normalize once so the downstream Buffer-only operations behave:\n // `isPngBuffer`'s `Buffer#equals` THROWS on a Uint8Array (no such method), and\n // `saveRecapImage`'s `png.toString(\"base64\")` SILENTLY mis-encodes it (a bare\n // Uint8Array ignores the encoding arg and returns comma-joined digits). Either\n // sinks the upload — the thrown TypeError surfaced as a 500, so the recap CLI\n // saw `!res.ok`, returned a null imageUrl, and the PR comment lost its inline\n // thumbnail. Copying into a Buffer is cheap for a ~5 MB-capped screenshot.\n const raw = Buffer.isBuffer(rawBody) ? rawBody : Buffer.from(rawBody);\n\n const contentType = (getHeader(event, \"content-type\") || \"\").toLowerCase();\n\n if (contentType.includes(\"application/json\")) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw.toString(\"utf8\"));\n } catch {\n return null;\n }\n const base64 = (parsed as { pngBase64?: unknown })?.pngBase64;\n if (typeof base64 !== \"string\" || !base64) return null;\n let bytes: Buffer;\n try {\n bytes = Buffer.from(base64, \"base64\");\n } catch {\n return null;\n }\n if (bytes.byteLength === 0 || bytes.byteLength > RECAP_IMAGE_MAX_BYTES) {\n return null;\n }\n return isPngBuffer(bytes) ? bytes : null;\n }\n\n // Default: treat the raw body as PNG bytes (image/png or unspecified).\n return isPngBuffer(raw) ? raw : null;\n}\n\n/** POST /_agent-native/recap-image — authenticated upload. */\nasync function handleUpload(event: H3Event): Promise<unknown> {\n const session = await resolveUploadSession(event);\n if (!session?.email) {\n setResponseStatus(event, 401);\n return { error: \"Authentication required\" };\n }\n\n const png = await readPngFromRequest(event);\n if (!png) {\n setResponseStatus(event, 400);\n return {\n error:\n \"Expected a PNG image (Content-Type: image/png raw bytes, or JSON { pngBase64 }), at most 5 MB.\",\n };\n }\n\n try {\n const { token } = await saveRecapImage(png, { ownerEmail: session.email });\n const imageUrl = getAppUrl(\n event,\n `/_agent-native/recap-image/${token}.png`,\n );\n setResponseStatus(event, 201);\n return { imageUrl };\n } catch (error) {\n console.error(\"[recap-image] failed to store image:\", error);\n setResponseStatus(event, 500);\n return { error: \"Failed to store recap image\" };\n }\n}\n\n/** GET/HEAD /_agent-native/recap-image/<token>.png — anonymous, content-only. */\nasync function handleServe(event: H3Event, segment: string): Promise<unknown> {\n // Require the strict `<hex>.png` shape — no directory traversal, no\n // alternate extensions, no extra path segments.\n const match = /^([0-9a-f]+)\\.png$/i.exec(segment);\n const token = match?.[1]?.toLowerCase() ?? \"\";\n if (!isValidRecapImageToken(token)) {\n setResponseStatus(event, 404);\n return { error: \"Not found\" };\n }\n\n const stored = await getRecapImage(token).catch(() => null);\n if (!stored) {\n setResponseStatus(event, 404);\n return { error: \"Not found\" };\n }\n\n // Strict image/png on read regardless of what was stored, plus a long\n // immutable cache and a cross-origin policy so the camo proxy can fetch it.\n const headers: Record<string, string> = {\n \"Content-Type\": RECAP_IMAGE_CONTENT_TYPE,\n \"Cache-Control\": RECAP_IMAGE_CACHE_CONTROL,\n \"CDN-Cache-Control\": RECAP_IMAGE_CACHE_CONTROL,\n \"Cross-Origin-Resource-Policy\": \"cross-origin\",\n \"Content-Length\": String(stored.bytes.byteLength),\n };\n for (const [name, value] of Object.entries(headers)) {\n setResponseHeader(event, name, value);\n }\n\n if (getMethod(event) === \"HEAD\") return \"\";\n\n const body = new ArrayBuffer(stored.bytes.byteLength);\n new Uint8Array(body).set(stored.bytes);\n return new Response(body, { headers });\n}\n\n/**\n * Combined handler for the recap-image routes. Mount as a PREFIX handler at\n * `/_agent-native/recap-image`; the framework strips the mount prefix, so:\n * - `event.url.pathname === \"/\"` → POST upload (authenticated)\n * - `event.url.pathname === \"/<token>.png\"` → GET/HEAD serve (anonymous)\n */\nexport function createRecapImageHandler() {\n return defineEventHandler(async (event: H3Event) => {\n const segment =\n (event.url?.pathname || \"\").replace(/^\\/+/, \"\").split(\"/\")[0] || \"\";\n const method = getMethod(event);\n\n if (!segment) {\n if (method === \"POST\") return handleUpload(event);\n setResponseStatus(event, 405);\n setResponseHeader(event, \"Allow\", \"POST\");\n return { error: \"Method not allowed\" };\n }\n\n if (method === \"GET\" || method === \"HEAD\") {\n return handleServe(event, segment);\n }\n setResponseStatus(event, 405);\n setResponseHeader(event, \"Allow\", \"GET, HEAD\");\n return { error: \"Method not allowed\" };\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"recap-image-route.js","sourceRoot":"","sources":["../../src/server/recap-image-route.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Routes for signed, content-only recap PNG images.\n *\n * POST /_agent-native/recap-image\n * Auth: `Authorization: Bearer <token>` — accepts the SAME tokens the MCP /\n * action surface accepts: a legacy `sessions` bearer (desktop/native) OR a\n * connect-minted MCP OAuth access token (the `agent-native connect` token,\n * audience-bound to this app's `{origin}/_agent-native/mcp` resource). A\n * normal browser session cookie is also accepted. Rejects unauthenticated\n * callers with 401.\n * Body: raw `image/png` bytes, or JSON `{ \"pngBase64\": \"...\" }`. Capped at\n * ~5 MB. Stores the PNG and returns `{ imageUrl: \"<origin>/_agent-native/\n * recap-image/<token>.png\" }`.\n *\n * GET /_agent-native/recap-image/<token>.png\n * ANONYMOUS (no auth) so GitHub's camo image proxy can fetch it into a\n * private-repo PR comment. Returns the stored PNG with a strict\n * `Content-Type: image/png` and a long immutable cache header. 404 on an\n * unknown/malformed token. Only ever serves opaque image bytes — no plan\n * data leaks through this route.\n */\nimport {\n defineEventHandler,\n getHeader,\n getMethod,\n readRawBody,\n setResponseHeader,\n setResponseStatus,\n type H3Event,\n} from \"h3\";\nimport { getSession, type AuthSession } from \"./auth.js\";\nimport { getAppUrl } from \"./google-oauth.js\";\nimport {\n RECAP_IMAGE_CONTENT_TYPE,\n RECAP_IMAGE_MAX_BYTES,\n getRecapImage,\n isValidRecapImageToken,\n saveRecapImage,\n} from \"./recap-image-store.js\";\n\nconst PNG_MAGIC = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);\n\n/** Long immutable cache — the bytes for a given token never change. */\nconst RECAP_IMAGE_CACHE_CONTROL =\n \"public, max-age=31536000, immutable, stale-while-revalidate=604800, stale-if-error=86400\";\n\nfunction isPngBuffer(buf: Buffer): boolean {\n return (\n buf.byteLength >= PNG_MAGIC.byteLength &&\n buf.subarray(0, 8).equals(PNG_MAGIC)\n );\n}\n\n/**\n * Resolve a session for the upload route. Reuses the SAME acceptance the MCP /\n * action surface uses:\n * 1. `getSession(event)` — browser cookie, ACCESS_TOKEN, and legacy bearer\n * (`sessions` table) tokens.\n * 2. A connect-minted MCP OAuth access token, verified through the MCP\n * surface's canonical `verifyAuth` with this app's MCP resource as the\n * expected audience and `allowDevOpen: false`. `getSession` only honors\n * this token on the `/_agent-native/actions/*` surface, so we mirror that\n * verification here for the recap-image upload route.\n */\nasync function resolveUploadSession(\n event: H3Event,\n): Promise<AuthSession | null> {\n const session = await getSession(event).catch(() => null);\n if (session?.email) return session;\n\n const authHeader = getHeader(event, \"authorization\")?.trim();\n const bearer = /^Bearer\\s+(.+)$/i.exec(authHeader ?? \"\")?.[1]?.trim();\n if (!authHeader || !bearer) return null;\n\n try {\n const [{ getMcpOAuthResource }, { verifyAuth, resolveOrgIdFromDomain }] =\n await Promise.all([\n import(\"../mcp/oauth-route.js\"),\n import(\"../mcp/build-server.js\"),\n ]);\n const result = await verifyAuth(authHeader, undefined, {\n resourceUrl: getMcpOAuthResource(event),\n allowDevOpen: false,\n });\n const identity = result.authed ? result.identity : undefined;\n if (!identity?.userEmail) return null;\n const orgId =\n identity.orgId ?? (await resolveOrgIdFromDomain(identity.orgDomain));\n return {\n email: identity.userEmail,\n token: bearer,\n ...(orgId ? { orgId } : {}),\n };\n } catch (error) {\n console.error(\"[recap-image] bearer verification error:\", error);\n return null;\n }\n}\n\n/**\n * Extract PNG bytes from the request. Supports raw `image/png` bytes and JSON\n * `{ pngBase64 }`. Returns `null` on a malformed/oversized/non-PNG payload.\n */\nasync function readPngFromRequest(event: H3Event): Promise<Buffer | null> {\n const rawBody = await readRawBody(event, false).catch(() => undefined);\n if (!rawBody || rawBody.byteLength === 0) return null;\n if (rawBody.byteLength > RECAP_IMAGE_MAX_BYTES) return null;\n\n // h3 v2's `readRawBody(event, false)` resolves a bare `Uint8Array`, not a Node\n // `Buffer`. Normalize once so the downstream Buffer-only operations behave:\n // `isPngBuffer`'s `Buffer#equals` THROWS on a Uint8Array (no such method), and\n // `saveRecapImage`'s `png.toString(\"base64\")` SILENTLY mis-encodes it (a bare\n // Uint8Array ignores the encoding arg and returns comma-joined digits). Either\n // sinks the upload — the thrown TypeError surfaced as a 500, so the recap CLI\n // saw `!res.ok`, returned a null imageUrl, and the PR comment lost its inline\n // thumbnail. Copying into a Buffer is cheap for a ~5 MB-capped screenshot.\n const raw = Buffer.isBuffer(rawBody) ? rawBody : Buffer.from(rawBody);\n\n const contentType = (getHeader(event, \"content-type\") || \"\").toLowerCase();\n\n if (contentType.includes(\"application/json\")) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw.toString(\"utf8\"));\n } catch {\n return null;\n }\n const base64 = (parsed as { pngBase64?: unknown })?.pngBase64;\n if (typeof base64 !== \"string\" || !base64) return null;\n let bytes: Buffer;\n try {\n bytes = Buffer.from(base64, \"base64\");\n } catch {\n return null;\n }\n if (bytes.byteLength === 0 || bytes.byteLength > RECAP_IMAGE_MAX_BYTES) {\n return null;\n }\n return isPngBuffer(bytes) ? bytes : null;\n }\n\n // Default: treat the raw body as PNG bytes (image/png or unspecified).\n return isPngBuffer(raw) ? raw : null;\n}\n\n/** POST /_agent-native/recap-image — authenticated upload. */\nasync function handleUpload(event: H3Event): Promise<unknown> {\n const session = await resolveUploadSession(event);\n if (!session?.email) {\n setResponseStatus(event, 401);\n return { error: \"Authentication required\" };\n }\n\n const png = await readPngFromRequest(event);\n if (!png) {\n setResponseStatus(event, 400);\n return {\n error:\n \"Expected a PNG image (Content-Type: image/png raw bytes, or JSON { pngBase64 }), at most 5 MB.\",\n };\n }\n\n try {\n const { token } = await saveRecapImage(png, { ownerEmail: session.email });\n const imageUrl = getAppUrl(\n event,\n `/_agent-native/recap-image/${token}.png`,\n );\n setResponseStatus(event, 201);\n return { imageUrl };\n } catch (error) {\n console.error(\"[recap-image] failed to store image:\", error);\n setResponseStatus(event, 500);\n return { error: \"Failed to store recap image\" };\n }\n}\n\n/** GET/HEAD /_agent-native/recap-image/<token>.png — anonymous, content-only. */\nasync function handleServe(event: H3Event, segment: string): Promise<unknown> {\n // Require the strict `<hex>.png` shape — no directory traversal, no\n // alternate extensions, no extra path segments.\n const match = /^([0-9a-f]+)\\.png$/i.exec(segment);\n const token = match?.[1]?.toLowerCase() ?? \"\";\n if (!isValidRecapImageToken(token)) {\n setResponseStatus(event, 404);\n return { error: \"Not found\" };\n }\n\n const stored = await getRecapImage(token).catch(() => null);\n if (!stored) {\n setResponseStatus(event, 404);\n return { error: \"Not found\" };\n }\n\n // Strict image/png on read regardless of what was stored, plus a long\n // immutable cache and a cross-origin policy so the camo proxy can fetch it.\n const headers: Record<string, string> = {\n \"Content-Type\": RECAP_IMAGE_CONTENT_TYPE,\n \"Cache-Control\": RECAP_IMAGE_CACHE_CONTROL,\n \"CDN-Cache-Control\": RECAP_IMAGE_CACHE_CONTROL,\n \"Cross-Origin-Resource-Policy\": \"cross-origin\",\n \"Content-Length\": String(stored.bytes.byteLength),\n };\n for (const [name, value] of Object.entries(headers)) {\n setResponseHeader(event, name, value);\n }\n\n if (getMethod(event) === \"HEAD\") return \"\";\n\n const body = new ArrayBuffer(stored.bytes.byteLength);\n new Uint8Array(body).set(stored.bytes);\n return new Response(body, { headers });\n}\n\n/**\n * Combined handler for the recap-image routes. Mount as a PREFIX handler at\n * `/_agent-native/recap-image`; the framework strips the mount prefix, so:\n * - `event.url.pathname === \"/\"` → POST upload (authenticated)\n * - `event.url.pathname === \"/<token>.png\"` → GET/HEAD serve (anonymous)\n */\nexport function createRecapImageHandler() {\n return defineEventHandler(async (event: H3Event) => {\n const segment =\n (event.url?.pathname || \"\").replace(/^\\/+/, \"\").split(\"/\")[0] || \"\";\n const method = getMethod(event);\n\n if (!segment) {\n if (method === \"POST\") return handleUpload(event);\n setResponseStatus(event, 405);\n setResponseHeader(event, \"Allow\", \"POST\");\n return { error: \"Method not allowed\" };\n }\n\n if (method === \"GET\" || method === \"HEAD\") {\n return handleServe(event, segment);\n }\n setResponseStatus(event, 405);\n setResponseHeader(event, \"Allow\", \"GET, HEAD\");\n return { error: \"Method not allowed\" };\n });\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sentry.d.ts","sourceRoot":"","sources":["../../src/server/sentry.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAwC7C;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,
|
|
1
|
+
{"version":3,"file":"sentry.d.ts","sourceRoot":"","sources":["../../src/server/sentry.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAwC7C;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAwM1C;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAE/C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,CAsBzE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,IAAI,CAgBP;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,OAAO,EACd,OAAO,EAAE;IAAE,KAAK,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAChE,MAAM,GAAG,SAAS,CAcpB;AAED,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAC1C;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACpD;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,iBAAsB,GAC9B,MAAM,GAAG,SAAS,CA2BpB"}
|
package/dist/server/sentry.js
CHANGED
|
@@ -128,13 +128,20 @@ export function initServerSentry() {
|
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
// Drop SDK-only ErrorEvent promise rejections. These arrive as Node
|
|
131
|
-
// unhandled rejections with no application frames
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
//
|
|
131
|
+
// unhandled rejections with no application frames — typically the Neon
|
|
132
|
+
// serverless driver's WebSocket dying across a Lambda freeze/thaw and
|
|
133
|
+
// rejecting a floating promise with the raw ErrorEvent (the per-client
|
|
134
|
+
// logger in db/client.ts already records these with context). Keep any
|
|
135
|
+
// event with real app frames so thrown ErrorEvents stay visible.
|
|
136
|
+
//
|
|
137
|
+
// "Application frame" must exclude the Sentry SDK's own bundled chunks:
|
|
138
|
+
// serverless bundles place them under the app root (e.g.
|
|
139
|
+
// `/var/task/_libs/@sentry/node+….mjs`), outside node_modules, so the
|
|
140
|
+
// SDK marks them in_app and the unhandled-rejection instrumentation
|
|
141
|
+
// stack alone would defeat this filter.
|
|
135
142
|
if (exceptionValue === "[object ErrorEvent]" && isUnhandledRejection) {
|
|
136
143
|
const frames = event.exception?.values?.[0]?.stacktrace?.frames ?? [];
|
|
137
|
-
const hasApplicationFrame = frames.some((frame) => frame?.in_app);
|
|
144
|
+
const hasApplicationFrame = frames.some((frame) => frame?.in_app && !String(frame?.filename ?? "").includes("sentry"));
|
|
138
145
|
const hasSentryFrame = frames.some((frame) => String(frame?.filename ?? "").includes("sentry"));
|
|
139
146
|
if (!hasApplicationFrame && hasSentryFrame) {
|
|
140
147
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sentry.js","sourceRoot":"","sources":["../../src/server/sentry.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Server-side Sentry initialization for Nitro.\n *\n * Errors thrown inside Nitro routes (the framework's own /_agent-native/*\n * handlers, the template's API routes, action handlers, agent-chat streams)\n * never reach the CLI's Sentry init — that only covers the developer's\n * machine. Without server-side Sentry the only signal a 500 ever produces\n * is a server-side console.error that lives and dies with the request.\n *\n * This module is the third Sentry init point in the framework:\n * - cli/index.ts → @sentry/node, hardcoded DSN, \"agent-native-cli\"\n * - client/analytics.ts → @sentry/browser, VITE_SENTRY_CLIENT_DSN / runtime config\n * - server/sentry.ts → @sentry/node, SENTRY_SERVER_DSN / SENTRY_DSN\n *\n * The browser and server can share a Sentry project/DSN. Separate projects\n * are an operational choice for noise, ownership, or quotas; not a runtime\n * requirement.\n */\nimport * as Sentry from \"@sentry/node\";\nimport path from \"node:path\";\nimport fs from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport type { AuthSession } from \"./auth.js\";\nimport {\n resolveSentryEnvironment,\n resolveServerSentryDsn,\n} from \"./sentry-config.js\";\n\nlet _initStarted = false;\nlet _initSucceeded = false;\n\n/**\n * Resolve the agent-native version baked into core's package.json so Sentry\n * \"release\" reflects the running framework version. Mirrors how the CLI\n * computes `_version` — same dist layout, same fallback string. Guarded so\n * a missing/unreadable package.json never crashes server boot.\n */\nfunction resolveServerRelease(): string {\n const explicit = process.env.AGENT_NATIVE_RELEASE;\n if (explicit) return explicit;\n try {\n const here = path.dirname(fileURLToPath(import.meta.url));\n // dist/server/sentry.js → ../../package.json\n const pkgPath = path.resolve(here, \"../../package.json\");\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf-8\")) as {\n version?: string;\n };\n if (pkg?.version) return `agent-native-server@${pkg.version}`;\n } catch {\n // ignore — fall through to \"unknown\"\n }\n return \"agent-native-server@unknown\";\n}\n\nfunction parseTracesSampleRate(): number {\n const raw = process.env.SENTRY_SERVER_TRACES_SAMPLE_RATE;\n if (!raw) return 0;\n const n = Number(raw);\n if (!Number.isFinite(n) || n < 0 || n > 1) return 0;\n return n;\n}\n\n/**\n * Initialize server-side Sentry. Idempotent — safe to call from multiple\n * plugin entrypoints. Returns `true` if initialization actually happened\n * (DSN was set), `false` if Sentry is disabled (no DSN).\n *\n * No DSN is hardcoded: unlike the CLI (a published binary that always wants\n * to phone home crashes), the server runs in customer environments. Operators\n * set `SENTRY_SERVER_DSN` or the common `SENTRY_DSN` when they want their own\n * Sentry project to receive these events; without one the module no-ops.\n */\nexport function initServerSentry(): boolean {\n if (_initStarted) return _initSucceeded;\n _initStarted = true;\n\n const dsn = resolveServerSentryDsn();\n if (!dsn) {\n if (process.env.DEBUG) {\n console.log(\n \"[agent-native] SENTRY_SERVER_DSN/SENTRY_DSN not set — server Sentry disabled.\",\n );\n }\n return false;\n }\n\n Sentry.init({\n dsn,\n environment: resolveSentryEnvironment(),\n release: resolveServerRelease(),\n tracesSampleRate: parseTracesSampleRate(),\n // sendDefaultPii MUST stay false — the framework runs inside customer\n // environments and we never want to silently ship request headers,\n // cookies, or process.env contents to Sentry without explicit consent.\n sendDefaultPii: false,\n beforeSend(event) {\n // Drop expected user-input rejections so they don't pollute Sentry\n // with non-bug noise. Mirrors the CLI's drop list — the framework\n // and CLI both throw `ValidationError` for the same class of input\n // failures, and exception type comes through as the class name.\n const exceptionType = event.exception?.values?.[0]?.type;\n if (\n exceptionType === \"ValidationError\" ||\n event.tags?.handled === \"validation\"\n ) {\n return null;\n }\n // Drop access-control rejections (caller lacks permission, signed\n // out, etc.). These are 4xx user-facing errors that propagated to\n // Nitro's error hook because a route forgot to catch them — fixing\n // the route is the right answer, but in the meantime they bury\n // real bugs and don't represent server failures. Auth-routes use\n // `captureAuthError` directly with `level: warning` so this filter\n // only sees the generic-handler escape path.\n if (\n exceptionType === \"ForbiddenError\" ||\n exceptionType === \"UnauthorizedError\"\n ) {\n return null;\n }\n // Drop \"socket hang up\" unhandled promise rejections that fire from\n // Lambda freeze cycles. AWS Lambda recycles long-lived sockets (e.g.\n // MCP Streamable HTTP long-polls, keep-alive HTTP agents) ~60s after\n // a function returns 200; the next thaw delivers a socket-end event\n // whose Promise has nobody left to await it. The function itself\n // already returned correctly, so there's no user impact — just\n // ~10k events/day of noise. The narrow shape (unhandled rejection\n // from `Socket.socketOnEnd` in `node:_http_client`) keeps real\n // application-thrown socket errors from being silenced.\n const exceptionValue = event.exception?.values?.[0]?.value ?? \"\";\n const exceptionMechanism = event.exception?.values?.[0]?.mechanism?.type;\n const isUnhandledRejection =\n typeof exceptionMechanism === \"string\" &&\n exceptionMechanism.endsWith(\"onunhandledrejection\");\n if (exceptionValue === \"socket hang up\" && isUnhandledRejection) {\n const frames = event.exception?.values?.[0]?.stacktrace?.frames ?? [];\n const fromHttpClient = frames.some(\n (f) =>\n f?.function === \"Socket.socketOnEnd\" ||\n f?.filename === \"node:_http_client\",\n );\n if (fromHttpClient) {\n return null;\n }\n }\n // Drop SDK-only ErrorEvent promise rejections. These arrive as Node\n // unhandled rejections with no application frames, usually from a\n // browser ErrorEvent object crossing the shared browser/server bundle\n // boundary. Keep any event with app frames so real thrown ErrorEvents\n // are still visible.\n if (exceptionValue === \"[object ErrorEvent]\" && isUnhandledRejection) {\n const frames = event.exception?.values?.[0]?.stacktrace?.frames ?? [];\n const hasApplicationFrame = frames.some((frame) => frame?.in_app);\n const hasSentryFrame = frames.some((frame) =>\n String(frame?.filename ?? \"\").includes(\"sentry\"),\n );\n if (!hasApplicationFrame && hasSentryFrame) {\n return null;\n }\n }\n const metadata = (\n event as {\n metadata?: {\n filename?: unknown;\n value?: unknown;\n };\n }\n ).metadata;\n if (\n metadata?.value === \"[object ErrorEvent]\" &&\n !event.exception?.values?.length &&\n String(metadata.filename ?? \"\").includes(\"sentry\")\n ) {\n return null;\n }\n // h3's `createError({ statusCode: 4xx, ... })` produces an\n // `HTTPError` (h3 v2) / `H3Error` (h3 v1). 4xx HTTPErrors are\n // handler-controlled \"expected failure\" responses (404 not found,\n // 400 bad input) that route through Nitro's error hook just\n // because they bubble out of `defineEventHandler`. They aren't\n // bugs — they're the documented way to return a 4xx in h3.\n // Capture only when the statusCode looks like 5xx (real failure)\n // or is missing (generic Error masquerading as HTTPError).\n if (exceptionType === \"HTTPError\" || exceptionType === \"H3Error\") {\n const statusCode = (event.tags?.statusCode ??\n (\n event.contexts as\n | Record<string, Record<string, unknown>>\n | undefined\n )?.h3?.statusCode) as number | string | undefined;\n const code =\n typeof statusCode === \"string\"\n ? parseInt(statusCode, 10)\n : statusCode;\n if (typeof code === \"number\" && code >= 400 && code < 500) {\n return null;\n }\n // No statusCode in the event payload — fall back to matching the\n // common 4xx messages so handler-thrown 404/400/403/401 don't\n // pollute Sentry. This is a heuristic, but the alternatives\n // (every 4xx becomes a \"real\" issue, or we patch every route to\n // catch+return) are worse.\n const value = event.exception?.values?.[0]?.value ?? \"\";\n if (\n /^Cannot find any route matching/i.test(value) ||\n / not found$/i.test(value) ||\n /Unauthenticated$/i.test(value) ||\n /^Unauthorized$/i.test(value) ||\n /^No access to /i.test(value)\n ) {\n return null;\n }\n }\n\n // Defense in depth: scrub PII even if some integration auto-attached\n // request metadata despite sendDefaultPii: false.\n if (event.request) {\n if (event.request.headers) {\n const headers = event.request.headers as Record<string, string>;\n for (const k of Object.keys(headers)) {\n const lk = k.toLowerCase();\n if (\n lk === \"cookie\" ||\n lk === \"authorization\" ||\n lk === \"set-cookie\" ||\n lk === \"proxy-authorization\"\n ) {\n delete headers[k];\n }\n }\n }\n // Cookies live in their own field too.\n delete (event.request as Record<string, unknown>).cookies;\n }\n\n // Keep user info that was explicitly set via Sentry.setUser\n // (id/email/username) so we can attribute crashes back to a real\n // operator. Always strip ip_address — auto-collected, no consent.\n if (event.user) {\n const user = event.user as Record<string, unknown>;\n delete user.ip_address;\n const hasIdentity =\n typeof user.id === \"string\" ||\n typeof user.email === \"string\" ||\n typeof user.username === \"string\";\n if (!hasIdentity) {\n delete event.user;\n }\n }\n\n // Sentry's contexts can carry process.env snapshots — strip env-shaped\n // contexts so we don't leak deployment secrets.\n if (event.contexts && typeof event.contexts === \"object\") {\n delete (event.contexts as Record<string, unknown>).runtime_env;\n }\n\n return event;\n },\n });\n\n _initSucceeded = true;\n return true;\n}\n\n/**\n * `true` once `initServerSentry()` has succeeded with a DSN. Plugins that\n * want to skip work when Sentry is disabled can check this before calling\n * the helpers below.\n */\nexport function isServerSentryEnabled(): boolean {\n return _initSucceeded;\n}\n\n/**\n * Attach the current request's user to Sentry's isolation scope so any\n * `captureException` triggered later in the request carries the right\n * `user.id` / `user.email` / `user.username` and `orgId` tag.\n *\n * Sentry node 10 uses Node's AsyncLocalStorage to give each async context\n * its own isolation scope, so setting on `getIsolationScope()` here only\n * affects events emitted while this request's async context is active.\n *\n * No-ops gracefully when Sentry isn't initialized or no session exists —\n * never throws into the request path.\n */\nexport function setSentryUserForRequest(session: AuthSession | null): void {\n if (!_initSucceeded) return;\n try {\n const scope = Sentry.getIsolationScope();\n if (!session) {\n scope.setUser(null);\n scope.setTag(\"orgId\", null);\n return;\n }\n scope.setUser({\n id: session.userId ?? session.email,\n email: session.email,\n username: session.name,\n });\n scope.setTag(\"orgId\", session.orgId ?? null);\n if (session.orgRole) {\n scope.setTag(\"orgRole\", session.orgRole);\n }\n } catch {\n // Sentry scope APIs should never throw, but if they do we'd rather\n // continue serving the request than crash on observability.\n }\n}\n\n/**\n * Pin a user/org onto the current isolation scope from a lighter\n * `RequestContext`-shaped payload. Used by the request-context observer so\n * action handlers, agent-chat runs, and integration webhook processors —\n * all of which already wrap their work in `runWithRequestContext({ userEmail,\n * orgId, ... })` — automatically tag Sentry events with the right user even\n * when the Nitro `request` hook didn't see a cookie (e.g. webhook delivery,\n * A2A calls, internal background runs).\n *\n * Skips overwriting a richer user identity already set by\n * `setSentryUserForRequest` — the cookie-resolved session has\n * userId/username on top of email, which we shouldn't clobber.\n */\nexport function setSentryRequestContext(ctx: {\n userEmail?: string;\n orgId?: string;\n}): void {\n if (!_initSucceeded) return;\n try {\n const scope = Sentry.getIsolationScope();\n if (ctx.userEmail) {\n const existing = scope.getScopeData().user;\n if (!existing?.id && !existing?.email) {\n scope.setUser({ id: ctx.userEmail, email: ctx.userEmail });\n }\n }\n if (ctx.orgId) {\n scope.setTag(\"orgId\", ctx.orgId);\n }\n } catch {\n // never throw\n }\n}\n\n/**\n * Capture an error from one of the auth attempt routes (login / signup)\n * with the email pinned to the event so support can filter by user. Sets\n * Sentry level to `warning` (not `error`) — bad-password attempts aren't\n * actionable, but a sustained spike of warnings on a route IS the signal\n * we care about.\n *\n * Caller should still return their normal HTTP response (401/409/etc.);\n * this just records the error for observability.\n */\nexport function captureAuthError(\n error: unknown,\n context: { route: \"login\" | \"signup\" | \"logout\"; email?: string },\n): string | undefined {\n if (!_initSucceeded) return undefined;\n try {\n return Sentry.withScope((scope) => {\n scope.setLevel(\"warning\");\n scope.setTag(\"auth\", context.route);\n if (context.email) {\n scope.setUser({ id: context.email, email: context.email });\n }\n return Sentry.captureException(error);\n });\n } catch {\n return undefined;\n }\n}\n\nexport interface RouteErrorContext {\n /** The full request path (e.g. `/_agent-native/agent-chat`). */\n route?: string;\n /** HTTP method (e.g. `GET`, `POST`). */\n method?: string;\n /** Caller's `User-Agent` header. */\n userAgent?: string;\n /** Free-form extra tags to add to the event (low-cardinality). */\n tags?: Record<string, string | undefined>;\n /**\n * High-cardinality / structured payload — not searchable but visible in\n * the Sentry event detail (recording IDs, byte counts, compression\n * metadata, response body tails, etc.).\n */\n extra?: Record<string, unknown>;\n /**\n * Grouped contexts shown as separate cards in the Sentry event UI.\n */\n contexts?: Record<string, Record<string, unknown>>;\n}\n\n/**\n * Capture an exception that surfaced in a Nitro route handler with the\n * request's route/method/userAgent attached as searchable Sentry tags.\n *\n * Non-throwing: if Sentry isn't initialized or the underlying capture\n * fails, this is a no-op. Returns the Sentry event ID when capture\n * succeeded, otherwise `undefined`.\n */\nexport function captureRouteError(\n error: unknown,\n context: RouteErrorContext = {},\n): string | undefined {\n if (!_initSucceeded) return undefined;\n try {\n return Sentry.withScope((scope) => {\n if (context.route) scope.setTag(\"route\", context.route);\n if (context.method) scope.setTag(\"method\", context.method);\n if (context.userAgent) scope.setTag(\"userAgent\", context.userAgent);\n if (context.tags) {\n for (const [k, v] of Object.entries(context.tags)) {\n if (typeof v === \"string\") scope.setTag(k, v);\n }\n }\n if (context.extra) {\n for (const [k, v] of Object.entries(context.extra)) {\n if (v !== undefined) scope.setExtra(k, v);\n }\n }\n if (context.contexts) {\n for (const [k, v] of Object.entries(context.contexts)) {\n scope.setContext(k, v);\n }\n }\n return Sentry.captureException(error);\n });\n } catch {\n return undefined;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sentry.js","sourceRoot":"","sources":["../../src/server/sentry.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Server-side Sentry initialization for Nitro.\n *\n * Errors thrown inside Nitro routes (the framework's own /_agent-native/*\n * handlers, the template's API routes, action handlers, agent-chat streams)\n * never reach the CLI's Sentry init — that only covers the developer's\n * machine. Without server-side Sentry the only signal a 500 ever produces\n * is a server-side console.error that lives and dies with the request.\n *\n * This module is the third Sentry init point in the framework:\n * - cli/index.ts → @sentry/node, hardcoded DSN, \"agent-native-cli\"\n * - client/analytics.ts → @sentry/browser, VITE_SENTRY_CLIENT_DSN / runtime config\n * - server/sentry.ts → @sentry/node, SENTRY_SERVER_DSN / SENTRY_DSN\n *\n * The browser and server can share a Sentry project/DSN. Separate projects\n * are an operational choice for noise, ownership, or quotas; not a runtime\n * requirement.\n */\nimport * as Sentry from \"@sentry/node\";\nimport path from \"node:path\";\nimport fs from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport type { AuthSession } from \"./auth.js\";\nimport {\n resolveSentryEnvironment,\n resolveServerSentryDsn,\n} from \"./sentry-config.js\";\n\nlet _initStarted = false;\nlet _initSucceeded = false;\n\n/**\n * Resolve the agent-native version baked into core's package.json so Sentry\n * \"release\" reflects the running framework version. Mirrors how the CLI\n * computes `_version` — same dist layout, same fallback string. Guarded so\n * a missing/unreadable package.json never crashes server boot.\n */\nfunction resolveServerRelease(): string {\n const explicit = process.env.AGENT_NATIVE_RELEASE;\n if (explicit) return explicit;\n try {\n const here = path.dirname(fileURLToPath(import.meta.url));\n // dist/server/sentry.js → ../../package.json\n const pkgPath = path.resolve(here, \"../../package.json\");\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf-8\")) as {\n version?: string;\n };\n if (pkg?.version) return `agent-native-server@${pkg.version}`;\n } catch {\n // ignore — fall through to \"unknown\"\n }\n return \"agent-native-server@unknown\";\n}\n\nfunction parseTracesSampleRate(): number {\n const raw = process.env.SENTRY_SERVER_TRACES_SAMPLE_RATE;\n if (!raw) return 0;\n const n = Number(raw);\n if (!Number.isFinite(n) || n < 0 || n > 1) return 0;\n return n;\n}\n\n/**\n * Initialize server-side Sentry. Idempotent — safe to call from multiple\n * plugin entrypoints. Returns `true` if initialization actually happened\n * (DSN was set), `false` if Sentry is disabled (no DSN).\n *\n * No DSN is hardcoded: unlike the CLI (a published binary that always wants\n * to phone home crashes), the server runs in customer environments. Operators\n * set `SENTRY_SERVER_DSN` or the common `SENTRY_DSN` when they want their own\n * Sentry project to receive these events; without one the module no-ops.\n */\nexport function initServerSentry(): boolean {\n if (_initStarted) return _initSucceeded;\n _initStarted = true;\n\n const dsn = resolveServerSentryDsn();\n if (!dsn) {\n if (process.env.DEBUG) {\n console.log(\n \"[agent-native] SENTRY_SERVER_DSN/SENTRY_DSN not set — server Sentry disabled.\",\n );\n }\n return false;\n }\n\n Sentry.init({\n dsn,\n environment: resolveSentryEnvironment(),\n release: resolveServerRelease(),\n tracesSampleRate: parseTracesSampleRate(),\n // sendDefaultPii MUST stay false — the framework runs inside customer\n // environments and we never want to silently ship request headers,\n // cookies, or process.env contents to Sentry without explicit consent.\n sendDefaultPii: false,\n beforeSend(event) {\n // Drop expected user-input rejections so they don't pollute Sentry\n // with non-bug noise. Mirrors the CLI's drop list — the framework\n // and CLI both throw `ValidationError` for the same class of input\n // failures, and exception type comes through as the class name.\n const exceptionType = event.exception?.values?.[0]?.type;\n if (\n exceptionType === \"ValidationError\" ||\n event.tags?.handled === \"validation\"\n ) {\n return null;\n }\n // Drop access-control rejections (caller lacks permission, signed\n // out, etc.). These are 4xx user-facing errors that propagated to\n // Nitro's error hook because a route forgot to catch them — fixing\n // the route is the right answer, but in the meantime they bury\n // real bugs and don't represent server failures. Auth-routes use\n // `captureAuthError` directly with `level: warning` so this filter\n // only sees the generic-handler escape path.\n if (\n exceptionType === \"ForbiddenError\" ||\n exceptionType === \"UnauthorizedError\"\n ) {\n return null;\n }\n // Drop \"socket hang up\" unhandled promise rejections that fire from\n // Lambda freeze cycles. AWS Lambda recycles long-lived sockets (e.g.\n // MCP Streamable HTTP long-polls, keep-alive HTTP agents) ~60s after\n // a function returns 200; the next thaw delivers a socket-end event\n // whose Promise has nobody left to await it. The function itself\n // already returned correctly, so there's no user impact — just\n // ~10k events/day of noise. The narrow shape (unhandled rejection\n // from `Socket.socketOnEnd` in `node:_http_client`) keeps real\n // application-thrown socket errors from being silenced.\n const exceptionValue = event.exception?.values?.[0]?.value ?? \"\";\n const exceptionMechanism = event.exception?.values?.[0]?.mechanism?.type;\n const isUnhandledRejection =\n typeof exceptionMechanism === \"string\" &&\n exceptionMechanism.endsWith(\"onunhandledrejection\");\n if (exceptionValue === \"socket hang up\" && isUnhandledRejection) {\n const frames = event.exception?.values?.[0]?.stacktrace?.frames ?? [];\n const fromHttpClient = frames.some(\n (f) =>\n f?.function === \"Socket.socketOnEnd\" ||\n f?.filename === \"node:_http_client\",\n );\n if (fromHttpClient) {\n return null;\n }\n }\n // Drop SDK-only ErrorEvent promise rejections. These arrive as Node\n // unhandled rejections with no application frames — typically the Neon\n // serverless driver's WebSocket dying across a Lambda freeze/thaw and\n // rejecting a floating promise with the raw ErrorEvent (the per-client\n // logger in db/client.ts already records these with context). Keep any\n // event with real app frames so thrown ErrorEvents stay visible.\n //\n // \"Application frame\" must exclude the Sentry SDK's own bundled chunks:\n // serverless bundles place them under the app root (e.g.\n // `/var/task/_libs/@sentry/node+….mjs`), outside node_modules, so the\n // SDK marks them in_app and the unhandled-rejection instrumentation\n // stack alone would defeat this filter.\n if (exceptionValue === \"[object ErrorEvent]\" && isUnhandledRejection) {\n const frames = event.exception?.values?.[0]?.stacktrace?.frames ?? [];\n const hasApplicationFrame = frames.some(\n (frame) =>\n frame?.in_app && !String(frame?.filename ?? \"\").includes(\"sentry\"),\n );\n const hasSentryFrame = frames.some((frame) =>\n String(frame?.filename ?? \"\").includes(\"sentry\"),\n );\n if (!hasApplicationFrame && hasSentryFrame) {\n return null;\n }\n }\n const metadata = (\n event as {\n metadata?: {\n filename?: unknown;\n value?: unknown;\n };\n }\n ).metadata;\n if (\n metadata?.value === \"[object ErrorEvent]\" &&\n !event.exception?.values?.length &&\n String(metadata.filename ?? \"\").includes(\"sentry\")\n ) {\n return null;\n }\n // h3's `createError({ statusCode: 4xx, ... })` produces an\n // `HTTPError` (h3 v2) / `H3Error` (h3 v1). 4xx HTTPErrors are\n // handler-controlled \"expected failure\" responses (404 not found,\n // 400 bad input) that route through Nitro's error hook just\n // because they bubble out of `defineEventHandler`. They aren't\n // bugs — they're the documented way to return a 4xx in h3.\n // Capture only when the statusCode looks like 5xx (real failure)\n // or is missing (generic Error masquerading as HTTPError).\n if (exceptionType === \"HTTPError\" || exceptionType === \"H3Error\") {\n const statusCode = (event.tags?.statusCode ??\n (\n event.contexts as\n | Record<string, Record<string, unknown>>\n | undefined\n )?.h3?.statusCode) as number | string | undefined;\n const code =\n typeof statusCode === \"string\"\n ? parseInt(statusCode, 10)\n : statusCode;\n if (typeof code === \"number\" && code >= 400 && code < 500) {\n return null;\n }\n // No statusCode in the event payload — fall back to matching the\n // common 4xx messages so handler-thrown 404/400/403/401 don't\n // pollute Sentry. This is a heuristic, but the alternatives\n // (every 4xx becomes a \"real\" issue, or we patch every route to\n // catch+return) are worse.\n const value = event.exception?.values?.[0]?.value ?? \"\";\n if (\n /^Cannot find any route matching/i.test(value) ||\n / not found$/i.test(value) ||\n /Unauthenticated$/i.test(value) ||\n /^Unauthorized$/i.test(value) ||\n /^No access to /i.test(value)\n ) {\n return null;\n }\n }\n\n // Defense in depth: scrub PII even if some integration auto-attached\n // request metadata despite sendDefaultPii: false.\n if (event.request) {\n if (event.request.headers) {\n const headers = event.request.headers as Record<string, string>;\n for (const k of Object.keys(headers)) {\n const lk = k.toLowerCase();\n if (\n lk === \"cookie\" ||\n lk === \"authorization\" ||\n lk === \"set-cookie\" ||\n lk === \"proxy-authorization\"\n ) {\n delete headers[k];\n }\n }\n }\n // Cookies live in their own field too.\n delete (event.request as Record<string, unknown>).cookies;\n }\n\n // Keep user info that was explicitly set via Sentry.setUser\n // (id/email/username) so we can attribute crashes back to a real\n // operator. Always strip ip_address — auto-collected, no consent.\n if (event.user) {\n const user = event.user as Record<string, unknown>;\n delete user.ip_address;\n const hasIdentity =\n typeof user.id === \"string\" ||\n typeof user.email === \"string\" ||\n typeof user.username === \"string\";\n if (!hasIdentity) {\n delete event.user;\n }\n }\n\n // Sentry's contexts can carry process.env snapshots — strip env-shaped\n // contexts so we don't leak deployment secrets.\n if (event.contexts && typeof event.contexts === \"object\") {\n delete (event.contexts as Record<string, unknown>).runtime_env;\n }\n\n return event;\n },\n });\n\n _initSucceeded = true;\n return true;\n}\n\n/**\n * `true` once `initServerSentry()` has succeeded with a DSN. Plugins that\n * want to skip work when Sentry is disabled can check this before calling\n * the helpers below.\n */\nexport function isServerSentryEnabled(): boolean {\n return _initSucceeded;\n}\n\n/**\n * Attach the current request's user to Sentry's isolation scope so any\n * `captureException` triggered later in the request carries the right\n * `user.id` / `user.email` / `user.username` and `orgId` tag.\n *\n * Sentry node 10 uses Node's AsyncLocalStorage to give each async context\n * its own isolation scope, so setting on `getIsolationScope()` here only\n * affects events emitted while this request's async context is active.\n *\n * No-ops gracefully when Sentry isn't initialized or no session exists —\n * never throws into the request path.\n */\nexport function setSentryUserForRequest(session: AuthSession | null): void {\n if (!_initSucceeded) return;\n try {\n const scope = Sentry.getIsolationScope();\n if (!session) {\n scope.setUser(null);\n scope.setTag(\"orgId\", null);\n return;\n }\n scope.setUser({\n id: session.userId ?? session.email,\n email: session.email,\n username: session.name,\n });\n scope.setTag(\"orgId\", session.orgId ?? null);\n if (session.orgRole) {\n scope.setTag(\"orgRole\", session.orgRole);\n }\n } catch {\n // Sentry scope APIs should never throw, but if they do we'd rather\n // continue serving the request than crash on observability.\n }\n}\n\n/**\n * Pin a user/org onto the current isolation scope from a lighter\n * `RequestContext`-shaped payload. Used by the request-context observer so\n * action handlers, agent-chat runs, and integration webhook processors —\n * all of which already wrap their work in `runWithRequestContext({ userEmail,\n * orgId, ... })` — automatically tag Sentry events with the right user even\n * when the Nitro `request` hook didn't see a cookie (e.g. webhook delivery,\n * A2A calls, internal background runs).\n *\n * Skips overwriting a richer user identity already set by\n * `setSentryUserForRequest` — the cookie-resolved session has\n * userId/username on top of email, which we shouldn't clobber.\n */\nexport function setSentryRequestContext(ctx: {\n userEmail?: string;\n orgId?: string;\n}): void {\n if (!_initSucceeded) return;\n try {\n const scope = Sentry.getIsolationScope();\n if (ctx.userEmail) {\n const existing = scope.getScopeData().user;\n if (!existing?.id && !existing?.email) {\n scope.setUser({ id: ctx.userEmail, email: ctx.userEmail });\n }\n }\n if (ctx.orgId) {\n scope.setTag(\"orgId\", ctx.orgId);\n }\n } catch {\n // never throw\n }\n}\n\n/**\n * Capture an error from one of the auth attempt routes (login / signup)\n * with the email pinned to the event so support can filter by user. Sets\n * Sentry level to `warning` (not `error`) — bad-password attempts aren't\n * actionable, but a sustained spike of warnings on a route IS the signal\n * we care about.\n *\n * Caller should still return their normal HTTP response (401/409/etc.);\n * this just records the error for observability.\n */\nexport function captureAuthError(\n error: unknown,\n context: { route: \"login\" | \"signup\" | \"logout\"; email?: string },\n): string | undefined {\n if (!_initSucceeded) return undefined;\n try {\n return Sentry.withScope((scope) => {\n scope.setLevel(\"warning\");\n scope.setTag(\"auth\", context.route);\n if (context.email) {\n scope.setUser({ id: context.email, email: context.email });\n }\n return Sentry.captureException(error);\n });\n } catch {\n return undefined;\n }\n}\n\nexport interface RouteErrorContext {\n /** The full request path (e.g. `/_agent-native/agent-chat`). */\n route?: string;\n /** HTTP method (e.g. `GET`, `POST`). */\n method?: string;\n /** Caller's `User-Agent` header. */\n userAgent?: string;\n /** Free-form extra tags to add to the event (low-cardinality). */\n tags?: Record<string, string | undefined>;\n /**\n * High-cardinality / structured payload — not searchable but visible in\n * the Sentry event detail (recording IDs, byte counts, compression\n * metadata, response body tails, etc.).\n */\n extra?: Record<string, unknown>;\n /**\n * Grouped contexts shown as separate cards in the Sentry event UI.\n */\n contexts?: Record<string, Record<string, unknown>>;\n}\n\n/**\n * Capture an exception that surfaced in a Nitro route handler with the\n * request's route/method/userAgent attached as searchable Sentry tags.\n *\n * Non-throwing: if Sentry isn't initialized or the underlying capture\n * fails, this is a no-op. Returns the Sentry event ID when capture\n * succeeded, otherwise `undefined`.\n */\nexport function captureRouteError(\n error: unknown,\n context: RouteErrorContext = {},\n): string | undefined {\n if (!_initSucceeded) return undefined;\n try {\n return Sentry.withScope((scope) => {\n if (context.route) scope.setTag(\"route\", context.route);\n if (context.method) scope.setTag(\"method\", context.method);\n if (context.userAgent) scope.setTag(\"userAgent\", context.userAgent);\n if (context.tags) {\n for (const [k, v] of Object.entries(context.tags)) {\n if (typeof v === \"string\") scope.setTag(k, v);\n }\n }\n if (context.extra) {\n for (const [k, v] of Object.entries(context.extra)) {\n if (v !== undefined) scope.setExtra(k, v);\n }\n }\n if (context.contexts) {\n for (const [k, v] of Object.entries(context.contexts)) {\n scope.setContext(k, v);\n }\n }\n return Sentry.captureException(error);\n });\n } catch {\n return undefined;\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"social-og-image.d.ts","sourceRoot":"","sources":["../../src/server/social-og-image.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,uBAAuB;IACtC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAChD,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAChD,eAAO,MAAM,mCAAmC,2EAC0B,CAAC;AAC3E,eAAO,MAAM,2CAA2C,oFAC2B,CAAC;AAsOpF,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"social-og-image.d.ts","sourceRoot":"","sources":["../../src/server/social-og-image.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,uBAAuB;IACtC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAChD,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAChD,eAAO,MAAM,mCAAmC,2EAC0B,CAAC;AAC3E,eAAO,MAAM,2CAA2C,oFAC2B,CAAC;AAsOpF,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAUtE;AAED,wBAAgB,2BAA2B,CACzC,KAAK,GAAE,uBAA4B,GAClC,MAAM,CAyCR;AAED,wBAAsB,2BAA2B,CAC/C,KAAK,GAAE,uBAA4B,GAClC,OAAO,CAAC,UAAU,CAAC,CAkBrB;AAED,wBAAgB,iCAAiC,CAC/C,UAAU,CAAC,EAAE,MAAM,EACnB,WAAW,SAAc,GACxB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAYxB;AAED,wBAAgB,+BAA+B,CAC7C,OAAO,GAAE,uBAA4B,2FAqCtC"}
|
|
@@ -173,7 +173,9 @@ function textByteLength(value) {
|
|
|
173
173
|
export function isResvgRuntimeUnavailableError(error) {
|
|
174
174
|
const message = error instanceof Error ? error.message : String(error ?? "");
|
|
175
175
|
return (/@resvg\/resvg-js|resvgjs\.[\w-]+\.node|native binding/i.test(message) &&
|
|
176
|
-
|
|
176
|
+
// "no such module" is workerd's wording when the package is externalized
|
|
177
|
+
// out of the Cloudflare worker bundle.
|
|
178
|
+
/cannot find|no such module|err_module_not_found|dlopen|invalid elf|wrong architecture|not a valid win32|native binding/i.test(message));
|
|
177
179
|
}
|
|
178
180
|
export function renderAgentNativeOgImageSvg(input = {}) {
|
|
179
181
|
const appName = cleanText(input.appName) || resolveDefaultAppName();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"social-og-image.js","sourceRoot":"","sources":["../../src/server/social-og-image.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n defineEventHandler,\n getHeader,\n getMethod,\n getQuery,\n getRequestURL,\n type H3Event,\n} from \"h3\";\nimport { resolveBuiltInAuthMarketing } from \"./auth-marketing.js\";\nimport { getAppName } from \"./app-name.js\";\nimport { OG_FONT_FAMILY, resolveOgFontFiles } from \"./og-fonts.js\";\n\nexport interface AgentNativeOgImageInput {\n appName?: string | null;\n title?: string | null;\n accentText?: string | null;\n}\n\nexport const AGENT_NATIVE_OG_IMAGE_WIDTH = 1200;\nexport const AGENT_NATIVE_OG_IMAGE_HEIGHT = 630;\nexport const AGENT_NATIVE_OG_IMAGE_CACHE_CONTROL =\n \"public, max-age=60, stale-while-revalidate=604800, stale-if-error=3600\";\nexport const AGENT_NATIVE_OG_IMAGE_NETLIFY_CACHE_CONTROL =\n \"public, durable, max-age=60, stale-while-revalidate=604800, stale-if-error=3600\";\n\nconst WIDTH = AGENT_NATIVE_OG_IMAGE_WIDTH;\nconst HEIGHT = AGENT_NATIVE_OG_IMAGE_HEIGHT;\nconst BRAND_BLUE = \"#00B5FF\";\nconst BRAND_MINT = \"#48FFE4\";\nconst BG = \"#000000\";\nconst FG = \"#f5f5f5\";\nconst FONT_FAMILY = `${OG_FONT_FAMILY}, Arial, Helvetica, system-ui, sans-serif`;\nconst DEFAULT_ACCENT_TEXT = \"100% free and open source\";\n\nconst LOGO_MARK = `\n <path d=\"M24.5537 65.7695H0L15.0859 39.4619L37.708 0L60.4912 39.4619H39.6396L24.5537 65.7695Z\" fill=\"white\"/>\n <path d=\"M89.446 0H114L76.2921 65.7704H51.7383L89.446 0Z\" fill=\"url(#brand)\"/>\n`;\n\nfunction escapeSvg(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\");\n}\n\nfunction cleanText(value: string | null | undefined): string {\n return String(value ?? \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\nfunction titleCase(value: string): string {\n return value\n .split(/[\\s._-]+/)\n .filter(Boolean)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())\n .join(\" \");\n}\n\nfunction titleFromAppName(appName: string): string {\n if (appName) return appName;\n const basePath =\n process.env.VITE_APP_BASE_PATH || process.env.APP_BASE_PATH || \"\";\n const slug = basePath.split(\"/\").filter(Boolean)[0] || \"\";\n return titleCase(slug) || \"Agent-Native\";\n}\n\ninterface WrappedText {\n lines: string[];\n truncated: boolean;\n}\n\ninterface TitleLayout {\n lines: string[];\n fontSize: number;\n lineHeight: number;\n}\n\nfunction estimateTextWidth(value: string, fontSize: number): number {\n let units = 0;\n for (const char of value) {\n if (char === \" \") {\n units += 0.28;\n } else if (/[MW@#%&]/.test(char)) {\n units += 0.86;\n } else if (/[A-Z]/.test(char)) {\n units += 0.64;\n } else if (/[ilI.,:;|!']/u.test(char)) {\n units += 0.26;\n } else if (/[0-9]/.test(char)) {\n units += 0.56;\n } else {\n units += 0.54;\n }\n }\n return units * fontSize;\n}\n\nfunction trimTextToWidth(\n value: string,\n fontSize: number,\n maxWidth: number,\n): string {\n const ellipsis = \"...\";\n let trimmed = value.trim();\n while (\n trimmed.length > 0 &&\n estimateTextWidth(`${trimmed}${ellipsis}`, fontSize) > maxWidth\n ) {\n trimmed = trimmed.slice(0, -1).trimEnd();\n }\n return trimmed ? `${trimmed}${ellipsis}` : ellipsis;\n}\n\nfunction wrapTextToWidth(\n value: string,\n fontSize: number,\n maxWidth: number,\n maxLines: number,\n): WrappedText {\n const words = value.split(/\\s+/).filter(Boolean);\n const lines: string[] = [];\n let current = \"\";\n let truncated = false;\n\n for (const word of words) {\n const next = current ? `${current} ${word}` : word;\n if (estimateTextWidth(next, fontSize) <= maxWidth) {\n current = next;\n continue;\n }\n if (!current) {\n lines.push(trimTextToWidth(word, fontSize, maxWidth));\n truncated = true;\n current = \"\";\n } else {\n lines.push(current);\n current = word;\n }\n if (lines.length === maxLines) {\n truncated = true;\n break;\n }\n }\n if (current && lines.length < maxLines) lines.push(current);\n\n const usedWordCount = lines.join(\" \").split(/\\s+/).filter(Boolean).length;\n if (usedWordCount < words.length && lines.length > 0) {\n lines[lines.length - 1] = trimTextToWidth(\n lines[lines.length - 1],\n fontSize,\n maxWidth,\n );\n truncated = true;\n }\n\n return {\n lines: lines.length ? lines : [trimTextToWidth(value, fontSize, maxWidth)],\n truncated,\n };\n}\n\nfunction getTitleLayout(title: string): TitleLayout {\n const maxTitleWidth = 900;\n if (estimateTextWidth(title, 88) <= maxTitleWidth) {\n return {\n lines: [title],\n fontSize: 88,\n lineHeight: 96,\n };\n }\n\n for (const fontSize of [76, 70, 64, 58, 52]) {\n const wrapped = wrapTextToWidth(title, fontSize, maxTitleWidth, 2);\n if (!wrapped.truncated) {\n const lineHeight = Math.round(fontSize * 1.1);\n return {\n lines: wrapped.lines,\n fontSize,\n lineHeight,\n };\n }\n }\n\n const fallbackFontSize = 52;\n const wrapped = wrapTextToWidth(title, fallbackFontSize, maxTitleWidth, 2);\n return {\n lines: wrapped.lines,\n fontSize: fallbackFontSize,\n lineHeight: 60,\n };\n}\n\nfunction textBlock({\n lines,\n x,\n y,\n fontSize,\n lineHeight,\n weight,\n fill,\n anchor = \"start\",\n}: {\n lines: string[];\n x: number;\n y: number;\n fontSize: number;\n lineHeight: number;\n weight: number;\n fill: string;\n anchor?: \"start\" | \"middle\";\n}): string {\n return `<text x=\"${x}\" y=\"${y}\" text-anchor=\"${anchor}\" font-family=\"${FONT_FAMILY}\" font-size=\"${fontSize}\" font-weight=\"${weight}\" fill=\"${fill}\">${lines\n .map(\n (line, index) =>\n `<tspan x=\"${x}\" dy=\"${index === 0 ? 0 : lineHeight}\">${escapeSvg(line)}</tspan>`,\n )\n .join(\"\")}</text>`;\n}\n\nfunction resolveDefaultAppName(event?: H3Event): string {\n const requestHost = event\n ? (getHeader(event, \"x-forwarded-host\") ?? getHeader(event, \"host\"))\n : undefined;\n const requestPath = event ? getRequestURL(event).pathname : undefined;\n return (\n getAppName() ??\n resolveBuiltInAuthMarketing({ requestHost, requestPath })?.appName ??\n \"Agent-Native\"\n );\n}\n\nfunction queryStringValue(\n value: unknown,\n maxLength: number,\n): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const clean = cleanText(value).slice(0, maxLength);\n return clean || undefined;\n}\n\nfunction pngBody(bytes: Uint8Array): ArrayBuffer {\n const body = new ArrayBuffer(bytes.byteLength);\n new Uint8Array(body).set(bytes);\n return body;\n}\n\nfunction textByteLength(value: string): number {\n return new TextEncoder().encode(value).byteLength;\n}\n\nexport function isResvgRuntimeUnavailableError(error: unknown): boolean {\n const message = error instanceof Error ? error.message : String(error ?? \"\");\n return (\n /@resvg\\/resvg-js|resvgjs\\.[\\w-]+\\.node|native binding/i.test(message) &&\n /cannot find|err_module_not_found|dlopen|invalid elf|wrong architecture|not a valid win32|native binding/i.test(\n message,\n )\n );\n}\n\nexport function renderAgentNativeOgImageSvg(\n input: AgentNativeOgImageInput = {},\n): string {\n const appName = cleanText(input.appName) || resolveDefaultAppName();\n const title = cleanText(input.title) || titleFromAppName(appName);\n const accentText = cleanText(input.accentText) || DEFAULT_ACCENT_TEXT;\n const titleLayout = getTitleLayout(title);\n const titleY = titleLayout.lines.length > 1 ? 288 : 330;\n const accentY =\n titleY + titleLayout.lineHeight * (titleLayout.lines.length - 1) + 70;\n\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${WIDTH}\" height=\"${HEIGHT}\" viewBox=\"0 0 ${WIDTH} ${HEIGHT}\">\n <title>${escapeSvg(title)} - Agent-Native preview</title>\n <defs>\n <linearGradient id=\"brand\" x1=\"101.702\" y1=\"67.4791\" x2=\"113.672\" y2=\"-37.4275\" gradientUnits=\"userSpaceOnUse\">\n <stop stop-color=\"${BRAND_BLUE}\"/>\n <stop offset=\"1\" stop-color=\"${BRAND_MINT}\"/>\n </linearGradient>\n <pattern id=\"grid\" width=\"48\" height=\"48\" patternUnits=\"userSpaceOnUse\">\n <path d=\"M 48 0 L 0 0 0 48\" fill=\"none\" stroke=\"#ffffff\" stroke-opacity=\"0.07\" stroke-width=\"1\"/>\n </pattern>\n </defs>\n <rect width=\"${WIDTH}\" height=\"${HEIGHT}\" fill=\"${BG}\"/>\n <rect width=\"${WIDTH}\" height=\"${HEIGHT}\" fill=\"url(#grid)\"/>\n <g transform=\"translate(80 116) scale(0.94)\">\n ${LOGO_MARK}\n </g>\n <g>\n ${textBlock({\n lines: titleLayout.lines,\n x: 80,\n y: titleY,\n fontSize: titleLayout.fontSize,\n lineHeight: titleLayout.lineHeight,\n // resvg's fontdb maps font-weight 850 to the Regular face (only 400/700\n // exist for Liberation Sans); 800 resolves to Bold, the heaviest face we\n // bundle, which is the intended look for the display title.\n weight: 800,\n fill: FG,\n })}\n <text x=\"84\" y=\"${accentY}\" font-family=\"${FONT_FAMILY}\" font-size=\"34\" font-weight=\"800\" fill=\"${BRAND_BLUE}\">${escapeSvg(accentText)}</text>\n </g>\n</svg>`;\n}\n\nexport async function renderAgentNativeOgImagePng(\n input: AgentNativeOgImageInput = {},\n): Promise<Uint8Array> {\n const { Resvg } = await import(/* @vite-ignore */ \"@resvg/resvg-js\");\n // Feed resvg the embedded Liberation Sans font explicitly. System fonts can't\n // be relied on: Linux serverless runtimes (Netlify/Lambda) ship neither Arial\n // nor Inter, so without a bundled font every `<text>` rendered blank.\n const fontFiles = resolveOgFontFiles();\n const hasBundledFonts = Boolean(fontFiles?.length);\n const image = new Resvg(renderAgentNativeOgImageSvg(input), {\n fitTo: { mode: \"width\", value: WIDTH },\n font: {\n loadSystemFonts: !hasBundledFonts,\n ...(hasBundledFonts ? { fontFiles } : {}),\n defaultFontFamily: OG_FONT_FAMILY,\n serifFamily: OG_FONT_FAMILY,\n sansSerifFamily: OG_FONT_FAMILY,\n },\n }).render();\n return image.asPng();\n}\n\nexport function agentNativeOgImageResponseHeaders(\n byteLength?: number,\n contentType = \"image/png\",\n): Record<string, string> {\n const headers: Record<string, string> = {\n \"Content-Type\": contentType,\n \"Cache-Control\": AGENT_NATIVE_OG_IMAGE_CACHE_CONTROL,\n \"CDN-Cache-Control\": AGENT_NATIVE_OG_IMAGE_CACHE_CONTROL,\n \"Netlify-CDN-Cache-Control\": AGENT_NATIVE_OG_IMAGE_NETLIFY_CACHE_CONTROL,\n \"Cross-Origin-Resource-Policy\": \"cross-origin\",\n };\n if (typeof byteLength === \"number\") {\n headers[\"Content-Length\"] = String(byteLength);\n }\n return headers;\n}\n\nexport function createAgentNativeOgImageHandler(\n options: AgentNativeOgImageInput = {},\n) {\n return defineEventHandler(async (event) => {\n if (getMethod(event) === \"HEAD\") {\n return new Response(null, {\n headers: agentNativeOgImageResponseHeaders(),\n });\n }\n\n const query = getQuery(event);\n const appName = cleanText(options.appName) || resolveDefaultAppName(event);\n const input = {\n ...options,\n appName,\n title: cleanText(options.title) || queryStringValue(query.title, 140),\n accentText:\n cleanText(options.accentText) || queryStringValue(query.accentText, 80),\n };\n\n let png: Uint8Array;\n try {\n png = await renderAgentNativeOgImagePng(input);\n } catch (error) {\n if (!isResvgRuntimeUnavailableError(error)) throw error;\n const svg = renderAgentNativeOgImageSvg(input);\n return new Response(svg, {\n headers: agentNativeOgImageResponseHeaders(\n textByteLength(svg),\n \"image/svg+xml; charset=utf-8\",\n ),\n });\n }\n\n return new Response(pngBody(png), {\n headers: agentNativeOgImageResponseHeaders(png.byteLength),\n });\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"social-og-image.js","sourceRoot":"","sources":["../../src/server/social-og-image.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n defineEventHandler,\n getHeader,\n getMethod,\n getQuery,\n getRequestURL,\n type H3Event,\n} from \"h3\";\nimport { resolveBuiltInAuthMarketing } from \"./auth-marketing.js\";\nimport { getAppName } from \"./app-name.js\";\nimport { OG_FONT_FAMILY, resolveOgFontFiles } from \"./og-fonts.js\";\n\nexport interface AgentNativeOgImageInput {\n appName?: string | null;\n title?: string | null;\n accentText?: string | null;\n}\n\nexport const AGENT_NATIVE_OG_IMAGE_WIDTH = 1200;\nexport const AGENT_NATIVE_OG_IMAGE_HEIGHT = 630;\nexport const AGENT_NATIVE_OG_IMAGE_CACHE_CONTROL =\n \"public, max-age=60, stale-while-revalidate=604800, stale-if-error=3600\";\nexport const AGENT_NATIVE_OG_IMAGE_NETLIFY_CACHE_CONTROL =\n \"public, durable, max-age=60, stale-while-revalidate=604800, stale-if-error=3600\";\n\nconst WIDTH = AGENT_NATIVE_OG_IMAGE_WIDTH;\nconst HEIGHT = AGENT_NATIVE_OG_IMAGE_HEIGHT;\nconst BRAND_BLUE = \"#00B5FF\";\nconst BRAND_MINT = \"#48FFE4\";\nconst BG = \"#000000\";\nconst FG = \"#f5f5f5\";\nconst FONT_FAMILY = `${OG_FONT_FAMILY}, Arial, Helvetica, system-ui, sans-serif`;\nconst DEFAULT_ACCENT_TEXT = \"100% free and open source\";\n\nconst LOGO_MARK = `\n <path d=\"M24.5537 65.7695H0L15.0859 39.4619L37.708 0L60.4912 39.4619H39.6396L24.5537 65.7695Z\" fill=\"white\"/>\n <path d=\"M89.446 0H114L76.2921 65.7704H51.7383L89.446 0Z\" fill=\"url(#brand)\"/>\n`;\n\nfunction escapeSvg(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\");\n}\n\nfunction cleanText(value: string | null | undefined): string {\n return String(value ?? \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\nfunction titleCase(value: string): string {\n return value\n .split(/[\\s._-]+/)\n .filter(Boolean)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())\n .join(\" \");\n}\n\nfunction titleFromAppName(appName: string): string {\n if (appName) return appName;\n const basePath =\n process.env.VITE_APP_BASE_PATH || process.env.APP_BASE_PATH || \"\";\n const slug = basePath.split(\"/\").filter(Boolean)[0] || \"\";\n return titleCase(slug) || \"Agent-Native\";\n}\n\ninterface WrappedText {\n lines: string[];\n truncated: boolean;\n}\n\ninterface TitleLayout {\n lines: string[];\n fontSize: number;\n lineHeight: number;\n}\n\nfunction estimateTextWidth(value: string, fontSize: number): number {\n let units = 0;\n for (const char of value) {\n if (char === \" \") {\n units += 0.28;\n } else if (/[MW@#%&]/.test(char)) {\n units += 0.86;\n } else if (/[A-Z]/.test(char)) {\n units += 0.64;\n } else if (/[ilI.,:;|!']/u.test(char)) {\n units += 0.26;\n } else if (/[0-9]/.test(char)) {\n units += 0.56;\n } else {\n units += 0.54;\n }\n }\n return units * fontSize;\n}\n\nfunction trimTextToWidth(\n value: string,\n fontSize: number,\n maxWidth: number,\n): string {\n const ellipsis = \"...\";\n let trimmed = value.trim();\n while (\n trimmed.length > 0 &&\n estimateTextWidth(`${trimmed}${ellipsis}`, fontSize) > maxWidth\n ) {\n trimmed = trimmed.slice(0, -1).trimEnd();\n }\n return trimmed ? `${trimmed}${ellipsis}` : ellipsis;\n}\n\nfunction wrapTextToWidth(\n value: string,\n fontSize: number,\n maxWidth: number,\n maxLines: number,\n): WrappedText {\n const words = value.split(/\\s+/).filter(Boolean);\n const lines: string[] = [];\n let current = \"\";\n let truncated = false;\n\n for (const word of words) {\n const next = current ? `${current} ${word}` : word;\n if (estimateTextWidth(next, fontSize) <= maxWidth) {\n current = next;\n continue;\n }\n if (!current) {\n lines.push(trimTextToWidth(word, fontSize, maxWidth));\n truncated = true;\n current = \"\";\n } else {\n lines.push(current);\n current = word;\n }\n if (lines.length === maxLines) {\n truncated = true;\n break;\n }\n }\n if (current && lines.length < maxLines) lines.push(current);\n\n const usedWordCount = lines.join(\" \").split(/\\s+/).filter(Boolean).length;\n if (usedWordCount < words.length && lines.length > 0) {\n lines[lines.length - 1] = trimTextToWidth(\n lines[lines.length - 1],\n fontSize,\n maxWidth,\n );\n truncated = true;\n }\n\n return {\n lines: lines.length ? lines : [trimTextToWidth(value, fontSize, maxWidth)],\n truncated,\n };\n}\n\nfunction getTitleLayout(title: string): TitleLayout {\n const maxTitleWidth = 900;\n if (estimateTextWidth(title, 88) <= maxTitleWidth) {\n return {\n lines: [title],\n fontSize: 88,\n lineHeight: 96,\n };\n }\n\n for (const fontSize of [76, 70, 64, 58, 52]) {\n const wrapped = wrapTextToWidth(title, fontSize, maxTitleWidth, 2);\n if (!wrapped.truncated) {\n const lineHeight = Math.round(fontSize * 1.1);\n return {\n lines: wrapped.lines,\n fontSize,\n lineHeight,\n };\n }\n }\n\n const fallbackFontSize = 52;\n const wrapped = wrapTextToWidth(title, fallbackFontSize, maxTitleWidth, 2);\n return {\n lines: wrapped.lines,\n fontSize: fallbackFontSize,\n lineHeight: 60,\n };\n}\n\nfunction textBlock({\n lines,\n x,\n y,\n fontSize,\n lineHeight,\n weight,\n fill,\n anchor = \"start\",\n}: {\n lines: string[];\n x: number;\n y: number;\n fontSize: number;\n lineHeight: number;\n weight: number;\n fill: string;\n anchor?: \"start\" | \"middle\";\n}): string {\n return `<text x=\"${x}\" y=\"${y}\" text-anchor=\"${anchor}\" font-family=\"${FONT_FAMILY}\" font-size=\"${fontSize}\" font-weight=\"${weight}\" fill=\"${fill}\">${lines\n .map(\n (line, index) =>\n `<tspan x=\"${x}\" dy=\"${index === 0 ? 0 : lineHeight}\">${escapeSvg(line)}</tspan>`,\n )\n .join(\"\")}</text>`;\n}\n\nfunction resolveDefaultAppName(event?: H3Event): string {\n const requestHost = event\n ? (getHeader(event, \"x-forwarded-host\") ?? getHeader(event, \"host\"))\n : undefined;\n const requestPath = event ? getRequestURL(event).pathname : undefined;\n return (\n getAppName() ??\n resolveBuiltInAuthMarketing({ requestHost, requestPath })?.appName ??\n \"Agent-Native\"\n );\n}\n\nfunction queryStringValue(\n value: unknown,\n maxLength: number,\n): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const clean = cleanText(value).slice(0, maxLength);\n return clean || undefined;\n}\n\nfunction pngBody(bytes: Uint8Array): ArrayBuffer {\n const body = new ArrayBuffer(bytes.byteLength);\n new Uint8Array(body).set(bytes);\n return body;\n}\n\nfunction textByteLength(value: string): number {\n return new TextEncoder().encode(value).byteLength;\n}\n\nexport function isResvgRuntimeUnavailableError(error: unknown): boolean {\n const message = error instanceof Error ? error.message : String(error ?? \"\");\n return (\n /@resvg\\/resvg-js|resvgjs\\.[\\w-]+\\.node|native binding/i.test(message) &&\n // \"no such module\" is workerd's wording when the package is externalized\n // out of the Cloudflare worker bundle.\n /cannot find|no such module|err_module_not_found|dlopen|invalid elf|wrong architecture|not a valid win32|native binding/i.test(\n message,\n )\n );\n}\n\nexport function renderAgentNativeOgImageSvg(\n input: AgentNativeOgImageInput = {},\n): string {\n const appName = cleanText(input.appName) || resolveDefaultAppName();\n const title = cleanText(input.title) || titleFromAppName(appName);\n const accentText = cleanText(input.accentText) || DEFAULT_ACCENT_TEXT;\n const titleLayout = getTitleLayout(title);\n const titleY = titleLayout.lines.length > 1 ? 288 : 330;\n const accentY =\n titleY + titleLayout.lineHeight * (titleLayout.lines.length - 1) + 70;\n\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${WIDTH}\" height=\"${HEIGHT}\" viewBox=\"0 0 ${WIDTH} ${HEIGHT}\">\n <title>${escapeSvg(title)} - Agent-Native preview</title>\n <defs>\n <linearGradient id=\"brand\" x1=\"101.702\" y1=\"67.4791\" x2=\"113.672\" y2=\"-37.4275\" gradientUnits=\"userSpaceOnUse\">\n <stop stop-color=\"${BRAND_BLUE}\"/>\n <stop offset=\"1\" stop-color=\"${BRAND_MINT}\"/>\n </linearGradient>\n <pattern id=\"grid\" width=\"48\" height=\"48\" patternUnits=\"userSpaceOnUse\">\n <path d=\"M 48 0 L 0 0 0 48\" fill=\"none\" stroke=\"#ffffff\" stroke-opacity=\"0.07\" stroke-width=\"1\"/>\n </pattern>\n </defs>\n <rect width=\"${WIDTH}\" height=\"${HEIGHT}\" fill=\"${BG}\"/>\n <rect width=\"${WIDTH}\" height=\"${HEIGHT}\" fill=\"url(#grid)\"/>\n <g transform=\"translate(80 116) scale(0.94)\">\n ${LOGO_MARK}\n </g>\n <g>\n ${textBlock({\n lines: titleLayout.lines,\n x: 80,\n y: titleY,\n fontSize: titleLayout.fontSize,\n lineHeight: titleLayout.lineHeight,\n // resvg's fontdb maps font-weight 850 to the Regular face (only 400/700\n // exist for Liberation Sans); 800 resolves to Bold, the heaviest face we\n // bundle, which is the intended look for the display title.\n weight: 800,\n fill: FG,\n })}\n <text x=\"84\" y=\"${accentY}\" font-family=\"${FONT_FAMILY}\" font-size=\"34\" font-weight=\"800\" fill=\"${BRAND_BLUE}\">${escapeSvg(accentText)}</text>\n </g>\n</svg>`;\n}\n\nexport async function renderAgentNativeOgImagePng(\n input: AgentNativeOgImageInput = {},\n): Promise<Uint8Array> {\n const { Resvg } = await import(/* @vite-ignore */ \"@resvg/resvg-js\");\n // Feed resvg the embedded Liberation Sans font explicitly. System fonts can't\n // be relied on: Linux serverless runtimes (Netlify/Lambda) ship neither Arial\n // nor Inter, so without a bundled font every `<text>` rendered blank.\n const fontFiles = resolveOgFontFiles();\n const hasBundledFonts = Boolean(fontFiles?.length);\n const image = new Resvg(renderAgentNativeOgImageSvg(input), {\n fitTo: { mode: \"width\", value: WIDTH },\n font: {\n loadSystemFonts: !hasBundledFonts,\n ...(hasBundledFonts ? { fontFiles } : {}),\n defaultFontFamily: OG_FONT_FAMILY,\n serifFamily: OG_FONT_FAMILY,\n sansSerifFamily: OG_FONT_FAMILY,\n },\n }).render();\n return image.asPng();\n}\n\nexport function agentNativeOgImageResponseHeaders(\n byteLength?: number,\n contentType = \"image/png\",\n): Record<string, string> {\n const headers: Record<string, string> = {\n \"Content-Type\": contentType,\n \"Cache-Control\": AGENT_NATIVE_OG_IMAGE_CACHE_CONTROL,\n \"CDN-Cache-Control\": AGENT_NATIVE_OG_IMAGE_CACHE_CONTROL,\n \"Netlify-CDN-Cache-Control\": AGENT_NATIVE_OG_IMAGE_NETLIFY_CACHE_CONTROL,\n \"Cross-Origin-Resource-Policy\": \"cross-origin\",\n };\n if (typeof byteLength === \"number\") {\n headers[\"Content-Length\"] = String(byteLength);\n }\n return headers;\n}\n\nexport function createAgentNativeOgImageHandler(\n options: AgentNativeOgImageInput = {},\n) {\n return defineEventHandler(async (event) => {\n if (getMethod(event) === \"HEAD\") {\n return new Response(null, {\n headers: agentNativeOgImageResponseHeaders(),\n });\n }\n\n const query = getQuery(event);\n const appName = cleanText(options.appName) || resolveDefaultAppName(event);\n const input = {\n ...options,\n appName,\n title: cleanText(options.title) || queryStringValue(query.title, 140),\n accentText:\n cleanText(options.accentText) || queryStringValue(query.accentText, 80),\n };\n\n let png: Uint8Array;\n try {\n png = await renderAgentNativeOgImagePng(input);\n } catch (error) {\n if (!isResvgRuntimeUnavailableError(error)) throw error;\n const svg = renderAgentNativeOgImageSvg(input);\n return new Response(svg, {\n headers: agentNativeOgImageResponseHeaders(\n textByteLength(svg),\n \"image/svg+xml; charset=utf-8\",\n ),\n });\n }\n\n return new Response(pngBody(png), {\n headers: agentNativeOgImageResponseHeaders(png.byteLength),\n });\n });\n}\n"]}
|
package/dist/styles/blocks.css
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
without a definition the declaration is invalid and code falls back to the
|
|
31
31
|
large prose size. The plan template overrides this var in its own
|
|
32
32
|
`global.css`; this is the cross-app default. */
|
|
33
|
-
--plan-code-size: 0.
|
|
33
|
+
--plan-code-size: 0.7rem;
|
|
34
34
|
|
|
35
35
|
/* AUTHORITATIVE code-body size for DENSE in-document code surfaces (diff rows,
|
|
36
36
|
annotated-code lines, API JSON examples, code-tabs, the `code` primitive
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
comfortable size here, those surfaces stay compact and scannable in every
|
|
47
47
|
app while a free-standing snippet can still follow `--plan-code-size`.
|
|
48
48
|
Resize ALL dense document code at once by overriding this var. */
|
|
49
|
-
--plan-doc-code-size: 0.
|
|
49
|
+
--plan-doc-code-size: 0.7rem;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
.dark {
|
|
@@ -122,7 +122,7 @@
|
|
|
122
122
|
dense size on those WHEREVER they render, so the standard annotated/diff
|
|
123
123
|
component can never show oversized code on any surface (embedded panels,
|
|
124
124
|
non-plan-document hosts, etc.), not just inside `.plan-document-flow`. The
|
|
125
|
-
`var(--plan-doc-code-size, 0.
|
|
125
|
+
`var(--plan-doc-code-size, 0.7rem)` fallback covers hosts that don't define
|
|
126
126
|
the token. */
|
|
127
127
|
:is(
|
|
128
128
|
[data-code-surface],
|
|
@@ -133,7 +133,7 @@
|
|
|
133
133
|
.an-code-block pre,
|
|
134
134
|
.an-code-block code
|
|
135
135
|
) {
|
|
136
|
-
font-size: var(--plan-doc-code-size, 0.
|
|
136
|
+
font-size: var(--plan-doc-code-size, 0.7rem) !important;
|
|
137
137
|
}
|
|
138
138
|
:is([data-code-surface], .an-code-block) :is(pre, code) span {
|
|
139
139
|
font-size: inherit !important;
|
|
@@ -149,7 +149,7 @@
|
|
|
149
149
|
prose plugin (no `.font-mono` class), so body copy is unaffected. */
|
|
150
150
|
:where(.plan-document-flow, .plan-block, .plan-block-node) .font-mono,
|
|
151
151
|
:is([data-code-surface], .an-code-block) .font-mono {
|
|
152
|
-
font-size: var(--plan-doc-code-size, 0.
|
|
152
|
+
font-size: var(--plan-doc-code-size, 0.7rem) !important;
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
/* Small, muted eyebrow label above a block (block title). */
|
|
@@ -664,7 +664,7 @@
|
|
|
664
664
|
|
|
665
665
|
/* Editable surface: ONE border, auto-grows to content (no drag-to-resize). */
|
|
666
666
|
.plan-code-editing {
|
|
667
|
-
overflow:
|
|
667
|
+
overflow: visible;
|
|
668
668
|
border: 1px solid hsl(var(--border));
|
|
669
669
|
border-radius: 12px;
|
|
670
670
|
/* Sit on the page background like the diff block — no distinct surface fill. */
|
|
@@ -700,13 +700,23 @@
|
|
|
700
700
|
|
|
701
701
|
.plan-code-editor-layer,
|
|
702
702
|
.plan-code-editor-input {
|
|
703
|
+
box-sizing: border-box;
|
|
703
704
|
margin: 0;
|
|
704
705
|
padding: 0.85rem 1.1rem;
|
|
705
706
|
font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
706
|
-
font-size:
|
|
707
|
+
font-size: var(--plan-code-size);
|
|
707
708
|
line-height: 1.6;
|
|
709
|
+
font-weight: 400;
|
|
710
|
+
letter-spacing: 0;
|
|
711
|
+
font-kerning: none;
|
|
712
|
+
font-variant-ligatures: none;
|
|
713
|
+
font-feature-settings:
|
|
714
|
+
"liga" 0,
|
|
715
|
+
"calt" 0;
|
|
708
716
|
white-space: pre;
|
|
709
717
|
tab-size: 2;
|
|
718
|
+
overflow-wrap: normal;
|
|
719
|
+
word-break: normal;
|
|
710
720
|
}
|
|
711
721
|
|
|
712
722
|
.plan-code-editor-layer {
|
|
@@ -717,20 +727,36 @@
|
|
|
717
727
|
color: hsl(var(--foreground));
|
|
718
728
|
/* This is a <pre>; a base `pre {}` style would leak in a bg + border + larger
|
|
719
729
|
font-size. Override so it's flat and lines up exactly with the textarea. */
|
|
730
|
+
margin: 0 !important;
|
|
731
|
+
padding: 0.85rem 1.1rem !important;
|
|
720
732
|
background: transparent !important;
|
|
721
733
|
border: 0 !important;
|
|
722
734
|
border-radius: 0;
|
|
723
|
-
font-
|
|
735
|
+
font-family:
|
|
736
|
+
"JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace !important;
|
|
737
|
+
font-size: var(--plan-code-size) !important;
|
|
724
738
|
line-height: 1.6 !important;
|
|
725
739
|
}
|
|
726
740
|
|
|
741
|
+
.plan-code-editor-layer :where(code, span) {
|
|
742
|
+
font: inherit !important;
|
|
743
|
+
letter-spacing: 0 !important;
|
|
744
|
+
font-kerning: none !important;
|
|
745
|
+
font-variant-ligatures: none !important;
|
|
746
|
+
font-feature-settings:
|
|
747
|
+
"liga" 0,
|
|
748
|
+
"calt" 0 !important;
|
|
749
|
+
overflow-wrap: normal !important;
|
|
750
|
+
word-break: normal !important;
|
|
751
|
+
}
|
|
752
|
+
|
|
727
753
|
.plan-code-editor-input {
|
|
728
754
|
position: relative;
|
|
729
755
|
display: block;
|
|
730
756
|
width: 100%;
|
|
731
757
|
min-height: 2.5rem;
|
|
732
758
|
resize: none;
|
|
733
|
-
overflow:
|
|
759
|
+
overflow: auto;
|
|
734
760
|
border: 0;
|
|
735
761
|
background: transparent;
|
|
736
762
|
color: transparent;
|
|
@@ -829,7 +855,7 @@
|
|
|
829
855
|
background: transparent;
|
|
830
856
|
color: hsl(var(--foreground));
|
|
831
857
|
font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
832
|
-
font-size:
|
|
858
|
+
font-size: var(--plan-code-size);
|
|
833
859
|
line-height: 1.6;
|
|
834
860
|
overflow-x: auto;
|
|
835
861
|
}
|
|
@@ -95,9 +95,9 @@ For local stdio proxying, Codex/Cowork compatibility, or clients without
|
|
|
95
95
|
remote MCP OAuth, use the hosted connect fallback:
|
|
96
96
|
|
|
97
97
|
```bash
|
|
98
|
-
npx @agent-native/core connect https://dispatch.agent-native.com
|
|
98
|
+
npx @agent-native/core@latest connect https://dispatch.agent-native.com
|
|
99
99
|
# or, for an isolated app:
|
|
100
|
-
npx @agent-native/core connect https://mail.agent-native.com
|
|
100
|
+
npx @agent-native/core@latest connect https://mail.agent-native.com
|
|
101
101
|
```
|
|
102
102
|
|
|
103
103
|
The command opens the app in the browser, the user clicks **Authorize**, and a
|
|
@@ -111,6 +111,19 @@ Claude bearer-token entry is the migration path: the CLI replaces
|
|
|
111
111
|
`Authorization` headers with URL-only OAuth config and tells the user to
|
|
112
112
|
authenticate from `/mcp`.
|
|
113
113
|
|
|
114
|
+
To re-authenticate an already-installed local/fallback client without
|
|
115
|
+
reinstalling skills or connectors, use:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
npx @agent-native/core@latest reconnect https://dispatch.agent-native.com --client codex
|
|
119
|
+
# or:
|
|
120
|
+
npx @agent-native/core@latest connect reconnect https://dispatch.agent-native.com --client codex
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
With no URL, `reconnect` searches the selected client config for the existing
|
|
124
|
+
Agent Native MCP entry. Pass `--name <serverName>` when the config has multiple
|
|
125
|
+
entries or a custom server name.
|
|
126
|
+
|
|
114
127
|
Under the hood: a logged-in browser session mints an `A2A_SECRET`-signed JWT
|
|
115
128
|
carrying the caller's `sub` + `org_domain` and a unique `jti`, so tool runs
|
|
116
129
|
stay tenant-scoped via `runWithRequestContext`. The existing
|
|
@@ -339,14 +352,17 @@ and mutating actions are filtered out (`filterPublicAgentActions`). The full
|
|
|
339
352
|
surface appears when authenticated as a real caller: a deployed /
|
|
340
353
|
`AGENT_MODE=production` app, or a local app reached through `connect` /
|
|
341
354
|
`agent-native mcp install` (which provisions an identity-bearing token). A
|
|
342
|
-
sparse `tools/list`
|
|
343
|
-
|
|
355
|
+
sparse or empty `tools/list` is diagnostic, not proof of auth failure: check
|
|
356
|
+
OAuth scopes, compact-catalog filtering, and the client/server auth status
|
|
357
|
+
before telling the user they are unauthenticated.
|
|
344
358
|
|
|
345
359
|
## Do
|
|
346
360
|
|
|
347
361
|
- Do connect local/fallback clients to Dispatch with
|
|
348
|
-
`npx @agent-native/core connect https://dispatch.agent-native.com`;
|
|
349
|
-
|
|
362
|
+
`npx @agent-native/core@latest connect https://dispatch.agent-native.com`;
|
|
363
|
+
use `npx @agent-native/core@latest reconnect ...` for reauth without
|
|
364
|
+
reinstalling; use a direct app URL only when the host should be isolated to
|
|
365
|
+
one app.
|
|
350
366
|
- Do add a `link` builder to any action that produces or lists a navigable
|
|
351
367
|
resource (draft, event, dashboard, document).
|
|
352
368
|
- Do add `mcpApp` when a UI-capable MCP host should render an inline review or
|