import { z } from 'zod'; export const BrowserTabStateSchema = z.object({ id: z.string(), url: z.string(), title: z.string(), canGoBack: z.boolean(), canGoForward: z.boolean(), loading: z.boolean(), }); export const BrowserStateSchema = z.object({ activeTabId: z.string().nullable(), tabs: z.array(BrowserTabStateSchema), }); export const BrowserPageElementSchema = z.object({ index: z.number().int().positive(), tagName: z.string(), role: z.string().nullable(), type: z.string().nullable(), label: z.string().nullable(), text: z.string().nullable(), placeholder: z.string().nullable(), href: z.string().nullable(), disabled: z.boolean(), }); export const BrowserPageSnapshotSchema = z.object({ snapshotId: z.string(), url: z.string(), title: z.string(), loading: z.boolean(), text: z.string(), elements: z.array(BrowserPageElementSchema), }); export const BrowserControlActionSchema = z.enum([ 'open', 'get-state', 'new-tab', 'switch-tab', 'close-tab', 'navigate', 'back', 'forward', 'reload', 'read-page', 'click', 'type', 'press', 'scroll', 'wait', ]); const BrowserElementTargetFields = { index: z.number().int().positive().optional(), selector: z.string().min(1).optional(), snapshotId: z.string().optional(), } as const; export const BrowserControlInputSchema = z.object({ action: BrowserControlActionSchema, target: z.string().min(1).optional(), tabId: z.string().min(1).optional(), text: z.string().optional(), key: z.string().min(1).optional(), direction: z.enum(['up', 'down']).optional(), amount: z.number().int().positive().max(5000).optional(), ms: z.number().int().positive().max(30000).optional(), maxElements: z.number().int().positive().max(100).optional(), maxTextLength: z.number().int().positive().max(20000).optional(), ...BrowserElementTargetFields, }).strict().superRefine((value, ctx) => { const needsElementTarget = value.action === 'click' || value.action === 'type'; const hasElementTarget = value.index !== undefined || value.selector !== undefined; if ((value.action === 'switch-tab' || value.action === 'close-tab') && !value.tabId) { ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['tabId'], message: 'tabId is required for this action.', }); } if ((value.action === 'navigate') && !value.target) { ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['target'], message: 'target is required for navigate.', }); } if (value.action === 'type' && value.text === undefined) { ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['text'], message: 'text is required for type.', }); } if (value.action === 'press' && !value.key) { ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['key'], message: 'key is required for press.', }); } if (needsElementTarget && !hasElementTarget) { ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['index'], message: 'Provide an element index or selector.', }); } }); export const BrowserControlResultSchema = z.object({ success: z.boolean(), action: BrowserControlActionSchema, message: z.string().optional(), error: z.string().optional(), browser: BrowserStateSchema, page: BrowserPageSnapshotSchema.optional(), }); export type BrowserTabState = z.infer; export type BrowserState = z.infer; export type BrowserPageElement = z.infer; export type BrowserPageSnapshot = z.infer; export type BrowserControlAction = z.infer; export type BrowserControlInput = z.infer; export type BrowserControlResult = z.infer;