import type { BrowserTransformExecuteInput, BrowserTransformPacket, BrowserTransformValidationExecuteInput, BrowserTransformProfileInput, } from '@/types/models'; import type { CapabilityDomainHandler } from '../capability-context'; import { allowedTarget, requireScope } from '../capability-context'; import { captureBrowserTransformRecovery, confirmBrowserTransformRecovery, deleteBrowserTransformProfile, executeBrowserTransform, getBrowserTransformProfile, getBrowserTransformRecovery, listBrowserTransformProfiles, saveBrowserTransformProfile, resetBrowserTransformRecovery, startBrowserTransformRecovery, validateBrowserTransformRecovery, } from '@/features/browser-transform/service'; import { compareBrowserPackets, browserTransformValidationById, executeBrowserTransformValidation, latestBrowserTransformValidation, prepareCapturedBrowserTransformProfile, proposeBrowserTransformProfile, validateInferredBrowserTransformProfile, } from '@/features/browser-analysis/service'; import { TRANSFORM_CAPABILITY_DOMAIN } from '../capability-domains'; export const transformCapabilityHandler: CapabilityDomainHandler = { ...TRANSFORM_CAPABILITY_DOMAIN, async handle({ method, input, grant }) { if (method === 'browser.transform.profile.save') { const profile = input as unknown as BrowserTransformProfileInput; await allowedTarget(grant, profile.target); return saveBrowserTransformProfile(profile); } if (method === 'browser.transform.validation.get') { const draft = await browserTransformValidationById(String(input.validationId || '')); await allowedTarget(grant, draft.profile.target); return { id: draft.id, expiresAt: draft.expiresAt, directions: { request: draft.profile.request.enabled, response: draft.profile.response.enabled }, }; } if (method === 'browser.transform.prepare') { requireScope(grant, 'browser.recording.read'); requireScope(grant, 'browser.callable.execute'); return prepareCapturedBrowserTransformProfile( await allowedTarget(grant, input), String(input.candidateId || ''), input.packet as BrowserTransformPacket, Array.isArray(input.inputPaths) ? input.inputPaths.map(String) : undefined, typeof input.name === 'string' ? input.name : undefined, { owner: { kind: 'grant', grantId: grant.id, expiresAt: grant.expiresAt }, trigger: input.trigger as { captureId: string; nodeId: string } | undefined, authorize: () => { requireScope(grant, 'browser.debugger.control'); requireScope(grant, 'browser.dom.read'); requireScope(grant, 'browser.dom.write'); }, }, ); } if (method === 'browser.packet.compare') { await allowedTarget(grant, input); return compareBrowserPackets( input.actual as BrowserTransformPacket, input.expected as BrowserTransformPacket, input.mode === 'exact' ? 'exact' : 'structure', ); } if (method === 'browser.profile.propose') { requireScope(grant, 'browser.recording.read'); const target = await allowedTarget(grant, input); return proposeBrowserTransformProfile( target, String(input.candidateId || ''), String(input.callableId || ''), Array.isArray(input.inputPaths) ? input.inputPaths.map(String) : undefined, typeof input.name === 'string' ? input.name : undefined, ); } if (method === 'browser.profile.validation.latest') { return latestBrowserTransformValidation(await allowedTarget(grant, input)); } if (method === 'browser.profile.validate') { requireScope(grant, 'browser.recording.read'); const target = await allowedTarget(grant, input); return validateInferredBrowserTransformProfile( target, String(input.candidateId || ''), String(input.callableId || ''), input.packet as BrowserTransformPacket, Array.isArray(input.inputPaths) ? input.inputPaths.map(String) : undefined, typeof input.name === 'string' ? input.name : undefined, input.observed as BrowserTransformPacket | undefined, input.comparisonMode === 'exact' ? 'exact' : 'structure', ); } if (method.startsWith('browser.transform.recovery.')) { const profile = await getBrowserTransformProfile(String(input.id || '')); const resolveInPage = method !== 'browser.transform.recovery.capture' && method !== 'browser.transform.recovery.reset'; const target = await allowedTarget(grant, { tabId: profile.target.tabId, frameId: profile.target.frameId, ...(typeof input.documentId === 'string' ? { documentId: input.documentId } : {}), }, resolveInPage); const owner = { kind: 'grant' as const, grantId: grant.id, expiresAt: grant.expiresAt }; if (method === 'browser.transform.recovery.get') { return getBrowserTransformRecovery(profile.id); } if (method === 'browser.transform.recovery.start') { requireScope(grant, 'browser.debugger.control'); return startBrowserTransformRecovery(profile.id, owner); } if (method === 'browser.transform.recovery.capture') { requireScope(grant, 'browser.debugger.control'); requireScope(grant, 'browser.callable.execute'); return captureBrowserTransformRecovery( profile.id, target, String(input.callFrameId || ''), input.strategy === 'request-transaction' ? 'request-transaction' : 'selected-frame', owner, ); } if (method === 'browser.transform.recovery.validate') { return validateBrowserTransformRecovery( profile.id, input.packet as BrowserTransformPacket, ); } if (method === 'browser.transform.recovery.confirm') { return confirmBrowserTransformRecovery(profile.id, String(input.validationId || '')); } return resetBrowserTransformRecovery(profile.id, owner); } if (method === 'browser.transform.profile.list') { const profiles = await listBrowserTransformProfiles(); const visible = await Promise.all(profiles.map(async (profile) => { try { await allowedTarget(grant, profile.target); return profile; } catch { return undefined; } })); return visible.filter(Boolean); } if (method === 'browser.transform.profile.delete') { const profile = await getBrowserTransformProfile(String(input.id || '')); await allowedTarget(grant, profile.target); return deleteBrowserTransformProfile(profile.id); } if (method === 'browser.transform.validation.execute') { requireScope(grant, 'browser.transform.execute'); const executeValidationInput = input as unknown as BrowserTransformValidationExecuteInput; const draft = await browserTransformValidationById(executeValidationInput.validationId); await allowedTarget(grant, draft.profile.target); return executeBrowserTransformValidation( executeValidationInput.validationId, executeValidationInput.direction, executeValidationInput.packet, ); } const executeInput = input as unknown as BrowserTransformExecuteInput; const profile = await getBrowserTransformProfile(executeInput.profileId); await allowedTarget(grant, profile.target); return executeBrowserTransform(executeInput); }, };