@hasna/testers 0.0.7 → 0.0.8
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/index.js +209 -16
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +206 -10
- package/dist/lib/browser-lightpanda.d.ts +43 -0
- package/dist/lib/browser-lightpanda.d.ts.map +1 -0
- package/dist/lib/browser.d.ts +9 -3
- package/dist/lib/browser.d.ts.map +1 -1
- package/dist/lib/runner.d.ts +1 -0
- package/dist/lib/runner.d.ts.map +1 -1
- package/dist/mcp/index.js +185 -5
- package/dist/server/index.js +185 -5
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2795,6 +2795,168 @@ var init_runs = __esm(() => {
|
|
|
2795
2795
|
init_database();
|
|
2796
2796
|
});
|
|
2797
2797
|
|
|
2798
|
+
// src/lib/browser-lightpanda.ts
|
|
2799
|
+
var exports_browser_lightpanda = {};
|
|
2800
|
+
__export(exports_browser_lightpanda, {
|
|
2801
|
+
startLightpandaServer: () => startLightpandaServer,
|
|
2802
|
+
launchLightpanda: () => launchLightpanda,
|
|
2803
|
+
isLightpandaAvailable: () => isLightpandaAvailable,
|
|
2804
|
+
installLightpanda: () => installLightpanda,
|
|
2805
|
+
getLightpandaPage: () => getLightpandaPage,
|
|
2806
|
+
closeLightpanda: () => closeLightpanda
|
|
2807
|
+
});
|
|
2808
|
+
import { chromium } from "playwright";
|
|
2809
|
+
import { spawn } from "child_process";
|
|
2810
|
+
function isLightpandaAvailable() {
|
|
2811
|
+
try {
|
|
2812
|
+
const possiblePaths = [
|
|
2813
|
+
`${process.env["HOME"]}/.cache/lightpanda-node/lightpanda`,
|
|
2814
|
+
process.env["LIGHTPANDA_EXECUTABLE_PATH"]
|
|
2815
|
+
];
|
|
2816
|
+
for (const p of possiblePaths) {
|
|
2817
|
+
if (p) {
|
|
2818
|
+
try {
|
|
2819
|
+
const { existsSync: existsSync2 } = __require("fs");
|
|
2820
|
+
if (existsSync2(p))
|
|
2821
|
+
return true;
|
|
2822
|
+
} catch {
|
|
2823
|
+
continue;
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
}
|
|
2827
|
+
const { execSync } = __require("child_process");
|
|
2828
|
+
execSync("lightpanda --version", { stdio: "ignore", timeout: 5000 });
|
|
2829
|
+
return true;
|
|
2830
|
+
} catch {
|
|
2831
|
+
return false;
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
function findLightpandaBinary() {
|
|
2835
|
+
const envPath = process.env["LIGHTPANDA_EXECUTABLE_PATH"];
|
|
2836
|
+
if (envPath)
|
|
2837
|
+
return envPath;
|
|
2838
|
+
const cachePath = `${process.env["HOME"]}/.cache/lightpanda-node/lightpanda`;
|
|
2839
|
+
try {
|
|
2840
|
+
const { existsSync: existsSync2 } = __require("fs");
|
|
2841
|
+
if (existsSync2(cachePath))
|
|
2842
|
+
return cachePath;
|
|
2843
|
+
} catch {}
|
|
2844
|
+
return "lightpanda";
|
|
2845
|
+
}
|
|
2846
|
+
async function startLightpandaServer(port) {
|
|
2847
|
+
const binary = findLightpandaBinary();
|
|
2848
|
+
const cdpPort = port ?? 9222 + Math.floor(Math.random() * 1000);
|
|
2849
|
+
return new Promise((resolve, reject) => {
|
|
2850
|
+
const proc = spawn(binary, ["serve", "--port", String(cdpPort)], {
|
|
2851
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
2852
|
+
});
|
|
2853
|
+
let resolved = false;
|
|
2854
|
+
const timeout = setTimeout(() => {
|
|
2855
|
+
if (!resolved) {
|
|
2856
|
+
resolved = true;
|
|
2857
|
+
resolve({
|
|
2858
|
+
process: proc,
|
|
2859
|
+
wsEndpoint: `ws://127.0.0.1:${cdpPort}`
|
|
2860
|
+
});
|
|
2861
|
+
}
|
|
2862
|
+
}, 5000);
|
|
2863
|
+
proc.stdout?.on("data", (data) => {
|
|
2864
|
+
const output = data.toString();
|
|
2865
|
+
if (output.includes("127.0.0.1") || output.includes("listening") || output.includes("DevTools")) {
|
|
2866
|
+
if (!resolved) {
|
|
2867
|
+
resolved = true;
|
|
2868
|
+
clearTimeout(timeout);
|
|
2869
|
+
resolve({
|
|
2870
|
+
process: proc,
|
|
2871
|
+
wsEndpoint: `ws://127.0.0.1:${cdpPort}`
|
|
2872
|
+
});
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
});
|
|
2876
|
+
proc.stderr?.on("data", (data) => {
|
|
2877
|
+
const output = data.toString();
|
|
2878
|
+
if (output.includes("127.0.0.1") || output.includes("listening")) {
|
|
2879
|
+
if (!resolved) {
|
|
2880
|
+
resolved = true;
|
|
2881
|
+
clearTimeout(timeout);
|
|
2882
|
+
resolve({
|
|
2883
|
+
process: proc,
|
|
2884
|
+
wsEndpoint: `ws://127.0.0.1:${cdpPort}`
|
|
2885
|
+
});
|
|
2886
|
+
}
|
|
2887
|
+
}
|
|
2888
|
+
});
|
|
2889
|
+
proc.on("error", (err) => {
|
|
2890
|
+
clearTimeout(timeout);
|
|
2891
|
+
if (!resolved) {
|
|
2892
|
+
resolved = true;
|
|
2893
|
+
reject(new BrowserError(`Failed to start Lightpanda: ${err.message}. ` + `Install it with: bun install @lightpanda/browser`));
|
|
2894
|
+
}
|
|
2895
|
+
});
|
|
2896
|
+
proc.on("exit", (code) => {
|
|
2897
|
+
if (!resolved) {
|
|
2898
|
+
resolved = true;
|
|
2899
|
+
clearTimeout(timeout);
|
|
2900
|
+
reject(new BrowserError(`Lightpanda exited with code ${code}. ` + `Install it with: bun install @lightpanda/browser`));
|
|
2901
|
+
}
|
|
2902
|
+
});
|
|
2903
|
+
});
|
|
2904
|
+
}
|
|
2905
|
+
async function launchLightpanda(_options) {
|
|
2906
|
+
try {
|
|
2907
|
+
const { process: proc, wsEndpoint } = await startLightpandaServer();
|
|
2908
|
+
lightpandaProcess = proc;
|
|
2909
|
+
const browser = await chromium.connectOverCDP(wsEndpoint);
|
|
2910
|
+
return browser;
|
|
2911
|
+
} catch (error) {
|
|
2912
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2913
|
+
throw new BrowserError(`Failed to launch Lightpanda: ${message}`);
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
async function getLightpandaPage(browser, options) {
|
|
2917
|
+
try {
|
|
2918
|
+
const contexts = browser.contexts();
|
|
2919
|
+
const context = contexts.length > 0 ? contexts[0] : await browser.newContext({
|
|
2920
|
+
viewport: options?.viewport ?? { width: 1280, height: 720 },
|
|
2921
|
+
userAgent: options?.userAgent,
|
|
2922
|
+
locale: options?.locale
|
|
2923
|
+
});
|
|
2924
|
+
const page = await context.newPage();
|
|
2925
|
+
return page;
|
|
2926
|
+
} catch (error) {
|
|
2927
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2928
|
+
throw new BrowserError(`Failed to create Lightpanda page: ${message}`);
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
async function closeLightpanda(browser) {
|
|
2932
|
+
try {
|
|
2933
|
+
await browser.close();
|
|
2934
|
+
} catch {}
|
|
2935
|
+
if (lightpandaProcess) {
|
|
2936
|
+
try {
|
|
2937
|
+
lightpandaProcess.kill("SIGTERM");
|
|
2938
|
+
lightpandaProcess = null;
|
|
2939
|
+
} catch {}
|
|
2940
|
+
}
|
|
2941
|
+
}
|
|
2942
|
+
async function installLightpanda() {
|
|
2943
|
+
const { execSync } = __require("child_process");
|
|
2944
|
+
try {
|
|
2945
|
+
execSync("bun install @lightpanda/browser", {
|
|
2946
|
+
stdio: "inherit",
|
|
2947
|
+
cwd: process.env["HOME"]
|
|
2948
|
+
});
|
|
2949
|
+
} catch (error) {
|
|
2950
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2951
|
+
throw new BrowserError(`Failed to install Lightpanda: ${message}
|
|
2952
|
+
` + `Try manually: bun install @lightpanda/browser`);
|
|
2953
|
+
}
|
|
2954
|
+
}
|
|
2955
|
+
var lightpandaProcess = null;
|
|
2956
|
+
var init_browser_lightpanda = __esm(() => {
|
|
2957
|
+
init_types();
|
|
2958
|
+
});
|
|
2959
|
+
|
|
2798
2960
|
// src/db/flows.ts
|
|
2799
2961
|
var exports_flows = {};
|
|
2800
2962
|
__export(exports_flows, {
|
|
@@ -3050,9 +3212,9 @@ __export(exports_recorder, {
|
|
|
3050
3212
|
recordAndSave: () => recordAndSave,
|
|
3051
3213
|
actionsToScenarioInput: () => actionsToScenarioInput
|
|
3052
3214
|
});
|
|
3053
|
-
import { chromium as
|
|
3215
|
+
import { chromium as chromium3 } from "playwright";
|
|
3054
3216
|
async function recordSession(url, options) {
|
|
3055
|
-
const browser = await
|
|
3217
|
+
const browser = await chromium3.launch({ headless: false });
|
|
3056
3218
|
const context = await browser.newContext({ viewport: { width: 1280, height: 720 } });
|
|
3057
3219
|
const page = await context.newPage();
|
|
3058
3220
|
const actions = [];
|
|
@@ -3336,14 +3498,22 @@ init_scenarios();
|
|
|
3336
3498
|
|
|
3337
3499
|
// src/lib/browser.ts
|
|
3338
3500
|
init_types();
|
|
3339
|
-
import { chromium } from "playwright";
|
|
3501
|
+
import { chromium as chromium2 } from "playwright";
|
|
3340
3502
|
import { execSync } from "child_process";
|
|
3341
3503
|
var DEFAULT_VIEWPORT = { width: 1280, height: 720 };
|
|
3342
3504
|
async function launchBrowser(options) {
|
|
3505
|
+
const engine = options?.engine ?? process.env["TESTERS_BROWSER_ENGINE"] ?? "playwright";
|
|
3506
|
+
if (engine === "lightpanda") {
|
|
3507
|
+
const { launchLightpanda: launchLightpanda2, isLightpandaAvailable: isLightpandaAvailable2 } = await Promise.resolve().then(() => (init_browser_lightpanda(), exports_browser_lightpanda));
|
|
3508
|
+
if (!isLightpandaAvailable2()) {
|
|
3509
|
+
throw new BrowserError("Lightpanda not installed. Run: testers install-browser --engine lightpanda");
|
|
3510
|
+
}
|
|
3511
|
+
return launchLightpanda2({ viewport: options?.viewport });
|
|
3512
|
+
}
|
|
3343
3513
|
const headless = options?.headless ?? true;
|
|
3344
3514
|
const viewport = options?.viewport ?? DEFAULT_VIEWPORT;
|
|
3345
3515
|
try {
|
|
3346
|
-
const browser = await
|
|
3516
|
+
const browser = await chromium2.launch({
|
|
3347
3517
|
headless,
|
|
3348
3518
|
args: [
|
|
3349
3519
|
`--window-size=${viewport.width},${viewport.height}`
|
|
@@ -3356,6 +3526,11 @@ async function launchBrowser(options) {
|
|
|
3356
3526
|
}
|
|
3357
3527
|
}
|
|
3358
3528
|
async function getPage(browser, options) {
|
|
3529
|
+
const engine = options?.engine ?? "playwright";
|
|
3530
|
+
if (engine === "lightpanda") {
|
|
3531
|
+
const { getLightpandaPage: getLightpandaPage2 } = await Promise.resolve().then(() => (init_browser_lightpanda(), exports_browser_lightpanda));
|
|
3532
|
+
return getLightpandaPage2(browser, options);
|
|
3533
|
+
}
|
|
3359
3534
|
const viewport = options?.viewport ?? DEFAULT_VIEWPORT;
|
|
3360
3535
|
try {
|
|
3361
3536
|
const context = await browser.newContext({
|
|
@@ -3370,7 +3545,11 @@ async function getPage(browser, options) {
|
|
|
3370
3545
|
throw new BrowserError(`Failed to create page: ${message}`);
|
|
3371
3546
|
}
|
|
3372
3547
|
}
|
|
3373
|
-
async function closeBrowser(browser) {
|
|
3548
|
+
async function closeBrowser(browser, engine) {
|
|
3549
|
+
if (engine === "lightpanda") {
|
|
3550
|
+
const { closeLightpanda: closeLightpanda2 } = await Promise.resolve().then(() => (init_browser_lightpanda(), exports_browser_lightpanda));
|
|
3551
|
+
return closeLightpanda2(browser);
|
|
3552
|
+
}
|
|
3374
3553
|
try {
|
|
3375
3554
|
await browser.close();
|
|
3376
3555
|
} catch (error) {
|
|
@@ -3378,7 +3557,11 @@ async function closeBrowser(browser) {
|
|
|
3378
3557
|
throw new BrowserError(`Failed to close browser: ${message}`);
|
|
3379
3558
|
}
|
|
3380
3559
|
}
|
|
3381
|
-
async function installBrowser() {
|
|
3560
|
+
async function installBrowser(engine) {
|
|
3561
|
+
if (engine === "lightpanda") {
|
|
3562
|
+
const { installLightpanda: installLightpanda2 } = await Promise.resolve().then(() => (init_browser_lightpanda(), exports_browser_lightpanda));
|
|
3563
|
+
return installLightpanda2();
|
|
3564
|
+
}
|
|
3382
3565
|
try {
|
|
3383
3566
|
execSync("bunx playwright install chromium", {
|
|
3384
3567
|
stdio: "inherit"
|
|
@@ -4452,7 +4635,7 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
4452
4635
|
let browser = null;
|
|
4453
4636
|
let page = null;
|
|
4454
4637
|
try {
|
|
4455
|
-
browser = await launchBrowser({ headless: !(options.headed ?? false) });
|
|
4638
|
+
browser = await launchBrowser({ headless: !(options.headed ?? false), engine: options.engine });
|
|
4456
4639
|
page = await getPage(browser, {
|
|
4457
4640
|
viewport: config.browser.viewport
|
|
4458
4641
|
});
|
|
@@ -4517,7 +4700,7 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
4517
4700
|
return updatedResult;
|
|
4518
4701
|
} finally {
|
|
4519
4702
|
if (browser)
|
|
4520
|
-
await closeBrowser(browser);
|
|
4703
|
+
await closeBrowser(browser, options.engine);
|
|
4521
4704
|
}
|
|
4522
4705
|
}
|
|
4523
4706
|
async function runBatch(scenarios, options) {
|
|
@@ -6489,7 +6672,7 @@ program2.command("delete <id>").description("Delete a scenario").action((id) =>
|
|
|
6489
6672
|
program2.command("run [url] [description]").description("Run test scenarios against a URL").option("-t, --tag <tag>", "Filter by tag (repeatable)", (val, acc) => {
|
|
6490
6673
|
acc.push(val);
|
|
6491
6674
|
return acc;
|
|
6492
|
-
}, []).option("-s, --scenario <id>", "Run specific scenario ID").option("-p, --priority <level>", "Filter by priority").option("--headed", "Run browser in headed mode", false).option("-m, --model <model>", "AI model to use").option("--parallel <n>", "Number of parallel browsers", "1").option("--json", "Output results as JSON", false).option("-o, --output <filepath>", "Write JSON results to file").option("--timeout <ms>", "Timeout in milliseconds").option("--from-todos", "Import scenarios from todos before running", false).option("--project <id>", "Project ID").option("-b, --background", "Start run in background and return immediately", false).option("--env <name>", "Use a named environment for the URL").action(async (urlArg, description, opts) => {
|
|
6675
|
+
}, []).option("-s, --scenario <id>", "Run specific scenario ID").option("-p, --priority <level>", "Filter by priority").option("--headed", "Run browser in headed mode", false).option("-m, --model <model>", "AI model to use").option("--parallel <n>", "Number of parallel browsers", "1").option("--json", "Output results as JSON", false).option("-o, --output <filepath>", "Write JSON results to file").option("--timeout <ms>", "Timeout in milliseconds").option("--from-todos", "Import scenarios from todos before running", false).option("--project <id>", "Project ID").option("-b, --background", "Start run in background and return immediately", false).option("--browser <engine>", "Browser engine: playwright or lightpanda", "playwright").option("--env <name>", "Use a named environment for the URL").action(async (urlArg, description, opts) => {
|
|
6493
6676
|
try {
|
|
6494
6677
|
const projectId = resolveProject(opts.project);
|
|
6495
6678
|
let url = urlArg;
|
|
@@ -6529,7 +6712,8 @@ program2.command("run [url] [description]").description("Run test scenarios agai
|
|
|
6529
6712
|
headed: opts.headed,
|
|
6530
6713
|
parallel: parseInt(opts.parallel, 10),
|
|
6531
6714
|
timeout: opts.timeout ? parseInt(opts.timeout, 10) : undefined,
|
|
6532
|
-
projectId
|
|
6715
|
+
projectId,
|
|
6716
|
+
engine: opts.browser
|
|
6533
6717
|
});
|
|
6534
6718
|
console.log(chalk5.green(`Run started in background: ${chalk5.bold(runId.slice(0, 8))}`));
|
|
6535
6719
|
console.log(chalk5.dim(` Scenarios: ${scenarioCount}`));
|
|
@@ -6592,7 +6776,8 @@ program2.command("run [url] [description]").description("Run test scenarios agai
|
|
|
6592
6776
|
headed: opts.headed,
|
|
6593
6777
|
parallel: parseInt(opts.parallel, 10),
|
|
6594
6778
|
timeout: opts.timeout ? parseInt(opts.timeout, 10) : undefined,
|
|
6595
|
-
projectId
|
|
6779
|
+
projectId,
|
|
6780
|
+
engine: opts.browser
|
|
6596
6781
|
});
|
|
6597
6782
|
if (opts.json || opts.output) {
|
|
6598
6783
|
const jsonOutput = formatJSON(run2, results2);
|
|
@@ -6617,7 +6802,8 @@ program2.command("run [url] [description]").description("Run test scenarios agai
|
|
|
6617
6802
|
headed: opts.headed,
|
|
6618
6803
|
parallel: parseInt(opts.parallel, 10),
|
|
6619
6804
|
timeout: opts.timeout ? parseInt(opts.timeout, 10) : undefined,
|
|
6620
|
-
projectId
|
|
6805
|
+
projectId,
|
|
6806
|
+
engine: opts.browser
|
|
6621
6807
|
});
|
|
6622
6808
|
if (opts.json || opts.output) {
|
|
6623
6809
|
const jsonOutput = formatJSON(run, results);
|
|
@@ -6781,11 +6967,18 @@ program2.command("status").description("Show database and auth status").action((
|
|
|
6781
6967
|
process.exit(1);
|
|
6782
6968
|
}
|
|
6783
6969
|
});
|
|
6784
|
-
program2.command("install-browser").description("Install
|
|
6970
|
+
program2.command("install-browser").description("Install browser engine").option("--engine <engine>", "Engine to install: playwright, lightpanda, or all", "playwright").action(async (opts) => {
|
|
6785
6971
|
try {
|
|
6786
|
-
|
|
6787
|
-
|
|
6788
|
-
|
|
6972
|
+
if (opts.engine === "all" || opts.engine === "playwright") {
|
|
6973
|
+
console.log(chalk5.blue("Installing Playwright Chromium..."));
|
|
6974
|
+
await installBrowser("playwright");
|
|
6975
|
+
console.log(chalk5.green("Playwright Chromium installed."));
|
|
6976
|
+
}
|
|
6977
|
+
if (opts.engine === "all" || opts.engine === "lightpanda") {
|
|
6978
|
+
console.log(chalk5.blue("Installing Lightpanda..."));
|
|
6979
|
+
await installBrowser("lightpanda");
|
|
6980
|
+
console.log(chalk5.green("Lightpanda installed."));
|
|
6981
|
+
}
|
|
6789
6982
|
} catch (error) {
|
|
6790
6983
|
console.error(chalk5.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
6791
6984
|
process.exit(1);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { ScenarioPriority, RunStatus, ResultStatus, ModelPreset, ProjectRow, AgentRow, ScenarioRow, RunRow, ResultRow, ScreenshotRow, Project, Agent, Scenario, Run, Result, Screenshot, CreateScenarioInput, UpdateScenarioInput, CreateRunInput, ScenarioFilter, RunFilter, ScheduleRow, Schedule, CreateScheduleInput, UpdateScheduleInput, ScheduleFilter, FlowRow, Flow, CreateFlowInput, AuthConfig, BrowserConfig, ScreenshotConfig, TestersConfig, } from "./types/index.js";
|
|
1
|
+
export type { ScenarioPriority, RunStatus, ResultStatus, ModelPreset, BrowserEngine, ProjectRow, AgentRow, ScenarioRow, RunRow, ResultRow, ScreenshotRow, Project, Agent, Scenario, Run, Result, Screenshot, CreateScenarioInput, UpdateScenarioInput, CreateRunInput, ScenarioFilter, RunFilter, ScheduleRow, Schedule, CreateScheduleInput, UpdateScheduleInput, ScheduleFilter, FlowRow, Flow, CreateFlowInput, AuthConfig, BrowserConfig, ScreenshotConfig, TestersConfig, } from "./types/index.js";
|
|
2
2
|
export { MODEL_MAP, projectFromRow, agentFromRow, scenarioFromRow, runFromRow, resultFromRow, screenshotFromRow, scheduleFromRow, ScenarioNotFoundError, RunNotFoundError, ResultNotFoundError, VersionConflictError, BrowserError, AIClientError, TodosConnectionError, ProjectNotFoundError, AgentNotFoundError, ScheduleNotFoundError, FlowNotFoundError, DependencyCycleError, flowFromRow, } from "./types/index.js";
|
|
3
3
|
export { getDatabase, closeDatabase, resetDatabase, resolvePartialId, now, uuid, shortUuid, } from "./db/database.js";
|
|
4
4
|
export { createScenario, getScenario, getScenarioByShortId, listScenarios, updateScenario, deleteScenario, } from "./db/scenarios.js";
|
|
@@ -11,6 +11,7 @@ export { createSchedule, getSchedule, listSchedules, updateSchedule, deleteSched
|
|
|
11
11
|
export { addDependency, removeDependency, getDependencies, getDependents, getTransitiveDependencies, topologicalSort, createFlow, getFlow, listFlows, deleteFlow, } from "./db/flows.js";
|
|
12
12
|
export { loadConfig, resolveModel as resolveModelConfig, getDefaultConfig, } from "./lib/config.js";
|
|
13
13
|
export { launchBrowser, getPage, closeBrowser, BrowserPool, installBrowser, } from "./lib/browser.js";
|
|
14
|
+
export { isLightpandaAvailable, launchLightpanda, getLightpandaPage, closeLightpanda, installLightpanda, } from "./lib/browser-lightpanda.js";
|
|
14
15
|
export { Screenshotter, slugify, generateFilename, getScreenshotDir, ensureDir, } from "./lib/screenshotter.js";
|
|
15
16
|
export { createClient, resolveModel, runAgentLoop, executeTool, BROWSER_TOOLS, } from "./lib/ai-client.js";
|
|
16
17
|
export { runSingleScenario, runBatch, runByFilter, startRunAsync, onRunEvent, } from "./lib/runner.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,7 @@ var __export = (target, all) => {
|
|
|
10
10
|
});
|
|
11
11
|
};
|
|
12
12
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
13
|
+
var __require = import.meta.require;
|
|
13
14
|
|
|
14
15
|
// src/types/index.ts
|
|
15
16
|
function projectFromRow(row) {
|
|
@@ -792,6 +793,168 @@ var init_flows = __esm(() => {
|
|
|
792
793
|
init_types();
|
|
793
794
|
});
|
|
794
795
|
|
|
796
|
+
// src/lib/browser-lightpanda.ts
|
|
797
|
+
var exports_browser_lightpanda = {};
|
|
798
|
+
__export(exports_browser_lightpanda, {
|
|
799
|
+
startLightpandaServer: () => startLightpandaServer,
|
|
800
|
+
launchLightpanda: () => launchLightpanda,
|
|
801
|
+
isLightpandaAvailable: () => isLightpandaAvailable,
|
|
802
|
+
installLightpanda: () => installLightpanda,
|
|
803
|
+
getLightpandaPage: () => getLightpandaPage,
|
|
804
|
+
closeLightpanda: () => closeLightpanda
|
|
805
|
+
});
|
|
806
|
+
import { chromium } from "playwright";
|
|
807
|
+
import { spawn } from "child_process";
|
|
808
|
+
function isLightpandaAvailable() {
|
|
809
|
+
try {
|
|
810
|
+
const possiblePaths = [
|
|
811
|
+
`${process.env["HOME"]}/.cache/lightpanda-node/lightpanda`,
|
|
812
|
+
process.env["LIGHTPANDA_EXECUTABLE_PATH"]
|
|
813
|
+
];
|
|
814
|
+
for (const p of possiblePaths) {
|
|
815
|
+
if (p) {
|
|
816
|
+
try {
|
|
817
|
+
const { existsSync: existsSync3 } = __require("fs");
|
|
818
|
+
if (existsSync3(p))
|
|
819
|
+
return true;
|
|
820
|
+
} catch {
|
|
821
|
+
continue;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
const { execSync } = __require("child_process");
|
|
826
|
+
execSync("lightpanda --version", { stdio: "ignore", timeout: 5000 });
|
|
827
|
+
return true;
|
|
828
|
+
} catch {
|
|
829
|
+
return false;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
function findLightpandaBinary() {
|
|
833
|
+
const envPath = process.env["LIGHTPANDA_EXECUTABLE_PATH"];
|
|
834
|
+
if (envPath)
|
|
835
|
+
return envPath;
|
|
836
|
+
const cachePath = `${process.env["HOME"]}/.cache/lightpanda-node/lightpanda`;
|
|
837
|
+
try {
|
|
838
|
+
const { existsSync: existsSync3 } = __require("fs");
|
|
839
|
+
if (existsSync3(cachePath))
|
|
840
|
+
return cachePath;
|
|
841
|
+
} catch {}
|
|
842
|
+
return "lightpanda";
|
|
843
|
+
}
|
|
844
|
+
async function startLightpandaServer(port) {
|
|
845
|
+
const binary = findLightpandaBinary();
|
|
846
|
+
const cdpPort = port ?? 9222 + Math.floor(Math.random() * 1000);
|
|
847
|
+
return new Promise((resolve, reject) => {
|
|
848
|
+
const proc = spawn(binary, ["serve", "--port", String(cdpPort)], {
|
|
849
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
850
|
+
});
|
|
851
|
+
let resolved = false;
|
|
852
|
+
const timeout = setTimeout(() => {
|
|
853
|
+
if (!resolved) {
|
|
854
|
+
resolved = true;
|
|
855
|
+
resolve({
|
|
856
|
+
process: proc,
|
|
857
|
+
wsEndpoint: `ws://127.0.0.1:${cdpPort}`
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
}, 5000);
|
|
861
|
+
proc.stdout?.on("data", (data) => {
|
|
862
|
+
const output = data.toString();
|
|
863
|
+
if (output.includes("127.0.0.1") || output.includes("listening") || output.includes("DevTools")) {
|
|
864
|
+
if (!resolved) {
|
|
865
|
+
resolved = true;
|
|
866
|
+
clearTimeout(timeout);
|
|
867
|
+
resolve({
|
|
868
|
+
process: proc,
|
|
869
|
+
wsEndpoint: `ws://127.0.0.1:${cdpPort}`
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
});
|
|
874
|
+
proc.stderr?.on("data", (data) => {
|
|
875
|
+
const output = data.toString();
|
|
876
|
+
if (output.includes("127.0.0.1") || output.includes("listening")) {
|
|
877
|
+
if (!resolved) {
|
|
878
|
+
resolved = true;
|
|
879
|
+
clearTimeout(timeout);
|
|
880
|
+
resolve({
|
|
881
|
+
process: proc,
|
|
882
|
+
wsEndpoint: `ws://127.0.0.1:${cdpPort}`
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
});
|
|
887
|
+
proc.on("error", (err) => {
|
|
888
|
+
clearTimeout(timeout);
|
|
889
|
+
if (!resolved) {
|
|
890
|
+
resolved = true;
|
|
891
|
+
reject(new BrowserError(`Failed to start Lightpanda: ${err.message}. ` + `Install it with: bun install @lightpanda/browser`));
|
|
892
|
+
}
|
|
893
|
+
});
|
|
894
|
+
proc.on("exit", (code) => {
|
|
895
|
+
if (!resolved) {
|
|
896
|
+
resolved = true;
|
|
897
|
+
clearTimeout(timeout);
|
|
898
|
+
reject(new BrowserError(`Lightpanda exited with code ${code}. ` + `Install it with: bun install @lightpanda/browser`));
|
|
899
|
+
}
|
|
900
|
+
});
|
|
901
|
+
});
|
|
902
|
+
}
|
|
903
|
+
async function launchLightpanda(_options) {
|
|
904
|
+
try {
|
|
905
|
+
const { process: proc, wsEndpoint } = await startLightpandaServer();
|
|
906
|
+
lightpandaProcess = proc;
|
|
907
|
+
const browser = await chromium.connectOverCDP(wsEndpoint);
|
|
908
|
+
return browser;
|
|
909
|
+
} catch (error) {
|
|
910
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
911
|
+
throw new BrowserError(`Failed to launch Lightpanda: ${message}`);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
async function getLightpandaPage(browser, options) {
|
|
915
|
+
try {
|
|
916
|
+
const contexts = browser.contexts();
|
|
917
|
+
const context = contexts.length > 0 ? contexts[0] : await browser.newContext({
|
|
918
|
+
viewport: options?.viewport ?? { width: 1280, height: 720 },
|
|
919
|
+
userAgent: options?.userAgent,
|
|
920
|
+
locale: options?.locale
|
|
921
|
+
});
|
|
922
|
+
const page = await context.newPage();
|
|
923
|
+
return page;
|
|
924
|
+
} catch (error) {
|
|
925
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
926
|
+
throw new BrowserError(`Failed to create Lightpanda page: ${message}`);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
async function closeLightpanda(browser) {
|
|
930
|
+
try {
|
|
931
|
+
await browser.close();
|
|
932
|
+
} catch {}
|
|
933
|
+
if (lightpandaProcess) {
|
|
934
|
+
try {
|
|
935
|
+
lightpandaProcess.kill("SIGTERM");
|
|
936
|
+
lightpandaProcess = null;
|
|
937
|
+
} catch {}
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
async function installLightpanda() {
|
|
941
|
+
const { execSync } = __require("child_process");
|
|
942
|
+
try {
|
|
943
|
+
execSync("bun install @lightpanda/browser", {
|
|
944
|
+
stdio: "inherit",
|
|
945
|
+
cwd: process.env["HOME"]
|
|
946
|
+
});
|
|
947
|
+
} catch (error) {
|
|
948
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
949
|
+
throw new BrowserError(`Failed to install Lightpanda: ${message}
|
|
950
|
+
` + `Try manually: bun install @lightpanda/browser`);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
var lightpandaProcess = null;
|
|
954
|
+
var init_browser_lightpanda = __esm(() => {
|
|
955
|
+
init_types();
|
|
956
|
+
});
|
|
957
|
+
|
|
795
958
|
// src/index.ts
|
|
796
959
|
init_types();
|
|
797
960
|
init_database();
|
|
@@ -1337,14 +1500,22 @@ function resolveModel(nameOrId) {
|
|
|
1337
1500
|
}
|
|
1338
1501
|
// src/lib/browser.ts
|
|
1339
1502
|
init_types();
|
|
1340
|
-
import { chromium } from "playwright";
|
|
1503
|
+
import { chromium as chromium2 } from "playwright";
|
|
1341
1504
|
import { execSync } from "child_process";
|
|
1342
1505
|
var DEFAULT_VIEWPORT = { width: 1280, height: 720 };
|
|
1343
1506
|
async function launchBrowser(options) {
|
|
1507
|
+
const engine = options?.engine ?? process.env["TESTERS_BROWSER_ENGINE"] ?? "playwright";
|
|
1508
|
+
if (engine === "lightpanda") {
|
|
1509
|
+
const { launchLightpanda: launchLightpanda2, isLightpandaAvailable: isLightpandaAvailable2 } = await Promise.resolve().then(() => (init_browser_lightpanda(), exports_browser_lightpanda));
|
|
1510
|
+
if (!isLightpandaAvailable2()) {
|
|
1511
|
+
throw new BrowserError("Lightpanda not installed. Run: testers install-browser --engine lightpanda");
|
|
1512
|
+
}
|
|
1513
|
+
return launchLightpanda2({ viewport: options?.viewport });
|
|
1514
|
+
}
|
|
1344
1515
|
const headless = options?.headless ?? true;
|
|
1345
1516
|
const viewport = options?.viewport ?? DEFAULT_VIEWPORT;
|
|
1346
1517
|
try {
|
|
1347
|
-
const browser = await
|
|
1518
|
+
const browser = await chromium2.launch({
|
|
1348
1519
|
headless,
|
|
1349
1520
|
args: [
|
|
1350
1521
|
`--window-size=${viewport.width},${viewport.height}`
|
|
@@ -1357,6 +1528,11 @@ async function launchBrowser(options) {
|
|
|
1357
1528
|
}
|
|
1358
1529
|
}
|
|
1359
1530
|
async function getPage(browser, options) {
|
|
1531
|
+
const engine = options?.engine ?? "playwright";
|
|
1532
|
+
if (engine === "lightpanda") {
|
|
1533
|
+
const { getLightpandaPage: getLightpandaPage2 } = await Promise.resolve().then(() => (init_browser_lightpanda(), exports_browser_lightpanda));
|
|
1534
|
+
return getLightpandaPage2(browser, options);
|
|
1535
|
+
}
|
|
1360
1536
|
const viewport = options?.viewport ?? DEFAULT_VIEWPORT;
|
|
1361
1537
|
try {
|
|
1362
1538
|
const context = await browser.newContext({
|
|
@@ -1371,7 +1547,11 @@ async function getPage(browser, options) {
|
|
|
1371
1547
|
throw new BrowserError(`Failed to create page: ${message}`);
|
|
1372
1548
|
}
|
|
1373
1549
|
}
|
|
1374
|
-
async function closeBrowser(browser) {
|
|
1550
|
+
async function closeBrowser(browser, engine) {
|
|
1551
|
+
if (engine === "lightpanda") {
|
|
1552
|
+
const { closeLightpanda: closeLightpanda2 } = await Promise.resolve().then(() => (init_browser_lightpanda(), exports_browser_lightpanda));
|
|
1553
|
+
return closeLightpanda2(browser);
|
|
1554
|
+
}
|
|
1375
1555
|
try {
|
|
1376
1556
|
await browser.close();
|
|
1377
1557
|
} catch (error) {
|
|
@@ -1385,26 +1565,29 @@ class BrowserPool {
|
|
|
1385
1565
|
maxSize;
|
|
1386
1566
|
headless;
|
|
1387
1567
|
viewport;
|
|
1568
|
+
engine;
|
|
1388
1569
|
constructor(size, options) {
|
|
1389
1570
|
this.maxSize = size;
|
|
1390
1571
|
this.headless = options?.headless ?? true;
|
|
1391
1572
|
this.viewport = options?.viewport ?? DEFAULT_VIEWPORT;
|
|
1573
|
+
this.engine = options?.engine ?? "playwright";
|
|
1392
1574
|
}
|
|
1393
1575
|
async acquire() {
|
|
1394
1576
|
const idle = this.pool.find((entry) => !entry.inUse);
|
|
1395
1577
|
if (idle) {
|
|
1396
1578
|
idle.inUse = true;
|
|
1397
|
-
const page = await getPage(idle.browser, { viewport: this.viewport });
|
|
1579
|
+
const page = await getPage(idle.browser, { viewport: this.viewport, engine: this.engine });
|
|
1398
1580
|
return { browser: idle.browser, page };
|
|
1399
1581
|
}
|
|
1400
1582
|
if (this.pool.length < this.maxSize) {
|
|
1401
1583
|
const browser = await launchBrowser({
|
|
1402
1584
|
headless: this.headless,
|
|
1403
|
-
viewport: this.viewport
|
|
1585
|
+
viewport: this.viewport,
|
|
1586
|
+
engine: this.engine
|
|
1404
1587
|
});
|
|
1405
1588
|
const entry = { browser, inUse: true };
|
|
1406
1589
|
this.pool.push(entry);
|
|
1407
|
-
const page = await getPage(browser, { viewport: this.viewport });
|
|
1590
|
+
const page = await getPage(browser, { viewport: this.viewport, engine: this.engine });
|
|
1408
1591
|
return { browser, page };
|
|
1409
1592
|
}
|
|
1410
1593
|
return new Promise((resolve, reject) => {
|
|
@@ -1413,7 +1596,7 @@ class BrowserPool {
|
|
|
1413
1596
|
if (available) {
|
|
1414
1597
|
clearInterval(interval);
|
|
1415
1598
|
available.inUse = true;
|
|
1416
|
-
getPage(available.browser, { viewport: this.viewport }).then((page) => resolve({ browser: available.browser, page })).catch(reject);
|
|
1599
|
+
getPage(available.browser, { viewport: this.viewport, engine: this.engine }).then((page) => resolve({ browser: available.browser, page })).catch(reject);
|
|
1417
1600
|
}
|
|
1418
1601
|
}, 50);
|
|
1419
1602
|
});
|
|
@@ -1430,7 +1613,11 @@ class BrowserPool {
|
|
|
1430
1613
|
this.pool.length = 0;
|
|
1431
1614
|
}
|
|
1432
1615
|
}
|
|
1433
|
-
async function installBrowser() {
|
|
1616
|
+
async function installBrowser(engine) {
|
|
1617
|
+
if (engine === "lightpanda") {
|
|
1618
|
+
const { installLightpanda: installLightpanda2 } = await Promise.resolve().then(() => (init_browser_lightpanda(), exports_browser_lightpanda));
|
|
1619
|
+
return installLightpanda2();
|
|
1620
|
+
}
|
|
1434
1621
|
try {
|
|
1435
1622
|
execSync("bunx playwright install chromium", {
|
|
1436
1623
|
stdio: "inherit"
|
|
@@ -1440,6 +1627,10 @@ async function installBrowser() {
|
|
|
1440
1627
|
throw new BrowserError(`Failed to install browser: ${message}`);
|
|
1441
1628
|
}
|
|
1442
1629
|
}
|
|
1630
|
+
|
|
1631
|
+
// src/index.ts
|
|
1632
|
+
init_browser_lightpanda();
|
|
1633
|
+
|
|
1443
1634
|
// src/lib/screenshotter.ts
|
|
1444
1635
|
import { mkdirSync as mkdirSync2, existsSync as existsSync3, writeFileSync } from "fs";
|
|
1445
1636
|
import { join as join3 } from "path";
|
|
@@ -2514,7 +2705,7 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
2514
2705
|
let browser = null;
|
|
2515
2706
|
let page = null;
|
|
2516
2707
|
try {
|
|
2517
|
-
browser = await launchBrowser({ headless: !(options.headed ?? false) });
|
|
2708
|
+
browser = await launchBrowser({ headless: !(options.headed ?? false), engine: options.engine });
|
|
2518
2709
|
page = await getPage(browser, {
|
|
2519
2710
|
viewport: config.browser.viewport
|
|
2520
2711
|
});
|
|
@@ -2579,7 +2770,7 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
2579
2770
|
return updatedResult;
|
|
2580
2771
|
} finally {
|
|
2581
2772
|
if (browser)
|
|
2582
|
-
await closeBrowser(browser);
|
|
2773
|
+
await closeBrowser(browser, options.engine);
|
|
2583
2774
|
}
|
|
2584
2775
|
}
|
|
2585
2776
|
async function runBatch(scenarios, options) {
|
|
@@ -4861,7 +5052,10 @@ export {
|
|
|
4861
5052
|
listFlows,
|
|
4862
5053
|
listAuthPresets,
|
|
4863
5054
|
listAgents,
|
|
5055
|
+
launchLightpanda,
|
|
4864
5056
|
launchBrowser,
|
|
5057
|
+
isLightpandaAvailable,
|
|
5058
|
+
installLightpanda,
|
|
4865
5059
|
installBrowser,
|
|
4866
5060
|
initProject,
|
|
4867
5061
|
importFromTodos,
|
|
@@ -4883,6 +5077,7 @@ export {
|
|
|
4883
5077
|
getProject,
|
|
4884
5078
|
getPage,
|
|
4885
5079
|
getNextRunTime,
|
|
5080
|
+
getLightpandaPage,
|
|
4886
5081
|
getFlow,
|
|
4887
5082
|
getExitCode,
|
|
4888
5083
|
getEnabledSchedules,
|
|
@@ -4932,6 +5127,7 @@ export {
|
|
|
4932
5127
|
createClient,
|
|
4933
5128
|
createAuthPreset,
|
|
4934
5129
|
connectToTodos,
|
|
5130
|
+
closeLightpanda,
|
|
4935
5131
|
closeDatabase,
|
|
4936
5132
|
closeBrowser,
|
|
4937
5133
|
checkBudget,
|