Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

78 lines
2.4 KiB
TypeScript

const ALLOWED_WEBSITE_PROTOCOLS = new Set(["http:", "https:"]);
export const DEFAULT_WEBSITE_NODE_URL = "https://www.google.com/";
const GOOGLE_SEARCH_BASE_URL = "https://www.google.com/search";
const LIKELY_HOST_PATTERN =
/^(localhost|(\d{1,3}\.){3}\d{1,3}|(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,})(?::\d{1,5})?(?:[/?#][^\s]*)?$/i;
const EXPLICIT_PROTOCOL_PATTERN = /^[a-zA-Z][a-zA-Z\d+\-.]*:\/\//;
const LOOPBACK_HOST_PATTERN =
/^(localhost|127(?:\.\d{1,3}){3})(?::\d{1,5})?(?:[/?#][^\s]*)?$/i;
function defaultSchemeForHostInput(value: string): "http" | "https" {
return LOOPBACK_HOST_PATTERN.test(value) ? "http" : "https";
}
export function resolveWebsiteNavigationUrl(rawUrl: string): {
url: string | null;
error: string | null;
} {
const trimmed = rawUrl.trim();
if (trimmed.length === 0) {
return { url: null, error: null };
}
if (EXPLICIT_PROTOCOL_PATTERN.test(trimmed)) {
try {
const parsed = new URL(trimmed);
if (!ALLOWED_WEBSITE_PROTOCOLS.has(parsed.protocol)) {
return { url: null, error: `Unsupported protocol: ${parsed.protocol}` };
}
return { url: parsed.toString(), error: null };
} catch {
return { url: null, error: "Invalid URL" };
}
}
if (!LIKELY_HOST_PATTERN.test(trimmed)) {
return { url: null, error: "Invalid URL" };
}
try {
const parsed = new URL(
`${defaultSchemeForHostInput(trimmed)}://${trimmed}`
);
return { url: parsed.toString(), error: null };
} catch {
return { url: null, error: "Invalid URL" };
}
}
export function isWebsiteUrlAllowedForNavigation(rawUrl: string): boolean {
const resolved = resolveWebsiteNavigationUrl(rawUrl);
return resolved.url !== null && resolved.error === null;
}
export function resolveWebsiteAddressInput(rawInput: string): {
url: string | null;
error: string | null;
} {
const trimmed = rawInput.trim();
if (trimmed.length === 0) {
return { url: null, error: null };
}
const navigation = resolveWebsiteNavigationUrl(trimmed);
if (navigation.url) {
return navigation;
}
const searchUrl = new URL(GOOGLE_SEARCH_BASE_URL);
searchUrl.searchParams.set("q", trimmed);
return { url: searchUrl.toString(), error: null };
}
export function normalizeWebsiteComparableUrl(rawUrl: string): string | null {
const resolved = resolveWebsiteNavigationUrl(rawUrl);
return resolved.error === null ? resolved.url : null;
}