























@@ -460,37 +460,105 @@ async function waitForChromeMcpReady(
460460session: ChromeMcpSession,
461461profileName: string,
462462timeoutMs?: number,
463+signal?: AbortSignal,
463464): Promise<void> {
464-if (!timeoutMs || timeoutMs <= 0) {
465+if (signal?.aborted) {
466+throw signal.reason ?? new Error("aborted");
467+}
468+if ((!timeoutMs || timeoutMs <= 0) && !signal) {
465469await session.ready;
466470return;
467471}
468472469473let timer: ReturnType<typeof setTimeout> | undefined;
474+let abortListener: (() => void) | undefined;
475+try {
476+const racers: Array<Promise<void> | Promise<never>> = [session.ready];
477+if (timeoutMs && timeoutMs > 0) {
478+racers.push(
479+new Promise<never>((_, reject) => {
480+timer = setTimeout(() => {
481+reject(
482+new BrowserProfileUnavailableError(
483+`Chrome MCP existing-session attach for profile "${profileName}" timed out after ${timeoutMs}ms.`,
484+),
485+);
486+}, timeoutMs);
487+}),
488+);
489+}
490+if (signal) {
491+racers.push(
492+new Promise<never>((_, reject) => {
493+abortListener = () => reject(signal.reason ?? new Error("aborted"));
494+signal.addEventListener("abort", abortListener, { once: true });
495+}),
496+);
497+}
498+await Promise.race(racers);
499+} finally {
500+if (timer) {
501+clearTimeout(timer);
502+}
503+if (signal && abortListener) {
504+signal.removeEventListener("abort", abortListener);
505+}
506+}
507+}
508+509+async function waitForChromeMcpPendingSession(
510+pending: Promise<ChromeMcpSession>,
511+signal?: AbortSignal,
512+): Promise<ChromeMcpSession> {
513+if (signal?.aborted) {
514+throw signal.reason ?? new Error("aborted");
515+}
516+if (!signal) {
517+return await pending;
518+}
519+520+let abortListener: (() => void) | undefined;
470521try {
471-await Promise.race([
472-session.ready,
522+return await Promise.race([
523+pending,
473524new Promise<never>((_, reject) => {
474-timer = setTimeout(() => {
475-reject(
476-new BrowserProfileUnavailableError(
477-`Chrome MCP existing-session attach for profile "${profileName}" timed out after ${timeoutMs}ms.`,
478-),
479-);
480-}, timeoutMs);
525+abortListener = () => reject(signal.reason ?? new Error("aborted"));
526+signal.addEventListener("abort", abortListener, { once: true });
481527}),
482528]);
483529} finally {
484-if (timer) {
485-clearTimeout(timer);
530+if (abortListener) {
531+signal.removeEventListener("abort", abortListener);
486532}
487533}
488534}
489535536+async function createChromeMcpSession(
537+profileName: string,
538+options: NormalizedChromeMcpProfileOptions,
539+signal?: AbortSignal,
540+): Promise<ChromeMcpSession> {
541+const created = (sessionFactory ?? createRealSession)(profileName, options);
542+try {
543+const session = await waitForChromeMcpPendingSession(created, signal);
544+if (signal?.aborted) {
545+await session.client.close().catch(() => {});
546+throw signal.reason ?? new Error("aborted");
547+}
548+return session;
549+} catch (err) {
550+if (signal?.aborted) {
551+void created.then((session) => session.client.close()).catch(() => {});
552+}
553+throw err;
554+}
555+}
556+490557async function getSession(
491558profileName: string,
492559profileOptions?: ChromeMcpOptionsInput,
493560timeoutMs?: number,
561+signal?: AbortSignal,
494562): Promise<ChromeMcpSession> {
495563const options = normalizeChromeMcpOptions(profileOptions);
496564const cacheKey = buildChromeMcpSessionCacheKey(profileName, options);
@@ -505,7 +573,7 @@ async function getSession(
505573let pending = pendingSessions.get(cacheKey);
506574if (!pending) {
507575pending = (async () => {
508-const created = await (sessionFactory ?? createRealSession)(profileName, options);
576+const created = await createChromeMcpSession(profileName, options, signal);
509577if (pendingSessions.get(cacheKey) === pending) {
510578sessions.set(cacheKey, created);
511579} else {
@@ -524,7 +592,7 @@ async function getSession(
524592}
525593}
526594try {
527-await waitForChromeMcpReady(session, profileName, timeoutMs);
595+await waitForChromeMcpReady(session, profileName, timeoutMs, signal);
528596return session;
529597} catch (err) {
530598const current = sessions.get(cacheKey);
@@ -539,6 +607,7 @@ async function getExistingSession(
539607cacheKey: string,
540608profileName: string,
541609timeoutMs?: number,
610+signal?: AbortSignal,
542611): Promise<ChromeMcpSession | null> {
543612let session = sessions.get(cacheKey);
544613if (session && session.transport.pid === null) {
@@ -547,7 +616,7 @@ async function getExistingSession(
547616}
548617if (session) {
549618try {
550-await waitForChromeMcpReady(session, profileName, timeoutMs);
619+await waitForChromeMcpReady(session, profileName, timeoutMs, signal);
551620return session;
552621} catch (err) {
553622const current = sessions.get(cacheKey);
@@ -563,9 +632,9 @@ async function getExistingSession(
563632return null;
564633}
565634566-session = await pending;
635+session = await waitForChromeMcpPendingSession(pending, signal);
567636try {
568-await waitForChromeMcpReady(session, profileName, timeoutMs);
637+await waitForChromeMcpReady(session, profileName, timeoutMs, signal);
569638return session;
570639} catch (err) {
571640const current = sessions.get(cacheKey);
@@ -580,11 +649,12 @@ async function createEphemeralSession(
580649profileName: string,
581650profileOptions?: ChromeMcpOptionsInput,
582651timeoutMs?: number,
652+signal?: AbortSignal,
583653): Promise<ChromeMcpSession> {
584654const options = normalizeChromeMcpOptions(profileOptions);
585-const session = await (sessionFactory ?? createRealSession)(profileName, options);
655+const session = await createChromeMcpSession(profileName, options, signal);
586656try {
587-await waitForChromeMcpReady(session, profileName, timeoutMs);
657+await waitForChromeMcpReady(session, profileName, timeoutMs, signal);
588658return session;
589659} catch (err) {
590660await session.client.close().catch(() => {});
@@ -601,15 +671,25 @@ async function leaseSession(
601671const cacheKey = buildChromeMcpSessionCacheKey(profileName, normalizedProfileOptions);
602672if (!options.ephemeral) {
603673return {
604-session: await getSession(profileName, normalizedProfileOptions, options.timeoutMs),
674+session: await getSession(
675+profileName,
676+normalizedProfileOptions,
677+options.timeoutMs,
678+options.signal,
679+),
605680 cacheKey,
606681temporary: false,
607682};
608683}
609684610685// Status probes should avoid seeding the shared attach session cache, but they can safely
611686// reuse a real cached session if one already exists.
612-const existingSession = await getExistingSession(cacheKey, profileName, options.timeoutMs);
687+const existingSession = await getExistingSession(
688+cacheKey,
689+profileName,
690+options.timeoutMs,
691+options.signal,
692+);
613693if (existingSession) {
614694return {
615695session: existingSession,
@@ -619,7 +699,12 @@ async function leaseSession(
619699}
620700621701return {
622-session: await createEphemeralSession(profileName, normalizedProfileOptions, options.timeoutMs),
702+session: await createEphemeralSession(
703+profileName,
704+normalizedProfileOptions,
705+options.timeoutMs,
706+options.signal,
707+),
623708 cacheKey,
624709temporary: true,
625710};
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。