@canaryai/cli 0.1.6 → 0.1.11
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/README.md +52 -1
- package/dist/bin.js +1 -6
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-7OCVIDC7.js → chunk-DGUM43GV.js} +1 -2
- package/dist/{chunk-55MFLJD7.js → chunk-G2X3H7AM.js} +1 -3
- package/dist/{chunk-55MFLJD7.js.map → chunk-G2X3H7AM.js.map} +1 -1
- package/dist/chunk-HJ2JWIJ7.js +91 -0
- package/dist/chunk-HJ2JWIJ7.js.map +1 -0
- package/dist/{chunk-7AP5KRVU.js → chunk-ROTCL5WO.js} +1 -3
- package/dist/{chunk-7AP5KRVU.js.map → chunk-ROTCL5WO.js.map} +1 -1
- package/dist/{chunk-Z6I3ZXZL.js → chunk-VYBCH4ZP.js} +2 -3
- package/dist/{chunk-Z6I3ZXZL.js.map → chunk-VYBCH4ZP.js.map} +1 -1
- package/dist/feature-flag-PN5IFFQR.js +226 -0
- package/dist/feature-flag-PN5IFFQR.js.map +1 -0
- package/dist/index.js +1207 -8
- package/dist/index.js.map +1 -1
- package/dist/knobs-DAG7HD2F.js +286 -0
- package/dist/knobs-DAG7HD2F.js.map +1 -0
- package/dist/{local-browser-5LJ7UPOH.js → local-browser-VOBIUIGT.js} +4 -5
- package/dist/{local-browser-5LJ7UPOH.js.map → local-browser-VOBIUIGT.js.map} +1 -1
- package/dist/{mcp-P2B24MTM.js → mcp-I6FCGDDR.js} +5 -6
- package/dist/{mcp-P2B24MTM.js.map → mcp-I6FCGDDR.js.map} +1 -1
- package/dist/psql-A3BADRQN.js +124 -0
- package/dist/psql-A3BADRQN.js.map +1 -0
- package/dist/redis-N2DSDDQU.js +130 -0
- package/dist/redis-N2DSDDQU.js.map +1 -0
- package/dist/runner/preload.js +2 -3
- package/dist/runner/preload.js.map +1 -1
- package/dist/test.js +2 -3
- package/dist/test.js.map +1 -1
- package/package.json +3 -4
- package/dist/bin.d.ts +0 -2
- package/dist/chunk-UBYYNMML.js +0 -21
- package/dist/chunk-UBYYNMML.js.map +0 -1
- package/dist/chunk-YA43CE6P.js +0 -781
- package/dist/chunk-YA43CE6P.js.map +0 -1
- /package/dist/{chunk-7OCVIDC7.js.map → chunk-DGUM43GV.js.map} +0 -0
package/dist/test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/test.ts","../src/runner/wrapper.ts"],"sourcesContent":["import * as playwright from \"@playwright/test\";\nimport { loadCanaryConfig } from \"./runner/config\";\nimport { getEventLog, markPatched, recordHealingEvent, setEventLogPath } from \"./runner/state\";\nimport { wrapPlaywright, type PlaywrightExports } from \"./runner/wrapper\";\n\nconst config = loadCanaryConfig();\nsetEventLogPath(config.eventLogPath);\ngetEventLog();\n\nconst patched = wrapPlaywright(playwright as unknown as PlaywrightExports, { debug: !!config.debug });\nmarkPatched();\n\nrecordHealingEvent({\n kind: \"unknown\",\n action: \"module_wrap_ready\",\n healed: false,\n});\n\nconst {\n test,\n expect,\n chromium,\n firefox,\n webkit,\n devices,\n request,\n selectors,\n defineConfig,\n} = patched as typeof import(\"@playwright/test\");\n\nexport { test, expect, chromium, firefox, webkit, devices, request, selectors, defineConfig };\nexport type * from \"@playwright/test\";\n","import { 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\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\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\ntype WrapOptions = {\n debug?: boolean;\n};\n\nconst 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\nconst 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\nconst LOCATOR_FACTORIES = new Set([\n \"locator\",\n \"getByRole\",\n \"getByText\",\n \"getByLabel\",\n \"getByPlaceholder\",\n \"getByAltText\",\n \"getByTitle\",\n \"getByTestId\",\n \"frameLocator\",\n]);\n\nconst 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\nexport function wrapPlaywright(real: PlaywrightExports, options: WrapOptions): PlaywrightExports {\n if ((real as { __canaryPatched?: boolean }).__canaryPatched) return real;\n\n const patchedTest = real.test.extend({\n page: async ({ page }, use) => {\n recordHealingEvent({\n kind: \"page\",\n action: \"fixture_initialized\",\n healed: false,\n });\n const wrappedPage = wrapPage(page, options);\n await use(wrappedPage);\n },\n });\n\n const patched: PlaywrightExports = {\n ...real,\n test: patchedTest,\n expect: wrapExpect(real.expect, options),\n };\n\n (patched as { __canaryPatched?: boolean }).__canaryPatched = true;\n if (options.debug) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] playwright surface wrapped`);\n }\n return patched;\n}\n\nfunction 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\nfunction 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\nfunction 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: 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\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\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 the agent marked healing complete,\n // consider it healed and return successfully without retrying the original\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\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\nasync function runMatcherWithHealing(params: MatcherContext): Promise<unknown> {\n const { matcher, matcherName, args, expectTarget, mode, debug } = params;\n\n const invoke = () => matcher.apply(expectTarget, args);\n\n // Skip healing for soft assertions to avoid altering semantics.\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\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"],"mappings":"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","names":["playwright","patched"]}
|
|
1
|
+
{"version":3,"sources":["../src/test.ts","../src/runner/wrapper.ts"],"sourcesContent":["import * as playwright from \"@playwright/test\";\nimport { loadCanaryConfig } from \"./runner/config\";\nimport { getEventLog, markPatched, recordHealingEvent, setEventLogPath } from \"./runner/state\";\nimport { wrapPlaywright, type PlaywrightExports } from \"./runner/wrapper\";\n\nconst config = loadCanaryConfig();\nsetEventLogPath(config.eventLogPath);\ngetEventLog();\n\nconst patched = wrapPlaywright(playwright as unknown as PlaywrightExports, { debug: !!config.debug });\nmarkPatched();\n\nrecordHealingEvent({\n kind: \"unknown\",\n action: \"module_wrap_ready\",\n healed: false,\n});\n\nconst {\n test,\n expect,\n chromium,\n firefox,\n webkit,\n devices,\n request,\n selectors,\n defineConfig,\n} = patched as typeof import(\"@playwright/test\");\n\nexport { test, expect, chromium, firefox, webkit, devices, request, selectors, defineConfig };\nexport type * from \"@playwright/test\";\n","import { 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\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\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\ntype WrapOptions = {\n debug?: boolean;\n};\n\nconst 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\nconst 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\nconst LOCATOR_FACTORIES = new Set([\n \"locator\",\n \"getByRole\",\n \"getByText\",\n \"getByLabel\",\n \"getByPlaceholder\",\n \"getByAltText\",\n \"getByTitle\",\n \"getByTestId\",\n \"frameLocator\",\n]);\n\nconst 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\nexport function wrapPlaywright(real: PlaywrightExports, options: WrapOptions): PlaywrightExports {\n if ((real as { __canaryPatched?: boolean }).__canaryPatched) return real;\n\n const patchedTest = real.test.extend({\n page: async ({ page }, use) => {\n recordHealingEvent({\n kind: \"page\",\n action: \"fixture_initialized\",\n healed: false,\n });\n const wrappedPage = wrapPage(page, options);\n await use(wrappedPage);\n },\n });\n\n const patched: PlaywrightExports = {\n ...real,\n test: patchedTest,\n expect: wrapExpect(real.expect, options),\n };\n\n (patched as { __canaryPatched?: boolean }).__canaryPatched = true;\n if (options.debug) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] playwright surface wrapped`);\n }\n return patched;\n}\n\nfunction 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\nfunction 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\nfunction 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: 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\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\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 the agent marked healing complete,\n // consider it healed and return successfully without retrying the original\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\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\nasync function runMatcherWithHealing(params: MatcherContext): Promise<unknown> {\n const { matcher, matcherName, args, expectTarget, mode, debug } = params;\n\n const invoke = () => matcher.apply(expectTarget, args);\n\n // Skip healing for soft assertions to avoid altering semantics.\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\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"],"mappings":"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","names":["playwright","patched"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canaryai/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -29,12 +29,11 @@
|
|
|
29
29
|
"access": "public"
|
|
30
30
|
},
|
|
31
31
|
"scripts": {
|
|
32
|
-
"build": "tsup",
|
|
32
|
+
"build": "rm -rf dist && tsup",
|
|
33
33
|
"dev": "tsup --watch",
|
|
34
|
-
"prepublishOnly": "bun run build",
|
|
35
34
|
"typecheck": "tsc --noEmit",
|
|
36
35
|
"test": "playwright test -c ./playwright.config.ts",
|
|
37
|
-
"unit": "bun test src/runner/healer.test.ts src/intent/intent-parser.test.ts src/run.test.ts",
|
|
36
|
+
"unit": "bun test src/runner/healer.test.ts src/intent/intent-parser.test.ts src/run.test.ts src/jwt.test.ts",
|
|
38
37
|
"canary": "bun run build && node ./dist/index.js test",
|
|
39
38
|
"canary:dev": "bun --bun ./src/index.ts test"
|
|
40
39
|
},
|
package/dist/bin.d.ts
DELETED
package/dist/chunk-UBYYNMML.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/auth.ts
|
|
4
|
-
import fs from "fs/promises";
|
|
5
|
-
import os from "os";
|
|
6
|
-
import path from "path";
|
|
7
|
-
async function readStoredToken() {
|
|
8
|
-
try {
|
|
9
|
-
const filePath = path.join(os.homedir(), ".config", "canary-cli", "auth.json");
|
|
10
|
-
const content = await fs.readFile(filePath, "utf8");
|
|
11
|
-
const parsed = JSON.parse(content);
|
|
12
|
-
return typeof parsed.token === "string" ? parsed.token : null;
|
|
13
|
-
} catch {
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export {
|
|
19
|
-
readStoredToken
|
|
20
|
-
};
|
|
21
|
-
//# sourceMappingURL=chunk-UBYYNMML.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/auth.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\ntype StoredAuth = {\n token?: string;\n};\n\nexport async function readStoredToken(): Promise<string | null> {\n try {\n const filePath = path.join(os.homedir(), \".config\", \"canary-cli\", \"auth.json\");\n const content = await fs.readFile(filePath, \"utf8\");\n const parsed = JSON.parse(content) as StoredAuth;\n return typeof parsed.token === \"string\" ? parsed.token : null;\n } catch {\n return null;\n }\n}\n"],"mappings":";;;AAAA,OAAO,QAAQ;AACf,OAAO,QAAQ;AACf,OAAO,UAAU;AAMjB,eAAsB,kBAA0C;AAC9D,MAAI;AACF,UAAM,WAAW,KAAK,KAAK,GAAG,QAAQ,GAAG,WAAW,cAAc,WAAW;AAC7E,UAAM,UAAU,MAAM,GAAG,SAAS,UAAU,MAAM;AAClD,UAAM,SAAS,KAAK,MAAM,OAAO;AACjC,WAAO,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;AAAA,EAC3D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
|