nexus / src /lib /app-data /client.server.ts
betterwithage's picture
deploy(hf): sync szl-holdings/nexus@00edebc993282efd5b43b0144b3d3d27ab43e264 derived COPY set
31b3c47 verified
Raw History Blame Contribute Delete
9.96 kB
import { createHash } from "node:crypto";
import { getRequest } from "@tanstack/react-start/server";
import {
assertSameSiteRequest,
CrossSiteRequestError,
} from "../auth/isolation.server.ts";
import { assertAppDataServerOnly } from "./server-only.ts";
import {
CONNECTOR_TOKEN_HEADER,
ConnectorType,
type CallToolOptions,
type CallToolResult,
type ToolArgs,
} from "./types.ts";
assertAppDataServerOnly("app-data/client.server");
export const CONNECTORS_HOST_STAGING = "connectors.app-builder-testing.com";
export const CONNECTORS_HOST_PROD = "connectors.grok.me";
function env(key: string): string | undefined {
const v = process.env[key]?.trim();
return v || undefined;
}
function isLoopbackHost(host: string): boolean {
return host === "localhost" || host === "127.0.0.1" || host === "[::1]";
}
type InboundContext = {
token: string | null;
publicHost: string | null;
connectorsBase: string | null;
};
function connectorsBaseFor(publicHost: string | null): string | null {
const explicit = env("GROK_CONNECTORS_URL");
if (explicit) return explicit.replace(/\/+$/, "");
const host = publicHost?.toLowerCase();
if (!host || isLoopbackHost(host)) return null;
if (
host === "app-builder-testing.com" ||
host.endsWith(".app-builder-testing.com")
) {
return `https://${CONNECTORS_HOST_STAGING}`;
}
if (host === "grok.me" || host.endsWith(".grok.me")) {
return `https://${CONNECTORS_HOST_PROD}`;
}
return null;
}
function tryGetRequest(): Request | null {
try {
return getRequest() ?? null;
} catch {
return null;
}
}
function inboundContext(): InboundContext {
const req = tryGetRequest();
const xf = req?.headers.get("x-forwarded-host")?.split(",")[0]?.trim();
const publicHost =
(xf || req?.headers.get("host") || "").split(":")[0]?.trim() || null;
const headerToken = req?.headers.get(CONNECTOR_TOKEN_HEADER)?.trim() || null;
const envToken =
process.env.NODE_ENV === "production"
? null
: (env("GROK_CONNECTOR_ACCESS_TOKEN") ?? null);
return {
token: headerToken ?? envToken,
publicHost,
connectorsBase: connectorsBaseFor(publicHost),
};
}
export function resolveGateAppDataBase(): string | null {
return inboundContext().connectorsBase;
}
export function getConnectorAccessToken(): string | null {
return inboundContext().token;
}
type GateJson = {
ok?: boolean;
data?: unknown;
errorMessage?: string;
loginUrl?: string;
};
async function gatePost(
ctx: InboundContext,
body: Record<string, unknown>,
token: string,
): Promise<{ status: number; json: GateJson }> {
const base = ctx.connectorsBase;
if (!base) {
throw new Error(
"cannot resolve gate host (missing x-forwarded-host/host on the server request); " +
"open the app through the gated public URL so the gate can proxy and inject credentials",
);
}
if (!/^https?:\/\//i.test(base)) {
throw new Error(
`gate base must be absolute http(s) URL (got ${base}); refusing relative fetch`,
);
}
const headers: Record<string, string> = {
"content-type": "application/json",
accept: "application/json",
authorization: `Bearer ${token}`,
};
if (ctx.publicHost) {
headers["x-forwarded-host"] = ctx.publicHost;
}
const res = await fetch(`${base}/call-tool`, {
method: "POST",
headers,
body: JSON.stringify(body),
redirect: "manual",
});
let json: GateJson = {};
const text = await res.text();
if (text) {
try {
json = JSON.parse(text) as GateJson;
} catch {
json = {
ok: false,
errorMessage: `gate non-JSON response (HTTP ${res.status}): ${text.slice(0, 200)}`,
};
}
}
return { status: res.status, json };
}
function gateSigninUrl(ctx: InboundContext): string | undefined {
const base = ctx.connectorsBase;
if (!base) return undefined;
try {
const connectorsHost = new URL(base).host.toLowerCase();
const gateHost = connectorsHost.replace(/^connectors\./, "gate.");
if (gateHost === connectorsHost) return undefined;
const publicHost = ctx.publicHost?.toLowerCase();
const gated =
publicHost && !isLoopbackHost(publicHost)
? `https://${publicHost}`
: undefined;
const signin = `https://${gateHost}/__gate/signin`;
return gated
? `${signin}?return_to=${encodeURIComponent(gated)}`
: signin;
} catch {
return undefined;
}
}
function missingAuthResult(ctx: InboundContext): CallToolResult {
const loginUrl = gateSigninUrl(ctx);
return {
ok: false,
data: null,
loginRequired: true,
errorMessage:
"missing_connector_token: open this app through the edge gate " +
"(the server must receive x-connector-access-token on the inbound request)",
...(loginUrl ? { loginUrl } : {}),
};
}
function crossSiteBlockedResult(): CallToolResult | null {
try {
assertSameSiteRequest();
return null;
} catch (e) {
if (e instanceof CrossSiteRequestError) {
return { ok: false, data: null, errorMessage: e.message };
}
return null;
}
}
const FAILURE_MEMO_TTL_MS = 5_000;
const failureMemo = new Map<string, { at: number; result: CallToolResult }>();
function tokenIdentityKey(token: string): string {
const payload = token.split(".")[1];
if (payload) {
try {
const claims: unknown = JSON.parse(
Buffer.from(payload, "base64url").toString("utf8"),
);
if (claims && typeof claims === "object" && !Array.isArray(claims)) {
const { sub, team_id: teamId } = claims as {
sub?: unknown;
team_id?: unknown;
};
if (typeof sub === "string" && sub) {
return createHash("sha256")
.update(
JSON.stringify([sub, typeof teamId === "string" ? teamId : null]),
)
.digest("base64url");
}
}
} catch {
// Not a decodable JWT payload: fall through to hashing the raw token.
}
}
return createHash("sha256").update(token).digest("base64url");
}
function memoizedFailure(key: string | null): CallToolResult | null {
if (!key) return null;
const hit = failureMemo.get(key);
if (!hit) return null;
if (Date.now() - hit.at > FAILURE_MEMO_TTL_MS) {
failureMemo.delete(key);
return null;
}
return hit.result;
}
function memoizeFailure(
key: string | null,
result: CallToolResult,
): CallToolResult {
if (!key) return result;
const now = Date.now();
for (const [staleKey, entry] of failureMemo) {
if (now - entry.at > FAILURE_MEMO_TTL_MS) failureMemo.delete(staleKey);
}
failureMemo.set(key, { at: now, result });
return result;
}
export function failureMemoSize(): number {
return failureMemo.size;
}
function safeMemoKey(parts: unknown[]): string | null {
try {
return JSON.stringify(parts);
} catch {
return null;
}
}
function nonPostBlockedResult(): CallToolResult | null {
const req = tryGetRequest();
if (!req || req.method === "POST") return null;
return {
ok: false,
data: null,
errorMessage:
`blocked ${req.method} inbound request: connector calls must run inside ` +
'a createServerFn({ method: "POST" }) handler',
};
}
export async function callTool(
toolName: string,
args: ToolArgs,
options: CallToolOptions,
): Promise<CallToolResult> {
const blocked = crossSiteBlockedResult() ?? nonPostBlockedResult();
if (blocked) return blocked;
const ctx = inboundContext();
const token = options.token ?? ctx.token;
if (!token) {
return missingAuthResult(ctx);
}
const connectorType = options.connectorType;
if (!connectorType) {
return {
ok: false,
data: null,
errorMessage:
"connectorType is required: pass the connector type granted to this app " +
"(e.g. { connectorType: ConnectorType.GoogleDrive })",
};
}
const memoKey = safeMemoKey([
toolName,
args,
connectorType,
options?.connectorCatalogId ?? null,
tokenIdentityKey(token),
]);
const memoized = memoizedFailure(memoKey);
if (memoized) {
return memoized;
}
const fail = (errorMessage: string): CallToolResult =>
memoizeFailure(memoKey, { ok: false, data: null, errorMessage });
if (connectorType === ConnectorType.Mcp && !options?.connectorCatalogId) {
return {
ok: false,
data: null,
errorMessage: "connectorCatalogId is required when connectorType is Mcp",
};
}
try {
const { status, json } = await gatePost(
ctx,
{
host: ctx.publicHost ?? undefined,
connector_type: connectorType,
tool_name: toolName,
arguments: args,
connector_catalog_id: options.connectorCatalogId,
},
token,
);
if (status === 401) {
const loginUrl =
gateSigninUrl(ctx) ??
(typeof json.loginUrl === "string" && json.loginUrl
? json.loginUrl
: undefined);
return {
ok: false,
data: null,
loginRequired: true,
errorMessage: json.errorMessage ?? "login required",
...(loginUrl ? { loginUrl } : {}),
};
}
if (status === 403) {
return fail(json.errorMessage ?? "access_denied");
}
if (json.errorMessage && json.ok === false) {
return fail(json.errorMessage);
}
if (status >= 400 && json.ok !== true) {
return fail(json.errorMessage ?? `HTTP ${status}`);
}
if (json.ok === false) {
return fail(json.errorMessage ?? "tool error");
}
return { ok: true, data: json.data ?? null };
} catch (e) {
return fail(e instanceof Error ? e.message : String(e));
}
}
export {
ConnectorType,
GoogleDriveTools,
CONNECTOR_TOKEN_HEADER,
} from "./types.ts";
export type {
CallToolResult,
CallToolOptions,
ToolArgs,
ConnectorTypeName,
} from "./types.ts";
export { isLoginRequired, redirectToLoginIfRequired } from "./login.ts";