@canaryai/cli 0.1.6 → 0.1.9
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-Z6I3ZXZL.js → chunk-NRMZHITS.js} +2 -3
- package/dist/{chunk-Z6I3ZXZL.js.map → chunk-NRMZHITS.js.map} +1 -1
- 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-SGNA6N2N.js +36 -0
- package/dist/chunk-SGNA6N2N.js.map +1 -0
- package/dist/feature-flag-43WAHIUZ.js +213 -0
- package/dist/feature-flag-43WAHIUZ.js.map +1 -0
- package/dist/index.js +1203 -8
- package/dist/index.js.map +1 -1
- package/dist/{local-browser-5LJ7UPOH.js → local-browser-REU2RIYX.js} +4 -5
- package/dist/{local-browser-5LJ7UPOH.js.map → local-browser-REU2RIYX.js.map} +1 -1
- package/dist/{mcp-P2B24MTM.js → mcp-5N5Z343W.js} +5 -6
- package/dist/{mcp-P2B24MTM.js.map → mcp-5N5Z343W.js.map} +1 -1
- package/dist/psql-7AEFGJWI.js +123 -0
- package/dist/psql-7AEFGJWI.js.map +1 -0
- package/dist/redis-BXYEPX4T.js +129 -0
- package/dist/redis-BXYEPX4T.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
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readStoredApiUrl,
|
|
3
|
+
readStoredToken
|
|
4
|
+
} from "./chunk-SGNA6N2N.js";
|
|
5
|
+
import "./chunk-DGUM43GV.js";
|
|
6
|
+
|
|
7
|
+
// src/redis.ts
|
|
8
|
+
import process from "process";
|
|
9
|
+
var MAX_COMMAND_SIZE = 1e4;
|
|
10
|
+
function getArgValue(argv, key) {
|
|
11
|
+
const index = argv.indexOf(key);
|
|
12
|
+
if (index === -1 || index >= argv.length - 1) return void 0;
|
|
13
|
+
return argv[index + 1];
|
|
14
|
+
}
|
|
15
|
+
function hasFlag(argv, ...flags) {
|
|
16
|
+
return flags.some((flag) => argv.includes(flag));
|
|
17
|
+
}
|
|
18
|
+
function formatOutput(data) {
|
|
19
|
+
if (data === null) {
|
|
20
|
+
return "(nil)";
|
|
21
|
+
}
|
|
22
|
+
if (typeof data === "string") {
|
|
23
|
+
return data;
|
|
24
|
+
}
|
|
25
|
+
if (typeof data === "number") {
|
|
26
|
+
return `(integer) ${data}`;
|
|
27
|
+
}
|
|
28
|
+
if (Array.isArray(data)) {
|
|
29
|
+
if (data.length === 0) {
|
|
30
|
+
return "(empty array)";
|
|
31
|
+
}
|
|
32
|
+
return data.map((item, index) => `${index + 1}) ${formatOutput(item)}`).join("\n");
|
|
33
|
+
}
|
|
34
|
+
if (typeof data === "object") {
|
|
35
|
+
return JSON.stringify(data, null, 2);
|
|
36
|
+
}
|
|
37
|
+
return String(data);
|
|
38
|
+
}
|
|
39
|
+
async function runRedis(argv) {
|
|
40
|
+
const storedApiUrl = await readStoredApiUrl();
|
|
41
|
+
const apiUrl = getArgValue(argv, "--api-url") ?? process.env.CANARY_API_URL ?? storedApiUrl ?? "https://api.trycanary.ai";
|
|
42
|
+
const token = getArgValue(argv, "--token") ?? process.env.CANARY_API_TOKEN ?? await readStoredToken();
|
|
43
|
+
const jsonOutput = hasFlag(argv, "--json");
|
|
44
|
+
const flagsWithValues = ["--api-url", "--token"];
|
|
45
|
+
const commandParts = [];
|
|
46
|
+
for (let i = 0; i < argv.length; i++) {
|
|
47
|
+
if (flagsWithValues.includes(argv[i])) {
|
|
48
|
+
i++;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (argv[i].startsWith("--")) continue;
|
|
52
|
+
commandParts.push(argv[i]);
|
|
53
|
+
}
|
|
54
|
+
const command = commandParts.join(" ");
|
|
55
|
+
if (!command) {
|
|
56
|
+
console.error("Error: No command provided.");
|
|
57
|
+
console.error("");
|
|
58
|
+
console.error("Usage: canary redis <command> [--json]");
|
|
59
|
+
console.error(' canary redis KEYS "job:*"');
|
|
60
|
+
console.error("");
|
|
61
|
+
console.error("Examples:");
|
|
62
|
+
console.error(" canary redis PING");
|
|
63
|
+
console.error(" canary redis INFO");
|
|
64
|
+
console.error(' canary redis KEYS "job:*"');
|
|
65
|
+
console.error(' canary redis HGETALL "job:123:status"');
|
|
66
|
+
console.error(' canary redis GET "some:key" --json');
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
if (command.length > MAX_COMMAND_SIZE) {
|
|
70
|
+
console.error(`Error: Command too large (${command.length} chars, max ${MAX_COMMAND_SIZE})`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
if (!token) {
|
|
74
|
+
console.error("Error: No API token found.");
|
|
75
|
+
console.error("Run: canary login");
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
const res = await fetch(`${apiUrl}/superadmin/redis`, {
|
|
80
|
+
method: "POST",
|
|
81
|
+
headers: {
|
|
82
|
+
Authorization: `Bearer ${token}`,
|
|
83
|
+
"Content-Type": "application/json"
|
|
84
|
+
},
|
|
85
|
+
body: JSON.stringify({ command })
|
|
86
|
+
});
|
|
87
|
+
if (!res.ok) {
|
|
88
|
+
const text = await res.text();
|
|
89
|
+
if (res.status === 401) {
|
|
90
|
+
console.error("Error: Unauthorized. Your session may have expired.");
|
|
91
|
+
console.error("Run: canary login");
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
if (res.status === 404) {
|
|
95
|
+
console.error("Error: Endpoint not found. The redis feature may not be deployed to this environment.");
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
const errorJson = JSON.parse(text);
|
|
100
|
+
console.error(`Error: ${errorJson.error ?? text}`);
|
|
101
|
+
} catch {
|
|
102
|
+
console.error(`Error (${res.status}): ${text || res.statusText}`);
|
|
103
|
+
}
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
const json_response = await res.json();
|
|
107
|
+
if (!json_response.ok) {
|
|
108
|
+
console.error(`Error: ${json_response.error}`);
|
|
109
|
+
if (json_response.code === "BLOCKED") {
|
|
110
|
+
console.error("The Redis debugging feature has been disabled due to this blocked command.");
|
|
111
|
+
}
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
if (jsonOutput) {
|
|
115
|
+
console.log(JSON.stringify(json_response.data, null, 2));
|
|
116
|
+
} else {
|
|
117
|
+
console.log(formatOutput(json_response.data));
|
|
118
|
+
console.log(`
|
|
119
|
+
Time: ${json_response.durationMs}ms`);
|
|
120
|
+
}
|
|
121
|
+
} catch (err) {
|
|
122
|
+
console.error(`Failed to execute command: ${err}`);
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
export {
|
|
127
|
+
runRedis
|
|
128
|
+
};
|
|
129
|
+
//# sourceMappingURL=redis-BXYEPX4T.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/redis.ts"],"sourcesContent":["/**\n * CLI Redis Passthrough\n *\n * Allows superadmins to execute read-only Redis commands against the production cache.\n */\n\nimport process from \"node:process\";\nimport { readStoredToken, readStoredApiUrl } from \"./auth.js\";\n\nconst MAX_COMMAND_SIZE = 10_000; // 10KB - reasonable for interactive use\n\ntype RedisResponse = {\n ok: boolean;\n data?: unknown;\n durationMs?: number;\n error?: string;\n code?: \"DISABLED\" | \"BLOCKED\";\n};\n\nfunction 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\nfunction hasFlag(argv: string[], ...flags: string[]): boolean {\n return flags.some((flag) => argv.includes(flag));\n}\n\n/**\n * Formats Redis response for display.\n */\nfunction formatOutput(data: unknown): string {\n if (data === null) {\n return \"(nil)\";\n }\n\n if (typeof data === \"string\") {\n return data;\n }\n\n if (typeof data === \"number\") {\n return `(integer) ${data}`;\n }\n\n if (Array.isArray(data)) {\n if (data.length === 0) {\n return \"(empty array)\";\n }\n return data\n .map((item, index) => `${index + 1}) ${formatOutput(item)}`)\n .join(\"\\n\");\n }\n\n if (typeof data === \"object\") {\n return JSON.stringify(data, null, 2);\n }\n\n return String(data);\n}\n\nexport async function runRedis(argv: string[]): Promise<void> {\n const storedApiUrl = await readStoredApiUrl();\n const apiUrl =\n getArgValue(argv, \"--api-url\") ??\n process.env.CANARY_API_URL ??\n storedApiUrl ??\n \"https://api.trycanary.ai\";\n\n const token =\n getArgValue(argv, \"--token\") ?? process.env.CANARY_API_TOKEN ?? (await readStoredToken());\n\n const jsonOutput = hasFlag(argv, \"--json\");\n\n // Get command: remaining args after filtering flags\n const flagsWithValues = [\"--api-url\", \"--token\"];\n const commandParts: string[] = [];\n for (let i = 0; i < argv.length; i++) {\n if (flagsWithValues.includes(argv[i])) {\n i++; // Skip flag value\n continue;\n }\n if (argv[i].startsWith(\"--\")) continue;\n commandParts.push(argv[i]);\n }\n const command = commandParts.join(\" \");\n\n if (!command) {\n console.error(\"Error: No command provided.\");\n console.error(\"\");\n console.error(\"Usage: canary redis <command> [--json]\");\n console.error(' canary redis KEYS \"job:*\"');\n console.error(\"\");\n console.error(\"Examples:\");\n console.error(\" canary redis PING\");\n console.error(\" canary redis INFO\");\n console.error(' canary redis KEYS \"job:*\"');\n console.error(' canary redis HGETALL \"job:123:status\"');\n console.error(' canary redis GET \"some:key\" --json');\n process.exit(1);\n }\n\n if (command.length > MAX_COMMAND_SIZE) {\n console.error(`Error: Command too large (${command.length} chars, max ${MAX_COMMAND_SIZE})`);\n process.exit(1);\n }\n\n if (!token) {\n console.error(\"Error: No API token found.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n\n try {\n const res = await fetch(`${apiUrl}/superadmin/redis`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ command }),\n });\n\n // Handle HTTP errors before parsing JSON\n if (!res.ok) {\n const text = await res.text();\n\n if (res.status === 401) {\n console.error(\"Error: Unauthorized. Your session may have expired.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n\n if (res.status === 404) {\n console.error(\"Error: Endpoint not found. The redis feature may not be deployed to this environment.\");\n process.exit(1);\n }\n\n // Try to parse error as JSON, fallback to raw text\n try {\n const errorJson = JSON.parse(text) as { error?: string };\n console.error(`Error: ${errorJson.error ?? text}`);\n } catch {\n console.error(`Error (${res.status}): ${text || res.statusText}`);\n }\n process.exit(1);\n }\n\n const json_response = (await res.json()) as RedisResponse;\n\n if (!json_response.ok) {\n console.error(`Error: ${json_response.error}`);\n if (json_response.code === \"BLOCKED\") {\n console.error(\"The Redis debugging feature has been disabled due to this blocked command.\");\n }\n process.exit(1);\n }\n\n if (jsonOutput) {\n console.log(JSON.stringify(json_response.data, null, 2));\n } else {\n console.log(formatOutput(json_response.data));\n console.log(`\\nTime: ${json_response.durationMs}ms`);\n }\n } catch (err) {\n console.error(`Failed to execute command: ${err}`);\n process.exit(1);\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/runner/preload.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
import {
|
|
3
2
|
alreadyPatched,
|
|
4
3
|
classifyFailure,
|
|
@@ -8,10 +7,10 @@ import {
|
|
|
8
7
|
markPatched,
|
|
9
8
|
recordHealingEvent,
|
|
10
9
|
setEventLogPath
|
|
11
|
-
} from "../chunk-
|
|
10
|
+
} from "../chunk-ROTCL5WO.js";
|
|
12
11
|
import {
|
|
13
12
|
__require
|
|
14
|
-
} from "../chunk-
|
|
13
|
+
} from "../chunk-DGUM43GV.js";
|
|
15
14
|
|
|
16
15
|
// src/runner/instrumentation.ts
|
|
17
16
|
import Module from "module";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/runner/instrumentation.ts","../../src/runner/preload.ts"],"sourcesContent":["import Module from \"node:module\";\nimport { createRequire } from \"node:module\";\nimport { pathToFileURL } from \"node:url\";\nimport { loadCanaryConfig } from \"./config\";\nimport { classifyFailure, executeHealActions, type FailureContext } from \"./healer\";\nimport { alreadyPatched, markPatched, recordHealingEvent, setEventLogPath } from \"./state\";\n\nlet DEBUG = false;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst requireFn: any = typeof require !== \"undefined\" ? require : createRequire(import.meta.url);\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 installInstrumentation(): void {\n if (alreadyPatched()) return;\n\n const config = loadCanaryConfig();\n DEBUG = !!config.debug;\n if (!config.enabled) {\n markPatched();\n return;\n }\n\n setEventLogPath(config.eventLogPath);\n if (!ensurePlaywrightVersion(config.allowedPlaywrightVersion)) {\n recordHealingEvent({\n kind: \"unknown\",\n action: \"playwright_version_mismatch\",\n errorMessage: \"Playwright version does not match CANARY_PLAYWRIGHT_VERSION\",\n healed: false,\n });\n markPatched();\n return;\n }\n hookPlaywrightModuleLoad();\n\n if (DEBUG) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] instrumentation installed (eventLog=${config.eventLogPath})`);\n }\n\n recordHealingEvent({\n kind: \"unknown\",\n action: \"instrumentation_ready\",\n healed: false,\n });\n markPatched();\n}\n\nfunction hookPlaywrightModuleLoad(): void {\n // Prevent double-hooking.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const mod = Module as any;\n const originalLoad: typeof mod._load = mod._canaryOriginalLoad ?? mod._load;\n if (!mod._canaryOriginalLoad) {\n mod._canaryOriginalLoad = originalLoad;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n mod._load = function patchedLoad(request: string, parent: any, isMain: boolean) {\n if (request === \"@playwright/test\" || request === \"playwright/test\") {\n if (DEBUG) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] patching playwright module (request=${request})`);\n }\n const real = originalLoad.call(this, request, parent, isMain);\n return createPatchedPlaywright(real);\n }\n return originalLoad.call(this, request, parent, isMain);\n };\n}\n\ntype PlaywrightExports = {\n test: { extend: typeof import(\"@playwright/test\").test.extend };\n expect: typeof import(\"@playwright/test\").expect;\n [key: string]: unknown;\n};\n\nfunction createPatchedPlaywright(real: PlaywrightExports): PlaywrightExports {\n if ((real as { __canaryPatched?: boolean }).__canaryPatched) return real;\n\n const extended = 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);\n await use(wrappedPage);\n },\n });\n\n const patched: PlaywrightExports = {\n ...real,\n test: extended,\n expect: wrapExpect(real.expect),\n };\n\n (patched as { __canaryPatched?: boolean }).__canaryPatched = true;\n if (DEBUG) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] playwright patched`);\n }\n return patched;\n}\n\nfunction ensurePlaywrightVersion(expected?: string): boolean {\n if (!expected) return true;\n try {\n const pkg = requireFn(\"playwright/package.json\") as { version?: string };\n if (pkg.version && pkg.version.startsWith(expected)) {\n return true;\n }\n // eslint-disable-next-line no-console\n console.warn(`[canary] Playwright version mismatch. Expected ${expected}, found ${pkg.version ?? \"unknown\"}`);\n return false;\n } catch {\n return true;\n }\n}\n\nfunction wrapExpect(expectImpl: PlaywrightExports[\"expect\"]): 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[]) => runMatcherWithHealing({ matcher: value as (...args: unknown[]) => unknown, matcherName: prop, args, expectTarget: target, mode });\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): 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);\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 });\n };\n }\n }\n\n return value;\n },\n });\n}\n\nfunction wrapLocator(locator: 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: undefined,\n invoke: () => (value as (...args: unknown[]) => unknown).apply(target, args),\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));\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};\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 (DEBUG) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] failure intercepted: kind=${ctx.kind} action=${ctx.action} reason=${decision.healable ? decision.reason ?? \"healable\" : decision.reason}`);\n }\n if (!decision.healable) {\n throw error;\n }\n\n // Get test context for correlation before passing to heal actions\n const testContext = getTestContext();\n\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: outcome.healed,\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 && !outcome.shouldRetryOriginal) {\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 (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 (DEBUG) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] retry_original failed for action=${ctx.action}: ${retryInfo.message ?? retryError}`);\n }\n throw retryError;\n }\n }\n\n recordHealingEvent(summaryBase);\n throw error;\n }\n}\n\nasync function runMatcherWithHealing(params: {\n matcher: (...args: unknown[]) => unknown;\n matcherName: string;\n args: unknown[];\n expectTarget: unknown;\n mode: \"hard\" | \"soft\";\n}): Promise<unknown> {\n const { matcher, matcherName, args, expectTarget, mode } = 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 // Try to get test context for correlation\n const testContext = getTestContext();\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.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 loadTestSource(filePath?: string): string | undefined {\n if (!filePath) return undefined;\n try {\n return requireFn(\"fs\").readFileSync(filePath, \"utf-8\") as string;\n } catch {\n return undefined;\n }\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 (\"selector\" in (candidate as Record<string, unknown>) && typeof (candidate as Record<string, unknown>).selector === \"string\") {\n return String((candidate as Record<string, unknown>).selector);\n }\n }\n return safeTargetString(candidate);\n}\n\n/**\n * Try to get test context from Playwright's test.info().\n * This is used to correlate healing events with tests in the report.\n */\nfunction getTestContext(): { testFile?: string; testTitle?: string } | undefined {\n try {\n // Try to dynamically require @playwright/test and call test.info()\n const playwright = requireFn(\"@playwright/test\") as { test?: { info?: () => { file?: string; title?: string } } };\n if (playwright?.test?.info) {\n const info = playwright.test.info();\n if (info) {\n return {\n testFile: info.file,\n testTitle: info.title,\n };\n }\n }\n } catch {\n // Not in a test context or @playwright/test not available\n }\n return undefined;\n}\n","import { installInstrumentation } from \"./instrumentation\";\nimport { getEventLog } from \"./state\";\n\n// Ensure globals are initialized and patch Playwright classes in each worker process.\ngetEventLog();\ninstallInstrumentation();\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/runner/instrumentation.ts","../../src/runner/preload.ts"],"sourcesContent":["import Module from \"node:module\";\nimport { createRequire } from \"node:module\";\nimport { pathToFileURL } from \"node:url\";\nimport { loadCanaryConfig } from \"./config\";\nimport { classifyFailure, executeHealActions, type FailureContext } from \"./healer\";\nimport { alreadyPatched, markPatched, recordHealingEvent, setEventLogPath } from \"./state\";\n\nlet DEBUG = false;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst requireFn: any = typeof require !== \"undefined\" ? require : createRequire(import.meta.url);\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 installInstrumentation(): void {\n if (alreadyPatched()) return;\n\n const config = loadCanaryConfig();\n DEBUG = !!config.debug;\n if (!config.enabled) {\n markPatched();\n return;\n }\n\n setEventLogPath(config.eventLogPath);\n if (!ensurePlaywrightVersion(config.allowedPlaywrightVersion)) {\n recordHealingEvent({\n kind: \"unknown\",\n action: \"playwright_version_mismatch\",\n errorMessage: \"Playwright version does not match CANARY_PLAYWRIGHT_VERSION\",\n healed: false,\n });\n markPatched();\n return;\n }\n hookPlaywrightModuleLoad();\n\n if (DEBUG) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] instrumentation installed (eventLog=${config.eventLogPath})`);\n }\n\n recordHealingEvent({\n kind: \"unknown\",\n action: \"instrumentation_ready\",\n healed: false,\n });\n markPatched();\n}\n\nfunction hookPlaywrightModuleLoad(): void {\n // Prevent double-hooking.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const mod = Module as any;\n const originalLoad: typeof mod._load = mod._canaryOriginalLoad ?? mod._load;\n if (!mod._canaryOriginalLoad) {\n mod._canaryOriginalLoad = originalLoad;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n mod._load = function patchedLoad(request: string, parent: any, isMain: boolean) {\n if (request === \"@playwright/test\" || request === \"playwright/test\") {\n if (DEBUG) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] patching playwright module (request=${request})`);\n }\n const real = originalLoad.call(this, request, parent, isMain);\n return createPatchedPlaywright(real);\n }\n return originalLoad.call(this, request, parent, isMain);\n };\n}\n\ntype PlaywrightExports = {\n test: { extend: typeof import(\"@playwright/test\").test.extend };\n expect: typeof import(\"@playwright/test\").expect;\n [key: string]: unknown;\n};\n\nfunction createPatchedPlaywright(real: PlaywrightExports): PlaywrightExports {\n if ((real as { __canaryPatched?: boolean }).__canaryPatched) return real;\n\n const extended = 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);\n await use(wrappedPage);\n },\n });\n\n const patched: PlaywrightExports = {\n ...real,\n test: extended,\n expect: wrapExpect(real.expect),\n };\n\n (patched as { __canaryPatched?: boolean }).__canaryPatched = true;\n if (DEBUG) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] playwright patched`);\n }\n return patched;\n}\n\nfunction ensurePlaywrightVersion(expected?: string): boolean {\n if (!expected) return true;\n try {\n const pkg = requireFn(\"playwright/package.json\") as { version?: string };\n if (pkg.version && pkg.version.startsWith(expected)) {\n return true;\n }\n // eslint-disable-next-line no-console\n console.warn(`[canary] Playwright version mismatch. Expected ${expected}, found ${pkg.version ?? \"unknown\"}`);\n return false;\n } catch {\n return true;\n }\n}\n\nfunction wrapExpect(expectImpl: PlaywrightExports[\"expect\"]): 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[]) => runMatcherWithHealing({ matcher: value as (...args: unknown[]) => unknown, matcherName: prop, args, expectTarget: target, mode });\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): 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);\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 });\n };\n }\n }\n\n return value;\n },\n });\n}\n\nfunction wrapLocator(locator: 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: undefined,\n invoke: () => (value as (...args: unknown[]) => unknown).apply(target, args),\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));\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};\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 (DEBUG) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] failure intercepted: kind=${ctx.kind} action=${ctx.action} reason=${decision.healable ? decision.reason ?? \"healable\" : decision.reason}`);\n }\n if (!decision.healable) {\n throw error;\n }\n\n // Get test context for correlation before passing to heal actions\n const testContext = getTestContext();\n\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: outcome.healed,\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 && !outcome.shouldRetryOriginal) {\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 (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 (DEBUG) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] retry_original failed for action=${ctx.action}: ${retryInfo.message ?? retryError}`);\n }\n throw retryError;\n }\n }\n\n recordHealingEvent(summaryBase);\n throw error;\n }\n}\n\nasync function runMatcherWithHealing(params: {\n matcher: (...args: unknown[]) => unknown;\n matcherName: string;\n args: unknown[];\n expectTarget: unknown;\n mode: \"hard\" | \"soft\";\n}): Promise<unknown> {\n const { matcher, matcherName, args, expectTarget, mode } = 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 // Try to get test context for correlation\n const testContext = getTestContext();\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.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 loadTestSource(filePath?: string): string | undefined {\n if (!filePath) return undefined;\n try {\n return requireFn(\"fs\").readFileSync(filePath, \"utf-8\") as string;\n } catch {\n return undefined;\n }\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 (\"selector\" in (candidate as Record<string, unknown>) && typeof (candidate as Record<string, unknown>).selector === \"string\") {\n return String((candidate as Record<string, unknown>).selector);\n }\n }\n return safeTargetString(candidate);\n}\n\n/**\n * Try to get test context from Playwright's test.info().\n * This is used to correlate healing events with tests in the report.\n */\nfunction getTestContext(): { testFile?: string; testTitle?: string } | undefined {\n try {\n // Try to dynamically require @playwright/test and call test.info()\n const playwright = requireFn(\"@playwright/test\") as { test?: { info?: () => { file?: string; title?: string } } };\n if (playwright?.test?.info) {\n const info = playwright.test.info();\n if (info) {\n return {\n testFile: info.file,\n testTitle: info.title,\n };\n }\n }\n } catch {\n // Not in a test context or @playwright/test not available\n }\n return undefined;\n}\n","import { installInstrumentation } from \"./instrumentation\";\nimport { getEventLog } from \"./state\";\n\n// Ensure globals are initialized and patch Playwright classes in each worker process.\ngetEventLog();\ninstallInstrumentation();\n"],"mappings":"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","names":[]}
|
package/dist/test.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
import {
|
|
3
2
|
classifyFailure,
|
|
4
3
|
executeHealActions,
|
|
@@ -7,10 +6,10 @@ import {
|
|
|
7
6
|
markPatched,
|
|
8
7
|
recordHealingEvent,
|
|
9
8
|
setEventLogPath
|
|
10
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-ROTCL5WO.js";
|
|
11
10
|
import {
|
|
12
11
|
__require
|
|
13
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-DGUM43GV.js";
|
|
14
13
|
|
|
15
14
|
// src/test.ts
|
|
16
15
|
import * as playwright from "@playwright/test";
|
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.9",
|
|
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":[]}
|