@agent-native/core 0.200.0 → 0.200.1-nightly-20261002213029
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/agent/actions/manage-builder-connection.js +1 -1
- package/dist/agent/engine/ai-sdk-engine.js +8 -1
- package/dist/agent/engine/builder-engine.js +1 -1
- package/dist/agent/engine/credential-errors.d.ts +1 -1
- package/dist/agent/engine/credential-errors.js +2 -2
- package/dist/agent/engine/translate-ai-sdk.d.ts +1 -1
- package/dist/agent/engine/translate-ai-sdk.js +2 -1
- package/dist/agent/production-agent.js +1 -1
- package/dist/agent/run-manager.d.ts +7 -0
- package/dist/agent/run-manager.js +4 -0
- package/dist/agent/thread-data-builder.js +63 -14
- package/dist/brand-kit/fig/index.js +1 -1
- package/dist/cli/skills-content/turn-into-app-skill.d.ts +1 -1
- package/dist/cli/skills-content/turn-into-app-skill.js +2 -2
- package/dist/client/agent-engine-key.js +1 -1
- package/dist/client/chat/agentkit-agent-native.js +26 -14
- package/dist/client/error-format.js +5 -5
- package/dist/client/uploads/upload-editor-image.js +1 -1
- package/dist/client/use-chat-threads.js +48 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/deploy/build.js +7 -1
- package/dist/extensions/web-search-tool.js +2 -2
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.js +1 -1
- package/dist/file-upload/builder.js +2 -2
- package/dist/file-upload/registry.js +1 -1
- package/dist/ingestion/pptx.d.ts +3 -1
- package/dist/ingestion/pptx.js +3 -2
- package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
- package/dist/localization/default-messages.js +11 -11
- package/dist/mcp/build-server.js +11 -5
- package/dist/mcp-client/manager.js +14 -2
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/metrics.js +10 -1
- package/dist/onboarding/app-profile.js +1 -1
- package/dist/onboarding/default-steps.js +4 -4
- package/dist/private-blob/attachment-errors.js +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.js +1 -1
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/action-routes.js +14 -3
- package/dist/server/agent-chat/browser-team-tools.js +4 -4
- package/dist/server/agent-chat/context-tools.js +2 -2
- package/dist/server/agent-chat-ai-setup.js +1 -1
- package/dist/server/agent-chat-plugin.d.ts +2 -2
- package/dist/server/agent-chat-plugin.js +9 -5
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-default-model-route.d.ts +1 -1
- package/dist/server/builder-api-auth.js +5 -5
- package/dist/server/builder-browser.js +1 -1
- package/dist/server/builder-design-systems.js +1 -1
- package/dist/server/core-routes-plugin.d.ts +5 -11
- package/dist/server/core-routes-plugin.js +36 -68
- package/dist/server/credential-provider.js +3 -3
- package/dist/server/deep-link.d.ts +2 -0
- package/dist/server/deep-link.js +30 -3
- package/dist/server/fusion-app.js +1 -1
- package/dist/server/prompts/framework-core.js +1 -1
- package/dist/server/realtime-voice.js +1 -1
- package/dist/server/transcribe-voice.js +3 -3
- package/dist/shared/connect-required.js +1 -1
- package/dist/templates/chat/.agents/skills/turn-into-app/SKILL.md +2 -2
- package/dist/templates/default/.agents/skills/turn-into-app/SKILL.md +2 -2
- package/dist/templates/default/app/i18n/ar-SA.ts +3 -3
- package/dist/templates/default/app/i18n/de-DE.ts +3 -3
- package/dist/templates/default/app/i18n/en-US.ts +3 -3
- package/dist/templates/default/app/i18n/es-ES.ts +3 -3
- package/dist/templates/default/app/i18n/fr-FR.ts +3 -3
- package/dist/templates/default/app/i18n/hi-IN.ts +3 -3
- package/dist/templates/default/app/i18n/ja-JP.ts +3 -3
- package/dist/templates/default/app/i18n/ko-KR.ts +3 -3
- package/dist/templates/default/app/i18n/pt-BR.ts +3 -3
- package/dist/templates/default/app/i18n/zh-CN.ts +3 -3
- package/dist/templates/default/app/i18n/zh-TW.ts +3 -3
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +2 -2
- package/dist/templates/workspace-root/.env.example +1 -1
- package/dist/transcription/builder-transcription.js +1 -1
- package/dist/triggers/actions/manage-automation.d.ts +2 -2
- package/docs/content/getting-started.mdx +1 -1
- package/docs/content/multi-app-workspace.mdx +1 -1
- package/docs/content/onboarding.mdx +7 -8
- package/docs/content/real-time-collaboration.mdx +1 -1
- package/docs/content/template-chat-developers.mdx +1 -1
- package/docs/content/template-clips-developers.mdx +1 -1
- package/docs/content/template-content-developers.mdx +1 -1
- package/docs/content/template-slides-features.mdx +2 -2
- package/docs/content/toolkit-settings.mdx +10 -10
- package/docs/content/toolkit-setup-connections.mdx +1 -1
- package/docs/content/tracking.mdx +1 -1
- package/docs/content/updating-ui-in-production.mdx +3 -3
- package/docs/content/voice-input.mdx +1 -1
- package/package.json +4 -4
- package/src/templates/chat/.agents/skills/turn-into-app/SKILL.md +2 -2
- package/src/templates/default/.agents/skills/turn-into-app/SKILL.md +2 -2
- package/src/templates/default/app/i18n/ar-SA.ts +3 -3
- package/src/templates/default/app/i18n/de-DE.ts +3 -3
- package/src/templates/default/app/i18n/en-US.ts +3 -3
- package/src/templates/default/app/i18n/es-ES.ts +3 -3
- package/src/templates/default/app/i18n/fr-FR.ts +3 -3
- package/src/templates/default/app/i18n/hi-IN.ts +3 -3
- package/src/templates/default/app/i18n/ja-JP.ts +3 -3
- package/src/templates/default/app/i18n/ko-KR.ts +3 -3
- package/src/templates/default/app/i18n/pt-BR.ts +3 -3
- package/src/templates/default/app/i18n/zh-CN.ts +3 -3
- package/src/templates/default/app/i18n/zh-TW.ts +3 -3
- package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +2 -2
- package/src/templates/workspace-root/.env.example +1 -1
|
@@ -123,7 +123,7 @@ export function createBuilderBrowserTool(deps) {
|
|
|
123
123
|
const entries = {
|
|
124
124
|
"connect-file-storage": {
|
|
125
125
|
tool: {
|
|
126
|
-
description: "Render the inline file-storage setup card when an image or file attachment could not be durably uploaded. The card lets the user
|
|
126
|
+
description: "Render the inline file-storage setup card when an image or file attachment could not be durably uploaded. The card lets the user use Builder.io for managed object storage or open the same custom-key setup used by onboarding. Call it immediately when the attachment context says storage is missing; do not ask the user to upload the file again.",
|
|
127
127
|
parameters: {
|
|
128
128
|
type: "object",
|
|
129
129
|
properties: {},
|
|
@@ -148,7 +148,7 @@ export function createBuilderBrowserTool(deps) {
|
|
|
148
148
|
properties: {
|
|
149
149
|
prompt: {
|
|
150
150
|
type: "string",
|
|
151
|
-
description: "The user's feature / change request, verbatim. Forwarded to Builder's cloud agent when the user clicks Send. Omit only for generic '
|
|
151
|
+
description: "The user's feature / change request, verbatim. Forwarded to Builder's cloud agent when the user clicks Send. Omit only for generic 'use Builder.io' requests that aren't tied to a specific code change.",
|
|
152
152
|
},
|
|
153
153
|
extensionId: {
|
|
154
154
|
type: "string",
|
|
@@ -293,7 +293,7 @@ export function createBuilderBrowserTool(deps) {
|
|
|
293
293
|
},
|
|
294
294
|
"activate-browser": {
|
|
295
295
|
tool: {
|
|
296
|
-
description: "Activate browser automation tools. Call this when you need to interact with a real browser — e.g. to extract design tokens from a rendered page, take screenshots, read computed styles from JS-heavy sites, or test a live URL. After activation, chrome-devtools MCP tools (navigate, click, evaluate_script, take_screenshot, etc.) become available on your next action. Requires
|
|
296
|
+
description: "Activate browser automation tools. Call this when you need to interact with a real browser — e.g. to extract design tokens from a rendered page, take screenshots, read computed styles from JS-heavy sites, or test a live URL. After activation, chrome-devtools MCP tools (navigate, click, evaluate_script, take_screenshot, etc.) become available on your next action. Requires Builder.io to be set up (free tier available).",
|
|
297
297
|
parameters: {
|
|
298
298
|
type: "object",
|
|
299
299
|
properties: {
|
|
@@ -310,7 +310,7 @@ export function createBuilderBrowserTool(deps) {
|
|
|
310
310
|
if (!creds.privateKey || !creds.publicKey) {
|
|
311
311
|
return JSON.stringify({
|
|
312
312
|
error: "builder-not-connected",
|
|
313
|
-
message: "Builder.io is not connected. Call `connect-builder` first
|
|
313
|
+
message: "Builder.io is not connected. Call `connect-builder` first so the user can use browser automation.",
|
|
314
314
|
});
|
|
315
315
|
}
|
|
316
316
|
const { requestBuilderBrowserConnection } = await import("../builder-browser.js");
|
|
@@ -101,9 +101,9 @@ When you finish a task that has obvious recurring value — daily inbox triage,
|
|
|
101
101
|
If the user says yes, call \`manage-jobs\` (action: "create") with the original prompt as the job's instructions and the cadence they confirmed.
|
|
102
102
|
|
|
103
103
|
Do NOT add this offer for one-shot work: lookups (find Alice, what's the schema, who reported X), single drafts/replies, navigation requests, or any task whose value is in the moment. Skip it when the prompt is already explicitly recurring (the user said "every morning…" — you'd be asking what they already told you). One short sentence at most; do not turn it into a list of cadence options.`,
|
|
104
|
-
builder: `###
|
|
104
|
+
builder: `### Using Builder.io
|
|
105
105
|
|
|
106
|
-
When the user asks to
|
|
106
|
+
When the user asks to use Builder.io or you hit a "Builder not configured" error, call the \`connect-builder\` tool. It renders a Builder.io setup/code-change card inline — do NOT write out multi-step setup instructions yourself. Inspect the returned \`builderEnabled\` flag: \`true\` means Builder Cloud Agents can take the code-change handoff, while \`false\` means this requires a code change and the user should edit locally or use Builder.io to edit this code in the cloud and continue customizing the app any way they like. If Builder Cloud Agents are not available for this workspace, never send the user to Builder org settings or beta settings.`,
|
|
107
107
|
browser: `### Browser Automation
|
|
108
108
|
|
|
109
109
|
You can activate a real Chrome browser via Builder.io for tasks that need full page rendering:
|
|
@@ -76,7 +76,7 @@ export async function requireAgentChatAiSetup() {
|
|
|
76
76
|
return;
|
|
77
77
|
throw createError({
|
|
78
78
|
statusCode: 403,
|
|
79
|
-
statusMessage: "
|
|
79
|
+
statusMessage: "Use Builder.io or a provider API key before chatting.",
|
|
80
80
|
data: { code: AGENT_CHAT_AI_SETUP_REQUIRED_CODE },
|
|
81
81
|
});
|
|
82
82
|
}
|
|
@@ -115,11 +115,11 @@ export declare function resolveFetchToolKeyAllowlist(keyName: string, resolvedKe
|
|
|
115
115
|
export declare function resolveHostedBuilderHandoff(browserTools: Record<string, ActionEntry>, canToggle: boolean): Record<string, ActionEntry>;
|
|
116
116
|
/** Setup CTAs that must be callable on the very first request.
|
|
117
117
|
*
|
|
118
|
-
* Both are recovery actions: the agent should answer "
|
|
118
|
+
* Both are recovery actions: the agent should answer "use Builder.io for me"
|
|
119
119
|
* or a failed upload by rendering the inline card, not by spending a turn in
|
|
120
120
|
* `tool-search` first. `connect-builder` is registered in every registry that
|
|
121
121
|
* receives `browserTools`, local dev included, so naming it only through the
|
|
122
|
-
* hosted-only handoff left local dev advertising "
|
|
122
|
+
* hosted-only handoff left local dev advertising "Use Builder.io" in the
|
|
123
123
|
* UI while the agent was never told the tool existed. Names the registry does
|
|
124
124
|
* not have are dropped by `filterInitialEngineTools`, so listing both here is
|
|
125
125
|
* safe for lean registries. */
|
|
@@ -578,11 +578,11 @@ export function resolveHostedBuilderHandoff(browserTools, canToggle) {
|
|
|
578
578
|
}
|
|
579
579
|
/** Setup CTAs that must be callable on the very first request.
|
|
580
580
|
*
|
|
581
|
-
* Both are recovery actions: the agent should answer "
|
|
581
|
+
* Both are recovery actions: the agent should answer "use Builder.io for me"
|
|
582
582
|
* or a failed upload by rendering the inline card, not by spending a turn in
|
|
583
583
|
* `tool-search` first. `connect-builder` is registered in every registry that
|
|
584
584
|
* receives `browserTools`, local dev included, so naming it only through the
|
|
585
|
-
* hosted-only handoff left local dev advertising "
|
|
585
|
+
* hosted-only handoff left local dev advertising "Use Builder.io" in the
|
|
586
586
|
* UI while the agent was never told the tool existed. Names the registry does
|
|
587
587
|
* not have are dropped by `filterInitialEngineTools`, so listing both here is
|
|
588
588
|
* safe for lean registries. */
|
|
@@ -5040,7 +5040,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
5040
5040
|
: null;
|
|
5041
5041
|
const { isBuilderGatewayDeployConfigured } = await import("./credential-provider.js");
|
|
5042
5042
|
return {
|
|
5043
|
-
active:
|
|
5043
|
+
active: run.inFlight,
|
|
5044
5044
|
runId: run.runId,
|
|
5045
5045
|
threadId: run.threadId,
|
|
5046
5046
|
turnId: run.turnId,
|
|
@@ -5383,7 +5383,11 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
5383
5383
|
const incomingScope = parseScopeFromBody(body.scope);
|
|
5384
5384
|
await setThreadScope(threadId, owner, incomingScope);
|
|
5385
5385
|
}
|
|
5386
|
-
|
|
5386
|
+
// The scope the thread really has now (a detach can land
|
|
5387
|
+
// between the read above and this save), so the client records
|
|
5388
|
+
// what the server holds instead of guessing from the page.
|
|
5389
|
+
const saved = await resolveThreadAccess(owner, threadId, "editor", { orgId });
|
|
5390
|
+
return { ok: true, scope: saved?.scope ?? null };
|
|
5387
5391
|
});
|
|
5388
5392
|
}
|
|
5389
5393
|
// POST /threads/:id/queued — apply a single queue mutation against
|
|
@@ -5765,7 +5769,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
5765
5769
|
code: AGENT_CHAT_AI_SETUP_REQUIRED_CODE,
|
|
5766
5770
|
message: error.statusMessage ??
|
|
5767
5771
|
error.message ??
|
|
5768
|
-
"
|
|
5772
|
+
"Use Builder.io or a provider API key before chatting.",
|
|
5769
5773
|
},
|
|
5770
5774
|
});
|
|
5771
5775
|
};
|
|
@@ -65,6 +65,7 @@ export declare function createAgentEngineApiKeyHandler(): import("h3").EventHand
|
|
|
65
65
|
error: any;
|
|
66
66
|
code: any;
|
|
67
67
|
} | {
|
|
68
|
+
error?: undefined;
|
|
68
69
|
errorCode?: undefined;
|
|
69
70
|
code?: undefined;
|
|
70
71
|
ok: boolean;
|
|
@@ -72,6 +73,5 @@ export declare function createAgentEngineApiKeyHandler(): import("h3").EventHand
|
|
|
72
73
|
baseUrlKey?: string;
|
|
73
74
|
scope: AgentEngineApiKeyScope;
|
|
74
75
|
defaultModel?: import("./agent-engine-default-model-route.js").SavedKeyDefaultModelOutcome;
|
|
75
|
-
error?: undefined;
|
|
76
76
|
}>>;
|
|
77
77
|
export {};
|
|
@@ -8,8 +8,8 @@ export declare const AGENT_ENGINE_API_KEY_ACTION_NAME = "agent-engine-api-key";
|
|
|
8
8
|
* resolveEngine's credential detection.
|
|
9
9
|
*/
|
|
10
10
|
export declare function createAgentEngineDisconnectHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
11
|
-
ok?: undefined;
|
|
12
11
|
error: string;
|
|
12
|
+
ok?: undefined;
|
|
13
13
|
scope?: undefined;
|
|
14
14
|
} | {
|
|
15
15
|
ok: boolean;
|
|
@@ -50,7 +50,7 @@ async function resolveBuilderPublishAuthorization(ownerEmail, orgId) {
|
|
|
50
50
|
if (!server)
|
|
51
51
|
continue;
|
|
52
52
|
if (!server.oauthSecretKey) {
|
|
53
|
-
throw new ActionContractError("Builder Publish is configured without OAuth custody.
|
|
53
|
+
throw new ActionContractError("Builder.io Publish is configured without OAuth custody. Sign in to Builder.io Publish again in Settings to continue.", {
|
|
54
54
|
errorCode: "builder_oauth_reauthorization_required",
|
|
55
55
|
statusCode: 400,
|
|
56
56
|
});
|
|
@@ -77,7 +77,7 @@ async function resolveBuilderPublishAuthorization(ownerEmail, orgId) {
|
|
|
77
77
|
!resourceBound ||
|
|
78
78
|
!scopes.has("mcp:publish:read") ||
|
|
79
79
|
[...scopes].some((scope) => scope !== "mcp:publish:read" && scope !== "offline_access")) {
|
|
80
|
-
throw new ActionContractError("Builder Publish access needs re-authorizing to grant mcp:publish:read. Open Settings and
|
|
80
|
+
throw new ActionContractError("Builder.io Publish access needs re-authorizing to grant mcp:publish:read. Open Settings and authorize Builder.io Publish again.", {
|
|
81
81
|
errorCode: "builder_oauth_reauthorization_required",
|
|
82
82
|
statusCode: 400,
|
|
83
83
|
});
|
|
@@ -86,7 +86,7 @@ async function resolveBuilderPublishAuthorization(ownerEmail, orgId) {
|
|
|
86
86
|
const authorization = config.headers?.Authorization;
|
|
87
87
|
const match = authorization?.match(/^Bearer\s+(.+)$/i);
|
|
88
88
|
if (!match?.[1]) {
|
|
89
|
-
throw new ActionContractError("Builder Publish access expired.
|
|
89
|
+
throw new ActionContractError("Builder.io Publish access expired. Sign in to Builder.io Publish again in Settings to continue.", {
|
|
90
90
|
errorCode: "builder_oauth_reauthorization_required",
|
|
91
91
|
statusCode: 400,
|
|
92
92
|
});
|
|
@@ -101,7 +101,7 @@ async function resolveBuilderPublishAuthorization(ownerEmail, orgId) {
|
|
|
101
101
|
if (ownerEmail) {
|
|
102
102
|
const personalServer = (await listRemoteServers("user", ownerEmail)).find((entry) => isBuilderPublishResource(entry.url));
|
|
103
103
|
if (personalServer) {
|
|
104
|
-
throw new ActionContractError("Builder Publish is connected only for this user. Remove it and
|
|
104
|
+
throw new ActionContractError("Builder.io Publish is connected only for this user. Remove it and sign in to Builder.io Publish for the workspace.", {
|
|
105
105
|
errorCode: "builder_oauth_reauthorization_required",
|
|
106
106
|
statusCode: 400,
|
|
107
107
|
});
|
|
@@ -117,7 +117,7 @@ export async function resolveBuilderRequestAuthorization(input = {}) {
|
|
|
117
117
|
if (publishAuthorization)
|
|
118
118
|
return publishAuthorization;
|
|
119
119
|
if (ownerEmail && (await readOAuthCustody(ownerEmail, orgId))) {
|
|
120
|
-
throw new ActionContractError("Builder Publish access is not connected for this workspace.
|
|
120
|
+
throw new ActionContractError("Builder.io Publish access is not connected for this workspace. Sign in to Builder.io Publish in Settings to grant mcp:publish:read.", {
|
|
121
121
|
errorCode: "builder_oauth_reauthorization_required",
|
|
122
122
|
statusCode: 400,
|
|
123
123
|
});
|
|
@@ -1561,7 +1561,7 @@ async function resolveBuilderApiAuthorization(requiredScope) {
|
|
|
1561
1561
|
requiredScope,
|
|
1562
1562
|
});
|
|
1563
1563
|
if (!authorization) {
|
|
1564
|
-
throw new ActionContractError("Builder.io is not connected.
|
|
1564
|
+
throw new ActionContractError("Builder.io is not connected. Sign in to Builder.io in Settings to continue.", { errorCode: "builder_not_connected", statusCode: 400 });
|
|
1565
1565
|
}
|
|
1566
1566
|
if (authorization.source === "legacy" && !authorization.legacyPublicKey) {
|
|
1567
1567
|
throw new ActionContractError("Builder legacy credentials require BUILDER_PUBLIC_KEY for this request.", { errorCode: "builder_legacy_public_key_required", statusCode: 400 });
|
|
@@ -404,7 +404,7 @@ async function resolveBuilderDesignSystemAuthorization(requiredScope) {
|
|
|
404
404
|
(authorization.source === "legacy" && !authorization.legacyPublicKey)) {
|
|
405
405
|
throw new FeatureNotConfiguredError({
|
|
406
406
|
requiredCredential: "BUILDER_PRIVATE_KEY",
|
|
407
|
-
message: "
|
|
407
|
+
message: "Use Builder.io (free tier available) to index a design system from Figma or code.",
|
|
408
408
|
builderConnectUrl: "/_agent-native/builder/connect",
|
|
409
409
|
});
|
|
410
410
|
}
|
|
@@ -88,11 +88,11 @@ export declare function resolveBuilderOrgMutation(event: H3Event, options?: {
|
|
|
88
88
|
deny: string | null;
|
|
89
89
|
whoCanFix?: CredentialFixer;
|
|
90
90
|
}>;
|
|
91
|
-
export declare const BUILDER_CONNECT_NEEDS_ORGANIZATION = "Builder.io
|
|
92
|
-
export declare const BUILDER_CONNECT_SESSION_NOT_SHARED = "This window can't see your sign-in, which happens in previews and embedded browsers. Open the app in its own browser tab, sign in, then
|
|
93
|
-
export declare const BUILDER_ORG_MEMBERSHIP_UNREADABLE = "Couldn't check your organization membership. Try
|
|
94
|
-
export declare const BUILDER_ORG_ALREADY_CONNECTED = "Your organization already has a Builder.io connection, and a new account would take its place. An organization owner or admin can
|
|
95
|
-
export declare const BUILDER_ORG_RECONNECT_BY_LOGIN = "Your organization already has a Builder.io account.
|
|
91
|
+
export declare const BUILDER_CONNECT_NEEDS_ORGANIZATION = "Builder.io access is managed through an organization. Switch to one you belong to, or ask an organization owner or admin to invite you, then set up Builder.io again.";
|
|
92
|
+
export declare const BUILDER_CONNECT_SESSION_NOT_SHARED = "This window can't see your sign-in, which happens in previews and embedded browsers. Open the app in its own browser tab, sign in, then set up Builder.io there.";
|
|
93
|
+
export declare const BUILDER_ORG_MEMBERSHIP_UNREADABLE = "Couldn't check your organization membership. Try starting Builder.io setup again in a moment.";
|
|
94
|
+
export declare const BUILDER_ORG_ALREADY_CONNECTED = "Your organization already has a Builder.io connection, and a new account would take its place. An organization owner or admin can sign in to Builder.io again in Settings.";
|
|
95
|
+
export declare const BUILDER_ORG_RECONNECT_BY_LOGIN = "Your organization already has a Builder.io account. Sign in to Builder.io to use it.";
|
|
96
96
|
/** Query/body field naming which Builder.io connection a request targets. */
|
|
97
97
|
export declare const BUILDER_CONNECTION_SCOPE_PARAM = "scope";
|
|
98
98
|
/**
|
|
@@ -665,12 +665,6 @@ interface BuilderAccountActivationInput {
|
|
|
665
665
|
* mode both run exactly this, after `authorizeBuilderConnectScope`.
|
|
666
666
|
*/
|
|
667
667
|
export declare function activateBuilderAccount(event: H3Event, input: BuilderAccountActivationInput): Promise<BuilderAccountActivationResult>;
|
|
668
|
-
/**
|
|
669
|
-
* The popup-era account activation: answers with a page that hands the result
|
|
670
|
-
* to its opener and closes. The error row is the opener's fallback channel,
|
|
671
|
-
* read by its status poll when the page's message never arrives.
|
|
672
|
-
*/
|
|
673
|
-
export declare function sendBuilderActivationPopup(event: H3Event, input: BuilderAccountActivationInput): Promise<string>;
|
|
674
668
|
/**
|
|
675
669
|
* `POST /_agent-native/builder/provision` — one-click Builder.io account
|
|
676
670
|
* activation as a single JSON request, no popup. Session cookies are
|
|
@@ -59,7 +59,7 @@ import { getSession } from "./auth.js";
|
|
|
59
59
|
import { createAutomationFailureUnsubscribeHandler } from "./automation-failure-notifications.js";
|
|
60
60
|
import { getBetterAuthInternalAdapter, getBetterAuthSync, } from "./better-auth-instance.js";
|
|
61
61
|
import { resolveBuilderRequestAuthorization } from "./builder-api-auth.js";
|
|
62
|
-
import { BUILDER_CONNECT_PARAM, BUILDER_CONNECT_MODE_PARAM, BUILDER_AGENT_NATIVE_PROVISION_MODE,
|
|
62
|
+
import { BUILDER_CONNECT_PARAM, BUILDER_CONNECT_MODE_PARAM, BUILDER_AGENT_NATIVE_PROVISION_MODE, BUILDER_CONNECT_ATTEMPT_PARAM, BUILDER_CONNECT_STATE_COOKIE, BUILDER_ENV_KEYS, BUILDER_OPENER_PARAM, BUILDER_RELAY_FLOW_HEADER, BUILDER_RELAY_SIGNATURE_HEADER, BUILDER_RELAY_STATE_PARAM, BUILDER_RELAY_TIMESTAMP_HEADER, appendBuilderConnectToken, appendBuilderConnectStateCookie, builderConnectTrackingProperties, BUILDER_UPSTREAM_FAILURE_STATUS, createBuilderConnectState, createBuilderBrowserCallbackErrorPage, createBuilderBrowserCallbackPage, createBuilderRelayRequest, getBuilderConnectTrackingParams, getBuilderBrowserOriginForEvent, getBuilderBrowserStatusForEvent, isBuilderAccountAlreadyExistsError, isBuilderAccountProvisioningEnabled, isBuilderConnectCallbackUrlAllowed, isSignedBuilderConnectState, normalizeBuilderAgentContext, parseBuilderConnectStateCookie, provisionBuilderAccount, resolveBuilderBranchProjectId, resolveBuilderConnectCallbackUrl, resolveBuilderConnectCallbackState, resolveBuilderPreviewRelayParentOrigin, removeBuilderConnectStateCookie, runBuilderAgent, sendBuilderPopupErrorPage, verifyBuilderRelayRequest, verifyBuilderPreviewRelayStateForCallback, verifyBuilderConnectTokenAndGetOwner, signBuilderProvisioningToken, verifyBuilderProvisioningToken, withBuilderConnectTrackingParams, } from "./builder-browser.js";
|
|
63
63
|
import { BUILDER_ASSETS_WRITE_SCOPE, BUILDER_OAUTH_SCOPE, builderOAuthScopeFor, canRoleConnectPersonalBuilder, deleteBuilderOAuthSession, exchangeBuilderOAuthAuthorization, getBuilderOAuthGrants, getBuilderOAuthStoredScope, hasStoredBuilderOAuthGrant, isBuilderOrgManagerRole, isPersonalBuilderGrantAllowed, saveBuilderOAuthCredentials, startBuilderOAuthAuthorization, } from "./builder-oauth.js";
|
|
64
64
|
import { captureError, registerErrorCaptureProvider } from "./capture-error.js";
|
|
65
65
|
import { createChatGPTSubscriptionOAuthCallbackHandler, createChatGPTSubscriptionOAuthStartHandler, } from "./chatgpt-subscription-oauth.js";
|
|
@@ -274,11 +274,11 @@ export async function resolveBuilderOrgMutation(event, options = {}) {
|
|
|
274
274
|
return { orgId, role, deny: null };
|
|
275
275
|
}
|
|
276
276
|
const BUILDER_ORG_CONNECTION_DENIED = "Only an organization owner or admin can change the shared Builder connection.";
|
|
277
|
-
export const BUILDER_CONNECT_NEEDS_ORGANIZATION = "Builder.io
|
|
278
|
-
export const BUILDER_CONNECT_SESSION_NOT_SHARED = "This window can't see your sign-in, which happens in previews and embedded browsers. Open the app in its own browser tab, sign in, then
|
|
279
|
-
export const BUILDER_ORG_MEMBERSHIP_UNREADABLE = "Couldn't check your organization membership. Try
|
|
280
|
-
export const BUILDER_ORG_ALREADY_CONNECTED = "Your organization already has a Builder.io connection, and a new account would take its place. An organization owner or admin can
|
|
281
|
-
export const BUILDER_ORG_RECONNECT_BY_LOGIN = "Your organization already has a Builder.io account.
|
|
277
|
+
export const BUILDER_CONNECT_NEEDS_ORGANIZATION = "Builder.io access is managed through an organization. Switch to one you belong to, or ask an organization owner or admin to invite you, then set up Builder.io again.";
|
|
278
|
+
export const BUILDER_CONNECT_SESSION_NOT_SHARED = "This window can't see your sign-in, which happens in previews and embedded browsers. Open the app in its own browser tab, sign in, then set up Builder.io there.";
|
|
279
|
+
export const BUILDER_ORG_MEMBERSHIP_UNREADABLE = "Couldn't check your organization membership. Try starting Builder.io setup again in a moment.";
|
|
280
|
+
export const BUILDER_ORG_ALREADY_CONNECTED = "Your organization already has a Builder.io connection, and a new account would take its place. An organization owner or admin can sign in to Builder.io again in Settings.";
|
|
281
|
+
export const BUILDER_ORG_RECONNECT_BY_LOGIN = "Your organization already has a Builder.io account. Sign in to Builder.io to use it.";
|
|
282
282
|
/** Query/body field naming which Builder.io connection a request targets. */
|
|
283
283
|
export const BUILDER_CONNECTION_SCOPE_PARAM = "scope";
|
|
284
284
|
/**
|
|
@@ -291,8 +291,8 @@ export function parseBuilderConnectionScope(value) {
|
|
|
291
291
|
return null;
|
|
292
292
|
return value === "org" || value === "personal" ? value : "invalid";
|
|
293
293
|
}
|
|
294
|
-
const BUILDER_PERSONAL_CONNECTION_DENIED = "Owners and admins
|
|
295
|
-
const BUILDER_CONNECTION_MEMBERSHIP_ENDED = "You're no longer a member of the organization this Builder.io
|
|
294
|
+
const BUILDER_PERSONAL_CONNECTION_DENIED = "Owners and admins set up Builder.io for the organization.";
|
|
295
|
+
const BUILDER_CONNECTION_MEMBERSHIP_ENDED = "You're no longer a member of the organization this Builder.io setup started in. Start it again from Settings.";
|
|
296
296
|
/**
|
|
297
297
|
* Who may start a connect for the named Builder.io connection. The org
|
|
298
298
|
* connection needs owner/admin; a personal one is for members only, and only
|
|
@@ -1527,10 +1527,10 @@ export async function activateBuilderAccount(event, input) {
|
|
|
1527
1527
|
}
|
|
1528
1528
|
if (!session?.token ||
|
|
1529
1529
|
!verifyBuilderProvisioningToken(input.provisioningToken, ownerEmail, session.token)) {
|
|
1530
|
-
return fail(403, "This activation link is expired.
|
|
1530
|
+
return fail(403, "This activation link is expired. Refresh Builder.io setup and try again.", "provision_token_invalid");
|
|
1531
1531
|
}
|
|
1532
1532
|
if (session.emailVerified !== true) {
|
|
1533
|
-
return fail(403, "Verify your email before
|
|
1533
|
+
return fail(403, "Verify your email before using Builder.io.", "email_not_verified");
|
|
1534
1534
|
}
|
|
1535
1535
|
try {
|
|
1536
1536
|
const activation = await decideBuilderActivation(event, {
|
|
@@ -1570,35 +1570,11 @@ export async function activateBuilderAccount(event, input) {
|
|
|
1570
1570
|
catch (error) {
|
|
1571
1571
|
console.error("[builder] Agent-Native account provisioning failed:", error instanceof Error ? error.message : error);
|
|
1572
1572
|
if (isBuilderAccountAlreadyExistsError(error)) {
|
|
1573
|
-
return fail(409, "A Builder account already exists for this email.
|
|
1573
|
+
return fail(409, "A Builder.io account already exists for this email. Sign in to Builder.io to use it.", "account_exists", "account_exists");
|
|
1574
1574
|
}
|
|
1575
|
-
return fail(BUILDER_UPSTREAM_FAILURE_STATUS, "Couldn't create your Builder account. Try again or
|
|
1575
|
+
return fail(BUILDER_UPSTREAM_FAILURE_STATUS, "Couldn't create your Builder.io account. Try again or sign in with an existing account.", "provision_failed");
|
|
1576
1576
|
}
|
|
1577
1577
|
}
|
|
1578
|
-
/**
|
|
1579
|
-
* The popup-era account activation: answers with a page that hands the result
|
|
1580
|
-
* to its opener and closes. The error row is the opener's fallback channel,
|
|
1581
|
-
* read by its status poll when the page's message never arrives.
|
|
1582
|
-
*/
|
|
1583
|
-
export async function sendBuilderActivationPopup(event, input) {
|
|
1584
|
-
const result = await activateBuilderAccount(event, input);
|
|
1585
|
-
const parentOrigin = getBuilderBrowserOriginForEvent(event);
|
|
1586
|
-
const attempt = input.connectAttemptId
|
|
1587
|
-
? { attemptId: input.connectAttemptId }
|
|
1588
|
-
: {};
|
|
1589
|
-
if (!result.ok) {
|
|
1590
|
-
const code = result.code ? { code: result.code } : {};
|
|
1591
|
-
await putSetting(getBuilderConnectErrorKey(input.ownerEmail, input.connectAttemptId), { message: result.message, at: Date.now(), ...code, ...attempt }).catch(() => { });
|
|
1592
|
-
return sendBuilderPopupErrorPage(event, result.status, result.message, {
|
|
1593
|
-
parentOrigin,
|
|
1594
|
-
...attempt,
|
|
1595
|
-
...code,
|
|
1596
|
-
});
|
|
1597
|
-
}
|
|
1598
|
-
setResponseHeader(event, "Cache-Control", "no-store");
|
|
1599
|
-
setResponseHeader(event, "Content-Type", "text/html; charset=utf-8");
|
|
1600
|
-
return createBuilderBrowserCallbackPage(`${parentOrigin}${getAppBasePath() || "/"}`, { parentOrigin, ...attempt });
|
|
1601
|
-
}
|
|
1602
1578
|
const builderProvisionBodySchema = z.object({
|
|
1603
1579
|
// Checked by activateBuilderAccount, so a missing token is refused like an
|
|
1604
1580
|
// expired one and the client's refresh-and-retry covers both.
|
|
@@ -2850,7 +2826,7 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
2850
2826
|
? "Builder connection status could not be read. Retry in a moment."
|
|
2851
2827
|
: error instanceof Error
|
|
2852
2828
|
? error.message
|
|
2853
|
-
: "Builder access expired.
|
|
2829
|
+
: "Builder access expired. Sign in to Builder.io again.",
|
|
2854
2830
|
at: Date.now(),
|
|
2855
2831
|
},
|
|
2856
2832
|
});
|
|
@@ -3026,9 +3002,9 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
3026
3002
|
isAnonymousWaitlistSessionEmail(ownerEmail)) {
|
|
3027
3003
|
setResponseStatus(event, 401);
|
|
3028
3004
|
setResponseHeader(event, "Content-Type", "text/html; charset=utf-8");
|
|
3029
|
-
return createBuilderBrowserCallbackErrorPage("Sign in to
|
|
3005
|
+
return createBuilderBrowserCallbackErrorPage("Sign in to use Builder.io.", {
|
|
3030
3006
|
title: "Sign in required",
|
|
3031
|
-
body: "Builder OAuth is tied to a signed-in account. Sign in, then
|
|
3007
|
+
body: "Builder OAuth is tied to a signed-in account. Sign in, then start Builder.io setup again.",
|
|
3032
3008
|
parentOrigin: getBuilderBrowserOriginForEvent(event),
|
|
3033
3009
|
...(connectAttemptId ? { attemptId: connectAttemptId } : {}),
|
|
3034
3010
|
});
|
|
@@ -3046,8 +3022,8 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
3046
3022
|
// local desktop popups stamped as same-site/cross-site by the browser.
|
|
3047
3023
|
if (!isSameOriginConnect(event) && !hasValidConnectToken) {
|
|
3048
3024
|
const crossOriginMessage = connectToken
|
|
3049
|
-
? "This Builder
|
|
3050
|
-
: "Builder
|
|
3025
|
+
? "This Builder.io setup link is expired or belongs to a different deployment. Close this popup and start Builder.io setup again from the app."
|
|
3026
|
+
: "Builder.io setup opened without a fresh signed link. Close this popup and start setup again from the app.";
|
|
3051
3027
|
await trackBuilderLifecycle(event, "builder connect failed", ownerEmail, {
|
|
3052
3028
|
...builderConnectTrackingProperties(connectTracking),
|
|
3053
3029
|
reason: "cross_origin",
|
|
@@ -3071,14 +3047,14 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
3071
3047
|
setResponseStatus(event, 403);
|
|
3072
3048
|
setResponseHeader(event, "Content-Type", "text/html; charset=utf-8");
|
|
3073
3049
|
return createBuilderBrowserCallbackErrorPage(crossOriginMessage, {
|
|
3074
|
-
title: "Couldn't start Builder
|
|
3075
|
-
body: "The
|
|
3076
|
-
closeHint: "Close this popup, refresh the app, and
|
|
3050
|
+
title: "Couldn't start Builder.io setup",
|
|
3051
|
+
body: "The Builder.io setup popup did not include a valid signed link for this app.",
|
|
3052
|
+
closeHint: "Close this popup, refresh the app, and start Builder.io setup again.",
|
|
3077
3053
|
parentOrigin: getBuilderBrowserOriginForEvent(event),
|
|
3078
3054
|
...(connectAttemptId ? { attemptId: connectAttemptId } : {}),
|
|
3079
3055
|
});
|
|
3080
3056
|
}
|
|
3081
|
-
const denyConnect = async (status, message, reason) => {
|
|
3057
|
+
const denyConnect = async (status, message, reason, title = "Not allowed to use Builder.io for this organization") => {
|
|
3082
3058
|
await putSetting(getBuilderConnectErrorKey(ownerEmail, connectAttemptId), {
|
|
3083
3059
|
message,
|
|
3084
3060
|
at: Date.now(),
|
|
@@ -3092,7 +3068,7 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
3092
3068
|
setResponseStatus(event, status);
|
|
3093
3069
|
setResponseHeader(event, "Content-Type", "text/html; charset=utf-8");
|
|
3094
3070
|
return createBuilderBrowserCallbackErrorPage(message, {
|
|
3095
|
-
title
|
|
3071
|
+
title,
|
|
3096
3072
|
body: message,
|
|
3097
3073
|
parentOrigin: getBuilderBrowserOriginForEvent(event),
|
|
3098
3074
|
...(connectAttemptId ? { attemptId: connectAttemptId } : {}),
|
|
@@ -3111,18 +3087,10 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
3111
3087
|
return denyConnect(403, scopeAuthorization.deny.message, scopeAuthorization.deny.reason);
|
|
3112
3088
|
}
|
|
3113
3089
|
const scopedConnectAuthorization = scopeAuthorization.member;
|
|
3114
|
-
//
|
|
3115
|
-
//
|
|
3090
|
+
// Account creation always uses POST /builder/provision so it never
|
|
3091
|
+
// depends on a popup or a browser's popup policy.
|
|
3116
3092
|
if (shouldProvisionAgentNativeAccount) {
|
|
3117
|
-
return
|
|
3118
|
-
ownerEmail,
|
|
3119
|
-
session: ownerContext.session,
|
|
3120
|
-
provisioningToken: requestUrl.searchParams.get(BUILDER_PROVISIONING_TOKEN_PARAM),
|
|
3121
|
-
requestedScope: requestedConnectionScope,
|
|
3122
|
-
member: scopedConnectAuthorization,
|
|
3123
|
-
connectAttemptId,
|
|
3124
|
-
tracking: connectTracking,
|
|
3125
|
-
});
|
|
3093
|
+
return denyConnect(400, "Use Builder.io setup in the app to create or activate an account.", "popup_provision_not_supported", "Use Builder.io setup in the app");
|
|
3126
3094
|
}
|
|
3127
3095
|
// Clear any prior failure row from a previous attempt — otherwise
|
|
3128
3096
|
// useBuilderStatus polling sees the stale error and aborts the
|
|
@@ -3195,7 +3163,7 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
3195
3163
|
reason: "pending_storage_unavailable",
|
|
3196
3164
|
stage: "connect",
|
|
3197
3165
|
});
|
|
3198
|
-
const msg = "Could not
|
|
3166
|
+
const msg = "Could not start Builder.io setup — storage unavailable. Try again.";
|
|
3199
3167
|
console.error("[builder] Could not store pending-connect state:", err?.message ?? err);
|
|
3200
3168
|
// Best-effort: also write the error row so the parent's
|
|
3201
3169
|
// /builder/status poll picks it up if BroadcastChannel doesn't.
|
|
@@ -3439,7 +3407,7 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
3439
3407
|
if (!privateKey || !publicKey) {
|
|
3440
3408
|
setResponseStatus(event, 400);
|
|
3441
3409
|
setResponseHeader(event, "Content-Type", "text/html; charset=utf-8");
|
|
3442
|
-
return createBuilderBrowserCallbackErrorPage("Builder didn't return credentials.
|
|
3410
|
+
return createBuilderBrowserCallbackErrorPage("Builder didn't return credentials. Start Builder.io setup again from Settings.", {
|
|
3443
3411
|
parentOrigin: relayParentOrigin,
|
|
3444
3412
|
...(requestConnectAttemptId
|
|
3445
3413
|
? { attemptId: requestConnectAttemptId }
|
|
@@ -3563,11 +3531,11 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
3563
3531
|
if (resetStateCookie && carriesOAuthResult) {
|
|
3564
3532
|
deleteCookie(event, BUILDER_CONNECT_STATE_COOKIE, { path: "/" });
|
|
3565
3533
|
}
|
|
3566
|
-
return fail(403, "No active Builder
|
|
3534
|
+
return fail(403, "No active Builder.io setup was found. Start it again from Settings.");
|
|
3567
3535
|
}
|
|
3568
3536
|
const pending = await readBuilderConnectPendingState(state);
|
|
3569
3537
|
if (!pending) {
|
|
3570
|
-
return fail(403, "No active Builder
|
|
3538
|
+
return fail(403, "No active Builder.io setup was found. Start it again from Settings.");
|
|
3571
3539
|
}
|
|
3572
3540
|
callbackAttemptId =
|
|
3573
3541
|
typeof pending.attemptId === "string"
|
|
@@ -3597,7 +3565,7 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
3597
3565
|
if (Date.now() >= expiresAt) {
|
|
3598
3566
|
await deleteSetting(`builder-connect-pending:${state}`).catch(() => false); // coercion-ok: expired pending cleanup is best-effort; callback already failed verification
|
|
3599
3567
|
}
|
|
3600
|
-
return fail(403, "Builder
|
|
3568
|
+
return fail(403, "Builder.io setup could not be verified. Start the setup again.", ownerEmail ?? undefined, "callback_verification_failed", tracking);
|
|
3601
3569
|
}
|
|
3602
3570
|
const denied = requestUrl.searchParams.get("error");
|
|
3603
3571
|
const code = requestUrl.searchParams.get("code");
|
|
@@ -3618,11 +3586,11 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
3618
3586
|
});
|
|
3619
3587
|
}
|
|
3620
3588
|
catch {
|
|
3621
|
-
return fail(BUILDER_UPSTREAM_FAILURE_STATUS, "Builder could not exchange the authorization code.
|
|
3589
|
+
return fail(BUILDER_UPSTREAM_FAILURE_STATUS, "Builder could not exchange the authorization code. Start Builder.io setup again.", ownerEmail, "code_exchange_failed", tracking);
|
|
3622
3590
|
}
|
|
3623
3591
|
const requestedConnectionScope = parseBuilderConnectionScope(pending.connectionScope);
|
|
3624
3592
|
if (requestedConnectionScope === "invalid") {
|
|
3625
|
-
return fail(403, "Builder
|
|
3593
|
+
return fail(403, "Builder.io setup could not be verified. Start the setup again.", ownerEmail, "callback_verification_failed", tracking);
|
|
3626
3594
|
}
|
|
3627
3595
|
const pendingOrgId = typeof pending.orgId === "string" ? pending.orgId : null;
|
|
3628
3596
|
// Authority is read again after the external OAuth round trip, in
|
|
@@ -3660,11 +3628,11 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
3660
3628
|
});
|
|
3661
3629
|
}
|
|
3662
3630
|
catch {
|
|
3663
|
-
return fail(500, "Builder credentials could not be saved.
|
|
3631
|
+
return fail(500, "Builder credentials could not be saved. Start Builder.io setup again.", ownerEmail, "credential_write_failed", tracking);
|
|
3664
3632
|
}
|
|
3665
3633
|
const consumed = await consumeBuilderConnectPendingState(state);
|
|
3666
3634
|
if (!consumed) {
|
|
3667
|
-
return fail(403, "No active Builder
|
|
3635
|
+
return fail(403, "No active Builder.io setup was found. Start it again from Settings.", ownerEmail, "callback_verification_failed", tracking);
|
|
3668
3636
|
}
|
|
3669
3637
|
dropConnectStateCookie(state);
|
|
3670
3638
|
try {
|
|
@@ -3674,7 +3642,7 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
3674
3642
|
]);
|
|
3675
3643
|
}
|
|
3676
3644
|
catch {
|
|
3677
|
-
return fail(500, "Builder credentials could not be saved.
|
|
3645
|
+
return fail(500, "Builder credentials could not be saved. Start Builder.io setup again.", ownerEmail, "credential_write_failed", tracking);
|
|
3678
3646
|
}
|
|
3679
3647
|
await trackBuilderLifecycle(event, "builder connect succeeded", ownerEmail, {
|
|
3680
3648
|
...builderConnectTrackingProperties(tracking),
|
|
@@ -4251,7 +4219,7 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
4251
4219
|
}
|
|
4252
4220
|
setResponseStatus(event, 503);
|
|
4253
4221
|
return {
|
|
4254
|
-
error: "No object storage is connected.
|
|
4222
|
+
error: "No object storage is connected. Use Builder.io's managed storage (free) or add your own S3-compatible storage keys in Settings → File uploads.",
|
|
4255
4223
|
};
|
|
4256
4224
|
}));
|
|
4257
4225
|
// ─── Voice transcription (Whisper) ───────────────────────────────
|
|
@@ -69,7 +69,7 @@ export class FeatureNotConfiguredError extends Error {
|
|
|
69
69
|
byokDocsUrl;
|
|
70
70
|
constructor(opts) {
|
|
71
71
|
super(opts.message ??
|
|
72
|
-
`Feature requires credential "${opts.requiredCredential}".
|
|
72
|
+
`Feature requires credential "${opts.requiredCredential}". Use Builder.io (free tier available) or set your own key.`);
|
|
73
73
|
this.name = "FeatureNotConfiguredError";
|
|
74
74
|
this.requiredCredential = opts.requiredCredential;
|
|
75
75
|
this.builderConnectUrl = opts.builderConnectUrl;
|
|
@@ -864,7 +864,7 @@ export async function getBuilderCredentialAuthFailure(creds = {}) {
|
|
|
864
864
|
fingerprint,
|
|
865
865
|
message: typeof row.message === "string" && row.message
|
|
866
866
|
? row.message
|
|
867
|
-
: "Builder rejected the connected credentials.
|
|
867
|
+
: "Builder rejected the connected credentials. Sign in to Builder.io again (free tier available).",
|
|
868
868
|
status: typeof row.status === "number" ? row.status : undefined,
|
|
869
869
|
code: typeof row.code === "string" ? row.code : undefined,
|
|
870
870
|
at,
|
|
@@ -888,7 +888,7 @@ export async function recordBuilderCredentialAuthFailure(details) {
|
|
|
888
888
|
await putSetting(settingKey, {
|
|
889
889
|
fingerprint,
|
|
890
890
|
message: details?.message ||
|
|
891
|
-
"Builder rejected the connected credentials.
|
|
891
|
+
"Builder rejected the connected credentials. Sign in to Builder.io again (free tier available).",
|
|
892
892
|
...(typeof details?.status === "number" && { status: details.status }),
|
|
893
893
|
...(details?.code && { code: details.code }),
|
|
894
894
|
strikes,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export declare const OPEN_ROUTE_SUBPATH = "/open";
|
|
2
2
|
export declare const DESKTOP_OPEN_URL = "agentnative://open";
|
|
3
3
|
export declare const VSCODE_OPEN_URL = "vscode://builder.agent-native/open";
|
|
4
|
+
export declare function isSameOriginUrl(urlOrPath: string, origin?: string): boolean;
|
|
5
|
+
export declare function isAgentNativeOpenUrl(urlOrPath: string, origin?: string, basePath?: string): boolean;
|
|
4
6
|
export interface DeepLinkInput {
|
|
5
7
|
app?: string;
|
|
6
8
|
view: string;
|
package/dist/server/deep-link.js
CHANGED
|
@@ -1,9 +1,31 @@
|
|
|
1
|
-
import { withCollapsedAgentSidebarParam } from "../shared/agent-sidebar-url.js";
|
|
2
|
-
import { getConfiguredAppBasePath, normalizeAppBasePath, } from "./app-base-path.js";
|
|
3
|
-
import { publicFrameworkPath } from "./framework-route-prefix.js";
|
|
1
|
+
import { AGENT_NATIVE_OPEN_PATH, withCollapsedAgentSidebarParam, } from "../shared/agent-sidebar-url.js";
|
|
2
|
+
import { getConfiguredAppBasePath, normalizeAppBasePath, stripAppBasePath, } from "./app-base-path.js";
|
|
3
|
+
import { canonicalFrameworkPathname, publicFrameworkPath, } from "./framework-route-prefix.js";
|
|
4
4
|
export const OPEN_ROUTE_SUBPATH = "/open";
|
|
5
5
|
export const DESKTOP_OPEN_URL = "agentnative://open";
|
|
6
6
|
export const VSCODE_OPEN_URL = "vscode://builder.agent-native/open";
|
|
7
|
+
export function isSameOriginUrl(urlOrPath, origin) {
|
|
8
|
+
const hasAuthority = /^[a-z][a-z0-9+.-]*:/i.test(urlOrPath) || urlOrPath.startsWith("//");
|
|
9
|
+
if (!hasAuthority)
|
|
10
|
+
return true;
|
|
11
|
+
if (!origin)
|
|
12
|
+
return false;
|
|
13
|
+
if (!URL.canParse(origin) || !URL.canParse(urlOrPath, origin))
|
|
14
|
+
return false;
|
|
15
|
+
return new URL(urlOrPath, origin).origin === new URL(origin).origin;
|
|
16
|
+
}
|
|
17
|
+
export function isAgentNativeOpenUrl(urlOrPath, origin, basePath = getConfiguredAppBasePath()) {
|
|
18
|
+
const base = origin ?? "http://agent-native.invalid";
|
|
19
|
+
if (!URL.canParse(urlOrPath, base))
|
|
20
|
+
return false;
|
|
21
|
+
const url = new URL(urlOrPath, base);
|
|
22
|
+
if (url.protocol === "agentnative:" && url.host === "open")
|
|
23
|
+
return true;
|
|
24
|
+
if (!isSameOriginUrl(urlOrPath, origin))
|
|
25
|
+
return false;
|
|
26
|
+
return (canonicalFrameworkPathname(stripAppBasePath(url.pathname, basePath)) ===
|
|
27
|
+
AGENT_NATIVE_OPEN_PATH);
|
|
28
|
+
}
|
|
7
29
|
function buildQuery(input) {
|
|
8
30
|
const sp = new URLSearchParams();
|
|
9
31
|
if (input.app)
|
|
@@ -24,6 +46,11 @@ export function buildDeepLink(input) {
|
|
|
24
46
|
export function toAbsoluteOpenUrl(urlOrPath, origin) {
|
|
25
47
|
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(urlOrPath))
|
|
26
48
|
return urlOrPath;
|
|
49
|
+
if (urlOrPath.startsWith("//")) {
|
|
50
|
+
return origin && URL.canParse(urlOrPath, origin)
|
|
51
|
+
? new URL(urlOrPath, origin).toString()
|
|
52
|
+
: urlOrPath;
|
|
53
|
+
}
|
|
27
54
|
const basePath = getConfiguredAppBasePath();
|
|
28
55
|
const path = withBasePath(urlOrPath, basePath);
|
|
29
56
|
if (!origin)
|
|
@@ -34,7 +34,7 @@ async function resolveFusionAuth(requiredScope) {
|
|
|
34
34
|
requiredScope,
|
|
35
35
|
});
|
|
36
36
|
if (!authorization) {
|
|
37
|
-
throw new Error("Builder.io is not connected.
|
|
37
|
+
throw new Error("Builder.io is not connected. Sign in to Builder.io in Settings to continue.");
|
|
38
38
|
}
|
|
39
39
|
if (authorization.source === "legacy" && !authorization.legacyPublicKey) {
|
|
40
40
|
throw new Error("Builder legacy credentials require BUILDER_PUBLIC_KEY for this request.");
|