@hasna/testers 0.0.61 → 0.0.63
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 +149 -8
- package/dist/index.js +5 -3
- package/dist/lib/browser.d.ts +3 -1
- package/dist/lib/browser.d.ts.map +1 -1
- package/dist/lib/workflow-fanout.d.ts +16 -0
- package/dist/lib/workflow-fanout.d.ts.map +1 -1
- package/dist/mcp/index.js +6 -4
- package/dist/server/index.js +6 -4
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -13997,14 +13997,15 @@ async function launchBrowserEngine(engine, config) {
|
|
|
13997
13997
|
}
|
|
13998
13998
|
return launchPlaywright({ headless: config.headless, viewport: config.viewport });
|
|
13999
13999
|
}
|
|
14000
|
-
async function installBrowser(engine) {
|
|
14000
|
+
async function installBrowser(engine, options = {}) {
|
|
14001
14001
|
if (engine === "lightpanda") {
|
|
14002
14002
|
const { installLightpanda: installLightpanda2 } = await Promise.resolve().then(() => (init_browser_lightpanda(), exports_browser_lightpanda));
|
|
14003
14003
|
return installLightpanda2();
|
|
14004
14004
|
}
|
|
14005
14005
|
const browserName = engine === "playwright-firefox" ? "firefox" : engine === "playwright-webkit" ? "webkit" : "chromium";
|
|
14006
|
+
const dependencyFlag = options.withDeps ? " --with-deps" : "";
|
|
14006
14007
|
try {
|
|
14007
|
-
execSync(`bunx playwright install ${browserName}`, {
|
|
14008
|
+
execSync(`bunx playwright install${dependencyFlag} ${browserName}`, {
|
|
14008
14009
|
stdio: "inherit"
|
|
14009
14010
|
});
|
|
14010
14011
|
} catch (error) {
|
|
@@ -27430,7 +27431,8 @@ function buildSandboxCommand(input) {
|
|
|
27430
27431
|
input.packageSpec,
|
|
27431
27432
|
"install-browser",
|
|
27432
27433
|
"--engine",
|
|
27433
|
-
"playwright"
|
|
27434
|
+
"playwright",
|
|
27435
|
+
"--with-deps"
|
|
27434
27436
|
];
|
|
27435
27437
|
return [
|
|
27436
27438
|
"set -euo pipefail",
|
|
@@ -60780,6 +60782,9 @@ function resolveWorkflowFanoutSelection(options) {
|
|
|
60780
60782
|
async function checkWorkflowFanoutReadiness(workflows, dependencies = {}) {
|
|
60781
60783
|
const checks = [];
|
|
60782
60784
|
const env = dependencies.env ?? process.env;
|
|
60785
|
+
const model = resolveModel(dependencies.model ?? loadConfig().defaultModel);
|
|
60786
|
+
const modelProvider = detectProvider(model);
|
|
60787
|
+
const modelEnvKey = MODEL_PROVIDER_ENV_KEYS[modelProvider];
|
|
60783
60788
|
for (const [provider, providerWorkflows] of groupWorkflowsByProvider(workflows)) {
|
|
60784
60789
|
const envKey = PROVIDER_ENV_KEYS[provider];
|
|
60785
60790
|
if (!envKey) {
|
|
@@ -60855,6 +60860,37 @@ async function checkWorkflowFanoutReadiness(workflows, dependencies = {}) {
|
|
|
60855
60860
|
details: { missing: optionalMissing }
|
|
60856
60861
|
});
|
|
60857
60862
|
}
|
|
60863
|
+
const modelCredentialResolution = collectModelCredentialReadiness(workflows, modelProvider, modelEnvKey, env, dependencies.credentialResolver);
|
|
60864
|
+
checks.push({
|
|
60865
|
+
name: `model:${modelProvider}`,
|
|
60866
|
+
ok: modelCredentialResolution.missing.length === 0,
|
|
60867
|
+
required: true,
|
|
60868
|
+
message: modelCredentialResolution.missing.length === 0 ? `Model provider "${modelProvider}" credential is available for ${workflows.length} workflow(s)` : `Missing sandbox model credential for "${modelProvider}". Add ${modelEnvKey} to workflow sandbox env or choose a model for a provider with credentials`,
|
|
60869
|
+
workflows: modelCredentialResolution.missing.length > 0 ? [...new Set(modelCredentialResolution.missing.map((item) => item.workflowName))] : workflows.map((workflow) => workflow.name),
|
|
60870
|
+
details: {
|
|
60871
|
+
provider: modelProvider,
|
|
60872
|
+
model,
|
|
60873
|
+
envKey: modelEnvKey,
|
|
60874
|
+
...modelCredentialResolution.missing.length > 0 ? { missing: modelCredentialResolution.missing } : {}
|
|
60875
|
+
}
|
|
60876
|
+
});
|
|
60877
|
+
if (dependencies.validateModelCredentials && modelCredentialResolution.available.length > 0) {
|
|
60878
|
+
const validator = dependencies.modelCredentialValidator ?? defaultModelCredentialValidator;
|
|
60879
|
+
const sample = modelCredentialResolution.available[0];
|
|
60880
|
+
const validation = await validator({ provider: modelProvider, model, apiKey: sample.apiKey });
|
|
60881
|
+
checks.push({
|
|
60882
|
+
name: `model:${modelProvider}:live`,
|
|
60883
|
+
ok: validation.ok,
|
|
60884
|
+
required: true,
|
|
60885
|
+
message: validation.ok ? `Model provider "${modelProvider}" credential passed live validation` : `Model provider "${modelProvider}" credential failed live validation${validation.status ? ` (${validation.status})` : ""}: ${validation.message ?? "provider rejected the credential"}`,
|
|
60886
|
+
workflows: workflows.map((workflow) => workflow.name),
|
|
60887
|
+
details: {
|
|
60888
|
+
provider: modelProvider,
|
|
60889
|
+
model,
|
|
60890
|
+
status: validation.status
|
|
60891
|
+
}
|
|
60892
|
+
});
|
|
60893
|
+
}
|
|
60858
60894
|
return {
|
|
60859
60895
|
ok: checks.every((check) => check.ok || !check.required),
|
|
60860
60896
|
checks
|
|
@@ -60887,6 +60923,9 @@ async function runWorkflowFanout(options, dependencies = {}) {
|
|
|
60887
60923
|
} = dependencies;
|
|
60888
60924
|
const preflight = preflightOverride ? await preflightOverride(workflows) : await checkWorkflowFanoutReadiness(workflows, {
|
|
60889
60925
|
providerApiKeyResolver,
|
|
60926
|
+
model: options.model,
|
|
60927
|
+
validateModelCredentials: options.validateModelCredentials,
|
|
60928
|
+
modelCredentialValidator: dependencies.modelCredentialValidator,
|
|
60890
60929
|
commandExists,
|
|
60891
60930
|
credentialResolver,
|
|
60892
60931
|
env
|
|
@@ -61068,6 +61107,28 @@ function collectMissingSandboxEnvRefs(workflows, env, credentialResolver) {
|
|
|
61068
61107
|
}
|
|
61069
61108
|
return { requiredMissing, optionalMissing };
|
|
61070
61109
|
}
|
|
61110
|
+
function collectModelCredentialReadiness(workflows, provider, envKey, env, credentialResolver) {
|
|
61111
|
+
const missing = [];
|
|
61112
|
+
const available = [];
|
|
61113
|
+
for (const workflow of workflows) {
|
|
61114
|
+
const reference = workflow.execution.env?.[envKey];
|
|
61115
|
+
if (!reference) {
|
|
61116
|
+
missing.push({ workflowId: workflow.id, workflowName: workflow.name, provider, key: envKey });
|
|
61117
|
+
continue;
|
|
61118
|
+
}
|
|
61119
|
+
const resolved = reference.startsWith("$?") ? resolveOptionalSandboxEnvReference(reference, env) : isResolvableEnvReference(reference) ? resolveSandboxEnvReference(reference, env, credentialResolver) : reference;
|
|
61120
|
+
if (!resolved) {
|
|
61121
|
+
missing.push({ workflowId: workflow.id, workflowName: workflow.name, provider, key: envKey, reference });
|
|
61122
|
+
continue;
|
|
61123
|
+
}
|
|
61124
|
+
available.push({ workflowId: workflow.id, workflowName: workflow.name, provider, key: envKey, apiKey: resolved });
|
|
61125
|
+
}
|
|
61126
|
+
return { missing, available };
|
|
61127
|
+
}
|
|
61128
|
+
function resolveOptionalSandboxEnvReference(value, env) {
|
|
61129
|
+
const varName = value.slice(2).trim();
|
|
61130
|
+
return varName ? env[varName] ?? null : null;
|
|
61131
|
+
}
|
|
61071
61132
|
function isResolvableEnvReference(value) {
|
|
61072
61133
|
return value.startsWith("$") || value.startsWith("@secrets:");
|
|
61073
61134
|
}
|
|
@@ -61078,20 +61139,99 @@ function resolveSandboxEnvReference(value, env, credentialResolver) {
|
|
|
61078
61139
|
}
|
|
61079
61140
|
return (credentialResolver ?? resolveCredential)(value);
|
|
61080
61141
|
}
|
|
61142
|
+
async function defaultModelCredentialValidator(input) {
|
|
61143
|
+
const endpoint = getModelCredentialValidationEndpoint(input.provider);
|
|
61144
|
+
if (!endpoint) {
|
|
61145
|
+
return { ok: true, message: `No live validation endpoint configured for provider ${input.provider}` };
|
|
61146
|
+
}
|
|
61147
|
+
try {
|
|
61148
|
+
const response = await fetch(endpoint.url, {
|
|
61149
|
+
headers: endpoint.headers(input.apiKey)
|
|
61150
|
+
});
|
|
61151
|
+
if (response.ok)
|
|
61152
|
+
return { ok: true, status: response.status };
|
|
61153
|
+
const text = await response.text().catch(() => "");
|
|
61154
|
+
return {
|
|
61155
|
+
ok: false,
|
|
61156
|
+
status: response.status,
|
|
61157
|
+
message: summarizeModelCredentialValidationError(text) ?? response.statusText
|
|
61158
|
+
};
|
|
61159
|
+
} catch (error) {
|
|
61160
|
+
return {
|
|
61161
|
+
ok: false,
|
|
61162
|
+
message: error instanceof Error ? error.message : String(error)
|
|
61163
|
+
};
|
|
61164
|
+
}
|
|
61165
|
+
}
|
|
61166
|
+
function getModelCredentialValidationEndpoint(provider) {
|
|
61167
|
+
if (provider === "anthropic") {
|
|
61168
|
+
return {
|
|
61169
|
+
url: "https://api.anthropic.com/v1/models",
|
|
61170
|
+
headers: (apiKey) => ({
|
|
61171
|
+
"x-api-key": apiKey,
|
|
61172
|
+
"anthropic-version": "2023-06-01"
|
|
61173
|
+
})
|
|
61174
|
+
};
|
|
61175
|
+
}
|
|
61176
|
+
if (provider === "openai") {
|
|
61177
|
+
return {
|
|
61178
|
+
url: "https://api.openai.com/v1/models",
|
|
61179
|
+
headers: (apiKey) => ({ Authorization: `Bearer ${apiKey}` })
|
|
61180
|
+
};
|
|
61181
|
+
}
|
|
61182
|
+
if (provider === "cerebras") {
|
|
61183
|
+
return {
|
|
61184
|
+
url: "https://api.cerebras.ai/v1/models",
|
|
61185
|
+
headers: (apiKey) => ({ Authorization: `Bearer ${apiKey}` })
|
|
61186
|
+
};
|
|
61187
|
+
}
|
|
61188
|
+
if (provider === "zai") {
|
|
61189
|
+
return {
|
|
61190
|
+
url: "https://api.z.ai/api/paas/v4/models",
|
|
61191
|
+
headers: (apiKey) => ({ Authorization: `Bearer ${apiKey}` })
|
|
61192
|
+
};
|
|
61193
|
+
}
|
|
61194
|
+
if (provider === "google") {
|
|
61195
|
+
return {
|
|
61196
|
+
url: "https://generativelanguage.googleapis.com/v1beta/openai/models",
|
|
61197
|
+
headers: (apiKey) => ({ Authorization: `Bearer ${apiKey}` })
|
|
61198
|
+
};
|
|
61199
|
+
}
|
|
61200
|
+
return null;
|
|
61201
|
+
}
|
|
61202
|
+
function summarizeModelCredentialValidationError(text) {
|
|
61203
|
+
if (!text.trim())
|
|
61204
|
+
return;
|
|
61205
|
+
try {
|
|
61206
|
+
const parsed = JSON.parse(text);
|
|
61207
|
+
return parsed.error?.type ?? parsed.error?.code ?? parsed.error?.message ?? parsed.message;
|
|
61208
|
+
} catch {
|
|
61209
|
+
return text.trim().slice(0, 200);
|
|
61210
|
+
}
|
|
61211
|
+
}
|
|
61081
61212
|
function summarizePreflightFailures(preflight) {
|
|
61082
61213
|
const requiredFailures = preflight.checks.filter((check) => !check.ok && check.required);
|
|
61083
61214
|
return requiredFailures.length > 0 ? requiredFailures.map((check) => check.message).join("; ") : "required checks did not pass";
|
|
61084
61215
|
}
|
|
61085
|
-
var PROVIDER_ENV_KEYS;
|
|
61216
|
+
var PROVIDER_ENV_KEYS, MODEL_PROVIDER_ENV_KEYS;
|
|
61086
61217
|
var init_workflow_fanout = __esm(() => {
|
|
61087
61218
|
init_workflows();
|
|
61088
61219
|
init_workflow_runner();
|
|
61089
61220
|
init_secrets_resolver();
|
|
61221
|
+
init_ai_client();
|
|
61222
|
+
init_config2();
|
|
61090
61223
|
PROVIDER_ENV_KEYS = {
|
|
61091
61224
|
e2b: "E2B_API_KEY",
|
|
61092
61225
|
daytona: "DAYTONA_API_KEY",
|
|
61093
61226
|
modal: "MODAL_TOKEN_ID"
|
|
61094
61227
|
};
|
|
61228
|
+
MODEL_PROVIDER_ENV_KEYS = {
|
|
61229
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
61230
|
+
openai: "OPENAI_API_KEY",
|
|
61231
|
+
google: "GOOGLE_API_KEY",
|
|
61232
|
+
cerebras: "CEREBRAS_API_KEY",
|
|
61233
|
+
zai: "ZAI_API_KEY"
|
|
61234
|
+
};
|
|
61095
61235
|
});
|
|
61096
61236
|
|
|
61097
61237
|
// node_modules/@ai-sdk/provider/dist/index.mjs
|
|
@@ -95751,7 +95891,7 @@ import chalk6 from "chalk";
|
|
|
95751
95891
|
// package.json
|
|
95752
95892
|
var package_default = {
|
|
95753
95893
|
name: "@hasna/testers",
|
|
95754
|
-
version: "0.0.
|
|
95894
|
+
version: "0.0.63",
|
|
95755
95895
|
description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
|
|
95756
95896
|
type: "module",
|
|
95757
95897
|
main: "dist/index.js",
|
|
@@ -99966,11 +100106,11 @@ program2.command("status").description("Show database and auth status").action((
|
|
|
99966
100106
|
process.exit(1);
|
|
99967
100107
|
}
|
|
99968
100108
|
});
|
|
99969
|
-
program2.command("install-browser").description("Install browser engine").option("--engine <engine>", "Engine to install: playwright, lightpanda, or all", "playwright").action(async (opts) => {
|
|
100109
|
+
program2.command("install-browser").description("Install browser engine").option("--engine <engine>", "Engine to install: playwright, lightpanda, or all", "playwright").option("--with-deps", "Install Playwright OS dependencies for fresh Linux sandboxes", false).action(async (opts) => {
|
|
99970
100110
|
try {
|
|
99971
100111
|
if (opts.engine === "all" || opts.engine === "playwright") {
|
|
99972
100112
|
log(chalk6.blue("Installing Playwright Chromium..."));
|
|
99973
|
-
await installBrowser("playwright");
|
|
100113
|
+
await installBrowser("playwright", { withDeps: opts.withDeps });
|
|
99974
100114
|
log(chalk6.green("Playwright Chromium installed."));
|
|
99975
100115
|
}
|
|
99976
100116
|
if (opts.engine === "all" || opts.engine === "lightpanda") {
|
|
@@ -102536,7 +102676,7 @@ workflowCmd.command("run <id>").description("Run a saved testing workflow").requ
|
|
|
102536
102676
|
workflowCmd.command("fanout [ids...]").description("Run multiple saved sandbox workflows concurrently").requiredOption("-u, --url <url>", "Target URL").option("--project <id>", "Project ID").option("--tag <tag>", "Workflow scenario tag filter (repeatable)", (val, acc) => {
|
|
102537
102677
|
acc.push(val);
|
|
102538
102678
|
return acc;
|
|
102539
|
-
}, []).option("--all", "Include disabled workflows when selecting by project/tag", false).option("--workers <n>", "Concurrent sandboxes, 1-12 (default: 6)", "6").option("--batch-size <n>", "Limit this run to a batch of selected workflows").option("--batch <n>", "1-based batch number to run with --batch-size").option("--offset <n>", "0-based selected-workflow offset for staged fanout").option("--all-batches", "Run all selected workflow batches sequentially with --batch-size", false).option("--from-batch <n>", "First batch to run when using --all-batches").option("--to-batch <n>", "Last batch to run when using --all-batches").option("--continue-on-failure", "Continue later batches after a failed batch", false).option("-m, --model <model>", "AI model").option("--headed", "Run headed", false).option("--parallel <n>", "Parallel browser workers inside each sandbox").option("--timeout <ms>", "Override workflow timeout").option("--dry-run", "Print resolved sandbox plans without spawning sandboxes", false).option("--json", "Output as JSON", false).action(async (ids, opts) => {
|
|
102679
|
+
}, []).option("--all", "Include disabled workflows when selecting by project/tag", false).option("--workers <n>", "Concurrent sandboxes, 1-12 (default: 6)", "6").option("--batch-size <n>", "Limit this run to a batch of selected workflows").option("--batch <n>", "1-based batch number to run with --batch-size").option("--offset <n>", "0-based selected-workflow offset for staged fanout").option("--all-batches", "Run all selected workflow batches sequentially with --batch-size", false).option("--from-batch <n>", "First batch to run when using --all-batches").option("--to-batch <n>", "Last batch to run when using --all-batches").option("--continue-on-failure", "Continue later batches after a failed batch", false).option("-m, --model <model>", "AI model").option("--headed", "Run headed", false).option("--parallel <n>", "Parallel browser workers inside each sandbox").option("--timeout <ms>", "Override workflow timeout").option("--validate-model-credentials", "Call model provider auth endpoints during fanout preflight", false).option("--dry-run", "Print resolved sandbox plans without spawning sandboxes", false).option("--json", "Output as JSON", false).action(async (ids, opts) => {
|
|
102540
102680
|
try {
|
|
102541
102681
|
const fanoutOptions = {
|
|
102542
102682
|
workflowIds: ids,
|
|
@@ -102555,6 +102695,7 @@ workflowCmd.command("fanout [ids...]").description("Run multiple saved sandbox w
|
|
|
102555
102695
|
headed: opts.headed,
|
|
102556
102696
|
parallel: opts.parallel ? parseInt(opts.parallel, 10) : undefined,
|
|
102557
102697
|
timeout: opts.timeout ? parseInt(opts.timeout, 10) : undefined,
|
|
102698
|
+
validateModelCredentials: opts.validateModelCredentials,
|
|
102558
102699
|
dryRun: opts.dryRun
|
|
102559
102700
|
};
|
|
102560
102701
|
const runAllBatches = opts.allBatches || opts.fromBatch !== undefined || opts.toBatch !== undefined;
|
package/dist/index.js
CHANGED
|
@@ -11546,14 +11546,15 @@ async function launchBrowserEngine(engine, config) {
|
|
|
11546
11546
|
}
|
|
11547
11547
|
return launchPlaywright({ headless: config.headless, viewport: config.viewport });
|
|
11548
11548
|
}
|
|
11549
|
-
async function installBrowser(engine) {
|
|
11549
|
+
async function installBrowser(engine, options = {}) {
|
|
11550
11550
|
if (engine === "lightpanda") {
|
|
11551
11551
|
const { installLightpanda: installLightpanda2 } = await Promise.resolve().then(() => (init_browser_lightpanda(), exports_browser_lightpanda));
|
|
11552
11552
|
return installLightpanda2();
|
|
11553
11553
|
}
|
|
11554
11554
|
const browserName = engine === "playwright-firefox" ? "firefox" : engine === "playwright-webkit" ? "webkit" : "chromium";
|
|
11555
|
+
const dependencyFlag = options.withDeps ? " --with-deps" : "";
|
|
11555
11556
|
try {
|
|
11556
|
-
execSync(`bunx playwright install ${browserName}`, {
|
|
11557
|
+
execSync(`bunx playwright install${dependencyFlag} ${browserName}`, {
|
|
11557
11558
|
stdio: "inherit"
|
|
11558
11559
|
});
|
|
11559
11560
|
} catch (error) {
|
|
@@ -17546,7 +17547,8 @@ function buildSandboxCommand(input) {
|
|
|
17546
17547
|
input.packageSpec,
|
|
17547
17548
|
"install-browser",
|
|
17548
17549
|
"--engine",
|
|
17549
|
-
"playwright"
|
|
17550
|
+
"playwright",
|
|
17551
|
+
"--with-deps"
|
|
17550
17552
|
];
|
|
17551
17553
|
return [
|
|
17552
17554
|
"set -euo pipefail",
|
package/dist/lib/browser.d.ts
CHANGED
|
@@ -73,6 +73,8 @@ export declare function launchBrowserEngine(engine: import("../types/index.js").
|
|
|
73
73
|
/**
|
|
74
74
|
* Installs Chromium for Playwright using bunx.
|
|
75
75
|
*/
|
|
76
|
-
export declare function installBrowser(engine?: import("../types/index.js").BrowserEngine
|
|
76
|
+
export declare function installBrowser(engine?: import("../types/index.js").BrowserEngine, options?: {
|
|
77
|
+
withDeps?: boolean;
|
|
78
|
+
}): Promise<void>;
|
|
77
79
|
export {};
|
|
78
80
|
//# sourceMappingURL=browser.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/lib/browser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAYhD,UAAU,YAAY;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,aAAa;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,mBAAmB,EAAE,aAAa,CAAC;CACpD;AAED,UAAU,WAAW;IACnB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AASD;;GAEG;AACH,wBAAsB,aAAa,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAoE7E;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAC3B,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,WAAW,GAAG;IAAE,MAAM,CAAC,EAAE,OAAO,mBAAmB,EAAE,aAAa,CAAA;CAAE,GAC7E,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,OAAO,mBAAmB,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBtH;AAED;;;GAGG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAmB;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IAExC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4C;gBAGjE,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,YAAY,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,mBAAmB,EAAE,aAAa,CAAA;KAAE;IAQ/G;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,IAAI,CAAA;KAAE,CAAC;IAqC1D;;OAEG;IACH,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAO/B;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAShC;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,YAAY,CAAC;CACzB;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,OAAO,mBAAmB,EAAE,aAAa,EACjD,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,OAAO,CAAC,CAalB;AAED;;GAEG;AACH,wBAAsB,cAAc,
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/lib/browser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAYhD,UAAU,YAAY;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,aAAa;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,mBAAmB,EAAE,aAAa,CAAC;CACpD;AAED,UAAU,WAAW;IACnB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AASD;;GAEG;AACH,wBAAsB,aAAa,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAoE7E;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAC3B,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,WAAW,GAAG;IAAE,MAAM,CAAC,EAAE,OAAO,mBAAmB,EAAE,aAAa,CAAA;CAAE,GAC7E,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,OAAO,mBAAmB,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBtH;AAED;;;GAGG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAmB;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IAExC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4C;gBAGjE,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,YAAY,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,mBAAmB,EAAE,aAAa,CAAA;KAAE;IAQ/G;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,IAAI,CAAA;KAAE,CAAC;IAqC1D;;OAEG;IACH,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAO/B;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAShC;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,YAAY,CAAC;CACzB;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,OAAO,mBAAmB,EAAE,aAAa,EACjD,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,OAAO,CAAC,CAalB;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,MAAM,CAAC,EAAE,OAAO,mBAAmB,EAAE,aAAa,EAClD,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAO,GACnC,OAAO,CAAC,IAAI,CAAC,CAiBf"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { runTestingWorkflow, type WorkflowRunOptions, type WorkflowRunnerDependencies } from "./workflow-runner.js";
|
|
2
|
+
import { type AIProvider } from "./ai-client.js";
|
|
2
3
|
import type { TestingWorkflow } from "../types/index.js";
|
|
3
4
|
export interface WorkflowFanoutOptions extends WorkflowRunOptions {
|
|
4
5
|
workflowIds?: string[];
|
|
@@ -12,6 +13,7 @@ export interface WorkflowFanoutOptions extends WorkflowRunOptions {
|
|
|
12
13
|
batchStart?: number;
|
|
13
14
|
batchEnd?: number;
|
|
14
15
|
continueOnFailure?: boolean;
|
|
16
|
+
validateModelCredentials?: boolean;
|
|
15
17
|
}
|
|
16
18
|
export interface WorkflowFanoutItem {
|
|
17
19
|
workflowId: string;
|
|
@@ -53,6 +55,7 @@ export interface WorkflowFanoutDependencies extends WorkflowRunnerDependencies {
|
|
|
53
55
|
runTestingWorkflow?: typeof runTestingWorkflow;
|
|
54
56
|
preflight?: (workflows: TestingWorkflow[]) => WorkflowFanoutPreflightResult | Promise<WorkflowFanoutPreflightResult>;
|
|
55
57
|
providerApiKeyResolver?: (provider: string, env: Record<string, string | undefined>) => string | undefined | Promise<string | undefined>;
|
|
58
|
+
modelCredentialValidator?: (input: WorkflowFanoutModelCredentialValidationInput) => Promise<WorkflowFanoutModelCredentialValidationResult>;
|
|
56
59
|
commandExists?: (command: string) => boolean;
|
|
57
60
|
credentialResolver?: (value: string) => string | null;
|
|
58
61
|
env?: Record<string, string | undefined>;
|
|
@@ -79,10 +82,23 @@ export interface WorkflowFanoutSelection {
|
|
|
79
82
|
}
|
|
80
83
|
interface WorkflowFanoutPreflightDependencies {
|
|
81
84
|
providerApiKeyResolver?: WorkflowFanoutDependencies["providerApiKeyResolver"];
|
|
85
|
+
model?: string;
|
|
86
|
+
validateModelCredentials?: boolean;
|
|
87
|
+
modelCredentialValidator?: WorkflowFanoutDependencies["modelCredentialValidator"];
|
|
82
88
|
commandExists?: WorkflowFanoutDependencies["commandExists"];
|
|
83
89
|
credentialResolver?: WorkflowFanoutDependencies["credentialResolver"];
|
|
84
90
|
env?: Record<string, string | undefined>;
|
|
85
91
|
}
|
|
92
|
+
export interface WorkflowFanoutModelCredentialValidationInput {
|
|
93
|
+
provider: AIProvider;
|
|
94
|
+
model: string;
|
|
95
|
+
apiKey: string;
|
|
96
|
+
}
|
|
97
|
+
export interface WorkflowFanoutModelCredentialValidationResult {
|
|
98
|
+
ok: boolean;
|
|
99
|
+
status?: number;
|
|
100
|
+
message?: string;
|
|
101
|
+
}
|
|
86
102
|
export declare function normalizeFanoutWorkerCount(value: number | undefined): number;
|
|
87
103
|
export declare function resolveWorkflowFanoutBatch(workflows: TestingWorkflow[], options?: Pick<WorkflowFanoutOptions, "batchSize" | "batch" | "offset">): {
|
|
88
104
|
workflows: TestingWorkflow[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-fanout.d.ts","sourceRoot":"","sources":["../../src/lib/workflow-fanout.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,KAAK,kBAAkB,EAAE,KAAK,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAEpH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD,MAAM,WAAW,qBAAsB,SAAQ,kBAAkB;IAC/D,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"workflow-fanout.d.ts","sourceRoot":"","sources":["../../src/lib/workflow-fanout.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp/index.js
CHANGED
|
@@ -52,7 +52,7 @@ var package_default;
|
|
|
52
52
|
var init_package = __esm(() => {
|
|
53
53
|
package_default = {
|
|
54
54
|
name: "@hasna/testers",
|
|
55
|
-
version: "0.0.
|
|
55
|
+
version: "0.0.63",
|
|
56
56
|
description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
|
|
57
57
|
type: "module",
|
|
58
58
|
main: "dist/index.js",
|
|
@@ -16592,14 +16592,15 @@ async function launchBrowserEngine(engine, config) {
|
|
|
16592
16592
|
}
|
|
16593
16593
|
return launchPlaywright({ headless: config.headless, viewport: config.viewport });
|
|
16594
16594
|
}
|
|
16595
|
-
async function installBrowser(engine) {
|
|
16595
|
+
async function installBrowser(engine, options = {}) {
|
|
16596
16596
|
if (engine === "lightpanda") {
|
|
16597
16597
|
const { installLightpanda: installLightpanda2 } = await Promise.resolve().then(() => (init_browser_lightpanda(), exports_browser_lightpanda));
|
|
16598
16598
|
return installLightpanda2();
|
|
16599
16599
|
}
|
|
16600
16600
|
const browserName = engine === "playwright-firefox" ? "firefox" : engine === "playwright-webkit" ? "webkit" : "chromium";
|
|
16601
|
+
const dependencyFlag = options.withDeps ? " --with-deps" : "";
|
|
16601
16602
|
try {
|
|
16602
|
-
execSync(`bunx playwright install ${browserName}`, {
|
|
16603
|
+
execSync(`bunx playwright install${dependencyFlag} ${browserName}`, {
|
|
16603
16604
|
stdio: "inherit"
|
|
16604
16605
|
});
|
|
16605
16606
|
} catch (error) {
|
|
@@ -23821,7 +23822,8 @@ function buildSandboxCommand(input) {
|
|
|
23821
23822
|
input.packageSpec,
|
|
23822
23823
|
"install-browser",
|
|
23823
23824
|
"--engine",
|
|
23824
|
-
"playwright"
|
|
23825
|
+
"playwright",
|
|
23826
|
+
"--with-deps"
|
|
23825
23827
|
];
|
|
23826
23828
|
return [
|
|
23827
23829
|
"set -euo pipefail",
|
package/dist/server/index.js
CHANGED
|
@@ -15306,14 +15306,15 @@ async function launchBrowserEngine(engine, config) {
|
|
|
15306
15306
|
}
|
|
15307
15307
|
return launchPlaywright({ headless: config.headless, viewport: config.viewport });
|
|
15308
15308
|
}
|
|
15309
|
-
async function installBrowser(engine) {
|
|
15309
|
+
async function installBrowser(engine, options = {}) {
|
|
15310
15310
|
if (engine === "lightpanda") {
|
|
15311
15311
|
const { installLightpanda: installLightpanda2 } = await Promise.resolve().then(() => (init_browser_lightpanda(), exports_browser_lightpanda));
|
|
15312
15312
|
return installLightpanda2();
|
|
15313
15313
|
}
|
|
15314
15314
|
const browserName = engine === "playwright-firefox" ? "firefox" : engine === "playwright-webkit" ? "webkit" : "chromium";
|
|
15315
|
+
const dependencyFlag = options.withDeps ? " --with-deps" : "";
|
|
15315
15316
|
try {
|
|
15316
|
-
execSync(`bunx playwright install ${browserName}`, {
|
|
15317
|
+
execSync(`bunx playwright install${dependencyFlag} ${browserName}`, {
|
|
15317
15318
|
stdio: "inherit"
|
|
15318
15319
|
});
|
|
15319
15320
|
} catch (error) {
|
|
@@ -47090,7 +47091,7 @@ import { join as join14 } from "path";
|
|
|
47090
47091
|
// package.json
|
|
47091
47092
|
var package_default = {
|
|
47092
47093
|
name: "@hasna/testers",
|
|
47093
|
-
version: "0.0.
|
|
47094
|
+
version: "0.0.63",
|
|
47094
47095
|
description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
|
|
47095
47096
|
type: "module",
|
|
47096
47097
|
main: "dist/index.js",
|
|
@@ -51662,7 +51663,8 @@ function buildSandboxCommand(input) {
|
|
|
51662
51663
|
input.packageSpec,
|
|
51663
51664
|
"install-browser",
|
|
51664
51665
|
"--engine",
|
|
51665
|
-
"playwright"
|
|
51666
|
+
"playwright",
|
|
51667
|
+
"--with-deps"
|
|
51666
51668
|
];
|
|
51667
51669
|
return [
|
|
51668
51670
|
"set -euo pipefail",
|