donobu 5.32.0 → 5.33.0

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.
@@ -451,15 +451,25 @@ exports.test = test_1.test.extend({
451
451
  * 3. `_steps` is fully populated by the time `finalizeTest()` runs (the
452
452
  * test body has already completed).
453
453
  */
454
- function collectNativeSteps(rawSteps) {
454
+ function collectNativeSteps(rawSteps, startTimes) {
455
455
  const result = [];
456
456
  for (const step of rawSteps) {
457
457
  const cat = step.category ?? '';
458
+ const childRaw = Array.isArray(step.steps) ? step.steps : [];
459
+ const collectedChildren = collectNativeSteps(childRaw, startTimes);
458
460
  if (cat === 'expect' || cat === 'test.step') {
461
+ // Start time comes from the onStepBegin payload (captured by the
462
+ // pwApiStepLogger fixture into startTimes). If unavailable, fall
463
+ // back to the step's endWallTime so the window is zero-width and
464
+ // sibling steps don't get falsely nested inside.
465
+ const endWallTime = step.endWallTime ?? Date.now();
466
+ const startWallTime = (typeof step.stepId === 'string' && startTimes.get(step.stepId)) ||
467
+ endWallTime;
459
468
  result.push({
460
469
  title: step.title ?? '',
461
470
  category: cat,
462
- endWallTime: step.endWallTime ?? Date.now(),
471
+ startWallTime,
472
+ endWallTime,
463
473
  passed: !step.error,
464
474
  error: step.error
465
475
  ? { message: step.error.message, stack: step.error.stack }
@@ -471,15 +481,16 @@ function collectNativeSteps(rawSteps) {
471
481
  column: step.location.column ?? 0,
472
482
  }
473
483
  : undefined,
484
+ children: collectedChildren,
474
485
  });
475
486
  }
476
- // Recurse into child steps regardless of parent category so nested
477
- // expect() calls inside test.step() blocks are captured.
478
- if (Array.isArray(step.steps) && step.steps.length > 0) {
479
- result.push(...collectNativeSteps(step.steps));
487
+ else {
488
+ // Parent is not a kept category (e.g. pw:api) promote any qualifying
489
+ // descendants so a nested expect() still appears in the report.
490
+ result.push(...collectedChildren);
480
491
  }
481
492
  }
482
- result.sort((a, b) => a.endWallTime - b.endWallTime);
493
+ result.sort((a, b) => a.startWallTime - b.startWallTime);
483
494
  return result;
484
495
  }
485
496
  // ---------------------------------------------------------------------------
@@ -689,9 +700,20 @@ function installPlaywrightStepLogger(testInfo) {
689
700
  const originalOnStepEnd = callbacks && typeof callbacks.onStepEnd === 'function'
690
701
  ? callbacks.onStepEnd
691
702
  : null;
703
+ const originalOnStepBegin = callbacks && typeof callbacks.onStepBegin === 'function'
704
+ ? callbacks.onStepBegin
705
+ : null;
692
706
  if (!callbacks || !originalOnStepEnd || !stepMap) {
693
707
  return () => { };
694
708
  }
709
+ // Stash a stepId -> wallTime map on testInfo so collectNativeSteps can
710
+ // look up start times when building the report hierarchy. Playwright
711
+ // doesn't store wallTime on the step object itself — it's only emitted
712
+ // via the onStepBegin payload — so we capture it here. Without this,
713
+ // `test.step` blocks have no recoverable start time and any preceding
714
+ // tool-call step gets falsely nested inside them.
715
+ const startTimes = new Map();
716
+ ti.__donobuStepStartTimes = startTimes;
695
717
  let installed = false;
696
718
  try {
697
719
  callbacks.onStepEnd = function patchedOnStepEnd(payload) {
@@ -710,6 +732,21 @@ function installPlaywrightStepLogger(testInfo) {
710
732
  }
711
733
  return ret;
712
734
  };
735
+ callbacks.onStepBegin = function patchedOnStepBegin(payload) {
736
+ const ret = originalOnStepBegin
737
+ ? originalOnStepBegin.call(this, payload)
738
+ : undefined;
739
+ try {
740
+ if (typeof payload?.stepId === 'string' &&
741
+ typeof payload?.wallTime === 'number') {
742
+ startTimes.set(payload.stepId, payload.wallTime);
743
+ }
744
+ }
745
+ catch (err) {
746
+ Logger_1.appLogger.debug('Failed to record Playwright step start time', err);
747
+ }
748
+ return ret;
749
+ };
713
750
  installed = true;
714
751
  }
715
752
  catch (err) {
@@ -718,6 +755,9 @@ function installPlaywrightStepLogger(testInfo) {
718
755
  return () => {
719
756
  if (installed) {
720
757
  callbacks.onStepEnd = originalOnStepEnd;
758
+ if (originalOnStepBegin) {
759
+ callbacks.onStepBegin = originalOnStepBegin;
760
+ }
721
761
  }
722
762
  };
723
763
  }
@@ -825,7 +865,8 @@ async function finalizeTest(page, testInfo, logBuffer, videoOption) {
825
865
  // Attach native Playwright steps (expect assertions, test.step blocks)
826
866
  // so the HTML report can show a unified timeline alongside AI tool calls.
827
867
  try {
828
- const nativeSteps = collectNativeSteps(testInfo._steps ?? []);
868
+ const startTimes = testInfo.__donobuStepStartTimes ?? new Map();
869
+ const nativeSteps = collectNativeSteps(testInfo._steps ?? [], startTimes);
829
870
  if (nativeSteps.length > 0) {
830
871
  await testInfo.attach('donobu-native-steps', {
831
872
  body: JSON.stringify(nativeSteps),
@@ -562,7 +562,7 @@ function renderErrors(errors) {
562
562
  }
563
563
  return html;
564
564
  }
565
- function renderNativeStep(ns) {
565
+ function renderNativeStep(ns, childrenHtml) {
566
566
  const statusIcon = ns.passed
567
567
  ? '<span class="step-status-ok">&#10003;</span>'
568
568
  : '<span class="step-status-fail">&#10007;</span>';
@@ -573,7 +573,8 @@ function renderNativeStep(ns) {
573
573
  const snippet = ns.location?.file
574
574
  ? readSourceSnippet(ns.location.file, ns.location.line)
575
575
  : null;
576
- const hasBody = snippet || (!ns.passed && ns.error?.message);
576
+ const hasError = !ns.passed && !!ns.error?.message;
577
+ const hasBody = !!snippet || hasError || !!childrenHtml;
577
578
  const renderHeader = (tag) => {
578
579
  let header = `<${tag} class="filmstrip-header">`;
579
580
  header += statusIcon;
@@ -592,17 +593,23 @@ function renderNativeStep(ns) {
592
593
  if (!hasBody) {
593
594
  return `<div class="filmstrip-step native-step">${renderHeader('div')}</div>`;
594
595
  }
595
- // Failing steps render expanded so the error is immediately visible;
596
- // passing steps collapse so a test with many expects stays scannable.
596
+ // Failures always render expanded so the error is immediately visible.
597
+ // test.step blocks with nested content also default open so users see
598
+ // what's inside; bare passing expects with just a snippet collapse to
599
+ // keep tests with many assertions scannable.
600
+ const defaultOpen = !ns.passed || (ns.category === 'test.step' && !!childrenHtml);
597
601
  const passClass = ns.passed ? 'native-step--passed' : 'native-step--failed';
598
- let html = `<details class="filmstrip-step native-step ${passClass}"${ns.passed ? '' : ' open'}>`;
602
+ let html = `<details class="filmstrip-step native-step ${passClass}"${defaultOpen ? ' open' : ''}>`;
599
603
  html += renderHeader('summary');
600
- if (!ns.passed && ns.error?.message) {
604
+ if (hasError) {
601
605
  html += `<pre class="native-step-error">${ansiToHtml(ns.error.message)}</pre>`;
602
606
  }
603
607
  if (snippet) {
604
608
  html += snippet;
605
609
  }
610
+ if (childrenHtml) {
611
+ html += childrenHtml;
612
+ }
606
613
  html += `</details>`;
607
614
  return html;
608
615
  }
@@ -853,31 +860,71 @@ function renderSteps(steps, stepScreenshots, nativeSteps, outputDir) {
853
860
  return '';
854
861
  }
855
862
  if (hasScreenshots || hasNative) {
856
- const timeline = [
857
- ...stepScreenshots.map((ss) => ({
858
- t: ss.completedAt,
859
- kind: 'donobu',
860
- ss,
861
- })),
862
- ...nativeSteps.map((ns) => ({
863
- t: ns.endWallTime,
864
- kind: 'native',
865
- ns,
866
- })),
867
- ];
868
- timeline.sort((a, b) => a.t - b.t);
869
- const stepCount = timeline.length;
863
+ const buildNativeTree = (nss) => nss.map((ns) => ({
864
+ kind: 'native',
865
+ ns,
866
+ t: ns.startWallTime,
867
+ tEnd: ns.endWallTime,
868
+ children: buildNativeTree(ns.children),
869
+ }));
870
+ const roots = buildNativeTree(nativeSteps);
871
+ // Place each Donobu screenshot under the deepest native step whose
872
+ // [start, end] window contains it. Falls back to top level if none.
873
+ const placeDonobu = (nodes, d) => {
874
+ for (const n of nodes) {
875
+ if (n.kind !== 'native') {
876
+ continue;
877
+ }
878
+ if (d.ss.startedAt >= n.t && d.ss.completedAt <= n.tEnd) {
879
+ if (!placeDonobu(n.children, d)) {
880
+ n.children.push(d);
881
+ }
882
+ return true;
883
+ }
884
+ }
885
+ return false;
886
+ };
887
+ for (const ss of stepScreenshots) {
888
+ const d = { kind: 'donobu', ss, t: ss.startedAt };
889
+ if (!placeDonobu(roots, d)) {
890
+ roots.push(d);
891
+ }
892
+ }
893
+ const sortTree = (nodes) => {
894
+ nodes.sort((a, b) => a.t - b.t);
895
+ for (const n of nodes) {
896
+ if (n.kind === 'native') {
897
+ sortTree(n.children);
898
+ }
899
+ }
900
+ };
901
+ sortTree(roots);
902
+ const countNodes = (nodes) => {
903
+ let c = 0;
904
+ for (const n of nodes) {
905
+ c += 1;
906
+ if (n.kind === 'native') {
907
+ c += countNodes(n.children);
908
+ }
909
+ }
910
+ return c;
911
+ };
912
+ const renderNode = (node) => {
913
+ if (node.kind === 'donobu') {
914
+ return renderFilmstripStep(node.ss, outputDir);
915
+ }
916
+ const childrenHtml = node.children.length > 0
917
+ ? `<div class="native-step-children">${node.children.map(renderNode).join('')}</div>`
918
+ : '';
919
+ return renderNativeStep(node.ns, childrenHtml);
920
+ };
921
+ const stepCount = countNodes(roots);
870
922
  let html = '<details class="steps-section"><summary>Steps (' +
871
923
  stepCount +
872
924
  ')</summary>';
873
925
  html += '<div class="step-filmstrip">';
874
- for (const entry of timeline) {
875
- if (entry.kind === 'donobu') {
876
- html += renderFilmstripStep(entry.ss, outputDir);
877
- }
878
- else {
879
- html += renderNativeStep(entry.ns);
880
- }
926
+ for (const node of roots) {
927
+ html += renderNode(node);
881
928
  }
882
929
  html += '</div>';
883
930
  html += '</details>';
@@ -1610,6 +1657,8 @@ details.native-step>summary::-webkit-details-marker{display:none}
1610
1657
  details.native-step[open]>summary .native-step-chevron{transform:rotate(90deg)}
1611
1658
  .native-step-error{font-size:11px;font-family:var(--mono);padding:4px 0 2px 22px;margin:0;white-space:pre-wrap;word-break:break-word;color:var(--text-muted)}
1612
1659
  .native-step-snippet{font-size:11px;font-family:var(--mono);margin:4px 0 2px 22px;overflow:hidden}
1660
+ .native-step-children{display:flex;flex-direction:column;margin:4px 0 0 10px;border-left:1px solid var(--border-subtle);padding-left:8px}
1661
+ .native-step-children>.filmstrip-step{padding-left:8px}
1613
1662
  .snippet-line{display:flex;padding:1px 8px;white-space:pre}
1614
1663
  .snippet-line--target{background:rgba(239,68,68,.10)}
1615
1664
  .snippet-linenum{color:var(--text-dim);min-width:40px;user-select:none}
@@ -451,15 +451,25 @@ exports.test = test_1.test.extend({
451
451
  * 3. `_steps` is fully populated by the time `finalizeTest()` runs (the
452
452
  * test body has already completed).
453
453
  */
454
- function collectNativeSteps(rawSteps) {
454
+ function collectNativeSteps(rawSteps, startTimes) {
455
455
  const result = [];
456
456
  for (const step of rawSteps) {
457
457
  const cat = step.category ?? '';
458
+ const childRaw = Array.isArray(step.steps) ? step.steps : [];
459
+ const collectedChildren = collectNativeSteps(childRaw, startTimes);
458
460
  if (cat === 'expect' || cat === 'test.step') {
461
+ // Start time comes from the onStepBegin payload (captured by the
462
+ // pwApiStepLogger fixture into startTimes). If unavailable, fall
463
+ // back to the step's endWallTime so the window is zero-width and
464
+ // sibling steps don't get falsely nested inside.
465
+ const endWallTime = step.endWallTime ?? Date.now();
466
+ const startWallTime = (typeof step.stepId === 'string' && startTimes.get(step.stepId)) ||
467
+ endWallTime;
459
468
  result.push({
460
469
  title: step.title ?? '',
461
470
  category: cat,
462
- endWallTime: step.endWallTime ?? Date.now(),
471
+ startWallTime,
472
+ endWallTime,
463
473
  passed: !step.error,
464
474
  error: step.error
465
475
  ? { message: step.error.message, stack: step.error.stack }
@@ -471,15 +481,16 @@ function collectNativeSteps(rawSteps) {
471
481
  column: step.location.column ?? 0,
472
482
  }
473
483
  : undefined,
484
+ children: collectedChildren,
474
485
  });
475
486
  }
476
- // Recurse into child steps regardless of parent category so nested
477
- // expect() calls inside test.step() blocks are captured.
478
- if (Array.isArray(step.steps) && step.steps.length > 0) {
479
- result.push(...collectNativeSteps(step.steps));
487
+ else {
488
+ // Parent is not a kept category (e.g. pw:api) promote any qualifying
489
+ // descendants so a nested expect() still appears in the report.
490
+ result.push(...collectedChildren);
480
491
  }
481
492
  }
482
- result.sort((a, b) => a.endWallTime - b.endWallTime);
493
+ result.sort((a, b) => a.startWallTime - b.startWallTime);
483
494
  return result;
484
495
  }
485
496
  // ---------------------------------------------------------------------------
@@ -689,9 +700,20 @@ function installPlaywrightStepLogger(testInfo) {
689
700
  const originalOnStepEnd = callbacks && typeof callbacks.onStepEnd === 'function'
690
701
  ? callbacks.onStepEnd
691
702
  : null;
703
+ const originalOnStepBegin = callbacks && typeof callbacks.onStepBegin === 'function'
704
+ ? callbacks.onStepBegin
705
+ : null;
692
706
  if (!callbacks || !originalOnStepEnd || !stepMap) {
693
707
  return () => { };
694
708
  }
709
+ // Stash a stepId -> wallTime map on testInfo so collectNativeSteps can
710
+ // look up start times when building the report hierarchy. Playwright
711
+ // doesn't store wallTime on the step object itself — it's only emitted
712
+ // via the onStepBegin payload — so we capture it here. Without this,
713
+ // `test.step` blocks have no recoverable start time and any preceding
714
+ // tool-call step gets falsely nested inside them.
715
+ const startTimes = new Map();
716
+ ti.__donobuStepStartTimes = startTimes;
695
717
  let installed = false;
696
718
  try {
697
719
  callbacks.onStepEnd = function patchedOnStepEnd(payload) {
@@ -710,6 +732,21 @@ function installPlaywrightStepLogger(testInfo) {
710
732
  }
711
733
  return ret;
712
734
  };
735
+ callbacks.onStepBegin = function patchedOnStepBegin(payload) {
736
+ const ret = originalOnStepBegin
737
+ ? originalOnStepBegin.call(this, payload)
738
+ : undefined;
739
+ try {
740
+ if (typeof payload?.stepId === 'string' &&
741
+ typeof payload?.wallTime === 'number') {
742
+ startTimes.set(payload.stepId, payload.wallTime);
743
+ }
744
+ }
745
+ catch (err) {
746
+ Logger_1.appLogger.debug('Failed to record Playwright step start time', err);
747
+ }
748
+ return ret;
749
+ };
713
750
  installed = true;
714
751
  }
715
752
  catch (err) {
@@ -718,6 +755,9 @@ function installPlaywrightStepLogger(testInfo) {
718
755
  return () => {
719
756
  if (installed) {
720
757
  callbacks.onStepEnd = originalOnStepEnd;
758
+ if (originalOnStepBegin) {
759
+ callbacks.onStepBegin = originalOnStepBegin;
760
+ }
721
761
  }
722
762
  };
723
763
  }
@@ -825,7 +865,8 @@ async function finalizeTest(page, testInfo, logBuffer, videoOption) {
825
865
  // Attach native Playwright steps (expect assertions, test.step blocks)
826
866
  // so the HTML report can show a unified timeline alongside AI tool calls.
827
867
  try {
828
- const nativeSteps = collectNativeSteps(testInfo._steps ?? []);
868
+ const startTimes = testInfo.__donobuStepStartTimes ?? new Map();
869
+ const nativeSteps = collectNativeSteps(testInfo._steps ?? [], startTimes);
829
870
  if (nativeSteps.length > 0) {
830
871
  await testInfo.attach('donobu-native-steps', {
831
872
  body: JSON.stringify(nativeSteps),
@@ -562,7 +562,7 @@ function renderErrors(errors) {
562
562
  }
563
563
  return html;
564
564
  }
565
- function renderNativeStep(ns) {
565
+ function renderNativeStep(ns, childrenHtml) {
566
566
  const statusIcon = ns.passed
567
567
  ? '<span class="step-status-ok">&#10003;</span>'
568
568
  : '<span class="step-status-fail">&#10007;</span>';
@@ -573,7 +573,8 @@ function renderNativeStep(ns) {
573
573
  const snippet = ns.location?.file
574
574
  ? readSourceSnippet(ns.location.file, ns.location.line)
575
575
  : null;
576
- const hasBody = snippet || (!ns.passed && ns.error?.message);
576
+ const hasError = !ns.passed && !!ns.error?.message;
577
+ const hasBody = !!snippet || hasError || !!childrenHtml;
577
578
  const renderHeader = (tag) => {
578
579
  let header = `<${tag} class="filmstrip-header">`;
579
580
  header += statusIcon;
@@ -592,17 +593,23 @@ function renderNativeStep(ns) {
592
593
  if (!hasBody) {
593
594
  return `<div class="filmstrip-step native-step">${renderHeader('div')}</div>`;
594
595
  }
595
- // Failing steps render expanded so the error is immediately visible;
596
- // passing steps collapse so a test with many expects stays scannable.
596
+ // Failures always render expanded so the error is immediately visible.
597
+ // test.step blocks with nested content also default open so users see
598
+ // what's inside; bare passing expects with just a snippet collapse to
599
+ // keep tests with many assertions scannable.
600
+ const defaultOpen = !ns.passed || (ns.category === 'test.step' && !!childrenHtml);
597
601
  const passClass = ns.passed ? 'native-step--passed' : 'native-step--failed';
598
- let html = `<details class="filmstrip-step native-step ${passClass}"${ns.passed ? '' : ' open'}>`;
602
+ let html = `<details class="filmstrip-step native-step ${passClass}"${defaultOpen ? ' open' : ''}>`;
599
603
  html += renderHeader('summary');
600
- if (!ns.passed && ns.error?.message) {
604
+ if (hasError) {
601
605
  html += `<pre class="native-step-error">${ansiToHtml(ns.error.message)}</pre>`;
602
606
  }
603
607
  if (snippet) {
604
608
  html += snippet;
605
609
  }
610
+ if (childrenHtml) {
611
+ html += childrenHtml;
612
+ }
606
613
  html += `</details>`;
607
614
  return html;
608
615
  }
@@ -853,31 +860,71 @@ function renderSteps(steps, stepScreenshots, nativeSteps, outputDir) {
853
860
  return '';
854
861
  }
855
862
  if (hasScreenshots || hasNative) {
856
- const timeline = [
857
- ...stepScreenshots.map((ss) => ({
858
- t: ss.completedAt,
859
- kind: 'donobu',
860
- ss,
861
- })),
862
- ...nativeSteps.map((ns) => ({
863
- t: ns.endWallTime,
864
- kind: 'native',
865
- ns,
866
- })),
867
- ];
868
- timeline.sort((a, b) => a.t - b.t);
869
- const stepCount = timeline.length;
863
+ const buildNativeTree = (nss) => nss.map((ns) => ({
864
+ kind: 'native',
865
+ ns,
866
+ t: ns.startWallTime,
867
+ tEnd: ns.endWallTime,
868
+ children: buildNativeTree(ns.children),
869
+ }));
870
+ const roots = buildNativeTree(nativeSteps);
871
+ // Place each Donobu screenshot under the deepest native step whose
872
+ // [start, end] window contains it. Falls back to top level if none.
873
+ const placeDonobu = (nodes, d) => {
874
+ for (const n of nodes) {
875
+ if (n.kind !== 'native') {
876
+ continue;
877
+ }
878
+ if (d.ss.startedAt >= n.t && d.ss.completedAt <= n.tEnd) {
879
+ if (!placeDonobu(n.children, d)) {
880
+ n.children.push(d);
881
+ }
882
+ return true;
883
+ }
884
+ }
885
+ return false;
886
+ };
887
+ for (const ss of stepScreenshots) {
888
+ const d = { kind: 'donobu', ss, t: ss.startedAt };
889
+ if (!placeDonobu(roots, d)) {
890
+ roots.push(d);
891
+ }
892
+ }
893
+ const sortTree = (nodes) => {
894
+ nodes.sort((a, b) => a.t - b.t);
895
+ for (const n of nodes) {
896
+ if (n.kind === 'native') {
897
+ sortTree(n.children);
898
+ }
899
+ }
900
+ };
901
+ sortTree(roots);
902
+ const countNodes = (nodes) => {
903
+ let c = 0;
904
+ for (const n of nodes) {
905
+ c += 1;
906
+ if (n.kind === 'native') {
907
+ c += countNodes(n.children);
908
+ }
909
+ }
910
+ return c;
911
+ };
912
+ const renderNode = (node) => {
913
+ if (node.kind === 'donobu') {
914
+ return renderFilmstripStep(node.ss, outputDir);
915
+ }
916
+ const childrenHtml = node.children.length > 0
917
+ ? `<div class="native-step-children">${node.children.map(renderNode).join('')}</div>`
918
+ : '';
919
+ return renderNativeStep(node.ns, childrenHtml);
920
+ };
921
+ const stepCount = countNodes(roots);
870
922
  let html = '<details class="steps-section"><summary>Steps (' +
871
923
  stepCount +
872
924
  ')</summary>';
873
925
  html += '<div class="step-filmstrip">';
874
- for (const entry of timeline) {
875
- if (entry.kind === 'donobu') {
876
- html += renderFilmstripStep(entry.ss, outputDir);
877
- }
878
- else {
879
- html += renderNativeStep(entry.ns);
880
- }
926
+ for (const node of roots) {
927
+ html += renderNode(node);
881
928
  }
882
929
  html += '</div>';
883
930
  html += '</details>';
@@ -1610,6 +1657,8 @@ details.native-step>summary::-webkit-details-marker{display:none}
1610
1657
  details.native-step[open]>summary .native-step-chevron{transform:rotate(90deg)}
1611
1658
  .native-step-error{font-size:11px;font-family:var(--mono);padding:4px 0 2px 22px;margin:0;white-space:pre-wrap;word-break:break-word;color:var(--text-muted)}
1612
1659
  .native-step-snippet{font-size:11px;font-family:var(--mono);margin:4px 0 2px 22px;overflow:hidden}
1660
+ .native-step-children{display:flex;flex-direction:column;margin:4px 0 0 10px;border-left:1px solid var(--border-subtle);padding-left:8px}
1661
+ .native-step-children>.filmstrip-step{padding-left:8px}
1613
1662
  .snippet-line{display:flex;padding:1px 8px;white-space:pre}
1614
1663
  .snippet-line--target{background:rgba(239,68,68,.10)}
1615
1664
  .snippet-linenum{color:var(--text-dim);min-width:40px;user-select:none}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "donobu",
3
- "version": "5.32.0",
3
+ "version": "5.33.0",
4
4
  "description": "Create browser automations with an LLM agent and replay them as Playwright scripts.",
5
5
  "main": "dist/main.js",
6
6
  "module": "dist/esm/main.js",