@agent-native/core 0.176.4-nightly-20260902015502 → 0.176.4-nightly-20260902024948
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/forms/actions/db-connect.ts +6 -3
- package/corpus/templates/forms/actions/db-status.ts +8 -8
- package/corpus/templates/forms/app/hooks/use-db-status.ts +6 -2
- package/corpus/templates/forms/server/plugins/core-routes.ts +35 -0
- package/corpus/templates/forms/server/routes/api/db-health.get.ts +2 -1
- package/dist/client/EnvironmentBadge.d.ts +1 -1
- package/dist/client/EnvironmentBadge.js +14 -2
- package/dist/client/analytics.d.ts +1 -0
- package/dist/client/app-providers.js +10 -1
- package/dist/client/sign-out.js +34 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/db/client.d.ts +1 -0
- package/dist/db/client.js +63 -11
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.js +1 -1
- package/dist/db/runtime-diagnostics.d.ts +5 -0
- package/dist/db/runtime-diagnostics.js +16 -14
- package/dist/deploy/build.js +38 -0
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +3 -3
- package/dist/provider-api/actions/custom-provider-registration.d.ts +6 -6
- package/dist/provider-api/actions/provider-api.d.ts +9 -9
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/app-origin-config.d.ts +1 -0
- package/dist/server/app-origin-config.js +36 -0
- package/dist/server/auth.js +3 -5
- package/dist/server/beta-opt-out-html.d.ts +1 -1
- package/dist/server/beta-opt-out-html.js +10 -2
- package/dist/server/core-routes-plugin.js +4 -2
- package/dist/server/create-server.js +13 -7
- package/dist/server/onboarding-html.d.ts +1 -0
- package/dist/server/onboarding-html.js +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/environment-lanes.d.ts +3 -0
- package/dist/shared/environment-lanes.js +3 -0
- package/dist/shared/index.d.ts +1 -1
- package/dist/shared/index.js +1 -1
- package/dist/shared/ssr-beta-redirect.d.ts +8 -0
- package/dist/shared/ssr-beta-redirect.js +215 -0
- package/package.json +1 -1
|
@@ -5,15 +5,18 @@ export default defineAction({
|
|
|
5
5
|
description:
|
|
6
6
|
"Explain how to configure deployment-level database connection settings.",
|
|
7
7
|
schema: z.object({
|
|
8
|
-
url: z.string().describe("
|
|
9
|
-
token: z
|
|
8
|
+
url: z.string().describe("Forms database URL value (required)"),
|
|
9
|
+
token: z
|
|
10
|
+
.string()
|
|
11
|
+
.optional()
|
|
12
|
+
.describe("Forms database auth token value (optional)"),
|
|
10
13
|
}),
|
|
11
14
|
http: false,
|
|
12
15
|
run: async (args) => {
|
|
13
16
|
const maskedUrl = args.url.replace(/\/\/.*@/, "//***@");
|
|
14
17
|
return [
|
|
15
18
|
`Database connection not written: ${maskedUrl}.`,
|
|
16
|
-
"
|
|
19
|
+
"For Forms, configure FORMS_DATABASE_URL_UNPOOLED when available, otherwise FORMS_DATABASE_URL. DATABASE_URL_UNPOOLED and DATABASE_URL are supported fallbacks. Use the matching *_DATABASE_AUTH_TOKEN for libSQL.",
|
|
17
20
|
"Configure them with your hosting provider and redeploy the app.",
|
|
18
21
|
].join(" ");
|
|
19
22
|
},
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core/action";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
getDbExec,
|
|
4
|
+
getRuntimeDatabaseUrl,
|
|
5
|
+
isLocalDatabase,
|
|
6
|
+
} from "@agent-native/core/db";
|
|
3
7
|
import { z } from "zod";
|
|
4
8
|
|
|
5
9
|
export default defineAction({
|
|
@@ -7,15 +11,11 @@ export default defineAction({
|
|
|
7
11
|
schema: z.object({}),
|
|
8
12
|
http: false,
|
|
9
13
|
run: async () => {
|
|
10
|
-
const url =
|
|
11
|
-
const isLocal =
|
|
14
|
+
const url = getRuntimeDatabaseUrl("file:./data/app.db");
|
|
15
|
+
const isLocal = isLocalDatabase();
|
|
12
16
|
|
|
13
17
|
try {
|
|
14
|
-
const
|
|
15
|
-
url,
|
|
16
|
-
authToken: process.env.DATABASE_AUTH_TOKEN,
|
|
17
|
-
});
|
|
18
|
-
const result = await client.execute("SELECT 1 as ok");
|
|
18
|
+
const result = await getDbExec().execute("SELECT 1 as ok");
|
|
19
19
|
return {
|
|
20
20
|
url: isLocal ? url : url.replace(/\/\/.*@/, "//***@"),
|
|
21
21
|
mode: isLocal ? "local (SQLite file)" : "remote (cloud)",
|
|
@@ -19,8 +19,12 @@ export function useDbStatus() {
|
|
|
19
19
|
staleTime: 30_000,
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
-
const
|
|
23
|
-
|
|
22
|
+
const configured =
|
|
23
|
+
data?.some(
|
|
24
|
+
(entry) =>
|
|
25
|
+
/(?:^|_)DATABASE_URL(?:_UNPOOLED)?$/.test(entry.key) &&
|
|
26
|
+
entry.configured,
|
|
27
|
+
) ?? false;
|
|
24
28
|
|
|
25
29
|
return {
|
|
26
30
|
configured,
|
|
@@ -3,12 +3,47 @@ import { createCoreRoutesPlugin } from "@agent-native/core/server";
|
|
|
3
3
|
export default createCoreRoutesPlugin({
|
|
4
4
|
googleOAuthManagedConnection: "not_applicable",
|
|
5
5
|
envKeys: [
|
|
6
|
+
{
|
|
7
|
+
key: "FORMS_DATABASE_URL",
|
|
8
|
+
label: "Forms Database URL",
|
|
9
|
+
required: false,
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
key: "FORMS_DATABASE_URL_UNPOOLED",
|
|
13
|
+
label: "Forms Unpooled Database URL",
|
|
14
|
+
required: false,
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
key: "FORMS_DATABASE_AUTH_TOKEN",
|
|
18
|
+
label: "Forms Database Auth Token",
|
|
19
|
+
required: false,
|
|
20
|
+
},
|
|
6
21
|
{ key: "DATABASE_URL", label: "Database URL", required: false },
|
|
22
|
+
{
|
|
23
|
+
key: "DATABASE_URL_UNPOOLED",
|
|
24
|
+
label: "Unpooled Database URL",
|
|
25
|
+
required: false,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
key: "NETLIFY_DATABASE_URL",
|
|
29
|
+
label: "Netlify Database URL",
|
|
30
|
+
required: false,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
key: "NETLIFY_DATABASE_URL_UNPOOLED",
|
|
34
|
+
label: "Netlify Unpooled Database URL",
|
|
35
|
+
required: false,
|
|
36
|
+
},
|
|
7
37
|
{
|
|
8
38
|
key: "DATABASE_AUTH_TOKEN",
|
|
9
39
|
label: "Database Auth Token",
|
|
10
40
|
required: false,
|
|
11
41
|
},
|
|
42
|
+
{
|
|
43
|
+
key: "NETLIFY_DATABASE_AUTH_TOKEN",
|
|
44
|
+
label: "Netlify Database Auth Token",
|
|
45
|
+
required: false,
|
|
46
|
+
},
|
|
12
47
|
{
|
|
13
48
|
key: "TURNSTILE_SECRET_KEY",
|
|
14
49
|
label: "Turnstile Secret Key",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getRuntimeDatabaseUrl } from "@agent-native/core/db";
|
|
1
2
|
import { sql } from "drizzle-orm";
|
|
2
3
|
import { defineEventHandler } from "h3";
|
|
3
4
|
|
|
@@ -7,7 +8,7 @@ export default defineEventHandler(async () => {
|
|
|
7
8
|
try {
|
|
8
9
|
const db = getDb();
|
|
9
10
|
await db.run(sql`SELECT 1`);
|
|
10
|
-
const url =
|
|
11
|
+
const url = getRuntimeDatabaseUrl("file:./data/app.db");
|
|
11
12
|
return { ok: true, local: url.startsWith("file:") };
|
|
12
13
|
} catch (e) {
|
|
13
14
|
return { ok: false, error: e instanceof Error ? e.message : "Unknown" };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentNativeDeploymentEnvironment, AgentNativeConfig } from "../config.js";
|
|
2
|
-
export { BETA_FORCE_QUERY_PARAM, BETA_FORCE_SESSION_STORAGE_KEY, BETA_OPT_OUT_DURATION_MS, BETA_OPT_OUT_QUERY_PARAM, BETA_OPT_OUT_STORAGE_KEY, buildEnvironmentOptOutUrl, buildEnvironmentUrl, resolveEnvironmentTargets, type EnvironmentBadgeTargets, } from "../shared/environment-lanes.js";
|
|
2
|
+
export { BETA_FORCE_QUERY_PARAM, BETA_FORCE_SESSION_STORAGE_KEY, BETA_OPT_OUT_DURATION_MS, BETA_OPT_OUT_QUERY_PARAM, BETA_OPT_OUT_STORAGE_KEY, BETA_REDIRECT_DURATION_MS, BETA_REDIRECT_STORAGE_KEY, buildEnvironmentOptOutUrl, buildEnvironmentUrl, resolveEnvironmentTargets, type EnvironmentBadgeTargets, } from "../shared/environment-lanes.js";
|
|
3
3
|
export declare function isBuilderIoEmployee(email: string | null | undefined): boolean;
|
|
4
4
|
export declare function isAgentNativeDesktopUserAgent(userAgent: string | undefined): boolean;
|
|
5
5
|
export declare function resolveEnvironmentChannel(config: AgentNativeConfig, hostname: string | undefined): Extract<AgentNativeDeploymentEnvironment, "local" | "beta" | "production"> | null;
|
|
@@ -2,12 +2,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Button } from "@agent-native/toolkit/ui/button";
|
|
3
3
|
import { Popover, PopoverContent, PopoverTrigger, } from "@agent-native/toolkit/ui/popover";
|
|
4
4
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
5
|
-
import { BETA_FORCE_QUERY_PARAM, BETA_FORCE_SESSION_STORAGE_KEY, BETA_OPT_OUT_QUERY_PARAM, BETA_OPT_OUT_STORAGE_KEY, buildEnvironmentOptOutUrl, buildEnvironmentUrl, resolveEnvironmentTargets, } from "../shared/environment-lanes.js";
|
|
5
|
+
import { BETA_FORCE_QUERY_PARAM, BETA_FORCE_SESSION_STORAGE_KEY, BETA_OPT_OUT_QUERY_PARAM, BETA_OPT_OUT_STORAGE_KEY, BETA_REDIRECT_DURATION_MS, BETA_REDIRECT_STORAGE_KEY, buildEnvironmentOptOutUrl, buildEnvironmentUrl, resolveEnvironmentTargets, } from "../shared/environment-lanes.js";
|
|
6
6
|
import { trackEvent } from "./analytics.js";
|
|
7
7
|
import { injectedAgentNativeConfig } from "./app-config.js";
|
|
8
8
|
import { useSession } from "./use-session.js";
|
|
9
9
|
import { cn } from "./utils.js";
|
|
10
|
-
export { BETA_FORCE_QUERY_PARAM, BETA_FORCE_SESSION_STORAGE_KEY, BETA_OPT_OUT_DURATION_MS, BETA_OPT_OUT_QUERY_PARAM, BETA_OPT_OUT_STORAGE_KEY, buildEnvironmentOptOutUrl, buildEnvironmentUrl, resolveEnvironmentTargets, } from "../shared/environment-lanes.js";
|
|
10
|
+
export { BETA_FORCE_QUERY_PARAM, BETA_FORCE_SESSION_STORAGE_KEY, BETA_OPT_OUT_DURATION_MS, BETA_OPT_OUT_QUERY_PARAM, BETA_OPT_OUT_STORAGE_KEY, BETA_REDIRECT_DURATION_MS, BETA_REDIRECT_STORAGE_KEY, buildEnvironmentOptOutUrl, buildEnvironmentUrl, resolveEnvironmentTargets, } from "../shared/environment-lanes.js";
|
|
11
11
|
export function isBuilderIoEmployee(email) {
|
|
12
12
|
return email?.trim().toLowerCase().endsWith("@builder.io") ?? false;
|
|
13
13
|
}
|
|
@@ -50,6 +50,16 @@ function readBetaOptOutUntil(now = Date.now()) {
|
|
|
50
50
|
}
|
|
51
51
|
return null;
|
|
52
52
|
}
|
|
53
|
+
function rememberBetaRedirectPreference() {
|
|
54
|
+
if (typeof window === "undefined")
|
|
55
|
+
return;
|
|
56
|
+
try {
|
|
57
|
+
window.localStorage.setItem(BETA_REDIRECT_STORAGE_KEY, String(Date.now() + BETA_REDIRECT_DURATION_MS));
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// coercion-ok: the marker is only a performance hint; session auth remains authoritative.
|
|
61
|
+
}
|
|
62
|
+
}
|
|
53
63
|
function rememberForcedProductionSession(sourceHref) {
|
|
54
64
|
let forcedByQuery = false;
|
|
55
65
|
try {
|
|
@@ -90,6 +100,7 @@ function consumeBetaOptOutQueryParam(sourceHref, now = Date.now()) {
|
|
|
90
100
|
try {
|
|
91
101
|
if (active) {
|
|
92
102
|
window.localStorage.setItem(BETA_OPT_OUT_STORAGE_KEY, String(Number(rawExpiry)));
|
|
103
|
+
window.localStorage.removeItem(BETA_REDIRECT_STORAGE_KEY);
|
|
93
104
|
}
|
|
94
105
|
window.history.replaceState(null, "", target.toString());
|
|
95
106
|
}
|
|
@@ -151,6 +162,7 @@ function ProductionEnvironmentBadge({ targets, }) {
|
|
|
151
162
|
const betaHref = buildEnvironmentUrl(window.location.href, targets.betaHost);
|
|
152
163
|
if (!betaHref || typeof window.location.replace !== "function")
|
|
153
164
|
return;
|
|
165
|
+
rememberBetaRedirectPreference();
|
|
154
166
|
didAutoRedirect.current = true;
|
|
155
167
|
trackEvent("environment switched", {
|
|
156
168
|
from_environment: "production",
|
|
@@ -56,6 +56,8 @@ import { ThemeProvider, useTheme } from "next-themes";
|
|
|
56
56
|
import { useEffect, useRef } from "react";
|
|
57
57
|
import { useInRouterContext } from "react-router";
|
|
58
58
|
import { isHumanReadableDocumentTitle, normalizeDocumentTitle, } from "../shared/document-title.js";
|
|
59
|
+
import { getSsrBetaRedirectScriptBody } from "../shared/ssr-beta-redirect.js";
|
|
60
|
+
import { agentNativePath } from "./api-path.js";
|
|
59
61
|
import { ClientOnly } from "./ClientOnly.js";
|
|
60
62
|
import { DefaultSpinner } from "./DefaultSpinner.js";
|
|
61
63
|
import { EnvironmentBadge } from "./EnvironmentBadge.js";
|
|
@@ -68,6 +70,11 @@ import { RuntimeConfigNotice } from "./RuntimeConfigNotice.js";
|
|
|
68
70
|
import { EMBEDDED_THEME_CHANGE_EVENT, applyEmbeddedThemeUpdate, parseEmbeddedThemeUpdate, } from "./theme.js";
|
|
69
71
|
import { createAgentNativeServerActionWebMcpRegistration } from "./webmcp.js";
|
|
70
72
|
const DEFAULT_TOASTER = (_jsx(Toaster, { richColors: true, position: "bottom-left", offset: { bottom: 44, left: 32 }, mobileOffset: { bottom: 44, left: 16 } }));
|
|
73
|
+
function EarlyBetaRedirectScript() {
|
|
74
|
+
return (_jsx("script", { "data-agent-native-beta-redirect": "1", dangerouslySetInnerHTML: {
|
|
75
|
+
__html: getSsrBetaRedirectScriptBody(agentNativePath("/_agent-native/auth/session")),
|
|
76
|
+
} }));
|
|
77
|
+
}
|
|
71
78
|
function RoutedAppEnhancements() {
|
|
72
79
|
const isInRouter = useInRouterContext();
|
|
73
80
|
if (!isInRouter)
|
|
@@ -165,5 +172,7 @@ export function AppProviders({ queryClient, isPublicPath = false, clientOnlyFall
|
|
|
165
172
|
if (isPublicPath) {
|
|
166
173
|
return (_jsx(ProvidersInner, { queryClient: queryClient, defaultTheme: defaultTheme, themeAttribute: themeAttribute, tooltipDelayDuration: tooltipDelayDuration, toaster: toaster, disableThemeTransitions: disableThemeTransitions, i18n: i18n, documentTitleFallback: documentTitleFallback, showProductionEnvironmentBadge: false, children: children }));
|
|
167
174
|
}
|
|
168
|
-
|
|
175
|
+
// Keep the bootstrap outside ClientOnly so the HTML parser can run it before
|
|
176
|
+
// the authenticated client bundle starts.
|
|
177
|
+
return (_jsxs(_Fragment, { children: [!sessionBypass && _jsx(EarlyBetaRedirectScript, {}), _jsx(ClientOnly, { fallback: fallback, children: _jsx(ProvidersInner, { queryClient: queryClient, defaultTheme: defaultTheme, themeAttribute: themeAttribute, tooltipDelayDuration: tooltipDelayDuration, toaster: toaster, disableThemeTransitions: disableThemeTransitions, i18n: i18n, documentTitleFallback: documentTitleFallback, showProductionEnvironmentBadge: !sessionBypass, children: _jsx(RequireSession, { bypass: sessionBypass, fallback: fallback, children: sessionBypass ? (children) : (_jsxs(FirstRunOnboardingStartupGate, { children: [_jsx(AutomaticWebMcpActionRegistration, {}), children] })) }) }) })] }));
|
|
169
178
|
}
|
package/dist/client/sign-out.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BETA_REDIRECT_SIGN_OUT_STORAGE_KEY, BETA_REDIRECT_STORAGE_KEY, } from "../shared/environment-lanes.js";
|
|
1
2
|
/**
|
|
2
3
|
* The one client-side sign-out.
|
|
3
4
|
*
|
|
@@ -27,6 +28,34 @@ import { beginSignOut, completeSignOut } from "./use-session.js";
|
|
|
27
28
|
const LOGOUT_PATH = "/_agent-native/auth/logout";
|
|
28
29
|
const SIGN_OUT_REQUEST_TIMEOUT_MS = 15_000;
|
|
29
30
|
let signOutOperation = null;
|
|
31
|
+
function setBetaRedirectSignOutSignal() {
|
|
32
|
+
const signOutWindow = window;
|
|
33
|
+
signOutWindow.__agentNativeBetaRedirectSignOutStarted = true;
|
|
34
|
+
try {
|
|
35
|
+
window.sessionStorage.setItem(BETA_REDIRECT_SIGN_OUT_STORAGE_KEY, "1");
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// coercion-ok: the in-memory signal still protects this document when storage is unavailable.
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function clearBetaRedirectSignOutSignal() {
|
|
42
|
+
const signOutWindow = window;
|
|
43
|
+
signOutWindow.__agentNativeBetaRedirectSignOutStarted = false;
|
|
44
|
+
try {
|
|
45
|
+
window.sessionStorage.removeItem(BETA_REDIRECT_SIGN_OUT_STORAGE_KEY);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// coercion-ok: sign-out navigation must not depend on optional storage.
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function clearBetaRedirectMarker() {
|
|
52
|
+
try {
|
|
53
|
+
window.localStorage.removeItem(BETA_REDIRECT_STORAGE_KEY);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// coercion-ok: local storage is optional; sign-out must still complete.
|
|
57
|
+
}
|
|
58
|
+
}
|
|
30
59
|
/**
|
|
31
60
|
* Sign the current user out and leave for the sign-in page when revocation
|
|
32
61
|
* succeeds. A failed revoke reloads the current document instead.
|
|
@@ -41,6 +70,7 @@ export function signOut(options = {}) {
|
|
|
41
70
|
return signOutOperation;
|
|
42
71
|
}
|
|
43
72
|
async function signOutFlow(options) {
|
|
73
|
+
setBetaRedirectSignOutSignal();
|
|
44
74
|
beginSignOut();
|
|
45
75
|
const controller = new AbortController();
|
|
46
76
|
const timeout = setTimeout(() => controller.abort(), SIGN_OUT_REQUEST_TIMEOUT_MS);
|
|
@@ -69,6 +99,8 @@ async function signOutFlow(options) {
|
|
|
69
99
|
if (!revoked) {
|
|
70
100
|
// Do not send an unrevoked session through sign-in's continuation, which
|
|
71
101
|
// can immediately authenticate it again.
|
|
102
|
+
clearBetaRedirectSignOutSignal();
|
|
103
|
+
clearBetaRedirectMarker();
|
|
72
104
|
window.location.reload();
|
|
73
105
|
return;
|
|
74
106
|
}
|
|
@@ -78,5 +110,7 @@ async function signOutFlow(options) {
|
|
|
78
110
|
completeSignOut();
|
|
79
111
|
// `replace`, not `assign`: the dead authenticated URL must not stay in
|
|
80
112
|
// history, or Back lands on a shell with no session.
|
|
113
|
+
clearBetaRedirectSignOutSignal();
|
|
114
|
+
clearBetaRedirectMarker();
|
|
81
115
|
window.location.replace(options.redirectTo ?? buildSignInReturnHref());
|
|
82
116
|
}
|
|
@@ -62,11 +62,11 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
|
|
|
62
62
|
error: string;
|
|
63
63
|
states?: undefined;
|
|
64
64
|
} | {
|
|
65
|
+
error?: undefined;
|
|
65
66
|
states: {
|
|
66
67
|
clientId: number;
|
|
67
68
|
state: string;
|
|
68
69
|
}[];
|
|
69
|
-
error?: undefined;
|
|
70
70
|
}>>;
|
|
71
71
|
/**
|
|
72
72
|
* GET /_agent-native/collab/:docId/users
|
|
@@ -77,9 +77,9 @@ export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("
|
|
|
77
77
|
error: string;
|
|
78
78
|
users?: undefined;
|
|
79
79
|
} | {
|
|
80
|
+
error?: undefined;
|
|
80
81
|
users: {
|
|
81
82
|
clientId: number;
|
|
82
83
|
lastSeen: number;
|
|
83
84
|
}[];
|
|
84
|
-
error?: undefined;
|
|
85
85
|
}>>;
|
package/dist/db/client.d.ts
CHANGED
|
@@ -60,6 +60,7 @@ export declare function getDatabaseUrl(fallback?: string): string;
|
|
|
60
60
|
* checks that intentionally inspect the configured deployment value.
|
|
61
61
|
*/
|
|
62
62
|
export declare function getRuntimeDatabaseUrl(fallback?: string): string;
|
|
63
|
+
export declare function getRuntimeDatabaseSource(fallback?: string): string;
|
|
63
64
|
/** Same per-app resolution for DATABASE_AUTH_TOKEN (used by Turso/libsql). */
|
|
64
65
|
export declare function getDatabaseAuthToken(): string | undefined;
|
|
65
66
|
/**
|
package/dist/db/client.js
CHANGED
|
@@ -59,6 +59,65 @@ function getConfiguredAppDatabaseUrl(suffix) {
|
|
|
59
59
|
function stripNeonPooler(url) {
|
|
60
60
|
return url.replace(/-pooler(\.[a-z0-9.-]+\.neon\.tech)/, "$1");
|
|
61
61
|
}
|
|
62
|
+
function envDatabaseValue(key) {
|
|
63
|
+
const value = process.env[key]?.trim();
|
|
64
|
+
return value || undefined;
|
|
65
|
+
}
|
|
66
|
+
function resolveRuntimeDatabase(fallback = "") {
|
|
67
|
+
const appName = getAppEnvPrefix();
|
|
68
|
+
if (appName) {
|
|
69
|
+
const appUnpooled = envDatabaseValue(`${appName}_DATABASE_URL_UNPOOLED`);
|
|
70
|
+
if (appUnpooled) {
|
|
71
|
+
return {
|
|
72
|
+
url: stripNeonPooler(appUnpooled),
|
|
73
|
+
source: `${appName}_DATABASE_URL_UNPOOLED`,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const appUrl = envDatabaseValue(`${appName}_DATABASE_URL`);
|
|
77
|
+
if (appUrl) {
|
|
78
|
+
return {
|
|
79
|
+
url: isServerlessRuntime() ? stripNeonPooler(appUrl) : appUrl,
|
|
80
|
+
source: `${appName}_DATABASE_URL`,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const configuredUnpooled = getAppConfig().runtime.databaseUrlUnpooled;
|
|
85
|
+
if (configuredUnpooled) {
|
|
86
|
+
const netlifyUnpooled = envDatabaseValue("NETLIFY_DATABASE_URL_UNPOOLED");
|
|
87
|
+
const databaseUnpooled = envDatabaseValue("DATABASE_URL_UNPOOLED");
|
|
88
|
+
return {
|
|
89
|
+
url: stripNeonPooler(configuredUnpooled),
|
|
90
|
+
source: netlifyUnpooled === configuredUnpooled
|
|
91
|
+
? "NETLIFY_DATABASE_URL_UNPOOLED"
|
|
92
|
+
: databaseUnpooled === configuredUnpooled
|
|
93
|
+
? "DATABASE_URL_UNPOOLED"
|
|
94
|
+
: "DATABASE_URL_UNPOOLED",
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
const netlifyUnpooled = envDatabaseValue("NETLIFY_DATABASE_URL_UNPOOLED");
|
|
98
|
+
if (netlifyUnpooled) {
|
|
99
|
+
return {
|
|
100
|
+
url: stripNeonPooler(netlifyUnpooled),
|
|
101
|
+
source: "NETLIFY_DATABASE_URL_UNPOOLED",
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const databaseUnpooled = envDatabaseValue("DATABASE_URL_UNPOOLED");
|
|
105
|
+
if (databaseUnpooled) {
|
|
106
|
+
return {
|
|
107
|
+
url: stripNeonPooler(databaseUnpooled),
|
|
108
|
+
source: "DATABASE_URL_UNPOOLED",
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const url = getDatabaseUrl(fallback);
|
|
112
|
+
return {
|
|
113
|
+
url: isServerlessRuntime() ? stripNeonPooler(url) : url,
|
|
114
|
+
source: envDatabaseValue("DATABASE_URL")
|
|
115
|
+
? "DATABASE_URL"
|
|
116
|
+
: envDatabaseValue("NETLIFY_DATABASE_URL")
|
|
117
|
+
? "NETLIFY_DATABASE_URL"
|
|
118
|
+
: "default",
|
|
119
|
+
};
|
|
120
|
+
}
|
|
62
121
|
/**
|
|
63
122
|
* Resolve the URL used by request-time database clients.
|
|
64
123
|
*
|
|
@@ -69,17 +128,10 @@ function stripNeonPooler(url) {
|
|
|
69
128
|
* checks that intentionally inspect the configured deployment value.
|
|
70
129
|
*/
|
|
71
130
|
export function getRuntimeDatabaseUrl(fallback = "") {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (appUrl)
|
|
77
|
-
return isServerlessRuntime() ? stripNeonPooler(appUrl) : appUrl;
|
|
78
|
-
const unpooled = getAppConfig().runtime.databaseUrlUnpooled;
|
|
79
|
-
if (unpooled)
|
|
80
|
-
return stripNeonPooler(unpooled);
|
|
81
|
-
const url = getDatabaseUrl(fallback);
|
|
82
|
-
return isServerlessRuntime() ? stripNeonPooler(url) : url;
|
|
131
|
+
return resolveRuntimeDatabase(fallback).url;
|
|
132
|
+
}
|
|
133
|
+
export function getRuntimeDatabaseSource(fallback = "") {
|
|
134
|
+
return resolveRuntimeDatabase(fallback).source;
|
|
83
135
|
}
|
|
84
136
|
/** Same per-app resolution for DATABASE_AUTH_TOKEN (used by Turso/libsql). */
|
|
85
137
|
export function getDatabaseAuthToken() {
|
package/dist/db/index.d.ts
CHANGED
|
@@ -22,6 +22,6 @@ export declare function createDb(config: DbConfig): Promise<(import("drizzle-orm
|
|
|
22
22
|
export type DrizzleDb = Awaited<ReturnType<typeof createDb>>;
|
|
23
23
|
export { createGetDb } from "./create-get-db.js";
|
|
24
24
|
export { deferMigration, MIGRATION_DEFERRED, runMigrations, withMigrationRuntime, type MigrationEntry, type MigrationRunResult, type MigrationSource, type MigrationSql, } from "./migrations.js";
|
|
25
|
-
export { getDbExec, createDbExec, getDatabaseUrl, getRuntimeDatabaseUrl, getDialect, isLocalDatabase, isPostgres, assertSchemaMutationAllowed, isSchemaMutationStatement, isProductionServerlessFunctionRuntime, intType, closeDbExec, type DbExec, type DbExecConfig, type DbExecQuery, type DbExecStatement, type Dialect, } from "./client.js";
|
|
25
|
+
export { getDbExec, createDbExec, getDatabaseUrl, getRuntimeDatabaseUrl, getRuntimeDatabaseSource, getDialect, isLocalDatabase, isPostgres, assertSchemaMutationAllowed, isSchemaMutationStatement, isProductionServerlessFunctionRuntime, intType, closeDbExec, type DbExec, type DbExecConfig, type DbExecQuery, type DbExecStatement, type Dialect, } from "./client.js";
|
|
26
26
|
export { table, text, integer, now } from "./schema.js";
|
|
27
27
|
export { ensureAdditiveColumns, type EnsureAdditiveColumnsOptions, type EnsureAdditiveColumnsResult, type EnsureAdditiveColumnsLogger, } from "./ensure-additive-columns.js";
|
package/dist/db/index.js
CHANGED
|
@@ -21,6 +21,6 @@ export async function createDb(config) {
|
|
|
21
21
|
}
|
|
22
22
|
export { createGetDb } from "./create-get-db.js";
|
|
23
23
|
export { deferMigration, MIGRATION_DEFERRED, runMigrations, withMigrationRuntime, } from "./migrations.js";
|
|
24
|
-
export { getDbExec, createDbExec, getDatabaseUrl, getRuntimeDatabaseUrl, getDialect, isLocalDatabase, isPostgres, assertSchemaMutationAllowed, isSchemaMutationStatement, isProductionServerlessFunctionRuntime, intType, closeDbExec, } from "./client.js";
|
|
24
|
+
export { getDbExec, createDbExec, getDatabaseUrl, getRuntimeDatabaseUrl, getRuntimeDatabaseSource, getDialect, isLocalDatabase, isPostgres, assertSchemaMutationAllowed, isSchemaMutationStatement, isProductionServerlessFunctionRuntime, intType, closeDbExec, } from "./client.js";
|
|
25
25
|
export { table, text, integer, now } from "./schema.js";
|
|
26
26
|
export { ensureAdditiveColumns, } from "./ensure-additive-columns.js";
|
|
@@ -43,6 +43,11 @@ export interface DatabaseSchemaHealthResult {
|
|
|
43
43
|
}
|
|
44
44
|
export declare const DEFAULT_REQUIRED_SCHEMA: RequiredSchemaTable[];
|
|
45
45
|
export declare function getDatabaseRuntimeFingerprint(): DatabaseRuntimeFingerprint;
|
|
46
|
+
/**
|
|
47
|
+
* Report whether a declared database URL represents the effective shared
|
|
48
|
+
* database, without exposing its value to the caller.
|
|
49
|
+
*/
|
|
50
|
+
export declare function getEffectiveDatabaseEnvStatus(key: string): boolean | undefined;
|
|
46
51
|
export declare function getRuntimeDebugFingerprint(): RuntimeDebugFingerprint;
|
|
47
52
|
export declare function runDatabaseSchemaHealthCheck(options?: {
|
|
48
53
|
exec?: DbExec;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { getDialect, getDbExec, getRuntimeDatabaseUrl, } from "./client.js";
|
|
2
|
+
import { getDialect, getDbExec, getRuntimeDatabaseUrl, getRuntimeDatabaseSource, isLocalDatabase, } from "./client.js";
|
|
3
3
|
export const DEFAULT_REQUIRED_SCHEMA = [
|
|
4
4
|
{
|
|
5
5
|
table: "agent_runs",
|
|
@@ -74,17 +74,6 @@ function envValue(key) {
|
|
|
74
74
|
function appEnvPrefix() {
|
|
75
75
|
return envValue("APP_NAME")?.toUpperCase().replace(/-/g, "_");
|
|
76
76
|
}
|
|
77
|
-
function databaseUrlSource() {
|
|
78
|
-
const appName = appEnvPrefix();
|
|
79
|
-
if (appName && envValue(`${appName}_DATABASE_URL`)) {
|
|
80
|
-
return `${appName}_DATABASE_URL`;
|
|
81
|
-
}
|
|
82
|
-
if (envValue("DATABASE_URL"))
|
|
83
|
-
return "DATABASE_URL";
|
|
84
|
-
if (envValue("NETLIFY_DATABASE_URL"))
|
|
85
|
-
return "NETLIFY_DATABASE_URL";
|
|
86
|
-
return "default";
|
|
87
|
-
}
|
|
88
77
|
function databaseAuthTokenConfigured() {
|
|
89
78
|
const appName = appEnvPrefix();
|
|
90
79
|
return Boolean((appName && envValue(`${appName}_DATABASE_AUTH_TOKEN`)) ||
|
|
@@ -137,15 +126,28 @@ export function getDatabaseRuntimeFingerprint() {
|
|
|
137
126
|
const parsed = parseDatabaseUrl(url);
|
|
138
127
|
return {
|
|
139
128
|
configured: Boolean(url),
|
|
140
|
-
source:
|
|
129
|
+
source: getRuntimeDatabaseSource(),
|
|
141
130
|
dialect: getDialect(),
|
|
142
131
|
urlHash: shortHash(url),
|
|
143
132
|
appName: envValue("APP_NAME"),
|
|
144
133
|
authTokenConfigured: databaseAuthTokenConfigured(),
|
|
145
|
-
netlifyDatabaseUrlConfigured: Boolean(envValue("NETLIFY_DATABASE_URL")
|
|
134
|
+
netlifyDatabaseUrlConfigured: Boolean(envValue("NETLIFY_DATABASE_URL") ||
|
|
135
|
+
envValue("NETLIFY_DATABASE_URL_UNPOOLED")),
|
|
146
136
|
...parsed,
|
|
147
137
|
};
|
|
148
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* Report whether a declared database URL represents the effective shared
|
|
141
|
+
* database, without exposing its value to the caller.
|
|
142
|
+
*/
|
|
143
|
+
export function getEffectiveDatabaseEnvStatus(key) {
|
|
144
|
+
if (!/(?:^|_)DATABASE_URL(?:_UNPOOLED)?$/.test(key))
|
|
145
|
+
return undefined;
|
|
146
|
+
const database = getDatabaseRuntimeFingerprint();
|
|
147
|
+
if (!database.configured || isLocalDatabase())
|
|
148
|
+
return false;
|
|
149
|
+
return database.source === key;
|
|
150
|
+
}
|
|
149
151
|
export function getRuntimeDebugFingerprint() {
|
|
150
152
|
return {
|
|
151
153
|
app: envValue("APP_NAME") ?? "unknown",
|
package/dist/deploy/build.js
CHANGED
|
@@ -1363,6 +1363,43 @@ function getAppOriginClientConfigScript() {
|
|
|
1363
1363
|
env.VITE_AGENT_NATIVE_WORKSPACE_APPS_JSON,
|
|
1364
1364
|
),
|
|
1365
1365
|
);
|
|
1366
|
+
const workspaceAppMountPaths = (() => {
|
|
1367
|
+
const raw = firstNonEmpty(
|
|
1368
|
+
env.AGENT_NATIVE_WORKSPACE_APPS_JSON,
|
|
1369
|
+
env.VITE_AGENT_NATIVE_WORKSPACE_APPS_JSON,
|
|
1370
|
+
);
|
|
1371
|
+
if (!raw) return;
|
|
1372
|
+
try {
|
|
1373
|
+
const parsed = JSON.parse(raw);
|
|
1374
|
+
const entries = Array.isArray(parsed)
|
|
1375
|
+
? parsed
|
|
1376
|
+
: parsed && typeof parsed === "object" && "apps" in parsed
|
|
1377
|
+
? parsed.apps
|
|
1378
|
+
: null;
|
|
1379
|
+
if (!Array.isArray(entries)) return;
|
|
1380
|
+
const paths = Array.from(
|
|
1381
|
+
new Set(
|
|
1382
|
+
entries
|
|
1383
|
+
.map((entry) => {
|
|
1384
|
+
if (!entry || typeof entry !== "object") return null;
|
|
1385
|
+
const rawPath =
|
|
1386
|
+
typeof entry.path === "string"
|
|
1387
|
+
? entry.path
|
|
1388
|
+
: typeof entry.id === "string"
|
|
1389
|
+
? "/" + entry.id
|
|
1390
|
+
: null;
|
|
1391
|
+
if (!rawPath) return null;
|
|
1392
|
+
const normalized = normalizeAppBasePath(rawPath);
|
|
1393
|
+
return normalized || null;
|
|
1394
|
+
})
|
|
1395
|
+
.filter(Boolean),
|
|
1396
|
+
),
|
|
1397
|
+
);
|
|
1398
|
+
return paths.length ? paths : undefined;
|
|
1399
|
+
} catch {
|
|
1400
|
+
return;
|
|
1401
|
+
}
|
|
1402
|
+
})();
|
|
1366
1403
|
const appHomePath = resolveAgentNativeAppHomePath(getAgentNativeAppConfig().app);
|
|
1367
1404
|
const config = {
|
|
1368
1405
|
appHomePath,
|
|
@@ -1370,6 +1407,7 @@ function getAppOriginClientConfigScript() {
|
|
|
1370
1407
|
...(workspaceGatewayUrl ? { workspaceGatewayUrl } : {}),
|
|
1371
1408
|
...(workspaceOAuthOrigin ? { workspaceOAuthOrigin } : {}),
|
|
1372
1409
|
...(workspaceRuntime ? { workspaceRuntime: true } : {}),
|
|
1410
|
+
...(workspaceAppMountPaths ? { workspaceAppMountPaths } : {}),
|
|
1373
1411
|
};
|
|
1374
1412
|
if (Object.keys(config).length === 0) return null;
|
|
1375
1413
|
return (
|
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
* DELETE /_agent-native/notifications/:id — delete
|
|
12
12
|
*/
|
|
13
13
|
export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
|
|
14
|
+
error?: undefined;
|
|
14
15
|
count: number;
|
|
15
16
|
updated?: undefined;
|
|
16
|
-
error?: undefined;
|
|
17
17
|
ok?: undefined;
|
|
18
18
|
} | {
|
|
19
|
+
error?: undefined;
|
|
19
20
|
count?: undefined;
|
|
20
21
|
updated: number;
|
|
21
|
-
error?: undefined;
|
|
22
22
|
ok?: undefined;
|
|
23
23
|
} | {
|
|
24
24
|
count?: undefined;
|
|
@@ -26,8 +26,8 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
|
|
|
26
26
|
error: string;
|
|
27
27
|
ok?: undefined;
|
|
28
28
|
} | {
|
|
29
|
+
error?: undefined;
|
|
29
30
|
count?: undefined;
|
|
30
31
|
updated?: undefined;
|
|
31
32
|
ok: boolean;
|
|
32
|
-
error?: undefined;
|
|
33
33
|
}>>;
|
|
@@ -41,16 +41,16 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
41
41
|
thumbsUpRate: number;
|
|
42
42
|
avgEvalScore: number;
|
|
43
43
|
} | {
|
|
44
|
-
error?: undefined;
|
|
45
44
|
summary: import("./types.js").TraceSummary;
|
|
46
45
|
spans: import("./types.js").TraceSpan[];
|
|
47
46
|
id?: undefined;
|
|
47
|
+
error?: undefined;
|
|
48
48
|
ok?: undefined;
|
|
49
49
|
} | {
|
|
50
|
-
error?: undefined;
|
|
51
50
|
summary?: undefined;
|
|
52
51
|
spans?: undefined;
|
|
53
52
|
id: string;
|
|
53
|
+
error?: undefined;
|
|
54
54
|
ok?: undefined;
|
|
55
55
|
} | {
|
|
56
56
|
summary?: undefined;
|
|
@@ -59,9 +59,9 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
59
59
|
error: any;
|
|
60
60
|
ok?: undefined;
|
|
61
61
|
} | {
|
|
62
|
-
error?: undefined;
|
|
63
62
|
summary?: undefined;
|
|
64
63
|
spans?: undefined;
|
|
65
64
|
id?: undefined;
|
|
65
|
+
error?: undefined;
|
|
66
66
|
ok: boolean;
|
|
67
67
|
}>>;
|