@canaryai/cli 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +1 -0
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-DXJNFJ3A.js → chunk-6WWHXWCS.js} +3 -2
- package/dist/{chunk-DXJNFJ3A.js.map → chunk-6WWHXWCS.js.map} +1 -1
- package/dist/{chunk-HOYYXZPV.js → chunk-DXIAHB72.js} +3 -2
- package/dist/{chunk-HOYYXZPV.js.map → chunk-DXIAHB72.js.map} +1 -1
- package/dist/{chunk-UEOXNF5X.js → chunk-FIQBGAKW.js} +3 -1
- package/dist/{chunk-UEOXNF5X.js.map → chunk-FIQBGAKW.js.map} +1 -1
- package/dist/{chunk-TO66FC4R.js → chunk-ILEPYWZX.js} +3 -2
- package/dist/{chunk-TO66FC4R.js.map → chunk-ILEPYWZX.js.map} +1 -1
- package/dist/{chunk-7R4YFGP6.js → chunk-PWWQGYFG.js} +3 -1
- package/dist/{chunk-7R4YFGP6.js.map → chunk-PWWQGYFG.js.map} +1 -1
- package/dist/{chunk-DGUM43GV.js → chunk-VLFUCAPZ.js} +2 -1
- package/dist/{debug-workflow-EHKNO7BJ.js → debug-workflow-PT3OUR3V.js} +5 -4
- package/dist/{debug-workflow-EHKNO7BJ.js.map → debug-workflow-PT3OUR3V.js.map} +1 -1
- package/dist/docs-GCYDTEOY.js +270 -0
- package/dist/docs-GCYDTEOY.js.map +1 -0
- package/dist/{feature-flag-ZDLDYRSF.js → feature-flag-S5B5GLPP.js} +77 -33
- package/dist/feature-flag-S5B5GLPP.js.map +1 -0
- package/dist/index.js +26 -12
- package/dist/index.js.map +1 -1
- package/dist/{issues-FI3RIWGV.js → issues-4ZEDHPLW.js} +5 -4
- package/dist/{issues-FI3RIWGV.js.map → issues-4ZEDHPLW.js.map} +1 -1
- package/dist/{knobs-3MKMOXIV.js → knobs-RKUVK3HC.js} +5 -4
- package/dist/{knobs-3MKMOXIV.js.map → knobs-RKUVK3HC.js.map} +1 -1
- package/dist/{local-browser-GG5GUXDS.js → local-browser-J6WGFLVD.js} +5 -4
- package/dist/{local-browser-GG5GUXDS.js.map → local-browser-J6WGFLVD.js.map} +1 -1
- package/dist/mcp-HGYBMDYZ.js +687 -0
- package/dist/mcp-HGYBMDYZ.js.map +1 -0
- package/dist/{psql-IVAPNYZV.js → psql-WVIHMC6A.js} +4 -3
- package/dist/{psql-IVAPNYZV.js.map → psql-WVIHMC6A.js.map} +1 -1
- package/dist/record-X4SVNYP3.js +334 -0
- package/dist/record-X4SVNYP3.js.map +1 -0
- package/dist/{redis-LWY7L6AS.js → redis-RGHECKV5.js} +4 -3
- package/dist/{redis-LWY7L6AS.js.map → redis-RGHECKV5.js.map} +1 -1
- package/dist/{release-KQFCTAXA.js → release-ZOD4Y2BF.js} +4 -3
- package/dist/{release-KQFCTAXA.js.map → release-ZOD4Y2BF.js.map} +1 -1
- package/dist/runner/preload.js +3 -2
- package/dist/runner/preload.js.map +1 -1
- package/dist/test.js +3 -2
- package/dist/test.js.map +1 -1
- package/package.json +2 -2
- package/dist/feature-flag-ZDLDYRSF.js.map +0 -1
- package/dist/mcp-AD67OLQM.js +0 -384
- package/dist/mcp-AD67OLQM.js.map +0 -1
- /package/dist/{chunk-DGUM43GV.js.map → chunk-VLFUCAPZ.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runner/config.ts","../src/runner/env.ts","../src/runner/state.ts","../src/runner/healing-helpers.ts","../src/runner/healer.ts","../src/runner/ai-client.ts","../src/runner/healer-tools.ts","../src/runner/healer-prompt.ts"],"sourcesContent":["import path from \"node:path\";\nimport { loadCanaryEnv } from \"./env\";\n\nexport type CanaryConfig = {\n enabled: boolean;\n allowedPlaywrightVersion?: string;\n aiProvider?: string;\n aiModel?: string;\n apiKey?: string;\n healTimeoutMs: number;\n maxActions: number;\n dryRun: boolean;\n warnOnly: boolean;\n visionEnabled: boolean;\n debug: boolean;\n eventLogPath: string;\n eventLoggingEnabled: boolean;\n readOnly: boolean;\n allowRunCode: boolean;\n allowEvaluate: boolean;\n maxPayloadBytes: number;\n};\n\nexport function loadCanaryConfig(): CanaryConfig {\n loadCanaryEnv();\n\n const eventLogPath = (() => {\n const userPath = process.env.CANARY_EVENT_LOG;\n if (userPath) return userPath;\n const worker = process.env.TEST_WORKER_INDEX ?? \"runner\";\n return path.join(process.cwd(), \"test-results\", \"ai-healer\", `events-worker-${worker}.jsonl`);\n })();\n\n const baseConfig: CanaryConfig = {\n enabled: true,\n allowedPlaywrightVersion: process.env.CANARY_PLAYWRIGHT_VERSION,\n aiProvider: process.env.AI_PROVIDER,\n aiModel: process.env.AI_MODEL,\n apiKey: process.env.AI_API_KEY,\n healTimeoutMs: Number(process.env.AI_TIMEOUT_MS ?? 120000), // 2 minutes for agentic healing\n maxActions: Number(process.env.CANARY_MAX_ACTIONS ?? 50), // Generous step limit for agentic healing\n dryRun: process.env.CANARY_DRY_RUN === \"1\",\n warnOnly: process.env.CANARY_WARN_ONLY === \"1\",\n visionEnabled: process.env.CANARY_VISION === \"1\" || process.env.AI_VISION === \"1\",\n debug: process.env.CANARY_DEBUG === \"1\",\n readOnly: process.env.CANARY_READ_ONLY === \"1\",\n allowRunCode: process.env.CANARY_ALLOW_RUN_CODE === \"1\",\n allowEvaluate: process.env.CANARY_ALLOW_EVALUATE !== \"0\",\n maxPayloadBytes: Number(process.env.CANARY_MAX_PAYLOAD_BYTES ?? 60000), // cap snapshots/screenshots/text\n eventLogPath,\n eventLoggingEnabled: process.env.CANARY_EVENT_LOG !== \"0\",\n };\n\n const disabled =\n process.env.CANARY_ENABLED === \"0\" ||\n process.env.CANARY_DISABLED === \"1\" ||\n process.env.AI_HEALING === \"0\";\n\n return {\n ...baseConfig,\n enabled: !disabled && baseConfig.enabled,\n };\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\n\nlet loaded = false;\n\nexport function loadCanaryEnv(): void {\n if (loaded) return;\n loaded = true;\n\n const explicitPath = process.env.CANARY_ENV_FILE;\n const defaultPath = path.join(process.cwd(), \".env\");\n const envPath = explicitPath || defaultPath;\n\n if (fs.existsSync(envPath)) {\n loadFile(envPath);\n }\n}\n\nfunction loadFile(filePath: string): void {\n try {\n const raw = fs.readFileSync(filePath, \"utf-8\");\n for (const line of raw.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n const idx = trimmed.indexOf(\"=\");\n if (idx === -1) continue;\n const key = trimmed.slice(0, idx).trim();\n const value = trimmed.slice(idx + 1).trim();\n if (!key) continue;\n if (process.env[key] === undefined) {\n process.env[key] = stripQuotes(value);\n }\n }\n } catch {\n // ignore\n }\n}\n\nfunction stripQuotes(value: string): string {\n if ((value.startsWith(\"\\\"\") && value.endsWith(\"\\\"\")) || (value.startsWith(\"'\") && value.endsWith(\"'\"))) {\n return value.slice(1, -1);\n }\n return value;\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { loadCanaryConfig } from \"./config\";\nimport { loadCanaryEnv } from \"./env\";\n\nexport type HealingEvent = {\n kind: \"locator\" | \"page\" | \"expect\" | \"unknown\";\n action?: string;\n target?: string;\n errorMessage?: string;\n healed: boolean;\n workerId?: string;\n timestamp?: string;\n decision?: string;\n reason?: string;\n strategy?: string;\n actions?: Array<{ type: string; detail?: string }>;\n durationMs?: number;\n mode?: \"dry-run\" | \"warn\" | \"full\";\n modelId?: string;\n // Report fields\n summary?: string; // Human-readable one-line healing summary from agent\n testFile?: string; // Test file path for correlation\n testTitle?: string; // Test title for correlation\n};\n\ndeclare global {\n // eslint-disable-next-line no-var\n var __CANARY_EVENTS: HealingEvent[] | undefined;\n // eslint-disable-next-line no-var\n var __CANARY_PATCHED: boolean | undefined;\n // eslint-disable-next-line no-var\n var __CANARY_EVENT_LOG_PATH: string | undefined;\n}\n\nexport function getEventLog(): HealingEvent[] {\n if (!globalThis.__CANARY_EVENTS) {\n globalThis.__CANARY_EVENTS = [];\n }\n return globalThis.__CANARY_EVENTS;\n}\n\nexport function setEventLogPath(path: string): void {\n globalThis.__CANARY_EVENT_LOG_PATH = path;\n}\n\nexport function getEventLogPath(): string | undefined {\n return globalThis.__CANARY_EVENT_LOG_PATH;\n}\n\nexport function recordHealingEvent(event: HealingEvent): void {\n loadCanaryEnv();\n const log = getEventLog();\n const entry: HealingEvent = {\n ...event,\n workerId: process.env.TEST_WORKER_INDEX,\n timestamp: new Date().toISOString(),\n };\n log.push(entry);\n appendEvent(entry);\n}\n\nexport function markPatched(): void {\n globalThis.__CANARY_PATCHED = true;\n}\n\nexport function alreadyPatched(): boolean {\n return globalThis.__CANARY_PATCHED === true;\n}\n\nfunction appendEvent(event: HealingEvent): void {\n const logPath = getEventLogPath();\n if (!logPath) return;\n const config = loadCanaryConfig();\n if (!config.eventLoggingEnabled) return;\n\n try {\n fs.mkdirSync(path.dirname(logPath), { recursive: true });\n fs.appendFileSync(logPath, JSON.stringify(event) + \"\\n\");\n } catch {\n // Swallow logging errors to avoid impacting tests.\n }\n}\n","/**\n * Shared helpers for Playwright healing instrumentation.\n *\n * Used by both `instrumentation.ts` (Module._load hook approach)\n * and `wrapper.ts` (direct wrap approach).\n */\n\nimport { createRequire } from \"node:module\";\nimport fs from \"node:fs\";\nimport { classifyFailure, executeHealActions, type FailureContext } from \"./healer\";\nimport { recordHealingEvent } from \"./state\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst requireFn: any = typeof require !== \"undefined\" ? require : createRequire(import.meta.url);\n\n// === Action / Factory Constants ===\n\nexport const LOCATOR_ACTIONS = new Set([\n \"click\",\n \"dblclick\",\n \"fill\",\n \"check\",\n \"uncheck\",\n \"hover\",\n \"press\",\n \"type\",\n \"selectOption\",\n \"tap\",\n]);\n\nexport const PAGE_ACTIONS = new Set([\n \"goto\",\n \"click\",\n \"dblclick\",\n \"fill\",\n \"check\",\n \"uncheck\",\n \"hover\",\n \"press\",\n \"type\",\n \"selectOption\",\n \"tap\",\n \"waitForSelector\",\n]);\n\nexport const LOCATOR_FACTORIES = new Set([\n \"locator\",\n \"getByRole\",\n \"getByText\",\n \"getByLabel\",\n \"getByPlaceholder\",\n \"getByAltText\",\n \"getByTitle\",\n \"getByTestId\",\n \"frameLocator\",\n]);\n\nexport const LOCATOR_CHAIN_METHODS = new Set([\n \"locator\",\n \"first\",\n \"last\",\n \"nth\",\n \"filter\",\n \"getByRole\",\n \"getByText\",\n \"getByLabel\",\n \"getByPlaceholder\",\n \"getByAltText\",\n \"getByTitle\",\n \"getByTestId\",\n]);\n\n// === Types ===\n\nexport type PlaywrightExports = {\n test: { extend: typeof import(\"@playwright/test\").test.extend };\n expect: typeof import(\"@playwright/test\").expect;\n [key: string]: unknown;\n};\n\nexport type WrapOptions = {\n debug?: boolean;\n};\n\ntype AttemptContext = {\n kind: FailureContext[\"kind\"];\n action: string;\n target?: string;\n locator?: unknown;\n page?: unknown;\n invoke: () => unknown;\n debug?: boolean;\n};\n\ntype MatcherContext = {\n matcher: (...args: unknown[]) => unknown;\n matcherName: string;\n args: unknown[];\n expectTarget: unknown;\n mode: \"hard\" | \"soft\";\n debug?: boolean;\n};\n\n// === Proxy Wrappers ===\n\nexport function wrapExpect(expectImpl: PlaywrightExports[\"expect\"], options: WrapOptions): PlaywrightExports[\"expect\"] {\n const wrapMatchers = (expectation: unknown, mode: \"hard\" | \"soft\") => {\n return new Proxy(expectation ?? {}, {\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (typeof prop === \"string\" && typeof value === \"function\") {\n return (...args: unknown[]) =>\n runMatcherWithHealing({\n matcher: value as (...args: unknown[]) => unknown,\n matcherName: prop,\n args,\n expectTarget: target,\n mode,\n debug: options.debug,\n });\n }\n return value;\n },\n });\n };\n\n const proxy = new Proxy(expectImpl, {\n apply(target, thisArg, argArray) {\n const expectation = (target as (...args: unknown[]) => unknown).apply(thisArg, argArray as unknown[]);\n return wrapMatchers(expectation, \"hard\");\n },\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (prop === \"soft\" && typeof value === \"function\") {\n return (...args: unknown[]) => wrapMatchers((value as (...args: unknown[]) => unknown).apply(target, args), \"soft\");\n }\n return value;\n },\n });\n\n return proxy as PlaywrightExports[\"expect\"];\n}\n\nexport function wrapPage<PageType extends object>(page: PageType, options: WrapOptions): PageType {\n return new Proxy(page, {\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n\n if (typeof prop === \"string\") {\n if (LOCATOR_FACTORIES.has(prop) && typeof value === \"function\") {\n return (...args: unknown[]) => {\n const locator = (value as (...args: unknown[]) => unknown).apply(target, args);\n return wrapLocator(locator, options, target);\n };\n }\n\n if (PAGE_ACTIONS.has(prop) && typeof value === \"function\") {\n return async (...args: unknown[]) => {\n return attemptWithHealing({\n kind: \"page\",\n action: prop,\n target: safeTargetString(target),\n locator: undefined,\n page: target as unknown,\n invoke: () => (value as (...args: unknown[]) => unknown).apply(target, args),\n debug: options.debug,\n });\n };\n }\n }\n\n return value;\n },\n });\n}\n\nexport function wrapLocator(locator: unknown, options: WrapOptions, page?: unknown): unknown {\n if (!locator || typeof locator !== \"object\") return locator;\n\n return new Proxy(locator, {\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n\n if (typeof prop === \"string\" && LOCATOR_ACTIONS.has(prop) && typeof value === \"function\") {\n return async (...args: unknown[]) => {\n return attemptWithHealing({\n kind: \"locator\",\n action: prop,\n target: safeTargetString(target),\n locator: target as unknown,\n page,\n invoke: () => (value as (...args: unknown[]) => unknown).apply(target, args),\n debug: options.debug,\n });\n };\n }\n\n if (typeof prop === \"string\" && LOCATOR_CHAIN_METHODS.has(prop) && typeof value === \"function\") {\n return (...args: unknown[]) => wrapLocator((value as (...args: unknown[]) => unknown).apply(target, args), options, page);\n }\n\n return value;\n },\n });\n}\n\n// === Healing Logic ===\n\nasync function attemptWithHealing<T>(ctx: AttemptContext): Promise<T> {\n try {\n return (await Promise.resolve(ctx.invoke())) as T;\n } catch (error) {\n const failure = buildFailureContext(ctx.kind, ctx.action, ctx.target, error);\n recordHealingEvent({ ...failure, healed: false });\n const decision = classifyFailure(failure);\n if (ctx.debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[canary][debug] failure intercepted: kind=${ctx.kind} action=${ctx.action} reason=${\n decision.healable ? decision.reason ?? \"healable\" : decision.reason\n }`\n );\n }\n if (!decision.healable) {\n throw error;\n }\n\n const testContext = getTestContext();\n const outcome = await executeHealActions(decision, failure, {\n kind: ctx.kind,\n action: ctx.action,\n target: ctx.target,\n locator: isLocatorLike(ctx.locator) ? ctx.locator : undefined,\n page: isPageLike(ctx.page) ? ctx.page : undefined,\n testContext: {\n testFile: testContext?.testFile,\n testTitle: testContext?.testTitle,\n testSource: loadTestSource(testContext?.testFile),\n },\n });\n\n const summaryBase = {\n ...failure,\n healed: false,\n strategy: 'agentic',\n reason: outcome.reason ?? decision.reason,\n actions: actionsToEventItems(outcome.actionsRun),\n durationMs: outcome.durationMs,\n mode: outcome.mode,\n decision: decision.reason,\n modelId: outcome.modelId,\n summary: outcome.summary,\n testFile: testContext?.testFile,\n testTitle: testContext?.testTitle,\n } as const;\n\n if (outcome.healed) {\n if (ctx.debug) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] healed via AI tool for action=${ctx.action}`);\n }\n recordHealingEvent({ ...summaryBase, healed: true });\n return undefined as T;\n }\n\n if (outcome.shouldRetryOriginal) {\n try {\n const retried = (await Promise.resolve(ctx.invoke())) as T;\n if (ctx.debug) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] retry_original succeeded for action=${ctx.action}`);\n }\n recordHealingEvent({ ...summaryBase, healed: true });\n return retried;\n } catch (retryError) {\n const retryInfo = errorInfo(retryError);\n recordHealingEvent({ ...summaryBase, healed: false, errorMessage: retryInfo.message });\n if (ctx.debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[canary][debug] retry_original failed for action=${ctx.action}: ${retryInfo.message ?? retryError}`\n );\n }\n throw retryError;\n }\n }\n\n recordHealingEvent(summaryBase);\n throw error;\n }\n}\n\nasync function runMatcherWithHealing(params: MatcherContext): Promise<unknown> {\n const { matcher, matcherName, args, expectTarget, mode } = params;\n const invoke = () => matcher.apply(expectTarget, args);\n\n if (mode === \"soft\") {\n return Promise.resolve(invoke());\n }\n\n try {\n return await Promise.resolve(invoke());\n } catch (error) {\n const target = stringifyTarget(args?.[0]);\n const failure = buildFailureContext(\"expect\", matcherName, target, error);\n recordHealingEvent({ ...failure, healed: false });\n const decision = classifyFailure(failure);\n if (!decision.healable) {\n throw error;\n }\n\n const outcome = await executeHealActions(decision, failure, {\n kind: \"expect\",\n action: matcherName,\n target,\n locator: isLocatorLike(args?.[0]) ? args[0] : undefined,\n });\n\n const testContextMatcher = getTestContext();\n const summaryBase = {\n ...failure,\n healed: false,\n strategy: 'agentic',\n reason: outcome.reason ?? decision.reason,\n actions: actionsToEventItems(outcome.actionsRun),\n durationMs: outcome.durationMs,\n mode: outcome.mode,\n decision: decision.reason,\n modelId: outcome.modelId,\n summary: outcome.summary,\n testFile: testContextMatcher?.testFile,\n testTitle: testContextMatcher?.testTitle,\n } as const;\n\n if (outcome.shouldRetryOriginal) {\n try {\n const retried = await Promise.resolve(invoke());\n recordHealingEvent({ ...summaryBase, healed: true });\n return retried;\n } catch (retryError) {\n const retryInfo = errorInfo(retryError);\n recordHealingEvent({ ...summaryBase, healed: false, errorMessage: retryInfo.message });\n throw retryError;\n }\n }\n\n recordHealingEvent(summaryBase);\n throw error;\n }\n}\n\n// === Utility Functions ===\n\nfunction buildFailureContext(\n kind: FailureContext[\"kind\"],\n action: string,\n target: string | undefined,\n error: unknown\n): FailureContext {\n const info = errorInfo(error);\n return {\n kind,\n action,\n target,\n errorMessage: info.message,\n errorName: info.name,\n stack: info.stack,\n };\n}\n\nfunction actionsToEventItems(actions: string[]): Array<{ type: string; detail?: string }> {\n return actions.map((action) => ({ type: action }));\n}\n\nfunction errorInfo(error: unknown): { message?: string; name?: string; stack?: string } {\n if (error instanceof Error) {\n return { message: error.message, name: error.name, stack: error.stack };\n }\n return { message: typeof error === \"string\" ? error : JSON.stringify(error) };\n}\n\nfunction isLocatorLike(candidate: unknown): candidate is { scrollIntoViewIfNeeded: () => Promise<unknown> } {\n return Boolean(candidate && typeof candidate === \"object\" && \"scrollIntoViewIfNeeded\" in (candidate as Record<string, unknown>));\n}\n\nfunction isPageLike(candidate: unknown): candidate is { waitForTimeout: (ms: number) => Promise<unknown> } {\n return Boolean(candidate && typeof candidate === \"object\" && \"waitForTimeout\" in (candidate as Record<string, unknown>));\n}\n\nfunction safeTargetString(target: unknown): string | undefined {\n try {\n if (typeof target === \"object\" && target !== null && \"toString\" in target) {\n const s = String((target as { toString: () => string }).toString());\n if (s && s !== \"[object Object]\") return s;\n }\n } catch {\n // ignore\n }\n return undefined;\n}\n\nfunction stringifyTarget(candidate: unknown): string | undefined {\n if (!candidate) return undefined;\n if (typeof candidate === \"string\") return candidate;\n if (typeof candidate === \"object\") {\n if (\n \"selector\" in (candidate as Record<string, unknown>) &&\n typeof (candidate as Record<string, unknown>).selector === \"string\"\n ) {\n return String((candidate as Record<string, unknown>).selector);\n }\n }\n return safeTargetString(candidate);\n}\n\nfunction getTestContext(): { testFile?: string; testTitle?: string } | undefined {\n try {\n const playwright = requireFn(\"@playwright/test\") as {\n test?: { info?: () => { file?: string; title?: string } };\n };\n if (playwright?.test?.info) {\n const info = playwright.test.info();\n if (info) {\n return { testFile: info.file, testTitle: info.title };\n }\n }\n } catch {\n // Not in a Playwright test context\n }\n return undefined;\n}\n\nfunction loadTestSource(filePath?: string): string | undefined {\n if (!filePath) return undefined;\n try {\n return fs.readFileSync(filePath, \"utf-8\");\n } catch {\n return undefined;\n }\n}\n","/**\n * AI Test Healer — Core types, failure classification, and execution entry point.\n *\n * Tool definitions are in `healer-tools.ts`.\n * Prompt construction is in `healer-prompt.ts`.\n */\n\nimport { stepCountIs, streamText } from \"ai\";\nimport { loadCanaryConfig } from \"./config\";\nimport { resolveHealerModel } from \"./ai-client\";\nimport { createAgenticTools, logToolStep } from \"./healer-tools\";\nimport { buildSystemPrompt, buildInitialMessages, buildInitialPageContext } from \"./healer-prompt\";\n\n// === Redaction / Security ===\n\nconst REDACTION_PATTERNS: Array<{ regex: RegExp; replacement: string }> = [\n { regex: /bearer\\s+[a-z0-9._-]+/gi, replacement: \"[REDACTED_BEARER]\" },\n { regex: /api[_-]?key[:\\s\"']+[a-z0-9._-]+/gi, replacement: \"[REDACTED_API_KEY]\" },\n { regex: /secret[:\\s\"']+[a-z0-9._-]+/gi, replacement: \"[REDACTED_SECRET]\" },\n { regex: /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}/g, replacement: \"[REDACTED_EMAIL]\" },\n { regex: /[0-9]{12,}/g, replacement: \"[REDACTED_NUMBER]\" },\n];\nconst CODE_DENY_PATTERNS = [/process\\.env/i, /child_process/i, /\\brequire\\s*\\(/i, /\\bimport\\s*\\(/i];\n\n// === Types ===\n\nexport type FailureContext = {\n kind: \"locator\" | \"page\" | \"expect\" | \"unknown\";\n action?: string;\n target?: string;\n errorMessage?: string;\n errorName?: string;\n stack?: string;\n};\n\nexport type TestContext = {\n testFile?: string;\n testTitle?: string;\n testSource?: string;\n currentStep: string;\n expectedAfter?: string;\n action: string;\n target?: string;\n errorMessage?: string;\n};\n\nexport type HealDecision =\n | { healable: false; reason: string }\n | { healable: true; reason?: string };\n\nexport type HealExecutionContext = {\n kind: FailureContext[\"kind\"];\n target?: string;\n action?: string;\n locator?: { scrollIntoViewIfNeeded?: (options?: { timeout?: number }) => Promise<unknown> };\n page?: unknown;\n testContext?: Partial<TestContext>;\n};\n\nexport type HealOutcome = {\n healed: boolean;\n shouldRetryOriginal: boolean;\n actionsRun: string[];\n timedOut: boolean;\n durationMs: number;\n errors: string[];\n mode: \"dry-run\" | \"warn\" | \"full\";\n reason?: string;\n modelId?: string;\n summary?: string;\n};\n\n// === Failure Classification ===\n\nexport function classifyFailure(context: FailureContext): HealDecision {\n const message = (context.errorMessage ?? \"\").toLowerCase();\n\n if (\n message.includes(\"closed\") ||\n message.includes(\"target page, context or browser has been closed\")\n ) {\n return { healable: false, reason: \"context_closed\" };\n }\n\n if (message.includes(\"navigation failed\") && message.includes(\"net::\")) {\n return { healable: false, reason: \"navigation_failed\" };\n }\n\n return { healable: true, reason: \"agent_healing\" };\n}\n\n// === Main Entry Point ===\n\nexport async function executeHealActions(\n decision: HealDecision,\n failure: FailureContext,\n execCtx: HealExecutionContext\n): Promise<HealOutcome> {\n const config = loadCanaryConfig();\n const started = Date.now();\n const mode = resolveMode(config);\n const actionsRun: string[] = [];\n const errors: string[] = [];\n const logTools = config.debug || process.env.CANARY_TOOL_LOG === \"1\";\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const initialContext = await buildInitialPageContext(execCtx.page as any, config);\n\n if (!decision.healable) {\n return baseOutcome({ mode, started, healed: false, shouldRetryOriginal: false, reason: decision.reason, actionsRun, errors });\n }\n\n const { model, modelId, reason: modelReason } = resolveHealerModel(config);\n if (!model) {\n return baseOutcome({ mode, started, healed: false, shouldRetryOriginal: false, reason: modelReason ?? \"no_model\", actionsRun, errors });\n }\n\n const testContext: TestContext = {\n testFile: execCtx.testContext?.testFile,\n testTitle: execCtx.testContext?.testTitle,\n testSource: execCtx.testContext?.testSource ? sanitizeString(execCtx.testContext.testSource) : undefined,\n currentStep: `${failure.action ?? \"unknown\"}(${failure.target ?? \"unknown\"})`,\n expectedAfter: execCtx.testContext?.expectedAfter,\n action: failure.action ?? \"unknown\",\n target: failure.target,\n errorMessage: failure.errorMessage,\n };\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const toolset = createAgenticTools({ page: execCtx.page as any, config, actionsRun, debug: config.debug });\n let completionReason: string | undefined;\n\n try {\n const abort = AbortSignal.timeout(config.healTimeoutMs);\n const result = await streamText({\n model,\n system: buildSystemPrompt(testContext, config),\n messages: buildInitialMessages(testContext, failure, initialContext),\n tools: toolset.tools,\n stopWhen: stepCountIs(Math.max(1, config.maxActions)),\n abortSignal: abort,\n maxRetries: 0,\n onStepFinish: (step) => {\n if (config.debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[canary][debug] step finish: finishReason=${step.finishReason}; toolCalls=${step.toolCalls?.length ?? 0}; toolResults=${step.toolResults?.length ?? 0}`\n );\n }\n if (logTools) logToolStep(step);\n },\n });\n\n for await (const _ of result.fullStream) {\n if (toolset.isComplete()) {\n completionReason = toolset.getCompletionReason();\n if (config.debug) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] agent marked complete: ${completionReason}`);\n }\n break;\n }\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n errors.push(message);\n if (config.debug) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] agent error: ${message}`);\n }\n }\n\n const healed = toolset.isComplete();\n\n return {\n healed,\n shouldRetryOriginal: false,\n actionsRun,\n timedOut: Date.now() - started > config.healTimeoutMs,\n durationMs: Date.now() - started,\n errors,\n mode,\n reason: completionReason ?? (healed ? \"agent_healed\" : \"agent_incomplete\"),\n modelId,\n summary: toolset.getSummary(),\n };\n}\n\n// === Utilities (exported for use by healer-tools.ts and healer-prompt.ts) ===\n\nfunction resolveMode(config: ReturnType<typeof loadCanaryConfig>): \"dry-run\" | \"warn\" | \"full\" {\n if (config.dryRun) return \"dry-run\";\n if (config.warnOnly) return \"warn\";\n return \"full\";\n}\n\nfunction baseOutcome({\n started,\n healed,\n shouldRetryOriginal,\n mode,\n reason,\n actionsRun = [],\n errors = [],\n}: {\n started: number;\n healed: boolean;\n shouldRetryOriginal: boolean;\n mode: \"dry-run\" | \"warn\" | \"full\";\n reason?: string;\n actionsRun?: string[];\n errors?: string[];\n}): HealOutcome {\n return {\n healed,\n shouldRetryOriginal,\n actionsRun,\n timedOut: false,\n durationMs: Date.now() - started,\n errors,\n mode,\n reason,\n };\n}\n\nexport function sanitizeString(value: string): string {\n let result = value;\n for (const { regex, replacement } of REDACTION_PATTERNS) {\n result = result.replace(regex, replacement);\n }\n return result;\n}\n\nexport function sanitizeUnknown(value: unknown, maxPayloadBytes: number, seen = new WeakSet()): unknown {\n if (value === null || value === undefined) return value;\n if (typeof value === \"string\") {\n return truncate(sanitizeString(value), maxPayloadBytes);\n }\n if (typeof value === \"number\" || typeof value === \"boolean\") return value;\n if (typeof value === \"object\") {\n if (seen.has(value as object)) return \"[REDACTED_CYCLE]\";\n seen.add(value as object);\n if (Array.isArray(value)) {\n return value.slice(0, 50).map((v) => sanitizeUnknown(v, maxPayloadBytes, seen));\n }\n const out: Record<string, unknown> = {};\n const entries = Object.entries(value as Record<string, unknown>).slice(0, 50);\n for (const [k, v] of entries) {\n out[k] = sanitizeUnknown(v, maxPayloadBytes, seen);\n }\n return out;\n }\n return \"[REDACTED_UNKNOWN]\";\n}\n\nexport function violatesCodeDenylist(code: string): boolean {\n return CODE_DENY_PATTERNS.some((p) => p.test(code));\n}\n\nfunction truncate(value: string, max: number): string {\n if (value.length <= max) return value;\n return value.slice(0, max) + \"...\";\n}\n","import { createOpenAI } from \"@ai-sdk/openai\";\nimport { createAnthropic } from \"@ai-sdk/anthropic\";\nimport { createAzure } from \"@ai-sdk/azure\";\nimport type { LanguageModel } from \"ai\";\nimport { loadCanaryConfig, type CanaryConfig } from \"./config\";\n\ntype ResolvedModel = {\n model: LanguageModel | null;\n modelId?: string;\n reason?: string;\n};\n\nexport function resolveHealerModel(config: CanaryConfig = loadCanaryConfig()): ResolvedModel {\n if (!config.enabled) {\n return { model: null, reason: \"healing_disabled\" };\n }\n\n if (!config.apiKey) {\n return { model: null, reason: \"missing_api_key\" };\n }\n\n const provider = (config.aiProvider ?? \"openai\").toLowerCase();\n const modelId = config.aiModel || (provider === \"anthropic\" ? \"claude-3-5-haiku-20241022\" : \"gpt-4o-mini\");\n\n try {\n switch (provider) {\n case \"openai\": {\n const client = createOpenAI({ apiKey: config.apiKey, baseURL: process.env.AI_BASE_URL });\n return { model: client(modelId), modelId };\n }\n case \"anthropic\": {\n const client = createAnthropic({ apiKey: config.apiKey, baseURL: process.env.AI_BASE_URL });\n return { model: client(modelId), modelId };\n }\n case \"azure\": {\n const resourceName = process.env.AZURE_OPENAI_RESOURCE_NAME;\n const apiVersion = process.env.AZURE_OPENAI_API_VERSION;\n if (!resourceName) {\n return { model: null, reason: \"missing_azure_resource\" };\n }\n const client = createAzure({\n apiKey: config.apiKey,\n resourceName,\n apiVersion,\n });\n return { model: client(modelId), modelId };\n }\n default:\n return { model: null, reason: \"unsupported_provider\" };\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { model: null, reason: `model_error:${message}` };\n }\n}\n","/**\n * Agentic tool definitions for the AI test healer.\n *\n * Uses a `makePageTool` factory to eliminate repetitive guard/try-catch boilerplate\n * across the ~15 tool definitions.\n */\n\nimport { tool, type Tool } from \"ai\";\nimport { z } from \"zod\";\nimport type { loadCanaryConfig } from \"./config\";\nimport { sanitizeString, sanitizeUnknown, violatesCodeDenylist } from \"./healer\";\n\ntype PageLike = {\n waitForTimeout?: (ms: number) => Promise<unknown>;\n accessibility?: { snapshot: () => Promise<unknown> };\n screenshot?: (options?: { fullPage?: boolean }) => Promise<Buffer | Uint8Array | string>;\n locator?: (selector: string) => LocatorLike & {\n click?: (opts?: unknown) => Promise<unknown>;\n fill?: (value: string, opts?: unknown) => Promise<unknown>;\n pressSequentially?: (text: string, opts?: unknown) => Promise<unknown>;\n hover?: () => Promise<unknown>;\n selectOption?: (value: string) => Promise<unknown>;\n textContent?: () => Promise<string | null>;\n isVisible?: () => Promise<boolean>;\n waitFor?: (opts?: { state?: string }) => Promise<unknown>;\n };\n getByText?: (text: string) => LocatorLike & {\n click?: (opts?: unknown) => Promise<unknown>;\n };\n keyboard?: { press?: (key: string) => Promise<unknown> };\n goto?: (url: string) => Promise<unknown>;\n goBack?: () => Promise<unknown>;\n reload?: () => Promise<unknown>;\n evaluate?: (script: string) => Promise<unknown>;\n};\n\ntype LocatorLike = {\n scrollIntoViewIfNeeded?: (options?: { timeout?: number }) => Promise<unknown>;\n};\n\ntype ToolConfig = ReturnType<typeof loadCanaryConfig>;\n\ntype ToolContext = {\n page?: PageLike;\n config: ToolConfig;\n actionsRun: string[];\n debug?: boolean;\n};\n\ntype ToolsetResult = {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n tools: Record<string, Tool<any, any>>;\n isComplete: () => boolean;\n getCompletionReason: () => string | undefined;\n getSummary: () => string | undefined;\n};\n\n// === Helper Factories ===\n\nfunction truncate(value: string, max: number): string {\n if (value.length <= max) return value;\n return value.slice(0, max) + \"...\";\n}\n\nfunction safeJson(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\nfunction errMsg(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Factory for tools that interact with the page. Handles:\n * - tracking the action name\n * - debug logging\n * - read-only / dry-run guards\n * - page availability check\n * - try/catch with error formatting\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction makePageTool(\n ctx: ToolContext,\n opts: {\n name: string;\n description: string;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n inputSchema: z.ZodType<any>;\n /** Whether this tool mutates state (subject to readOnly guard). Default true. */\n mutates?: boolean;\n /** Key to check on page for availability (e.g. 'locator', 'goto'). Skips guard if omitted. */\n pageCheck?: keyof PageLike;\n /** The dry-run result to return. */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n dryResult: (input: any) => unknown;\n /** The actual execution. */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n execute: (input: any) => Promise<unknown>;\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n): Tool<any, any> {\n const { page, config, actionsRun, debug } = ctx;\n const mutates = opts.mutates ?? true;\n\n const log = (msg: string) => {\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] ${msg}`);\n }\n };\n\n return tool({\n description: opts.description,\n inputSchema: opts.inputSchema,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n execute: async (input: any) => {\n const trackSuffix = input?.text ?? input?.selector ?? input?.name ?? input?.key ?? input?.url ?? input?.ms ?? \"\";\n actionsRun.push(trackSuffix ? `${opts.name}:${trackSuffix}` : opts.name);\n log(`${opts.name} called`);\n\n if (mutates && config.readOnly) return { error: \"read_only_mode\" };\n if (config.dryRun) return opts.dryResult(input);\n if (opts.pageCheck && !page?.[opts.pageCheck]) return { error: \"Page unavailable\" };\n\n try {\n const result = await opts.execute(input);\n log(`${opts.name} succeeded`);\n return result;\n } catch (err) {\n const msg = errMsg(err);\n log(`${opts.name} error: ${msg}`);\n return { error: msg };\n }\n },\n });\n}\n\n// === Main Export ===\n\nexport function createAgenticTools(params: {\n page?: PageLike;\n config: ToolConfig;\n actionsRun: string[];\n debug?: boolean;\n}): ToolsetResult {\n const ctx: ToolContext = params;\n const { page, config } = ctx;\n let complete = false;\n let completionReason: string | undefined;\n let completionSummary: string | undefined;\n const maxPayloadBytes = Math.max(1000, config.maxPayloadBytes || 60000);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const tools: Record<string, Tool<any, any>> = {\n // === Core Action Tools ===\n click: makePageTool(ctx, {\n name: \"click\",\n description: \"Click an element by its visible text\",\n inputSchema: z.object({ text: z.string().describe(\"The visible text to click\") }),\n pageCheck: \"getByText\",\n dryResult: ({ text }) => ({ clicked: text, mode: \"dry-run\" as const }),\n execute: async ({ text }) => {\n const el = page!.getByText!(text);\n if (el?.scrollIntoViewIfNeeded) await el.scrollIntoViewIfNeeded({ timeout: 2000 });\n const clickable = el as { click?: (opts?: unknown) => Promise<unknown> };\n if (clickable?.click) await clickable.click({ timeout: 5000 });\n return { clicked: text };\n },\n }),\n\n click_selector: makePageTool(ctx, {\n name: \"click_selector\",\n description: \"Click an element by CSS or Playwright selector\",\n inputSchema: z.object({ selector: z.string().describe(\"CSS or Playwright selector\") }),\n pageCheck: \"locator\",\n dryResult: ({ selector }) => ({ clicked: selector, mode: \"dry-run\" as const }),\n execute: async ({ selector }) => {\n const el = page!.locator!(selector);\n if (el?.scrollIntoViewIfNeeded) await el.scrollIntoViewIfNeeded({ timeout: 2000 });\n if (el?.click) await el.click({ timeout: 5000 });\n return { clicked: selector };\n },\n }),\n\n fill: makePageTool(ctx, {\n name: \"fill\",\n description: \"Fill an input field with a value using CSS selector (clears existing content)\",\n inputSchema: z.object({\n selector: z.string().describe('CSS selector for the input (e.g. input[placeholder=\"...\"])'),\n value: z.string().describe(\"Value to fill\"),\n }),\n pageCheck: \"locator\",\n dryResult: ({ selector }) => ({ filled: selector, mode: \"dry-run\" as const }),\n execute: async ({ selector, value }) => {\n const el = page!.locator!(selector);\n if (el?.fill) await el.fill(value, { timeout: 5000 });\n return { filled: selector, value };\n },\n }),\n\n fill_by_name: makePageTool(ctx, {\n name: \"fill_by_name\",\n description: 'Fill a textbox by its accessible name (shown in snapshot as textbox \"NAME\"). This is the PREFERRED way to fill inputs - use the name from the snapshot directly.',\n inputSchema: z.object({\n name: z.string().describe('The accessible name of the textbox (from snapshot, e.g. \"Enter 6-digit code\")'),\n value: z.string().describe(\"Value to fill\"),\n }),\n pageCheck: \"locator\",\n dryResult: ({ name }) => ({ filled: name, mode: \"dry-run\" as const }),\n execute: async ({ name, value }) => {\n const el = page!.locator!(`role=textbox[name=\"${name}\"]`);\n if (el?.fill) await el.fill(value, { timeout: 5000 });\n return { filled: name, value };\n },\n }),\n\n type: makePageTool(ctx, {\n name: \"type\",\n description: \"Type text character by character (triggers key events)\",\n inputSchema: z.object({\n selector: z.string().describe(\"CSS or Playwright selector\"),\n text: z.string().describe(\"Text to type\"),\n }),\n pageCheck: \"locator\",\n dryResult: ({ text }) => ({ typed: text, mode: \"dry-run\" as const }),\n execute: async ({ selector, text }) => {\n const el = page!.locator!(selector);\n if (el?.pressSequentially) await el.pressSequentially(text);\n return { typed: text, into: selector };\n },\n }),\n\n press_key: makePageTool(ctx, {\n name: \"press_key\",\n description: \"Press a keyboard key (Enter, Tab, Escape, ArrowDown, etc.)\",\n inputSchema: z.object({ key: z.string().describe(\"Key to press\") }),\n pageCheck: \"keyboard\",\n dryResult: ({ key }) => ({ pressed: key, mode: \"dry-run\" as const }),\n execute: async ({ key }) => {\n await page!.keyboard!.press!(key);\n return { pressed: key };\n },\n }),\n\n hover: makePageTool(ctx, {\n name: \"hover\",\n description: \"Hover over an element\",\n inputSchema: z.object({ selector: z.string().describe(\"CSS or Playwright selector\") }),\n pageCheck: \"locator\",\n dryResult: ({ selector }) => ({ hovered: selector, mode: \"dry-run\" as const }),\n execute: async ({ selector }) => {\n const el = page!.locator!(selector);\n if (el?.hover) await el.hover();\n return { hovered: selector };\n },\n }),\n\n select_option: makePageTool(ctx, {\n name: \"select_option\",\n description: \"Select an option from a dropdown\",\n inputSchema: z.object({\n selector: z.string().describe(\"CSS or Playwright selector for the select element\"),\n value: z.string().describe(\"Value or label to select\"),\n }),\n pageCheck: \"locator\",\n dryResult: ({ value }) => ({ selected: value, mode: \"dry-run\" as const }),\n execute: async ({ selector, value }) => {\n const el = page!.locator!(selector);\n if (el?.selectOption) await el.selectOption(value);\n return { selected: value, from: selector };\n },\n }),\n\n // === Navigation & Waiting ===\n wait: makePageTool(ctx, {\n name: \"wait\",\n description: \"Wait for a duration in milliseconds\",\n inputSchema: z.object({ ms: z.number().min(100).max(30000).describe(\"Milliseconds to wait\") }),\n mutates: false,\n dryResult: ({ ms }) => ({ waited: ms, mode: \"dry-run\" as const }),\n execute: async ({ ms }) => {\n if (page?.waitForTimeout) {\n await page.waitForTimeout(ms);\n } else {\n await new Promise((resolve) => setTimeout(resolve, ms));\n }\n return { waited: ms };\n },\n }),\n\n wait_for_selector: makePageTool(ctx, {\n name: \"wait_for_selector\",\n description: \"Wait for an element to reach a state (visible, hidden, attached, detached)\",\n inputSchema: z.object({\n selector: z.string().describe(\"CSS or Playwright selector\"),\n state: z.enum([\"visible\", \"hidden\", \"attached\", \"detached\"]).optional().describe(\"State to wait for\"),\n }),\n mutates: false,\n pageCheck: \"locator\",\n dryResult: ({ selector }) => ({ found: selector, mode: \"dry-run\" as const }),\n execute: async ({ selector, state }) => {\n const el = page!.locator!(selector);\n if (el?.waitFor) await el.waitFor({ state: state ?? \"visible\" });\n return { found: selector, state: state ?? \"visible\" };\n },\n }),\n\n navigate: makePageTool(ctx, {\n name: \"navigate\",\n description: \"Navigate to a URL\",\n inputSchema: z.object({ url: z.string().describe(\"URL to navigate to\") }),\n pageCheck: \"goto\",\n dryResult: ({ url }) => ({ navigated: url, mode: \"dry-run\" as const }),\n execute: async ({ url }) => {\n await page!.goto!(url);\n return { navigated: url };\n },\n }),\n\n go_back: makePageTool(ctx, {\n name: \"go_back\",\n description: \"Go back in browser history\",\n inputSchema: z.object({}),\n pageCheck: \"goBack\",\n dryResult: () => ({ action: \"back\", mode: \"dry-run\" as const }),\n execute: async () => {\n await page!.goBack!();\n return { action: \"back\" };\n },\n }),\n\n reload: makePageTool(ctx, {\n name: \"reload\",\n description: \"Reload the current page\",\n inputSchema: z.object({}),\n pageCheck: \"reload\",\n dryResult: () => ({ action: \"reload\", mode: \"dry-run\" as const }),\n execute: async () => {\n await page!.reload!();\n return { action: \"reload\" };\n },\n }),\n\n // === Inspection Tools ===\n snapshot: tool({\n description: \"Get the accessibility tree of the current page to understand its structure. ALWAYS call this first!\",\n inputSchema: z.object({}),\n execute: async () => {\n params.actionsRun.push(\"snapshot\");\n if (config.dryRun) return { tree: null, mode: \"dry-run\" };\n if (!page) return { error: \"Page unavailable\" };\n\n let tree: unknown;\n let html: string | undefined;\n\n try {\n if (page.accessibility && typeof page.accessibility.snapshot === \"function\") {\n tree = await page.accessibility.snapshot();\n }\n } catch (err) {\n if (params.debug) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] accessibility snapshot error: ${errMsg(err)}`);\n }\n }\n\n if (!tree && page.evaluate) {\n try {\n html = (await page.evaluate(\"document.body.innerHTML\")) as string;\n } catch (err) {\n if (params.debug) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] HTML fallback error: ${errMsg(err)}`);\n }\n }\n }\n\n if (!tree && !html) return { error: \"Could not get page content\" };\n\n const sanitizedTree = sanitizeUnknown(tree, maxPayloadBytes);\n const sanitizedHtml = html ? truncate(sanitizeString(html), maxPayloadBytes) : undefined;\n return { tree: sanitizedTree, html: sanitizedHtml };\n },\n }),\n\n screenshot: tool({\n description: \"Capture a screenshot of the current page\",\n inputSchema: z.object({\n fullPage: z.boolean().optional().describe(\"Whether to capture the full page\"),\n }),\n execute: async ({ fullPage }) => {\n params.actionsRun.push(\"screenshot\");\n if (config.dryRun) return { screenshot: undefined, mode: \"dry-run\" };\n if (!page?.screenshot) return { error: \"Page unavailable\" };\n try {\n const buffer = await page.screenshot({ fullPage: fullPage ?? false });\n const b = typeof buffer === \"string\" ? Buffer.from(buffer) : Buffer.from(buffer);\n const base64 = b.toString(\"base64\");\n if (b.byteLength > maxPayloadBytes || base64.length > maxPayloadBytes) {\n return { error: \"screenshot_too_large\" };\n }\n return { screenshot: `data:image/png;base64,${truncate(base64, maxPayloadBytes)}` };\n } catch (err) {\n return { error: errMsg(err) };\n }\n },\n }),\n\n get_text: makePageTool(ctx, {\n name: \"get_text\",\n description: \"Get the text content of an element\",\n inputSchema: z.object({ selector: z.string().describe(\"CSS or Playwright selector\") }),\n mutates: false,\n pageCheck: \"locator\",\n dryResult: () => ({ text: null, mode: \"dry-run\" as const }),\n execute: async ({ selector }) => {\n const el = page!.locator!(selector);\n const text = el?.textContent ? await el.textContent() : null;\n const sanitized = text ? truncate(sanitizeString(text), maxPayloadBytes) : text;\n return { text: sanitized };\n },\n }),\n\n is_visible: makePageTool(ctx, {\n name: \"is_visible\",\n description: \"Check if an element is visible\",\n inputSchema: z.object({ selector: z.string().describe(\"CSS or Playwright selector\") }),\n mutates: false,\n pageCheck: \"locator\",\n dryResult: () => ({ visible: false, mode: \"dry-run\" as const }),\n execute: async ({ selector }) => {\n const el = page!.locator!(selector);\n const visible = el?.isVisible ? await el.isVisible() : false;\n return { visible };\n },\n }),\n\n evaluate: tool({\n description: \"Run JavaScript in the page context\",\n inputSchema: z.object({ script: z.string().describe(\"JavaScript code to execute\") }),\n execute: async ({ script }) => {\n params.actionsRun.push(\"evaluate\");\n if (!config.allowEvaluate) return { error: \"evaluate_disabled\" };\n if (violatesCodeDenylist(script)) return { error: \"evaluate_blocked\" };\n if (config.dryRun) return { result: null, mode: \"dry-run\" };\n if (!page?.evaluate) return { error: \"Page unavailable\" };\n try {\n const scriptSafe = truncate(script, maxPayloadBytes);\n const result = await page.evaluate(scriptSafe);\n return { result: sanitizeUnknown(result, maxPayloadBytes) };\n } catch (err) {\n return { error: errMsg(err) };\n }\n },\n }),\n\n run_playwright_code: tool({\n description:\n 'Execute arbitrary Playwright code. Has access to `page` object. Use for complex operations not covered by other tools. Example: `await page.getByRole(\"button\", { name: \"Submit\" }).click();`',\n inputSchema: z.object({\n code: z.string().describe(\"Playwright code to execute (has access to `page` object)\"),\n }),\n execute: async ({ code }) => {\n params.actionsRun.push(\"run_playwright_code\");\n if (!config.allowRunCode) return { executed: false, error: \"run_code_disabled\" };\n if (violatesCodeDenylist(code)) return { executed: false, error: \"run_code_blocked\" };\n if (config.dryRun) return { executed: false, mode: \"dry-run\" };\n if (!page) return { executed: false, error: \"Page unavailable\" };\n try {\n const boundedCode = truncate(code, maxPayloadBytes);\n const fn = new Function(\"page\", `return (async () => { ${boundedCode} })();`);\n const result = await fn(page);\n return { executed: true, result: sanitizeUnknown(result, maxPayloadBytes) };\n } catch (err) {\n return { executed: false, error: errMsg(err) };\n }\n },\n }),\n\n // === Completion Tool ===\n mark_complete: tool({\n description:\n \"Call this when the browser is ready for the test to continue. You MUST call this when done healing!\",\n inputSchema: z.object({\n reason: z.string().describe(\"Brief technical description of what was done\"),\n summary: z.string().describe('One-line human-readable summary for the test report, e.g. \"Clicked \\'Send OTP\\' instead of \\'Submit\\'\" or \"Filled email field by accessible name\"'),\n }),\n execute: async ({ reason, summary }) => {\n params.actionsRun.push(\"mark_complete\");\n if (!config.dryRun) {\n complete = true;\n completionReason = reason;\n completionSummary = summary;\n }\n return { complete: !config.dryRun, reason, summary, mode: config.dryRun ? \"dry-run\" : undefined };\n },\n }),\n };\n\n return {\n tools,\n isComplete: () => complete,\n getCompletionReason: () => completionReason,\n getSummary: () => completionSummary,\n };\n}\n\n// === Tool Step Logging ===\n\ntype ToolStep = {\n finishReason?: string;\n toolCalls?: Array<{ toolName?: string; args?: unknown }>;\n toolResults?: Array<{ toolName?: string; result?: unknown; error?: unknown }>;\n};\n\nexport function logToolStep(step: ToolStep): void {\n const color = {\n magenta: \"\\x1b[35m\",\n cyan: \"\\x1b[36m\",\n yellow: \"\\x1b[33m\",\n green: \"\\x1b[32m\",\n reset: \"\\x1b[0m\",\n } as const;\n\n const calls = step.toolCalls ?? [];\n const results = step.toolResults ?? [];\n\n if (calls.length === 0 && results.length === 0) {\n // eslint-disable-next-line no-console\n console.log(\n `${color.magenta}[canary][tool] step${color.reset} (no tool calls/results; finish=${step.finishReason ?? \"unknown\"})`\n );\n }\n\n for (const call of calls) {\n const name = call.toolName ?? \"unknown_tool\";\n const args = call.args ? truncate(safeJson(call.args), 300) : \"\";\n // eslint-disable-next-line no-console\n console.log(\n `${color.magenta}[canary][tool] call${color.reset} ${color.cyan}${name}${color.reset}${args ? ` args=${args}` : \"\"}`\n );\n }\n\n for (const result of results) {\n const name = result.toolName ?? \"unknown_tool\";\n const res = result.result ? truncate(safeJson(result.result), 300) : undefined;\n const err = result.error ? truncate(safeJson(result.error), 300) : undefined;\n const statusColor = err ? color.yellow : color.green;\n // eslint-disable-next-line no-console\n console.log(\n `${color.magenta}[canary][tool] result${color.reset} ${color.cyan}${name}${color.reset}` +\n (res ? ` ${statusColor}->${color.reset} ${res}` : \"\") +\n (err ? ` ${color.yellow}error=${err}${color.reset}` : \"\")\n );\n }\n}\n","/**\n * System prompt and message construction for the AI test healer.\n */\n\nimport type { loadCanaryConfig } from \"./config\";\nimport type { FailureContext, TestContext } from \"./healer\";\nimport { sanitizeString, sanitizeUnknown } from \"./healer\";\n\ntype PageLike = {\n accessibility?: { snapshot: () => Promise<unknown> };\n screenshot?: (options?: { fullPage?: boolean }) => Promise<Buffer | Uint8Array | string>;\n evaluate?: (script: string) => Promise<unknown>;\n};\n\ntype ToolConfig = ReturnType<typeof loadCanaryConfig>;\n\nfunction resolveMode(config: ToolConfig): \"dry-run\" | \"warn\" | \"full\" {\n if (config.dryRun) return \"dry-run\";\n if (config.warnOnly) return \"warn\";\n return \"full\";\n}\n\nfunction truncate(value: string, max: number): string {\n if (value.length <= max) return value;\n return value.slice(0, max) + \"...\";\n}\n\nfunction safeJson(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\nexport function buildSystemPrompt(\n testContext: TestContext,\n config: ToolConfig\n): string {\n const testInfo = testContext.testFile\n ? `\\n## Test Context\\nFile: ${testContext.testFile}\\nTest: \"${testContext.testTitle ?? \"unknown\"}\"\\n`\n : \"\";\n\n const testSource = testContext.testSource\n ? `\\n### Full Test Code:\\n\\`\\`\\`typescript\\n${testContext.testSource}\\n\\`\\`\\`\\n`\n : \"\";\n\n const expectedAfter = testContext.expectedAfter\n ? `\\n### What the Test Expects After This Step:\\n${testContext.expectedAfter}\\n`\n : \"\";\n\n return `You are an AI test healer. A Playwright test step failed. Your job is to make the browser ready for the next step in the test **without violating the intent of the test**. Heal only the failing step; do not advance beyond it.\n\n## Your Goal\nGet the browser into the state the test expects, then call mark_complete.\n${testInfo}${testSource}\n### Current Step That Failed:\n${testContext.currentStep}\n${expectedAfter}\n## The Failure\nAction attempted: ${testContext.action}\nTarget: ${testContext.target ?? \"unknown\"}\nError: ${testContext.errorMessage ?? \"unknown\"}\n\n## Test Intent and Discipline\n- Adhere to the spirit of the test. If advancing would bypass a required validation or skip a missing field, you must not proceed—report failure instead.\n- Heal only this step. Avoid extra navigation or state changes unrelated to the intended action. If you take a detour, undo it before completion.\n- If you cannot confidently achieve the expected postcondition for this step, do not mark complete; let the test fail.\n\n## Tools Available\n\n### Inspection (use first!)\n- snapshot() - Get accessibility tree to see page structure\n- screenshot() - Capture visual screenshot\n- get_text(selector) - Get text content of an element\n- is_visible(selector) - Check if element is visible\n\n### Actions\n- click(text) - Click element by visible text\n- click_selector(selector) - Click by CSS/Playwright selector\n- fill_by_name(name, value) - PREFERRED: Fill textbox by accessible name from snapshot (e.g. textbox \"Enter 6-digit code\")\n- fill(selector, value) - Fill input by CSS selector (use placeholder attribute, NOT aria-label)\n- type(selector, text) - Type text character by character\n- press_key(key) - Press keyboard key (Enter, Tab, etc.)\n- hover(selector) - Hover over element\n- select_option(selector, value) - Select from dropdown\n\n### Navigation & Waiting\n- wait(ms) - Wait for a duration\n- wait_for_selector(selector, state?) - Wait for element state\n- navigate(url) - Navigate to URL\n- go_back() - Go back in history\n- reload() - Reload page\n\n### Power Tool\n- run_playwright_code(code) - Execute any Playwright code with \\`page\\` object\n Example: \\`await page.getByRole('button', { name: 'Submit' }).click();\\`\n\n### Completion (REQUIRED)\n- mark_complete(reason, summary) - Call when browser is ready for test to continue\n - reason: technical description of what was done\n - summary: one-line human-readable summary for test report (e.g. \"Clicked 'Send OTP' instead of 'Submit'\")\n\n## CRITICAL RULES - READ CAREFULLY\n\n### Rule 1: YOU MUST PERFORM THE ACTION YOURSELF\nThe test tried to ${testContext.action}(\"${testContext.target ?? \"\"}\") but it failed.\nYOU must perform the equivalent action on the correct element.\nDO NOT leave it for \"the next step\" - there is no next step until YOU complete the action.\n\n### Rule 2: The workflow is ALWAYS:\n1. snapshot() - understand the page\n2. click() / fill() / type() - PERFORM THE ACTION on the correct element\n3. mark_complete() - report what you did\n\n### Rule 3: NEVER call mark_complete without performing an action\nIf mark_complete is called without a click/fill/type, the test WILL FAIL.\n\n## Example 1: Test tried click(\"Submit\") but button says \"Send OTP\"\nCORRECT:\n1. snapshot() → see \"Send OTP\" button exists\n2. click(\"Send OTP\") → CLICK THE BUTTON\n3. mark_complete({reason: \"clicked Send OTP button\", summary: \"Clicked 'Send OTP' instead of 'Submit'\"})\n\nWRONG (this will cause test failure):\n1. snapshot() → see \"Send OTP\" button exists\n2. mark_complete(...) ← WRONG! No click performed!\n\n## Example 2: Test tried fill with wrong placeholder\nIf snapshot shows: textbox \"Enter 6-digit code\" [ref=e37]\nCORRECT:\n1. snapshot() → see textbox \"Enter 6-digit code\"\n2. fill_by_name(\"Enter 6-digit code\", \"111222\") → USE THE NAME FROM SNAPSHOT\n3. mark_complete({reason: \"filled by accessible name\", summary: \"Filled 'Enter 6-digit code' field\"})\n\n## If healing would break test intent\n- If the page is missing required UI (e.g., a field not present) or advancing would skip validation, do NOT hack around it. Report failure by not calling mark_complete.\n- If you temporarily navigate or change state, undo it before completion so the test can continue from the intended point.\n\n## Start now\n1. Call snapshot()\n2. Identify the correct element for the intended action\n3. PERFORM THE ACTION (click, fill, type, etc.)\n4. Call mark_complete with what you did\n\nMode: ${resolveMode(config)}.`;\n}\n\nexport function buildInitialMessages(\n testContext: TestContext,\n failure: FailureContext,\n initial: { snapshot?: unknown; html?: string; screenshot?: string }\n): Array<{ role: \"user\"; content: string }> {\n const parts: string[] = [];\n parts.push(\n `Please heal this test step. Start by reviewing the snapshot below. Failing step: ${failure.action ?? \"unknown\"}(${failure.target ?? \"unknown\"}). Error: ${failure.errorMessage ?? \"unknown\"}.`\n );\n\n if (testContext.testSource) {\n parts.push(\n `Full test source (truncated/redacted):\\n\\`\\`\\`typescript\\n${truncate(testContext.testSource, 15000)}\\n\\`\\`\\``\n );\n }\n\n parts.push(`Test file: ${testContext.testFile ?? \"unknown\"}`);\n parts.push(`Test title: ${testContext.testTitle ?? \"unknown\"}`);\n\n if (initial.snapshot) {\n parts.push(`Initial accessibility snapshot (sanitized):\\n\\`\\`\\`json\\n${truncate(safeJson(initial.snapshot), 8000)}\\n\\`\\`\\``);\n }\n if (initial.html) {\n parts.push(`HTML fallback (sanitized):\\n\\`\\`\\`html\\n${truncate(initial.html, 4000)}\\n\\`\\`\\``);\n }\n if (initial.screenshot) {\n parts.push(`Screenshot (base64 data URL, truncated): ${truncate(initial.screenshot, 12000)}`);\n }\n\n parts.push(`Remember: perform only the intended step and mark_complete only when the postcondition for this step is truly met.`);\n\n return [{ role: \"user\", content: parts.join(\"\\n\\n\") }];\n}\n\nexport async function buildInitialPageContext(\n page: PageLike | undefined,\n config: ToolConfig\n): Promise<{ snapshot?: unknown; html?: string; screenshot?: string }> {\n const maxPayloadBytes = Math.max(1000, config.maxPayloadBytes || 60000);\n const result: { snapshot?: unknown; html?: string; screenshot?: string } = {};\n if (!page) return result;\n\n try {\n if (page.accessibility?.snapshot) {\n const tree = await page.accessibility.snapshot();\n if (tree) {\n result.snapshot = sanitizeUnknown(tree, maxPayloadBytes);\n }\n }\n } catch {\n // ignore\n }\n\n if (!result.snapshot && page.evaluate) {\n try {\n const html = (await page.evaluate(\"document.body.innerHTML\")) as string;\n if (html) {\n result.html = truncate(sanitizeString(html), maxPayloadBytes);\n }\n } catch {\n // ignore\n }\n }\n\n if (config.visionEnabled && page.screenshot) {\n try {\n const buffer = await page.screenshot({ fullPage: false });\n const b = typeof buffer === \"string\" ? Buffer.from(buffer) : Buffer.from(buffer);\n const base64 = b.toString(\"base64\");\n if (b.byteLength <= maxPayloadBytes && base64.length <= maxPayloadBytes) {\n result.screenshot = `data:image/png;base64,${truncate(base64, maxPayloadBytes)}`;\n }\n } catch {\n // ignore\n }\n }\n\n return result;\n}\n"],"mappings":"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","names":["path","path","fs","path","path","fs","fs","truncate","safeJson","resolveMode","truncate","fs"]}
|
|
1
|
+
{"version":3,"sources":["../src/runner/config.ts","../src/runner/env.ts","../src/runner/state.ts","../src/runner/healing-helpers.ts","../src/runner/healer.ts","../src/runner/ai-client.ts","../src/runner/healer-tools.ts","../src/runner/healer-prompt.ts"],"sourcesContent":["import path from \"node:path\";\nimport { loadCanaryEnv } from \"./env\";\n\nexport type CanaryConfig = {\n enabled: boolean;\n allowedPlaywrightVersion?: string;\n aiProvider?: string;\n aiModel?: string;\n apiKey?: string;\n healTimeoutMs: number;\n maxActions: number;\n dryRun: boolean;\n warnOnly: boolean;\n visionEnabled: boolean;\n debug: boolean;\n eventLogPath: string;\n eventLoggingEnabled: boolean;\n readOnly: boolean;\n allowRunCode: boolean;\n allowEvaluate: boolean;\n maxPayloadBytes: number;\n};\n\nexport function loadCanaryConfig(): CanaryConfig {\n loadCanaryEnv();\n\n const eventLogPath = (() => {\n const userPath = process.env.CANARY_EVENT_LOG;\n if (userPath) return userPath;\n const worker = process.env.TEST_WORKER_INDEX ?? \"runner\";\n return path.join(process.cwd(), \"test-results\", \"ai-healer\", `events-worker-${worker}.jsonl`);\n })();\n\n const baseConfig: CanaryConfig = {\n enabled: true,\n allowedPlaywrightVersion: process.env.CANARY_PLAYWRIGHT_VERSION,\n aiProvider: process.env.AI_PROVIDER,\n aiModel: process.env.AI_MODEL,\n apiKey: process.env.AI_API_KEY,\n healTimeoutMs: Number(process.env.AI_TIMEOUT_MS ?? 120000), // 2 minutes for agentic healing\n maxActions: Number(process.env.CANARY_MAX_ACTIONS ?? 50), // Generous step limit for agentic healing\n dryRun: process.env.CANARY_DRY_RUN === \"1\",\n warnOnly: process.env.CANARY_WARN_ONLY === \"1\",\n visionEnabled: process.env.CANARY_VISION === \"1\" || process.env.AI_VISION === \"1\",\n debug: process.env.CANARY_DEBUG === \"1\",\n readOnly: process.env.CANARY_READ_ONLY === \"1\",\n allowRunCode: process.env.CANARY_ALLOW_RUN_CODE === \"1\",\n allowEvaluate: process.env.CANARY_ALLOW_EVALUATE !== \"0\",\n maxPayloadBytes: Number(process.env.CANARY_MAX_PAYLOAD_BYTES ?? 60000), // cap snapshots/screenshots/text\n eventLogPath,\n eventLoggingEnabled: process.env.CANARY_EVENT_LOG !== \"0\",\n };\n\n const disabled =\n process.env.CANARY_ENABLED === \"0\" ||\n process.env.CANARY_DISABLED === \"1\" ||\n process.env.AI_HEALING === \"0\";\n\n return {\n ...baseConfig,\n enabled: !disabled && baseConfig.enabled,\n };\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\n\nlet loaded = false;\n\nexport function loadCanaryEnv(): void {\n if (loaded) return;\n loaded = true;\n\n const explicitPath = process.env.CANARY_ENV_FILE;\n const defaultPath = path.join(process.cwd(), \".env\");\n const envPath = explicitPath || defaultPath;\n\n if (fs.existsSync(envPath)) {\n loadFile(envPath);\n }\n}\n\nfunction loadFile(filePath: string): void {\n try {\n const raw = fs.readFileSync(filePath, \"utf-8\");\n for (const line of raw.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n const idx = trimmed.indexOf(\"=\");\n if (idx === -1) continue;\n const key = trimmed.slice(0, idx).trim();\n const value = trimmed.slice(idx + 1).trim();\n if (!key) continue;\n if (process.env[key] === undefined) {\n process.env[key] = stripQuotes(value);\n }\n }\n } catch {\n // ignore\n }\n}\n\nfunction stripQuotes(value: string): string {\n if ((value.startsWith(\"\\\"\") && value.endsWith(\"\\\"\")) || (value.startsWith(\"'\") && value.endsWith(\"'\"))) {\n return value.slice(1, -1);\n }\n return value;\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { loadCanaryConfig } from \"./config\";\nimport { loadCanaryEnv } from \"./env\";\n\nexport type HealingEvent = {\n kind: \"locator\" | \"page\" | \"expect\" | \"unknown\";\n action?: string;\n target?: string;\n errorMessage?: string;\n healed: boolean;\n workerId?: string;\n timestamp?: string;\n decision?: string;\n reason?: string;\n strategy?: string;\n actions?: Array<{ type: string; detail?: string }>;\n durationMs?: number;\n mode?: \"dry-run\" | \"warn\" | \"full\";\n modelId?: string;\n // Report fields\n summary?: string; // Human-readable one-line healing summary from agent\n testFile?: string; // Test file path for correlation\n testTitle?: string; // Test title for correlation\n};\n\ndeclare global {\n // eslint-disable-next-line no-var\n var __CANARY_EVENTS: HealingEvent[] | undefined;\n // eslint-disable-next-line no-var\n var __CANARY_PATCHED: boolean | undefined;\n // eslint-disable-next-line no-var\n var __CANARY_EVENT_LOG_PATH: string | undefined;\n}\n\nexport function getEventLog(): HealingEvent[] {\n if (!globalThis.__CANARY_EVENTS) {\n globalThis.__CANARY_EVENTS = [];\n }\n return globalThis.__CANARY_EVENTS;\n}\n\nexport function setEventLogPath(path: string): void {\n globalThis.__CANARY_EVENT_LOG_PATH = path;\n}\n\nexport function getEventLogPath(): string | undefined {\n return globalThis.__CANARY_EVENT_LOG_PATH;\n}\n\nexport function recordHealingEvent(event: HealingEvent): void {\n loadCanaryEnv();\n const log = getEventLog();\n const entry: HealingEvent = {\n ...event,\n workerId: process.env.TEST_WORKER_INDEX,\n timestamp: new Date().toISOString(),\n };\n log.push(entry);\n appendEvent(entry);\n}\n\nexport function markPatched(): void {\n globalThis.__CANARY_PATCHED = true;\n}\n\nexport function alreadyPatched(): boolean {\n return globalThis.__CANARY_PATCHED === true;\n}\n\nfunction appendEvent(event: HealingEvent): void {\n const logPath = getEventLogPath();\n if (!logPath) return;\n const config = loadCanaryConfig();\n if (!config.eventLoggingEnabled) return;\n\n try {\n fs.mkdirSync(path.dirname(logPath), { recursive: true });\n fs.appendFileSync(logPath, JSON.stringify(event) + \"\\n\");\n } catch {\n // Swallow logging errors to avoid impacting tests.\n }\n}\n","/**\n * Shared helpers for Playwright healing instrumentation.\n *\n * Used by both `instrumentation.ts` (Module._load hook approach)\n * and `wrapper.ts` (direct wrap approach).\n */\n\nimport { createRequire } from \"node:module\";\nimport fs from \"node:fs\";\nimport { classifyFailure, executeHealActions, type FailureContext } from \"./healer\";\nimport { recordHealingEvent } from \"./state\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst requireFn: any = typeof require !== \"undefined\" ? require : createRequire(import.meta.url);\n\n// === Action / Factory Constants ===\n\nexport const LOCATOR_ACTIONS = new Set([\n \"click\",\n \"dblclick\",\n \"fill\",\n \"check\",\n \"uncheck\",\n \"hover\",\n \"press\",\n \"type\",\n \"selectOption\",\n \"tap\",\n]);\n\nexport const PAGE_ACTIONS = new Set([\n \"goto\",\n \"click\",\n \"dblclick\",\n \"fill\",\n \"check\",\n \"uncheck\",\n \"hover\",\n \"press\",\n \"type\",\n \"selectOption\",\n \"tap\",\n \"waitForSelector\",\n]);\n\nexport const LOCATOR_FACTORIES = new Set([\n \"locator\",\n \"getByRole\",\n \"getByText\",\n \"getByLabel\",\n \"getByPlaceholder\",\n \"getByAltText\",\n \"getByTitle\",\n \"getByTestId\",\n \"frameLocator\",\n]);\n\nexport const LOCATOR_CHAIN_METHODS = new Set([\n \"locator\",\n \"first\",\n \"last\",\n \"nth\",\n \"filter\",\n \"getByRole\",\n \"getByText\",\n \"getByLabel\",\n \"getByPlaceholder\",\n \"getByAltText\",\n \"getByTitle\",\n \"getByTestId\",\n]);\n\n// === Types ===\n\nexport type PlaywrightExports = {\n test: { extend: typeof import(\"@playwright/test\").test.extend };\n expect: typeof import(\"@playwright/test\").expect;\n [key: string]: unknown;\n};\n\nexport type WrapOptions = {\n debug?: boolean;\n};\n\ntype AttemptContext = {\n kind: FailureContext[\"kind\"];\n action: string;\n target?: string;\n locator?: unknown;\n page?: unknown;\n invoke: () => unknown;\n debug?: boolean;\n};\n\ntype MatcherContext = {\n matcher: (...args: unknown[]) => unknown;\n matcherName: string;\n args: unknown[];\n expectTarget: unknown;\n mode: \"hard\" | \"soft\";\n debug?: boolean;\n};\n\n// === Proxy Wrappers ===\n\nexport function wrapExpect(expectImpl: PlaywrightExports[\"expect\"], options: WrapOptions): PlaywrightExports[\"expect\"] {\n const wrapMatchers = (expectation: unknown, mode: \"hard\" | \"soft\") => {\n return new Proxy(expectation ?? {}, {\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (typeof prop === \"string\" && typeof value === \"function\") {\n return (...args: unknown[]) =>\n runMatcherWithHealing({\n matcher: value as (...args: unknown[]) => unknown,\n matcherName: prop,\n args,\n expectTarget: target,\n mode,\n debug: options.debug,\n });\n }\n return value;\n },\n });\n };\n\n const proxy = new Proxy(expectImpl, {\n apply(target, thisArg, argArray) {\n const expectation = (target as (...args: unknown[]) => unknown).apply(thisArg, argArray as unknown[]);\n return wrapMatchers(expectation, \"hard\");\n },\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (prop === \"soft\" && typeof value === \"function\") {\n return (...args: unknown[]) => wrapMatchers((value as (...args: unknown[]) => unknown).apply(target, args), \"soft\");\n }\n return value;\n },\n });\n\n return proxy as PlaywrightExports[\"expect\"];\n}\n\nexport function wrapPage<PageType extends object>(page: PageType, options: WrapOptions): PageType {\n return new Proxy(page, {\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n\n if (typeof prop === \"string\") {\n if (LOCATOR_FACTORIES.has(prop) && typeof value === \"function\") {\n return (...args: unknown[]) => {\n const locator = (value as (...args: unknown[]) => unknown).apply(target, args);\n return wrapLocator(locator, options, target);\n };\n }\n\n if (PAGE_ACTIONS.has(prop) && typeof value === \"function\") {\n return async (...args: unknown[]) => {\n return attemptWithHealing({\n kind: \"page\",\n action: prop,\n target: safeTargetString(target),\n locator: undefined,\n page: target as unknown,\n invoke: () => (value as (...args: unknown[]) => unknown).apply(target, args),\n debug: options.debug,\n });\n };\n }\n }\n\n return value;\n },\n });\n}\n\nexport function wrapLocator(locator: unknown, options: WrapOptions, page?: unknown): unknown {\n if (!locator || typeof locator !== \"object\") return locator;\n\n return new Proxy(locator, {\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n\n if (typeof prop === \"string\" && LOCATOR_ACTIONS.has(prop) && typeof value === \"function\") {\n return async (...args: unknown[]) => {\n return attemptWithHealing({\n kind: \"locator\",\n action: prop,\n target: safeTargetString(target),\n locator: target as unknown,\n page,\n invoke: () => (value as (...args: unknown[]) => unknown).apply(target, args),\n debug: options.debug,\n });\n };\n }\n\n if (typeof prop === \"string\" && LOCATOR_CHAIN_METHODS.has(prop) && typeof value === \"function\") {\n return (...args: unknown[]) => wrapLocator((value as (...args: unknown[]) => unknown).apply(target, args), options, page);\n }\n\n return value;\n },\n });\n}\n\n// === Healing Logic ===\n\nasync function attemptWithHealing<T>(ctx: AttemptContext): Promise<T> {\n try {\n return (await Promise.resolve(ctx.invoke())) as T;\n } catch (error) {\n const failure = buildFailureContext(ctx.kind, ctx.action, ctx.target, error);\n recordHealingEvent({ ...failure, healed: false });\n const decision = classifyFailure(failure);\n if (ctx.debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[canary][debug] failure intercepted: kind=${ctx.kind} action=${ctx.action} reason=${\n decision.healable ? decision.reason ?? \"healable\" : decision.reason\n }`\n );\n }\n if (!decision.healable) {\n throw error;\n }\n\n const testContext = getTestContext();\n const outcome = await executeHealActions(decision, failure, {\n kind: ctx.kind,\n action: ctx.action,\n target: ctx.target,\n locator: isLocatorLike(ctx.locator) ? ctx.locator : undefined,\n page: isPageLike(ctx.page) ? ctx.page : undefined,\n testContext: {\n testFile: testContext?.testFile,\n testTitle: testContext?.testTitle,\n testSource: loadTestSource(testContext?.testFile),\n },\n });\n\n const summaryBase = {\n ...failure,\n healed: false,\n strategy: 'agentic',\n reason: outcome.reason ?? decision.reason,\n actions: actionsToEventItems(outcome.actionsRun),\n durationMs: outcome.durationMs,\n mode: outcome.mode,\n decision: decision.reason,\n modelId: outcome.modelId,\n summary: outcome.summary,\n testFile: testContext?.testFile,\n testTitle: testContext?.testTitle,\n } as const;\n\n if (outcome.healed) {\n if (ctx.debug) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] healed via AI tool for action=${ctx.action}`);\n }\n recordHealingEvent({ ...summaryBase, healed: true });\n return undefined as T;\n }\n\n if (outcome.shouldRetryOriginal) {\n try {\n const retried = (await Promise.resolve(ctx.invoke())) as T;\n if (ctx.debug) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] retry_original succeeded for action=${ctx.action}`);\n }\n recordHealingEvent({ ...summaryBase, healed: true });\n return retried;\n } catch (retryError) {\n const retryInfo = errorInfo(retryError);\n recordHealingEvent({ ...summaryBase, healed: false, errorMessage: retryInfo.message });\n if (ctx.debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[canary][debug] retry_original failed for action=${ctx.action}: ${retryInfo.message ?? retryError}`\n );\n }\n throw retryError;\n }\n }\n\n recordHealingEvent(summaryBase);\n throw error;\n }\n}\n\nasync function runMatcherWithHealing(params: MatcherContext): Promise<unknown> {\n const { matcher, matcherName, args, expectTarget, mode } = params;\n const invoke = () => matcher.apply(expectTarget, args);\n\n if (mode === \"soft\") {\n return Promise.resolve(invoke());\n }\n\n try {\n return await Promise.resolve(invoke());\n } catch (error) {\n const target = stringifyTarget(args?.[0]);\n const failure = buildFailureContext(\"expect\", matcherName, target, error);\n recordHealingEvent({ ...failure, healed: false });\n const decision = classifyFailure(failure);\n if (!decision.healable) {\n throw error;\n }\n\n const outcome = await executeHealActions(decision, failure, {\n kind: \"expect\",\n action: matcherName,\n target,\n locator: isLocatorLike(args?.[0]) ? args[0] : undefined,\n });\n\n const testContextMatcher = getTestContext();\n const summaryBase = {\n ...failure,\n healed: false,\n strategy: 'agentic',\n reason: outcome.reason ?? decision.reason,\n actions: actionsToEventItems(outcome.actionsRun),\n durationMs: outcome.durationMs,\n mode: outcome.mode,\n decision: decision.reason,\n modelId: outcome.modelId,\n summary: outcome.summary,\n testFile: testContextMatcher?.testFile,\n testTitle: testContextMatcher?.testTitle,\n } as const;\n\n if (outcome.shouldRetryOriginal) {\n try {\n const retried = await Promise.resolve(invoke());\n recordHealingEvent({ ...summaryBase, healed: true });\n return retried;\n } catch (retryError) {\n const retryInfo = errorInfo(retryError);\n recordHealingEvent({ ...summaryBase, healed: false, errorMessage: retryInfo.message });\n throw retryError;\n }\n }\n\n recordHealingEvent(summaryBase);\n throw error;\n }\n}\n\n// === Utility Functions ===\n\nfunction buildFailureContext(\n kind: FailureContext[\"kind\"],\n action: string,\n target: string | undefined,\n error: unknown\n): FailureContext {\n const info = errorInfo(error);\n return {\n kind,\n action,\n target,\n errorMessage: info.message,\n errorName: info.name,\n stack: info.stack,\n };\n}\n\nfunction actionsToEventItems(actions: string[]): Array<{ type: string; detail?: string }> {\n return actions.map((action) => ({ type: action }));\n}\n\nfunction errorInfo(error: unknown): { message?: string; name?: string; stack?: string } {\n if (error instanceof Error) {\n return { message: error.message, name: error.name, stack: error.stack };\n }\n return { message: typeof error === \"string\" ? error : JSON.stringify(error) };\n}\n\nfunction isLocatorLike(candidate: unknown): candidate is { scrollIntoViewIfNeeded: () => Promise<unknown> } {\n return Boolean(candidate && typeof candidate === \"object\" && \"scrollIntoViewIfNeeded\" in (candidate as Record<string, unknown>));\n}\n\nfunction isPageLike(candidate: unknown): candidate is { waitForTimeout: (ms: number) => Promise<unknown> } {\n return Boolean(candidate && typeof candidate === \"object\" && \"waitForTimeout\" in (candidate as Record<string, unknown>));\n}\n\nfunction safeTargetString(target: unknown): string | undefined {\n try {\n if (typeof target === \"object\" && target !== null && \"toString\" in target) {\n const s = String((target as { toString: () => string }).toString());\n if (s && s !== \"[object Object]\") return s;\n }\n } catch {\n // ignore\n }\n return undefined;\n}\n\nfunction stringifyTarget(candidate: unknown): string | undefined {\n if (!candidate) return undefined;\n if (typeof candidate === \"string\") return candidate;\n if (typeof candidate === \"object\") {\n if (\n \"selector\" in (candidate as Record<string, unknown>) &&\n typeof (candidate as Record<string, unknown>).selector === \"string\"\n ) {\n return String((candidate as Record<string, unknown>).selector);\n }\n }\n return safeTargetString(candidate);\n}\n\nfunction getTestContext(): { testFile?: string; testTitle?: string } | undefined {\n try {\n const playwright = requireFn(\"@playwright/test\") as {\n test?: { info?: () => { file?: string; title?: string } };\n };\n if (playwright?.test?.info) {\n const info = playwright.test.info();\n if (info) {\n return { testFile: info.file, testTitle: info.title };\n }\n }\n } catch {\n // Not in a Playwright test context\n }\n return undefined;\n}\n\nfunction loadTestSource(filePath?: string): string | undefined {\n if (!filePath) return undefined;\n try {\n return fs.readFileSync(filePath, \"utf-8\");\n } catch {\n return undefined;\n }\n}\n","/**\n * AI Test Healer — Core types, failure classification, and execution entry point.\n *\n * Tool definitions are in `healer-tools.ts`.\n * Prompt construction is in `healer-prompt.ts`.\n */\n\nimport { stepCountIs, streamText } from \"ai\";\nimport { loadCanaryConfig } from \"./config\";\nimport { resolveHealerModel } from \"./ai-client\";\nimport { createAgenticTools, logToolStep } from \"./healer-tools\";\nimport { buildSystemPrompt, buildInitialMessages, buildInitialPageContext } from \"./healer-prompt\";\n\n// === Redaction / Security ===\n\nconst REDACTION_PATTERNS: Array<{ regex: RegExp; replacement: string }> = [\n { regex: /bearer\\s+[a-z0-9._-]+/gi, replacement: \"[REDACTED_BEARER]\" },\n { regex: /api[_-]?key[:\\s\"']+[a-z0-9._-]+/gi, replacement: \"[REDACTED_API_KEY]\" },\n { regex: /secret[:\\s\"']+[a-z0-9._-]+/gi, replacement: \"[REDACTED_SECRET]\" },\n { regex: /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}/g, replacement: \"[REDACTED_EMAIL]\" },\n { regex: /[0-9]{12,}/g, replacement: \"[REDACTED_NUMBER]\" },\n];\nconst CODE_DENY_PATTERNS = [/process\\.env/i, /child_process/i, /\\brequire\\s*\\(/i, /\\bimport\\s*\\(/i];\n\n// === Types ===\n\nexport type FailureContext = {\n kind: \"locator\" | \"page\" | \"expect\" | \"unknown\";\n action?: string;\n target?: string;\n errorMessage?: string;\n errorName?: string;\n stack?: string;\n};\n\nexport type TestContext = {\n testFile?: string;\n testTitle?: string;\n testSource?: string;\n currentStep: string;\n expectedAfter?: string;\n action: string;\n target?: string;\n errorMessage?: string;\n};\n\nexport type HealDecision =\n | { healable: false; reason: string }\n | { healable: true; reason?: string };\n\nexport type HealExecutionContext = {\n kind: FailureContext[\"kind\"];\n target?: string;\n action?: string;\n locator?: { scrollIntoViewIfNeeded?: (options?: { timeout?: number }) => Promise<unknown> };\n page?: unknown;\n testContext?: Partial<TestContext>;\n};\n\nexport type HealOutcome = {\n healed: boolean;\n shouldRetryOriginal: boolean;\n actionsRun: string[];\n timedOut: boolean;\n durationMs: number;\n errors: string[];\n mode: \"dry-run\" | \"warn\" | \"full\";\n reason?: string;\n modelId?: string;\n summary?: string;\n};\n\n// === Failure Classification ===\n\nexport function classifyFailure(context: FailureContext): HealDecision {\n const message = (context.errorMessage ?? \"\").toLowerCase();\n\n if (\n message.includes(\"closed\") ||\n message.includes(\"target page, context or browser has been closed\")\n ) {\n return { healable: false, reason: \"context_closed\" };\n }\n\n if (message.includes(\"navigation failed\") && message.includes(\"net::\")) {\n return { healable: false, reason: \"navigation_failed\" };\n }\n\n return { healable: true, reason: \"agent_healing\" };\n}\n\n// === Main Entry Point ===\n\nexport async function executeHealActions(\n decision: HealDecision,\n failure: FailureContext,\n execCtx: HealExecutionContext\n): Promise<HealOutcome> {\n const config = loadCanaryConfig();\n const started = Date.now();\n const mode = resolveMode(config);\n const actionsRun: string[] = [];\n const errors: string[] = [];\n const logTools = config.debug || process.env.CANARY_TOOL_LOG === \"1\";\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const initialContext = await buildInitialPageContext(execCtx.page as any, config);\n\n if (!decision.healable) {\n return baseOutcome({ mode, started, healed: false, shouldRetryOriginal: false, reason: decision.reason, actionsRun, errors });\n }\n\n const { model, modelId, reason: modelReason } = resolveHealerModel(config);\n if (!model) {\n return baseOutcome({ mode, started, healed: false, shouldRetryOriginal: false, reason: modelReason ?? \"no_model\", actionsRun, errors });\n }\n\n const testContext: TestContext = {\n testFile: execCtx.testContext?.testFile,\n testTitle: execCtx.testContext?.testTitle,\n testSource: execCtx.testContext?.testSource ? sanitizeString(execCtx.testContext.testSource) : undefined,\n currentStep: `${failure.action ?? \"unknown\"}(${failure.target ?? \"unknown\"})`,\n expectedAfter: execCtx.testContext?.expectedAfter,\n action: failure.action ?? \"unknown\",\n target: failure.target,\n errorMessage: failure.errorMessage,\n };\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const toolset = createAgenticTools({ page: execCtx.page as any, config, actionsRun, debug: config.debug });\n let completionReason: string | undefined;\n\n try {\n const abort = AbortSignal.timeout(config.healTimeoutMs);\n const result = await streamText({\n model,\n system: buildSystemPrompt(testContext, config),\n messages: buildInitialMessages(testContext, failure, initialContext),\n tools: toolset.tools,\n stopWhen: stepCountIs(Math.max(1, config.maxActions)),\n abortSignal: abort,\n maxRetries: 0,\n onStepFinish: (step) => {\n if (config.debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[canary][debug] step finish: finishReason=${step.finishReason}; toolCalls=${step.toolCalls?.length ?? 0}; toolResults=${step.toolResults?.length ?? 0}`\n );\n }\n if (logTools) logToolStep(step);\n },\n });\n\n for await (const _ of result.fullStream) {\n if (toolset.isComplete()) {\n completionReason = toolset.getCompletionReason();\n if (config.debug) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] agent marked complete: ${completionReason}`);\n }\n break;\n }\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n errors.push(message);\n if (config.debug) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] agent error: ${message}`);\n }\n }\n\n const healed = toolset.isComplete();\n\n return {\n healed,\n shouldRetryOriginal: false,\n actionsRun,\n timedOut: Date.now() - started > config.healTimeoutMs,\n durationMs: Date.now() - started,\n errors,\n mode,\n reason: completionReason ?? (healed ? \"agent_healed\" : \"agent_incomplete\"),\n modelId,\n summary: toolset.getSummary(),\n };\n}\n\n// === Utilities (exported for use by healer-tools.ts and healer-prompt.ts) ===\n\nfunction resolveMode(config: ReturnType<typeof loadCanaryConfig>): \"dry-run\" | \"warn\" | \"full\" {\n if (config.dryRun) return \"dry-run\";\n if (config.warnOnly) return \"warn\";\n return \"full\";\n}\n\nfunction baseOutcome({\n started,\n healed,\n shouldRetryOriginal,\n mode,\n reason,\n actionsRun = [],\n errors = [],\n}: {\n started: number;\n healed: boolean;\n shouldRetryOriginal: boolean;\n mode: \"dry-run\" | \"warn\" | \"full\";\n reason?: string;\n actionsRun?: string[];\n errors?: string[];\n}): HealOutcome {\n return {\n healed,\n shouldRetryOriginal,\n actionsRun,\n timedOut: false,\n durationMs: Date.now() - started,\n errors,\n mode,\n reason,\n };\n}\n\nexport function sanitizeString(value: string): string {\n let result = value;\n for (const { regex, replacement } of REDACTION_PATTERNS) {\n result = result.replace(regex, replacement);\n }\n return result;\n}\n\nexport function sanitizeUnknown(value: unknown, maxPayloadBytes: number, seen = new WeakSet()): unknown {\n if (value === null || value === undefined) return value;\n if (typeof value === \"string\") {\n return truncate(sanitizeString(value), maxPayloadBytes);\n }\n if (typeof value === \"number\" || typeof value === \"boolean\") return value;\n if (typeof value === \"object\") {\n if (seen.has(value as object)) return \"[REDACTED_CYCLE]\";\n seen.add(value as object);\n if (Array.isArray(value)) {\n return value.slice(0, 50).map((v) => sanitizeUnknown(v, maxPayloadBytes, seen));\n }\n const out: Record<string, unknown> = {};\n const entries = Object.entries(value as Record<string, unknown>).slice(0, 50);\n for (const [k, v] of entries) {\n out[k] = sanitizeUnknown(v, maxPayloadBytes, seen);\n }\n return out;\n }\n return \"[REDACTED_UNKNOWN]\";\n}\n\nexport function violatesCodeDenylist(code: string): boolean {\n return CODE_DENY_PATTERNS.some((p) => p.test(code));\n}\n\nfunction truncate(value: string, max: number): string {\n if (value.length <= max) return value;\n return value.slice(0, max) + \"...\";\n}\n","import { createOpenAI } from \"@ai-sdk/openai\";\nimport { createAnthropic } from \"@ai-sdk/anthropic\";\nimport { createAzure } from \"@ai-sdk/azure\";\nimport type { LanguageModel } from \"ai\";\nimport { loadCanaryConfig, type CanaryConfig } from \"./config\";\n\ntype ResolvedModel = {\n model: LanguageModel | null;\n modelId?: string;\n reason?: string;\n};\n\nexport function resolveHealerModel(config: CanaryConfig = loadCanaryConfig()): ResolvedModel {\n if (!config.enabled) {\n return { model: null, reason: \"healing_disabled\" };\n }\n\n if (!config.apiKey) {\n return { model: null, reason: \"missing_api_key\" };\n }\n\n const provider = (config.aiProvider ?? \"openai\").toLowerCase();\n const modelId = config.aiModel || (provider === \"anthropic\" ? \"claude-3-5-haiku-20241022\" : \"gpt-4o-mini\");\n\n try {\n switch (provider) {\n case \"openai\": {\n const client = createOpenAI({ apiKey: config.apiKey, baseURL: process.env.AI_BASE_URL });\n return { model: client(modelId), modelId };\n }\n case \"anthropic\": {\n const client = createAnthropic({ apiKey: config.apiKey, baseURL: process.env.AI_BASE_URL });\n return { model: client(modelId), modelId };\n }\n case \"azure\": {\n const resourceName = process.env.AZURE_OPENAI_RESOURCE_NAME;\n const apiVersion = process.env.AZURE_OPENAI_API_VERSION;\n if (!resourceName) {\n return { model: null, reason: \"missing_azure_resource\" };\n }\n const client = createAzure({\n apiKey: config.apiKey,\n resourceName,\n apiVersion,\n });\n return { model: client(modelId), modelId };\n }\n default:\n return { model: null, reason: \"unsupported_provider\" };\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { model: null, reason: `model_error:${message}` };\n }\n}\n","/**\n * Agentic tool definitions for the AI test healer.\n *\n * Uses a `makePageTool` factory to eliminate repetitive guard/try-catch boilerplate\n * across the ~15 tool definitions.\n */\n\nimport { tool, type Tool } from \"ai\";\nimport { z } from \"zod\";\nimport type { loadCanaryConfig } from \"./config\";\nimport { sanitizeString, sanitizeUnknown, violatesCodeDenylist } from \"./healer\";\n\ntype PageLike = {\n waitForTimeout?: (ms: number) => Promise<unknown>;\n accessibility?: { snapshot: () => Promise<unknown> };\n screenshot?: (options?: { fullPage?: boolean }) => Promise<Buffer | Uint8Array | string>;\n locator?: (selector: string) => LocatorLike & {\n click?: (opts?: unknown) => Promise<unknown>;\n fill?: (value: string, opts?: unknown) => Promise<unknown>;\n pressSequentially?: (text: string, opts?: unknown) => Promise<unknown>;\n hover?: () => Promise<unknown>;\n selectOption?: (value: string) => Promise<unknown>;\n textContent?: () => Promise<string | null>;\n isVisible?: () => Promise<boolean>;\n waitFor?: (opts?: { state?: string }) => Promise<unknown>;\n };\n getByText?: (text: string) => LocatorLike & {\n click?: (opts?: unknown) => Promise<unknown>;\n };\n keyboard?: { press?: (key: string) => Promise<unknown> };\n goto?: (url: string) => Promise<unknown>;\n goBack?: () => Promise<unknown>;\n reload?: () => Promise<unknown>;\n evaluate?: (script: string) => Promise<unknown>;\n};\n\ntype LocatorLike = {\n scrollIntoViewIfNeeded?: (options?: { timeout?: number }) => Promise<unknown>;\n};\n\ntype ToolConfig = ReturnType<typeof loadCanaryConfig>;\n\ntype ToolContext = {\n page?: PageLike;\n config: ToolConfig;\n actionsRun: string[];\n debug?: boolean;\n};\n\ntype ToolsetResult = {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n tools: Record<string, Tool<any, any>>;\n isComplete: () => boolean;\n getCompletionReason: () => string | undefined;\n getSummary: () => string | undefined;\n};\n\n// === Helper Factories ===\n\nfunction truncate(value: string, max: number): string {\n if (value.length <= max) return value;\n return value.slice(0, max) + \"...\";\n}\n\nfunction safeJson(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\nfunction errMsg(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Factory for tools that interact with the page. Handles:\n * - tracking the action name\n * - debug logging\n * - read-only / dry-run guards\n * - page availability check\n * - try/catch with error formatting\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction makePageTool(\n ctx: ToolContext,\n opts: {\n name: string;\n description: string;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n inputSchema: z.ZodType<any>;\n /** Whether this tool mutates state (subject to readOnly guard). Default true. */\n mutates?: boolean;\n /** Key to check on page for availability (e.g. 'locator', 'goto'). Skips guard if omitted. */\n pageCheck?: keyof PageLike;\n /** The dry-run result to return. */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n dryResult: (input: any) => unknown;\n /** The actual execution. */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n execute: (input: any) => Promise<unknown>;\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n): Tool<any, any> {\n const { page, config, actionsRun, debug } = ctx;\n const mutates = opts.mutates ?? true;\n\n const log = (msg: string) => {\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] ${msg}`);\n }\n };\n\n return tool({\n description: opts.description,\n inputSchema: opts.inputSchema,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n execute: async (input: any) => {\n const trackSuffix = input?.text ?? input?.selector ?? input?.name ?? input?.key ?? input?.url ?? input?.ms ?? \"\";\n actionsRun.push(trackSuffix ? `${opts.name}:${trackSuffix}` : opts.name);\n log(`${opts.name} called`);\n\n if (mutates && config.readOnly) return { error: \"read_only_mode\" };\n if (config.dryRun) return opts.dryResult(input);\n if (opts.pageCheck && !page?.[opts.pageCheck]) return { error: \"Page unavailable\" };\n\n try {\n const result = await opts.execute(input);\n log(`${opts.name} succeeded`);\n return result;\n } catch (err) {\n const msg = errMsg(err);\n log(`${opts.name} error: ${msg}`);\n return { error: msg };\n }\n },\n });\n}\n\n// === Main Export ===\n\nexport function createAgenticTools(params: {\n page?: PageLike;\n config: ToolConfig;\n actionsRun: string[];\n debug?: boolean;\n}): ToolsetResult {\n const ctx: ToolContext = params;\n const { page, config } = ctx;\n let complete = false;\n let completionReason: string | undefined;\n let completionSummary: string | undefined;\n const maxPayloadBytes = Math.max(1000, config.maxPayloadBytes || 60000);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const tools: Record<string, Tool<any, any>> = {\n // === Core Action Tools ===\n click: makePageTool(ctx, {\n name: \"click\",\n description: \"Click an element by its visible text\",\n inputSchema: z.object({ text: z.string().describe(\"The visible text to click\") }),\n pageCheck: \"getByText\",\n dryResult: ({ text }) => ({ clicked: text, mode: \"dry-run\" as const }),\n execute: async ({ text }) => {\n const el = page!.getByText!(text);\n if (el?.scrollIntoViewIfNeeded) await el.scrollIntoViewIfNeeded({ timeout: 2000 });\n const clickable = el as { click?: (opts?: unknown) => Promise<unknown> };\n if (clickable?.click) await clickable.click({ timeout: 5000 });\n return { clicked: text };\n },\n }),\n\n click_selector: makePageTool(ctx, {\n name: \"click_selector\",\n description: \"Click an element by CSS or Playwright selector\",\n inputSchema: z.object({ selector: z.string().describe(\"CSS or Playwright selector\") }),\n pageCheck: \"locator\",\n dryResult: ({ selector }) => ({ clicked: selector, mode: \"dry-run\" as const }),\n execute: async ({ selector }) => {\n const el = page!.locator!(selector);\n if (el?.scrollIntoViewIfNeeded) await el.scrollIntoViewIfNeeded({ timeout: 2000 });\n if (el?.click) await el.click({ timeout: 5000 });\n return { clicked: selector };\n },\n }),\n\n fill: makePageTool(ctx, {\n name: \"fill\",\n description: \"Fill an input field with a value using CSS selector (clears existing content)\",\n inputSchema: z.object({\n selector: z.string().describe('CSS selector for the input (e.g. input[placeholder=\"...\"])'),\n value: z.string().describe(\"Value to fill\"),\n }),\n pageCheck: \"locator\",\n dryResult: ({ selector }) => ({ filled: selector, mode: \"dry-run\" as const }),\n execute: async ({ selector, value }) => {\n const el = page!.locator!(selector);\n if (el?.fill) await el.fill(value, { timeout: 5000 });\n return { filled: selector, value };\n },\n }),\n\n fill_by_name: makePageTool(ctx, {\n name: \"fill_by_name\",\n description: 'Fill a textbox by its accessible name (shown in snapshot as textbox \"NAME\"). This is the PREFERRED way to fill inputs - use the name from the snapshot directly.',\n inputSchema: z.object({\n name: z.string().describe('The accessible name of the textbox (from snapshot, e.g. \"Enter 6-digit code\")'),\n value: z.string().describe(\"Value to fill\"),\n }),\n pageCheck: \"locator\",\n dryResult: ({ name }) => ({ filled: name, mode: \"dry-run\" as const }),\n execute: async ({ name, value }) => {\n const el = page!.locator!(`role=textbox[name=\"${name}\"]`);\n if (el?.fill) await el.fill(value, { timeout: 5000 });\n return { filled: name, value };\n },\n }),\n\n type: makePageTool(ctx, {\n name: \"type\",\n description: \"Type text character by character (triggers key events)\",\n inputSchema: z.object({\n selector: z.string().describe(\"CSS or Playwright selector\"),\n text: z.string().describe(\"Text to type\"),\n }),\n pageCheck: \"locator\",\n dryResult: ({ text }) => ({ typed: text, mode: \"dry-run\" as const }),\n execute: async ({ selector, text }) => {\n const el = page!.locator!(selector);\n if (el?.pressSequentially) await el.pressSequentially(text);\n return { typed: text, into: selector };\n },\n }),\n\n press_key: makePageTool(ctx, {\n name: \"press_key\",\n description: \"Press a keyboard key (Enter, Tab, Escape, ArrowDown, etc.)\",\n inputSchema: z.object({ key: z.string().describe(\"Key to press\") }),\n pageCheck: \"keyboard\",\n dryResult: ({ key }) => ({ pressed: key, mode: \"dry-run\" as const }),\n execute: async ({ key }) => {\n await page!.keyboard!.press!(key);\n return { pressed: key };\n },\n }),\n\n hover: makePageTool(ctx, {\n name: \"hover\",\n description: \"Hover over an element\",\n inputSchema: z.object({ selector: z.string().describe(\"CSS or Playwright selector\") }),\n pageCheck: \"locator\",\n dryResult: ({ selector }) => ({ hovered: selector, mode: \"dry-run\" as const }),\n execute: async ({ selector }) => {\n const el = page!.locator!(selector);\n if (el?.hover) await el.hover();\n return { hovered: selector };\n },\n }),\n\n select_option: makePageTool(ctx, {\n name: \"select_option\",\n description: \"Select an option from a dropdown\",\n inputSchema: z.object({\n selector: z.string().describe(\"CSS or Playwright selector for the select element\"),\n value: z.string().describe(\"Value or label to select\"),\n }),\n pageCheck: \"locator\",\n dryResult: ({ value }) => ({ selected: value, mode: \"dry-run\" as const }),\n execute: async ({ selector, value }) => {\n const el = page!.locator!(selector);\n if (el?.selectOption) await el.selectOption(value);\n return { selected: value, from: selector };\n },\n }),\n\n // === Navigation & Waiting ===\n wait: makePageTool(ctx, {\n name: \"wait\",\n description: \"Wait for a duration in milliseconds\",\n inputSchema: z.object({ ms: z.number().min(100).max(30000).describe(\"Milliseconds to wait\") }),\n mutates: false,\n dryResult: ({ ms }) => ({ waited: ms, mode: \"dry-run\" as const }),\n execute: async ({ ms }) => {\n if (page?.waitForTimeout) {\n await page.waitForTimeout(ms);\n } else {\n await new Promise((resolve) => setTimeout(resolve, ms));\n }\n return { waited: ms };\n },\n }),\n\n wait_for_selector: makePageTool(ctx, {\n name: \"wait_for_selector\",\n description: \"Wait for an element to reach a state (visible, hidden, attached, detached)\",\n inputSchema: z.object({\n selector: z.string().describe(\"CSS or Playwright selector\"),\n state: z.enum([\"visible\", \"hidden\", \"attached\", \"detached\"]).optional().describe(\"State to wait for\"),\n }),\n mutates: false,\n pageCheck: \"locator\",\n dryResult: ({ selector }) => ({ found: selector, mode: \"dry-run\" as const }),\n execute: async ({ selector, state }) => {\n const el = page!.locator!(selector);\n if (el?.waitFor) await el.waitFor({ state: state ?? \"visible\" });\n return { found: selector, state: state ?? \"visible\" };\n },\n }),\n\n navigate: makePageTool(ctx, {\n name: \"navigate\",\n description: \"Navigate to a URL\",\n inputSchema: z.object({ url: z.string().describe(\"URL to navigate to\") }),\n pageCheck: \"goto\",\n dryResult: ({ url }) => ({ navigated: url, mode: \"dry-run\" as const }),\n execute: async ({ url }) => {\n await page!.goto!(url);\n return { navigated: url };\n },\n }),\n\n go_back: makePageTool(ctx, {\n name: \"go_back\",\n description: \"Go back in browser history\",\n inputSchema: z.object({}),\n pageCheck: \"goBack\",\n dryResult: () => ({ action: \"back\", mode: \"dry-run\" as const }),\n execute: async () => {\n await page!.goBack!();\n return { action: \"back\" };\n },\n }),\n\n reload: makePageTool(ctx, {\n name: \"reload\",\n description: \"Reload the current page\",\n inputSchema: z.object({}),\n pageCheck: \"reload\",\n dryResult: () => ({ action: \"reload\", mode: \"dry-run\" as const }),\n execute: async () => {\n await page!.reload!();\n return { action: \"reload\" };\n },\n }),\n\n // === Inspection Tools ===\n snapshot: tool({\n description: \"Get the accessibility tree of the current page to understand its structure. ALWAYS call this first!\",\n inputSchema: z.object({}),\n execute: async () => {\n params.actionsRun.push(\"snapshot\");\n if (config.dryRun) return { tree: null, mode: \"dry-run\" };\n if (!page) return { error: \"Page unavailable\" };\n\n let tree: unknown;\n let html: string | undefined;\n\n try {\n if (page.accessibility && typeof page.accessibility.snapshot === \"function\") {\n tree = await page.accessibility.snapshot();\n }\n } catch (err) {\n if (params.debug) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] accessibility snapshot error: ${errMsg(err)}`);\n }\n }\n\n if (!tree && page.evaluate) {\n try {\n html = (await page.evaluate(\"document.body.innerHTML\")) as string;\n } catch (err) {\n if (params.debug) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] HTML fallback error: ${errMsg(err)}`);\n }\n }\n }\n\n if (!tree && !html) return { error: \"Could not get page content\" };\n\n const sanitizedTree = sanitizeUnknown(tree, maxPayloadBytes);\n const sanitizedHtml = html ? truncate(sanitizeString(html), maxPayloadBytes) : undefined;\n return { tree: sanitizedTree, html: sanitizedHtml };\n },\n }),\n\n screenshot: tool({\n description: \"Capture a screenshot of the current page\",\n inputSchema: z.object({\n fullPage: z.boolean().optional().describe(\"Whether to capture the full page\"),\n }),\n execute: async ({ fullPage }) => {\n params.actionsRun.push(\"screenshot\");\n if (config.dryRun) return { screenshot: undefined, mode: \"dry-run\" };\n if (!page?.screenshot) return { error: \"Page unavailable\" };\n try {\n const buffer = await page.screenshot({ fullPage: fullPage ?? false });\n const b = typeof buffer === \"string\" ? Buffer.from(buffer) : Buffer.from(buffer);\n const base64 = b.toString(\"base64\");\n if (b.byteLength > maxPayloadBytes || base64.length > maxPayloadBytes) {\n return { error: \"screenshot_too_large\" };\n }\n return { screenshot: `data:image/png;base64,${truncate(base64, maxPayloadBytes)}` };\n } catch (err) {\n return { error: errMsg(err) };\n }\n },\n }),\n\n get_text: makePageTool(ctx, {\n name: \"get_text\",\n description: \"Get the text content of an element\",\n inputSchema: z.object({ selector: z.string().describe(\"CSS or Playwright selector\") }),\n mutates: false,\n pageCheck: \"locator\",\n dryResult: () => ({ text: null, mode: \"dry-run\" as const }),\n execute: async ({ selector }) => {\n const el = page!.locator!(selector);\n const text = el?.textContent ? await el.textContent() : null;\n const sanitized = text ? truncate(sanitizeString(text), maxPayloadBytes) : text;\n return { text: sanitized };\n },\n }),\n\n is_visible: makePageTool(ctx, {\n name: \"is_visible\",\n description: \"Check if an element is visible\",\n inputSchema: z.object({ selector: z.string().describe(\"CSS or Playwright selector\") }),\n mutates: false,\n pageCheck: \"locator\",\n dryResult: () => ({ visible: false, mode: \"dry-run\" as const }),\n execute: async ({ selector }) => {\n const el = page!.locator!(selector);\n const visible = el?.isVisible ? await el.isVisible() : false;\n return { visible };\n },\n }),\n\n evaluate: tool({\n description: \"Run JavaScript in the page context\",\n inputSchema: z.object({ script: z.string().describe(\"JavaScript code to execute\") }),\n execute: async ({ script }) => {\n params.actionsRun.push(\"evaluate\");\n if (!config.allowEvaluate) return { error: \"evaluate_disabled\" };\n if (violatesCodeDenylist(script)) return { error: \"evaluate_blocked\" };\n if (config.dryRun) return { result: null, mode: \"dry-run\" };\n if (!page?.evaluate) return { error: \"Page unavailable\" };\n try {\n const scriptSafe = truncate(script, maxPayloadBytes);\n const result = await page.evaluate(scriptSafe);\n return { result: sanitizeUnknown(result, maxPayloadBytes) };\n } catch (err) {\n return { error: errMsg(err) };\n }\n },\n }),\n\n run_playwright_code: tool({\n description:\n 'Execute arbitrary Playwright code. Has access to `page` object. Use for complex operations not covered by other tools. Example: `await page.getByRole(\"button\", { name: \"Submit\" }).click();`',\n inputSchema: z.object({\n code: z.string().describe(\"Playwright code to execute (has access to `page` object)\"),\n }),\n execute: async ({ code }) => {\n params.actionsRun.push(\"run_playwright_code\");\n if (!config.allowRunCode) return { executed: false, error: \"run_code_disabled\" };\n if (violatesCodeDenylist(code)) return { executed: false, error: \"run_code_blocked\" };\n if (config.dryRun) return { executed: false, mode: \"dry-run\" };\n if (!page) return { executed: false, error: \"Page unavailable\" };\n try {\n const boundedCode = truncate(code, maxPayloadBytes);\n const fn = new Function(\"page\", `return (async () => { ${boundedCode} })();`);\n const result = await fn(page);\n return { executed: true, result: sanitizeUnknown(result, maxPayloadBytes) };\n } catch (err) {\n return { executed: false, error: errMsg(err) };\n }\n },\n }),\n\n // === Completion Tool ===\n mark_complete: tool({\n description:\n \"Call this when the browser is ready for the test to continue. You MUST call this when done healing!\",\n inputSchema: z.object({\n reason: z.string().describe(\"Brief technical description of what was done\"),\n summary: z.string().describe('One-line human-readable summary for the test report, e.g. \"Clicked \\'Send OTP\\' instead of \\'Submit\\'\" or \"Filled email field by accessible name\"'),\n }),\n execute: async ({ reason, summary }) => {\n params.actionsRun.push(\"mark_complete\");\n if (!config.dryRun) {\n complete = true;\n completionReason = reason;\n completionSummary = summary;\n }\n return { complete: !config.dryRun, reason, summary, mode: config.dryRun ? \"dry-run\" : undefined };\n },\n }),\n };\n\n return {\n tools,\n isComplete: () => complete,\n getCompletionReason: () => completionReason,\n getSummary: () => completionSummary,\n };\n}\n\n// === Tool Step Logging ===\n\ntype ToolStep = {\n finishReason?: string;\n toolCalls?: Array<{ toolName?: string; args?: unknown }>;\n toolResults?: Array<{ toolName?: string; result?: unknown; error?: unknown }>;\n};\n\nexport function logToolStep(step: ToolStep): void {\n const color = {\n magenta: \"\\x1b[35m\",\n cyan: \"\\x1b[36m\",\n yellow: \"\\x1b[33m\",\n green: \"\\x1b[32m\",\n reset: \"\\x1b[0m\",\n } as const;\n\n const calls = step.toolCalls ?? [];\n const results = step.toolResults ?? [];\n\n if (calls.length === 0 && results.length === 0) {\n // eslint-disable-next-line no-console\n console.log(\n `${color.magenta}[canary][tool] step${color.reset} (no tool calls/results; finish=${step.finishReason ?? \"unknown\"})`\n );\n }\n\n for (const call of calls) {\n const name = call.toolName ?? \"unknown_tool\";\n const args = call.args ? truncate(safeJson(call.args), 300) : \"\";\n // eslint-disable-next-line no-console\n console.log(\n `${color.magenta}[canary][tool] call${color.reset} ${color.cyan}${name}${color.reset}${args ? ` args=${args}` : \"\"}`\n );\n }\n\n for (const result of results) {\n const name = result.toolName ?? \"unknown_tool\";\n const res = result.result ? truncate(safeJson(result.result), 300) : undefined;\n const err = result.error ? truncate(safeJson(result.error), 300) : undefined;\n const statusColor = err ? color.yellow : color.green;\n // eslint-disable-next-line no-console\n console.log(\n `${color.magenta}[canary][tool] result${color.reset} ${color.cyan}${name}${color.reset}` +\n (res ? ` ${statusColor}->${color.reset} ${res}` : \"\") +\n (err ? ` ${color.yellow}error=${err}${color.reset}` : \"\")\n );\n }\n}\n","/**\n * System prompt and message construction for the AI test healer.\n */\n\nimport type { loadCanaryConfig } from \"./config\";\nimport type { FailureContext, TestContext } from \"./healer\";\nimport { sanitizeString, sanitizeUnknown } from \"./healer\";\n\ntype PageLike = {\n accessibility?: { snapshot: () => Promise<unknown> };\n screenshot?: (options?: { fullPage?: boolean }) => Promise<Buffer | Uint8Array | string>;\n evaluate?: (script: string) => Promise<unknown>;\n};\n\ntype ToolConfig = ReturnType<typeof loadCanaryConfig>;\n\nfunction resolveMode(config: ToolConfig): \"dry-run\" | \"warn\" | \"full\" {\n if (config.dryRun) return \"dry-run\";\n if (config.warnOnly) return \"warn\";\n return \"full\";\n}\n\nfunction truncate(value: string, max: number): string {\n if (value.length <= max) return value;\n return value.slice(0, max) + \"...\";\n}\n\nfunction safeJson(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\nexport function buildSystemPrompt(\n testContext: TestContext,\n config: ToolConfig\n): string {\n const testInfo = testContext.testFile\n ? `\\n## Test Context\\nFile: ${testContext.testFile}\\nTest: \"${testContext.testTitle ?? \"unknown\"}\"\\n`\n : \"\";\n\n const testSource = testContext.testSource\n ? `\\n### Full Test Code:\\n\\`\\`\\`typescript\\n${testContext.testSource}\\n\\`\\`\\`\\n`\n : \"\";\n\n const expectedAfter = testContext.expectedAfter\n ? `\\n### What the Test Expects After This Step:\\n${testContext.expectedAfter}\\n`\n : \"\";\n\n return `You are an AI test healer. A Playwright test step failed. Your job is to make the browser ready for the next step in the test **without violating the intent of the test**. Heal only the failing step; do not advance beyond it.\n\n## Your Goal\nGet the browser into the state the test expects, then call mark_complete.\n${testInfo}${testSource}\n### Current Step That Failed:\n${testContext.currentStep}\n${expectedAfter}\n## The Failure\nAction attempted: ${testContext.action}\nTarget: ${testContext.target ?? \"unknown\"}\nError: ${testContext.errorMessage ?? \"unknown\"}\n\n## Test Intent and Discipline\n- Adhere to the spirit of the test. If advancing would bypass a required validation or skip a missing field, you must not proceed—report failure instead.\n- Heal only this step. Avoid extra navigation or state changes unrelated to the intended action. If you take a detour, undo it before completion.\n- If you cannot confidently achieve the expected postcondition for this step, do not mark complete; let the test fail.\n\n## Tools Available\n\n### Inspection (use first!)\n- snapshot() - Get accessibility tree to see page structure\n- screenshot() - Capture visual screenshot\n- get_text(selector) - Get text content of an element\n- is_visible(selector) - Check if element is visible\n\n### Actions\n- click(text) - Click element by visible text\n- click_selector(selector) - Click by CSS/Playwright selector\n- fill_by_name(name, value) - PREFERRED: Fill textbox by accessible name from snapshot (e.g. textbox \"Enter 6-digit code\")\n- fill(selector, value) - Fill input by CSS selector (use placeholder attribute, NOT aria-label)\n- type(selector, text) - Type text character by character\n- press_key(key) - Press keyboard key (Enter, Tab, etc.)\n- hover(selector) - Hover over element\n- select_option(selector, value) - Select from dropdown\n\n### Navigation & Waiting\n- wait(ms) - Wait for a duration\n- wait_for_selector(selector, state?) - Wait for element state\n- navigate(url) - Navigate to URL\n- go_back() - Go back in history\n- reload() - Reload page\n\n### Power Tool\n- run_playwright_code(code) - Execute any Playwright code with \\`page\\` object\n Example: \\`await page.getByRole('button', { name: 'Submit' }).click();\\`\n\n### Completion (REQUIRED)\n- mark_complete(reason, summary) - Call when browser is ready for test to continue\n - reason: technical description of what was done\n - summary: one-line human-readable summary for test report (e.g. \"Clicked 'Send OTP' instead of 'Submit'\")\n\n## CRITICAL RULES - READ CAREFULLY\n\n### Rule 1: YOU MUST PERFORM THE ACTION YOURSELF\nThe test tried to ${testContext.action}(\"${testContext.target ?? \"\"}\") but it failed.\nYOU must perform the equivalent action on the correct element.\nDO NOT leave it for \"the next step\" - there is no next step until YOU complete the action.\n\n### Rule 2: The workflow is ALWAYS:\n1. snapshot() - understand the page\n2. click() / fill() / type() - PERFORM THE ACTION on the correct element\n3. mark_complete() - report what you did\n\n### Rule 3: NEVER call mark_complete without performing an action\nIf mark_complete is called without a click/fill/type, the test WILL FAIL.\n\n## Example 1: Test tried click(\"Submit\") but button says \"Send OTP\"\nCORRECT:\n1. snapshot() → see \"Send OTP\" button exists\n2. click(\"Send OTP\") → CLICK THE BUTTON\n3. mark_complete({reason: \"clicked Send OTP button\", summary: \"Clicked 'Send OTP' instead of 'Submit'\"})\n\nWRONG (this will cause test failure):\n1. snapshot() → see \"Send OTP\" button exists\n2. mark_complete(...) ← WRONG! No click performed!\n\n## Example 2: Test tried fill with wrong placeholder\nIf snapshot shows: textbox \"Enter 6-digit code\" [ref=e37]\nCORRECT:\n1. snapshot() → see textbox \"Enter 6-digit code\"\n2. fill_by_name(\"Enter 6-digit code\", \"111222\") → USE THE NAME FROM SNAPSHOT\n3. mark_complete({reason: \"filled by accessible name\", summary: \"Filled 'Enter 6-digit code' field\"})\n\n## If healing would break test intent\n- If the page is missing required UI (e.g., a field not present) or advancing would skip validation, do NOT hack around it. Report failure by not calling mark_complete.\n- If you temporarily navigate or change state, undo it before completion so the test can continue from the intended point.\n\n## Start now\n1. Call snapshot()\n2. Identify the correct element for the intended action\n3. PERFORM THE ACTION (click, fill, type, etc.)\n4. Call mark_complete with what you did\n\nMode: ${resolveMode(config)}.`;\n}\n\nexport function buildInitialMessages(\n testContext: TestContext,\n failure: FailureContext,\n initial: { snapshot?: unknown; html?: string; screenshot?: string }\n): Array<{ role: \"user\"; content: string }> {\n const parts: string[] = [];\n parts.push(\n `Please heal this test step. Start by reviewing the snapshot below. Failing step: ${failure.action ?? \"unknown\"}(${failure.target ?? \"unknown\"}). Error: ${failure.errorMessage ?? \"unknown\"}.`\n );\n\n if (testContext.testSource) {\n parts.push(\n `Full test source (truncated/redacted):\\n\\`\\`\\`typescript\\n${truncate(testContext.testSource, 15000)}\\n\\`\\`\\``\n );\n }\n\n parts.push(`Test file: ${testContext.testFile ?? \"unknown\"}`);\n parts.push(`Test title: ${testContext.testTitle ?? \"unknown\"}`);\n\n if (initial.snapshot) {\n parts.push(`Initial accessibility snapshot (sanitized):\\n\\`\\`\\`json\\n${truncate(safeJson(initial.snapshot), 8000)}\\n\\`\\`\\``);\n }\n if (initial.html) {\n parts.push(`HTML fallback (sanitized):\\n\\`\\`\\`html\\n${truncate(initial.html, 4000)}\\n\\`\\`\\``);\n }\n if (initial.screenshot) {\n parts.push(`Screenshot (base64 data URL, truncated): ${truncate(initial.screenshot, 12000)}`);\n }\n\n parts.push(`Remember: perform only the intended step and mark_complete only when the postcondition for this step is truly met.`);\n\n return [{ role: \"user\", content: parts.join(\"\\n\\n\") }];\n}\n\nexport async function buildInitialPageContext(\n page: PageLike | undefined,\n config: ToolConfig\n): Promise<{ snapshot?: unknown; html?: string; screenshot?: string }> {\n const maxPayloadBytes = Math.max(1000, config.maxPayloadBytes || 60000);\n const result: { snapshot?: unknown; html?: string; screenshot?: string } = {};\n if (!page) return result;\n\n try {\n if (page.accessibility?.snapshot) {\n const tree = await page.accessibility.snapshot();\n if (tree) {\n result.snapshot = sanitizeUnknown(tree, maxPayloadBytes);\n }\n }\n } catch {\n // ignore\n }\n\n if (!result.snapshot && page.evaluate) {\n try {\n const html = (await page.evaluate(\"document.body.innerHTML\")) as string;\n if (html) {\n result.html = truncate(sanitizeString(html), maxPayloadBytes);\n }\n } catch {\n // ignore\n }\n }\n\n if (config.visionEnabled && page.screenshot) {\n try {\n const buffer = await page.screenshot({ fullPage: false });\n const b = typeof buffer === \"string\" ? Buffer.from(buffer) : Buffer.from(buffer);\n const base64 = b.toString(\"base64\");\n if (b.byteLength <= maxPayloadBytes && base64.length <= maxPayloadBytes) {\n result.screenshot = `data:image/png;base64,${truncate(base64, maxPayloadBytes)}`;\n }\n } catch {\n // ignore\n }\n }\n\n return result;\n}\n"],"mappings":"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","names":["path","path","fs","path","path","fs","fs","truncate","safeJson","resolveMode","truncate","fs"]}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
|
|
2
|
+
|
|
1
3
|
// src/auth.ts
|
|
2
4
|
import fs from "fs/promises";
|
|
3
5
|
import os from "os";
|
|
@@ -145,4 +147,4 @@ export {
|
|
|
145
147
|
saveAuth,
|
|
146
148
|
resolveConfig
|
|
147
149
|
};
|
|
148
|
-
//# sourceMappingURL=chunk-
|
|
150
|
+
//# sourceMappingURL=chunk-PWWQGYFG.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/auth.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport process from \"node:process\";\n\ntype StoredAuth = {\n token?: string;\n apiUrl?: string;\n orgId?: string;\n orgName?: string;\n};\n\n/** Multi-profile auth file format */\ntype MultiProfileAuth = {\n profiles: Record<string, StoredAuth>;\n default: string;\n};\n\n/** Legacy flat format (single profile) */\ntype LegacyAuth = StoredAuth;\n\nconst AUTH_DIR = path.join(os.homedir(), \".config\", \"canary-cli\");\nconst AUTH_FILE = path.join(AUTH_DIR, \"auth.json\");\n\n/* ── Shared argv helpers ─────────────────────────────────────────────── */\n\nexport function getArgValue(argv: string[], key: string): string | undefined {\n const index = argv.indexOf(key);\n if (index === -1 || index >= argv.length - 1) return undefined;\n return argv[index + 1];\n}\n\nexport function hasFlag(argv: string[], ...flags: string[]): boolean {\n return flags.some((flag) => argv.includes(flag));\n}\n\n/* ── Environment / profile mapping ───────────────────────────────────── */\n\n/** Map --env values to profile names for multi-profile auth storage */\nexport function envToProfile(env: string): string {\n if (env === \"prod\" || env === \"production\") return \"production\";\n if (env === \"dev\") return \"dev\";\n if (env === \"local\") return \"local\";\n return env;\n}\n\nconst VALID_ENVS = [\"prod\", \"dev\", \"local\"];\n\n/** Environment URL mappings shared across CLI commands */\nexport const ENV_URLS: Record<string, { api: string; app: string }> = {\n prod: {\n api: \"https://api.trycanary.ai\",\n app: \"https://app.trycanary.ai\",\n },\n production: {\n api: \"https://api.trycanary.ai\",\n app: \"https://app.trycanary.ai\",\n },\n dev: {\n api: \"https://api.dev.trycanary.ai\",\n app: \"https://app.dev.trycanary.ai\",\n },\n local: {\n api: \"http://localhost:3000\",\n app: \"http://localhost:5173\",\n },\n};\n\nfunction isMultiProfile(data: unknown): data is MultiProfileAuth {\n return typeof data === \"object\" && data !== null && \"profiles\" in data;\n}\n\n/** Read the raw auth file and normalize to multi-profile format */\nasync function readAuthFile(): Promise<MultiProfileAuth | null> {\n try {\n const content = await fs.readFile(AUTH_FILE, \"utf8\");\n const data = JSON.parse(content) as unknown;\n\n if (isMultiProfile(data)) {\n return data;\n }\n\n // Legacy flat format — treat as the \"default\" profile\n const legacy = data as LegacyAuth;\n return {\n profiles: { default: legacy },\n default: \"default\",\n };\n } catch {\n return null;\n }\n}\n\nexport async function readStoredAuth(profile?: string): Promise<StoredAuth | null> {\n const auth = await readAuthFile();\n if (!auth) return null;\n\n const key = profile ?? auth.default;\n return auth.profiles[key] ?? null;\n}\n\nexport async function readStoredToken(profile?: string): Promise<string | null> {\n const auth = await readStoredAuth(profile);\n return auth?.token ?? null;\n}\n\nexport async function readStoredApiUrl(profile?: string): Promise<string | null> {\n const auth = await readStoredAuth(profile);\n return auth?.apiUrl ?? null;\n}\n\n/** Return all stored tokens across every profile */\nexport async function readAllStoredTokens(): Promise<string[]> {\n const auth = await readAuthFile();\n if (!auth) return [];\n return Object.values(auth.profiles)\n .map((p) => p.token)\n .filter((t): t is string => !!t);\n}\n\nexport async function saveAuth(auth: StoredAuth, profile?: string): Promise<string> {\n await fs.mkdir(AUTH_DIR, { recursive: true, mode: 0o700 });\n\n // Read existing file (may be legacy or multi-profile)\n let existing: MultiProfileAuth;\n try {\n const content = await fs.readFile(AUTH_FILE, \"utf8\");\n const data = JSON.parse(content) as unknown;\n\n if (isMultiProfile(data)) {\n existing = data;\n } else {\n // Migrate legacy flat format into profiles.default\n existing = {\n profiles: { default: data as LegacyAuth },\n default: \"default\",\n };\n }\n } catch {\n existing = { profiles: {}, default: \"default\" };\n }\n\n const key = profile ?? \"default\";\n existing.profiles[key] = auth;\n\n // Set default to this profile if no default exists yet\n if (!existing.profiles[existing.default]) {\n existing.default = key;\n }\n\n await fs.writeFile(AUTH_FILE, JSON.stringify(existing, null, 2), { encoding: \"utf8\", mode: 0o600 });\n return AUTH_FILE;\n}\n\n/* ── Shared config resolution ────────────────────────────────────────── */\n\nexport type ResolvedConfig = { apiUrl: string; token: string };\n\n/**\n * Resolve API URL and auth token from argv flags, env vars, and stored profiles.\n * Used by knobs, feature-flag, release, and debug-session commands.\n */\nexport async function resolveConfig(argv: string[]): Promise<ResolvedConfig> {\n const env = getArgValue(argv, \"--env\");\n const profile = env ? envToProfile(env) : undefined;\n\n if (env && !ENV_URLS[env]) {\n console.error(`Unknown environment: ${env}`);\n console.error(`Valid environments: ${VALID_ENVS.join(\", \")}`);\n process.exit(1);\n }\n\n const storedApiUrl = await readStoredApiUrl(profile);\n const apiUrl =\n getArgValue(argv, \"--api-url\") ??\n (env ? ENV_URLS[env].api : undefined) ??\n process.env.CANARY_API_URL ??\n storedApiUrl ??\n \"https://api.trycanary.ai\";\n\n // Explicit --token flag always wins\n const explicitToken = getArgValue(argv, \"--token\");\n\n let token: string | null;\n if (explicitToken) {\n token = explicitToken;\n } else if (profile) {\n // --env was specified: profile token takes priority, env var is fallback\n token = (await readStoredToken(profile)) ?? process.env.CANARY_API_TOKEN ?? null;\n if (!token) {\n console.error(`Error: No token found for \"${env}\" profile.`);\n console.error(`Run: canary login --env ${env}`);\n process.exit(1);\n }\n } else {\n // No --env: env var first, then default profile\n token = process.env.CANARY_API_TOKEN ?? (await readStoredToken()) ?? null;\n if (!token) {\n console.error(\"Error: No API token found.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n }\n\n return { apiUrl, token };\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/auth.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport process from \"node:process\";\n\ntype StoredAuth = {\n token?: string;\n apiUrl?: string;\n orgId?: string;\n orgName?: string;\n};\n\n/** Multi-profile auth file format */\ntype MultiProfileAuth = {\n profiles: Record<string, StoredAuth>;\n default: string;\n};\n\n/** Legacy flat format (single profile) */\ntype LegacyAuth = StoredAuth;\n\nconst AUTH_DIR = path.join(os.homedir(), \".config\", \"canary-cli\");\nconst AUTH_FILE = path.join(AUTH_DIR, \"auth.json\");\n\n/* ── Shared argv helpers ─────────────────────────────────────────────── */\n\nexport function getArgValue(argv: string[], key: string): string | undefined {\n const index = argv.indexOf(key);\n if (index === -1 || index >= argv.length - 1) return undefined;\n return argv[index + 1];\n}\n\nexport function hasFlag(argv: string[], ...flags: string[]): boolean {\n return flags.some((flag) => argv.includes(flag));\n}\n\n/* ── Environment / profile mapping ───────────────────────────────────── */\n\n/** Map --env values to profile names for multi-profile auth storage */\nexport function envToProfile(env: string): string {\n if (env === \"prod\" || env === \"production\") return \"production\";\n if (env === \"dev\") return \"dev\";\n if (env === \"local\") return \"local\";\n return env;\n}\n\nconst VALID_ENVS = [\"prod\", \"dev\", \"local\"];\n\n/** Environment URL mappings shared across CLI commands */\nexport const ENV_URLS: Record<string, { api: string; app: string }> = {\n prod: {\n api: \"https://api.trycanary.ai\",\n app: \"https://app.trycanary.ai\",\n },\n production: {\n api: \"https://api.trycanary.ai\",\n app: \"https://app.trycanary.ai\",\n },\n dev: {\n api: \"https://api.dev.trycanary.ai\",\n app: \"https://app.dev.trycanary.ai\",\n },\n local: {\n api: \"http://localhost:3000\",\n app: \"http://localhost:5173\",\n },\n};\n\nfunction isMultiProfile(data: unknown): data is MultiProfileAuth {\n return typeof data === \"object\" && data !== null && \"profiles\" in data;\n}\n\n/** Read the raw auth file and normalize to multi-profile format */\nasync function readAuthFile(): Promise<MultiProfileAuth | null> {\n try {\n const content = await fs.readFile(AUTH_FILE, \"utf8\");\n const data = JSON.parse(content) as unknown;\n\n if (isMultiProfile(data)) {\n return data;\n }\n\n // Legacy flat format — treat as the \"default\" profile\n const legacy = data as LegacyAuth;\n return {\n profiles: { default: legacy },\n default: \"default\",\n };\n } catch {\n return null;\n }\n}\n\nexport async function readStoredAuth(profile?: string): Promise<StoredAuth | null> {\n const auth = await readAuthFile();\n if (!auth) return null;\n\n const key = profile ?? auth.default;\n return auth.profiles[key] ?? null;\n}\n\nexport async function readStoredToken(profile?: string): Promise<string | null> {\n const auth = await readStoredAuth(profile);\n return auth?.token ?? null;\n}\n\nexport async function readStoredApiUrl(profile?: string): Promise<string | null> {\n const auth = await readStoredAuth(profile);\n return auth?.apiUrl ?? null;\n}\n\n/** Return all stored tokens across every profile */\nexport async function readAllStoredTokens(): Promise<string[]> {\n const auth = await readAuthFile();\n if (!auth) return [];\n return Object.values(auth.profiles)\n .map((p) => p.token)\n .filter((t): t is string => !!t);\n}\n\nexport async function saveAuth(auth: StoredAuth, profile?: string): Promise<string> {\n await fs.mkdir(AUTH_DIR, { recursive: true, mode: 0o700 });\n\n // Read existing file (may be legacy or multi-profile)\n let existing: MultiProfileAuth;\n try {\n const content = await fs.readFile(AUTH_FILE, \"utf8\");\n const data = JSON.parse(content) as unknown;\n\n if (isMultiProfile(data)) {\n existing = data;\n } else {\n // Migrate legacy flat format into profiles.default\n existing = {\n profiles: { default: data as LegacyAuth },\n default: \"default\",\n };\n }\n } catch {\n existing = { profiles: {}, default: \"default\" };\n }\n\n const key = profile ?? \"default\";\n existing.profiles[key] = auth;\n\n // Set default to this profile if no default exists yet\n if (!existing.profiles[existing.default]) {\n existing.default = key;\n }\n\n await fs.writeFile(AUTH_FILE, JSON.stringify(existing, null, 2), { encoding: \"utf8\", mode: 0o600 });\n return AUTH_FILE;\n}\n\n/* ── Shared config resolution ────────────────────────────────────────── */\n\nexport type ResolvedConfig = { apiUrl: string; token: string };\n\n/**\n * Resolve API URL and auth token from argv flags, env vars, and stored profiles.\n * Used by knobs, feature-flag, release, and debug-session commands.\n */\nexport async function resolveConfig(argv: string[]): Promise<ResolvedConfig> {\n const env = getArgValue(argv, \"--env\");\n const profile = env ? envToProfile(env) : undefined;\n\n if (env && !ENV_URLS[env]) {\n console.error(`Unknown environment: ${env}`);\n console.error(`Valid environments: ${VALID_ENVS.join(\", \")}`);\n process.exit(1);\n }\n\n const storedApiUrl = await readStoredApiUrl(profile);\n const apiUrl =\n getArgValue(argv, \"--api-url\") ??\n (env ? ENV_URLS[env].api : undefined) ??\n process.env.CANARY_API_URL ??\n storedApiUrl ??\n \"https://api.trycanary.ai\";\n\n // Explicit --token flag always wins\n const explicitToken = getArgValue(argv, \"--token\");\n\n let token: string | null;\n if (explicitToken) {\n token = explicitToken;\n } else if (profile) {\n // --env was specified: profile token takes priority, env var is fallback\n token = (await readStoredToken(profile)) ?? process.env.CANARY_API_TOKEN ?? null;\n if (!token) {\n console.error(`Error: No token found for \"${env}\" profile.`);\n console.error(`Run: canary login --env ${env}`);\n process.exit(1);\n }\n } else {\n // No --env: env var first, then default profile\n token = process.env.CANARY_API_TOKEN ?? (await readStoredToken()) ?? null;\n if (!token) {\n console.error(\"Error: No API token found.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n }\n\n return { apiUrl, token };\n}\n"],"mappings":";;;AAAA,OAAO,QAAQ;AACf,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,aAAa;AAkBpB,IAAM,WAAW,KAAK,KAAK,GAAG,QAAQ,GAAG,WAAW,YAAY;AAChE,IAAM,YAAY,KAAK,KAAK,UAAU,WAAW;AAI1C,SAAS,YAAY,MAAgB,KAAiC;AAC3E,QAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9B,MAAI,UAAU,MAAM,SAAS,KAAK,SAAS,EAAG,QAAO;AACrD,SAAO,KAAK,QAAQ,CAAC;AACvB;AAEO,SAAS,QAAQ,SAAmB,OAA0B;AACnE,SAAO,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI,CAAC;AACjD;AAKO,SAAS,aAAa,KAAqB;AAChD,MAAI,QAAQ,UAAU,QAAQ,aAAc,QAAO;AACnD,MAAI,QAAQ,MAAO,QAAO;AAC1B,MAAI,QAAQ,QAAS,QAAO;AAC5B,SAAO;AACT;AAEA,IAAM,aAAa,CAAC,QAAQ,OAAO,OAAO;AAGnC,IAAM,WAAyD;AAAA,EACpE,MAAM;AAAA,IACJ,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AAAA,EACA,YAAY;AAAA,IACV,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AAAA,EACA,KAAK;AAAA,IACH,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AAAA,EACA,OAAO;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AACF;AAEA,SAAS,eAAe,MAAyC;AAC/D,SAAO,OAAO,SAAS,YAAY,SAAS,QAAQ,cAAc;AACpE;AAGA,eAAe,eAAiD;AAC9D,MAAI;AACF,UAAM,UAAU,MAAM,GAAG,SAAS,WAAW,MAAM;AACnD,UAAM,OAAO,KAAK,MAAM,OAAO;AAE/B,QAAI,eAAe,IAAI,GAAG;AACxB,aAAO;AAAA,IACT;AAGA,UAAM,SAAS;AACf,WAAO;AAAA,MACL,UAAU,EAAE,SAAS,OAAO;AAAA,MAC5B,SAAS;AAAA,IACX;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,eAAe,SAA8C;AACjF,QAAM,OAAO,MAAM,aAAa;AAChC,MAAI,CAAC,KAAM,QAAO;AAElB,QAAM,MAAM,WAAW,KAAK;AAC5B,SAAO,KAAK,SAAS,GAAG,KAAK;AAC/B;AAEA,eAAsB,gBAAgB,SAA0C;AAC9E,QAAM,OAAO,MAAM,eAAe,OAAO;AACzC,SAAO,MAAM,SAAS;AACxB;AAEA,eAAsB,iBAAiB,SAA0C;AAC/E,QAAM,OAAO,MAAM,eAAe,OAAO;AACzC,SAAO,MAAM,UAAU;AACzB;AAGA,eAAsB,sBAAyC;AAC7D,QAAM,OAAO,MAAM,aAAa;AAChC,MAAI,CAAC,KAAM,QAAO,CAAC;AACnB,SAAO,OAAO,OAAO,KAAK,QAAQ,EAC/B,IAAI,CAAC,MAAM,EAAE,KAAK,EAClB,OAAO,CAAC,MAAmB,CAAC,CAAC,CAAC;AACnC;AAEA,eAAsB,SAAS,MAAkB,SAAmC;AAClF,QAAM,GAAG,MAAM,UAAU,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAGzD,MAAI;AACJ,MAAI;AACF,UAAM,UAAU,MAAM,GAAG,SAAS,WAAW,MAAM;AACnD,UAAM,OAAO,KAAK,MAAM,OAAO;AAE/B,QAAI,eAAe,IAAI,GAAG;AACxB,iBAAW;AAAA,IACb,OAAO;AAEL,iBAAW;AAAA,QACT,UAAU,EAAE,SAAS,KAAmB;AAAA,QACxC,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF,QAAQ;AACN,eAAW,EAAE,UAAU,CAAC,GAAG,SAAS,UAAU;AAAA,EAChD;AAEA,QAAM,MAAM,WAAW;AACvB,WAAS,SAAS,GAAG,IAAI;AAGzB,MAAI,CAAC,SAAS,SAAS,SAAS,OAAO,GAAG;AACxC,aAAS,UAAU;AAAA,EACrB;AAEA,QAAM,GAAG,UAAU,WAAW,KAAK,UAAU,UAAU,MAAM,CAAC,GAAG,EAAE,UAAU,QAAQ,MAAM,IAAM,CAAC;AAClG,SAAO;AACT;AAUA,eAAsB,cAAc,MAAyC;AAC3E,QAAM,MAAM,YAAY,MAAM,OAAO;AACrC,QAAM,UAAU,MAAM,aAAa,GAAG,IAAI;AAE1C,MAAI,OAAO,CAAC,SAAS,GAAG,GAAG;AACzB,YAAQ,MAAM,wBAAwB,GAAG,EAAE;AAC3C,YAAQ,MAAM,uBAAuB,WAAW,KAAK,IAAI,CAAC,EAAE;AAC5D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,eAAe,MAAM,iBAAiB,OAAO;AACnD,QAAM,SACJ,YAAY,MAAM,WAAW,MAC5B,MAAM,SAAS,GAAG,EAAE,MAAM,WAC3B,QAAQ,IAAI,kBACZ,gBACA;AAGF,QAAM,gBAAgB,YAAY,MAAM,SAAS;AAEjD,MAAI;AACJ,MAAI,eAAe;AACjB,YAAQ;AAAA,EACV,WAAW,SAAS;AAElB,YAAS,MAAM,gBAAgB,OAAO,KAAM,QAAQ,IAAI,oBAAoB;AAC5E,QAAI,CAAC,OAAO;AACV,cAAQ,MAAM,8BAA8B,GAAG,YAAY;AAC3D,cAAQ,MAAM,2BAA2B,GAAG,EAAE;AAC9C,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,OAAO;AAEL,YAAQ,QAAQ,IAAI,oBAAqB,MAAM,gBAAgB,KAAM;AACrE,QAAI,CAAC,OAAO;AACV,cAAQ,MAAM,4BAA4B;AAC1C,cAAQ,MAAM,mBAAmB;AACjC,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAEA,SAAO,EAAE,QAAQ,MAAM;AACzB;","names":[]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
|
|
1
2
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
3
|
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
4
|
}) : x)(function(x) {
|
|
@@ -8,4 +9,4 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
8
9
|
export {
|
|
9
10
|
__require
|
|
10
11
|
};
|
|
11
|
-
//# sourceMappingURL=chunk-
|
|
12
|
+
//# sourceMappingURL=chunk-VLFUCAPZ.js.map
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
|
|
1
2
|
import {
|
|
2
3
|
LocalBrowserHost
|
|
3
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-FIQBGAKW.js";
|
|
4
5
|
import {
|
|
5
6
|
getArgValue,
|
|
6
7
|
hasFlag,
|
|
7
8
|
resolveConfig
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-PWWQGYFG.js";
|
|
10
|
+
import "./chunk-VLFUCAPZ.js";
|
|
10
11
|
|
|
11
12
|
// src/debug-workflow.ts
|
|
12
13
|
import process from "process";
|
|
@@ -237,4 +238,4 @@ async function runDebugWorkflow(argv) {
|
|
|
237
238
|
export {
|
|
238
239
|
runDebugWorkflow
|
|
239
240
|
};
|
|
240
|
-
//# sourceMappingURL=debug-workflow-
|
|
241
|
+
//# sourceMappingURL=debug-workflow-PT3OUR3V.js.map
|