UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

175 lines (174 loc) 7.07 kB
//#region extensions/browser/src/browser/errors.ts /** * Browser domain errors. * * Provides HTTP-mappable error classes and stable blocked-policy messages used * by route handlers, clients, and Gateway proxy code. */ /** Stable message for blocked CDP endpoint configuration. */ const BROWSER_ENDPOINT_BLOCKED_MESSAGE = "browser endpoint blocked by policy"; /** Stable message for blocked page navigation targets. */ const BROWSER_NAVIGATION_BLOCKED_MESSAGE = "browser navigation blocked by policy"; /** Stable machine-readable browser error reasons. */ const BROWSER_ERROR_REASONS = { noDisplayForHeadedProfile: "no_display_for_headed_profile", navigationBlocked: "navigation_blocked" }; /** Stable machine-readable codes returned by browser action routes. */ const BROWSER_ACT_ERROR_CODES = { kindRequired: "ACT_KIND_REQUIRED", invalidRequest: "ACT_INVALID_REQUEST", selectorUnsupported: "ACT_SELECTOR_UNSUPPORTED", evaluateDisabled: "ACT_EVALUATE_DISABLED", unsupportedForExistingSession: "ACT_EXISTING_SESSION_UNSUPPORTED", targetIdMismatch: "ACT_TARGET_ID_MISMATCH" }; const BROWSER_ACT_ERROR_CODE_VALUES = new Set(Object.values(BROWSER_ACT_ERROR_CODES)); function isBrowserActErrorCode(value) { return typeof value === "string" && BROWSER_ACT_ERROR_CODE_VALUES.has(value); } const NO_DISPLAY_HEADLESS_SOURCES = [ "request", "env", "profile", "config", "default" ]; /** Base browser error carrying an HTTP status code. */ var BrowserError = class extends Error { constructor(message, status = 500, options) { super(message, options); this.name = new.target.name; this.status = status; } }; /** * Raised when a browser CDP endpoint (the cdpUrl itself) fails the * configured SSRF policy. Distinct from a blocked navigation target so * callers see "fix your browser endpoint config" rather than "fix your * navigation URL". */ var BrowserCdpEndpointBlockedError = class extends BrowserError { constructor(options) { super(BROWSER_ENDPOINT_BLOCKED_MESSAGE, 400, options); } }; /** Validation failure for browser route or config input. */ var BrowserValidationError = class extends BrowserError { constructor(message, options) { super(message, 400, options); } }; /** Raised when one tab reference matches multiple tabs. */ var BrowserTargetAmbiguousError = class extends BrowserError { constructor(message = "ambiguous browser tab reference", options) { super(message, 409, options); } }; /** Raised when a requested browser tab cannot be resolved. */ var BrowserTabNotFoundError = class extends BrowserError { constructor(inputOrMessage, options) { const input = typeof inputOrMessage === "object" ? inputOrMessage.input?.trim() : inputOrMessage?.trim(); const message = input ? /^\d+$/.test(input) ? `tab not found: browser tab "${input}" not found. Numeric values are not tab targets; use a stable tab id like "t1", a label, or a raw targetId. For positional selection, use "openclaw browser tab select ${input}".` : `tab not found: browser tab "${input}" not found. Use action=tabs and pass suggestedTargetId, tabId, label, or raw targetId.` : "tab not found"; super(message, 404, options); } }; /** Raised when a requested browser profile does not exist. */ var BrowserProfileNotFoundError = class extends BrowserError { constructor(message, options) { super(message, 404, options); } }; /** Raised when a browser config mutation conflicts with existing state. */ var BrowserConflictError = class extends BrowserError { constructor(message, options) { super(message, 409, options); } }; /** Raised when a browser profile cannot be reset by the current driver. */ var BrowserResetUnsupportedError = class extends BrowserError { constructor(message, options) { super(message, 400, options); } }; /** Raised when a profile is configured but not currently reachable. */ var BrowserProfileUnavailableError = class extends BrowserError { constructor(message, options) { super(message, 409, options); this.metadata = options?.metadata; } }; /** Raised when browser resource allocation, such as CDP ports, is exhausted. */ var BrowserResourceExhaustedError = class extends BrowserError { constructor(message, options) { super(message, 507, options); } }; /** Map browser-domain errors to HTTP response details. */ function toBrowserErrorResponse(err) { if (err instanceof BrowserProfileUnavailableError && err.metadata) return { status: err.status, message: err.message, ...err.metadata }; if (err instanceof BrowserError) return { status: err.status, message: err.message }; if (err instanceof Error && err.name === "BlockedBrowserTargetError") return { status: 409, message: err.message, reason: BROWSER_ERROR_REASONS.navigationBlocked }; if (err instanceof Error && err.name === "SsrFBlockedError") return { status: 400, message: BROWSER_NAVIGATION_BLOCKED_MESSAGE, reason: BROWSER_ERROR_REASONS.navigationBlocked }; if (err instanceof Error && err.name === "InvalidBrowserNavigationUrlError") return { status: 400, message: err.message, reason: BROWSER_ERROR_REASONS.navigationBlocked }; return null; } function parseNoDisplayDetails(value) { if (!value || typeof value !== "object" || Array.isArray(value)) return null; const details = value; if (typeof details.profile !== "string" || details.profile.length === 0 || details.requestedHeadless !== false || !NO_DISPLAY_HEADLESS_SOURCES.includes(details.headlessSource) || details.displayPresent !== false) return null; return { profile: details.profile, requestedHeadless: false, headlessSource: details.headlessSource, displayPresent: false }; } /** Parse only the closed browser error metadata contract from a route payload. */ function parseBrowserErrorPayload(value) { if (!value || typeof value !== "object" || Array.isArray(value)) return null; const body = value; if (typeof body.error !== "string" || body.error.length === 0) return null; const code = isBrowserActErrorCode(body.code) ? body.code : void 0; const unrecognizedCode = body.unrecognizedCode === true || body.code !== void 0 && !code ? true : void 0; const actionCode = code ? { code } : unrecognizedCode ? { unrecognizedCode: true } : {}; if (body.reason === BROWSER_ERROR_REASONS.navigationBlocked) return { error: body.error, ...actionCode, reason: body.reason }; if (body.reason === BROWSER_ERROR_REASONS.noDisplayForHeadedProfile) { const details = parseNoDisplayDetails(body.details); if (details) return { error: body.error, ...actionCode, reason: body.reason, details }; } return { error: body.error, ...actionCode }; } //#endregion export { BrowserError as a, BrowserResetUnsupportedError as c, BrowserTargetAmbiguousError as d, BrowserValidationError as f, BrowserConflictError as i, BrowserResourceExhaustedError as l, toBrowserErrorResponse as m, BROWSER_ERROR_REASONS as n, BrowserProfileNotFoundError as o, parseBrowserErrorPayload as p, BrowserCdpEndpointBlockedError as r, BrowserProfileUnavailableError as s, BROWSER_ACT_ERROR_CODES as t, BrowserTabNotFoundError as u };