@agent-native/core 0.153.0 → 0.153.1
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/templates/calendar/app/components/calendar/DayView.tsx +3 -2
- package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +3 -2
- package/corpus/templates/calendar/app/lib/event-layout.ts +16 -35
- package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +27 -44
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +72 -33
- package/corpus/templates/clips/app/components/sharing/share-ui.tsx +18 -6
- package/corpus/templates/clips/desktop/src/app.tsx +1 -1
- package/corpus/templates/clips/desktop/src/styles.css +10 -2
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +2 -2
- package/corpus/templates/clips/desktop/src-tauri/src/util.rs +1 -1
- package/corpus/templates/factory/app/routes/factory.tsx +0 -3
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +1 -1
- package/corpus/templates/slides/app/components/editor/ShareDialog.tsx +28 -25
- package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +50 -0
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +12 -60
- package/dist/agent/production-agent.d.ts +8 -0
- package/dist/agent/production-agent.js +25 -2
- package/dist/agent/run-manager.js +13 -3
- package/dist/client/AgentPanel.js +6 -4
- package/dist/client/org/WorkspaceNotice.js +7 -4
- package/dist/client/sharing/ShareButton.d.ts +2 -2
- package/dist/client/sharing/ShareButton.js +18 -13
- package/dist/client/sharing/ShareDialog.d.ts +1 -1
- package/dist/client/sharing/ShareDialog.js +13 -10
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.js +18 -4
- package/dist/server/better-auth-instance.js +8 -1
- package/dist/server/magic-link-attribution.d.ts +21 -0
- package/dist/server/magic-link-attribution.js +115 -0
- package/dist/server/realtime-token.d.ts +1 -1
- package/package.json +1 -1
|
@@ -27,10 +27,10 @@ export declare function resolveAgentEngineApiKeyWriteTarget(event: H3Event, scop
|
|
|
27
27
|
export declare function createAgentEngineApiKeyHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
28
28
|
error: any;
|
|
29
29
|
} | {
|
|
30
|
-
error?: undefined;
|
|
31
30
|
ok: boolean;
|
|
32
31
|
key: string;
|
|
33
32
|
baseUrlKey?: string;
|
|
34
33
|
scope: AgentEngineApiKeyScope;
|
|
34
|
+
error?: undefined;
|
|
35
35
|
}>>;
|
|
36
36
|
export {};
|
package/dist/server/auth.js
CHANGED
|
@@ -56,9 +56,9 @@ import { isValidWorkspaceAppIdFormat } from "../shared/workspace-app-id.js";
|
|
|
56
56
|
import { injectAnalyticsIntoHtml } from "./analytics.js";
|
|
57
57
|
import { getConfiguredAppBasePath } from "./app-base-path.js";
|
|
58
58
|
import { getAppProductionUrl } from "./app-url.js";
|
|
59
|
-
import { readAnalyticsAnonymousId, signupAttributionFromCookieHeader, } from "./attribution.js";
|
|
59
|
+
import { readAnalyticsAnonymousId, readFirstTouchAttribution, signupAttributionFromCookieHeader, } from "./attribution.js";
|
|
60
60
|
import { getAuthLoginMode } from "./auth-login-mode.js";
|
|
61
|
-
import { createBetterAuthSessionForEmail, ensureGoogleAuthIdentity, getBetterAuth, getBetterAuthSync, isDeployPreview, } from "./better-auth-instance.js";
|
|
61
|
+
import { createBetterAuthSessionForEmail, ensureGoogleAuthIdentity, getAuthSecret, getBetterAuth, getBetterAuthSync, isDeployPreview, } from "./better-auth-instance.js";
|
|
62
62
|
import { BUILDER_CONNECT_OWNER_COOKIE, BUILDER_CONNECT_PARAM, BUILDER_RELAY_PATH, BUILDER_RELAY_STATE_PARAM, BUILDER_STATE_PARAM, verifyBuilderCallbackStateAndGetOwner, verifyBuilderConnectTokenAndGetOwner, verifyBuilderPreviewRelayStateForCallback, } from "./builder-browser.js";
|
|
63
63
|
import { resolveAuthCookieNamespace } from "./cookie-namespace.js";
|
|
64
64
|
import { getAllowedCorsOrigin, readCorsAllowedOrigins, } from "./cors-origins.js";
|
|
@@ -67,6 +67,7 @@ import { resolveGoogleSignInCredentials } from "./google-oauth-credentials.js";
|
|
|
67
67
|
import { isElectron as isElectronRequest, getAppBasePath, getAppUrl, getOrigin, encodeOAuthState, decodeOAuthState, createOAuthSession, oauthCallbackResponse, oauthDesktopExchangePage, oauthErrorPage, resolveOAuthRedirectUri, isAllowedOAuthRedirectUri, } from "./google-oauth.js";
|
|
68
68
|
import { isCanonicalAgentNativeAppRequest, isDesktopSsoCanaryUserAgent, isIdentitySsoEnabled, } from "./identity-sso-store.js";
|
|
69
69
|
import { ensureCanonicalUserForLegacySession } from "./legacy-auth-migration.js";
|
|
70
|
+
import { encodeMagicLinkSignupAttribution, MAGIC_LINK_ATTRIBUTION_PARAM, } from "./magic-link-attribution.js";
|
|
70
71
|
import { safeOAuthReturnUrl } from "./oauth-return-url.js";
|
|
71
72
|
import { getOnboardingHtml, getResetPasswordHtml, } from "./onboarding-html.js";
|
|
72
73
|
import { captureAuthError } from "./sentry.js";
|
|
@@ -3333,12 +3334,25 @@ async function mountBetterAuthRoutes(app, options) {
|
|
|
3333
3334
|
callbackPath = withDesktopMagicLinkFlow(callbackPath, desktopFlow);
|
|
3334
3335
|
}
|
|
3335
3336
|
const callbackURL = betterAuthCallbackURL(callbackPath, true, event);
|
|
3336
|
-
const
|
|
3337
|
+
const cookieHeader = getHeader(event, "cookie") ?? null;
|
|
3338
|
+
const signupAttribution = signupAttributionFromCookieHeader(cookieHeader);
|
|
3339
|
+
const signupAnonymousId = readAnalyticsAnonymousId(cookieHeader);
|
|
3340
|
+
const hasSignupAttribution = !!readFirstTouchAttribution(cookieHeader) || !!signupAnonymousId;
|
|
3341
|
+
const attributionToken = hasSignupAttribution
|
|
3342
|
+
? encodeMagicLinkSignupAttribution({
|
|
3343
|
+
attribution: signupAttribution,
|
|
3344
|
+
anonymousId: signupAnonymousId,
|
|
3345
|
+
}, getAuthSecret())
|
|
3346
|
+
: undefined;
|
|
3347
|
+
const newUserCallbackUrl = new URL(`${getAppBasePath()}/_agent-native/auth/magic-link/new-user?return=${encodeURIComponent(callbackPath)}`, getOrigin(event));
|
|
3348
|
+
if (attributionToken) {
|
|
3349
|
+
newUserCallbackUrl.searchParams.set(MAGIC_LINK_ATTRIBUTION_PARAM, attributionToken);
|
|
3350
|
+
}
|
|
3337
3351
|
await auth.api.signInMagicLink({
|
|
3338
3352
|
body: {
|
|
3339
3353
|
email,
|
|
3340
3354
|
callbackURL,
|
|
3341
|
-
newUserCallbackURL: betterAuthCallbackURL(
|
|
3355
|
+
newUserCallbackURL: betterAuthCallbackURL(`${newUserCallbackUrl.pathname}${newUserCallbackUrl.search}`, true, event),
|
|
3342
3356
|
},
|
|
3343
3357
|
headers: event.headers,
|
|
3344
3358
|
});
|
|
@@ -32,6 +32,7 @@ import { getWorkspaceA2ADerivedSecret } from "./derived-secret.js";
|
|
|
32
32
|
import { renderMagicLinkEmail, renderResetPasswordEmail, renderVerifySignupEmail, } from "./email-templates.js";
|
|
33
33
|
import { getEmailReadiness, sendEmail } from "./email.js";
|
|
34
34
|
import { resolveGoogleSignInCredentials } from "./google-oauth-credentials.js";
|
|
35
|
+
import { readMagicLinkSignupAttribution } from "./magic-link-attribution.js";
|
|
35
36
|
export { getAuthLoginMode, resolveAuthLoginMode, resolveAuthLoginModeFromReadiness, } from "./auth-login-mode.js";
|
|
36
37
|
async function flushSignupTracking() {
|
|
37
38
|
try {
|
|
@@ -982,8 +983,14 @@ async function createBetterAuthInstance(config) {
|
|
|
982
983
|
const cookieHeader = context?.headers?.get("cookie") ??
|
|
983
984
|
context?.request?.headers?.get("cookie") ??
|
|
984
985
|
null;
|
|
986
|
+
const magicLinkAttribution = context?.request?.url?.includes("newUserCallbackURL")
|
|
987
|
+
? readMagicLinkSignupAttribution(context.request.url, getAuthSecret())
|
|
988
|
+
: undefined;
|
|
985
989
|
attribution = signupAttributionFromCookieHeader(cookieHeader);
|
|
986
|
-
|
|
990
|
+
attribution = magicLinkAttribution?.attribution ?? attribution;
|
|
991
|
+
anonymousId =
|
|
992
|
+
magicLinkAttribution?.anonymousId ??
|
|
993
|
+
readAnalyticsAnonymousId(cookieHeader);
|
|
987
994
|
}
|
|
988
995
|
catch (err) {
|
|
989
996
|
console.error("[auth] failed to derive signup attribution", err);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Query parameter carried by Better Auth's new-user callback URL. */
|
|
2
|
+
export declare const MAGIC_LINK_ATTRIBUTION_PARAM = "signup_attribution";
|
|
3
|
+
export interface MagicLinkSignupAttribution {
|
|
4
|
+
attribution?: Record<string, string>;
|
|
5
|
+
anonymousId?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Sign the attribution captured when a magic link is requested. The token is
|
|
9
|
+
* analytics context only; it never authorizes the user or changes the return
|
|
10
|
+
* URL. The Better Auth secret keeps the callback from accepting forged data.
|
|
11
|
+
*/
|
|
12
|
+
export declare function encodeMagicLinkSignupAttribution(value: MagicLinkSignupAttribution, secret: string, now?: number): string | undefined;
|
|
13
|
+
/** Verify and decode a token previously produced by
|
|
14
|
+
* {@link encodeMagicLinkSignupAttribution}. */
|
|
15
|
+
export declare function decodeMagicLinkSignupAttribution(token: string | null | undefined, secret: string, now?: number): MagicLinkSignupAttribution | null | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* Read the signed handoff from Better Auth's magic-link verification request.
|
|
18
|
+
* Better Auth copies `newUserCallbackURL` into the verification URL, so this
|
|
19
|
+
* remains available even when the link is opened on another device.
|
|
20
|
+
*/
|
|
21
|
+
export declare function readMagicLinkSignupAttribution(requestUrl: string | undefined, secret: string, now?: number): MagicLinkSignupAttribution | null | undefined;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import { normalizeAnalyticsAnonymousId } from "../shared/analytics-anonymous-id.js";
|
|
3
|
+
/** Query parameter carried by Better Auth's new-user callback URL. */
|
|
4
|
+
export const MAGIC_LINK_ATTRIBUTION_PARAM = "signup_attribution";
|
|
5
|
+
/** Better Auth's magic-link token is valid for five minutes; keep this handoff
|
|
6
|
+
* slightly longer so clock skew does not discard a still-valid link. */
|
|
7
|
+
const MAGIC_LINK_ATTRIBUTION_TTL_SECONDS = 10 * 60;
|
|
8
|
+
const MAX_ATTRIBUTION_FIELDS = 32;
|
|
9
|
+
const MAX_ATTRIBUTION_VALUE_LENGTH = 200;
|
|
10
|
+
function sanitizeAttribution(value) {
|
|
11
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
const out = {};
|
|
15
|
+
for (const [key, raw] of Object.entries(value).slice(0, MAX_ATTRIBUTION_FIELDS)) {
|
|
16
|
+
if (!/^[a-z][a-z0-9_]{0,63}$/.test(key) ||
|
|
17
|
+
typeof raw !== "string" ||
|
|
18
|
+
raw.length === 0) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
out[key] = raw.slice(0, MAX_ATTRIBUTION_VALUE_LENGTH);
|
|
22
|
+
}
|
|
23
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
24
|
+
}
|
|
25
|
+
function encodePayload(payload) {
|
|
26
|
+
return Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Sign the attribution captured when a magic link is requested. The token is
|
|
30
|
+
* analytics context only; it never authorizes the user or changes the return
|
|
31
|
+
* URL. The Better Auth secret keeps the callback from accepting forged data.
|
|
32
|
+
*/
|
|
33
|
+
export function encodeMagicLinkSignupAttribution(value, secret, now = Date.now()) {
|
|
34
|
+
if (!secret)
|
|
35
|
+
return undefined;
|
|
36
|
+
const attribution = sanitizeAttribution(value.attribution);
|
|
37
|
+
const anonymousId = normalizeAnalyticsAnonymousId(value.anonymousId);
|
|
38
|
+
if (!attribution && !anonymousId)
|
|
39
|
+
return undefined;
|
|
40
|
+
const payload = {
|
|
41
|
+
exp: Math.floor(now / 1000) + MAGIC_LINK_ATTRIBUTION_TTL_SECONDS,
|
|
42
|
+
...(attribution ? { attribution } : {}),
|
|
43
|
+
...(anonymousId ? { anonymousId } : {}),
|
|
44
|
+
};
|
|
45
|
+
const data = encodePayload(payload);
|
|
46
|
+
const signature = crypto
|
|
47
|
+
.createHmac("sha256", secret)
|
|
48
|
+
.update(data)
|
|
49
|
+
.digest("base64url");
|
|
50
|
+
return `${data}.${signature}`;
|
|
51
|
+
}
|
|
52
|
+
/** Verify and decode a token previously produced by
|
|
53
|
+
* {@link encodeMagicLinkSignupAttribution}. */
|
|
54
|
+
export function decodeMagicLinkSignupAttribution(token, secret, now = Date.now()) {
|
|
55
|
+
if (token === null || token === undefined)
|
|
56
|
+
return undefined;
|
|
57
|
+
if (!token || !secret)
|
|
58
|
+
return null;
|
|
59
|
+
try {
|
|
60
|
+
const dotIndex = token.lastIndexOf(".");
|
|
61
|
+
if (dotIndex <= 0 || dotIndex === token.length - 1)
|
|
62
|
+
return null;
|
|
63
|
+
const data = token.slice(0, dotIndex);
|
|
64
|
+
const signature = token.slice(dotIndex + 1);
|
|
65
|
+
const expected = crypto
|
|
66
|
+
.createHmac("sha256", secret)
|
|
67
|
+
.update(data)
|
|
68
|
+
.digest("base64url");
|
|
69
|
+
if (signature.length !== expected.length ||
|
|
70
|
+
!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
const payload = JSON.parse(Buffer.from(data, "base64url").toString("utf8"));
|
|
74
|
+
if (typeof payload.exp !== "number" ||
|
|
75
|
+
payload.exp < Math.floor(now / 1000)) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const attribution = sanitizeAttribution(payload.attribution);
|
|
79
|
+
const anonymousId = normalizeAnalyticsAnonymousId(payload.anonymousId);
|
|
80
|
+
if (!attribution && !anonymousId)
|
|
81
|
+
return undefined;
|
|
82
|
+
return {
|
|
83
|
+
...(attribution ? { attribution } : {}),
|
|
84
|
+
...(anonymousId ? { anonymousId } : {}),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// coercion-ok: malformed signed input is distinct from absent context.
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Read the signed handoff from Better Auth's magic-link verification request.
|
|
94
|
+
* Better Auth copies `newUserCallbackURL` into the verification URL, so this
|
|
95
|
+
* remains available even when the link is opened on another device.
|
|
96
|
+
*/
|
|
97
|
+
export function readMagicLinkSignupAttribution(requestUrl, secret, now = Date.now()) {
|
|
98
|
+
if (!requestUrl)
|
|
99
|
+
return undefined;
|
|
100
|
+
try {
|
|
101
|
+
const verificationUrl = new URL(requestUrl, "http://localhost");
|
|
102
|
+
if (!verificationUrl.pathname.endsWith("/magic-link/verify")) {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
const callback = verificationUrl.searchParams.get("newUserCallbackURL");
|
|
106
|
+
if (!callback)
|
|
107
|
+
return undefined;
|
|
108
|
+
const callbackUrl = new URL(callback, verificationUrl.origin);
|
|
109
|
+
return decodeMagicLinkSignupAttribution(callbackUrl.searchParams.get(MAGIC_LINK_ATTRIBUTION_PARAM), secret, now);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// coercion-ok: malformed request input is distinct from absent context.
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -26,8 +26,8 @@ export declare function createRealtimeTokenHandler(): import("h3").EventHandlerW
|
|
|
26
26
|
expiresAt?: undefined;
|
|
27
27
|
ttlSeconds?: undefined;
|
|
28
28
|
} | {
|
|
29
|
+
error?: undefined;
|
|
29
30
|
token: string;
|
|
30
31
|
expiresAt: string;
|
|
31
32
|
ttlSeconds: number;
|
|
32
|
-
error?: undefined;
|
|
33
33
|
}>>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.153.
|
|
3
|
+
"version": "0.153.1",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|