@agentic-qe/v3 3.0.0-alpha.34 → 3.0.0-alpha.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/bundle.js +2880 -177
- package/dist/cli/index.js +14 -10
- package/dist/cli/index.js.map +1 -1
- package/dist/domains/test-execution/index.d.ts +1 -1
- package/dist/domains/test-execution/index.d.ts.map +1 -1
- package/dist/domains/test-execution/index.js +1 -1
- package/dist/domains/test-execution/index.js.map +1 -1
- package/dist/domains/test-execution/services/auth-state-manager.d.ts +175 -0
- package/dist/domains/test-execution/services/auth-state-manager.d.ts.map +1 -0
- package/dist/domains/test-execution/services/auth-state-manager.js +376 -0
- package/dist/domains/test-execution/services/auth-state-manager.js.map +1 -0
- package/dist/domains/test-execution/services/e2e-runner.d.ts +145 -7
- package/dist/domains/test-execution/services/e2e-runner.d.ts.map +1 -1
- package/dist/domains/test-execution/services/e2e-runner.js +687 -40
- package/dist/domains/test-execution/services/e2e-runner.js.map +1 -1
- package/dist/domains/test-execution/services/index.d.ts +2 -0
- package/dist/domains/test-execution/services/index.d.ts.map +1 -1
- package/dist/domains/test-execution/services/index.js +2 -0
- package/dist/domains/test-execution/services/index.js.map +1 -1
- package/dist/domains/test-execution/services/network-mocker.d.ts +187 -0
- package/dist/domains/test-execution/services/network-mocker.d.ts.map +1 -0
- package/dist/domains/test-execution/services/network-mocker.js +283 -0
- package/dist/domains/test-execution/services/network-mocker.js.map +1 -0
- package/dist/domains/test-execution/types/e2e-step.types.d.ts +16 -2
- package/dist/domains/test-execution/types/e2e-step.types.d.ts.map +1 -1
- package/dist/domains/test-execution/types/e2e-step.types.js +33 -5
- package/dist/domains/test-execution/types/e2e-step.types.js.map +1 -1
- package/dist/domains/visual-accessibility/services/accessibility-tester.d.ts +90 -11
- package/dist/domains/visual-accessibility/services/accessibility-tester.d.ts.map +1 -1
- package/dist/domains/visual-accessibility/services/accessibility-tester.js +336 -24
- package/dist/domains/visual-accessibility/services/accessibility-tester.js.map +1 -1
- package/dist/domains/visual-accessibility/services/viewport-capture.d.ts +51 -0
- package/dist/domains/visual-accessibility/services/viewport-capture.d.ts.map +1 -1
- package/dist/domains/visual-accessibility/services/viewport-capture.js +164 -0
- package/dist/domains/visual-accessibility/services/viewport-capture.js.map +1 -1
- package/dist/domains/visual-accessibility/services/visual-regression.d.ts +49 -6
- package/dist/domains/visual-accessibility/services/visual-regression.d.ts.map +1 -1
- package/dist/domains/visual-accessibility/services/visual-regression.js +158 -11
- package/dist/domains/visual-accessibility/services/visual-regression.js.map +1 -1
- package/dist/integrations/browser/agent-browser/client.d.ts +200 -0
- package/dist/integrations/browser/agent-browser/client.d.ts.map +1 -0
- package/dist/integrations/browser/agent-browser/client.js +837 -0
- package/dist/integrations/browser/agent-browser/client.js.map +1 -0
- package/dist/integrations/browser/agent-browser/command-executor.d.ts +149 -0
- package/dist/integrations/browser/agent-browser/command-executor.d.ts.map +1 -0
- package/dist/integrations/browser/agent-browser/command-executor.js +381 -0
- package/dist/integrations/browser/agent-browser/command-executor.js.map +1 -0
- package/dist/integrations/browser/agent-browser/index.d.ts +9 -0
- package/dist/integrations/browser/agent-browser/index.d.ts.map +1 -0
- package/dist/integrations/browser/agent-browser/index.js +9 -0
- package/dist/integrations/browser/agent-browser/index.js.map +1 -0
- package/dist/integrations/browser/agent-browser/session-manager.d.ts +105 -0
- package/dist/integrations/browser/agent-browser/session-manager.d.ts.map +1 -0
- package/dist/integrations/browser/agent-browser/session-manager.js +272 -0
- package/dist/integrations/browser/agent-browser/session-manager.js.map +1 -0
- package/dist/integrations/browser/agent-browser/snapshot-parser.d.ts +90 -0
- package/dist/integrations/browser/agent-browser/snapshot-parser.d.ts.map +1 -0
- package/dist/integrations/browser/agent-browser/snapshot-parser.js +302 -0
- package/dist/integrations/browser/agent-browser/snapshot-parser.js.map +1 -0
- package/dist/integrations/browser/client-factory.d.ts +144 -0
- package/dist/integrations/browser/client-factory.d.ts.map +1 -0
- package/dist/integrations/browser/client-factory.js +323 -0
- package/dist/integrations/browser/client-factory.js.map +1 -0
- package/dist/integrations/browser/index.d.ts +54 -0
- package/dist/integrations/browser/index.d.ts.map +1 -0
- package/dist/integrations/browser/index.js +64 -0
- package/dist/integrations/browser/index.js.map +1 -0
- package/dist/integrations/browser/types.d.ts +437 -0
- package/dist/integrations/browser/types.d.ts.map +1 -0
- package/dist/integrations/browser/types.js +68 -0
- package/dist/integrations/browser/types.js.map +1 -0
- package/dist/mcp/bundle.js +2849 -147
- package/package.json +1 -1
package/dist/cli/bundle.js
CHANGED
|
@@ -13698,12 +13698,12 @@ var TestGenerationCoordinator = class {
|
|
|
13698
13698
|
await this.agentCoordinator.stop(agentResult.value);
|
|
13699
13699
|
return result;
|
|
13700
13700
|
} catch (error) {
|
|
13701
|
-
const
|
|
13702
|
-
this.failWorkflow(workflowId,
|
|
13701
|
+
const err3 = error instanceof Error ? error : new Error(String(error));
|
|
13702
|
+
this.failWorkflow(workflowId, err3.message);
|
|
13703
13703
|
if (this.config.publishEvents) {
|
|
13704
|
-
await this.publishGenerationFailed(
|
|
13704
|
+
await this.publishGenerationFailed(err3, "generateTests");
|
|
13705
13705
|
}
|
|
13706
|
-
return { success: false, error:
|
|
13706
|
+
return { success: false, error: err3 };
|
|
13707
13707
|
}
|
|
13708
13708
|
}
|
|
13709
13709
|
/**
|
|
@@ -14475,13 +14475,13 @@ var TestGenerationPlugin = class extends BaseDomainPlugin {
|
|
|
14475
14475
|
}
|
|
14476
14476
|
}
|
|
14477
14477
|
handleError(error) {
|
|
14478
|
-
const
|
|
14478
|
+
const err3 = error instanceof Error ? error : new Error(String(error));
|
|
14479
14479
|
const currentHealth = this.getHealth();
|
|
14480
14480
|
this.updateHealth({
|
|
14481
|
-
errors: [...currentHealth.errors.slice(-9),
|
|
14481
|
+
errors: [...currentHealth.errors.slice(-9), err3.message],
|
|
14482
14482
|
status: currentHealth.errors.length >= 5 ? "degraded" : currentHealth.status
|
|
14483
14483
|
});
|
|
14484
|
-
return { success: false, error:
|
|
14484
|
+
return { success: false, error: err3 };
|
|
14485
14485
|
}
|
|
14486
14486
|
trackSuccessfulGeneration(_tests) {
|
|
14487
14487
|
const health = this.getHealth();
|
|
@@ -16367,10 +16367,10 @@ var RetryHandlerService = class {
|
|
|
16367
16367
|
const result = this.parseTestResult(code, stdout, stderr);
|
|
16368
16368
|
resolve7(result);
|
|
16369
16369
|
});
|
|
16370
|
-
proc.on("error", (
|
|
16370
|
+
proc.on("error", (err3) => {
|
|
16371
16371
|
clearTimeout(timeoutHandle);
|
|
16372
16372
|
reject(new Error(
|
|
16373
|
-
`Failed to spawn test process: ${
|
|
16373
|
+
`Failed to spawn test process: ${err3.message}. Command: ${command} ${args.join(" ")}. Ensure the test runner is installed and accessible.`
|
|
16374
16374
|
));
|
|
16375
16375
|
});
|
|
16376
16376
|
});
|
|
@@ -16946,6 +16946,1906 @@ var TestPrioritizerService = class {
|
|
|
16946
16946
|
}
|
|
16947
16947
|
};
|
|
16948
16948
|
|
|
16949
|
+
// src/integrations/browser/types.ts
|
|
16950
|
+
var BrowserError = class _BrowserError extends Error {
|
|
16951
|
+
constructor(message, code, tool, cause) {
|
|
16952
|
+
super(message);
|
|
16953
|
+
this.code = code;
|
|
16954
|
+
this.tool = tool;
|
|
16955
|
+
this.cause = cause;
|
|
16956
|
+
this.name = "BrowserError";
|
|
16957
|
+
Object.setPrototypeOf(this, _BrowserError.prototype);
|
|
16958
|
+
}
|
|
16959
|
+
};
|
|
16960
|
+
var BrowserUnavailableError = class _BrowserUnavailableError extends BrowserError {
|
|
16961
|
+
constructor(tool, message, cause) {
|
|
16962
|
+
super(message || "Browser tool is unavailable", "BROWSER_UNAVAILABLE", tool, cause);
|
|
16963
|
+
this.name = "BrowserUnavailableError";
|
|
16964
|
+
Object.setPrototypeOf(this, _BrowserUnavailableError.prototype);
|
|
16965
|
+
}
|
|
16966
|
+
};
|
|
16967
|
+
var BrowserTimeoutError = class _BrowserTimeoutError extends BrowserError {
|
|
16968
|
+
constructor(tool, operation, cause) {
|
|
16969
|
+
super(`${operation} timed out`, "BROWSER_TIMEOUT", tool, cause);
|
|
16970
|
+
this.name = "BrowserTimeoutError";
|
|
16971
|
+
Object.setPrototypeOf(this, _BrowserTimeoutError.prototype);
|
|
16972
|
+
}
|
|
16973
|
+
};
|
|
16974
|
+
var BrowserElementNotFoundError = class _BrowserElementNotFoundError extends BrowserError {
|
|
16975
|
+
constructor(tool, target, cause) {
|
|
16976
|
+
const targetStr = typeof target === "string" ? target : JSON.stringify(target);
|
|
16977
|
+
super(`Element not found: ${targetStr}`, "ELEMENT_NOT_FOUND", tool, cause);
|
|
16978
|
+
this.name = "BrowserElementNotFoundError";
|
|
16979
|
+
Object.setPrototypeOf(this, _BrowserElementNotFoundError.prototype);
|
|
16980
|
+
}
|
|
16981
|
+
};
|
|
16982
|
+
|
|
16983
|
+
// src/integrations/browser/agent-browser/command-executor.ts
|
|
16984
|
+
import { execSync, spawn as spawn4 } from "child_process";
|
|
16985
|
+
var AgentBrowserCommandExecutor = class {
|
|
16986
|
+
config;
|
|
16987
|
+
browserLaunched = false;
|
|
16988
|
+
constructor(config = {}) {
|
|
16989
|
+
this.config = {
|
|
16990
|
+
sessionName: config.sessionName ?? "default",
|
|
16991
|
+
timeout: config.timeout ?? 3e4,
|
|
16992
|
+
headed: config.headed ?? false,
|
|
16993
|
+
debug: config.debug ?? false
|
|
16994
|
+
};
|
|
16995
|
+
}
|
|
16996
|
+
// ========================================================================
|
|
16997
|
+
// Core execution methods
|
|
16998
|
+
// ========================================================================
|
|
16999
|
+
/**
|
|
17000
|
+
* Execute a command synchronously and parse JSON result
|
|
17001
|
+
*/
|
|
17002
|
+
execute(command, args = []) {
|
|
17003
|
+
const fullArgs = this.buildArgs(command, args);
|
|
17004
|
+
const cmdString = `npx agent-browser ${fullArgs.join(" ")}`;
|
|
17005
|
+
if (this.config.debug) {
|
|
17006
|
+
console.log(`[agent-browser] Executing: ${cmdString}`);
|
|
17007
|
+
}
|
|
17008
|
+
try {
|
|
17009
|
+
const output = execSync(cmdString, {
|
|
17010
|
+
encoding: "utf-8",
|
|
17011
|
+
timeout: this.config.timeout,
|
|
17012
|
+
maxBuffer: 10 * 1024 * 1024
|
|
17013
|
+
// 10MB for screenshots
|
|
17014
|
+
});
|
|
17015
|
+
try {
|
|
17016
|
+
const parsed = JSON.parse(output.trim());
|
|
17017
|
+
return { success: true, data: parsed };
|
|
17018
|
+
} catch {
|
|
17019
|
+
return { success: true, data: output.trim() };
|
|
17020
|
+
}
|
|
17021
|
+
} catch (error) {
|
|
17022
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
17023
|
+
return { success: false, error: message };
|
|
17024
|
+
}
|
|
17025
|
+
}
|
|
17026
|
+
/**
|
|
17027
|
+
* Execute a command asynchronously (for long-running ops)
|
|
17028
|
+
*/
|
|
17029
|
+
async executeAsync(command, args = []) {
|
|
17030
|
+
return new Promise((resolve7) => {
|
|
17031
|
+
const fullArgs = this.buildArgs(command, args);
|
|
17032
|
+
if (this.config.debug) {
|
|
17033
|
+
console.log(`[agent-browser] Executing async: npx agent-browser ${fullArgs.join(" ")}`);
|
|
17034
|
+
}
|
|
17035
|
+
const process2 = spawn4("npx", ["agent-browser", ...fullArgs], {
|
|
17036
|
+
timeout: this.config.timeout
|
|
17037
|
+
});
|
|
17038
|
+
let stdout = "";
|
|
17039
|
+
let stderr = "";
|
|
17040
|
+
process2.stdout?.on("data", (data) => {
|
|
17041
|
+
stdout += data.toString();
|
|
17042
|
+
});
|
|
17043
|
+
process2.stderr?.on("data", (data) => {
|
|
17044
|
+
stderr += data.toString();
|
|
17045
|
+
});
|
|
17046
|
+
process2.on("close", (code) => {
|
|
17047
|
+
if (code === 0) {
|
|
17048
|
+
try {
|
|
17049
|
+
const parsed = JSON.parse(stdout.trim());
|
|
17050
|
+
resolve7({ success: true, data: parsed });
|
|
17051
|
+
} catch {
|
|
17052
|
+
resolve7({ success: true, data: stdout.trim() });
|
|
17053
|
+
}
|
|
17054
|
+
} else {
|
|
17055
|
+
resolve7({ success: false, error: stderr || `Exit code: ${code}` });
|
|
17056
|
+
}
|
|
17057
|
+
});
|
|
17058
|
+
process2.on("error", (error) => {
|
|
17059
|
+
resolve7({ success: false, error: error.message });
|
|
17060
|
+
});
|
|
17061
|
+
});
|
|
17062
|
+
}
|
|
17063
|
+
// ========================================================================
|
|
17064
|
+
// Browser lifecycle commands
|
|
17065
|
+
// ========================================================================
|
|
17066
|
+
/**
|
|
17067
|
+
* Open URL in browser (auto-launches if needed)
|
|
17068
|
+
*/
|
|
17069
|
+
open(url) {
|
|
17070
|
+
const args = [url];
|
|
17071
|
+
if (this.config.headed) {
|
|
17072
|
+
args.push("--headed");
|
|
17073
|
+
}
|
|
17074
|
+
const result = this.execute("open", args);
|
|
17075
|
+
if (result.success) {
|
|
17076
|
+
this.browserLaunched = true;
|
|
17077
|
+
}
|
|
17078
|
+
return result;
|
|
17079
|
+
}
|
|
17080
|
+
/**
|
|
17081
|
+
* Close browser
|
|
17082
|
+
*/
|
|
17083
|
+
close() {
|
|
17084
|
+
const result = this.execute("close");
|
|
17085
|
+
if (result.success) {
|
|
17086
|
+
this.browserLaunched = false;
|
|
17087
|
+
}
|
|
17088
|
+
return result;
|
|
17089
|
+
}
|
|
17090
|
+
/**
|
|
17091
|
+
* Terminate the daemon process for this session
|
|
17092
|
+
* CRITICAL: Must be called to prevent memory leaks!
|
|
17093
|
+
* The daemon persists even after close() - this kills it entirely.
|
|
17094
|
+
*/
|
|
17095
|
+
terminateDaemon() {
|
|
17096
|
+
try {
|
|
17097
|
+
this.close();
|
|
17098
|
+
const sessionName = this.config.sessionName;
|
|
17099
|
+
try {
|
|
17100
|
+
execSync(
|
|
17101
|
+
`pkill -f "agent-browser.*--session[= ]${sessionName}" 2>/dev/null || true`,
|
|
17102
|
+
{ timeout: 5e3, stdio: "ignore" }
|
|
17103
|
+
);
|
|
17104
|
+
} catch {
|
|
17105
|
+
}
|
|
17106
|
+
this.browserLaunched = false;
|
|
17107
|
+
return { success: true };
|
|
17108
|
+
} catch (error) {
|
|
17109
|
+
return {
|
|
17110
|
+
success: false,
|
|
17111
|
+
error: error instanceof Error ? error.message : "Failed to terminate daemon"
|
|
17112
|
+
};
|
|
17113
|
+
}
|
|
17114
|
+
}
|
|
17115
|
+
// ========================================================================
|
|
17116
|
+
// Snapshot commands (key feature)
|
|
17117
|
+
// ========================================================================
|
|
17118
|
+
/**
|
|
17119
|
+
* Get accessibility snapshot with refs
|
|
17120
|
+
*/
|
|
17121
|
+
snapshot(options) {
|
|
17122
|
+
const args = ["--json"];
|
|
17123
|
+
if (options?.interactive) args.push("-i");
|
|
17124
|
+
if (options?.compact) args.push("-c");
|
|
17125
|
+
if (options?.depth) args.push("-d", String(options.depth));
|
|
17126
|
+
return this.execute("snapshot", args);
|
|
17127
|
+
}
|
|
17128
|
+
// ========================================================================
|
|
17129
|
+
// Element interaction commands
|
|
17130
|
+
// ========================================================================
|
|
17131
|
+
/**
|
|
17132
|
+
* Click element by ref or selector
|
|
17133
|
+
*/
|
|
17134
|
+
click(target) {
|
|
17135
|
+
return this.execute("click", [target]);
|
|
17136
|
+
}
|
|
17137
|
+
/**
|
|
17138
|
+
* Fill input by ref or selector
|
|
17139
|
+
*/
|
|
17140
|
+
fill(target, text) {
|
|
17141
|
+
return this.execute("fill", [target, `"${text.replace(/"/g, '\\"')}"`]);
|
|
17142
|
+
}
|
|
17143
|
+
/**
|
|
17144
|
+
* Type text (without clearing)
|
|
17145
|
+
*/
|
|
17146
|
+
type(target, text) {
|
|
17147
|
+
return this.execute("type", [target, `"${text.replace(/"/g, '\\"')}"`]);
|
|
17148
|
+
}
|
|
17149
|
+
/**
|
|
17150
|
+
* Get element text
|
|
17151
|
+
*/
|
|
17152
|
+
getText(target) {
|
|
17153
|
+
return this.execute("get", ["text", target, "--json"]);
|
|
17154
|
+
}
|
|
17155
|
+
/**
|
|
17156
|
+
* Check if element is visible
|
|
17157
|
+
*/
|
|
17158
|
+
isVisible(target) {
|
|
17159
|
+
const result = this.execute("is", ["visible", target, "--json"]);
|
|
17160
|
+
if (result.success && result.data) {
|
|
17161
|
+
return { success: true, data: result.data.success };
|
|
17162
|
+
}
|
|
17163
|
+
return { success: false, error: result.error };
|
|
17164
|
+
}
|
|
17165
|
+
// ========================================================================
|
|
17166
|
+
// Screenshot commands
|
|
17167
|
+
// ========================================================================
|
|
17168
|
+
/**
|
|
17169
|
+
* Take screenshot
|
|
17170
|
+
*/
|
|
17171
|
+
screenshot(path13, fullPage) {
|
|
17172
|
+
const args = [];
|
|
17173
|
+
if (path13) args.push(path13);
|
|
17174
|
+
if (fullPage) args.push("--full");
|
|
17175
|
+
if (!path13) args.push("--json");
|
|
17176
|
+
return this.execute("screenshot", args);
|
|
17177
|
+
}
|
|
17178
|
+
// ========================================================================
|
|
17179
|
+
// Wait commands
|
|
17180
|
+
// ========================================================================
|
|
17181
|
+
/**
|
|
17182
|
+
* Wait for element
|
|
17183
|
+
*/
|
|
17184
|
+
waitForElement(target, timeout) {
|
|
17185
|
+
const args = [target];
|
|
17186
|
+
if (timeout) {
|
|
17187
|
+
return this.execute("wait", args);
|
|
17188
|
+
}
|
|
17189
|
+
return this.execute("wait", args);
|
|
17190
|
+
}
|
|
17191
|
+
/**
|
|
17192
|
+
* Wait for text to appear
|
|
17193
|
+
*/
|
|
17194
|
+
waitForText(text) {
|
|
17195
|
+
return this.execute("wait", ["--text", text]);
|
|
17196
|
+
}
|
|
17197
|
+
/**
|
|
17198
|
+
* Wait for URL pattern
|
|
17199
|
+
*/
|
|
17200
|
+
waitForUrl(pattern) {
|
|
17201
|
+
const urlPattern = pattern.includes("*") ? pattern : `**${pattern}**`;
|
|
17202
|
+
return this.execute("wait", ["--url", urlPattern]);
|
|
17203
|
+
}
|
|
17204
|
+
/**
|
|
17205
|
+
* Wait for network idle
|
|
17206
|
+
*/
|
|
17207
|
+
waitForNetworkIdle() {
|
|
17208
|
+
return this.execute("wait", ["--load", "networkidle"]);
|
|
17209
|
+
}
|
|
17210
|
+
// ========================================================================
|
|
17211
|
+
// Device/viewport commands
|
|
17212
|
+
// ========================================================================
|
|
17213
|
+
/**
|
|
17214
|
+
* Set device emulation
|
|
17215
|
+
*/
|
|
17216
|
+
setDevice(deviceName) {
|
|
17217
|
+
return this.execute("set", ["device", `"${deviceName}"`]);
|
|
17218
|
+
}
|
|
17219
|
+
/**
|
|
17220
|
+
* Set viewport size
|
|
17221
|
+
*/
|
|
17222
|
+
setViewport(width, height) {
|
|
17223
|
+
return this.execute("set", ["viewport", String(width), String(height)]);
|
|
17224
|
+
}
|
|
17225
|
+
// ========================================================================
|
|
17226
|
+
// Network commands
|
|
17227
|
+
// ========================================================================
|
|
17228
|
+
/**
|
|
17229
|
+
* Mock network route
|
|
17230
|
+
*/
|
|
17231
|
+
mockRoute(urlPattern, body) {
|
|
17232
|
+
const bodyJson = JSON.stringify(body).replace(/"/g, '\\"');
|
|
17233
|
+
return this.execute("network", ["route", urlPattern, "--body", `"${bodyJson}"`]);
|
|
17234
|
+
}
|
|
17235
|
+
/**
|
|
17236
|
+
* Abort network route
|
|
17237
|
+
*/
|
|
17238
|
+
abortRoute(urlPattern) {
|
|
17239
|
+
return this.execute("network", ["route", urlPattern, "--abort"]);
|
|
17240
|
+
}
|
|
17241
|
+
/**
|
|
17242
|
+
* Clear network routes
|
|
17243
|
+
*/
|
|
17244
|
+
clearRoutes() {
|
|
17245
|
+
return this.execute("network", ["unroute"]);
|
|
17246
|
+
}
|
|
17247
|
+
// ========================================================================
|
|
17248
|
+
// State commands
|
|
17249
|
+
// ========================================================================
|
|
17250
|
+
/**
|
|
17251
|
+
* Save browser state (cookies, storage)
|
|
17252
|
+
*/
|
|
17253
|
+
saveState(path13) {
|
|
17254
|
+
return this.execute("state", ["save", path13]);
|
|
17255
|
+
}
|
|
17256
|
+
/**
|
|
17257
|
+
* Load browser state
|
|
17258
|
+
*/
|
|
17259
|
+
loadState(path13) {
|
|
17260
|
+
return this.execute("state", ["load", path13]);
|
|
17261
|
+
}
|
|
17262
|
+
// ========================================================================
|
|
17263
|
+
// Trace recording
|
|
17264
|
+
// ========================================================================
|
|
17265
|
+
/**
|
|
17266
|
+
* Start trace recording
|
|
17267
|
+
* Records browser activity for debugging and analysis
|
|
17268
|
+
*/
|
|
17269
|
+
startTrace() {
|
|
17270
|
+
return this.execute("trace", ["start"]);
|
|
17271
|
+
}
|
|
17272
|
+
/**
|
|
17273
|
+
* Stop trace recording and save to file
|
|
17274
|
+
* @param outputPath - Path to save the trace file
|
|
17275
|
+
*/
|
|
17276
|
+
stopTrace(outputPath) {
|
|
17277
|
+
const result = this.execute("trace", ["stop", outputPath]);
|
|
17278
|
+
if (result.success) {
|
|
17279
|
+
return { success: true, data: outputPath };
|
|
17280
|
+
}
|
|
17281
|
+
return { success: false, error: result.error };
|
|
17282
|
+
}
|
|
17283
|
+
// ========================================================================
|
|
17284
|
+
// JavaScript evaluation
|
|
17285
|
+
// ========================================================================
|
|
17286
|
+
/**
|
|
17287
|
+
* Evaluate JavaScript in page context
|
|
17288
|
+
*/
|
|
17289
|
+
eval(script) {
|
|
17290
|
+
const escapedScript = script.replace(/"/g, '\\"').replace(/\n/g, " ");
|
|
17291
|
+
return this.execute("eval", [`"${escapedScript}"`, "--json"]);
|
|
17292
|
+
}
|
|
17293
|
+
// ========================================================================
|
|
17294
|
+
// Session info
|
|
17295
|
+
// ========================================================================
|
|
17296
|
+
/**
|
|
17297
|
+
* Get current session name
|
|
17298
|
+
*/
|
|
17299
|
+
getSessionName() {
|
|
17300
|
+
return this.config.sessionName;
|
|
17301
|
+
}
|
|
17302
|
+
/**
|
|
17303
|
+
* Check if browser is launched
|
|
17304
|
+
*/
|
|
17305
|
+
isBrowserLaunched() {
|
|
17306
|
+
return this.browserLaunched;
|
|
17307
|
+
}
|
|
17308
|
+
// ========================================================================
|
|
17309
|
+
// Private helpers
|
|
17310
|
+
// ========================================================================
|
|
17311
|
+
buildArgs(command, args) {
|
|
17312
|
+
const fullArgs = [];
|
|
17313
|
+
if (this.config.sessionName !== "default") {
|
|
17314
|
+
fullArgs.push("--session", this.config.sessionName);
|
|
17315
|
+
}
|
|
17316
|
+
fullArgs.push(command, ...args);
|
|
17317
|
+
return fullArgs;
|
|
17318
|
+
}
|
|
17319
|
+
};
|
|
17320
|
+
function isAgentBrowserAvailable() {
|
|
17321
|
+
try {
|
|
17322
|
+
const output = execSync("npx agent-browser 2>&1", { encoding: "utf-8", timeout: 1e4 });
|
|
17323
|
+
return output.includes("agent-browser") && output.includes("Usage:");
|
|
17324
|
+
} catch {
|
|
17325
|
+
return false;
|
|
17326
|
+
}
|
|
17327
|
+
}
|
|
17328
|
+
|
|
17329
|
+
// src/integrations/browser/agent-browser/snapshot-parser.ts
|
|
17330
|
+
var INTERACTIVE_ROLES = /* @__PURE__ */ new Set([
|
|
17331
|
+
"button",
|
|
17332
|
+
"link",
|
|
17333
|
+
"textbox",
|
|
17334
|
+
"checkbox",
|
|
17335
|
+
"radio",
|
|
17336
|
+
"combobox",
|
|
17337
|
+
"listbox",
|
|
17338
|
+
"menuitem",
|
|
17339
|
+
"menuitemcheckbox",
|
|
17340
|
+
"menuitemradio",
|
|
17341
|
+
"option",
|
|
17342
|
+
"searchbox",
|
|
17343
|
+
"slider",
|
|
17344
|
+
"spinbutton",
|
|
17345
|
+
"switch",
|
|
17346
|
+
"tab",
|
|
17347
|
+
"treeitem"
|
|
17348
|
+
]);
|
|
17349
|
+
var SnapshotParser = class {
|
|
17350
|
+
/**
|
|
17351
|
+
* Parse raw snapshot text into structured format
|
|
17352
|
+
*/
|
|
17353
|
+
parse(snapshotOutput) {
|
|
17354
|
+
const lines = snapshotOutput.split("\n");
|
|
17355
|
+
const elements = [];
|
|
17356
|
+
const refMap = /* @__PURE__ */ new Map();
|
|
17357
|
+
let maxDepth = 0;
|
|
17358
|
+
for (const line of lines) {
|
|
17359
|
+
const element = this.parseLine(line);
|
|
17360
|
+
if (element) {
|
|
17361
|
+
elements.push(element);
|
|
17362
|
+
refMap.set(element.ref, element);
|
|
17363
|
+
if (element.depth > maxDepth) {
|
|
17364
|
+
maxDepth = element.depth;
|
|
17365
|
+
}
|
|
17366
|
+
}
|
|
17367
|
+
}
|
|
17368
|
+
this.buildRelationships(elements);
|
|
17369
|
+
const interactiveElements = elements.filter(
|
|
17370
|
+
(el) => INTERACTIVE_ROLES.has(el.role.toLowerCase())
|
|
17371
|
+
);
|
|
17372
|
+
return {
|
|
17373
|
+
rawTree: snapshotOutput,
|
|
17374
|
+
elements,
|
|
17375
|
+
interactiveElements,
|
|
17376
|
+
refMap,
|
|
17377
|
+
stats: {
|
|
17378
|
+
totalElements: elements.length,
|
|
17379
|
+
interactiveCount: interactiveElements.length,
|
|
17380
|
+
maxDepth
|
|
17381
|
+
},
|
|
17382
|
+
parsedAt: /* @__PURE__ */ new Date()
|
|
17383
|
+
};
|
|
17384
|
+
}
|
|
17385
|
+
/**
|
|
17386
|
+
* Parse JSON output from `agent-browser snapshot --json`
|
|
17387
|
+
*/
|
|
17388
|
+
parseJson(jsonOutput) {
|
|
17389
|
+
const data = typeof jsonOutput === "string" ? JSON.parse(jsonOutput) : jsonOutput;
|
|
17390
|
+
if (data.success && data.data) {
|
|
17391
|
+
const snapshot = data.data.snapshot || data.data;
|
|
17392
|
+
const refs = data.data.refs || {};
|
|
17393
|
+
return this.parseWithRefs(snapshot, refs);
|
|
17394
|
+
}
|
|
17395
|
+
if (typeof data === "string") {
|
|
17396
|
+
return this.parse(data);
|
|
17397
|
+
}
|
|
17398
|
+
throw new Error("Invalid snapshot JSON format");
|
|
17399
|
+
}
|
|
17400
|
+
/**
|
|
17401
|
+
* Parse snapshot with pre-extracted refs (from JSON)
|
|
17402
|
+
*/
|
|
17403
|
+
parseWithRefs(tree, refs) {
|
|
17404
|
+
const elements = [];
|
|
17405
|
+
const refMap = /* @__PURE__ */ new Map();
|
|
17406
|
+
let maxDepth = 0;
|
|
17407
|
+
for (const [refId, refData] of Object.entries(refs)) {
|
|
17408
|
+
const element = {
|
|
17409
|
+
ref: refId,
|
|
17410
|
+
refWithAt: `@${refId}`,
|
|
17411
|
+
role: refData.role,
|
|
17412
|
+
name: refData.name,
|
|
17413
|
+
attributes: {},
|
|
17414
|
+
depth: 0,
|
|
17415
|
+
children: []
|
|
17416
|
+
};
|
|
17417
|
+
elements.push(element);
|
|
17418
|
+
refMap.set(refId, element);
|
|
17419
|
+
}
|
|
17420
|
+
const treeElements = this.parse(tree);
|
|
17421
|
+
for (const treeEl of treeElements.elements) {
|
|
17422
|
+
const existing = refMap.get(treeEl.ref);
|
|
17423
|
+
if (existing) {
|
|
17424
|
+
existing.text = treeEl.text;
|
|
17425
|
+
existing.depth = treeEl.depth;
|
|
17426
|
+
existing.level = treeEl.level;
|
|
17427
|
+
if (treeEl.depth > maxDepth) maxDepth = treeEl.depth;
|
|
17428
|
+
}
|
|
17429
|
+
}
|
|
17430
|
+
const interactiveElements = elements.filter(
|
|
17431
|
+
(el) => INTERACTIVE_ROLES.has(el.role.toLowerCase())
|
|
17432
|
+
);
|
|
17433
|
+
return {
|
|
17434
|
+
rawTree: tree,
|
|
17435
|
+
elements,
|
|
17436
|
+
interactiveElements,
|
|
17437
|
+
refMap,
|
|
17438
|
+
stats: {
|
|
17439
|
+
totalElements: elements.length,
|
|
17440
|
+
interactiveCount: interactiveElements.length,
|
|
17441
|
+
maxDepth
|
|
17442
|
+
},
|
|
17443
|
+
parsedAt: /* @__PURE__ */ new Date()
|
|
17444
|
+
};
|
|
17445
|
+
}
|
|
17446
|
+
/**
|
|
17447
|
+
* Parse a single line from snapshot output
|
|
17448
|
+
*/
|
|
17449
|
+
parseLine(line) {
|
|
17450
|
+
if (!line.trim()) return null;
|
|
17451
|
+
const depth = this.getIndentLevel(line);
|
|
17452
|
+
const match = line.match(/^(\s*-\s*)(\w+)(?:\s+"([^"]*)")?(.*)$/);
|
|
17453
|
+
if (!match) return null;
|
|
17454
|
+
const [, , role, name, suffix] = match;
|
|
17455
|
+
const refMatch = suffix?.match(/\[ref=(\w+)\]/);
|
|
17456
|
+
if (!refMatch) return null;
|
|
17457
|
+
const ref = refMatch[1];
|
|
17458
|
+
const levelMatch = suffix?.match(/\[level=(\d+)\]/);
|
|
17459
|
+
const level = levelMatch ? parseInt(levelMatch[1], 10) : void 0;
|
|
17460
|
+
const nthMatch = suffix?.match(/\[nth=(\d+)\]/);
|
|
17461
|
+
const nth = nthMatch ? parseInt(nthMatch[1], 10) : void 0;
|
|
17462
|
+
const textMatch = line.match(/:\s*(.+)$/);
|
|
17463
|
+
const text = textMatch ? textMatch[1].trim() : void 0;
|
|
17464
|
+
const attributes = {};
|
|
17465
|
+
if (level) attributes["level"] = String(level);
|
|
17466
|
+
if (nth !== void 0) attributes["nth"] = String(nth);
|
|
17467
|
+
return {
|
|
17468
|
+
ref,
|
|
17469
|
+
refWithAt: `@${ref}`,
|
|
17470
|
+
role: role.toLowerCase(),
|
|
17471
|
+
name,
|
|
17472
|
+
text,
|
|
17473
|
+
level,
|
|
17474
|
+
attributes,
|
|
17475
|
+
depth,
|
|
17476
|
+
children: []
|
|
17477
|
+
};
|
|
17478
|
+
}
|
|
17479
|
+
/**
|
|
17480
|
+
* Get indentation level (2 spaces per level)
|
|
17481
|
+
*/
|
|
17482
|
+
getIndentLevel(line) {
|
|
17483
|
+
const match = line.match(/^(\s*)/);
|
|
17484
|
+
return match ? Math.floor(match[1].length / 2) : 0;
|
|
17485
|
+
}
|
|
17486
|
+
/**
|
|
17487
|
+
* Build parent-child relationships between elements
|
|
17488
|
+
*/
|
|
17489
|
+
buildRelationships(elements) {
|
|
17490
|
+
const stack = [];
|
|
17491
|
+
for (const element of elements) {
|
|
17492
|
+
while (stack.length > 0 && stack[stack.length - 1].depth >= element.depth) {
|
|
17493
|
+
stack.pop();
|
|
17494
|
+
}
|
|
17495
|
+
if (stack.length > 0) {
|
|
17496
|
+
const parent = stack[stack.length - 1];
|
|
17497
|
+
element.parent = parent.ref;
|
|
17498
|
+
parent.children.push(element.ref);
|
|
17499
|
+
}
|
|
17500
|
+
stack.push(element);
|
|
17501
|
+
}
|
|
17502
|
+
}
|
|
17503
|
+
// ========================================================================
|
|
17504
|
+
// Query methods
|
|
17505
|
+
// ========================================================================
|
|
17506
|
+
/**
|
|
17507
|
+
* Find element by ref
|
|
17508
|
+
*/
|
|
17509
|
+
findByRef(snapshot, ref) {
|
|
17510
|
+
const normalizedRef = ref.startsWith("@") ? ref.slice(1) : ref;
|
|
17511
|
+
return snapshot.refMap.get(normalizedRef) || null;
|
|
17512
|
+
}
|
|
17513
|
+
/**
|
|
17514
|
+
* Find elements by role
|
|
17515
|
+
*/
|
|
17516
|
+
findByRole(snapshot, role) {
|
|
17517
|
+
return snapshot.elements.filter(
|
|
17518
|
+
(el) => el.role.toLowerCase() === role.toLowerCase()
|
|
17519
|
+
);
|
|
17520
|
+
}
|
|
17521
|
+
/**
|
|
17522
|
+
* Find elements by name (accessible name)
|
|
17523
|
+
*/
|
|
17524
|
+
findByName(snapshot, name, exact = false) {
|
|
17525
|
+
return snapshot.elements.filter((el) => {
|
|
17526
|
+
if (!el.name) return false;
|
|
17527
|
+
if (exact) return el.name === name;
|
|
17528
|
+
return el.name.toLowerCase().includes(name.toLowerCase());
|
|
17529
|
+
});
|
|
17530
|
+
}
|
|
17531
|
+
/**
|
|
17532
|
+
* Find elements by text content
|
|
17533
|
+
*/
|
|
17534
|
+
findByText(snapshot, text, exact = false) {
|
|
17535
|
+
return snapshot.elements.filter((el) => {
|
|
17536
|
+
const content = el.text || el.name || "";
|
|
17537
|
+
if (exact) return content === text;
|
|
17538
|
+
return content.toLowerCase().includes(text.toLowerCase());
|
|
17539
|
+
});
|
|
17540
|
+
}
|
|
17541
|
+
/**
|
|
17542
|
+
* Find first interactive element matching criteria
|
|
17543
|
+
*/
|
|
17544
|
+
findInteractive(snapshot, criteria) {
|
|
17545
|
+
for (const el of snapshot.interactiveElements) {
|
|
17546
|
+
if (criteria.role && el.role.toLowerCase() !== criteria.role.toLowerCase()) {
|
|
17547
|
+
continue;
|
|
17548
|
+
}
|
|
17549
|
+
if (criteria.name && el.name !== criteria.name) {
|
|
17550
|
+
continue;
|
|
17551
|
+
}
|
|
17552
|
+
if (criteria.text) {
|
|
17553
|
+
const content = el.text || el.name || "";
|
|
17554
|
+
if (!content.toLowerCase().includes(criteria.text.toLowerCase())) {
|
|
17555
|
+
continue;
|
|
17556
|
+
}
|
|
17557
|
+
}
|
|
17558
|
+
return el;
|
|
17559
|
+
}
|
|
17560
|
+
return null;
|
|
17561
|
+
}
|
|
17562
|
+
/**
|
|
17563
|
+
* Convert ref to CSS selector (for Vibium fallback)
|
|
17564
|
+
* Uses role + name as best guess
|
|
17565
|
+
*/
|
|
17566
|
+
refToCssSelector(snapshot, ref) {
|
|
17567
|
+
const element = this.findByRef(snapshot, ref);
|
|
17568
|
+
if (!element) return null;
|
|
17569
|
+
const role = element.role;
|
|
17570
|
+
const name = element.name;
|
|
17571
|
+
const selectorMap = {
|
|
17572
|
+
button: name ? `button:has-text("${name}")` : "button",
|
|
17573
|
+
link: name ? `a:has-text("${name}")` : "a",
|
|
17574
|
+
textbox: name ? `input[placeholder="${name}"], input[aria-label="${name}"]` : 'input[type="text"]',
|
|
17575
|
+
heading: name ? `h1:has-text("${name}"), h2:has-text("${name}"), h3:has-text("${name}")` : "h1, h2, h3",
|
|
17576
|
+
checkbox: name ? `input[type="checkbox"][aria-label="${name}"]` : 'input[type="checkbox"]'
|
|
17577
|
+
};
|
|
17578
|
+
return selectorMap[role] || `[role="${role}"]`;
|
|
17579
|
+
}
|
|
17580
|
+
};
|
|
17581
|
+
var parserInstance = null;
|
|
17582
|
+
function getSnapshotParser() {
|
|
17583
|
+
if (!parserInstance) {
|
|
17584
|
+
parserInstance = new SnapshotParser();
|
|
17585
|
+
}
|
|
17586
|
+
return parserInstance;
|
|
17587
|
+
}
|
|
17588
|
+
|
|
17589
|
+
// src/integrations/browser/agent-browser/session-manager.ts
|
|
17590
|
+
import { execSync as execSync2 } from "child_process";
|
|
17591
|
+
var AgentBrowserSessionManager = class {
|
|
17592
|
+
constructor(defaultConfig) {
|
|
17593
|
+
this.defaultConfig = defaultConfig;
|
|
17594
|
+
}
|
|
17595
|
+
sessions = /* @__PURE__ */ new Map();
|
|
17596
|
+
activeSession = null;
|
|
17597
|
+
sessionCounter = 0;
|
|
17598
|
+
// ========================================================================
|
|
17599
|
+
// Session lifecycle
|
|
17600
|
+
// ========================================================================
|
|
17601
|
+
/**
|
|
17602
|
+
* Create a new isolated session
|
|
17603
|
+
*/
|
|
17604
|
+
async createSession(options) {
|
|
17605
|
+
const name = options?.name ?? this.generateSessionName();
|
|
17606
|
+
if (this.sessions.has(name)) {
|
|
17607
|
+
throw new Error(`Session "${name}" already exists`);
|
|
17608
|
+
}
|
|
17609
|
+
const executor = new AgentBrowserCommandExecutor({
|
|
17610
|
+
sessionName: name,
|
|
17611
|
+
headed: options?.headed ?? this.defaultConfig?.headed ?? false,
|
|
17612
|
+
timeout: options?.timeout ?? this.defaultConfig?.timeout ?? 3e4
|
|
17613
|
+
});
|
|
17614
|
+
const session = {
|
|
17615
|
+
name,
|
|
17616
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
17617
|
+
lastActivity: /* @__PURE__ */ new Date(),
|
|
17618
|
+
status: "idle",
|
|
17619
|
+
executor
|
|
17620
|
+
};
|
|
17621
|
+
this.sessions.set(name, session);
|
|
17622
|
+
if (options?.initialUrl) {
|
|
17623
|
+
const result = executor.open(options.initialUrl);
|
|
17624
|
+
if (result.success) {
|
|
17625
|
+
session.currentUrl = options.initialUrl;
|
|
17626
|
+
session.status = "active";
|
|
17627
|
+
}
|
|
17628
|
+
}
|
|
17629
|
+
if (!this.activeSession) {
|
|
17630
|
+
this.activeSession = name;
|
|
17631
|
+
}
|
|
17632
|
+
return session;
|
|
17633
|
+
}
|
|
17634
|
+
/**
|
|
17635
|
+
* Get or create a session by name
|
|
17636
|
+
*/
|
|
17637
|
+
async getOrCreateSession(name, options) {
|
|
17638
|
+
const existing = this.sessions.get(name);
|
|
17639
|
+
if (existing) {
|
|
17640
|
+
return existing;
|
|
17641
|
+
}
|
|
17642
|
+
return this.createSession({ ...options, name });
|
|
17643
|
+
}
|
|
17644
|
+
/**
|
|
17645
|
+
* Get session by name
|
|
17646
|
+
*/
|
|
17647
|
+
getSession(name) {
|
|
17648
|
+
return this.sessions.get(name);
|
|
17649
|
+
}
|
|
17650
|
+
/**
|
|
17651
|
+
* Get currently active session
|
|
17652
|
+
*/
|
|
17653
|
+
getActiveSession() {
|
|
17654
|
+
if (!this.activeSession) return void 0;
|
|
17655
|
+
return this.sessions.get(this.activeSession);
|
|
17656
|
+
}
|
|
17657
|
+
/**
|
|
17658
|
+
* Switch to a different session
|
|
17659
|
+
*/
|
|
17660
|
+
switchSession(name) {
|
|
17661
|
+
if (!this.sessions.has(name)) {
|
|
17662
|
+
throw new Error(`Session "${name}" not found`);
|
|
17663
|
+
}
|
|
17664
|
+
this.activeSession = name;
|
|
17665
|
+
}
|
|
17666
|
+
/**
|
|
17667
|
+
* List all sessions
|
|
17668
|
+
*/
|
|
17669
|
+
listSessions() {
|
|
17670
|
+
return Array.from(this.sessions.values());
|
|
17671
|
+
}
|
|
17672
|
+
/**
|
|
17673
|
+
* List active session names from CLI
|
|
17674
|
+
*/
|
|
17675
|
+
listCliSessions() {
|
|
17676
|
+
try {
|
|
17677
|
+
const output = execSync2("npx agent-browser session list", {
|
|
17678
|
+
encoding: "utf-8",
|
|
17679
|
+
timeout: 5e3
|
|
17680
|
+
});
|
|
17681
|
+
const lines = output.split("\n");
|
|
17682
|
+
const sessions = [];
|
|
17683
|
+
for (const line of lines) {
|
|
17684
|
+
const match = line.match(/^\s*(?:->)?\s*(\w+)\s*$/);
|
|
17685
|
+
if (match) {
|
|
17686
|
+
sessions.push(match[1]);
|
|
17687
|
+
}
|
|
17688
|
+
}
|
|
17689
|
+
return sessions;
|
|
17690
|
+
} catch {
|
|
17691
|
+
return [];
|
|
17692
|
+
}
|
|
17693
|
+
}
|
|
17694
|
+
// ========================================================================
|
|
17695
|
+
// Session control
|
|
17696
|
+
// ========================================================================
|
|
17697
|
+
/**
|
|
17698
|
+
* Close a specific session
|
|
17699
|
+
*/
|
|
17700
|
+
async closeSession(name) {
|
|
17701
|
+
const session = this.sessions.get(name);
|
|
17702
|
+
if (!session) {
|
|
17703
|
+
return { success: false, error: `Session "${name}" not found` };
|
|
17704
|
+
}
|
|
17705
|
+
const result = session.executor.terminateDaemon();
|
|
17706
|
+
if (result.success) {
|
|
17707
|
+
session.status = "closed";
|
|
17708
|
+
this.sessions.delete(name);
|
|
17709
|
+
if (this.activeSession === name) {
|
|
17710
|
+
const remaining = Array.from(this.sessions.keys());
|
|
17711
|
+
this.activeSession = remaining.length > 0 ? remaining[0] : null;
|
|
17712
|
+
}
|
|
17713
|
+
}
|
|
17714
|
+
return result;
|
|
17715
|
+
}
|
|
17716
|
+
/**
|
|
17717
|
+
* Close all sessions
|
|
17718
|
+
*/
|
|
17719
|
+
async closeAllSessions() {
|
|
17720
|
+
const names = Array.from(this.sessions.keys());
|
|
17721
|
+
for (const name of names) {
|
|
17722
|
+
await this.closeSession(name);
|
|
17723
|
+
}
|
|
17724
|
+
this.sessions.clear();
|
|
17725
|
+
this.activeSession = null;
|
|
17726
|
+
}
|
|
17727
|
+
/**
|
|
17728
|
+
* Clean up idle sessions older than maxAge
|
|
17729
|
+
*/
|
|
17730
|
+
async cleanupIdleSessions(maxAgeMs = 5 * 60 * 1e3) {
|
|
17731
|
+
const now = Date.now();
|
|
17732
|
+
let closedCount = 0;
|
|
17733
|
+
for (const [name, session] of this.sessions) {
|
|
17734
|
+
if (session.status === "idle") {
|
|
17735
|
+
const age = now - session.lastActivity.getTime();
|
|
17736
|
+
if (age > maxAgeMs) {
|
|
17737
|
+
await this.closeSession(name);
|
|
17738
|
+
closedCount++;
|
|
17739
|
+
}
|
|
17740
|
+
}
|
|
17741
|
+
}
|
|
17742
|
+
return closedCount;
|
|
17743
|
+
}
|
|
17744
|
+
// ========================================================================
|
|
17745
|
+
// Session operations via active session
|
|
17746
|
+
// ========================================================================
|
|
17747
|
+
/**
|
|
17748
|
+
* Get executor for active session
|
|
17749
|
+
*/
|
|
17750
|
+
getExecutor() {
|
|
17751
|
+
const session = this.getActiveSession();
|
|
17752
|
+
if (!session) {
|
|
17753
|
+
throw new Error("No active session. Create one first.");
|
|
17754
|
+
}
|
|
17755
|
+
return session.executor;
|
|
17756
|
+
}
|
|
17757
|
+
/**
|
|
17758
|
+
* Get executor for specific session
|
|
17759
|
+
*/
|
|
17760
|
+
getSessionExecutor(name) {
|
|
17761
|
+
const session = this.sessions.get(name);
|
|
17762
|
+
if (!session) {
|
|
17763
|
+
throw new Error(`Session "${name}" not found`);
|
|
17764
|
+
}
|
|
17765
|
+
return session.executor;
|
|
17766
|
+
}
|
|
17767
|
+
/**
|
|
17768
|
+
* Update session activity timestamp
|
|
17769
|
+
*/
|
|
17770
|
+
touchSession(name) {
|
|
17771
|
+
const session = this.sessions.get(name);
|
|
17772
|
+
if (session) {
|
|
17773
|
+
session.lastActivity = /* @__PURE__ */ new Date();
|
|
17774
|
+
}
|
|
17775
|
+
}
|
|
17776
|
+
/**
|
|
17777
|
+
* Update session URL
|
|
17778
|
+
*/
|
|
17779
|
+
updateSessionUrl(name, url) {
|
|
17780
|
+
const session = this.sessions.get(name);
|
|
17781
|
+
if (session) {
|
|
17782
|
+
session.currentUrl = url;
|
|
17783
|
+
session.lastActivity = /* @__PURE__ */ new Date();
|
|
17784
|
+
session.status = "active";
|
|
17785
|
+
}
|
|
17786
|
+
}
|
|
17787
|
+
// ========================================================================
|
|
17788
|
+
// Private helpers
|
|
17789
|
+
// ========================================================================
|
|
17790
|
+
generateSessionName() {
|
|
17791
|
+
return `session-${++this.sessionCounter}-${Date.now().toString(36)}`;
|
|
17792
|
+
}
|
|
17793
|
+
// ========================================================================
|
|
17794
|
+
// Stats
|
|
17795
|
+
// ========================================================================
|
|
17796
|
+
/**
|
|
17797
|
+
* Get session statistics
|
|
17798
|
+
*/
|
|
17799
|
+
getStats() {
|
|
17800
|
+
let active = 0;
|
|
17801
|
+
let idle = 0;
|
|
17802
|
+
let closed = 0;
|
|
17803
|
+
for (const session of this.sessions.values()) {
|
|
17804
|
+
switch (session.status) {
|
|
17805
|
+
case "active":
|
|
17806
|
+
active++;
|
|
17807
|
+
break;
|
|
17808
|
+
case "idle":
|
|
17809
|
+
idle++;
|
|
17810
|
+
break;
|
|
17811
|
+
case "closed":
|
|
17812
|
+
closed++;
|
|
17813
|
+
break;
|
|
17814
|
+
}
|
|
17815
|
+
}
|
|
17816
|
+
return {
|
|
17817
|
+
totalSessions: this.sessions.size,
|
|
17818
|
+
activeSessions: active,
|
|
17819
|
+
idleSessions: idle,
|
|
17820
|
+
closedSessions: closed,
|
|
17821
|
+
activeSessionName: this.activeSession
|
|
17822
|
+
};
|
|
17823
|
+
}
|
|
17824
|
+
};
|
|
17825
|
+
|
|
17826
|
+
// src/integrations/browser/agent-browser/client.ts
|
|
17827
|
+
function resolveTarget(target) {
|
|
17828
|
+
if (typeof target === "string") {
|
|
17829
|
+
if (/^@?e\d+$/.test(target)) {
|
|
17830
|
+
return target.startsWith("@") ? target : `@${target}`;
|
|
17831
|
+
}
|
|
17832
|
+
return target;
|
|
17833
|
+
}
|
|
17834
|
+
switch (target.type) {
|
|
17835
|
+
case "ref":
|
|
17836
|
+
return target.value.startsWith("@") ? target.value : `@${target.value}`;
|
|
17837
|
+
case "css":
|
|
17838
|
+
return target.value;
|
|
17839
|
+
case "xpath":
|
|
17840
|
+
return `xpath=${target.value}`;
|
|
17841
|
+
case "text":
|
|
17842
|
+
return `text=${target.value}`;
|
|
17843
|
+
default:
|
|
17844
|
+
return target.value;
|
|
17845
|
+
}
|
|
17846
|
+
}
|
|
17847
|
+
function ok2(value) {
|
|
17848
|
+
return { success: true, value };
|
|
17849
|
+
}
|
|
17850
|
+
function err2(error) {
|
|
17851
|
+
return { success: false, error };
|
|
17852
|
+
}
|
|
17853
|
+
function toSessionInfo(info) {
|
|
17854
|
+
return {
|
|
17855
|
+
id: info.name,
|
|
17856
|
+
tool: "agent-browser",
|
|
17857
|
+
status: info.status,
|
|
17858
|
+
currentUrl: info.currentUrl,
|
|
17859
|
+
createdAt: info.createdAt
|
|
17860
|
+
};
|
|
17861
|
+
}
|
|
17862
|
+
function toTypedSnapshot(parsed) {
|
|
17863
|
+
const elements = parsed.elements.map((el) => ({
|
|
17864
|
+
ref: el.refWithAt,
|
|
17865
|
+
role: el.role,
|
|
17866
|
+
name: el.name,
|
|
17867
|
+
text: el.text,
|
|
17868
|
+
depth: el.depth
|
|
17869
|
+
}));
|
|
17870
|
+
const interactiveElements = parsed.interactiveElements.map((el) => ({
|
|
17871
|
+
ref: el.refWithAt,
|
|
17872
|
+
role: el.role,
|
|
17873
|
+
name: el.name,
|
|
17874
|
+
text: el.text,
|
|
17875
|
+
depth: el.depth
|
|
17876
|
+
}));
|
|
17877
|
+
const refMap = /* @__PURE__ */ new Map();
|
|
17878
|
+
for (const el of elements) {
|
|
17879
|
+
refMap.set(el.ref, el);
|
|
17880
|
+
}
|
|
17881
|
+
return {
|
|
17882
|
+
url: parsed.url ?? "",
|
|
17883
|
+
title: parsed.title ?? "",
|
|
17884
|
+
elements,
|
|
17885
|
+
interactiveElements,
|
|
17886
|
+
refMap,
|
|
17887
|
+
timestamp: parsed.parsedAt
|
|
17888
|
+
};
|
|
17889
|
+
}
|
|
17890
|
+
var AgentBrowserClient = class {
|
|
17891
|
+
tool = "agent-browser";
|
|
17892
|
+
executor;
|
|
17893
|
+
sessionManager;
|
|
17894
|
+
snapshotParser;
|
|
17895
|
+
config;
|
|
17896
|
+
isLaunched = false;
|
|
17897
|
+
currentSessionId = null;
|
|
17898
|
+
constructor(config = {}) {
|
|
17899
|
+
this.config = {
|
|
17900
|
+
headed: config.headed ?? false,
|
|
17901
|
+
timeout: config.timeout ?? 3e4,
|
|
17902
|
+
debug: config.debug ?? false,
|
|
17903
|
+
sessionName: config.sessionName ?? "default"
|
|
17904
|
+
};
|
|
17905
|
+
const executorConfig = {
|
|
17906
|
+
sessionName: this.config.sessionName,
|
|
17907
|
+
timeout: this.config.timeout,
|
|
17908
|
+
headed: this.config.headed,
|
|
17909
|
+
debug: this.config.debug
|
|
17910
|
+
};
|
|
17911
|
+
this.executor = new AgentBrowserCommandExecutor(executorConfig);
|
|
17912
|
+
this.sessionManager = new AgentBrowserSessionManager({
|
|
17913
|
+
headed: this.config.headed,
|
|
17914
|
+
timeout: this.config.timeout
|
|
17915
|
+
});
|
|
17916
|
+
this.snapshotParser = getSnapshotParser();
|
|
17917
|
+
}
|
|
17918
|
+
// ==========================================================================
|
|
17919
|
+
// Lifecycle
|
|
17920
|
+
// ==========================================================================
|
|
17921
|
+
/**
|
|
17922
|
+
* Launch a browser instance
|
|
17923
|
+
*/
|
|
17924
|
+
async launch(options) {
|
|
17925
|
+
try {
|
|
17926
|
+
if (this.isLaunched && this.currentSessionId) {
|
|
17927
|
+
const existingSession = this.sessionManager.getSession(this.currentSessionId);
|
|
17928
|
+
if (existingSession) {
|
|
17929
|
+
if (options?.viewport) {
|
|
17930
|
+
await this.setViewport(options.viewport.width, options.viewport.height);
|
|
17931
|
+
}
|
|
17932
|
+
return ok2(toSessionInfo(existingSession));
|
|
17933
|
+
}
|
|
17934
|
+
}
|
|
17935
|
+
const available = await this.isAvailable();
|
|
17936
|
+
if (!available) {
|
|
17937
|
+
return err2(new BrowserUnavailableError("agent-browser", "agent-browser CLI is not available"));
|
|
17938
|
+
}
|
|
17939
|
+
const sessionName = options?.sessionName ?? this.config.sessionName;
|
|
17940
|
+
const session = await this.sessionManager.createSession({
|
|
17941
|
+
name: sessionName,
|
|
17942
|
+
headed: options?.headless === false || this.config.headed,
|
|
17943
|
+
timeout: this.config.timeout
|
|
17944
|
+
});
|
|
17945
|
+
this.currentSessionId = session.name;
|
|
17946
|
+
this.isLaunched = true;
|
|
17947
|
+
this.executor = new AgentBrowserCommandExecutor({
|
|
17948
|
+
sessionName: session.name,
|
|
17949
|
+
timeout: this.config.timeout,
|
|
17950
|
+
headed: options?.headless === false || this.config.headed,
|
|
17951
|
+
debug: this.config.debug
|
|
17952
|
+
});
|
|
17953
|
+
if (options?.viewport) {
|
|
17954
|
+
await this.setViewport(options.viewport.width, options.viewport.height);
|
|
17955
|
+
}
|
|
17956
|
+
if (options?.deviceName) {
|
|
17957
|
+
await this.setDevice(options.deviceName);
|
|
17958
|
+
}
|
|
17959
|
+
return ok2(toSessionInfo(session));
|
|
17960
|
+
} catch (error) {
|
|
17961
|
+
const browserError = new BrowserError(
|
|
17962
|
+
error instanceof Error ? error.message : String(error),
|
|
17963
|
+
"LAUNCH_FAILED",
|
|
17964
|
+
"agent-browser",
|
|
17965
|
+
error instanceof Error ? error : void 0
|
|
17966
|
+
);
|
|
17967
|
+
return err2(browserError);
|
|
17968
|
+
}
|
|
17969
|
+
}
|
|
17970
|
+
/**
|
|
17971
|
+
* Close the browser and terminate the daemon process
|
|
17972
|
+
* CRITICAL: This must fully terminate all processes to prevent memory leaks!
|
|
17973
|
+
*/
|
|
17974
|
+
async quit() {
|
|
17975
|
+
try {
|
|
17976
|
+
if (this.currentSessionId) {
|
|
17977
|
+
const result = await this.sessionManager.closeSession(this.currentSessionId);
|
|
17978
|
+
if (!result.success) {
|
|
17979
|
+
if (this.config.debug) {
|
|
17980
|
+
console.warn(`[agent-browser] Session close warning: ${result.error}`);
|
|
17981
|
+
}
|
|
17982
|
+
}
|
|
17983
|
+
}
|
|
17984
|
+
if (this.executor) {
|
|
17985
|
+
try {
|
|
17986
|
+
this.executor.terminateDaemon();
|
|
17987
|
+
} catch {
|
|
17988
|
+
}
|
|
17989
|
+
}
|
|
17990
|
+
this.isLaunched = false;
|
|
17991
|
+
this.currentSessionId = null;
|
|
17992
|
+
return ok2(void 0);
|
|
17993
|
+
} catch (error) {
|
|
17994
|
+
this.isLaunched = false;
|
|
17995
|
+
this.currentSessionId = null;
|
|
17996
|
+
return err2(
|
|
17997
|
+
new BrowserError(
|
|
17998
|
+
error instanceof Error ? error.message : String(error),
|
|
17999
|
+
"QUIT_FAILED",
|
|
18000
|
+
"agent-browser",
|
|
18001
|
+
error instanceof Error ? error : void 0
|
|
18002
|
+
)
|
|
18003
|
+
);
|
|
18004
|
+
}
|
|
18005
|
+
}
|
|
18006
|
+
/**
|
|
18007
|
+
* Check if agent-browser CLI is available
|
|
18008
|
+
*/
|
|
18009
|
+
async isAvailable() {
|
|
18010
|
+
return isAgentBrowserAvailable();
|
|
18011
|
+
}
|
|
18012
|
+
/**
|
|
18013
|
+
* Dispose all resources
|
|
18014
|
+
*/
|
|
18015
|
+
async dispose() {
|
|
18016
|
+
await this.sessionManager.closeAllSessions();
|
|
18017
|
+
this.isLaunched = false;
|
|
18018
|
+
this.currentSessionId = null;
|
|
18019
|
+
}
|
|
18020
|
+
// ==========================================================================
|
|
18021
|
+
// Navigation
|
|
18022
|
+
// ==========================================================================
|
|
18023
|
+
/**
|
|
18024
|
+
* Navigate to a URL
|
|
18025
|
+
*/
|
|
18026
|
+
async navigate(url) {
|
|
18027
|
+
const startTime = Date.now();
|
|
18028
|
+
try {
|
|
18029
|
+
const result = this.executor.open(url);
|
|
18030
|
+
if (!result.success) {
|
|
18031
|
+
return err2(new BrowserError(result.error ?? "Navigation failed", "NAVIGATION_FAILED", "agent-browser"));
|
|
18032
|
+
}
|
|
18033
|
+
if (this.currentSessionId) {
|
|
18034
|
+
this.sessionManager.updateSessionUrl(this.currentSessionId, url);
|
|
18035
|
+
}
|
|
18036
|
+
const evalResult = this.executor.eval(
|
|
18037
|
+
"JSON.stringify({ title: document.title, url: window.location.href })"
|
|
18038
|
+
);
|
|
18039
|
+
const pageInfo = evalResult.success && evalResult.data ? typeof evalResult.data === "string" ? JSON.parse(evalResult.data) : evalResult.data : { title: "", url };
|
|
18040
|
+
return ok2({
|
|
18041
|
+
url: pageInfo.url || url,
|
|
18042
|
+
title: pageInfo.title || "",
|
|
18043
|
+
success: true,
|
|
18044
|
+
durationMs: Date.now() - startTime
|
|
18045
|
+
});
|
|
18046
|
+
} catch (error) {
|
|
18047
|
+
return err2(
|
|
18048
|
+
new BrowserError(
|
|
18049
|
+
error instanceof Error ? error.message : String(error),
|
|
18050
|
+
"NAVIGATION_FAILED",
|
|
18051
|
+
"agent-browser",
|
|
18052
|
+
error instanceof Error ? error : void 0
|
|
18053
|
+
)
|
|
18054
|
+
);
|
|
18055
|
+
}
|
|
18056
|
+
}
|
|
18057
|
+
/**
|
|
18058
|
+
* Reload the current page
|
|
18059
|
+
*/
|
|
18060
|
+
async reload() {
|
|
18061
|
+
try {
|
|
18062
|
+
const result = this.executor.eval("window.location.reload()");
|
|
18063
|
+
if (!result.success) {
|
|
18064
|
+
return err2(new BrowserError(result.error ?? "Reload failed", "RELOAD_FAILED", "agent-browser"));
|
|
18065
|
+
}
|
|
18066
|
+
return ok2(void 0);
|
|
18067
|
+
} catch (error) {
|
|
18068
|
+
return err2(
|
|
18069
|
+
new BrowserError(
|
|
18070
|
+
error instanceof Error ? error.message : String(error),
|
|
18071
|
+
"RELOAD_FAILED",
|
|
18072
|
+
"agent-browser",
|
|
18073
|
+
error instanceof Error ? error : void 0
|
|
18074
|
+
)
|
|
18075
|
+
);
|
|
18076
|
+
}
|
|
18077
|
+
}
|
|
18078
|
+
/**
|
|
18079
|
+
* Go back in browser history
|
|
18080
|
+
*/
|
|
18081
|
+
async goBack() {
|
|
18082
|
+
try {
|
|
18083
|
+
const result = this.executor.eval("window.history.back()");
|
|
18084
|
+
if (!result.success) {
|
|
18085
|
+
return err2(new BrowserError(result.error ?? "Go back failed", "HISTORY_FAILED", "agent-browser"));
|
|
18086
|
+
}
|
|
18087
|
+
return ok2(void 0);
|
|
18088
|
+
} catch (error) {
|
|
18089
|
+
return err2(
|
|
18090
|
+
new BrowserError(
|
|
18091
|
+
error instanceof Error ? error.message : String(error),
|
|
18092
|
+
"HISTORY_FAILED",
|
|
18093
|
+
"agent-browser",
|
|
18094
|
+
error instanceof Error ? error : void 0
|
|
18095
|
+
)
|
|
18096
|
+
);
|
|
18097
|
+
}
|
|
18098
|
+
}
|
|
18099
|
+
/**
|
|
18100
|
+
* Go forward in browser history
|
|
18101
|
+
*/
|
|
18102
|
+
async goForward() {
|
|
18103
|
+
try {
|
|
18104
|
+
const result = this.executor.eval("window.history.forward()");
|
|
18105
|
+
if (!result.success) {
|
|
18106
|
+
return err2(new BrowserError(result.error ?? "Go forward failed", "HISTORY_FAILED", "agent-browser"));
|
|
18107
|
+
}
|
|
18108
|
+
return ok2(void 0);
|
|
18109
|
+
} catch (error) {
|
|
18110
|
+
return err2(
|
|
18111
|
+
new BrowserError(
|
|
18112
|
+
error instanceof Error ? error.message : String(error),
|
|
18113
|
+
"HISTORY_FAILED",
|
|
18114
|
+
"agent-browser",
|
|
18115
|
+
error instanceof Error ? error : void 0
|
|
18116
|
+
)
|
|
18117
|
+
);
|
|
18118
|
+
}
|
|
18119
|
+
}
|
|
18120
|
+
// ==========================================================================
|
|
18121
|
+
// Element Interaction
|
|
18122
|
+
// ==========================================================================
|
|
18123
|
+
/**
|
|
18124
|
+
* Click an element
|
|
18125
|
+
*/
|
|
18126
|
+
async click(target) {
|
|
18127
|
+
try {
|
|
18128
|
+
const resolvedTarget = resolveTarget(target);
|
|
18129
|
+
const result = this.executor.click(resolvedTarget);
|
|
18130
|
+
if (!result.success) {
|
|
18131
|
+
return err2(new BrowserElementNotFoundError("agent-browser", target, new Error(result.error)));
|
|
18132
|
+
}
|
|
18133
|
+
return ok2(void 0);
|
|
18134
|
+
} catch (error) {
|
|
18135
|
+
return err2(
|
|
18136
|
+
new BrowserError(
|
|
18137
|
+
error instanceof Error ? error.message : String(error),
|
|
18138
|
+
"CLICK_FAILED",
|
|
18139
|
+
"agent-browser",
|
|
18140
|
+
error instanceof Error ? error : void 0
|
|
18141
|
+
)
|
|
18142
|
+
);
|
|
18143
|
+
}
|
|
18144
|
+
}
|
|
18145
|
+
/**
|
|
18146
|
+
* Fill input field with text
|
|
18147
|
+
*/
|
|
18148
|
+
async fill(target, text) {
|
|
18149
|
+
try {
|
|
18150
|
+
const resolvedTarget = resolveTarget(target);
|
|
18151
|
+
const result = this.executor.fill(resolvedTarget, text);
|
|
18152
|
+
if (!result.success) {
|
|
18153
|
+
return err2(new BrowserElementNotFoundError("agent-browser", target, new Error(result.error)));
|
|
18154
|
+
}
|
|
18155
|
+
return ok2(void 0);
|
|
18156
|
+
} catch (error) {
|
|
18157
|
+
return err2(
|
|
18158
|
+
new BrowserError(
|
|
18159
|
+
error instanceof Error ? error.message : String(error),
|
|
18160
|
+
"FILL_FAILED",
|
|
18161
|
+
"agent-browser",
|
|
18162
|
+
error instanceof Error ? error : void 0
|
|
18163
|
+
)
|
|
18164
|
+
);
|
|
18165
|
+
}
|
|
18166
|
+
}
|
|
18167
|
+
/**
|
|
18168
|
+
* Get text content of an element
|
|
18169
|
+
*/
|
|
18170
|
+
async getText(target) {
|
|
18171
|
+
try {
|
|
18172
|
+
const resolvedTarget = resolveTarget(target);
|
|
18173
|
+
const result = this.executor.getText(resolvedTarget);
|
|
18174
|
+
if (!result.success) {
|
|
18175
|
+
return err2(new BrowserElementNotFoundError("agent-browser", target, new Error(result.error)));
|
|
18176
|
+
}
|
|
18177
|
+
let text = result.data;
|
|
18178
|
+
if (text && typeof text === "object") {
|
|
18179
|
+
const obj = text;
|
|
18180
|
+
if ("text" in obj) {
|
|
18181
|
+
text = obj.text;
|
|
18182
|
+
} else if ("data" in obj && typeof obj.data === "object") {
|
|
18183
|
+
const dataObj = obj.data;
|
|
18184
|
+
if ("text" in dataObj) {
|
|
18185
|
+
text = dataObj.text;
|
|
18186
|
+
} else if ("result" in dataObj) {
|
|
18187
|
+
text = dataObj.result;
|
|
18188
|
+
}
|
|
18189
|
+
} else if ("result" in obj) {
|
|
18190
|
+
text = obj.result;
|
|
18191
|
+
}
|
|
18192
|
+
}
|
|
18193
|
+
return ok2(String(text ?? ""));
|
|
18194
|
+
} catch (error) {
|
|
18195
|
+
return err2(
|
|
18196
|
+
new BrowserError(
|
|
18197
|
+
error instanceof Error ? error.message : String(error),
|
|
18198
|
+
"GET_TEXT_FAILED",
|
|
18199
|
+
"agent-browser",
|
|
18200
|
+
error instanceof Error ? error : void 0
|
|
18201
|
+
)
|
|
18202
|
+
);
|
|
18203
|
+
}
|
|
18204
|
+
}
|
|
18205
|
+
/**
|
|
18206
|
+
* Check if element is visible
|
|
18207
|
+
*/
|
|
18208
|
+
async isVisible(target) {
|
|
18209
|
+
try {
|
|
18210
|
+
const resolvedTarget = resolveTarget(target);
|
|
18211
|
+
const result = this.executor.isVisible(resolvedTarget);
|
|
18212
|
+
if (!result.success) {
|
|
18213
|
+
return ok2(false);
|
|
18214
|
+
}
|
|
18215
|
+
return ok2(Boolean(result.data));
|
|
18216
|
+
} catch (error) {
|
|
18217
|
+
return err2(
|
|
18218
|
+
new BrowserError(
|
|
18219
|
+
error instanceof Error ? error.message : String(error),
|
|
18220
|
+
"VISIBILITY_CHECK_FAILED",
|
|
18221
|
+
"agent-browser",
|
|
18222
|
+
error instanceof Error ? error : void 0
|
|
18223
|
+
)
|
|
18224
|
+
);
|
|
18225
|
+
}
|
|
18226
|
+
}
|
|
18227
|
+
// ==========================================================================
|
|
18228
|
+
// Screenshots
|
|
18229
|
+
// ==========================================================================
|
|
18230
|
+
/**
|
|
18231
|
+
* Capture a screenshot
|
|
18232
|
+
*/
|
|
18233
|
+
async screenshot(options) {
|
|
18234
|
+
try {
|
|
18235
|
+
const result = this.executor.screenshot(options?.path, options?.fullPage);
|
|
18236
|
+
if (!result.success) {
|
|
18237
|
+
return err2(new BrowserError(result.error ?? "Screenshot failed", "SCREENSHOT_FAILED", "agent-browser"));
|
|
18238
|
+
}
|
|
18239
|
+
const dimensionsResult = this.executor.eval(
|
|
18240
|
+
"JSON.stringify({ width: window.innerWidth, height: window.innerHeight })"
|
|
18241
|
+
);
|
|
18242
|
+
let dimensions = { width: 1280, height: 720 };
|
|
18243
|
+
if (dimensionsResult.success && dimensionsResult.data) {
|
|
18244
|
+
const parsed = typeof dimensionsResult.data === "string" ? JSON.parse(dimensionsResult.data) : dimensionsResult.data;
|
|
18245
|
+
dimensions = { width: parsed.width || 1280, height: parsed.height || 720 };
|
|
18246
|
+
}
|
|
18247
|
+
return ok2({
|
|
18248
|
+
base64: options?.path ? void 0 : String(result.data ?? ""),
|
|
18249
|
+
path: options?.path,
|
|
18250
|
+
format: "png",
|
|
18251
|
+
dimensions
|
|
18252
|
+
});
|
|
18253
|
+
} catch (error) {
|
|
18254
|
+
return err2(
|
|
18255
|
+
new BrowserError(
|
|
18256
|
+
error instanceof Error ? error.message : String(error),
|
|
18257
|
+
"SCREENSHOT_FAILED",
|
|
18258
|
+
"agent-browser",
|
|
18259
|
+
error instanceof Error ? error : void 0
|
|
18260
|
+
)
|
|
18261
|
+
);
|
|
18262
|
+
}
|
|
18263
|
+
}
|
|
18264
|
+
// ==========================================================================
|
|
18265
|
+
// Script Evaluation
|
|
18266
|
+
// ==========================================================================
|
|
18267
|
+
/**
|
|
18268
|
+
* Evaluate JavaScript in the browser context
|
|
18269
|
+
*/
|
|
18270
|
+
async evaluate(script) {
|
|
18271
|
+
try {
|
|
18272
|
+
const result = this.executor.eval(script);
|
|
18273
|
+
if (!result.success) {
|
|
18274
|
+
return err2(new BrowserError(result.error ?? "Evaluation failed", "EVAL_FAILED", "agent-browser"));
|
|
18275
|
+
}
|
|
18276
|
+
let value = result.data;
|
|
18277
|
+
if (value && typeof value === "object") {
|
|
18278
|
+
const obj = value;
|
|
18279
|
+
if ("data" in obj && obj.data && typeof obj.data === "object") {
|
|
18280
|
+
const dataObj = obj.data;
|
|
18281
|
+
if ("result" in dataObj) {
|
|
18282
|
+
value = dataObj.result;
|
|
18283
|
+
}
|
|
18284
|
+
} else if ("result" in obj) {
|
|
18285
|
+
value = obj.result;
|
|
18286
|
+
}
|
|
18287
|
+
}
|
|
18288
|
+
return ok2(value);
|
|
18289
|
+
} catch (error) {
|
|
18290
|
+
return err2(
|
|
18291
|
+
new BrowserError(
|
|
18292
|
+
error instanceof Error ? error.message : String(error),
|
|
18293
|
+
"EVAL_FAILED",
|
|
18294
|
+
"agent-browser",
|
|
18295
|
+
error instanceof Error ? error : void 0
|
|
18296
|
+
)
|
|
18297
|
+
);
|
|
18298
|
+
}
|
|
18299
|
+
}
|
|
18300
|
+
// ==========================================================================
|
|
18301
|
+
// Snapshots (agent-browser specific)
|
|
18302
|
+
// ==========================================================================
|
|
18303
|
+
/**
|
|
18304
|
+
* Get accessibility snapshot with element refs
|
|
18305
|
+
*/
|
|
18306
|
+
async getSnapshot(options) {
|
|
18307
|
+
try {
|
|
18308
|
+
const result = this.executor.snapshot({
|
|
18309
|
+
interactive: options?.interactive,
|
|
18310
|
+
depth: options?.depth
|
|
18311
|
+
});
|
|
18312
|
+
if (!result.success) {
|
|
18313
|
+
return err2(new BrowserError(result.error ?? "Snapshot failed", "SNAPSHOT_FAILED", "agent-browser"));
|
|
18314
|
+
}
|
|
18315
|
+
let parsed;
|
|
18316
|
+
try {
|
|
18317
|
+
parsed = this.snapshotParser.parseJson(result.data);
|
|
18318
|
+
} catch {
|
|
18319
|
+
parsed = this.snapshotParser.parse(String(result.data));
|
|
18320
|
+
}
|
|
18321
|
+
const urlResult = this.executor.eval("window.location.href");
|
|
18322
|
+
const titleResult = this.executor.eval("document.title");
|
|
18323
|
+
if (urlResult.success) {
|
|
18324
|
+
parsed.url = String(urlResult.data);
|
|
18325
|
+
}
|
|
18326
|
+
if (titleResult.success) {
|
|
18327
|
+
parsed.title = String(titleResult.data);
|
|
18328
|
+
}
|
|
18329
|
+
return ok2(toTypedSnapshot(parsed));
|
|
18330
|
+
} catch (error) {
|
|
18331
|
+
return err2(
|
|
18332
|
+
new BrowserError(
|
|
18333
|
+
error instanceof Error ? error.message : String(error),
|
|
18334
|
+
"SNAPSHOT_FAILED",
|
|
18335
|
+
"agent-browser",
|
|
18336
|
+
error instanceof Error ? error : void 0
|
|
18337
|
+
)
|
|
18338
|
+
);
|
|
18339
|
+
}
|
|
18340
|
+
}
|
|
18341
|
+
// ==========================================================================
|
|
18342
|
+
// Session Management (agent-browser specific)
|
|
18343
|
+
// ==========================================================================
|
|
18344
|
+
/**
|
|
18345
|
+
* Create a new isolated session
|
|
18346
|
+
*/
|
|
18347
|
+
async createSession(name) {
|
|
18348
|
+
try {
|
|
18349
|
+
const session = await this.sessionManager.createSession({ name });
|
|
18350
|
+
return ok2(toSessionInfo(session));
|
|
18351
|
+
} catch (error) {
|
|
18352
|
+
return err2(
|
|
18353
|
+
new BrowserError(
|
|
18354
|
+
error instanceof Error ? error.message : String(error),
|
|
18355
|
+
"SESSION_CREATE_FAILED",
|
|
18356
|
+
"agent-browser",
|
|
18357
|
+
error instanceof Error ? error : void 0
|
|
18358
|
+
)
|
|
18359
|
+
);
|
|
18360
|
+
}
|
|
18361
|
+
}
|
|
18362
|
+
/**
|
|
18363
|
+
* Switch to an existing session
|
|
18364
|
+
*/
|
|
18365
|
+
async switchSession(name) {
|
|
18366
|
+
try {
|
|
18367
|
+
this.sessionManager.switchSession(name);
|
|
18368
|
+
this.currentSessionId = name;
|
|
18369
|
+
this.executor = new AgentBrowserCommandExecutor({
|
|
18370
|
+
sessionName: name,
|
|
18371
|
+
timeout: this.config.timeout,
|
|
18372
|
+
headed: this.config.headed,
|
|
18373
|
+
debug: this.config.debug
|
|
18374
|
+
});
|
|
18375
|
+
return ok2(void 0);
|
|
18376
|
+
} catch (error) {
|
|
18377
|
+
return err2(
|
|
18378
|
+
new BrowserError(
|
|
18379
|
+
error instanceof Error ? error.message : String(error),
|
|
18380
|
+
"SESSION_SWITCH_FAILED",
|
|
18381
|
+
"agent-browser",
|
|
18382
|
+
error instanceof Error ? error : void 0
|
|
18383
|
+
)
|
|
18384
|
+
);
|
|
18385
|
+
}
|
|
18386
|
+
}
|
|
18387
|
+
/**
|
|
18388
|
+
* List all available sessions
|
|
18389
|
+
*/
|
|
18390
|
+
async listSessions() {
|
|
18391
|
+
try {
|
|
18392
|
+
const sessions = this.sessionManager.listSessions();
|
|
18393
|
+
return ok2(sessions.map(toSessionInfo));
|
|
18394
|
+
} catch (error) {
|
|
18395
|
+
return err2(
|
|
18396
|
+
new BrowserError(
|
|
18397
|
+
error instanceof Error ? error.message : String(error),
|
|
18398
|
+
"SESSION_LIST_FAILED",
|
|
18399
|
+
"agent-browser",
|
|
18400
|
+
error instanceof Error ? error : void 0
|
|
18401
|
+
)
|
|
18402
|
+
);
|
|
18403
|
+
}
|
|
18404
|
+
}
|
|
18405
|
+
// ==========================================================================
|
|
18406
|
+
// Network Interception (agent-browser specific)
|
|
18407
|
+
// ==========================================================================
|
|
18408
|
+
/**
|
|
18409
|
+
* Mock network route
|
|
18410
|
+
*/
|
|
18411
|
+
async mockRoute(urlPattern, response) {
|
|
18412
|
+
try {
|
|
18413
|
+
const mockBody = {
|
|
18414
|
+
status: response.status ?? 200,
|
|
18415
|
+
body: response.body,
|
|
18416
|
+
headers: response.headers
|
|
18417
|
+
};
|
|
18418
|
+
const result = this.executor.mockRoute(urlPattern, mockBody);
|
|
18419
|
+
if (!result.success) {
|
|
18420
|
+
return err2(new BrowserError(result.error ?? "Mock route failed", "MOCK_ROUTE_FAILED", "agent-browser"));
|
|
18421
|
+
}
|
|
18422
|
+
return ok2(void 0);
|
|
18423
|
+
} catch (error) {
|
|
18424
|
+
return err2(
|
|
18425
|
+
new BrowserError(
|
|
18426
|
+
error instanceof Error ? error.message : String(error),
|
|
18427
|
+
"MOCK_ROUTE_FAILED",
|
|
18428
|
+
"agent-browser",
|
|
18429
|
+
error instanceof Error ? error : void 0
|
|
18430
|
+
)
|
|
18431
|
+
);
|
|
18432
|
+
}
|
|
18433
|
+
}
|
|
18434
|
+
/**
|
|
18435
|
+
* Abort requests matching a pattern
|
|
18436
|
+
*/
|
|
18437
|
+
async abortRoute(urlPattern) {
|
|
18438
|
+
try {
|
|
18439
|
+
const result = this.executor.abortRoute(urlPattern);
|
|
18440
|
+
if (!result.success) {
|
|
18441
|
+
return err2(new BrowserError(result.error ?? "Abort route failed", "ABORT_ROUTE_FAILED", "agent-browser"));
|
|
18442
|
+
}
|
|
18443
|
+
return ok2(void 0);
|
|
18444
|
+
} catch (error) {
|
|
18445
|
+
return err2(
|
|
18446
|
+
new BrowserError(
|
|
18447
|
+
error instanceof Error ? error.message : String(error),
|
|
18448
|
+
"ABORT_ROUTE_FAILED",
|
|
18449
|
+
"agent-browser",
|
|
18450
|
+
error instanceof Error ? error : void 0
|
|
18451
|
+
)
|
|
18452
|
+
);
|
|
18453
|
+
}
|
|
18454
|
+
}
|
|
18455
|
+
/**
|
|
18456
|
+
* Clear all mocked routes
|
|
18457
|
+
*/
|
|
18458
|
+
async clearRoutes() {
|
|
18459
|
+
try {
|
|
18460
|
+
const result = this.executor.clearRoutes();
|
|
18461
|
+
if (!result.success) {
|
|
18462
|
+
return err2(new BrowserError(result.error ?? "Clear routes failed", "CLEAR_ROUTES_FAILED", "agent-browser"));
|
|
18463
|
+
}
|
|
18464
|
+
return ok2(void 0);
|
|
18465
|
+
} catch (error) {
|
|
18466
|
+
return err2(
|
|
18467
|
+
new BrowserError(
|
|
18468
|
+
error instanceof Error ? error.message : String(error),
|
|
18469
|
+
"CLEAR_ROUTES_FAILED",
|
|
18470
|
+
"agent-browser",
|
|
18471
|
+
error instanceof Error ? error : void 0
|
|
18472
|
+
)
|
|
18473
|
+
);
|
|
18474
|
+
}
|
|
18475
|
+
}
|
|
18476
|
+
// ==========================================================================
|
|
18477
|
+
// Device Emulation (agent-browser specific)
|
|
18478
|
+
// ==========================================================================
|
|
18479
|
+
/**
|
|
18480
|
+
* Emulate a specific device
|
|
18481
|
+
*/
|
|
18482
|
+
async setDevice(deviceName) {
|
|
18483
|
+
try {
|
|
18484
|
+
const result = this.executor.setDevice(deviceName);
|
|
18485
|
+
if (!result.success) {
|
|
18486
|
+
return err2(new BrowserError(result.error ?? "Set device failed", "SET_DEVICE_FAILED", "agent-browser"));
|
|
18487
|
+
}
|
|
18488
|
+
return ok2(void 0);
|
|
18489
|
+
} catch (error) {
|
|
18490
|
+
return err2(
|
|
18491
|
+
new BrowserError(
|
|
18492
|
+
error instanceof Error ? error.message : String(error),
|
|
18493
|
+
"SET_DEVICE_FAILED",
|
|
18494
|
+
"agent-browser",
|
|
18495
|
+
error instanceof Error ? error : void 0
|
|
18496
|
+
)
|
|
18497
|
+
);
|
|
18498
|
+
}
|
|
18499
|
+
}
|
|
18500
|
+
/**
|
|
18501
|
+
* Set custom viewport dimensions
|
|
18502
|
+
*/
|
|
18503
|
+
async setViewport(width, height) {
|
|
18504
|
+
try {
|
|
18505
|
+
const result = this.executor.setViewport(width, height);
|
|
18506
|
+
if (!result.success) {
|
|
18507
|
+
return err2(new BrowserError(result.error ?? "Set viewport failed", "SET_VIEWPORT_FAILED", "agent-browser"));
|
|
18508
|
+
}
|
|
18509
|
+
return ok2(void 0);
|
|
18510
|
+
} catch (error) {
|
|
18511
|
+
return err2(
|
|
18512
|
+
new BrowserError(
|
|
18513
|
+
error instanceof Error ? error.message : String(error),
|
|
18514
|
+
"SET_VIEWPORT_FAILED",
|
|
18515
|
+
"agent-browser",
|
|
18516
|
+
error instanceof Error ? error : void 0
|
|
18517
|
+
)
|
|
18518
|
+
);
|
|
18519
|
+
}
|
|
18520
|
+
}
|
|
18521
|
+
// ==========================================================================
|
|
18522
|
+
// Authentication State Persistence (agent-browser specific)
|
|
18523
|
+
// ==========================================================================
|
|
18524
|
+
/**
|
|
18525
|
+
* Save browser state (cookies, storage, etc.)
|
|
18526
|
+
*/
|
|
18527
|
+
async saveState(path13) {
|
|
18528
|
+
try {
|
|
18529
|
+
const result = this.executor.saveState(path13);
|
|
18530
|
+
if (!result.success) {
|
|
18531
|
+
return err2(new BrowserError(result.error ?? "Save state failed", "SAVE_STATE_FAILED", "agent-browser"));
|
|
18532
|
+
}
|
|
18533
|
+
return ok2(void 0);
|
|
18534
|
+
} catch (error) {
|
|
18535
|
+
return err2(
|
|
18536
|
+
new BrowserError(
|
|
18537
|
+
error instanceof Error ? error.message : String(error),
|
|
18538
|
+
"SAVE_STATE_FAILED",
|
|
18539
|
+
"agent-browser",
|
|
18540
|
+
error instanceof Error ? error : void 0
|
|
18541
|
+
)
|
|
18542
|
+
);
|
|
18543
|
+
}
|
|
18544
|
+
}
|
|
18545
|
+
/**
|
|
18546
|
+
* Load previously saved browser state
|
|
18547
|
+
*/
|
|
18548
|
+
async loadState(path13) {
|
|
18549
|
+
try {
|
|
18550
|
+
const result = this.executor.loadState(path13);
|
|
18551
|
+
if (!result.success) {
|
|
18552
|
+
return err2(new BrowserError(result.error ?? "Load state failed", "LOAD_STATE_FAILED", "agent-browser"));
|
|
18553
|
+
}
|
|
18554
|
+
return ok2(void 0);
|
|
18555
|
+
} catch (error) {
|
|
18556
|
+
return err2(
|
|
18557
|
+
new BrowserError(
|
|
18558
|
+
error instanceof Error ? error.message : String(error),
|
|
18559
|
+
"LOAD_STATE_FAILED",
|
|
18560
|
+
"agent-browser",
|
|
18561
|
+
error instanceof Error ? error : void 0
|
|
18562
|
+
)
|
|
18563
|
+
);
|
|
18564
|
+
}
|
|
18565
|
+
}
|
|
18566
|
+
// ==========================================================================
|
|
18567
|
+
// Advanced Wait Strategies (agent-browser specific)
|
|
18568
|
+
// ==========================================================================
|
|
18569
|
+
/**
|
|
18570
|
+
* Wait for an element to be present
|
|
18571
|
+
*/
|
|
18572
|
+
async waitForElement(target, timeout) {
|
|
18573
|
+
try {
|
|
18574
|
+
const resolvedTarget = resolveTarget(target);
|
|
18575
|
+
const result = this.executor.waitForElement(resolvedTarget, timeout);
|
|
18576
|
+
if (!result.success) {
|
|
18577
|
+
return err2(
|
|
18578
|
+
new BrowserTimeoutError("agent-browser", `waitForElement(${resolvedTarget})`, new Error(result.error))
|
|
18579
|
+
);
|
|
18580
|
+
}
|
|
18581
|
+
return ok2(void 0);
|
|
18582
|
+
} catch (error) {
|
|
18583
|
+
return err2(
|
|
18584
|
+
new BrowserError(
|
|
18585
|
+
error instanceof Error ? error.message : String(error),
|
|
18586
|
+
"WAIT_FAILED",
|
|
18587
|
+
"agent-browser",
|
|
18588
|
+
error instanceof Error ? error : void 0
|
|
18589
|
+
)
|
|
18590
|
+
);
|
|
18591
|
+
}
|
|
18592
|
+
}
|
|
18593
|
+
/**
|
|
18594
|
+
* Wait for text to appear on page
|
|
18595
|
+
*/
|
|
18596
|
+
async waitForText(text, timeout) {
|
|
18597
|
+
try {
|
|
18598
|
+
void timeout;
|
|
18599
|
+
const result = this.executor.waitForText(text);
|
|
18600
|
+
if (!result.success) {
|
|
18601
|
+
return err2(new BrowserTimeoutError("agent-browser", `waitForText("${text}")`, new Error(result.error)));
|
|
18602
|
+
}
|
|
18603
|
+
return ok2(void 0);
|
|
18604
|
+
} catch (error) {
|
|
18605
|
+
return err2(
|
|
18606
|
+
new BrowserError(
|
|
18607
|
+
error instanceof Error ? error.message : String(error),
|
|
18608
|
+
"WAIT_FAILED",
|
|
18609
|
+
"agent-browser",
|
|
18610
|
+
error instanceof Error ? error : void 0
|
|
18611
|
+
)
|
|
18612
|
+
);
|
|
18613
|
+
}
|
|
18614
|
+
}
|
|
18615
|
+
/**
|
|
18616
|
+
* Wait for URL to match pattern
|
|
18617
|
+
*/
|
|
18618
|
+
async waitForUrl(pattern, timeout) {
|
|
18619
|
+
try {
|
|
18620
|
+
void timeout;
|
|
18621
|
+
const result = this.executor.waitForUrl(pattern);
|
|
18622
|
+
if (!result.success) {
|
|
18623
|
+
return err2(new BrowserTimeoutError("agent-browser", `waitForUrl("${pattern}")`, new Error(result.error)));
|
|
18624
|
+
}
|
|
18625
|
+
return ok2(void 0);
|
|
18626
|
+
} catch (error) {
|
|
18627
|
+
return err2(
|
|
18628
|
+
new BrowserError(
|
|
18629
|
+
error instanceof Error ? error.message : String(error),
|
|
18630
|
+
"WAIT_FAILED",
|
|
18631
|
+
"agent-browser",
|
|
18632
|
+
error instanceof Error ? error : void 0
|
|
18633
|
+
)
|
|
18634
|
+
);
|
|
18635
|
+
}
|
|
18636
|
+
}
|
|
18637
|
+
/**
|
|
18638
|
+
* Wait for network to idle
|
|
18639
|
+
*/
|
|
18640
|
+
async waitForNetworkIdle(timeout) {
|
|
18641
|
+
try {
|
|
18642
|
+
void timeout;
|
|
18643
|
+
const result = this.executor.waitForNetworkIdle();
|
|
18644
|
+
if (!result.success) {
|
|
18645
|
+
return err2(new BrowserTimeoutError("agent-browser", "waitForNetworkIdle", new Error(result.error)));
|
|
18646
|
+
}
|
|
18647
|
+
return ok2(void 0);
|
|
18648
|
+
} catch (error) {
|
|
18649
|
+
return err2(
|
|
18650
|
+
new BrowserError(
|
|
18651
|
+
error instanceof Error ? error.message : String(error),
|
|
18652
|
+
"WAIT_FAILED",
|
|
18653
|
+
"agent-browser",
|
|
18654
|
+
error instanceof Error ? error : void 0
|
|
18655
|
+
)
|
|
18656
|
+
);
|
|
18657
|
+
}
|
|
18658
|
+
}
|
|
18659
|
+
// ==========================================================================
|
|
18660
|
+
// Trace Recording (agent-browser specific)
|
|
18661
|
+
// ==========================================================================
|
|
18662
|
+
/**
|
|
18663
|
+
* Start trace recording
|
|
18664
|
+
*
|
|
18665
|
+
* Records browser activity including network requests, console logs,
|
|
18666
|
+
* and user interactions. Useful for debugging test failures.
|
|
18667
|
+
*/
|
|
18668
|
+
async startTrace() {
|
|
18669
|
+
try {
|
|
18670
|
+
const result = this.executor.startTrace();
|
|
18671
|
+
if (!result.success) {
|
|
18672
|
+
return err2(new BrowserError(result.error ?? "Start trace failed", "START_TRACE_FAILED", "agent-browser"));
|
|
18673
|
+
}
|
|
18674
|
+
return ok2(void 0);
|
|
18675
|
+
} catch (error) {
|
|
18676
|
+
return err2(
|
|
18677
|
+
new BrowserError(
|
|
18678
|
+
error instanceof Error ? error.message : String(error),
|
|
18679
|
+
"START_TRACE_FAILED",
|
|
18680
|
+
"agent-browser",
|
|
18681
|
+
error instanceof Error ? error : void 0
|
|
18682
|
+
)
|
|
18683
|
+
);
|
|
18684
|
+
}
|
|
18685
|
+
}
|
|
18686
|
+
/**
|
|
18687
|
+
* Stop trace recording and save to file
|
|
18688
|
+
*
|
|
18689
|
+
* @param outputPath - Path to save the trace file (.zip recommended)
|
|
18690
|
+
* @returns Path where trace was saved
|
|
18691
|
+
*/
|
|
18692
|
+
async stopTrace(outputPath) {
|
|
18693
|
+
try {
|
|
18694
|
+
const result = this.executor.stopTrace(outputPath);
|
|
18695
|
+
if (!result.success) {
|
|
18696
|
+
return err2(new BrowserError(result.error ?? "Stop trace failed", "STOP_TRACE_FAILED", "agent-browser"));
|
|
18697
|
+
}
|
|
18698
|
+
return ok2(result.data ?? outputPath);
|
|
18699
|
+
} catch (error) {
|
|
18700
|
+
return err2(
|
|
18701
|
+
new BrowserError(
|
|
18702
|
+
error instanceof Error ? error.message : String(error),
|
|
18703
|
+
"STOP_TRACE_FAILED",
|
|
18704
|
+
"agent-browser",
|
|
18705
|
+
error instanceof Error ? error : void 0
|
|
18706
|
+
)
|
|
18707
|
+
);
|
|
18708
|
+
}
|
|
18709
|
+
}
|
|
18710
|
+
};
|
|
18711
|
+
|
|
18712
|
+
// src/integrations/browser/client-factory.ts
|
|
18713
|
+
async function isVibiumAvailable() {
|
|
18714
|
+
return false;
|
|
18715
|
+
}
|
|
18716
|
+
async function isAgentBrowserAvailable2() {
|
|
18717
|
+
try {
|
|
18718
|
+
const client = new AgentBrowserClient();
|
|
18719
|
+
return await client.isAvailable();
|
|
18720
|
+
} catch {
|
|
18721
|
+
return false;
|
|
18722
|
+
}
|
|
18723
|
+
}
|
|
18724
|
+
var AGENT_BROWSER_REQUIRED_USE_CASES = [
|
|
18725
|
+
"e2e-testing",
|
|
18726
|
+
// Requires refs and sessions
|
|
18727
|
+
"api-mocking",
|
|
18728
|
+
// Only agent-browser supports network interception
|
|
18729
|
+
"responsive-testing",
|
|
18730
|
+
// Requires device emulation
|
|
18731
|
+
"auth-testing"
|
|
18732
|
+
// Requires state persistence
|
|
18733
|
+
];
|
|
18734
|
+
function requiresAgentBrowser(useCase) {
|
|
18735
|
+
return AGENT_BROWSER_REQUIRED_USE_CASES.includes(useCase);
|
|
18736
|
+
}
|
|
18737
|
+
var VibiumClientStub = class {
|
|
18738
|
+
tool = "vibium";
|
|
18739
|
+
async isAvailable() {
|
|
18740
|
+
return false;
|
|
18741
|
+
}
|
|
18742
|
+
async launch() {
|
|
18743
|
+
return {
|
|
18744
|
+
success: false,
|
|
18745
|
+
error: new BrowserUnavailableError("vibium", "Vibium client not yet implemented")
|
|
18746
|
+
};
|
|
18747
|
+
}
|
|
18748
|
+
async quit() {
|
|
18749
|
+
return {
|
|
18750
|
+
success: false,
|
|
18751
|
+
error: new BrowserUnavailableError("vibium", "Vibium client not yet implemented")
|
|
18752
|
+
};
|
|
18753
|
+
}
|
|
18754
|
+
async navigate() {
|
|
18755
|
+
return {
|
|
18756
|
+
success: false,
|
|
18757
|
+
error: new BrowserUnavailableError("vibium", "Vibium client not yet implemented")
|
|
18758
|
+
};
|
|
18759
|
+
}
|
|
18760
|
+
async reload() {
|
|
18761
|
+
return {
|
|
18762
|
+
success: false,
|
|
18763
|
+
error: new BrowserUnavailableError("vibium", "Vibium client not yet implemented")
|
|
18764
|
+
};
|
|
18765
|
+
}
|
|
18766
|
+
async goBack() {
|
|
18767
|
+
return {
|
|
18768
|
+
success: false,
|
|
18769
|
+
error: new BrowserUnavailableError("vibium", "Vibium client not yet implemented")
|
|
18770
|
+
};
|
|
18771
|
+
}
|
|
18772
|
+
async goForward() {
|
|
18773
|
+
return {
|
|
18774
|
+
success: false,
|
|
18775
|
+
error: new BrowserUnavailableError("vibium", "Vibium client not yet implemented")
|
|
18776
|
+
};
|
|
18777
|
+
}
|
|
18778
|
+
async click() {
|
|
18779
|
+
return {
|
|
18780
|
+
success: false,
|
|
18781
|
+
error: new BrowserUnavailableError("vibium", "Vibium client not yet implemented")
|
|
18782
|
+
};
|
|
18783
|
+
}
|
|
18784
|
+
async fill() {
|
|
18785
|
+
return {
|
|
18786
|
+
success: false,
|
|
18787
|
+
error: new BrowserUnavailableError("vibium", "Vibium client not yet implemented")
|
|
18788
|
+
};
|
|
18789
|
+
}
|
|
18790
|
+
async getText() {
|
|
18791
|
+
return {
|
|
18792
|
+
success: false,
|
|
18793
|
+
error: new BrowserUnavailableError("vibium", "Vibium client not yet implemented")
|
|
18794
|
+
};
|
|
18795
|
+
}
|
|
18796
|
+
async isVisible() {
|
|
18797
|
+
return {
|
|
18798
|
+
success: false,
|
|
18799
|
+
error: new BrowserUnavailableError("vibium", "Vibium client not yet implemented")
|
|
18800
|
+
};
|
|
18801
|
+
}
|
|
18802
|
+
async screenshot() {
|
|
18803
|
+
return {
|
|
18804
|
+
success: false,
|
|
18805
|
+
error: new BrowserUnavailableError("vibium", "Vibium client not yet implemented")
|
|
18806
|
+
};
|
|
18807
|
+
}
|
|
18808
|
+
async evaluate() {
|
|
18809
|
+
return {
|
|
18810
|
+
success: false,
|
|
18811
|
+
error: new BrowserUnavailableError("vibium", "Vibium client not yet implemented")
|
|
18812
|
+
};
|
|
18813
|
+
}
|
|
18814
|
+
async dispose() {
|
|
18815
|
+
}
|
|
18816
|
+
};
|
|
18817
|
+
async function createBrowserClient(options = {}) {
|
|
18818
|
+
const { preference = "auto", useCase } = options;
|
|
18819
|
+
if (useCase && requiresAgentBrowser(useCase)) {
|
|
18820
|
+
return createAgentBrowserClient();
|
|
18821
|
+
}
|
|
18822
|
+
if (preference === "agent-browser") {
|
|
18823
|
+
return createAgentBrowserClient();
|
|
18824
|
+
}
|
|
18825
|
+
if (preference === "vibium") {
|
|
18826
|
+
const available = await isVibiumAvailable();
|
|
18827
|
+
if (available) {
|
|
18828
|
+
return new VibiumClientStub();
|
|
18829
|
+
}
|
|
18830
|
+
throw new BrowserUnavailableError("vibium", "Vibium is not available");
|
|
18831
|
+
}
|
|
18832
|
+
const vibiumAvailable = await isVibiumAvailable();
|
|
18833
|
+
if (vibiumAvailable) {
|
|
18834
|
+
return new VibiumClientStub();
|
|
18835
|
+
}
|
|
18836
|
+
const agentBrowserAvailable = await isAgentBrowserAvailable2();
|
|
18837
|
+
if (agentBrowserAvailable) {
|
|
18838
|
+
return createAgentBrowserClient();
|
|
18839
|
+
}
|
|
18840
|
+
return createAgentBrowserClient();
|
|
18841
|
+
}
|
|
18842
|
+
async function createAgentBrowserClient() {
|
|
18843
|
+
return new AgentBrowserClient();
|
|
18844
|
+
}
|
|
18845
|
+
async function getBrowserClientForUseCase(useCase) {
|
|
18846
|
+
return createBrowserClient({ useCase });
|
|
18847
|
+
}
|
|
18848
|
+
|
|
16949
18849
|
// src/domains/test-execution/types/e2e-step.types.ts
|
|
16950
18850
|
var E2EStepType = {
|
|
16951
18851
|
/** Navigate to a URL */
|
|
@@ -16994,7 +18894,9 @@ var DEFAULT_E2E_RUNNER_CONFIG = {
|
|
|
16994
18894
|
stopOnFirstFailure: false,
|
|
16995
18895
|
pollingInterval: 100,
|
|
16996
18896
|
maxParallelWorkers: 4,
|
|
16997
|
-
verbose: false
|
|
18897
|
+
verbose: false,
|
|
18898
|
+
preferAgentBrowser: true,
|
|
18899
|
+
browserClientType: "auto"
|
|
16998
18900
|
};
|
|
16999
18901
|
var E2ERunnerError = class extends Error {
|
|
17000
18902
|
constructor(message, code, stepId, cause) {
|
|
@@ -17019,17 +18921,102 @@ var AssertionError = class extends E2ERunnerError {
|
|
|
17019
18921
|
this.name = "AssertionError";
|
|
17020
18922
|
}
|
|
17021
18923
|
};
|
|
18924
|
+
function isAgentBrowserClient(client) {
|
|
18925
|
+
return "tool" in client && client.tool === "agent-browser";
|
|
18926
|
+
}
|
|
18927
|
+
function isVibiumClient(client) {
|
|
18928
|
+
return !("tool" in client) || !client.tool;
|
|
18929
|
+
}
|
|
18930
|
+
function toElementTarget(selector) {
|
|
18931
|
+
if (/^@?e\d+$/.test(selector)) {
|
|
18932
|
+
const value = selector.startsWith("@") ? selector : `@${selector}`;
|
|
18933
|
+
return { type: "ref", value };
|
|
18934
|
+
}
|
|
18935
|
+
if (selector.startsWith("//") || selector.startsWith("xpath=")) {
|
|
18936
|
+
const value = selector.replace(/^xpath=/, "");
|
|
18937
|
+
return { type: "xpath", value };
|
|
18938
|
+
}
|
|
18939
|
+
if (selector.startsWith("text=")) {
|
|
18940
|
+
const value = selector.replace(/^text=/, "");
|
|
18941
|
+
return { type: "text", value };
|
|
18942
|
+
}
|
|
18943
|
+
return { type: "css", value: selector };
|
|
18944
|
+
}
|
|
18945
|
+
function toVibiumScreenshotResult(result) {
|
|
18946
|
+
return {
|
|
18947
|
+
base64: result.base64,
|
|
18948
|
+
path: result.path,
|
|
18949
|
+
format: result.format,
|
|
18950
|
+
dimensions: result.dimensions,
|
|
18951
|
+
sizeBytes: result.base64 ? Math.ceil(result.base64.length * 0.75) : 0,
|
|
18952
|
+
// Estimate size from base64
|
|
18953
|
+
capturedAt: /* @__PURE__ */ new Date()
|
|
18954
|
+
};
|
|
18955
|
+
}
|
|
18956
|
+
function toVibiumAccessibilityResult(axeResults) {
|
|
18957
|
+
const violationsBySeverity = {
|
|
18958
|
+
critical: 0,
|
|
18959
|
+
high: 0,
|
|
18960
|
+
medium: 0,
|
|
18961
|
+
low: 0,
|
|
18962
|
+
info: 0
|
|
18963
|
+
};
|
|
18964
|
+
const violations = axeResults.violations.map((v) => {
|
|
18965
|
+
const impact = v.impact;
|
|
18966
|
+
if (impact in violationsBySeverity) {
|
|
18967
|
+
violationsBySeverity[impact]++;
|
|
18968
|
+
}
|
|
18969
|
+
return {
|
|
18970
|
+
id: v.id,
|
|
18971
|
+
impact: v.impact,
|
|
18972
|
+
description: v.description,
|
|
18973
|
+
nodes: v.nodes.length
|
|
18974
|
+
};
|
|
18975
|
+
});
|
|
18976
|
+
return {
|
|
18977
|
+
passes: violations.length === 0,
|
|
18978
|
+
violations,
|
|
18979
|
+
violationsBySeverity,
|
|
18980
|
+
passedRules: axeResults.passes.map((p) => p.id),
|
|
18981
|
+
incompleteRules: axeResults.incomplete.map((i) => i.id),
|
|
18982
|
+
checkedAt: /* @__PURE__ */ new Date()
|
|
18983
|
+
};
|
|
18984
|
+
}
|
|
17022
18985
|
var E2ETestRunnerService = class {
|
|
17023
18986
|
/**
|
|
17024
18987
|
* Create E2E Test Runner Service
|
|
17025
|
-
*
|
|
18988
|
+
*
|
|
18989
|
+
* @param client - Browser automation client (VibiumClient or IBrowserClient)
|
|
17026
18990
|
* @param config - Runner configuration
|
|
18991
|
+
*
|
|
18992
|
+
* @example
|
|
18993
|
+
* ```typescript
|
|
18994
|
+
* // Using VibiumClient (legacy)
|
|
18995
|
+
* const vibiumClient = await createVibiumClient({ enabled: true });
|
|
18996
|
+
* const runner = new E2ETestRunnerService(vibiumClient);
|
|
18997
|
+
*
|
|
18998
|
+
* // Using agent-browser (recommended for E2E)
|
|
18999
|
+
* const agentClient = await createAgentBrowserClient();
|
|
19000
|
+
* const runner = new E2ETestRunnerService(agentClient, {
|
|
19001
|
+
* preferAgentBrowser: true
|
|
19002
|
+
* });
|
|
19003
|
+
*
|
|
19004
|
+
* // Using auto-selection
|
|
19005
|
+
* const runner = createE2ETestRunnerServiceWithBrowserClient(undefined, {
|
|
19006
|
+
* browserClientType: 'auto'
|
|
19007
|
+
* });
|
|
19008
|
+
* ```
|
|
17027
19009
|
*/
|
|
17028
19010
|
constructor(client, config = {}) {
|
|
17029
19011
|
this.client = client;
|
|
17030
19012
|
this.config = { ...DEFAULT_E2E_RUNNER_CONFIG, ...config };
|
|
19013
|
+
this.unifiedClient = config.browserClient ?? client;
|
|
19014
|
+
this.useAgentBrowser = isAgentBrowserClient(this.unifiedClient);
|
|
19015
|
+
this.log(`E2E Runner initialized with ${this.useAgentBrowser ? "agent-browser" : "vibium"} client`);
|
|
17031
19016
|
}
|
|
17032
19017
|
config;
|
|
19018
|
+
unifiedClient;
|
|
19019
|
+
useAgentBrowser;
|
|
17033
19020
|
// ==========================================================================
|
|
17034
19021
|
// Public Methods
|
|
17035
19022
|
// ==========================================================================
|
|
@@ -17055,27 +19042,20 @@ var E2ETestRunnerService = class {
|
|
|
17055
19042
|
}
|
|
17056
19043
|
}
|
|
17057
19044
|
try {
|
|
17058
|
-
const
|
|
17059
|
-
if (
|
|
17060
|
-
|
|
17061
|
-
headless: true,
|
|
17062
|
-
viewport: testCase.viewport,
|
|
17063
|
-
...this.getBrowserContextOptions(testCase)
|
|
17064
|
-
});
|
|
17065
|
-
if (!launchResult.success) {
|
|
17066
|
-
return this.createErrorResult(
|
|
17067
|
-
testCase,
|
|
17068
|
-
startedAt,
|
|
17069
|
-
`Failed to launch browser: ${launchResult.error.message}`
|
|
17070
|
-
);
|
|
17071
|
-
}
|
|
19045
|
+
const launchError = await this.ensureBrowserLaunched(testCase);
|
|
19046
|
+
if (launchError) {
|
|
19047
|
+
return this.createErrorResult(testCase, startedAt, launchError);
|
|
17072
19048
|
}
|
|
17073
19049
|
const context2 = {
|
|
17074
19050
|
testCase,
|
|
17075
19051
|
baseUrl: testCase.baseUrl,
|
|
17076
19052
|
variables: testCase.testData ?? {},
|
|
17077
|
-
previousResults: stepResults
|
|
19053
|
+
previousResults: stepResults,
|
|
19054
|
+
useAgentBrowser: this.useAgentBrowser
|
|
17078
19055
|
};
|
|
19056
|
+
if (this.useAgentBrowser) {
|
|
19057
|
+
context2.currentSnapshot = await this.refreshSnapshot();
|
|
19058
|
+
}
|
|
17079
19059
|
if (testCase.hooks?.beforeAll) {
|
|
17080
19060
|
const hookResults = await this.executeHooks(
|
|
17081
19061
|
testCase.hooks.beforeAll,
|
|
@@ -17189,14 +19169,95 @@ var E2ETestRunnerService = class {
|
|
|
17189
19169
|
};
|
|
17190
19170
|
}
|
|
17191
19171
|
// ==========================================================================
|
|
19172
|
+
// Browser Launch Helper
|
|
19173
|
+
// ==========================================================================
|
|
19174
|
+
/**
|
|
19175
|
+
* Ensure browser is launched for the test case
|
|
19176
|
+
* Handles both agent-browser and Vibium clients
|
|
19177
|
+
*/
|
|
19178
|
+
async ensureBrowserLaunched(testCase) {
|
|
19179
|
+
try {
|
|
19180
|
+
if (this.useAgentBrowser && isAgentBrowserClient(this.unifiedClient)) {
|
|
19181
|
+
const launchResult = await this.unifiedClient.launch({
|
|
19182
|
+
headless: true,
|
|
19183
|
+
viewport: testCase.viewport
|
|
19184
|
+
// Use client's default session name, not test-specific
|
|
19185
|
+
});
|
|
19186
|
+
if (!launchResult.success) {
|
|
19187
|
+
return `Failed to launch browser: ${launchResult.error.message}`;
|
|
19188
|
+
}
|
|
19189
|
+
return null;
|
|
19190
|
+
} else if (isVibiumClient(this.client)) {
|
|
19191
|
+
const session = await this.client.getSession();
|
|
19192
|
+
if (!session) {
|
|
19193
|
+
const launchResult = await this.client.launch({
|
|
19194
|
+
headless: true,
|
|
19195
|
+
viewport: testCase.viewport,
|
|
19196
|
+
...this.getBrowserContextOptions(testCase)
|
|
19197
|
+
});
|
|
19198
|
+
if (!launchResult.success) {
|
|
19199
|
+
return `Failed to launch browser: ${launchResult.error.message}`;
|
|
19200
|
+
}
|
|
19201
|
+
}
|
|
19202
|
+
return null;
|
|
19203
|
+
} else {
|
|
19204
|
+
const launchResult = await this.unifiedClient.launch({
|
|
19205
|
+
headless: true,
|
|
19206
|
+
viewport: testCase.viewport
|
|
19207
|
+
});
|
|
19208
|
+
if (!launchResult.success) {
|
|
19209
|
+
return `Failed to launch browser: ${launchResult.error.message}`;
|
|
19210
|
+
}
|
|
19211
|
+
return null;
|
|
19212
|
+
}
|
|
19213
|
+
} catch (error) {
|
|
19214
|
+
return error instanceof Error ? error.message : String(error);
|
|
19215
|
+
}
|
|
19216
|
+
}
|
|
19217
|
+
/**
|
|
19218
|
+
* Refresh the page snapshot (agent-browser only)
|
|
19219
|
+
*/
|
|
19220
|
+
async refreshSnapshot() {
|
|
19221
|
+
if (!this.useAgentBrowser || !isAgentBrowserClient(this.unifiedClient)) {
|
|
19222
|
+
return void 0;
|
|
19223
|
+
}
|
|
19224
|
+
try {
|
|
19225
|
+
const snapshotResult = await this.unifiedClient.getSnapshot({ interactive: true });
|
|
19226
|
+
if (snapshotResult.success) {
|
|
19227
|
+
return snapshotResult.value;
|
|
19228
|
+
}
|
|
19229
|
+
} catch {
|
|
19230
|
+
this.log("Failed to refresh snapshot");
|
|
19231
|
+
}
|
|
19232
|
+
return void 0;
|
|
19233
|
+
}
|
|
19234
|
+
// ==========================================================================
|
|
17192
19235
|
// Step Executors
|
|
17193
19236
|
// ==========================================================================
|
|
17194
19237
|
/**
|
|
17195
19238
|
* Execute a navigate step
|
|
19239
|
+
* Supports both agent-browser and Vibium clients
|
|
17196
19240
|
*/
|
|
17197
19241
|
async executeNavigateStep(step, client, context2) {
|
|
17198
19242
|
const url = this.resolveUrl(step.target, context2.baseUrl);
|
|
17199
|
-
|
|
19243
|
+
if (!isVibiumClient(client)) {
|
|
19244
|
+
const browserClient = client;
|
|
19245
|
+
const result2 = await browserClient.navigate(url);
|
|
19246
|
+
if (!result2.success) {
|
|
19247
|
+
throw result2.error;
|
|
19248
|
+
}
|
|
19249
|
+
if (context2.useAgentBrowser) {
|
|
19250
|
+
context2.currentSnapshot = await this.refreshSnapshot();
|
|
19251
|
+
}
|
|
19252
|
+
return {
|
|
19253
|
+
data: {
|
|
19254
|
+
url: result2.value.url,
|
|
19255
|
+
title: result2.value.title
|
|
19256
|
+
}
|
|
19257
|
+
};
|
|
19258
|
+
}
|
|
19259
|
+
const vibiumClient = client;
|
|
19260
|
+
const result = await vibiumClient.navigate({
|
|
17200
19261
|
url,
|
|
17201
19262
|
waitUntil: step.options?.waitUntil ?? "load",
|
|
17202
19263
|
timeout: step.timeout ?? this.config.defaultStepTimeout
|
|
@@ -17213,18 +19274,43 @@ var E2ETestRunnerService = class {
|
|
|
17213
19274
|
}
|
|
17214
19275
|
/**
|
|
17215
19276
|
* Execute a click step
|
|
19277
|
+
* Supports both agent-browser and Vibium clients
|
|
17216
19278
|
*/
|
|
17217
|
-
async executeClickStep(step, client,
|
|
19279
|
+
async executeClickStep(step, client, context2) {
|
|
19280
|
+
if (!isVibiumClient(client)) {
|
|
19281
|
+
const browserClient = client;
|
|
19282
|
+
const target = toElementTarget(step.target);
|
|
19283
|
+
if (context2.useAgentBrowser && isAgentBrowserClient(browserClient)) {
|
|
19284
|
+
const waitResult = await browserClient.waitForElement(target, step.timeout);
|
|
19285
|
+
if (!waitResult.success) {
|
|
19286
|
+
throw waitResult.error;
|
|
19287
|
+
}
|
|
19288
|
+
}
|
|
19289
|
+
const result2 = await browserClient.click(target);
|
|
19290
|
+
if (!result2.success) {
|
|
19291
|
+
throw result2.error;
|
|
19292
|
+
}
|
|
19293
|
+
if (context2.useAgentBrowser) {
|
|
19294
|
+
context2.currentSnapshot = await this.refreshSnapshot();
|
|
19295
|
+
}
|
|
19296
|
+
if (step.options?.waitForNavigation && isAgentBrowserClient(browserClient)) {
|
|
19297
|
+
await browserClient.waitForNetworkIdle(step.timeout);
|
|
19298
|
+
}
|
|
19299
|
+
return {
|
|
19300
|
+
data: {}
|
|
19301
|
+
};
|
|
19302
|
+
}
|
|
19303
|
+
const vibiumClient = client;
|
|
17218
19304
|
if (step.options?.scrollIntoView) {
|
|
17219
|
-
await this.scrollIntoView(
|
|
19305
|
+
await this.scrollIntoView(vibiumClient, step.target);
|
|
17220
19306
|
}
|
|
17221
19307
|
if (step.options?.hoverFirst) {
|
|
17222
|
-
const findResult = await
|
|
19308
|
+
const findResult = await vibiumClient.findElement({ selector: step.target });
|
|
17223
19309
|
if (!findResult.success) {
|
|
17224
19310
|
throw findResult.error;
|
|
17225
19311
|
}
|
|
17226
19312
|
}
|
|
17227
|
-
const result = await
|
|
19313
|
+
const result = await vibiumClient.click({
|
|
17228
19314
|
selector: step.target,
|
|
17229
19315
|
button: step.options?.button,
|
|
17230
19316
|
clickCount: step.options?.clickCount,
|
|
@@ -17239,7 +19325,7 @@ var E2ETestRunnerService = class {
|
|
|
17239
19325
|
}
|
|
17240
19326
|
if (step.options?.waitForNavigation) {
|
|
17241
19327
|
await this.delay(500);
|
|
17242
|
-
const pageInfo = await
|
|
19328
|
+
const pageInfo = await vibiumClient.getPageInfo();
|
|
17243
19329
|
if (pageInfo.success) {
|
|
17244
19330
|
return {
|
|
17245
19331
|
data: {
|
|
@@ -17256,9 +19342,33 @@ var E2ETestRunnerService = class {
|
|
|
17256
19342
|
}
|
|
17257
19343
|
/**
|
|
17258
19344
|
* Execute a type step
|
|
19345
|
+
* Supports both agent-browser and Vibium clients
|
|
17259
19346
|
*/
|
|
17260
|
-
async executeTypeStep(step, client,
|
|
17261
|
-
|
|
19347
|
+
async executeTypeStep(step, client, context2) {
|
|
19348
|
+
if (!isVibiumClient(client)) {
|
|
19349
|
+
const browserClient = client;
|
|
19350
|
+
const target = toElementTarget(step.target);
|
|
19351
|
+
if (context2.useAgentBrowser && isAgentBrowserClient(browserClient)) {
|
|
19352
|
+
const waitResult = await browserClient.waitForElement(target, step.timeout);
|
|
19353
|
+
if (!waitResult.success) {
|
|
19354
|
+
throw waitResult.error;
|
|
19355
|
+
}
|
|
19356
|
+
}
|
|
19357
|
+
const result2 = await browserClient.fill(target, step.value);
|
|
19358
|
+
if (!result2.success) {
|
|
19359
|
+
throw result2.error;
|
|
19360
|
+
}
|
|
19361
|
+
if (context2.useAgentBrowser) {
|
|
19362
|
+
context2.currentSnapshot = await this.refreshSnapshot();
|
|
19363
|
+
}
|
|
19364
|
+
return {
|
|
19365
|
+
data: {
|
|
19366
|
+
elementText: step.options?.sensitive ? "[MASKED]" : step.value
|
|
19367
|
+
}
|
|
19368
|
+
};
|
|
19369
|
+
}
|
|
19370
|
+
const vibiumClient = client;
|
|
19371
|
+
const result = await vibiumClient.type({
|
|
17262
19372
|
selector: step.target,
|
|
17263
19373
|
text: step.value,
|
|
17264
19374
|
delay: step.options?.delay,
|
|
@@ -17277,11 +19387,51 @@ var E2ETestRunnerService = class {
|
|
|
17277
19387
|
}
|
|
17278
19388
|
/**
|
|
17279
19389
|
* Execute a wait step
|
|
19390
|
+
* Supports both agent-browser and Vibium clients
|
|
17280
19391
|
*/
|
|
17281
|
-
async executeWaitStep(step, client,
|
|
19392
|
+
async executeWaitStep(step, client, context2) {
|
|
17282
19393
|
const timeout = step.timeout ?? this.config.defaultStepTimeout;
|
|
17283
19394
|
const pollingInterval = step.options.pollingInterval ?? this.config.pollingInterval;
|
|
17284
|
-
|
|
19395
|
+
if (!isVibiumClient(client) && isAgentBrowserClient(client)) {
|
|
19396
|
+
const browserClient = client;
|
|
19397
|
+
let waitResult;
|
|
19398
|
+
switch (step.options.condition) {
|
|
19399
|
+
case "element-visible":
|
|
19400
|
+
case "element-hidden":
|
|
19401
|
+
if (step.target) {
|
|
19402
|
+
waitResult = await browserClient.waitForElement(toElementTarget(step.target), timeout);
|
|
19403
|
+
}
|
|
19404
|
+
break;
|
|
19405
|
+
case "element-text":
|
|
19406
|
+
if (step.options.expectedText) {
|
|
19407
|
+
waitResult = await browserClient.waitForText(step.options.expectedText, timeout);
|
|
19408
|
+
}
|
|
19409
|
+
break;
|
|
19410
|
+
case "url-match":
|
|
19411
|
+
if (step.options.urlPattern) {
|
|
19412
|
+
const pattern = typeof step.options.urlPattern === "string" ? step.options.urlPattern : step.options.urlPattern.source;
|
|
19413
|
+
waitResult = await browserClient.waitForUrl(pattern, timeout);
|
|
19414
|
+
}
|
|
19415
|
+
break;
|
|
19416
|
+
case "network-idle":
|
|
19417
|
+
case "page-loaded":
|
|
19418
|
+
case "dom-loaded":
|
|
19419
|
+
waitResult = await browserClient.waitForNetworkIdle(timeout);
|
|
19420
|
+
break;
|
|
19421
|
+
default:
|
|
19422
|
+
break;
|
|
19423
|
+
}
|
|
19424
|
+
if (waitResult && !waitResult.success) {
|
|
19425
|
+
throw waitResult.error;
|
|
19426
|
+
}
|
|
19427
|
+
context2.currentSnapshot = await this.refreshSnapshot();
|
|
19428
|
+
return { data: {} };
|
|
19429
|
+
}
|
|
19430
|
+
if (!isVibiumClient(client)) {
|
|
19431
|
+
await this.delay(pollingInterval);
|
|
19432
|
+
return { data: {} };
|
|
19433
|
+
}
|
|
19434
|
+
const waitData = await this.waitForCondition(
|
|
17285
19435
|
step.options.condition,
|
|
17286
19436
|
client,
|
|
17287
19437
|
step,
|
|
@@ -17289,16 +19439,33 @@ var E2ETestRunnerService = class {
|
|
|
17289
19439
|
pollingInterval
|
|
17290
19440
|
);
|
|
17291
19441
|
return {
|
|
17292
|
-
data:
|
|
19442
|
+
data: waitData
|
|
17293
19443
|
};
|
|
17294
19444
|
}
|
|
17295
19445
|
/**
|
|
17296
19446
|
* Execute an assert step
|
|
17297
|
-
|
|
17298
|
-
|
|
19447
|
+
* Supports both agent-browser and Vibium clients
|
|
19448
|
+
*/
|
|
19449
|
+
async executeAssertStep(step, client, context2) {
|
|
19450
|
+
if (!isVibiumClient(client)) {
|
|
19451
|
+
const browserClient = client;
|
|
19452
|
+
const assertResult2 = await this.performUnifiedAssertion(
|
|
19453
|
+
step.options.assertion,
|
|
19454
|
+
browserClient,
|
|
19455
|
+
step,
|
|
19456
|
+
context2
|
|
19457
|
+
);
|
|
19458
|
+
return {
|
|
19459
|
+
data: {
|
|
19460
|
+
actualValue: assertResult2.actual,
|
|
19461
|
+
expectedValue: assertResult2.expected
|
|
19462
|
+
}
|
|
19463
|
+
};
|
|
19464
|
+
}
|
|
19465
|
+
const vibiumClient = client;
|
|
17299
19466
|
const assertResult = await this.performAssertion(
|
|
17300
19467
|
step.options.assertion,
|
|
17301
|
-
|
|
19468
|
+
vibiumClient,
|
|
17302
19469
|
step
|
|
17303
19470
|
);
|
|
17304
19471
|
return {
|
|
@@ -17310,9 +19477,28 @@ var E2ETestRunnerService = class {
|
|
|
17310
19477
|
}
|
|
17311
19478
|
/**
|
|
17312
19479
|
* Execute a screenshot step
|
|
19480
|
+
* Supports both agent-browser and Vibium clients
|
|
17313
19481
|
*/
|
|
17314
19482
|
async executeScreenshotStep(step, client, _context) {
|
|
17315
|
-
|
|
19483
|
+
if (!isVibiumClient(client)) {
|
|
19484
|
+
const browserClient = client;
|
|
19485
|
+
const result2 = await browserClient.screenshot({
|
|
19486
|
+
path: step.target,
|
|
19487
|
+
fullPage: step.options?.fullPage
|
|
19488
|
+
});
|
|
19489
|
+
if (!result2.success) {
|
|
19490
|
+
throw result2.error;
|
|
19491
|
+
}
|
|
19492
|
+
const screenshotResult = toVibiumScreenshotResult(result2.value);
|
|
19493
|
+
return {
|
|
19494
|
+
screenshot: screenshotResult,
|
|
19495
|
+
data: {
|
|
19496
|
+
url: result2.value.path
|
|
19497
|
+
}
|
|
19498
|
+
};
|
|
19499
|
+
}
|
|
19500
|
+
const vibiumClient = client;
|
|
19501
|
+
const result = await vibiumClient.screenshot({
|
|
17316
19502
|
selector: step.target,
|
|
17317
19503
|
fullPage: step.options?.fullPage,
|
|
17318
19504
|
format: step.options?.format,
|
|
@@ -17331,9 +19517,56 @@ var E2ETestRunnerService = class {
|
|
|
17331
19517
|
}
|
|
17332
19518
|
/**
|
|
17333
19519
|
* Execute an accessibility check step
|
|
19520
|
+
* Supports both agent-browser and Vibium clients
|
|
17334
19521
|
*/
|
|
17335
19522
|
async executeA11yCheckStep(step, client, _context) {
|
|
17336
|
-
|
|
19523
|
+
if (!isVibiumClient(client)) {
|
|
19524
|
+
const browserClient = client;
|
|
19525
|
+
const axeScript = `
|
|
19526
|
+
(async () => {
|
|
19527
|
+
if (!window.axe) {
|
|
19528
|
+
const script = document.createElement('script');
|
|
19529
|
+
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.7.2/axe.min.js';
|
|
19530
|
+
document.head.appendChild(script);
|
|
19531
|
+
await new Promise(resolve => script.onload = resolve);
|
|
19532
|
+
}
|
|
19533
|
+
const results = await axe.run(${step.target ? `'${step.target}'` : "document"}, {
|
|
19534
|
+
runOnly: ${JSON.stringify(step.options?.tags ?? ["wcag2a", "wcag2aa"])},
|
|
19535
|
+
});
|
|
19536
|
+
return JSON.stringify(results);
|
|
19537
|
+
})()
|
|
19538
|
+
`;
|
|
19539
|
+
const evalResult = await browserClient.evaluate(axeScript);
|
|
19540
|
+
if (!evalResult.success) {
|
|
19541
|
+
throw evalResult.error;
|
|
19542
|
+
}
|
|
19543
|
+
const axeResults = JSON.parse(evalResult.value);
|
|
19544
|
+
const a11yResult2 = toVibiumAccessibilityResult(axeResults);
|
|
19545
|
+
if (step.options?.failOnSeverity) {
|
|
19546
|
+
const severityOrder = {
|
|
19547
|
+
critical: 0,
|
|
19548
|
+
high: 1,
|
|
19549
|
+
medium: 2,
|
|
19550
|
+
low: 3,
|
|
19551
|
+
info: 4
|
|
19552
|
+
};
|
|
19553
|
+
const threshold = severityOrder[step.options.failOnSeverity];
|
|
19554
|
+
const violationsOverThreshold = axeResults.violations.filter(
|
|
19555
|
+
(v) => severityOrder[v.impact] <= threshold
|
|
19556
|
+
);
|
|
19557
|
+
if (violationsOverThreshold.length > 0) {
|
|
19558
|
+
throw new AssertionError(
|
|
19559
|
+
`Accessibility violations found: ${violationsOverThreshold.length} at or above ${step.options.failOnSeverity}`,
|
|
19560
|
+
step.id,
|
|
19561
|
+
0,
|
|
19562
|
+
violationsOverThreshold.length
|
|
19563
|
+
);
|
|
19564
|
+
}
|
|
19565
|
+
}
|
|
19566
|
+
return { accessibilityResult: a11yResult2 };
|
|
19567
|
+
}
|
|
19568
|
+
const vibiumClient = client;
|
|
19569
|
+
const result = await vibiumClient.checkAccessibility({
|
|
17337
19570
|
selector: step.target,
|
|
17338
19571
|
wcagLevel: step.options?.wcagLevel ?? "AA",
|
|
17339
19572
|
rules: step.options?.rules ? {
|
|
@@ -17472,7 +19705,188 @@ var E2ETestRunnerService = class {
|
|
|
17472
19705
|
// Assertion Handlers
|
|
17473
19706
|
// ==========================================================================
|
|
17474
19707
|
/**
|
|
17475
|
-
* Perform an assertion
|
|
19708
|
+
* Perform an assertion using the unified browser client (IBrowserClient)
|
|
19709
|
+
* Supports agent-browser and generic browser clients
|
|
19710
|
+
*/
|
|
19711
|
+
async performUnifiedAssertion(assertion, client, step, _context) {
|
|
19712
|
+
let actual;
|
|
19713
|
+
const expected = step.options.expected ?? step.value;
|
|
19714
|
+
switch (assertion) {
|
|
19715
|
+
case "element-exists":
|
|
19716
|
+
case "element-visible":
|
|
19717
|
+
case "visible": {
|
|
19718
|
+
if (step.target) {
|
|
19719
|
+
const result = await client.isVisible(toElementTarget(step.target));
|
|
19720
|
+
actual = result.success ? result.value : false;
|
|
19721
|
+
} else {
|
|
19722
|
+
actual = false;
|
|
19723
|
+
}
|
|
19724
|
+
this.assertCondition(actual === true, step, true, actual);
|
|
19725
|
+
break;
|
|
19726
|
+
}
|
|
19727
|
+
case "element-not-exists":
|
|
19728
|
+
case "element-hidden":
|
|
19729
|
+
case "hidden": {
|
|
19730
|
+
if (step.target) {
|
|
19731
|
+
const result = await client.isVisible(toElementTarget(step.target));
|
|
19732
|
+
actual = result.success ? !result.value : true;
|
|
19733
|
+
} else {
|
|
19734
|
+
actual = true;
|
|
19735
|
+
}
|
|
19736
|
+
this.assertCondition(actual === true, step, true, actual);
|
|
19737
|
+
break;
|
|
19738
|
+
}
|
|
19739
|
+
case "element-text":
|
|
19740
|
+
case "text": {
|
|
19741
|
+
if (step.target) {
|
|
19742
|
+
const result = await client.getText(toElementTarget(step.target));
|
|
19743
|
+
if (!result.success) {
|
|
19744
|
+
throw result.error;
|
|
19745
|
+
}
|
|
19746
|
+
actual = result.value;
|
|
19747
|
+
this.assertTextMatch(actual, expected, step.options.operator, step);
|
|
19748
|
+
}
|
|
19749
|
+
break;
|
|
19750
|
+
}
|
|
19751
|
+
case "url-equals":
|
|
19752
|
+
case "url-contains":
|
|
19753
|
+
case "url-matches": {
|
|
19754
|
+
const urlResult = await client.evaluate("window.location.href");
|
|
19755
|
+
if (!urlResult.success) {
|
|
19756
|
+
throw urlResult.error;
|
|
19757
|
+
}
|
|
19758
|
+
actual = urlResult.value;
|
|
19759
|
+
if (assertion === "url-equals") {
|
|
19760
|
+
this.assertCondition(actual === expected, step, expected, actual);
|
|
19761
|
+
} else if (assertion === "url-contains") {
|
|
19762
|
+
this.assertCondition(
|
|
19763
|
+
actual.includes(expected),
|
|
19764
|
+
step,
|
|
19765
|
+
expected,
|
|
19766
|
+
actual
|
|
19767
|
+
);
|
|
19768
|
+
} else {
|
|
19769
|
+
const regex = new RegExp(expected);
|
|
19770
|
+
this.assertCondition(regex.test(actual), step, expected, actual);
|
|
19771
|
+
}
|
|
19772
|
+
break;
|
|
19773
|
+
}
|
|
19774
|
+
case "title-equals":
|
|
19775
|
+
case "title-contains": {
|
|
19776
|
+
const titleResult = await client.evaluate("document.title");
|
|
19777
|
+
if (!titleResult.success) {
|
|
19778
|
+
throw titleResult.error;
|
|
19779
|
+
}
|
|
19780
|
+
actual = titleResult.value;
|
|
19781
|
+
if (assertion === "title-equals") {
|
|
19782
|
+
this.assertCondition(actual === expected, step, expected, actual);
|
|
19783
|
+
} else {
|
|
19784
|
+
this.assertCondition(
|
|
19785
|
+
actual.includes(expected),
|
|
19786
|
+
step,
|
|
19787
|
+
expected,
|
|
19788
|
+
actual
|
|
19789
|
+
);
|
|
19790
|
+
}
|
|
19791
|
+
break;
|
|
19792
|
+
}
|
|
19793
|
+
case "page-has-text": {
|
|
19794
|
+
const textResult = await client.evaluate(
|
|
19795
|
+
`document.body.innerText.includes('${expected}')`
|
|
19796
|
+
);
|
|
19797
|
+
actual = textResult.success ? textResult.value : false;
|
|
19798
|
+
this.assertCondition(actual === true, step, true, actual);
|
|
19799
|
+
break;
|
|
19800
|
+
}
|
|
19801
|
+
case "element-attribute": {
|
|
19802
|
+
if (step.target && step.options.attributeName) {
|
|
19803
|
+
const attrResult = await client.evaluate(
|
|
19804
|
+
`document.querySelector('${step.target}')?.getAttribute('${step.options.attributeName}')`
|
|
19805
|
+
);
|
|
19806
|
+
if (attrResult.success) {
|
|
19807
|
+
actual = attrResult.value;
|
|
19808
|
+
this.assertCondition(actual === expected, step, expected, actual);
|
|
19809
|
+
} else {
|
|
19810
|
+
throw attrResult.error;
|
|
19811
|
+
}
|
|
19812
|
+
}
|
|
19813
|
+
break;
|
|
19814
|
+
}
|
|
19815
|
+
case "element-value": {
|
|
19816
|
+
if (step.target) {
|
|
19817
|
+
const valueResult = await client.evaluate(
|
|
19818
|
+
`document.querySelector('${step.target}')?.value`
|
|
19819
|
+
);
|
|
19820
|
+
if (valueResult.success) {
|
|
19821
|
+
actual = valueResult.value;
|
|
19822
|
+
this.assertCondition(actual === expected, step, expected, actual);
|
|
19823
|
+
} else {
|
|
19824
|
+
throw valueResult.error;
|
|
19825
|
+
}
|
|
19826
|
+
}
|
|
19827
|
+
break;
|
|
19828
|
+
}
|
|
19829
|
+
case "element-count": {
|
|
19830
|
+
if (step.target) {
|
|
19831
|
+
const countResult = await client.evaluate(
|
|
19832
|
+
`document.querySelectorAll('${step.target}').length`
|
|
19833
|
+
);
|
|
19834
|
+
if (countResult.success) {
|
|
19835
|
+
actual = countResult.value;
|
|
19836
|
+
const expectedCount = step.options.count ?? expected;
|
|
19837
|
+
this.assertNumericCondition(
|
|
19838
|
+
actual,
|
|
19839
|
+
expectedCount,
|
|
19840
|
+
step.options.operator ?? "eq",
|
|
19841
|
+
step
|
|
19842
|
+
);
|
|
19843
|
+
} else {
|
|
19844
|
+
throw countResult.error;
|
|
19845
|
+
}
|
|
19846
|
+
}
|
|
19847
|
+
break;
|
|
19848
|
+
}
|
|
19849
|
+
case "element-class": {
|
|
19850
|
+
if (step.target && step.options.className) {
|
|
19851
|
+
const classResult = await client.evaluate(
|
|
19852
|
+
`document.querySelector('${step.target}')?.classList.contains('${step.options.className}')`
|
|
19853
|
+
);
|
|
19854
|
+
actual = classResult.success ? classResult.value : false;
|
|
19855
|
+
this.assertCondition(actual === true, step, true, actual);
|
|
19856
|
+
}
|
|
19857
|
+
break;
|
|
19858
|
+
}
|
|
19859
|
+
case "element-enabled":
|
|
19860
|
+
case "element-disabled": {
|
|
19861
|
+
if (step.target) {
|
|
19862
|
+
const enabledResult = await client.evaluate(
|
|
19863
|
+
`!document.querySelector('${step.target}')?.disabled`
|
|
19864
|
+
);
|
|
19865
|
+
actual = enabledResult.success ? enabledResult.value : false;
|
|
19866
|
+
if (assertion === "element-disabled") {
|
|
19867
|
+
actual = !actual;
|
|
19868
|
+
}
|
|
19869
|
+
this.assertCondition(actual === true, step, true, actual);
|
|
19870
|
+
}
|
|
19871
|
+
break;
|
|
19872
|
+
}
|
|
19873
|
+
case "console-no-errors":
|
|
19874
|
+
actual = true;
|
|
19875
|
+
break;
|
|
19876
|
+
case "custom":
|
|
19877
|
+
actual = true;
|
|
19878
|
+
break;
|
|
19879
|
+
default:
|
|
19880
|
+
throw new E2ERunnerError(
|
|
19881
|
+
`Unsupported assertion type: ${assertion}`,
|
|
19882
|
+
"UNSUPPORTED_ASSERTION",
|
|
19883
|
+
step.id
|
|
19884
|
+
);
|
|
19885
|
+
}
|
|
19886
|
+
return { actual, expected };
|
|
19887
|
+
}
|
|
19888
|
+
/**
|
|
19889
|
+
* Perform an assertion (Legacy Vibium path)
|
|
17476
19890
|
*/
|
|
17477
19891
|
async performAssertion(assertion, client, step) {
|
|
17478
19892
|
let actual;
|
|
@@ -17701,20 +20115,21 @@ var E2ETestRunnerService = class {
|
|
|
17701
20115
|
return { data: {} };
|
|
17702
20116
|
}
|
|
17703
20117
|
}
|
|
20118
|
+
const client = this.unifiedClient;
|
|
17704
20119
|
if (isNavigateStep(step)) {
|
|
17705
|
-
return this.executeNavigateStep(step,
|
|
20120
|
+
return this.executeNavigateStep(step, client, context2);
|
|
17706
20121
|
} else if (isClickStep(step)) {
|
|
17707
|
-
return this.executeClickStep(step,
|
|
20122
|
+
return this.executeClickStep(step, client, context2);
|
|
17708
20123
|
} else if (isTypeStep(step)) {
|
|
17709
|
-
return this.executeTypeStep(step,
|
|
20124
|
+
return this.executeTypeStep(step, client, context2);
|
|
17710
20125
|
} else if (isWaitStep(step)) {
|
|
17711
|
-
return this.executeWaitStep(step,
|
|
20126
|
+
return this.executeWaitStep(step, client, context2);
|
|
17712
20127
|
} else if (isAssertStep(step)) {
|
|
17713
|
-
return this.executeAssertStep(step,
|
|
20128
|
+
return this.executeAssertStep(step, client, context2);
|
|
17714
20129
|
} else if (isScreenshotStep(step)) {
|
|
17715
|
-
return this.executeScreenshotStep(step,
|
|
20130
|
+
return this.executeScreenshotStep(step, client, context2);
|
|
17716
20131
|
} else if (isA11yCheckStep(step)) {
|
|
17717
|
-
return this.executeA11yCheckStep(step,
|
|
20132
|
+
return this.executeA11yCheckStep(step, client, context2);
|
|
17718
20133
|
}
|
|
17719
20134
|
const unknownStep = step;
|
|
17720
20135
|
throw new E2ERunnerError(
|
|
@@ -17930,9 +20345,18 @@ var E2ETestRunnerService = class {
|
|
|
17930
20345
|
}
|
|
17931
20346
|
/**
|
|
17932
20347
|
* Capture screenshot on failure
|
|
20348
|
+
* Supports both agent-browser and Vibium clients
|
|
17933
20349
|
*/
|
|
17934
20350
|
async captureFailureScreenshot(stepId) {
|
|
17935
20351
|
try {
|
|
20352
|
+
if (!isVibiumClient(this.unifiedClient)) {
|
|
20353
|
+
const browserClient = this.unifiedClient;
|
|
20354
|
+
const result2 = await browserClient.screenshot({ fullPage: true });
|
|
20355
|
+
if (result2.success) {
|
|
20356
|
+
return toVibiumScreenshotResult(result2.value);
|
|
20357
|
+
}
|
|
20358
|
+
return null;
|
|
20359
|
+
}
|
|
17936
20360
|
const result = await this.client.screenshot({
|
|
17937
20361
|
fullPage: true,
|
|
17938
20362
|
format: "png"
|
|
@@ -25737,13 +28161,13 @@ var QualityAssessmentPlugin = class extends BaseDomainPlugin {
|
|
|
25737
28161
|
}
|
|
25738
28162
|
}
|
|
25739
28163
|
handleError(error) {
|
|
25740
|
-
const
|
|
28164
|
+
const err3 = error instanceof Error ? error : new Error(String(error));
|
|
25741
28165
|
const currentHealth = this.getHealth();
|
|
25742
28166
|
this.updateHealth({
|
|
25743
|
-
errors: [...currentHealth.errors.slice(-9),
|
|
28167
|
+
errors: [...currentHealth.errors.slice(-9), err3.message],
|
|
25744
28168
|
status: currentHealth.errors.length >= 5 ? "degraded" : currentHealth.status
|
|
25745
28169
|
});
|
|
25746
|
-
return { success: false, error:
|
|
28170
|
+
return { success: false, error: err3 };
|
|
25747
28171
|
}
|
|
25748
28172
|
trackSuccessfulOperation(type) {
|
|
25749
28173
|
const health = this.getHealth();
|
|
@@ -28053,9 +30477,9 @@ var DefectIntelligenceCoordinator = class {
|
|
|
28053
30477
|
await this.agentCoordinator.stop(agentResult.value);
|
|
28054
30478
|
return result;
|
|
28055
30479
|
} catch (error) {
|
|
28056
|
-
const
|
|
28057
|
-
this.failWorkflow(workflowId,
|
|
28058
|
-
return { success: false, error:
|
|
30480
|
+
const err3 = error instanceof Error ? error : new Error(String(error));
|
|
30481
|
+
this.failWorkflow(workflowId, err3.message);
|
|
30482
|
+
return { success: false, error: err3 };
|
|
28059
30483
|
}
|
|
28060
30484
|
}
|
|
28061
30485
|
/**
|
|
@@ -28635,13 +31059,13 @@ var DefectIntelligencePlugin = class extends BaseDomainPlugin {
|
|
|
28635
31059
|
}
|
|
28636
31060
|
}
|
|
28637
31061
|
handleError(error) {
|
|
28638
|
-
const
|
|
31062
|
+
const err3 = error instanceof Error ? error : new Error(String(error));
|
|
28639
31063
|
const currentHealth = this.getHealth();
|
|
28640
31064
|
this.updateHealth({
|
|
28641
|
-
errors: [...currentHealth.errors.slice(-9),
|
|
31065
|
+
errors: [...currentHealth.errors.slice(-9), err3.message],
|
|
28642
31066
|
status: currentHealth.errors.length >= 5 ? "degraded" : currentHealth.status
|
|
28643
31067
|
});
|
|
28644
|
-
return { success: false, error:
|
|
31068
|
+
return { success: false, error: err3 };
|
|
28645
31069
|
}
|
|
28646
31070
|
trackSuccessfulOperation(_type) {
|
|
28647
31071
|
const health = this.getHealth();
|
|
@@ -30451,12 +32875,12 @@ var RequirementsValidationCoordinator = class {
|
|
|
30451
32875
|
}
|
|
30452
32876
|
return ok(analysis);
|
|
30453
32877
|
} catch (error) {
|
|
30454
|
-
const
|
|
30455
|
-
this.failWorkflow(workflowId,
|
|
32878
|
+
const err3 = error instanceof Error ? error : new Error(String(error));
|
|
32879
|
+
this.failWorkflow(workflowId, err3.message);
|
|
30456
32880
|
if (this.config.publishEvents) {
|
|
30457
|
-
await this.publishValidationFailed(
|
|
32881
|
+
await this.publishValidationFailed(err3, "analyzeRequirement");
|
|
30458
32882
|
}
|
|
30459
|
-
return { success: false, error:
|
|
32883
|
+
return { success: false, error: err3 };
|
|
30460
32884
|
}
|
|
30461
32885
|
}
|
|
30462
32886
|
/**
|
|
@@ -30526,9 +32950,9 @@ var RequirementsValidationCoordinator = class {
|
|
|
30526
32950
|
}
|
|
30527
32951
|
return ok(artifacts);
|
|
30528
32952
|
} catch (error) {
|
|
30529
|
-
const
|
|
30530
|
-
this.failWorkflow(workflowId,
|
|
30531
|
-
return { success: false, error:
|
|
32953
|
+
const err3 = error instanceof Error ? error : new Error(String(error));
|
|
32954
|
+
this.failWorkflow(workflowId, err3.message);
|
|
32955
|
+
return { success: false, error: err3 };
|
|
30532
32956
|
}
|
|
30533
32957
|
}
|
|
30534
32958
|
/**
|
|
@@ -30611,9 +33035,9 @@ var RequirementsValidationCoordinator = class {
|
|
|
30611
33035
|
}
|
|
30612
33036
|
return ok(validation);
|
|
30613
33037
|
} catch (error) {
|
|
30614
|
-
const
|
|
30615
|
-
this.failWorkflow(workflowId,
|
|
30616
|
-
return { success: false, error:
|
|
33038
|
+
const err3 = error instanceof Error ? error : new Error(String(error));
|
|
33039
|
+
this.failWorkflow(workflowId, err3.message);
|
|
33040
|
+
return { success: false, error: err3 };
|
|
30617
33041
|
}
|
|
30618
33042
|
}
|
|
30619
33043
|
// ============================================================================
|
|
@@ -31371,13 +33795,13 @@ var RequirementsValidationPlugin = class extends BaseDomainPlugin {
|
|
|
31371
33795
|
}
|
|
31372
33796
|
}
|
|
31373
33797
|
handleError(error) {
|
|
31374
|
-
const
|
|
33798
|
+
const err3 = error instanceof Error ? error : new Error(String(error));
|
|
31375
33799
|
const currentHealth = this.getHealth();
|
|
31376
33800
|
this.updateHealth({
|
|
31377
|
-
errors: [...currentHealth.errors.slice(-9),
|
|
33801
|
+
errors: [...currentHealth.errors.slice(-9), err3.message],
|
|
31378
33802
|
status: currentHealth.errors.length >= 5 ? "degraded" : currentHealth.status
|
|
31379
33803
|
});
|
|
31380
|
-
return { success: false, error:
|
|
33804
|
+
return { success: false, error: err3 };
|
|
31381
33805
|
}
|
|
31382
33806
|
trackSuccessfulOperation(_type) {
|
|
31383
33807
|
const health = this.getHealth();
|
|
@@ -33080,7 +35504,7 @@ var DEFAULT_METRIC_CONFIG = {
|
|
|
33080
35504
|
};
|
|
33081
35505
|
|
|
33082
35506
|
// src/domains/code-intelligence/services/metric-collector/loc-counter.ts
|
|
33083
|
-
import { execSync, spawnSync } from "child_process";
|
|
35507
|
+
import { execSync as execSync3, spawnSync } from "child_process";
|
|
33084
35508
|
import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync4 } from "fs";
|
|
33085
35509
|
import { join as join5, extname } from "path";
|
|
33086
35510
|
async function countLOC(projectPath, config = {}) {
|
|
@@ -33125,7 +35549,7 @@ async function tryClocCount(projectPath, config) {
|
|
|
33125
35549
|
try {
|
|
33126
35550
|
const excludeDirs = config.excludeDirs.join(",");
|
|
33127
35551
|
const command = `cloc --json --exclude-dir=${excludeDirs} "${projectPath}"`;
|
|
33128
|
-
const output =
|
|
35552
|
+
const output = execSync3(command, {
|
|
33129
35553
|
encoding: "utf-8",
|
|
33130
35554
|
timeout: config.timeout,
|
|
33131
35555
|
maxBuffer: 10 * 1024 * 1024
|
|
@@ -33159,7 +35583,7 @@ async function tryTokeiCount(projectPath, config) {
|
|
|
33159
35583
|
try {
|
|
33160
35584
|
const excludeArgs = config.excludeDirs.map((dir) => `-e "${dir}"`).join(" ");
|
|
33161
35585
|
const command = `tokei --output json ${excludeArgs} "${projectPath}"`;
|
|
33162
|
-
const output =
|
|
35586
|
+
const output = execSync3(command, {
|
|
33163
35587
|
encoding: "utf-8",
|
|
33164
35588
|
timeout: config.timeout,
|
|
33165
35589
|
maxBuffer: 10 * 1024 * 1024
|
|
@@ -33428,7 +35852,7 @@ function getBlockCommentEnd(ext) {
|
|
|
33428
35852
|
}
|
|
33429
35853
|
|
|
33430
35854
|
// src/domains/code-intelligence/services/metric-collector/test-counter.ts
|
|
33431
|
-
import { execSync as
|
|
35855
|
+
import { execSync as execSync4, spawnSync as spawnSync2 } from "child_process";
|
|
33432
35856
|
import { existsSync as existsSync6, readFileSync as readFileSync5, readdirSync as readdirSync2 } from "fs";
|
|
33433
35857
|
import { join as join6, extname as extname2 } from "path";
|
|
33434
35858
|
async function countTests(projectPath, config = {}) {
|
|
@@ -33541,7 +35965,7 @@ function checkTestRunners(projectPath) {
|
|
|
33541
35965
|
}
|
|
33542
35966
|
function countVitestTests(projectPath, config) {
|
|
33543
35967
|
try {
|
|
33544
|
-
const output =
|
|
35968
|
+
const output = execSync4(
|
|
33545
35969
|
"npx vitest list --reporter=json 2>/dev/null",
|
|
33546
35970
|
{
|
|
33547
35971
|
cwd: projectPath,
|
|
@@ -33601,7 +36025,7 @@ function countTestsRecursive(tests) {
|
|
|
33601
36025
|
}
|
|
33602
36026
|
function countJestTests(projectPath, config) {
|
|
33603
36027
|
try {
|
|
33604
|
-
const output =
|
|
36028
|
+
const output = execSync4(
|
|
33605
36029
|
"npx jest --listTests 2>/dev/null",
|
|
33606
36030
|
{
|
|
33607
36031
|
cwd: projectPath,
|
|
@@ -33627,7 +36051,7 @@ function countJestTests(projectPath, config) {
|
|
|
33627
36051
|
}
|
|
33628
36052
|
function countCargoTests(projectPath, config) {
|
|
33629
36053
|
try {
|
|
33630
|
-
const output =
|
|
36054
|
+
const output = execSync4(
|
|
33631
36055
|
'cargo test --list 2>/dev/null || echo ""',
|
|
33632
36056
|
{
|
|
33633
36057
|
cwd: projectPath,
|
|
@@ -33661,7 +36085,7 @@ function countCargoTests(projectPath, config) {
|
|
|
33661
36085
|
}
|
|
33662
36086
|
function countPytestTests(projectPath, config) {
|
|
33663
36087
|
try {
|
|
33664
|
-
const output =
|
|
36088
|
+
const output = execSync4(
|
|
33665
36089
|
'pytest --collect-only -q 2>/dev/null || echo ""',
|
|
33666
36090
|
{
|
|
33667
36091
|
cwd: projectPath,
|
|
@@ -33702,7 +36126,7 @@ function countPytestTests(projectPath, config) {
|
|
|
33702
36126
|
}
|
|
33703
36127
|
function countGoTests(projectPath, config) {
|
|
33704
36128
|
try {
|
|
33705
|
-
const output =
|
|
36129
|
+
const output = execSync4(
|
|
33706
36130
|
'go test -list ".*" ./... 2>/dev/null || echo ""',
|
|
33707
36131
|
{
|
|
33708
36132
|
cwd: projectPath,
|
|
@@ -35301,13 +37725,13 @@ var CodeIntelligencePlugin = class extends BaseDomainPlugin {
|
|
|
35301
37725
|
}
|
|
35302
37726
|
}
|
|
35303
37727
|
handleError(error) {
|
|
35304
|
-
const
|
|
37728
|
+
const err3 = error instanceof Error ? error : new Error(String(error));
|
|
35305
37729
|
const currentHealth = this.getHealth();
|
|
35306
37730
|
this.updateHealth({
|
|
35307
|
-
errors: [...currentHealth.errors.slice(-9),
|
|
37731
|
+
errors: [...currentHealth.errors.slice(-9), err3.message],
|
|
35308
37732
|
status: currentHealth.errors.length >= 5 ? "degraded" : currentHealth.status
|
|
35309
37733
|
});
|
|
35310
|
-
return { success: false, error:
|
|
37734
|
+
return { success: false, error: err3 };
|
|
35311
37735
|
}
|
|
35312
37736
|
trackSuccessfulOperation(_operation, _result) {
|
|
35313
37737
|
const health = this.getHealth();
|
|
@@ -42067,9 +44491,9 @@ var SecurityComplianceCoordinator = class {
|
|
|
42067
44491
|
await this.agentCoordinator.stop(agentResult.value);
|
|
42068
44492
|
return result;
|
|
42069
44493
|
} catch (error) {
|
|
42070
|
-
const
|
|
42071
|
-
this.failWorkflow(workflowId,
|
|
42072
|
-
return { success: false, error:
|
|
44494
|
+
const err3 = error instanceof Error ? error : new Error(String(error));
|
|
44495
|
+
this.failWorkflow(workflowId, err3.message);
|
|
44496
|
+
return { success: false, error: err3 };
|
|
42073
44497
|
}
|
|
42074
44498
|
}
|
|
42075
44499
|
/**
|
|
@@ -42094,9 +44518,9 @@ var SecurityComplianceCoordinator = class {
|
|
|
42094
44518
|
}
|
|
42095
44519
|
return result;
|
|
42096
44520
|
} catch (error) {
|
|
42097
|
-
const
|
|
42098
|
-
this.failWorkflow(workflowId,
|
|
42099
|
-
return { success: false, error:
|
|
44521
|
+
const err3 = error instanceof Error ? error : new Error(String(error));
|
|
44522
|
+
this.failWorkflow(workflowId, err3.message);
|
|
44523
|
+
return { success: false, error: err3 };
|
|
42100
44524
|
}
|
|
42101
44525
|
}
|
|
42102
44526
|
/**
|
|
@@ -42122,9 +44546,9 @@ var SecurityComplianceCoordinator = class {
|
|
|
42122
44546
|
}
|
|
42123
44547
|
return result;
|
|
42124
44548
|
} catch (error) {
|
|
42125
|
-
const
|
|
42126
|
-
this.failWorkflow(workflowId,
|
|
42127
|
-
return { success: false, error:
|
|
44549
|
+
const err3 = error instanceof Error ? error : new Error(String(error));
|
|
44550
|
+
this.failWorkflow(workflowId, err3.message);
|
|
44551
|
+
return { success: false, error: err3 };
|
|
42128
44552
|
}
|
|
42129
44553
|
}
|
|
42130
44554
|
/**
|
|
@@ -42150,9 +44574,9 @@ var SecurityComplianceCoordinator = class {
|
|
|
42150
44574
|
return result;
|
|
42151
44575
|
}
|
|
42152
44576
|
} catch (error) {
|
|
42153
|
-
const
|
|
42154
|
-
this.failWorkflow(workflowId,
|
|
42155
|
-
return { success: false, error:
|
|
44577
|
+
const err3 = error instanceof Error ? error : new Error(String(error));
|
|
44578
|
+
this.failWorkflow(workflowId, err3.message);
|
|
44579
|
+
return { success: false, error: err3 };
|
|
42156
44580
|
}
|
|
42157
44581
|
}
|
|
42158
44582
|
/**
|
|
@@ -42822,13 +45246,13 @@ var SecurityCompliancePlugin = class extends BaseDomainPlugin {
|
|
|
42822
45246
|
}
|
|
42823
45247
|
}
|
|
42824
45248
|
handleError(error) {
|
|
42825
|
-
const
|
|
45249
|
+
const err3 = error instanceof Error ? error : new Error(String(error));
|
|
42826
45250
|
const currentHealth = this.getHealth();
|
|
42827
45251
|
this.updateHealth({
|
|
42828
|
-
errors: [...currentHealth.errors.slice(-9),
|
|
45252
|
+
errors: [...currentHealth.errors.slice(-9), err3.message],
|
|
42829
45253
|
status: currentHealth.errors.length >= 5 ? "degraded" : currentHealth.status
|
|
42830
45254
|
});
|
|
42831
|
-
return { success: false, error:
|
|
45255
|
+
return { success: false, error: err3 };
|
|
42832
45256
|
}
|
|
42833
45257
|
trackSuccessfulOperation(operation) {
|
|
42834
45258
|
const health = this.getHealth();
|
|
@@ -47134,13 +49558,13 @@ var ContractTestingPlugin = class extends BaseDomainPlugin {
|
|
|
47134
49558
|
}
|
|
47135
49559
|
}
|
|
47136
49560
|
handleError(error) {
|
|
47137
|
-
const
|
|
49561
|
+
const err3 = error instanceof Error ? error : new Error(String(error));
|
|
47138
49562
|
const currentHealth = this.getHealth();
|
|
47139
49563
|
this.updateHealth({
|
|
47140
|
-
errors: [...currentHealth.errors.slice(-9),
|
|
49564
|
+
errors: [...currentHealth.errors.slice(-9), err3.message],
|
|
47141
49565
|
status: currentHealth.errors.length >= 5 ? "degraded" : currentHealth.status
|
|
47142
49566
|
});
|
|
47143
|
-
return { success: false, error:
|
|
49567
|
+
return { success: false, error: err3 };
|
|
47144
49568
|
}
|
|
47145
49569
|
trackSuccessfulOperation(_operation) {
|
|
47146
49570
|
const health = this.getHealth();
|
|
@@ -47667,7 +50091,9 @@ var DEFAULT_CONFIG36 = {
|
|
|
47667
50091
|
browserConfig: {
|
|
47668
50092
|
headless: true,
|
|
47669
50093
|
timeout: 3e4
|
|
47670
|
-
}
|
|
50094
|
+
},
|
|
50095
|
+
browserClient: void 0,
|
|
50096
|
+
preferAgentBrowser: true
|
|
47671
50097
|
};
|
|
47672
50098
|
var WCAG_CRITERIA = {
|
|
47673
50099
|
"1.1.1": { id: "1.1.1", level: "A", title: "Non-text Content" },
|
|
@@ -47691,35 +50117,49 @@ var AccessibilityTesterService = class {
|
|
|
47691
50117
|
*
|
|
47692
50118
|
* @param memory - Memory backend for storing audit results
|
|
47693
50119
|
* @param config - Service configuration options
|
|
47694
|
-
* @param vibiumClient - Optional Vibium client for browser-based testing
|
|
50120
|
+
* @param vibiumClient - Optional Vibium client for browser-based testing (legacy)
|
|
47695
50121
|
*/
|
|
47696
50122
|
constructor(memory, config = {}, vibiumClient) {
|
|
47697
50123
|
this.memory = memory;
|
|
47698
50124
|
this.config = { ...DEFAULT_CONFIG36, ...config };
|
|
47699
50125
|
this.rules = this.initializeRules();
|
|
47700
50126
|
this.vibiumClient = vibiumClient ?? null;
|
|
50127
|
+
this.browserClient = config.browserClient ?? null;
|
|
47701
50128
|
}
|
|
47702
50129
|
config;
|
|
47703
50130
|
rules;
|
|
47704
50131
|
vibiumClient;
|
|
50132
|
+
browserClient;
|
|
50133
|
+
managedBrowserClient = null;
|
|
47705
50134
|
/**
|
|
47706
50135
|
* Check if browser mode is available and enabled
|
|
47707
50136
|
*
|
|
47708
50137
|
* Browser mode requires:
|
|
47709
|
-
* 1. useBrowserMode
|
|
47710
|
-
* 2. VibiumClient instance provided
|
|
47711
|
-
* 3.
|
|
50138
|
+
* 1. useBrowserMode config setting enabled
|
|
50139
|
+
* 2. Browser client (IBrowserClient) or VibiumClient instance provided
|
|
50140
|
+
* 3. Feature flags enabled (if using Vibium)
|
|
50141
|
+
*
|
|
50142
|
+
* Priority order:
|
|
50143
|
+
* 1. Provided browserClient (from config)
|
|
50144
|
+
* 2. agent-browser (if preferAgentBrowser is true)
|
|
50145
|
+
* 3. Vibium (if available and feature flags enabled)
|
|
47712
50146
|
*
|
|
47713
50147
|
* @returns true if browser mode should be used
|
|
47714
50148
|
*/
|
|
47715
50149
|
shouldUseBrowserMode() {
|
|
47716
|
-
if (!
|
|
50150
|
+
if (!this.config.useBrowserMode) {
|
|
47717
50151
|
return false;
|
|
47718
50152
|
}
|
|
47719
|
-
if (
|
|
50153
|
+
if (this.browserClient) {
|
|
50154
|
+
return true;
|
|
50155
|
+
}
|
|
50156
|
+
if (this.config.preferAgentBrowser) {
|
|
50157
|
+
return true;
|
|
50158
|
+
}
|
|
50159
|
+
if (!isBrowserModeEnabled()) {
|
|
47720
50160
|
return false;
|
|
47721
50161
|
}
|
|
47722
|
-
if (!
|
|
50162
|
+
if (!isAxeCoreEnabled()) {
|
|
47723
50163
|
return false;
|
|
47724
50164
|
}
|
|
47725
50165
|
if (!this.vibiumClient) {
|
|
@@ -47727,11 +50167,45 @@ var AccessibilityTesterService = class {
|
|
|
47727
50167
|
}
|
|
47728
50168
|
return true;
|
|
47729
50169
|
}
|
|
50170
|
+
/**
|
|
50171
|
+
* Get or create a browser client for accessibility testing
|
|
50172
|
+
* Prefers agent-browser, falls back to Vibium
|
|
50173
|
+
*
|
|
50174
|
+
* @returns Browser client or null if unavailable
|
|
50175
|
+
*/
|
|
50176
|
+
async getBrowserClient() {
|
|
50177
|
+
if (this.browserClient) {
|
|
50178
|
+
return this.browserClient;
|
|
50179
|
+
}
|
|
50180
|
+
if (this.managedBrowserClient) {
|
|
50181
|
+
return this.managedBrowserClient;
|
|
50182
|
+
}
|
|
50183
|
+
if (this.config.preferAgentBrowser) {
|
|
50184
|
+
try {
|
|
50185
|
+
const client = await getBrowserClientForUseCase("accessibility");
|
|
50186
|
+
const available = await client.isAvailable();
|
|
50187
|
+
if (available) {
|
|
50188
|
+
this.managedBrowserClient = client;
|
|
50189
|
+
return client;
|
|
50190
|
+
}
|
|
50191
|
+
} catch {
|
|
50192
|
+
}
|
|
50193
|
+
}
|
|
50194
|
+
return null;
|
|
50195
|
+
}
|
|
50196
|
+
/**
|
|
50197
|
+
* Check if client is an IAgentBrowserClient (has getSnapshot method)
|
|
50198
|
+
*/
|
|
50199
|
+
isAgentBrowserClient(client) {
|
|
50200
|
+
return client.tool === "agent-browser" && "getSnapshot" in client;
|
|
50201
|
+
}
|
|
47730
50202
|
/**
|
|
47731
50203
|
* Run full accessibility audit
|
|
47732
50204
|
*
|
|
47733
|
-
*
|
|
47734
|
-
*
|
|
50205
|
+
* Priority order for browser-based testing:
|
|
50206
|
+
* 1. agent-browser (if preferAgentBrowser is true and available)
|
|
50207
|
+
* 2. Vibium (if available and enabled via feature flags)
|
|
50208
|
+
* 3. Heuristic mode (URL pattern analysis)
|
|
47735
50209
|
*
|
|
47736
50210
|
* @param url - URL to audit
|
|
47737
50211
|
* @param options - Audit configuration options
|
|
@@ -47741,13 +50215,30 @@ var AccessibilityTesterService = class {
|
|
|
47741
50215
|
try {
|
|
47742
50216
|
const wcagLevel = options?.wcagLevel || this.config.defaultWCAGLevel;
|
|
47743
50217
|
if (this.shouldUseBrowserMode()) {
|
|
47744
|
-
const
|
|
47745
|
-
if (
|
|
47746
|
-
await this.
|
|
47747
|
-
|
|
50218
|
+
const browserClient = await this.getBrowserClient();
|
|
50219
|
+
if (browserClient) {
|
|
50220
|
+
const browserResult = await this.auditWithBrowserClient(
|
|
50221
|
+
browserClient,
|
|
50222
|
+
url,
|
|
50223
|
+
wcagLevel,
|
|
50224
|
+
options
|
|
50225
|
+
);
|
|
50226
|
+
if (browserResult.success) {
|
|
50227
|
+
await this.storeReport(browserResult.value);
|
|
50228
|
+
return browserResult;
|
|
50229
|
+
}
|
|
50230
|
+
const errorMsg = this.getErrorMessage(browserResult);
|
|
50231
|
+
console.warn(`Browser client audit failed: ${errorMsg}`);
|
|
50232
|
+
}
|
|
50233
|
+
if (this.vibiumClient && isBrowserModeEnabled() && isAxeCoreEnabled()) {
|
|
50234
|
+
const vibiumResult = await this.auditWithBrowser(url, wcagLevel, options);
|
|
50235
|
+
if (vibiumResult.success) {
|
|
50236
|
+
await this.storeReport(vibiumResult.value);
|
|
50237
|
+
return vibiumResult;
|
|
50238
|
+
}
|
|
50239
|
+
const errorMsg = this.getErrorMessage(vibiumResult);
|
|
50240
|
+
console.warn(`Vibium audit failed, falling back to heuristic mode: ${errorMsg}`);
|
|
47748
50241
|
}
|
|
47749
|
-
const errorMsg = this.getErrorMessage(browserResult);
|
|
47750
|
-
console.warn(`Browser mode audit failed, falling back to heuristic mode: ${errorMsg}`);
|
|
47751
50242
|
}
|
|
47752
50243
|
return this.auditWithHeuristics(url, wcagLevel, options);
|
|
47753
50244
|
} catch (error) {
|
|
@@ -47755,7 +50246,208 @@ var AccessibilityTesterService = class {
|
|
|
47755
50246
|
}
|
|
47756
50247
|
}
|
|
47757
50248
|
/**
|
|
47758
|
-
* Run accessibility audit using browser
|
|
50249
|
+
* Run accessibility audit using unified browser client (agent-browser or other)
|
|
50250
|
+
*
|
|
50251
|
+
* This method uses the IBrowserClient interface for browser automation.
|
|
50252
|
+
* For agent-browser, it uses getSnapshot() for element discovery and
|
|
50253
|
+
* evaluate() to inject and run axe-core for accessibility testing.
|
|
50254
|
+
*
|
|
50255
|
+
* @param client - Browser client instance
|
|
50256
|
+
* @param url - URL to audit
|
|
50257
|
+
* @param wcagLevel - WCAG conformance level
|
|
50258
|
+
* @param options - Audit options
|
|
50259
|
+
* @returns AccessibilityReport from browser-based axe-core audit
|
|
50260
|
+
*/
|
|
50261
|
+
async auditWithBrowserClient(client, url, wcagLevel, options) {
|
|
50262
|
+
try {
|
|
50263
|
+
const launchResult = await client.launch({
|
|
50264
|
+
headless: this.config.browserConfig.headless
|
|
50265
|
+
});
|
|
50266
|
+
if (!launchResult.success) {
|
|
50267
|
+
return err(new Error(`Failed to launch browser: ${launchResult.error?.message ?? "Unknown error"}`));
|
|
50268
|
+
}
|
|
50269
|
+
try {
|
|
50270
|
+
const navResult = await client.navigate(url);
|
|
50271
|
+
if (!navResult.success) {
|
|
50272
|
+
return err(new Error(`Failed to navigate to ${url}: ${navResult.error?.message ?? "Unknown error"}`));
|
|
50273
|
+
}
|
|
50274
|
+
if (this.isAgentBrowserClient(client)) {
|
|
50275
|
+
const snapshotResult = await client.getSnapshot({ interactive: true });
|
|
50276
|
+
if (snapshotResult.success) {
|
|
50277
|
+
const elementCount = snapshotResult.value.interactiveElements.length;
|
|
50278
|
+
console.debug(`[AccessibilityTester] Found ${elementCount} interactive elements`);
|
|
50279
|
+
}
|
|
50280
|
+
}
|
|
50281
|
+
const axeResult = await this.runAxeCore(client, wcagLevel, options);
|
|
50282
|
+
if (!axeResult.success) {
|
|
50283
|
+
return err(axeResult.error);
|
|
50284
|
+
}
|
|
50285
|
+
const report = this.mapAxeResultToReport(url, axeResult.value, wcagLevel);
|
|
50286
|
+
return ok(report);
|
|
50287
|
+
} finally {
|
|
50288
|
+
await client.quit();
|
|
50289
|
+
}
|
|
50290
|
+
} catch (error) {
|
|
50291
|
+
return err(error instanceof Error ? error : new Error(String(error)));
|
|
50292
|
+
}
|
|
50293
|
+
}
|
|
50294
|
+
/**
|
|
50295
|
+
* Inject and run axe-core in the browser context
|
|
50296
|
+
*
|
|
50297
|
+
* @param client - Browser client
|
|
50298
|
+
* @param wcagLevel - WCAG conformance level
|
|
50299
|
+
* @param options - Audit options
|
|
50300
|
+
* @returns Axe-core results
|
|
50301
|
+
*/
|
|
50302
|
+
async runAxeCore(client, wcagLevel, options) {
|
|
50303
|
+
const tags = this.getAxeTagsForWcagLevel(wcagLevel);
|
|
50304
|
+
const excludeSelectors = options?.excludeSelectors ?? [];
|
|
50305
|
+
const axeScript = `
|
|
50306
|
+
(async function() {
|
|
50307
|
+
// Check if axe is already loaded
|
|
50308
|
+
if (typeof axe === 'undefined') {
|
|
50309
|
+
// Inject axe-core from CDN
|
|
50310
|
+
const script = document.createElement('script');
|
|
50311
|
+
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.8.4/axe.min.js';
|
|
50312
|
+
script.crossOrigin = 'anonymous';
|
|
50313
|
+
document.head.appendChild(script);
|
|
50314
|
+
|
|
50315
|
+
// Wait for script to load
|
|
50316
|
+
await new Promise((resolve, reject) => {
|
|
50317
|
+
script.onload = resolve;
|
|
50318
|
+
script.onerror = () => reject(new Error('Failed to load axe-core'));
|
|
50319
|
+
setTimeout(() => reject(new Error('Timeout loading axe-core')), 10000);
|
|
50320
|
+
});
|
|
50321
|
+
}
|
|
50322
|
+
|
|
50323
|
+
// Configure and run axe
|
|
50324
|
+
const config = {
|
|
50325
|
+
runOnly: {
|
|
50326
|
+
type: 'tag',
|
|
50327
|
+
values: ${JSON.stringify(tags)}
|
|
50328
|
+
},
|
|
50329
|
+
exclude: ${JSON.stringify(excludeSelectors.map((s) => [s]))}
|
|
50330
|
+
};
|
|
50331
|
+
|
|
50332
|
+
const results = await axe.run(document, config);
|
|
50333
|
+
|
|
50334
|
+
return JSON.stringify({
|
|
50335
|
+
violations: results.violations.map(v => ({
|
|
50336
|
+
id: v.id,
|
|
50337
|
+
impact: v.impact,
|
|
50338
|
+
description: v.description,
|
|
50339
|
+
help: v.help,
|
|
50340
|
+
helpUrl: v.helpUrl,
|
|
50341
|
+
tags: v.tags,
|
|
50342
|
+
nodes: v.nodes.map(n => ({
|
|
50343
|
+
selector: n.target.join(' > '),
|
|
50344
|
+
html: n.html,
|
|
50345
|
+
target: n.target,
|
|
50346
|
+
failureSummary: n.failureSummary
|
|
50347
|
+
}))
|
|
50348
|
+
})),
|
|
50349
|
+
passes: results.passes.map(p => p.id),
|
|
50350
|
+
incomplete: results.incomplete.map(i => i.id),
|
|
50351
|
+
inapplicable: results.inapplicable.map(i => i.id)
|
|
50352
|
+
});
|
|
50353
|
+
})();
|
|
50354
|
+
`;
|
|
50355
|
+
const evalResult = await client.evaluate(axeScript);
|
|
50356
|
+
if (!evalResult.success) {
|
|
50357
|
+
return err(new Error(`Failed to run axe-core: ${evalResult.error?.message ?? "Unknown error"}`));
|
|
50358
|
+
}
|
|
50359
|
+
try {
|
|
50360
|
+
const parsed = JSON.parse(evalResult.value);
|
|
50361
|
+
return ok(parsed);
|
|
50362
|
+
} catch (parseError) {
|
|
50363
|
+
return err(new Error(`Failed to parse axe-core results: ${parseError}`));
|
|
50364
|
+
}
|
|
50365
|
+
}
|
|
50366
|
+
/**
|
|
50367
|
+
* Get axe-core tags for a WCAG conformance level
|
|
50368
|
+
*/
|
|
50369
|
+
getAxeTagsForWcagLevel(level) {
|
|
50370
|
+
const baseTags = ["wcag2a", "wcag21a", "wcag22a", "best-practice"];
|
|
50371
|
+
if (level === "A") {
|
|
50372
|
+
return baseTags;
|
|
50373
|
+
}
|
|
50374
|
+
const aaTags = [...baseTags, "wcag2aa", "wcag21aa", "wcag22aa"];
|
|
50375
|
+
if (level === "AA") {
|
|
50376
|
+
return aaTags;
|
|
50377
|
+
}
|
|
50378
|
+
return [...aaTags, "wcag2aaa", "wcag21aaa", "wcag22aaa"];
|
|
50379
|
+
}
|
|
50380
|
+
/**
|
|
50381
|
+
* Map axe-core result to AccessibilityReport
|
|
50382
|
+
*/
|
|
50383
|
+
mapAxeResultToReport(url, axeResult, wcagLevel) {
|
|
50384
|
+
const violations = axeResult.violations.map((v) => ({
|
|
50385
|
+
id: v.id,
|
|
50386
|
+
impact: this.mapImpactSeverity(v.impact ?? "moderate"),
|
|
50387
|
+
wcagCriteria: this.extractWcagCriteria(v.tags ?? [], wcagLevel),
|
|
50388
|
+
description: v.description,
|
|
50389
|
+
help: v.help,
|
|
50390
|
+
helpUrl: v.helpUrl ?? `https://dequeuniversity.com/rules/axe/4.8/${v.id}`,
|
|
50391
|
+
nodes: v.nodes.map((n) => ({
|
|
50392
|
+
selector: n.selector,
|
|
50393
|
+
html: n.html,
|
|
50394
|
+
target: n.target,
|
|
50395
|
+
failureSummary: n.failureSummary ?? ""
|
|
50396
|
+
}))
|
|
50397
|
+
}));
|
|
50398
|
+
const passes = axeResult.passes.map((ruleId) => ({
|
|
50399
|
+
id: ruleId,
|
|
50400
|
+
description: `Rule ${ruleId} passed`,
|
|
50401
|
+
nodes: 0
|
|
50402
|
+
}));
|
|
50403
|
+
const incomplete = axeResult.incomplete.map((ruleId) => ({
|
|
50404
|
+
id: ruleId,
|
|
50405
|
+
description: `Rule ${ruleId} requires manual review`,
|
|
50406
|
+
reason: "Could not automatically determine compliance",
|
|
50407
|
+
nodes: []
|
|
50408
|
+
}));
|
|
50409
|
+
const totalRules = violations.length + passes.length + incomplete.length;
|
|
50410
|
+
const failedWeight = violations.reduce((sum, v) => {
|
|
50411
|
+
const weights = { critical: 4, serious: 3, moderate: 2, minor: 1 };
|
|
50412
|
+
return sum + weights[v.impact];
|
|
50413
|
+
}, 0);
|
|
50414
|
+
const maxWeight = totalRules * 4;
|
|
50415
|
+
const score = totalRules > 0 ? Math.round((maxWeight - failedWeight) / maxWeight * 100) : 100;
|
|
50416
|
+
return {
|
|
50417
|
+
url,
|
|
50418
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
50419
|
+
violations,
|
|
50420
|
+
passes,
|
|
50421
|
+
incomplete,
|
|
50422
|
+
score: Math.max(0, Math.min(100, score)),
|
|
50423
|
+
wcagLevel
|
|
50424
|
+
};
|
|
50425
|
+
}
|
|
50426
|
+
/**
|
|
50427
|
+
* Extract WCAG criteria from axe-core tags
|
|
50428
|
+
*/
|
|
50429
|
+
extractWcagCriteria(tags, defaultLevel) {
|
|
50430
|
+
const criteria = [];
|
|
50431
|
+
for (const tag of tags) {
|
|
50432
|
+
const match = tag.match(/^wcag(\d)(\d)(\d)(\d)?$/);
|
|
50433
|
+
if (match) {
|
|
50434
|
+
const id = match[4] ? `${match[1]}.${match[2]}.${match[3]}${match[4]}` : `${match[1]}.${match[2]}.${match[3]}`;
|
|
50435
|
+
const existing = WCAG_CRITERIA[id];
|
|
50436
|
+
if (existing) {
|
|
50437
|
+
criteria.push(existing);
|
|
50438
|
+
} else {
|
|
50439
|
+
criteria.push({
|
|
50440
|
+
id,
|
|
50441
|
+
level: defaultLevel,
|
|
50442
|
+
title: `WCAG ${id}`
|
|
50443
|
+
});
|
|
50444
|
+
}
|
|
50445
|
+
}
|
|
50446
|
+
}
|
|
50447
|
+
return criteria;
|
|
50448
|
+
}
|
|
50449
|
+
/**
|
|
50450
|
+
* Run accessibility audit using browser via Vibium (legacy method)
|
|
47759
50451
|
*
|
|
47760
50452
|
* @param url - URL to audit
|
|
47761
50453
|
* @param wcagLevel - WCAG conformance level
|
|
@@ -48867,6 +51559,16 @@ var AccessibilityTesterService = class {
|
|
|
48867
51559
|
}
|
|
48868
51560
|
return Math.abs(hash).toString(36);
|
|
48869
51561
|
}
|
|
51562
|
+
/**
|
|
51563
|
+
* Dispose service resources
|
|
51564
|
+
* Cleans up any managed browser clients
|
|
51565
|
+
*/
|
|
51566
|
+
async dispose() {
|
|
51567
|
+
if (this.managedBrowserClient) {
|
|
51568
|
+
await this.managedBrowserClient.dispose();
|
|
51569
|
+
this.managedBrowserClient = null;
|
|
51570
|
+
}
|
|
51571
|
+
}
|
|
48870
51572
|
};
|
|
48871
51573
|
|
|
48872
51574
|
// src/domains/visual-accessibility/services/responsive-tester.ts
|
|
@@ -49922,9 +52624,9 @@ var VisualAccessibilityCoordinator = class {
|
|
|
49922
52624
|
await this.agentCoordinator.stop(agentResult.value);
|
|
49923
52625
|
return ok(report);
|
|
49924
52626
|
} catch (error) {
|
|
49925
|
-
const
|
|
49926
|
-
this.failWorkflow(workflowId,
|
|
49927
|
-
return { success: false, error:
|
|
52627
|
+
const err3 = error instanceof Error ? error : new Error(String(error));
|
|
52628
|
+
this.failWorkflow(workflowId, err3.message);
|
|
52629
|
+
return { success: false, error: err3 };
|
|
49928
52630
|
}
|
|
49929
52631
|
}
|
|
49930
52632
|
/**
|
|
@@ -49978,9 +52680,9 @@ var VisualAccessibilityCoordinator = class {
|
|
|
49978
52680
|
await this.agentCoordinator.stop(agentResult.value);
|
|
49979
52681
|
return ok(auditReport);
|
|
49980
52682
|
} catch (error) {
|
|
49981
|
-
const
|
|
49982
|
-
this.failWorkflow(workflowId,
|
|
49983
|
-
return { success: false, error:
|
|
52683
|
+
const err3 = error instanceof Error ? error : new Error(String(error));
|
|
52684
|
+
this.failWorkflow(workflowId, err3.message);
|
|
52685
|
+
return { success: false, error: err3 };
|
|
49984
52686
|
}
|
|
49985
52687
|
}
|
|
49986
52688
|
/**
|
|
@@ -50906,13 +53608,13 @@ var VisualAccessibilityPlugin = class extends BaseDomainPlugin {
|
|
|
50906
53608
|
}
|
|
50907
53609
|
}
|
|
50908
53610
|
handleError(error) {
|
|
50909
|
-
const
|
|
53611
|
+
const err3 = error instanceof Error ? error : new Error(String(error));
|
|
50910
53612
|
const currentHealth = this.getHealth();
|
|
50911
53613
|
this.updateHealth({
|
|
50912
|
-
errors: [...currentHealth.errors.slice(-9),
|
|
53614
|
+
errors: [...currentHealth.errors.slice(-9), err3.message],
|
|
50913
53615
|
status: currentHealth.errors.length >= 5 ? "degraded" : currentHealth.status
|
|
50914
53616
|
});
|
|
50915
|
-
return { success: false, error:
|
|
53617
|
+
return { success: false, error: err3 };
|
|
50916
53618
|
}
|
|
50917
53619
|
trackSuccessfulTest(_type, _count) {
|
|
50918
53620
|
const health = this.getHealth();
|
|
@@ -51327,10 +54029,10 @@ var ChaosEngineerService = class _ChaosEngineerService {
|
|
|
51327
54029
|
socket.destroy();
|
|
51328
54030
|
resolve7(true);
|
|
51329
54031
|
});
|
|
51330
|
-
socket.on("error", (
|
|
54032
|
+
socket.on("error", (err3) => {
|
|
51331
54033
|
clearTimeout(timer);
|
|
51332
54034
|
socket.destroy();
|
|
51333
|
-
console.log(`TCP probe error: ${probe.name} -> ${
|
|
54035
|
+
console.log(`TCP probe error: ${probe.name} -> ${err3.message}`);
|
|
51334
54036
|
resolve7(false);
|
|
51335
54037
|
});
|
|
51336
54038
|
} catch (error) {
|
|
@@ -54689,13 +57391,13 @@ var ChaosResiliencePlugin = class extends BaseDomainPlugin {
|
|
|
54689
57391
|
}
|
|
54690
57392
|
}
|
|
54691
57393
|
handleError(error) {
|
|
54692
|
-
const
|
|
57394
|
+
const err3 = error instanceof Error ? error : new Error(String(error));
|
|
54693
57395
|
const currentHealth = this.getHealth();
|
|
54694
57396
|
this.updateHealth({
|
|
54695
|
-
errors: [...currentHealth.errors.slice(-9),
|
|
57397
|
+
errors: [...currentHealth.errors.slice(-9), err3.message],
|
|
54696
57398
|
status: currentHealth.errors.length >= 5 ? "degraded" : currentHealth.status
|
|
54697
57399
|
});
|
|
54698
|
-
return { success: false, error:
|
|
57400
|
+
return { success: false, error: err3 };
|
|
54699
57401
|
}
|
|
54700
57402
|
trackSuccessfulOperation(_type) {
|
|
54701
57403
|
const health = this.getHealth();
|
|
@@ -61751,8 +64453,8 @@ var MinCutHealthMonitor = class {
|
|
|
61751
64453
|
...payload
|
|
61752
64454
|
}
|
|
61753
64455
|
};
|
|
61754
|
-
this.eventBus.publish(event).catch((
|
|
61755
|
-
console.error("Failed to publish MinCut event:",
|
|
64456
|
+
this.eventBus.publish(event).catch((err3) => {
|
|
64457
|
+
console.error("Failed to publish MinCut event:", err3);
|
|
61756
64458
|
});
|
|
61757
64459
|
}
|
|
61758
64460
|
};
|
|
@@ -67518,13 +70220,13 @@ var InitOrchestrator = class {
|
|
|
67518
70220
|
db.close();
|
|
67519
70221
|
console.log(` \u2713 Version ${version} written to memory.db`);
|
|
67520
70222
|
return true;
|
|
67521
|
-
} catch (
|
|
70223
|
+
} catch (err3) {
|
|
67522
70224
|
db.close();
|
|
67523
|
-
console.warn(` \u26A0 Could not write version: ${
|
|
70225
|
+
console.warn(` \u26A0 Could not write version: ${err3 instanceof Error ? err3.message : String(err3)}`);
|
|
67524
70226
|
return false;
|
|
67525
70227
|
}
|
|
67526
|
-
} catch (
|
|
67527
|
-
console.warn(` \u26A0 Could not open memory.db: ${
|
|
70228
|
+
} catch (err3) {
|
|
70229
|
+
console.warn(` \u26A0 Could not open memory.db: ${err3 instanceof Error ? err3.message : String(err3)}`);
|
|
67528
70230
|
return false;
|
|
67529
70231
|
}
|
|
67530
70232
|
}
|
|
@@ -72530,8 +75232,8 @@ var TokenMetricsCollectorImpl = class {
|
|
|
72530
75232
|
if (this.persistenceConfig.autoSaveIntervalMs > 0) {
|
|
72531
75233
|
this.autoSaveTimer = setInterval(() => {
|
|
72532
75234
|
if (this.isDirty) {
|
|
72533
|
-
this.save().catch((
|
|
72534
|
-
console.warn("[TokenMetricsCollector] Auto-save failed:",
|
|
75235
|
+
this.save().catch((err3) => {
|
|
75236
|
+
console.warn("[TokenMetricsCollector] Auto-save failed:", err3);
|
|
72535
75237
|
});
|
|
72536
75238
|
}
|
|
72537
75239
|
}, this.persistenceConfig.autoSaveIntervalMs);
|
|
@@ -73304,10 +76006,10 @@ function parsePipelineFile(filePath) {
|
|
|
73304
76006
|
let content;
|
|
73305
76007
|
try {
|
|
73306
76008
|
content = fs10.readFileSync(filePath, "utf-8");
|
|
73307
|
-
} catch (
|
|
76009
|
+
} catch (err3) {
|
|
73308
76010
|
return {
|
|
73309
76011
|
success: false,
|
|
73310
|
-
errors: [`Failed to read file: ${
|
|
76012
|
+
errors: [`Failed to read file: ${err3}`]
|
|
73311
76013
|
};
|
|
73312
76014
|
}
|
|
73313
76015
|
return parsePipelineContent(content, filePath);
|
|
@@ -73317,10 +76019,10 @@ function parsePipelineContent(content, sourcePath) {
|
|
|
73317
76019
|
let parsed;
|
|
73318
76020
|
try {
|
|
73319
76021
|
parsed = parseYAMLContent(content);
|
|
73320
|
-
} catch (
|
|
76022
|
+
} catch (err3) {
|
|
73321
76023
|
return {
|
|
73322
76024
|
success: false,
|
|
73323
|
-
errors: [`Invalid YAML syntax: ${
|
|
76025
|
+
errors: [`Invalid YAML syntax: ${err3}`]
|
|
73324
76026
|
};
|
|
73325
76027
|
}
|
|
73326
76028
|
if (!parsed.name || typeof parsed.name !== "string") {
|
|
@@ -78159,13 +80861,13 @@ async function ensureInitialized() {
|
|
|
78159
80861
|
]);
|
|
78160
80862
|
console.log(chalk8.green("\u2713 System ready\n"));
|
|
78161
80863
|
return true;
|
|
78162
|
-
} catch (
|
|
78163
|
-
const error =
|
|
80864
|
+
} catch (err3) {
|
|
80865
|
+
const error = err3;
|
|
78164
80866
|
if (error.message.includes("timeout")) {
|
|
78165
80867
|
console.error(chalk8.red("Initialization timed out after 30 seconds."));
|
|
78166
80868
|
console.log(chalk8.yellow("Try running `aqe init` manually."));
|
|
78167
80869
|
} else {
|
|
78168
|
-
console.error(chalk8.red("Failed to auto-initialize:"),
|
|
80870
|
+
console.error(chalk8.red("Failed to auto-initialize:"), err3);
|
|
78169
80871
|
console.log(chalk8.yellow("Try running `aqe init` manually."));
|
|
78170
80872
|
}
|
|
78171
80873
|
return false;
|
|
@@ -78186,6 +80888,7 @@ async function cleanupAndExit(code = 0) {
|
|
|
78186
80888
|
if (context.kernel) {
|
|
78187
80889
|
await context.kernel.dispose();
|
|
78188
80890
|
}
|
|
80891
|
+
UnifiedMemoryManager.resetInstance();
|
|
78189
80892
|
} catch {
|
|
78190
80893
|
}
|
|
78191
80894
|
process.exit(code);
|
|
@@ -78382,7 +81085,7 @@ program.command("health").description("Check system health").option("-d, --domai
|
|
|
78382
81085
|
if (health.errors.length > 0) {
|
|
78383
81086
|
console.log(chalk8.red(`
|
|
78384
81087
|
Errors:`));
|
|
78385
|
-
health.errors.forEach((
|
|
81088
|
+
health.errors.forEach((err3) => console.log(chalk8.red(` \u2022 ${err3}`)));
|
|
78386
81089
|
}
|
|
78387
81090
|
} else {
|
|
78388
81091
|
const health = context.queen.getHealth();
|
|
@@ -78684,7 +81387,7 @@ domainCmd.command("health <domain>").description("Get domain health").action(asy
|
|
|
78684
81387
|
}
|
|
78685
81388
|
if (health.errors.length > 0) {
|
|
78686
81389
|
console.log(chalk8.red("\n Errors:"));
|
|
78687
|
-
health.errors.forEach((
|
|
81390
|
+
health.errors.forEach((err3) => console.log(chalk8.red(` \u2022 ${err3}`)));
|
|
78688
81391
|
}
|
|
78689
81392
|
console.log("");
|
|
78690
81393
|
} catch (error) {
|
|
@@ -79132,7 +81835,7 @@ program.command("test").description("Test generation shortcut").argument("<actio
|
|
|
79132
81835
|
console.log(chalk8.blue(`
|
|
79133
81836
|
\u{1F9EA} Generating tests for ${target || "current directory"}...
|
|
79134
81837
|
`));
|
|
79135
|
-
const testGenAPI = context.kernel.
|
|
81838
|
+
const testGenAPI = await context.kernel.getDomainAPIAsync("test-generation");
|
|
79136
81839
|
if (!testGenAPI) {
|
|
79137
81840
|
console.log(chalk8.red("\u274C Test generation domain not available"));
|
|
79138
81841
|
return;
|
|
@@ -79201,7 +81904,7 @@ program.command("test").description("Test generation shortcut").argument("<actio
|
|
|
79201
81904
|
console.log(chalk8.blue(`
|
|
79202
81905
|
\u{1F9EA} Executing tests in ${target || "current directory"}...
|
|
79203
81906
|
`));
|
|
79204
|
-
const testExecAPI = context.kernel.
|
|
81907
|
+
const testExecAPI = await context.kernel.getDomainAPIAsync("test-execution");
|
|
79205
81908
|
if (!testExecAPI) {
|
|
79206
81909
|
console.log(chalk8.red("\u274C Test execution domain not available"));
|
|
79207
81910
|
return;
|
|
@@ -79293,8 +81996,8 @@ program.command("coverage").description("Coverage analysis shortcut").argument("
|
|
|
79293
81996
|
detectGaps = true;
|
|
79294
81997
|
threshold = wizardResult.threshold;
|
|
79295
81998
|
console.log(chalk8.green("\n Starting coverage analysis...\n"));
|
|
79296
|
-
} catch (
|
|
79297
|
-
console.error(chalk8.red("\n Wizard error:"),
|
|
81999
|
+
} catch (err3) {
|
|
82000
|
+
console.error(chalk8.red("\n Wizard error:"), err3);
|
|
79298
82001
|
await cleanupAndExit(1);
|
|
79299
82002
|
}
|
|
79300
82003
|
}
|
|
@@ -79303,7 +82006,7 @@ program.command("coverage").description("Coverage analysis shortcut").argument("
|
|
|
79303
82006
|
console.log(chalk8.blue(`
|
|
79304
82007
|
Analyzing coverage for ${analyzeTarget}...
|
|
79305
82008
|
`));
|
|
79306
|
-
const coverageAPI = context.kernel.
|
|
82009
|
+
const coverageAPI = await context.kernel.getDomainAPIAsync("coverage-analysis");
|
|
79307
82010
|
if (!coverageAPI) {
|
|
79308
82011
|
console.log(chalk8.red("\u274C Coverage analysis domain not available"));
|
|
79309
82012
|
return;
|
|
@@ -79482,7 +82185,7 @@ program.command("security").description("Security scanning shortcut").option("--
|
|
|
79482
82185
|
console.log(chalk8.blue(`
|
|
79483
82186
|
\u{1F512} Running security scan on ${options.target}...
|
|
79484
82187
|
`));
|
|
79485
|
-
const securityAPI = context.kernel.
|
|
82188
|
+
const securityAPI = await context.kernel.getDomainAPIAsync("security-compliance");
|
|
79486
82189
|
if (!securityAPI) {
|
|
79487
82190
|
console.log(chalk8.red("\u274C Security domain not available"));
|
|
79488
82191
|
return;
|
|
@@ -79568,15 +82271,15 @@ program.command("security").description("Security scanning shortcut").option("--
|
|
|
79568
82271
|
}
|
|
79569
82272
|
console.log(chalk8.green("\u2705 Security scan complete\n"));
|
|
79570
82273
|
await cleanupAndExit(0);
|
|
79571
|
-
} catch (
|
|
79572
|
-
console.error(chalk8.red("\n\u274C Failed:"),
|
|
82274
|
+
} catch (err3) {
|
|
82275
|
+
console.error(chalk8.red("\n\u274C Failed:"), err3);
|
|
79573
82276
|
await cleanupAndExit(1);
|
|
79574
82277
|
}
|
|
79575
82278
|
});
|
|
79576
82279
|
program.command("code").description("Code intelligence analysis").argument("<action>", "Action (index|search|impact|deps)").argument("[target]", "Target path or query").option("--depth <depth>", "Analysis depth", "3").option("--include-tests", "Include test files").action(async (action, target, options) => {
|
|
79577
82280
|
if (!await ensureInitialized()) return;
|
|
79578
82281
|
try {
|
|
79579
|
-
const codeAPI = context.kernel.
|
|
82282
|
+
const codeAPI = await context.kernel.getDomainAPIAsync("code-intelligence");
|
|
79580
82283
|
if (!codeAPI) {
|
|
79581
82284
|
console.log(chalk8.red("\u274C Code intelligence domain not available"));
|
|
79582
82285
|
return;
|
|
@@ -79631,8 +82334,8 @@ program.command("code").description("Code intelligence analysis").argument("<act
|
|
|
79631
82334
|
if (idx.errors.length > 0) {
|
|
79632
82335
|
console.log(chalk8.red(`
|
|
79633
82336
|
Errors (${idx.errors.length}):`));
|
|
79634
|
-
for (const
|
|
79635
|
-
console.log(chalk8.red(` ${
|
|
82337
|
+
for (const err3 of idx.errors.slice(0, 5)) {
|
|
82338
|
+
console.log(chalk8.red(` ${err3.file}: ${err3.error}`));
|
|
79636
82339
|
}
|
|
79637
82340
|
}
|
|
79638
82341
|
} else {
|
|
@@ -79900,8 +82603,8 @@ migrateCmd.command("run").description("Run full migration from v2 to v3").option
|
|
|
79900
82603
|
if (hasClaudeAgents) copyDir(claudeAgentDir, path13.join(backupDir, ".claude", "agents"));
|
|
79901
82604
|
console.log(chalk8.green(` \u2713 Backup created at .aqe-backup/
|
|
79902
82605
|
`));
|
|
79903
|
-
} catch (
|
|
79904
|
-
console.log(chalk8.red(` \u2717 Backup failed: ${
|
|
82606
|
+
} catch (err3) {
|
|
82607
|
+
console.log(chalk8.red(` \u2717 Backup failed: ${err3}`));
|
|
79905
82608
|
await cleanupAndExit(1);
|
|
79906
82609
|
}
|
|
79907
82610
|
} else {
|
|
@@ -79916,8 +82619,8 @@ migrateCmd.command("run").description("Run full migration from v2 to v3").option
|
|
|
79916
82619
|
fs13.mkdirSync(path13.join(v3Dir, "cache"), { recursive: true });
|
|
79917
82620
|
fs13.mkdirSync(path13.join(v3Dir, "logs"), { recursive: true });
|
|
79918
82621
|
console.log(chalk8.green(" \u2713 Directory structure created\n"));
|
|
79919
|
-
} catch (
|
|
79920
|
-
console.log(chalk8.red(` \u2717 Failed: ${
|
|
82622
|
+
} catch (err3) {
|
|
82623
|
+
console.log(chalk8.red(` \u2717 Failed: ${err3}
|
|
79921
82624
|
`));
|
|
79922
82625
|
await cleanupAndExit(1);
|
|
79923
82626
|
}
|
|
@@ -79938,8 +82641,8 @@ migrateCmd.command("run").description("Run full migration from v2 to v3").option
|
|
|
79938
82641
|
const stats = fs13.statSync(v2Files.memoryDb);
|
|
79939
82642
|
console.log(chalk8.green(` \u2713 Memory database migrated (${(stats.size / 1024).toFixed(1)} KB)
|
|
79940
82643
|
`));
|
|
79941
|
-
} catch (
|
|
79942
|
-
console.log(chalk8.red(` \u2717 Migration failed: ${
|
|
82644
|
+
} catch (err3) {
|
|
82645
|
+
console.log(chalk8.red(` \u2717 Migration failed: ${err3}
|
|
79943
82646
|
`));
|
|
79944
82647
|
}
|
|
79945
82648
|
} else if (options.target && options.target !== "memory") {
|
|
@@ -79991,8 +82694,8 @@ migrateCmd.command("run").description("Run full migration from v2 to v3").option
|
|
|
79991
82694
|
const destConfig = path13.join(v3Dir, "config.json");
|
|
79992
82695
|
fs13.writeFileSync(destConfig, JSON.stringify(v3Config, null, 2));
|
|
79993
82696
|
console.log(chalk8.green(" \u2713 Configuration migrated\n"));
|
|
79994
|
-
} catch (
|
|
79995
|
-
console.log(chalk8.red(` \u2717 Config migration failed: ${
|
|
82697
|
+
} catch (err3) {
|
|
82698
|
+
console.log(chalk8.red(` \u2717 Config migration failed: ${err3}
|
|
79996
82699
|
`));
|
|
79997
82700
|
}
|
|
79998
82701
|
} else if (options.target && options.target !== "config") {
|
|
@@ -80025,8 +82728,8 @@ migrateCmd.command("run").description("Run full migration from v2 to v3").option
|
|
|
80025
82728
|
}, null, 2));
|
|
80026
82729
|
console.log(chalk8.green(` \u2713 ${migratedCount} patterns migrated
|
|
80027
82730
|
`));
|
|
80028
|
-
} catch (
|
|
80029
|
-
console.log(chalk8.red(` \u2717 Pattern migration failed: ${
|
|
82731
|
+
} catch (err3) {
|
|
82732
|
+
console.log(chalk8.red(` \u2717 Pattern migration failed: ${err3}
|
|
80030
82733
|
`));
|
|
80031
82734
|
}
|
|
80032
82735
|
} else if (options.skipPatterns) {
|
|
@@ -80079,8 +82782,8 @@ ${newFrontmatter}
|
|
|
80079
82782
|
fs13.renameSync(v2FilePath, deprecatedPath);
|
|
80080
82783
|
console.log(chalk8.gray(` ${v2Name} \u2192 ${v3Name}`));
|
|
80081
82784
|
migratedAgents++;
|
|
80082
|
-
} catch (
|
|
80083
|
-
console.log(chalk8.red(` \u2717 ${v2Name}: ${
|
|
82785
|
+
} catch (err3) {
|
|
82786
|
+
console.log(chalk8.red(` \u2717 ${v2Name}: ${err3}`));
|
|
80084
82787
|
}
|
|
80085
82788
|
}
|
|
80086
82789
|
if (migratedAgents > 0) {
|
|
@@ -80278,8 +82981,8 @@ ${newFrontmatter}
|
|
|
80278
82981
|
console.log(chalk8.green(` \u2713 Migrated ${v2Name} \u2192 ${v3Name}`));
|
|
80279
82982
|
fixedCount++;
|
|
80280
82983
|
}
|
|
80281
|
-
} catch (
|
|
80282
|
-
console.log(chalk8.red(` \u2717 Failed to migrate ${v2Name}: ${
|
|
82984
|
+
} catch (err3) {
|
|
82985
|
+
console.log(chalk8.red(` \u2717 Failed to migrate ${v2Name}: ${err3}`));
|
|
80283
82986
|
}
|
|
80284
82987
|
}
|
|
80285
82988
|
}
|
|
@@ -80404,8 +83107,8 @@ Installing completions for ${shellInfo.name}...
|
|
|
80404
83107
|
fs13.writeFileSync(completionFile, script);
|
|
80405
83108
|
console.log(chalk8.green(`Completions installed to: ${completionFile}`));
|
|
80406
83109
|
console.log(chalk8.gray("\nRestart your shell or run: source ~/.config/fish/completions/aqe.fish\n"));
|
|
80407
|
-
} catch (
|
|
80408
|
-
console.log(chalk8.red(`Failed to install: ${
|
|
83110
|
+
} catch (err3) {
|
|
83111
|
+
console.log(chalk8.red(`Failed to install: ${err3}`));
|
|
80409
83112
|
console.log(chalk8.yellow("\nManual installation:"));
|
|
80410
83113
|
console.log(getInstallInstructions("fish"));
|
|
80411
83114
|
}
|