@agent-native/core 0.101.7 → 0.101.11
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/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +28 -0
- package/corpus/core/docs/content/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/ar-SA/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/de-DE/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/es-ES/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/fr-FR/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/hi-IN/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/ja-JP/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/ko-KR/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/pt-BR/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/zh-CN/deployment.mdx +9 -7
- package/corpus/core/docs/content/locales/zh-TW/deployment.mdx +9 -7
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/client.ts +23 -1
- package/corpus/core/src/a2a/handlers.ts +65 -1
- package/corpus/core/src/a2a/server.ts +124 -0
- package/corpus/core/src/a2a/task-store.ts +253 -0
- package/corpus/core/src/a2a/types.ts +26 -0
- package/corpus/core/src/agent/production-agent.ts +5 -2
- package/corpus/core/src/client/review/ReviewCommentComposer.tsx +9 -1
- package/corpus/core/src/client/review/ReviewThreadPanel.tsx +17 -2
- package/corpus/core/src/client/settings/SettingsPanel.tsx +41 -21
- package/corpus/core/src/client/settings/useBuilderStatus.ts +28 -9
- package/corpus/core/src/client/setup-connections/BuilderConnectCard.tsx +2 -7
- package/corpus/core/src/mcp/builtin-tools.ts +25 -1
- package/corpus/core/src/scripts/call-agent.ts +22 -3
- package/corpus/core/src/server/agent-chat-plugin.ts +86 -1
- package/corpus/core/src/server/auth.ts +25 -1
- package/corpus/core/src/server/builder-browser.ts +353 -1
- package/corpus/core/src/server/core-routes-plugin.ts +601 -223
- package/corpus/core/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +25 -0
- package/corpus/templates/calendar/app/components/calendar/DayView.tsx +80 -1
- package/corpus/templates/calendar/app/components/calendar/EventCard.tsx +28 -3
- package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +5 -1
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +30 -3
- package/corpus/templates/calendar/app/components/calendar/OutOfOfficeEvent.tsx +196 -0
- package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +115 -1
- package/corpus/templates/calendar/app/lib/out-of-office.ts +45 -0
- package/corpus/templates/calendar/changelog/2026-07-14-out-of-office-blocks-now-sit-behind-meetings-and-event-stack.md +6 -0
- package/corpus/templates/content/.agents/skills/document-editing/SKILL.md +23 -0
- package/corpus/templates/content/AGENTS.md +3 -2
- package/corpus/templates/content/actions/_builder-cms-intent-lookup.ts +87 -0
- package/corpus/templates/content/actions/_builder-cms-read-client.ts +173 -25
- package/corpus/templates/content/actions/_builder-cms-source-adapter.ts +1 -0
- package/corpus/templates/content/actions/_builder-cms-write-adapter.ts +427 -18
- package/corpus/templates/content/actions/_builder-cms-write-client.ts +17 -73
- package/corpus/templates/content/actions/_builder-cms-write-settings.ts +27 -7
- package/corpus/templates/content/actions/_builder-source-execution-claim.ts +124 -0
- package/corpus/templates/content/actions/_builder-source-execution-preservation.ts +21 -0
- package/corpus/templates/content/actions/_builder-source-timings.ts +57 -0
- package/corpus/templates/content/actions/_database-source-utils.ts +1041 -154
- package/corpus/templates/content/actions/_database-utils.ts +89 -12
- package/corpus/templates/content/actions/_preview-document-draft.ts +31 -0
- package/corpus/templates/content/actions/cancel-prepared-builder-source-update.ts +309 -0
- package/corpus/templates/content/actions/execute-builder-source-batch.ts +120 -49
- package/corpus/templates/content/actions/execute-builder-source-execution.ts +608 -264
- package/corpus/templates/content/actions/get-preview-document-draft.ts +26 -0
- package/corpus/templates/content/actions/materialize-builder-required-fields.ts +550 -0
- package/corpus/templates/content/actions/prepare-builder-source-execution.ts +112 -65
- package/corpus/templates/content/actions/prepare-builder-source-review.ts +463 -170
- package/corpus/templates/content/actions/preview-builder-source-review.ts +99 -0
- package/corpus/templates/content/actions/set-content-database-source-write-mode.ts +2 -2
- package/corpus/templates/content/actions/update-preview-document-draft.ts +153 -0
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +27 -15
- package/corpus/templates/content/app/components/editor/MathRenderer.tsx +43 -0
- package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +260 -4
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +57 -5
- package/corpus/templates/content/app/components/editor/body-hydration.ts +27 -0
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +2353 -576
- package/corpus/templates/content/app/components/editor/database/settings.tsx +13 -10
- package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +358 -75
- package/corpus/templates/content/app/components/editor/extensions/NotionExtensions.tsx +78 -0
- package/corpus/templates/content/app/components/editor/previewDocumentSaveController.ts +124 -29
- package/corpus/templates/content/app/components/editor/previewDocumentSaveRegistry.ts +2 -0
- package/corpus/templates/content/app/global.css +71 -1
- package/corpus/templates/content/app/hooks/use-content-database.ts +68 -0
- package/corpus/templates/content/app/hooks/use-documents.ts +53 -4
- package/corpus/templates/content/app/i18n/zh-TW.ts +38 -0
- package/corpus/templates/content/app/i18n-data.ts +450 -0
- package/corpus/templates/content/app/root.tsx +2 -0
- package/corpus/templates/content/changelog/2026-07-10-builder-backed-preview-edits-are-preserved-when-article-bodi.md +6 -0
- package/corpus/templates/content/changelog/2026-07-10-builder-cms-sources-can-now-be-enabled-for-guarded-staged-or.md +6 -0
- package/corpus/templates/content/changelog/2026-07-13-builder-connection-setup-is-now-available-directly-from-conn.md +6 -0
- package/corpus/templates/content/changelog/2026-07-13-builder-reviews-now-hide-already-applied-writes-detect-nativ.md +6 -0
- package/corpus/templates/content/changelog/2026-07-13-required-builder-publishing-fields-can-now-be-added-directly.md +6 -0
- package/corpus/templates/content/changelog/2026-07-13-you-can-now-cancel-a-prepared-builder-update-safely-before-i.md +6 -0
- package/corpus/templates/content/changelog/2026-07-14-builder-account-connection-now-remains-available-when-branch.md +6 -0
- package/corpus/templates/content/changelog/2026-07-14-builder-backed-articles-now-preserve-fifth-and-sixth-level-h.md +6 -0
- package/corpus/templates/content/changelog/2026-07-14-builder-sources-now-review-and-publish-rich-article-updates-.md +6 -0
- package/corpus/templates/content/changelog/2026-07-14-builder-writes-now-stop-retries-when-reconciliation-is-needed.md +6 -0
- package/corpus/templates/content/changelog/2026-07-14-file-and-media-links-now-validate-before-saving.md +6 -0
- package/corpus/templates/content/changelog/2026-07-14-latex-equations-render-in-documents-public-pages-and-exports.md +6 -0
- package/corpus/templates/content/package.json +1 -0
- package/corpus/templates/content/parity/matrix.md +27 -25
- package/corpus/templates/content/parity/matrix.ts +50 -2
- package/corpus/templates/content/server/db/schema.ts +47 -0
- package/corpus/templates/content/server/plugins/auth.ts +1 -0
- package/corpus/templates/content/server/plugins/db.ts +53 -0
- package/corpus/templates/content/shared/api.ts +70 -1
- package/corpus/templates/content/shared/builder-mdx.ts +705 -15
- package/corpus/templates/content/shared/document-export.ts +296 -25
- package/corpus/templates/content/shared/inline-math.ts +128 -0
- package/corpus/templates/content/shared/math-rendering.ts +50 -0
- package/corpus/templates/content/shared/nfm.ts +20 -17
- package/corpus/templates/content/vite.config.ts +0 -1
- package/corpus/templates/design/app/components/design/ReadOnlyDesignBanner.tsx +52 -7
- package/corpus/templates/design/app/components/design/ReviewCommentsPanel.tsx +36 -4
- package/corpus/templates/design/app/components/visual-editor/ReviewCanvasPins.tsx +4 -1
- package/corpus/templates/design/app/i18n/ar-SA.ts +1 -0
- package/corpus/templates/design/app/i18n/de-DE.ts +1 -0
- package/corpus/templates/design/app/i18n/en-US.ts +1 -0
- package/corpus/templates/design/app/i18n/es-ES.ts +1 -0
- package/corpus/templates/design/app/i18n/fr-FR.ts +1 -0
- package/corpus/templates/design/app/i18n/hi-IN.ts +1 -0
- package/corpus/templates/design/app/i18n/ja-JP.ts +1 -0
- package/corpus/templates/design/app/i18n/ko-KR.ts +1 -0
- package/corpus/templates/design/app/i18n/pt-BR.ts +1 -0
- package/corpus/templates/design/app/i18n/zh-CN.ts +1 -0
- package/corpus/templates/design/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/design/app/pages/design-editor/selection-state.ts +8 -0
- package/corpus/templates/design/app/pages/design-editor/tool-state.ts +11 -0
- package/corpus/templates/design/changelog/2026-07-14-reviewers-can-now-pin-comments-from-the-canvas-toolbar-targe.md +6 -0
- package/corpus/templates/design/changelog/2026-07-15-viewer-comment-controls-now-sit-inside-the-read-only-notice-.md +6 -0
- package/corpus/templates/design/shared/review-anchor.ts +48 -0
- package/corpus/templates/mail/.agents/skills/email-drafts/SKILL.md +17 -0
- package/corpus/templates/mail/AGENTS.md +21 -19
- package/corpus/templates/mail/actions/create-attachment-upload.ts +49 -0
- package/corpus/templates/mail/changelog/2026-07-14-connected-agents-can-now-securely-upload-local-files-for-mai.md +6 -0
- package/corpus/templates/mail/server/handlers/media.ts +84 -61
- package/corpus/templates/mail/server/lib/attachment-upload-ticket.ts +234 -0
- package/corpus/templates/mail/server/lib/mail-connector-catalog.ts +8 -4
- package/corpus/templates/mail/server/lib/media-upload.ts +114 -0
- package/corpus/templates/mail/server/plugins/auth.ts +9 -1
- package/corpus/templates/mail/server/routes/api/media/attachment-upload/[uploadId].put.ts +1 -0
- package/corpus/templates/plan/server/plan-mdx.ts +53 -7
- package/dist/a2a/client.d.ts +5 -1
- package/dist/a2a/client.d.ts.map +1 -1
- package/dist/a2a/client.js +18 -1
- package/dist/a2a/client.js.map +1 -1
- package/dist/a2a/handlers.d.ts.map +1 -1
- package/dist/a2a/handlers.js +57 -8
- package/dist/a2a/handlers.js.map +1 -1
- package/dist/a2a/server.d.ts.map +1 -1
- package/dist/a2a/server.js +87 -1
- package/dist/a2a/server.js.map +1 -1
- package/dist/a2a/task-store.d.ts +27 -0
- package/dist/a2a/task-store.d.ts.map +1 -1
- package/dist/a2a/task-store.js +216 -0
- package/dist/a2a/task-store.js.map +1 -1
- package/dist/a2a/types.d.ts +24 -0
- package/dist/a2a/types.d.ts.map +1 -1
- package/dist/a2a/types.js.map +1 -1
- package/dist/agent/production-agent.d.ts +1 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +5 -2
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/client/review/ReviewCommentComposer.d.ts +2 -1
- package/dist/client/review/ReviewCommentComposer.d.ts.map +1 -1
- package/dist/client/review/ReviewCommentComposer.js +3 -3
- package/dist/client/review/ReviewCommentComposer.js.map +1 -1
- package/dist/client/review/ReviewThreadPanel.d.ts +9 -1
- package/dist/client/review/ReviewThreadPanel.d.ts.map +1 -1
- package/dist/client/review/ReviewThreadPanel.js +6 -4
- package/dist/client/review/ReviewThreadPanel.js.map +1 -1
- package/dist/client/settings/SettingsPanel.d.ts +3 -0
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +11 -4
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/settings/useBuilderStatus.d.ts +8 -4
- package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
- package/dist/client/settings/useBuilderStatus.js +19 -6
- package/dist/client/settings/useBuilderStatus.js.map +1 -1
- package/dist/client/setup-connections/BuilderConnectCard.d.ts.map +1 -1
- package/dist/client/setup-connections/BuilderConnectCard.js +2 -5
- package/dist/client/setup-connections/BuilderConnectCard.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/mcp/builtin-tools.d.ts.map +1 -1
- package/dist/mcp/builtin-tools.js +22 -3
- package/dist/mcp/builtin-tools.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/scripts/call-agent.d.ts +1 -1
- package/dist/scripts/call-agent.d.ts.map +1 -1
- package/dist/scripts/call-agent.js +19 -1
- package/dist/scripts/call-agent.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +71 -2
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.d.ts +1 -0
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +19 -2
- package/dist/server/auth.js.map +1 -1
- package/dist/server/builder-browser.d.ts +78 -0
- package/dist/server/builder-browser.d.ts.map +1 -1
- package/dist/server/builder-browser.js +246 -1
- package/dist/server/builder-browser.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts +41 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +260 -14
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +25 -0
- package/docs/content/deployment.mdx +9 -7
- package/docs/content/locales/ar-SA/deployment.mdx +9 -7
- package/docs/content/locales/de-DE/deployment.mdx +9 -7
- package/docs/content/locales/es-ES/deployment.mdx +9 -7
- package/docs/content/locales/fr-FR/deployment.mdx +9 -7
- package/docs/content/locales/hi-IN/deployment.mdx +9 -7
- package/docs/content/locales/ja-JP/deployment.mdx +9 -7
- package/docs/content/locales/ko-KR/deployment.mdx +9 -7
- package/docs/content/locales/pt-BR/deployment.mdx +9 -7
- package/docs/content/locales/zh-CN/deployment.mdx +9 -7
- package/docs/content/locales/zh-TW/deployment.mdx +9 -7
- package/package.json +2 -2
- package/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +25 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineEventHandler, setResponseStatus, setResponseHeader, getMethod, getHeader, getCookie, setCookie, deleteCookie, getRequestURL, getRequestIP, } from "h3";
|
|
1
|
+
import { assertBodySize, defineEventHandler, setResponseStatus, setResponseHeader, getMethod, getHeader, getCookie, setCookie, deleteCookie, getRequestURL, getRequestIP, readRawBody, } from "h3";
|
|
2
2
|
import { readMultipartFormData } from "h3";
|
|
3
3
|
import { DEFAULT_MODEL } from "../agent/default-model.js";
|
|
4
4
|
import { registerBuiltinEngines } from "../agent/engine/builtin.js";
|
|
@@ -10,6 +10,7 @@ import { mountBrowserSessionRoutes } from "../browser-sessions/routes.js";
|
|
|
10
10
|
import { mountDbAdminRoutes } from "../db-admin/routes.js";
|
|
11
11
|
import { getDbExec } from "../db/client.js";
|
|
12
12
|
import { getDatabaseRuntimeFingerprint, getRuntimeDebugFingerprint, runDatabaseSchemaHealthCheck, } from "../db/runtime-diagnostics.js";
|
|
13
|
+
import { ssrfSafeFetch } from "../extensions/url-safety.js";
|
|
13
14
|
import { uploadFile, getActiveFileUploadProviderForRequest, listFileUploadProviders, } from "../file-upload/index.js";
|
|
14
15
|
import { handleMcpConnect } from "../mcp/connect-route.js";
|
|
15
16
|
import { handleMcpOAuth, handleMcpOAuthAuthorizationServerMetadata, handleMcpOAuthProtectedResourceMetadata, } from "../mcp/oauth-route.js";
|
|
@@ -34,7 +35,7 @@ import { createAgentEngineApiKeyHandler } from "./agent-engine-api-key-route.js"
|
|
|
34
35
|
import { getConfiguredAppBasePath, stripAppBasePath } from "./app-base-path.js";
|
|
35
36
|
import { getAppName } from "./app-name.js";
|
|
36
37
|
import { getSession } from "./auth.js";
|
|
37
|
-
import { BUILDER_CONNECT_PARAM, BUILDER_CONNECT_OWNER_COOKIE, BUILDER_ENV_KEYS, BUILDER_OPENER_PARAM, BUILDER_STATE_PARAM, appendBuilderConnectToken, builderConnectTrackingProperties, buildBuilderCliAuthUrl, createBuilderBrowserCallbackErrorPage, createBuilderBrowserCallbackPage, getBuilderConnectTrackingParams, getBuilderCliAuthCallbackOriginForEvent, getBuilderBrowserOriginForEvent, resolveBuilderCallbackReturnUrl, getBuilderBrowserStatusForEvent, resolveBuilderBranchProjectId, resolveSafePreviewUrl, runBuilderAgent, signBuilderCallbackState, verifyBuilderConnectTokenAndGetOwner, verifyBuilderCallbackStateAndGetOwner, signBuilderConnectToken, } from "./builder-browser.js";
|
|
38
|
+
import { BUILDER_CONNECT_PARAM, BUILDER_CONNECT_OWNER_COOKIE, BUILDER_ENV_KEYS, BUILDER_OPENER_PARAM, BUILDER_RELAY_FLOW_HEADER, BUILDER_RELAY_SIGNATURE_HEADER, BUILDER_RELAY_STATE_PARAM, BUILDER_RELAY_TIMESTAMP_HEADER, BUILDER_STATE_PARAM, appendBuilderConnectToken, builderConnectTrackingProperties, buildBuilderCliAuthUrl, createBuilderBrowserCallbackErrorPage, createBuilderBrowserCallbackPage, createBuilderRelayRequest, getBuilderConnectTrackingParams, getBuilderCliAuthCallbackOriginForEvent, getBuilderBrowserOriginForEvent, resolveBuilderCallbackReturnUrl, getBuilderBrowserStatusForEvent, resolveBuilderBranchProjectId, resolveSafePreviewUrl, runBuilderAgent, signBuilderCallbackState, signBuilderPreviewRelayState, verifyBuilderRelayRequest, verifyBuilderPreviewRelayStateForCallback, verifyBuilderConnectTokenAndGetOwner, verifyBuilderCallbackStateAndGetOwner, signBuilderConnectToken, } from "./builder-browser.js";
|
|
38
39
|
import { captureError } from "./capture-error.js";
|
|
39
40
|
import { getAllowedCorsOrigin, readCorsAllowedOrigins, } from "./cors-origins.js";
|
|
40
41
|
import { canUseDeployCredentialFallbackForRequest, readDeployCredentialEnv, resolveSecret, } from "./credential-provider.js";
|
|
@@ -161,6 +162,15 @@ export function resolveFrameworkSseRoutes(sseRoute) {
|
|
|
161
162
|
LEGACY_FRAMEWORK_EVENTS_ROUTE,
|
|
162
163
|
]));
|
|
163
164
|
}
|
|
165
|
+
export const BUILDER_STATUS_ROUTE_SUFFIXES = [
|
|
166
|
+
"/builder/status",
|
|
167
|
+
"/connection-status/builder",
|
|
168
|
+
];
|
|
169
|
+
export function mountBuilderStatusRouteAliases(mount, prefix, handler) {
|
|
170
|
+
for (const routeSuffix of BUILDER_STATUS_ROUTE_SUFFIXES) {
|
|
171
|
+
mount(`${prefix}${routeSuffix}`, handler);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
164
174
|
registerBuiltinEngines();
|
|
165
175
|
function cleanBuilderWaitlistText(value, maxLength = BUILDER_WAITLIST_TEXT_LIMIT) {
|
|
166
176
|
if (typeof value !== "string")
|
|
@@ -532,6 +542,90 @@ export function getFrameworkRouteRequestUrl(event) {
|
|
|
532
542
|
url.search = rawUrl.slice(queryStart);
|
|
533
543
|
return url;
|
|
534
544
|
}
|
|
545
|
+
function builderRelayPendingKey(flowId) {
|
|
546
|
+
return `builder-pending-relay:${flowId}`;
|
|
547
|
+
}
|
|
548
|
+
function parseBuilderRelayPendingRecord(value) {
|
|
549
|
+
if (!value ||
|
|
550
|
+
typeof value.ownerEmail !== "string" ||
|
|
551
|
+
typeof value.targetOrigin !== "string" ||
|
|
552
|
+
typeof value.basePath !== "string" ||
|
|
553
|
+
typeof value.expiresAt !== "number") {
|
|
554
|
+
return null;
|
|
555
|
+
}
|
|
556
|
+
return {
|
|
557
|
+
ownerEmail: value.ownerEmail,
|
|
558
|
+
orgId: typeof value.orgId === "string" ? value.orgId : null,
|
|
559
|
+
role: typeof value.role === "string" ? value.role : null,
|
|
560
|
+
targetOrigin: value.targetOrigin,
|
|
561
|
+
basePath: value.basePath,
|
|
562
|
+
expiresAt: value.expiresAt,
|
|
563
|
+
tracking: value.tracking && typeof value.tracking === "object"
|
|
564
|
+
? value.tracking
|
|
565
|
+
: undefined,
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Authenticated one-shot receiver for the second hop of Builder preview auth.
|
|
570
|
+
* Owner and org scope always come from the preview's pending record; the
|
|
571
|
+
* corporate callback cannot choose them in its POST body.
|
|
572
|
+
*/
|
|
573
|
+
export async function consumeBuilderRelayRequest(input, dependencies) {
|
|
574
|
+
if (input.rawBody.length > 64 * 1024) {
|
|
575
|
+
return {
|
|
576
|
+
ok: false,
|
|
577
|
+
status: 413,
|
|
578
|
+
error: "Builder relay request is too large",
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
let verified;
|
|
582
|
+
try {
|
|
583
|
+
verified = verifyBuilderRelayRequest({
|
|
584
|
+
body: input.rawBody,
|
|
585
|
+
timestamp: input.timestamp,
|
|
586
|
+
flowId: input.flowId,
|
|
587
|
+
signature: input.signature,
|
|
588
|
+
requestOrigin: input.requestOrigin,
|
|
589
|
+
requestBasePath: input.requestBasePath,
|
|
590
|
+
now: input.now,
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
catch {
|
|
594
|
+
return { ok: false, status: 503, error: "Builder relay is not configured" };
|
|
595
|
+
}
|
|
596
|
+
if (!verified) {
|
|
597
|
+
return { ok: false, status: 401, error: "Invalid Builder relay request" };
|
|
598
|
+
}
|
|
599
|
+
const pendingKey = builderRelayPendingKey(verified.payload.flowId);
|
|
600
|
+
const pending = parseBuilderRelayPendingRecord(await dependencies.getPending(pendingKey).catch(() => null));
|
|
601
|
+
const now = input.now ?? Date.now();
|
|
602
|
+
if (!pending ||
|
|
603
|
+
pending.expiresAt < now ||
|
|
604
|
+
pending.ownerEmail !== verified.payload.ownerEmail ||
|
|
605
|
+
pending.targetOrigin !== verified.payload.targetOrigin ||
|
|
606
|
+
pending.basePath !== verified.payload.basePath) {
|
|
607
|
+
return { ok: false, status: 403, error: "No active Builder relay flow" };
|
|
608
|
+
}
|
|
609
|
+
// A successful delete, not merely a resolved promise, is the one-shot gate.
|
|
610
|
+
// It happens before credential persistence so replay is impossible even if
|
|
611
|
+
// the downstream write fails and the human has to start a fresh flow.
|
|
612
|
+
const consumed = await dependencies
|
|
613
|
+
.deletePending(pendingKey)
|
|
614
|
+
.catch(() => false);
|
|
615
|
+
if (consumed !== true) {
|
|
616
|
+
return {
|
|
617
|
+
ok: false,
|
|
618
|
+
status: 409,
|
|
619
|
+
error: "Builder relay flow was already consumed",
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
await dependencies.writeCredentials(pending.ownerEmail, verified.body.credentials, { orgId: pending.orgId, role: pending.role });
|
|
623
|
+
return { ok: true };
|
|
624
|
+
}
|
|
625
|
+
export async function readBuilderRelayRequestBody(event) {
|
|
626
|
+
await assertBodySize(event, 64 * 1024);
|
|
627
|
+
return (await readRawBody(event, "utf8")) ?? "";
|
|
628
|
+
}
|
|
535
629
|
function redactValues(text, values) {
|
|
536
630
|
let out = text;
|
|
537
631
|
for (const value of values) {
|
|
@@ -943,7 +1037,7 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
943
1037
|
// db-admin/routes.ts), so on a deployed / production app it always 403s.
|
|
944
1038
|
mountDbAdminRoutes(nitroApp, { routePrefix: P });
|
|
945
1039
|
const resolveBuilderOwnerContext = async (event, mode) => resolveBuilderOwnerContextForRequest(event, { anonymousOwner: options.anonymousOwner }, mode);
|
|
946
|
-
|
|
1040
|
+
const builderStatusHandler = defineEventHandler(async (event) => {
|
|
947
1041
|
const envStatus = getBuilderBrowserStatusForEvent(event);
|
|
948
1042
|
const ownerContext = await resolveBuilderOwnerContext(event);
|
|
949
1043
|
const userEmail = ownerContext.email;
|
|
@@ -1105,9 +1199,7 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
1105
1199
|
envStatus.subscriptionName ||
|
|
1106
1200
|
undefined,
|
|
1107
1201
|
isEnterprise: creds.isEnterprise ?? envStatus.isEnterprise ?? undefined,
|
|
1108
|
-
isFreeAccount: creds.isFreeAccount ??
|
|
1109
|
-
envStatus.isFreeAccount ??
|
|
1110
|
-
undefined,
|
|
1202
|
+
isFreeAccount: creds.isFreeAccount ?? envStatus.isFreeAccount ?? undefined,
|
|
1111
1203
|
credentialSource: credentialSource ?? undefined,
|
|
1112
1204
|
});
|
|
1113
1205
|
}
|
|
@@ -1156,7 +1248,8 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
1156
1248
|
isFreeAccount: undefined,
|
|
1157
1249
|
});
|
|
1158
1250
|
});
|
|
1159
|
-
})
|
|
1251
|
+
});
|
|
1252
|
+
mountBuilderStatusRouteAliases((path, handler) => getH3App(nitroApp).use(path, handler), P, builderStatusHandler);
|
|
1160
1253
|
// How long a pending-connect row is valid. Must be long enough for
|
|
1161
1254
|
// the user to complete the Builder CLI-auth flow, but short enough
|
|
1162
1255
|
// that a stale row from an abandoned attempt doesn't accept a new
|
|
@@ -1279,15 +1372,65 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
1279
1372
|
catch {
|
|
1280
1373
|
// No prior error row — fine
|
|
1281
1374
|
}
|
|
1375
|
+
const previewOrigin = getBuilderBrowserOriginForEvent(event).replace(/\/+$/, "");
|
|
1376
|
+
const callbackOrigin = getBuilderCliAuthCallbackOriginForEvent(event).replace(/\/+$/, "");
|
|
1377
|
+
let relay;
|
|
1378
|
+
if (previewOrigin !== callbackOrigin) {
|
|
1379
|
+
try {
|
|
1380
|
+
relay = signBuilderPreviewRelayState({
|
|
1381
|
+
ownerEmail,
|
|
1382
|
+
targetOrigin: previewOrigin,
|
|
1383
|
+
basePath: getAppBasePath(),
|
|
1384
|
+
});
|
|
1385
|
+
}
|
|
1386
|
+
catch (err) {
|
|
1387
|
+
const msg = err instanceof Error
|
|
1388
|
+
? err.message
|
|
1389
|
+
: "Builder preview relay is not configured.";
|
|
1390
|
+
setResponseStatus(event, 503);
|
|
1391
|
+
setResponseHeader(event, "Content-Type", "text/html; charset=utf-8");
|
|
1392
|
+
return createBuilderBrowserCallbackErrorPage(msg, {
|
|
1393
|
+
title: "Builder preview connection isn't configured",
|
|
1394
|
+
body: "This preview needs its secure Builder callback relay configured before authorization can start.",
|
|
1395
|
+
closeHint: "Close this popup and ask the preview owner to finish Builder relay setup.",
|
|
1396
|
+
parentOrigin: previewOrigin,
|
|
1397
|
+
});
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
let pendingOrgId = null;
|
|
1401
|
+
let pendingRole = null;
|
|
1402
|
+
if (!ownerContext.anonymous) {
|
|
1403
|
+
try {
|
|
1404
|
+
const orgContext = await getOrgContext(event);
|
|
1405
|
+
pendingOrgId = orgContext.orgId ?? null;
|
|
1406
|
+
pendingRole = orgContext.role ?? null;
|
|
1407
|
+
}
|
|
1408
|
+
catch {
|
|
1409
|
+
// The pending owner remains user-scoped when org context is absent.
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1282
1412
|
// Store a short-lived pending row. If the DB is unavailable we
|
|
1283
1413
|
// surface a popup-renderable error page that signals the parent
|
|
1284
1414
|
// via BroadcastChannel, rather than letting the popup show raw
|
|
1285
1415
|
// JSON and the parent poll for 5 minutes.
|
|
1286
1416
|
try {
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1417
|
+
if (relay) {
|
|
1418
|
+
await putSetting(builderRelayPendingKey(relay.payload.flowId), {
|
|
1419
|
+
ownerEmail,
|
|
1420
|
+
orgId: pendingOrgId,
|
|
1421
|
+
role: pendingRole,
|
|
1422
|
+
targetOrigin: relay.payload.targetOrigin,
|
|
1423
|
+
basePath: relay.payload.basePath,
|
|
1424
|
+
expiresAt: relay.payload.exp,
|
|
1425
|
+
tracking: connectTracking,
|
|
1426
|
+
});
|
|
1427
|
+
}
|
|
1428
|
+
else {
|
|
1429
|
+
await putSetting(`builder-pending-connect:${ownerEmail}`, {
|
|
1430
|
+
expiresAt: Date.now() + BUILDER_CONNECT_PENDING_TTL_MS,
|
|
1431
|
+
tracking: connectTracking,
|
|
1432
|
+
});
|
|
1433
|
+
}
|
|
1291
1434
|
}
|
|
1292
1435
|
catch (err) {
|
|
1293
1436
|
await trackBuilderLifecycle(event, "builder connect failed", ownerEmail, {
|
|
@@ -1319,8 +1462,9 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
1319
1462
|
// from /builder/status. Keep this legacy trampoline working for older
|
|
1320
1463
|
// clients, but still send it to Builder immediately and include signed
|
|
1321
1464
|
// callback state so the callback does not depend on popup cookies.
|
|
1322
|
-
const cliAuthUrl = buildBuilderCliAuthUrl(
|
|
1323
|
-
previewOrigin
|
|
1465
|
+
const cliAuthUrl = buildBuilderCliAuthUrl(callbackOrigin, signBuilderCallbackState(ownerEmail), {
|
|
1466
|
+
previewOrigin,
|
|
1467
|
+
relayState: relay?.state,
|
|
1324
1468
|
tracking: connectTracking,
|
|
1325
1469
|
});
|
|
1326
1470
|
setResponseStatus(event, 302);
|
|
@@ -1449,11 +1593,114 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
1449
1593
|
});
|
|
1450
1594
|
return { ok: true, formSubmitted: formSubmission.submitted };
|
|
1451
1595
|
}));
|
|
1596
|
+
getH3App(nitroApp).use(`${P}/builder/relay`, defineEventHandler(async (event) => {
|
|
1597
|
+
if (getMethod(event) !== "POST") {
|
|
1598
|
+
setResponseStatus(event, 405);
|
|
1599
|
+
return { error: "Method not allowed" };
|
|
1600
|
+
}
|
|
1601
|
+
const rawBody = await readBuilderRelayRequestBody(event);
|
|
1602
|
+
const result = await consumeBuilderRelayRequest({
|
|
1603
|
+
rawBody,
|
|
1604
|
+
timestamp: getHeader(event, BUILDER_RELAY_TIMESTAMP_HEADER),
|
|
1605
|
+
flowId: getHeader(event, BUILDER_RELAY_FLOW_HEADER),
|
|
1606
|
+
signature: getHeader(event, BUILDER_RELAY_SIGNATURE_HEADER),
|
|
1607
|
+
requestOrigin: getFrameworkRouteRequestUrl(event).origin,
|
|
1608
|
+
requestBasePath: getAppBasePath(),
|
|
1609
|
+
}, {
|
|
1610
|
+
getPending: getSetting,
|
|
1611
|
+
deletePending: deleteSetting,
|
|
1612
|
+
writeCredentials: async (ownerEmail, credentials, scope) => {
|
|
1613
|
+
const { writeBuilderCredentials } = await import("./credential-provider.js");
|
|
1614
|
+
await writeBuilderCredentials(ownerEmail, credentials, scope);
|
|
1615
|
+
await Promise.all([
|
|
1616
|
+
deleteSetting("builder-disconnected").catch(() => false),
|
|
1617
|
+
deleteSetting(`builder-connect-error:${ownerEmail}`).catch(() => false),
|
|
1618
|
+
]);
|
|
1619
|
+
},
|
|
1620
|
+
}).catch(() => ({
|
|
1621
|
+
ok: false,
|
|
1622
|
+
status: 500,
|
|
1623
|
+
error: "Builder relay credential persistence failed",
|
|
1624
|
+
}));
|
|
1625
|
+
if (!result.ok) {
|
|
1626
|
+
setResponseStatus(event, result.status);
|
|
1627
|
+
return { error: result.error };
|
|
1628
|
+
}
|
|
1629
|
+
return { ok: true };
|
|
1630
|
+
}));
|
|
1452
1631
|
getH3App(nitroApp).use(`${P}/builder/callback`, defineEventHandler(async (event) => {
|
|
1453
1632
|
if (getMethod(event) !== "GET") {
|
|
1454
1633
|
setResponseStatus(event, 405);
|
|
1455
1634
|
return { error: "Method not allowed" };
|
|
1456
1635
|
}
|
|
1636
|
+
// Builder's provider contract puts credentials on this first-hop
|
|
1637
|
+
// URL. Keep the response out of caches and suppress referrer
|
|
1638
|
+
// propagation even though the second hop carries secrets only in
|
|
1639
|
+
// its authenticated POST body.
|
|
1640
|
+
setResponseHeader(event, "Cache-Control", "no-store");
|
|
1641
|
+
setResponseHeader(event, "Referrer-Policy", "no-referrer");
|
|
1642
|
+
const requestUrl = getFrameworkRouteRequestUrl(event);
|
|
1643
|
+
const relayStateRaw = requestUrl.searchParams.get(BUILDER_RELAY_STATE_PARAM);
|
|
1644
|
+
if (relayStateRaw) {
|
|
1645
|
+
let relayPayload = null;
|
|
1646
|
+
try {
|
|
1647
|
+
relayPayload =
|
|
1648
|
+
verifyBuilderPreviewRelayStateForCallback(relayStateRaw);
|
|
1649
|
+
}
|
|
1650
|
+
catch {
|
|
1651
|
+
// A preview relay must fail closed when its dedicated shared
|
|
1652
|
+
// secret is absent on the corporate callback deployment.
|
|
1653
|
+
}
|
|
1654
|
+
if (!relayPayload) {
|
|
1655
|
+
setResponseStatus(event, 403);
|
|
1656
|
+
setResponseHeader(event, "Content-Type", "text/html; charset=utf-8");
|
|
1657
|
+
return createBuilderBrowserCallbackErrorPage("Builder preview relay state is invalid or expired.");
|
|
1658
|
+
}
|
|
1659
|
+
const privateKey = requestUrl.searchParams.get("p-key");
|
|
1660
|
+
const publicKey = requestUrl.searchParams.get("api-key");
|
|
1661
|
+
if (!privateKey || !publicKey) {
|
|
1662
|
+
setResponseStatus(event, 400);
|
|
1663
|
+
setResponseHeader(event, "Content-Type", "text/html; charset=utf-8");
|
|
1664
|
+
return createBuilderBrowserCallbackErrorPage("Builder didn't return credentials. Restart the connect flow from settings.", { parentOrigin: relayPayload.targetOrigin });
|
|
1665
|
+
}
|
|
1666
|
+
const credentials = {
|
|
1667
|
+
privateKey,
|
|
1668
|
+
publicKey,
|
|
1669
|
+
userId: requestUrl.searchParams.get("user-id"),
|
|
1670
|
+
orgName: requestUrl.searchParams.get("org-name"),
|
|
1671
|
+
orgKind: requestUrl.searchParams.get("kind"),
|
|
1672
|
+
subscription: requestUrl.searchParams.get("subscription"),
|
|
1673
|
+
subscriptionLevel: requestUrl.searchParams.get("subscription-level"),
|
|
1674
|
+
subscriptionName: requestUrl.searchParams.get("subscription-name"),
|
|
1675
|
+
isEnterprise: parseBuilderCallbackBoolean(requestUrl.searchParams.get("is-enterprise")),
|
|
1676
|
+
isFreeAccount: parseBuilderCallbackBoolean(requestUrl.searchParams.get("is-free-account")),
|
|
1677
|
+
};
|
|
1678
|
+
try {
|
|
1679
|
+
const relayRequest = createBuilderRelayRequest(relayStateRaw, credentials);
|
|
1680
|
+
const response = await ssrfSafeFetch(relayRequest.url, {
|
|
1681
|
+
method: "POST",
|
|
1682
|
+
headers: relayRequest.headers,
|
|
1683
|
+
body: relayRequest.body,
|
|
1684
|
+
}, { maxRedirects: 0, httpsOnly: true });
|
|
1685
|
+
if (!response.ok) {
|
|
1686
|
+
throw new Error(`Preview relay rejected the callback (${response.status}).`);
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
catch (err) {
|
|
1690
|
+
const message = err instanceof Error
|
|
1691
|
+
? err.message
|
|
1692
|
+
: "Builder preview relay failed.";
|
|
1693
|
+
// Never log the first-hop URL or relay body: both contain
|
|
1694
|
+
// credentials. The popup gets a bounded, credential-free error.
|
|
1695
|
+
setResponseStatus(event, 502);
|
|
1696
|
+
setResponseHeader(event, "Content-Type", "text/html; charset=utf-8");
|
|
1697
|
+
return createBuilderBrowserCallbackErrorPage(message, {
|
|
1698
|
+
parentOrigin: relayPayload.targetOrigin,
|
|
1699
|
+
});
|
|
1700
|
+
}
|
|
1701
|
+
setResponseHeader(event, "Content-Type", "text/html; charset=utf-8");
|
|
1702
|
+
return createBuilderBrowserCallbackPage(`${relayPayload.targetOrigin}${relayPayload.basePath || "/"}`, { parentOrigin: relayPayload.targetOrigin });
|
|
1703
|
+
}
|
|
1457
1704
|
// A real session or a template-approved anonymous owner is required;
|
|
1458
1705
|
// the pending-row check below (combined with the same-origin gate on
|
|
1459
1706
|
// /builder/connect) blocks CSRF and callback replay.
|
|
@@ -1478,7 +1725,6 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
1478
1725
|
return { error: "Authentication required" };
|
|
1479
1726
|
}
|
|
1480
1727
|
clearBuilderConnectOwnerCookie(event);
|
|
1481
|
-
const requestUrl = getFrameworkRouteRequestUrl(event);
|
|
1482
1728
|
let connectTracking = getBuilderConnectTrackingParams(requestUrl.searchParams);
|
|
1483
1729
|
// postMessage from the callback success/error pages must target the
|
|
1484
1730
|
// original preview opener, not the callback server. On the fallback
|