donobu 5.43.0 → 5.43.2

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.
@@ -33,7 +33,15 @@ class DonobuGptClient extends GptClient_1.GptClient {
33
33
  if (resp.status === 401) {
34
34
  throw new GptPlatformAuthenticationFailedException_1.GptPlatformAuthenticationFailedException(this.config.type);
35
35
  }
36
- else if (!resp.ok) {
36
+ // 402 Payment Required = the key is valid, but the account has no
37
+ // wallet / no credits. The credential itself authenticated fine, so
38
+ // accept the config — the user just can't run flows until they top up
39
+ // (Studio surfaces a "0 credits remaining" nudge). Rejecting here would
40
+ // block the managed donobu gpt-config from ever being created.
41
+ if (resp.status === 402) {
42
+ return;
43
+ }
44
+ if (!resp.ok) {
37
45
  throw new Error(`Donobu API ping failed with status ${resp.status}: ${await resp.text()}`);
38
46
  }
39
47
  }
@@ -33,7 +33,15 @@ class DonobuGptClient extends GptClient_1.GptClient {
33
33
  if (resp.status === 401) {
34
34
  throw new GptPlatformAuthenticationFailedException_1.GptPlatformAuthenticationFailedException(this.config.type);
35
35
  }
36
- else if (!resp.ok) {
36
+ // 402 Payment Required = the key is valid, but the account has no
37
+ // wallet / no credits. The credential itself authenticated fine, so
38
+ // accept the config — the user just can't run flows until they top up
39
+ // (Studio surfaces a "0 credits remaining" nudge). Rejecting here would
40
+ // block the managed donobu gpt-config from ever being created.
41
+ if (resp.status === 402) {
42
+ return;
43
+ }
44
+ if (!resp.ok) {
37
45
  throw new Error(`Donobu API ping failed with status ${resp.status}: ${await resp.text()}`);
38
46
  }
39
47
  }
@@ -1678,7 +1678,7 @@ function renderHtml(report, triage, outputDir) {
1678
1678
  ? `<div class="flow-id-detail"><span class="detail-label">Flow ID</span><span class="flow-id-value">${esc(test.flowId)}<button class="copy-flow-id" data-flow-id="${esc(test.flowId)}" title="Copy flow ID"><svg viewBox="0 0 24 24"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg></button></span></div>`
1679
1679
  : '';
1680
1680
  testSectionsHtml += `
1681
- <div class="test-card ${sc.label.toLowerCase().replace(/ /g, '')} ${expandableClass}" id="${testId}" data-status="${test.status}" data-tags="${esc(JSON.stringify(test.tags))}"${test.plan ? ` data-reason="${esc(test.plan.plan.failureReason)}"` : ''} ${hasDetails ? `data-detail="${testId}"` : ''}>
1681
+ <div class="test-card ${sc.label.toLowerCase().replace(/ /g, '')} ${expandableClass}" id="${testId}" data-status="${test.status}" data-file="${esc(test.file)}" data-tags="${esc(JSON.stringify(test.tags))}"${test.plan ? ` data-reason="${esc(test.plan.plan.failureReason)}"` : ''} ${hasDetails ? `data-detail="${testId}"` : ''}>
1682
1682
  <div class="test-summary">
1683
1683
  ${chevron}
1684
1684
  <span class="status-dot" style="background:${sc.color}" title="${sc.label}"></span>
@@ -1761,6 +1761,7 @@ body::before{content:'';position:fixed;top:-750px;left:50%;transform:translateX(
1761
1761
  .test-bar-block.bar-failed{background:var(--bar-fail)}
1762
1762
  .test-bar-block.bar-timedout,.test-bar-block.bar-interrupted{background:var(--bar-warn)}
1763
1763
  .test-bar-block.bar-skipped{background:var(--bar-skip)}
1764
+ .test-bar-block.hidden-by-filter{display:none}
1764
1765
  .summary-stats{display:flex;align-items:center;gap:8px;padding:12px 0}
1765
1766
  .stat-pills{display:flex;gap:6px;flex-wrap:wrap;flex:1}
1766
1767
  .stat-pill{display:flex;align-items:center;gap:8px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:6px 14px;cursor:pointer;font-size:13px;font-weight:600;font-family:inherit;color:var(--text-muted);transition:all .2s}
@@ -1793,12 +1794,6 @@ body::before{content:'';position:fixed;top:-750px;left:50%;transform:translateX(
1793
1794
  .tag-chip-remove{background:transparent;border:none;color:inherit;cursor:pointer;font-size:14px;line-height:1;padding:0 4px;font-family:inherit;opacity:.7;transition:opacity .15s}
1794
1795
  .tag-chip-remove:hover{opacity:1}
1795
1796
 
1796
- /* Total of cards visible under the currently composed filters (status + tags).
1797
- * The stat-pill counts always reflect totals; this disambiguates when filters
1798
- * intersect. Hidden until any filter is active. */
1799
- .match-count{display:none;align-items:center;font-size:12px;color:var(--text-muted);font-family:var(--mono);padding:0 8px;height:28px;flex-shrink:0}
1800
- .match-count.visible{display:inline-flex}
1801
- .match-count-value{color:var(--text);font-weight:600;margin-left:6px}
1802
1797
 
1803
1798
  /* Test cards */
1804
1799
  .test-card{background:var(--surface);border:1px solid var(--border-subtle);border-radius:var(--radius-lg);margin-bottom:10px;overflow:hidden}
@@ -2132,7 +2127,7 @@ details.ai-invocation[open]>summary .native-step-chevron{transform:rotate(90deg)
2132
2127
  ${mergedBanner}
2133
2128
 
2134
2129
  <div class="summary-card">
2135
- <div class="summary-sub">${total} test${total !== 1 ? 's' : ''} across ${uniqueFiles.size} file${uniqueFiles.size !== 1 ? 's' : ''}</div>
2130
+ <div class="summary-sub" data-summary-sub data-total-tests="${total}" data-total-files="${uniqueFiles.size}">${total} test${total !== 1 ? 's' : ''} across ${uniqueFiles.size} file${uniqueFiles.size !== 1 ? 's' : ''}</div>
2136
2131
  <div class="test-bar">${testBarHtml}</div>
2137
2132
  <div class="summary-stats">
2138
2133
  <div class="stat-pills">${statPillsHtml}</div>
@@ -2143,7 +2138,6 @@ details.ai-invocation[open]>summary .native-step-chevron{transform:rotate(90deg)
2143
2138
  </div>
2144
2139
  <div class="active-tag-filters" data-active-tag-filters></div>
2145
2140
  </div>
2146
- <span class="match-count" data-match-count>Matches:<span class="match-count-value" data-match-count-value>0</span></span>
2147
2141
  <button class="clear-filter" data-clear-filter>&#x2716; Clear Filters</button>
2148
2142
  </div>
2149
2143
  </div>
@@ -2181,7 +2175,8 @@ details.ai-invocation[open]>summary .native-step-chevron{transform:rotate(90deg)
2181
2175
  function applyFilters(){
2182
2176
  var anyActive=activeStatus!==null||activeTags.size>0||activeReasons.size>0;
2183
2177
  document.querySelector('.clear-filter').classList.toggle('visible',anyActive);
2184
- var visible=0;
2178
+ var visibleTests=0;
2179
+ var visibleFiles=Object.create(null);
2185
2180
  document.querySelectorAll('.test-card').forEach(function(card){
2186
2181
  var statusOk=activeStatus===null||card.getAttribute('data-status')===activeStatus;
2187
2182
  var tagsOk=true;
@@ -2196,13 +2191,30 @@ details.ai-invocation[open]>summary .native-step-chevron{transform:rotate(90deg)
2196
2191
  }
2197
2192
  var hide=!(statusOk&&tagsOk&&reasonOk);
2198
2193
  card.classList.toggle('hidden-by-filter',hide);
2199
- if(!hide)visible++;
2194
+ if(!hide){
2195
+ visibleTests++;
2196
+ var f=card.getAttribute('data-file');
2197
+ if(f)visibleFiles[f]=true;
2198
+ }
2199
+ });
2200
+ // Mirror visibility onto the colored test-bar squares so the bar collapses
2201
+ // to the matching subset rather than dangling stale tiles.
2202
+ document.querySelectorAll('.test-bar-block[data-target]').forEach(function(block){
2203
+ var card=document.getElementById(block.getAttribute('data-target'));
2204
+ block.classList.toggle('hidden-by-filter',!!(card&&card.classList.contains('hidden-by-filter')));
2200
2205
  });
2201
- var mc=document.querySelector('[data-match-count]');
2202
- if(mc){
2203
- mc.classList.toggle('visible',anyActive);
2204
- var mv=mc.querySelector('[data-match-count-value]');
2205
- if(mv)mv.textContent=visible;
2206
+ // "X tests across Y files" subtitle reflects the current filter result.
2207
+ // When no filter is active the form matches the original (no "of Y").
2208
+ var sub=document.querySelector('[data-summary-sub]');
2209
+ if(sub){
2210
+ var totalTests=parseInt(sub.getAttribute('data-total-tests'),10)||0;
2211
+ var totalFiles=parseInt(sub.getAttribute('data-total-files'),10)||0;
2212
+ var visFiles=Object.keys(visibleFiles).length;
2213
+ if(anyActive){
2214
+ sub.textContent=visibleTests+' of '+totalTests+' test'+(totalTests!==1?'s':'')+' across '+visFiles+' of '+totalFiles+' file'+(totalFiles!==1?'s':'');
2215
+ }else{
2216
+ sub.textContent=totalTests+' test'+(totalTests!==1?'s':'')+' across '+totalFiles+' file'+(totalFiles!==1?'s':'');
2217
+ }
2206
2218
  }
2207
2219
  syncFiltersToUrl();
2208
2220
  }
@@ -1678,7 +1678,7 @@ function renderHtml(report, triage, outputDir) {
1678
1678
  ? `<div class="flow-id-detail"><span class="detail-label">Flow ID</span><span class="flow-id-value">${esc(test.flowId)}<button class="copy-flow-id" data-flow-id="${esc(test.flowId)}" title="Copy flow ID"><svg viewBox="0 0 24 24"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg></button></span></div>`
1679
1679
  : '';
1680
1680
  testSectionsHtml += `
1681
- <div class="test-card ${sc.label.toLowerCase().replace(/ /g, '')} ${expandableClass}" id="${testId}" data-status="${test.status}" data-tags="${esc(JSON.stringify(test.tags))}"${test.plan ? ` data-reason="${esc(test.plan.plan.failureReason)}"` : ''} ${hasDetails ? `data-detail="${testId}"` : ''}>
1681
+ <div class="test-card ${sc.label.toLowerCase().replace(/ /g, '')} ${expandableClass}" id="${testId}" data-status="${test.status}" data-file="${esc(test.file)}" data-tags="${esc(JSON.stringify(test.tags))}"${test.plan ? ` data-reason="${esc(test.plan.plan.failureReason)}"` : ''} ${hasDetails ? `data-detail="${testId}"` : ''}>
1682
1682
  <div class="test-summary">
1683
1683
  ${chevron}
1684
1684
  <span class="status-dot" style="background:${sc.color}" title="${sc.label}"></span>
@@ -1761,6 +1761,7 @@ body::before{content:'';position:fixed;top:-750px;left:50%;transform:translateX(
1761
1761
  .test-bar-block.bar-failed{background:var(--bar-fail)}
1762
1762
  .test-bar-block.bar-timedout,.test-bar-block.bar-interrupted{background:var(--bar-warn)}
1763
1763
  .test-bar-block.bar-skipped{background:var(--bar-skip)}
1764
+ .test-bar-block.hidden-by-filter{display:none}
1764
1765
  .summary-stats{display:flex;align-items:center;gap:8px;padding:12px 0}
1765
1766
  .stat-pills{display:flex;gap:6px;flex-wrap:wrap;flex:1}
1766
1767
  .stat-pill{display:flex;align-items:center;gap:8px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:6px 14px;cursor:pointer;font-size:13px;font-weight:600;font-family:inherit;color:var(--text-muted);transition:all .2s}
@@ -1793,12 +1794,6 @@ body::before{content:'';position:fixed;top:-750px;left:50%;transform:translateX(
1793
1794
  .tag-chip-remove{background:transparent;border:none;color:inherit;cursor:pointer;font-size:14px;line-height:1;padding:0 4px;font-family:inherit;opacity:.7;transition:opacity .15s}
1794
1795
  .tag-chip-remove:hover{opacity:1}
1795
1796
 
1796
- /* Total of cards visible under the currently composed filters (status + tags).
1797
- * The stat-pill counts always reflect totals; this disambiguates when filters
1798
- * intersect. Hidden until any filter is active. */
1799
- .match-count{display:none;align-items:center;font-size:12px;color:var(--text-muted);font-family:var(--mono);padding:0 8px;height:28px;flex-shrink:0}
1800
- .match-count.visible{display:inline-flex}
1801
- .match-count-value{color:var(--text);font-weight:600;margin-left:6px}
1802
1797
 
1803
1798
  /* Test cards */
1804
1799
  .test-card{background:var(--surface);border:1px solid var(--border-subtle);border-radius:var(--radius-lg);margin-bottom:10px;overflow:hidden}
@@ -2132,7 +2127,7 @@ details.ai-invocation[open]>summary .native-step-chevron{transform:rotate(90deg)
2132
2127
  ${mergedBanner}
2133
2128
 
2134
2129
  <div class="summary-card">
2135
- <div class="summary-sub">${total} test${total !== 1 ? 's' : ''} across ${uniqueFiles.size} file${uniqueFiles.size !== 1 ? 's' : ''}</div>
2130
+ <div class="summary-sub" data-summary-sub data-total-tests="${total}" data-total-files="${uniqueFiles.size}">${total} test${total !== 1 ? 's' : ''} across ${uniqueFiles.size} file${uniqueFiles.size !== 1 ? 's' : ''}</div>
2136
2131
  <div class="test-bar">${testBarHtml}</div>
2137
2132
  <div class="summary-stats">
2138
2133
  <div class="stat-pills">${statPillsHtml}</div>
@@ -2143,7 +2138,6 @@ details.ai-invocation[open]>summary .native-step-chevron{transform:rotate(90deg)
2143
2138
  </div>
2144
2139
  <div class="active-tag-filters" data-active-tag-filters></div>
2145
2140
  </div>
2146
- <span class="match-count" data-match-count>Matches:<span class="match-count-value" data-match-count-value>0</span></span>
2147
2141
  <button class="clear-filter" data-clear-filter>&#x2716; Clear Filters</button>
2148
2142
  </div>
2149
2143
  </div>
@@ -2181,7 +2175,8 @@ details.ai-invocation[open]>summary .native-step-chevron{transform:rotate(90deg)
2181
2175
  function applyFilters(){
2182
2176
  var anyActive=activeStatus!==null||activeTags.size>0||activeReasons.size>0;
2183
2177
  document.querySelector('.clear-filter').classList.toggle('visible',anyActive);
2184
- var visible=0;
2178
+ var visibleTests=0;
2179
+ var visibleFiles=Object.create(null);
2185
2180
  document.querySelectorAll('.test-card').forEach(function(card){
2186
2181
  var statusOk=activeStatus===null||card.getAttribute('data-status')===activeStatus;
2187
2182
  var tagsOk=true;
@@ -2196,13 +2191,30 @@ details.ai-invocation[open]>summary .native-step-chevron{transform:rotate(90deg)
2196
2191
  }
2197
2192
  var hide=!(statusOk&&tagsOk&&reasonOk);
2198
2193
  card.classList.toggle('hidden-by-filter',hide);
2199
- if(!hide)visible++;
2194
+ if(!hide){
2195
+ visibleTests++;
2196
+ var f=card.getAttribute('data-file');
2197
+ if(f)visibleFiles[f]=true;
2198
+ }
2199
+ });
2200
+ // Mirror visibility onto the colored test-bar squares so the bar collapses
2201
+ // to the matching subset rather than dangling stale tiles.
2202
+ document.querySelectorAll('.test-bar-block[data-target]').forEach(function(block){
2203
+ var card=document.getElementById(block.getAttribute('data-target'));
2204
+ block.classList.toggle('hidden-by-filter',!!(card&&card.classList.contains('hidden-by-filter')));
2200
2205
  });
2201
- var mc=document.querySelector('[data-match-count]');
2202
- if(mc){
2203
- mc.classList.toggle('visible',anyActive);
2204
- var mv=mc.querySelector('[data-match-count-value]');
2205
- if(mv)mv.textContent=visible;
2206
+ // "X tests across Y files" subtitle reflects the current filter result.
2207
+ // When no filter is active the form matches the original (no "of Y").
2208
+ var sub=document.querySelector('[data-summary-sub]');
2209
+ if(sub){
2210
+ var totalTests=parseInt(sub.getAttribute('data-total-tests'),10)||0;
2211
+ var totalFiles=parseInt(sub.getAttribute('data-total-files'),10)||0;
2212
+ var visFiles=Object.keys(visibleFiles).length;
2213
+ if(anyActive){
2214
+ sub.textContent=visibleTests+' of '+totalTests+' test'+(totalTests!==1?'s':'')+' across '+visFiles+' of '+totalFiles+' file'+(totalFiles!==1?'s':'');
2215
+ }else{
2216
+ sub.textContent=totalTests+' test'+(totalTests!==1?'s':'')+' across '+totalFiles+' file'+(totalFiles!==1?'s':'');
2217
+ }
2206
2218
  }
2207
2219
  syncFiltersToUrl();
2208
2220
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "donobu",
3
- "version": "5.43.0",
3
+ "version": "5.43.2",
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",