@hasna/testers 0.0.56 → 0.0.58

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -39,9 +39,11 @@ testers workflow create "Projects CRUD" \
39
39
 
40
40
  testers workflow fanout --project alumia --workers 6 --url https://preview.example.com
41
41
  testers workflow fanout wf_abc,wf_def wf_xyz --workers 12 --url https://preview.example.com --json
42
+ testers workflow fanout --project alumia --tag action-specific --workers 6 --batch-size 12 --batch 1 --url https://preview.example.com
43
+ testers workflow fanout --project alumia --tag action-specific --workers 6 --batch-size 12 --all-batches --url https://preview.example.com
42
44
  ```
43
45
 
44
- `--workers` is bounded to 1-12 concurrent sandboxes. Fanout preflights provider credentials, required sandbox environment references, `rsync`, and app source directories before launching workers. Use `--dry-run` to inspect the remote commands, upload plans, and preflight checks without spawning sandboxes.
46
+ `--workers` is bounded to 1-12 concurrent sandboxes. Use `--batch-size` with a 1-based `--batch` to run large workflow corpora in deterministic waves, or `--all-batches` with optional `--from-batch` / `--to-batch` to run a staged range in one command. `--offset` is available for a manual selected-workflow cursor. Fanout preflights provider credentials, required sandbox environment references, `rsync`, and app source directories before launching workers. Use `--dry-run` to inspect the remote commands, upload plans, and preflight checks without spawning sandboxes.
45
47
 
46
48
  ### Next.js Route and Action Inventory
47
49
 
@@ -63,7 +65,7 @@ testers inventory next /path/to/app \
63
65
  --sandbox-app-wait-url http://127.0.0.1:3000/health \
64
66
  --sandbox-env-optional OPENAI_API_KEY
65
67
 
66
- testers workflow fanout --project alumia --tag action-specific --workers 6 --url https://preview.example.com --dry-run
68
+ testers workflow fanout --project alumia --tag action-specific --workers 6 --batch-size 12 --all-batches --from-batch 1 --to-batch 3 --url https://preview.example.com --dry-run
67
69
  ```
68
70
 
69
71
  Use `--action-workflow-grouping action` for one workflow per discovered action, `route` for route-specific workflows, or `area-kind` for broader workflows such as commerce buttons or admin API methods. Add the `--sandbox-app-*` flags when the sandbox should rsync, install, start, and test the app source instead of only testing an already-running URL.
package/dist/cli/index.js CHANGED
@@ -60626,8 +60626,11 @@ var init_agents = __esm(() => {
60626
60626
  // src/lib/workflow-fanout.ts
60627
60627
  var exports_workflow_fanout = {};
60628
60628
  __export(exports_workflow_fanout, {
60629
+ runWorkflowFanoutBatches: () => runWorkflowFanoutBatches,
60629
60630
  runWorkflowFanout: () => runWorkflowFanout,
60630
60631
  resolveWorkflowFanoutSelection: () => resolveWorkflowFanoutSelection,
60632
+ resolveWorkflowFanoutBatchRange: () => resolveWorkflowFanoutBatchRange,
60633
+ resolveWorkflowFanoutBatch: () => resolveWorkflowFanoutBatch,
60631
60634
  normalizeFanoutWorkerCount: () => normalizeFanoutWorkerCount,
60632
60635
  checkWorkflowFanoutReadiness: () => checkWorkflowFanoutReadiness
60633
60636
  });
@@ -60643,6 +60646,55 @@ function normalizeFanoutWorkerCount(value) {
60643
60646
  }
60644
60647
  return workers;
60645
60648
  }
60649
+ function resolveWorkflowFanoutBatch(workflows, options = {}) {
60650
+ const batchSize = normalizeOptionalPositiveInteger(options.batchSize, "workflow fanout batch size");
60651
+ const batch = normalizeOptionalPositiveInteger(options.batch, "workflow fanout batch");
60652
+ const offset = normalizeOptionalNonNegativeInteger(options.offset, "workflow fanout offset");
60653
+ if (batch !== undefined && offset !== undefined) {
60654
+ throw new Error("workflow fanout batch and offset cannot both be set");
60655
+ }
60656
+ if (batch !== undefined && batchSize === undefined) {
60657
+ throw new Error("workflow fanout batch requires batch size");
60658
+ }
60659
+ const resolvedOffset = batch !== undefined && batchSize !== undefined ? (batch - 1) * batchSize : offset ?? 0;
60660
+ const limit = batchSize;
60661
+ const selected = workflows.slice(resolvedOffset, limit === undefined ? undefined : resolvedOffset + limit);
60662
+ if (selected.length === 0) {
60663
+ throw new Error(`No testing workflows matched the fanout batch selection (matched ${workflows.length}, offset ${resolvedOffset})`);
60664
+ }
60665
+ return {
60666
+ workflows: selected,
60667
+ selection: {
60668
+ matched: workflows.length,
60669
+ offset: resolvedOffset,
60670
+ ...limit !== undefined ? { limit } : {},
60671
+ ...batch !== undefined ? { batch } : {},
60672
+ ...batchSize !== undefined ? { batchSize, totalBatches: Math.ceil(workflows.length / batchSize) } : {}
60673
+ }
60674
+ };
60675
+ }
60676
+ function resolveWorkflowFanoutBatchRange(matched, options) {
60677
+ const batchSize = normalizeOptionalPositiveInteger(options.batchSize, "workflow fanout batch size");
60678
+ if (batchSize === undefined) {
60679
+ throw new Error("workflow fanout batch range requires batch size");
60680
+ }
60681
+ if (matched < 1) {
60682
+ throw new Error("workflow fanout batch range requires at least one matched workflow");
60683
+ }
60684
+ const totalBatches = Math.ceil(matched / batchSize);
60685
+ const batchStart = normalizeOptionalPositiveInteger(options.batchStart, "workflow fanout start batch") ?? 1;
60686
+ const batchEnd = normalizeOptionalPositiveInteger(options.batchEnd, "workflow fanout end batch") ?? totalBatches;
60687
+ if (batchStart > batchEnd) {
60688
+ throw new Error("workflow fanout start batch must be less than or equal to end batch");
60689
+ }
60690
+ if (batchStart > totalBatches) {
60691
+ throw new Error(`workflow fanout start batch ${batchStart} exceeds total batches ${totalBatches}`);
60692
+ }
60693
+ if (batchEnd > totalBatches) {
60694
+ throw new Error(`workflow fanout end batch ${batchEnd} exceeds total batches ${totalBatches}`);
60695
+ }
60696
+ return { batchSize, batchStart, batchEnd, totalBatches };
60697
+ }
60646
60698
  function resolveWorkflowFanoutSelection(options) {
60647
60699
  const ids = splitWorkflowIds(options.workflowIds);
60648
60700
  const workflows = ids.length > 0 ? ids.map((id) => {
@@ -60762,7 +60814,8 @@ async function mapWithConcurrency(items, limit, worker) {
60762
60814
  }
60763
60815
  async function runWorkflowFanout(options, dependencies = {}) {
60764
60816
  const workers = normalizeFanoutWorkerCount(options.workers);
60765
- const workflows = resolveWorkflowFanoutSelection(options);
60817
+ const matchedWorkflows = resolveWorkflowFanoutSelection(options);
60818
+ const { workflows, selection } = resolveWorkflowFanoutBatch(matchedWorkflows, options);
60766
60819
  const {
60767
60820
  runTestingWorkflow: runOne = runTestingWorkflow,
60768
60821
  preflight: preflightOverride,
@@ -60783,6 +60836,7 @@ async function runWorkflowFanout(options, dependencies = {}) {
60783
60836
  return {
60784
60837
  status: "failed",
60785
60838
  workers,
60839
+ selection,
60786
60840
  total: workflows.length,
60787
60841
  passed: 0,
60788
60842
  failed: workflows.length,
@@ -60838,6 +60892,7 @@ async function runWorkflowFanout(options, dependencies = {}) {
60838
60892
  return {
60839
60893
  status: dryRun ? "dry-run" : failed > 0 ? "failed" : "passed",
60840
60894
  workers,
60895
+ selection,
60841
60896
  total: items.length,
60842
60897
  passed,
60843
60898
  failed,
@@ -60845,6 +60900,49 @@ async function runWorkflowFanout(options, dependencies = {}) {
60845
60900
  preflight
60846
60901
  };
60847
60902
  }
60903
+ async function runWorkflowFanoutBatches(options, dependencies = {}) {
60904
+ if (options.batch !== undefined || options.offset !== undefined) {
60905
+ throw new Error("workflow fanout all-batches cannot be combined with batch or offset");
60906
+ }
60907
+ const workers = normalizeFanoutWorkerCount(options.workers);
60908
+ const matchedWorkflows = resolveWorkflowFanoutSelection(options);
60909
+ const { batchSize, batchStart, batchEnd, totalBatches } = resolveWorkflowFanoutBatchRange(matchedWorkflows.length, options);
60910
+ const batches = [];
60911
+ let stoppedEarly = false;
60912
+ for (let batch = batchStart;batch <= batchEnd; batch++) {
60913
+ const result = await runWorkflowFanout({
60914
+ ...options,
60915
+ batchSize,
60916
+ batch,
60917
+ batchStart: undefined,
60918
+ batchEnd: undefined,
60919
+ offset: undefined
60920
+ }, dependencies);
60921
+ batches.push(result);
60922
+ if (result.status === "failed" && !options.continueOnFailure) {
60923
+ stoppedEarly = batch < batchEnd;
60924
+ break;
60925
+ }
60926
+ }
60927
+ const total = batches.reduce((sum, batch) => sum + batch.total, 0);
60928
+ const passed = batches.reduce((sum, batch) => sum + batch.passed, 0);
60929
+ const failed = batches.reduce((sum, batch) => sum + batch.failed, 0);
60930
+ const dryRun = options.dryRun === true;
60931
+ return {
60932
+ status: dryRun ? "dry-run" : failed > 0 || stoppedEarly ? "failed" : "passed",
60933
+ workers,
60934
+ matched: matchedWorkflows.length,
60935
+ batchSize,
60936
+ batchStart,
60937
+ batchEnd,
60938
+ totalBatches,
60939
+ stoppedEarly,
60940
+ total,
60941
+ passed,
60942
+ failed,
60943
+ batches
60944
+ };
60945
+ }
60848
60946
  function groupWorkflowsByProvider(workflows) {
60849
60947
  const byProvider = new Map;
60850
60948
  for (const workflow of workflows) {
@@ -60871,6 +60969,24 @@ function defaultCommandExists(command) {
60871
60969
  const result = spawnSync2(command, ["--version"], { encoding: "utf8" });
60872
60970
  return !result.error && result.status === 0;
60873
60971
  }
60972
+ function normalizeOptionalPositiveInteger(value, label) {
60973
+ if (value === undefined)
60974
+ return;
60975
+ const normalized = Math.floor(value);
60976
+ if (!Number.isFinite(normalized) || normalized < 1) {
60977
+ throw new Error(`${label} must be a positive integer`);
60978
+ }
60979
+ return normalized;
60980
+ }
60981
+ function normalizeOptionalNonNegativeInteger(value, label) {
60982
+ if (value === undefined)
60983
+ return;
60984
+ const normalized = Math.floor(value);
60985
+ if (!Number.isFinite(normalized) || normalized < 0) {
60986
+ throw new Error(`${label} must be a non-negative integer`);
60987
+ }
60988
+ return normalized;
60989
+ }
60874
60990
  function collectMissingSandboxEnvRefs(workflows, env, credentialResolver) {
60875
60991
  const requiredMissing = [];
60876
60992
  const optionalMissing = [];
@@ -95575,7 +95691,7 @@ import chalk6 from "chalk";
95575
95691
  // package.json
95576
95692
  var package_default = {
95577
95693
  name: "@hasna/testers",
95578
- version: "0.0.56",
95694
+ version: "0.0.58",
95579
95695
  description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
95580
95696
  type: "module",
95581
95697
  main: "dist/index.js",
@@ -102360,22 +102476,58 @@ workflowCmd.command("run <id>").description("Run a saved testing workflow").requ
102360
102476
  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) => {
102361
102477
  acc.push(val);
102362
102478
  return acc;
102363
- }, []).option("--all", "Include disabled workflows when selecting by project/tag", false).option("--workers <n>", "Concurrent sandboxes, 1-12 (default: 6)", "6").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) => {
102479
+ }, []).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) => {
102364
102480
  try {
102365
- const { runWorkflowFanout: runWorkflowFanout2 } = await Promise.resolve().then(() => (init_workflow_fanout(), exports_workflow_fanout));
102366
- const result = await runWorkflowFanout2({
102481
+ const fanoutOptions = {
102367
102482
  workflowIds: ids,
102368
102483
  projectId: opts.project ? resolveProject2(opts.project) : undefined,
102369
102484
  tags: opts.tag,
102370
102485
  includeDisabled: opts.all,
102371
102486
  workers: opts.workers ? parseInt(opts.workers, 10) : undefined,
102487
+ batchSize: opts.batchSize ? parseInt(opts.batchSize, 10) : undefined,
102488
+ batch: opts.batch ? parseInt(opts.batch, 10) : undefined,
102489
+ offset: opts.offset ? parseInt(opts.offset, 10) : undefined,
102490
+ batchStart: opts.fromBatch ? parseInt(opts.fromBatch, 10) : undefined,
102491
+ batchEnd: opts.toBatch ? parseInt(opts.toBatch, 10) : undefined,
102492
+ continueOnFailure: opts.continueOnFailure,
102372
102493
  url: opts.url,
102373
102494
  model: opts.model,
102374
102495
  headed: opts.headed,
102375
102496
  parallel: opts.parallel ? parseInt(opts.parallel, 10) : undefined,
102376
102497
  timeout: opts.timeout ? parseInt(opts.timeout, 10) : undefined,
102377
102498
  dryRun: opts.dryRun
102378
- });
102499
+ };
102500
+ const runAllBatches = opts.allBatches || opts.fromBatch !== undefined || opts.toBatch !== undefined;
102501
+ if (runAllBatches) {
102502
+ const { runWorkflowFanoutBatches: runWorkflowFanoutBatches2 } = await Promise.resolve().then(() => (init_workflow_fanout(), exports_workflow_fanout));
102503
+ const result2 = await runWorkflowFanoutBatches2(fanoutOptions);
102504
+ if (opts.json || opts.dryRun) {
102505
+ log(JSON.stringify(result2, null, 2));
102506
+ } else {
102507
+ const status = result2.status === "passed" ? chalk6.green("passed") : chalk6.red("failed");
102508
+ const stop = result2.stoppedEarly ? chalk6.yellow(" stopped early") : "";
102509
+ log(chalk6.bold(`Sandbox workflow fanout batches ${status}: ${result2.passed}/${result2.total} passed across ${result2.batches.length} batch(es)${stop}`));
102510
+ log(chalk6.dim(`Selected ${result2.matched} workflow(s), batch size ${result2.batchSize}, running batch ${result2.batchStart}-${result2.batchEnd}/${result2.totalBatches} with ${result2.workers} worker(s).`));
102511
+ for (const batch of result2.batches) {
102512
+ const batchStatus = batch.status === "passed" ? chalk6.green(batch.status) : batch.status === "dry-run" ? chalk6.yellow(batch.status) : chalk6.red(batch.status);
102513
+ const batchNumber = batch.selection.batch ?? "?";
102514
+ log(` ${batchStatus} batch ${batchNumber}/${batch.selection.totalBatches ?? result2.totalBatches}: ${batch.passed}/${batch.total} passed`);
102515
+ const failedItems = batch.items.filter((item) => item.status === "failed").slice(0, 5);
102516
+ for (const item of failedItems) {
102517
+ const error40 = item.error ? chalk6.dim(` ${item.error}`) : "";
102518
+ log(` ${chalk6.red("failed")} ${item.workflowName}${error40}`);
102519
+ }
102520
+ if (batch.items.filter((item) => item.status === "failed").length > failedItems.length) {
102521
+ log(chalk6.dim(` ... ${batch.items.filter((item) => item.status === "failed").length - failedItems.length} more failure(s)`));
102522
+ }
102523
+ }
102524
+ }
102525
+ if (result2.status === "failed")
102526
+ process.exit(1);
102527
+ return;
102528
+ }
102529
+ const { runWorkflowFanout: runWorkflowFanout2 } = await Promise.resolve().then(() => (init_workflow_fanout(), exports_workflow_fanout));
102530
+ const result = await runWorkflowFanout2(fanoutOptions);
102379
102531
  if (opts.json || opts.dryRun) {
102380
102532
  log(JSON.stringify(result, null, 2));
102381
102533
  } else {
@@ -102392,6 +102544,10 @@ workflowCmd.command("fanout [ids...]").description("Run multiple saved sandbox w
102392
102544
  log(` ${chalk6.yellow("warning")} ${check2.message}`);
102393
102545
  }
102394
102546
  }
102547
+ if (result.selection.matched !== result.total) {
102548
+ const batch = result.selection.batch !== undefined && result.selection.totalBatches !== undefined ? ` batch ${result.selection.batch}/${result.selection.totalBatches}` : "";
102549
+ log(chalk6.dim(`Selected ${result.total}/${result.selection.matched} workflow(s) from offset ${result.selection.offset}${batch}.`));
102550
+ }
102395
102551
  const status = result.status === "passed" ? chalk6.green("passed") : chalk6.red("failed");
102396
102552
  log(chalk6.bold(`Sandbox workflow fanout ${status}: ${result.passed}/${result.total} passed with ${result.workers} worker(s)`));
102397
102553
  for (const item of result.items) {
@@ -6,6 +6,12 @@ export interface WorkflowFanoutOptions extends WorkflowRunOptions {
6
6
  tags?: string[];
7
7
  includeDisabled?: boolean;
8
8
  workers?: number;
9
+ batchSize?: number;
10
+ batch?: number;
11
+ offset?: number;
12
+ batchStart?: number;
13
+ batchEnd?: number;
14
+ continueOnFailure?: boolean;
9
15
  }
10
16
  export interface WorkflowFanoutItem {
11
17
  workflowId: string;
@@ -22,12 +28,27 @@ export interface WorkflowFanoutItem {
22
28
  export interface WorkflowFanoutResult {
23
29
  status: "passed" | "failed" | "dry-run";
24
30
  workers: number;
31
+ selection: WorkflowFanoutSelection;
25
32
  total: number;
26
33
  passed: number;
27
34
  failed: number;
28
35
  items: WorkflowFanoutItem[];
29
36
  preflight?: WorkflowFanoutPreflightResult;
30
37
  }
38
+ export interface WorkflowFanoutBatchesResult {
39
+ status: "passed" | "failed" | "dry-run";
40
+ workers: number;
41
+ matched: number;
42
+ batchSize: number;
43
+ batchStart: number;
44
+ batchEnd: number;
45
+ totalBatches: number;
46
+ stoppedEarly: boolean;
47
+ total: number;
48
+ passed: number;
49
+ failed: number;
50
+ batches: WorkflowFanoutResult[];
51
+ }
31
52
  export interface WorkflowFanoutDependencies extends WorkflowRunnerDependencies {
32
53
  runTestingWorkflow?: typeof runTestingWorkflow;
33
54
  preflight?: (workflows: TestingWorkflow[]) => WorkflowFanoutPreflightResult | Promise<WorkflowFanoutPreflightResult>;
@@ -48,6 +69,14 @@ export interface WorkflowFanoutPreflightResult {
48
69
  ok: boolean;
49
70
  checks: WorkflowFanoutPreflightCheck[];
50
71
  }
72
+ export interface WorkflowFanoutSelection {
73
+ matched: number;
74
+ offset: number;
75
+ limit?: number;
76
+ batch?: number;
77
+ batchSize?: number;
78
+ totalBatches?: number;
79
+ }
51
80
  interface WorkflowFanoutPreflightDependencies {
52
81
  providerApiKeyResolver?: WorkflowFanoutDependencies["providerApiKeyResolver"];
53
82
  commandExists?: WorkflowFanoutDependencies["commandExists"];
@@ -55,8 +84,19 @@ interface WorkflowFanoutPreflightDependencies {
55
84
  env?: Record<string, string | undefined>;
56
85
  }
57
86
  export declare function normalizeFanoutWorkerCount(value: number | undefined): number;
87
+ export declare function resolveWorkflowFanoutBatch(workflows: TestingWorkflow[], options?: Pick<WorkflowFanoutOptions, "batchSize" | "batch" | "offset">): {
88
+ workflows: TestingWorkflow[];
89
+ selection: WorkflowFanoutSelection;
90
+ };
91
+ export declare function resolveWorkflowFanoutBatchRange(matched: number, options: Pick<WorkflowFanoutOptions, "batchSize" | "batchStart" | "batchEnd">): {
92
+ batchSize: number;
93
+ batchStart: number;
94
+ batchEnd: number;
95
+ totalBatches: number;
96
+ };
58
97
  export declare function resolveWorkflowFanoutSelection(options: Pick<WorkflowFanoutOptions, "workflowIds" | "projectId" | "tags" | "includeDisabled">): TestingWorkflow[];
59
98
  export declare function checkWorkflowFanoutReadiness(workflows: TestingWorkflow[], dependencies?: WorkflowFanoutPreflightDependencies): Promise<WorkflowFanoutPreflightResult>;
60
99
  export declare function runWorkflowFanout(options: WorkflowFanoutOptions, dependencies?: WorkflowFanoutDependencies): Promise<WorkflowFanoutResult>;
100
+ export declare function runWorkflowFanoutBatches(options: WorkflowFanoutOptions, dependencies?: WorkflowFanoutDependencies): Promise<WorkflowFanoutBatchesResult>;
61
101
  export {};
62
102
  //# sourceMappingURL=workflow-fanout.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-fanout.d.ts","sourceRoot":"","sources":["../../src/lib/workflow-fanout.ts"],"names":[],"mappings":"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"}
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.56",
55
+ version: "0.0.58",
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",
@@ -47090,7 +47090,7 @@ import { join as join14 } from "path";
47090
47090
  // package.json
47091
47091
  var package_default = {
47092
47092
  name: "@hasna/testers",
47093
- version: "0.0.56",
47093
+ version: "0.0.58",
47094
47094
  description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
47095
47095
  type: "module",
47096
47096
  main: "dist/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/testers",
3
- "version": "0.0.56",
3
+ "version": "0.0.58",
4
4
  "description": "AI-powered QA testing CLI — spawns cheap AI agents to test web apps with headless browsers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",