@agent-native/core 0.15.7 → 0.15.9

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.
@@ -6,7 +6,7 @@ import { createPollHandler } from "./poll.js";
6
6
  import { createPollEventsHandler } from "./poll-events.js";
7
7
  import { upsertEnvFile } from "./create-server.js";
8
8
  import { readBody } from "./h3-helpers.js";
9
- import { BUILDER_CONNECT_PARAM, BUILDER_CONNECT_OWNER_COOKIE, BUILDER_ENV_KEYS, BUILDER_STATE_PARAM, appendBuilderConnectToken, buildBuilderCliAuthUrl, createBuilderBrowserCallbackErrorPage, createBuilderBrowserCallbackPage, getBuilderBrowserOriginForEvent, getBuilderBrowserStatusForEvent, resolveBuilderBranchProjectId, resolveSafePreviewUrl, runBuilderAgent, signBuilderCallbackState, verifyBuilderConnectTokenAndGetOwner, verifyBuilderCallbackStateAndGetOwner, signBuilderConnectToken, } from "./builder-browser.js";
9
+ import { BUILDER_CONNECT_PARAM, BUILDER_CONNECT_OWNER_COOKIE, BUILDER_ENV_KEYS, BUILDER_OPENER_PARAM, BUILDER_STATE_PARAM, appendBuilderConnectToken, buildBuilderCliAuthUrl, createBuilderBrowserCallbackErrorPage, createBuilderBrowserCallbackPage, getBuilderCliAuthCallbackOriginForEvent, getBuilderBrowserOriginForEvent, getBuilderBrowserStatusForEvent, resolveBuilderBranchProjectId, resolveSafePreviewUrl, runBuilderAgent, signBuilderCallbackState, verifyBuilderConnectTokenAndGetOwner, verifyBuilderCallbackStateAndGetOwner, signBuilderConnectToken, } from "./builder-browser.js";
10
10
  import { getState, putState, deleteState, listComposeDrafts, getComposeDraft, putComposeDraft, deleteComposeDraft, deleteAllComposeDrafts, } from "../application-state/handlers.js";
11
11
  import { getSetting, putSetting, deleteSetting } from "../settings/store.js";
12
12
  import { getUserSetting, putUserSetting, deleteUserSetting, } from "../settings/user-settings.js";
@@ -413,8 +413,9 @@ export function createCoreRoutesPlugin(options = {}) {
413
413
  // instead of bouncing through the gateway. getOrigin(event) falls
414
414
  // back to the gateway on Builder preview hosts, which defeats the
415
415
  // whole preview-aware isolation goal of this PR.
416
- const callbackOrigin = getBuilderBrowserOriginForEvent(event);
417
- const cliAuthUrl = buildBuilderCliAuthUrl(callbackOrigin, signBuilderCallbackState(userEmail));
416
+ const previewOrigin = getBuilderBrowserOriginForEvent(event);
417
+ const callbackOrigin = getBuilderCliAuthCallbackOriginForEvent(event);
418
+ const cliAuthUrl = buildBuilderCliAuthUrl(callbackOrigin, signBuilderCallbackState(userEmail), { previewOrigin });
418
419
  return {
419
420
  ...status,
420
421
  connectUrl: appendBuilderConnectToken(status.connectUrl, userEmail),
@@ -480,12 +481,32 @@ export function createCoreRoutesPlugin(options = {}) {
480
481
  // the fallback. This keeps a root/local BUILDER_PRIVATE_KEY from
481
482
  // blocking a user from connecting their own Builder account.
482
483
  try {
483
- const { resolveBuilderCredentials, resolveBuilderCredentialSource, } = await import("./credential-provider.js");
484
+ const { resolveBuilderCredentials, resolveBuilderCredentialSource, getBuilderCredentialAuthFailure, } = await import("./credential-provider.js");
484
485
  const [creds, credentialSource] = await Promise.all([
485
486
  resolveBuilderCredentials(),
486
487
  resolveBuilderCredentialSource(),
487
488
  ]);
488
- if (creds.privateKey) {
489
+ const authFailure = await getBuilderCredentialAuthFailure(creds);
490
+ if (authFailure) {
491
+ return withConnectToken({
492
+ ...requestStatus,
493
+ configured: false,
494
+ privateKeyConfigured: false,
495
+ publicKeyConfigured: false,
496
+ userId: undefined,
497
+ orgName: undefined,
498
+ orgKind: undefined,
499
+ credentialSource: credentialSource ?? undefined,
500
+ // Surface the failure message so the UI can explain why the
501
+ // connection is broken instead of silently showing "Connect
502
+ // Builder" with no context.
503
+ connectError: {
504
+ message: authFailure.message,
505
+ at: authFailure.at,
506
+ },
507
+ });
508
+ }
509
+ if (creds.privateKey && creds.publicKey) {
489
510
  return withConnectToken({
490
511
  ...requestStatus,
491
512
  configured: true,
@@ -641,6 +662,7 @@ export function createCoreRoutesPlugin(options = {}) {
641
662
  title: "Couldn't start Builder connection",
642
663
  body: "The connect popup did not include a valid signed link for this app.",
643
664
  closeHint: "Close this popup, refresh the app, and try Connect account again.",
665
+ parentOrigin: getBuilderBrowserOriginForEvent(event),
644
666
  });
645
667
  }
646
668
  // Clear any prior failure row from a previous attempt — otherwise
@@ -676,7 +698,9 @@ export function createCoreRoutesPlugin(options = {}) {
676
698
  }).catch(() => { });
677
699
  setResponseStatus(event, 503);
678
700
  setResponseHeader(event, "Content-Type", "text/html; charset=utf-8");
679
- return createBuilderBrowserCallbackErrorPage(msg);
701
+ return createBuilderBrowserCallbackErrorPage(msg, {
702
+ parentOrigin: getBuilderBrowserOriginForEvent(event),
703
+ });
680
704
  }
681
705
  await trackBuilderLifecycle(event, "builder connect started", ownerEmail, {
682
706
  stage: "connect",
@@ -687,7 +711,7 @@ export function createCoreRoutesPlugin(options = {}) {
687
711
  // from /builder/status. Keep this legacy trampoline working for older
688
712
  // clients, but still send it to Builder immediately and include signed
689
713
  // callback state so the callback does not depend on popup cookies.
690
- const cliAuthUrl = buildBuilderCliAuthUrl(getBuilderBrowserOriginForEvent(event), signBuilderCallbackState(ownerEmail));
714
+ const cliAuthUrl = buildBuilderCliAuthUrl(getBuilderCliAuthCallbackOriginForEvent(event), signBuilderCallbackState(ownerEmail), { previewOrigin: getBuilderBrowserOriginForEvent(event) });
691
715
  setResponseStatus(event, 302);
692
716
  setResponseHeader(event, "Location", cliAuthUrl);
693
717
  return "";
@@ -793,6 +817,23 @@ export function createCoreRoutesPlugin(options = {}) {
793
817
  }
794
818
  clearBuilderConnectOwnerCookie(event);
795
819
  const requestUrl = new URL(`${event.url?.pathname || "/"}${event.url?.search || ""}`, getOrigin(event));
820
+ // postMessage from the callback success/error pages must target the
821
+ // original preview opener, not the callback server. On the fallback
822
+ // path the callback is served from the env-configured gateway while
823
+ // the opener lives on the preview origin. Three sources of opener
824
+ // origin, in priority order:
825
+ // 1. `_an_opener` — written into the callback URL's query by
826
+ // buildBuilderCliAuthUrl when cli-auth's allow-list forced
827
+ // preview_url onto the gateway. Survives Builder's redirect
828
+ // verbatim (Builder preserves redirect_url's query string).
829
+ // 2. `preview-url` — Builder echoes the top-level preview_url back
830
+ // as a query param on the callback. Reflects the gateway on
831
+ // the fallback path, but matches the opener on the happy path.
832
+ // 3. The event's own origin — last-resort fallback.
833
+ const openerOriginFromQuery = requestUrl.searchParams.get(BUILDER_OPENER_PARAM);
834
+ const callbackParentOrigin = resolveSafePreviewUrl(openerOriginFromQuery, event) ||
835
+ resolveSafePreviewUrl(requestUrl.searchParams.get("preview-url"), event) ||
836
+ getBuilderBrowserOriginForEvent(event);
796
837
  const callbackStateOwner = verifyBuilderCallbackStateAndGetOwner(requestUrl.searchParams.get(BUILDER_STATE_PARAM));
797
838
  const hasValidCallbackState = callbackStateOwner === ownerEmail;
798
839
  // Verify either:
@@ -844,7 +885,9 @@ export function createCoreRoutesPlugin(options = {}) {
844
885
  }).catch(() => { });
845
886
  setResponseStatus(event, 503);
846
887
  setResponseHeader(event, "Content-Type", "text/html; charset=utf-8");
847
- return createBuilderBrowserCallbackErrorPage(pendingError);
888
+ return createBuilderBrowserCallbackErrorPage(pendingError, {
889
+ parentOrigin: callbackParentOrigin,
890
+ });
848
891
  }
849
892
  if (!pendingValid) {
850
893
  await trackBuilderLifecycle(event, "builder connect failed", ownerEmail, {
@@ -868,7 +911,9 @@ export function createCoreRoutesPlugin(options = {}) {
868
911
  }
869
912
  setResponseStatus(event, 403);
870
913
  setResponseHeader(event, "Content-Type", "text/html; charset=utf-8");
871
- return createBuilderBrowserCallbackErrorPage(msg);
914
+ return createBuilderBrowserCallbackErrorPage(msg, {
915
+ parentOrigin: callbackParentOrigin,
916
+ });
872
917
  }
873
918
  const privateKey = requestUrl.searchParams.get("p-key");
874
919
  const publicKey = requestUrl.searchParams.get("api-key");
@@ -888,7 +933,9 @@ export function createCoreRoutesPlugin(options = {}) {
888
933
  }).catch(() => { });
889
934
  setResponseStatus(event, 400);
890
935
  setResponseHeader(event, "Content-Type", "text/html; charset=utf-8");
891
- return createBuilderBrowserCallbackErrorPage(msg);
936
+ return createBuilderBrowserCallbackErrorPage(msg, {
937
+ parentOrigin: callbackParentOrigin,
938
+ });
892
939
  }
893
940
  const userId = requestUrl.searchParams.get("user-id");
894
941
  const orgName = requestUrl.searchParams.get("org-name");
@@ -952,7 +999,9 @@ export function createCoreRoutesPlugin(options = {}) {
952
999
  }
953
1000
  setResponseStatus(event, 500);
954
1001
  setResponseHeader(event, "Content-Type", "text/html; charset=utf-8");
955
- return createBuilderBrowserCallbackErrorPage(writeError);
1002
+ return createBuilderBrowserCallbackErrorPage(writeError, {
1003
+ parentOrigin: callbackParentOrigin,
1004
+ });
956
1005
  }
957
1006
  // Clear any legacy disconnect flag and any prior connect-error row
958
1007
  // (so a successful retry doesn't surface the previous failure).
@@ -975,7 +1024,17 @@ export function createCoreRoutesPlugin(options = {}) {
975
1024
  org_kind: orgKind || undefined,
976
1025
  });
977
1026
  setResponseHeader(event, "Content-Type", "text/html; charset=utf-8");
978
- return createBuilderBrowserCallbackPage(previewUrl);
1027
+ // The parent (opener) is the original preview surface that started the
1028
+ // connect flow, NOT the callback server's own origin — when the
1029
+ // env-configured gateway is used as the callback fallback (because
1030
+ // Builder rejects the preview host), the callback server and the
1031
+ // opener live on different origins, and postMessage to the gateway
1032
+ // origin would be dropped by the preview opener. callbackParentOrigin
1033
+ // is the precomputed best-available opener origin (`_an_opener` →
1034
+ // `preview-url` → event origin).
1035
+ return createBuilderBrowserCallbackPage(previewUrl, {
1036
+ parentOrigin: callbackParentOrigin,
1037
+ });
979
1038
  }));
980
1039
  // POST /_agent-native/builder/disconnect — revoke the user's per-user
981
1040
  // or org-scoped Builder credentials in app_secrets. Deploy-level env
@@ -1051,7 +1110,7 @@ export function createCoreRoutesPlugin(options = {}) {
1051
1110
  setResponseStatus(event, 400);
1052
1111
  return { error: "userMessage is required" };
1053
1112
  }
1054
- const apiHost = process.env.BUILDER_API_HOST || "https://ai-services.builder.io";
1113
+ const apiHost = process.env.BUILDER_API_HOST || "https://api.builder.io";
1055
1114
  try {
1056
1115
  const res = await fetch(`${apiHost}/agents/run?apiKey=${encodeURIComponent(creds.publicKey)}`, {
1057
1116
  method: "POST",