67 lines
1.7 KiB
TypeScript
67 lines
1.7 KiB
TypeScript
export const NIANCODE_APP_PROTOCOL = 'niancode';
|
|
|
|
export type NianCodeDeepLink = {
|
|
type: 'desktop-auth-callback';
|
|
requestId: string;
|
|
url: string;
|
|
} | { type: 'cloud-agent'; slug: string; url: string };
|
|
|
|
let pendingCloudAgentRoute: string | null = null;
|
|
export function queueCloudAgentLink(slug: string): void {
|
|
pendingCloudAgentRoute = '/cloud-agents/shared/' + slug;
|
|
}
|
|
export function takeCloudAgentRoute(): string | null {
|
|
const route = pendingCloudAgentRoute;
|
|
pendingCloudAgentRoute = null;
|
|
return route;
|
|
}
|
|
|
|
const SENSITIVE_QUERY_KEYS = new Set([
|
|
'access_token',
|
|
'refresh_token',
|
|
'device_secret',
|
|
'token',
|
|
]);
|
|
|
|
export function parseNianCodeDeepLinkUrl(rawUrl: string): NianCodeDeepLink | null {
|
|
let url: URL;
|
|
try {
|
|
url = new URL(rawUrl);
|
|
} catch {
|
|
return null;
|
|
}
|
|
|
|
if (url.protocol === `${NIANCODE_APP_PROTOCOL}:` && url.hostname === 'agents'
|
|
&& /^\/ml-[a-f0-9]{32}$/.test(url.pathname) && !url.username && !url.password && !url.port && !url.search && !url.hash) {
|
|
return { type: 'cloud-agent', slug: url.pathname.slice(1), url: rawUrl };
|
|
}
|
|
if (url.protocol !== `${NIANCODE_APP_PROTOCOL}:`) {
|
|
return null;
|
|
}
|
|
|
|
if (url.hostname !== 'auth' || url.pathname !== '/callback') {
|
|
return null;
|
|
}
|
|
|
|
for (const key of SENSITIVE_QUERY_KEYS) {
|
|
if (url.searchParams.has(key)) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
const requestId = url.searchParams.get('request_id')?.trim();
|
|
if (!requestId || requestId.length > 128) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
type: 'desktop-auth-callback',
|
|
requestId,
|
|
url: rawUrl,
|
|
};
|
|
}
|
|
|
|
export function findNianCodeDeepLinkUrl(args: readonly string[]): string | null {
|
|
return args.find((arg) => parseNianCodeDeepLinkUrl(arg) !== null) ?? null;
|
|
}
|