@beastmode-develeap/beastmode 0.1.261 → 0.1.262

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.
@@ -15,7 +15,7 @@
15
15
  }
16
16
  </script>
17
17
  <!--BOARD_DATA-->
18
- <script>window.__BUILD_STAMP__ = "20260515-100532-d75f8b8";</script>
18
+ <script>window.__BUILD_STAMP__ = "20260515-105557-41eb86a";</script>
19
19
  <link rel="preconnect" href="https://fonts.googleapis.com">
20
20
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
21
21
  <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
@@ -5579,8 +5579,11 @@ function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject, all
5579
5579
  const [attachments, setAttachments] = useState([]);
5580
5580
  const [loadingAttachments, setLoadingAttachments] = useState(true);
5581
5581
  const [costSummary, setCostSummary] = useState(null);
5582
+ const [liveCost, setLiveCost] = useState(null);
5582
5583
  const [loadingCost, setLoadingCost] = useState(true);
5583
5584
  const [phaseData, setPhaseData] = useState([]);
5585
+ const TERMINAL_STATUSES = ['Done', 'Superseded', 'New', 'Drafts', 'Ready'];
5586
+ const isNonTerminal = item && item.status && !TERMINAL_STATUSES.includes(item.status);
5584
5587
  const [envTimeline, setEnvTimeline] = useState(null);
5585
5588
  const [deployModal, setDeployModal] = useState(null);
5586
5589
  const [auditEvents, setAuditEvents] = useState([]);
@@ -5658,7 +5661,15 @@ function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject, all
5658
5661
  .then(r => r.ok ? r.json() : null)
5659
5662
  .then(data => setCostSummary(data && data.record_count > 0 ? data : null))
5660
5663
  .catch(() => setCostSummary(null));
5661
- }, [item && item.id]);
5664
+ if (isNonTerminal) {
5665
+ fetch('/api/items/' + item.id + '/costs/live' + boardParam)
5666
+ .then(r => r.ok ? r.json() : null)
5667
+ .then(data => setLiveCost(data && data.record_count > 0 ? data : null))
5668
+ .catch(() => setLiveCost(null));
5669
+ } else {
5670
+ setLiveCost(null);
5671
+ }
5672
+ }, [item && item.id, isNonTerminal]);
5662
5673
 
5663
5674
  const refreshPhaseData = useCallback(() => {
5664
5675
  if (!item) return;
@@ -5716,6 +5727,14 @@ function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject, all
5716
5727
  .then(data => setCostSummary(data && data.record_count > 0 ? data : null))
5717
5728
  .catch(() => setCostSummary(null))
5718
5729
  .finally(() => setLoadingCost(false));
5730
+ if (isNonTerminal) {
5731
+ fetch('/api/items/' + item.id + '/costs/live' + boardParam)
5732
+ .then(r => r.ok ? r.json() : null)
5733
+ .then(data => setLiveCost(data && data.record_count > 0 ? data : null))
5734
+ .catch(() => setLiveCost(null));
5735
+ } else {
5736
+ setLiveCost(null);
5737
+ }
5719
5738
  // Also fetch the per-phase breakdown for the expandable table
5720
5739
  fetch('/api/items/' + item.id + '/costs/by-phase' + boardParam)
5721
5740
  .then(r => r.ok ? r.json() : [])
@@ -5902,6 +5921,11 @@ function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject, all
5902
5921
  <span style="font-family:var(--font-mono);color:var(--success);">$</span>
5903
5922
  Cost Summary
5904
5923
  </h4>
5924
+ ${liveCost && html`
5925
+ <div class="cost-live-iter-indicator" data-testid="live-iteration-indicator" style="margin-bottom:8px;padding:6px 10px;border-radius:4px;background:rgba(245,158,11,0.1);color:#f59e0b;font-size:12px;font-weight:600;">
5926
+ Iteration ${liveCost.current_iteration} · ${liveCost.latest_phase || 'pending'}
5927
+ </div>
5928
+ `}
5905
5929
  <div class="cost-totals">
5906
5930
  <div class="cost-total-item">
5907
5931
  <span class="cost-total-label">Total Cost</span>
@@ -5916,6 +5940,21 @@ function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject, all
5916
5940
  <span class="cost-total-value">${formatDuration(costSummary.total_duration_seconds)}</span>
5917
5941
  </div>
5918
5942
  </div>
5943
+ ${liveCost && liveCost.by_iteration && liveCost.by_iteration.length > 0 && html`
5944
+ <div class="cost-by-iteration" data-testid="live-by-iteration" style="margin-top:10px;font-size:12px;">
5945
+ <div style="font-weight:600;margin-bottom:4px;color:var(--text-dim);">By Iteration</div>
5946
+ ${liveCost.by_iteration.map((it, idx) => {
5947
+ const isLast = idx === liveCost.by_iteration.length - 1;
5948
+ const isCurrent = isNonTerminal && isLast;
5949
+ const phaseList = (it.phases || []).join(', ');
5950
+ return html`
5951
+ <div key=${it.iteration} data-testid=${'live-iter-' + it.iteration} style="display:flex;justify-content:space-between;padding:2px 0;">
5952
+ <span>Iter ${it.iteration}: ${formatCost(it.cost_usd) || '$0.00'} (${phaseList})${isCurrent ? html`<span style="color:#f59e0b;margin-left:6px;">← current</span>` : ''}</span>
5953
+ </div>
5954
+ `;
5955
+ })}
5956
+ </div>
5957
+ `}
5919
5958
  ${phaseData && phaseData.length > 0
5920
5959
  ? html`<${CostPhaseTable} itemId=${item.id} boardParam=${(() => { const p = localStorage.getItem('beastmode-selected-project') || ''; return (p && p !== 'all') ? '?board=' + encodeURIComponent(p) : ''; })()} />`
5921
5960
  : (costSummary.phases && Object.keys(costSummary.phases).length > 0 && html`
@@ -10736,6 +10775,7 @@ function CostsPage({ selectedProject }) {
10736
10775
  const [costsByItems, setCostsByItems] = useState({});
10737
10776
  const [boardItems, setBoardItems] = useState([]);
10738
10777
  const [phaseData, setPhaseData] = useState([]);
10778
+ const [inFlightItems, setInFlightItems] = useState([]);
10739
10779
  const [loading, setLoading] = useState(true);
10740
10780
  const [error, setError] = useState(null);
10741
10781
  const [sortCol, setSortCol] = useState('cost');
@@ -10749,15 +10789,30 @@ function CostsPage({ selectedProject }) {
10749
10789
  const costQs = (selectedProject && selectedProject !== 'all')
10750
10790
  ? '?board=' + encodeURIComponent(selectedProject)
10751
10791
  : '';
10792
+ const inFlightQs = (selectedProject && selectedProject !== 'all')
10793
+ ? '?board=' + encodeURIComponent(selectedProject) + '&threshold=5'
10794
+ : '?threshold=5';
10752
10795
 
10753
- const [costsData, boardData] = await Promise.all([
10796
+ const [costsData, boardData, inFlightData] = await Promise.all([
10754
10797
  api('GET', '/api/costs/by-items' + costQs),
10755
10798
  api('GET', '/api/board/items'),
10799
+ api('GET', '/api/costs/in-flight' + inFlightQs).catch(() => ({})),
10756
10800
  ]);
10757
10801
 
10758
10802
  if (cancelled) return;
10759
10803
  setCostsByItems(costsData || {});
10760
10804
  setBoardItems((boardData && boardData.items) || []);
10805
+ const inFlightArr = Object.values(inFlightData || {})
10806
+ .map(r => ({
10807
+ id: r.item_id,
10808
+ name: r.name || ('Item #' + r.item_id),
10809
+ status: r.status || '',
10810
+ costUsd: r.total_cost_usd || 0,
10811
+ iteration: r.max_iteration || 0,
10812
+ latestPhase: r.latest_phase || '',
10813
+ }))
10814
+ .sort((a, b) => b.costUsd - a.costUsd);
10815
+ setInFlightItems(inFlightArr);
10761
10816
 
10762
10817
  const itemIds = Object.keys(costsData || {});
10763
10818
  if (itemIds.length > 0) {
@@ -10883,6 +10938,42 @@ function CostsPage({ selectedProject }) {
10883
10938
  </div>
10884
10939
  </div>
10885
10940
 
10941
+ ${inFlightItems.length > 0 && html`
10942
+ <div class="card" data-testid="currently-running-card" style="margin-bottom:24px;border-left:4px solid #f59e0b;">
10943
+ <div class="card-header">
10944
+ <h3 style="color:#f59e0b;">Currently Running</h3>
10945
+ <span class="badge badge-accent">${inFlightItems.length} in-flight</span>
10946
+ </div>
10947
+ <div class="costs-table-wrapper">
10948
+ <table class="costs-table" data-testid="currently-running-table">
10949
+ <thead>
10950
+ <tr>
10951
+ <th class="costs-th costs-th-id">ID</th>
10952
+ <th class="costs-th costs-th-name">Task</th>
10953
+ <th class="costs-th costs-th-status">Status</th>
10954
+ <th class="costs-th costs-th-cost">Cost (USD)</th>
10955
+ <th class="costs-th costs-th-records">Iter</th>
10956
+ </tr>
10957
+ </thead>
10958
+ <tbody>
10959
+ ${inFlightItems.map(row => {
10960
+ const truncatedName = row.name.length > 40 ? row.name.slice(0, 40) + '…' : row.name;
10961
+ return html`
10962
+ <tr key=${row.id} class="costs-tr" data-testid=${'in-flight-row-' + row.id} onClick=${() => navigate('#/board?item=' + row.id)}>
10963
+ <td class="costs-td costs-td-mono">#${row.id}</td>
10964
+ <td class="costs-td costs-td-name" title=${row.name}>${truncatedName}</td>
10965
+ <td class="costs-td"><span class=${'badge ' + statusBadgeClass(row.status)}>${row.status}</span></td>
10966
+ <td class="costs-td costs-td-mono" data-testid=${'in-flight-cost-' + row.id}>$${row.costUsd.toFixed(2)}</td>
10967
+ <td class="costs-td costs-td-mono">${row.iteration}</td>
10968
+ </tr>
10969
+ `;
10970
+ })}
10971
+ </tbody>
10972
+ </table>
10973
+ </div>
10974
+ </div>
10975
+ `}
10976
+
10886
10977
  <div class="card">
10887
10978
  <div class="card-header">
10888
10979
  <h3>Cost by Task</h3>
@@ -1 +1 @@
1
- d75f8b8d1254cbe77cd31012325cc3dc64c7cd37
1
+ 41eb86adb1d258a1079cdd20df0288a10655dc77
@@ -1 +1 @@
1
- 20260515-100532-d75f8b8
1
+ 20260515-105557-41eb86a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beastmode-develeap/beastmode",
3
- "version": "0.1.261",
3
+ "version": "0.1.262",
4
4
  "description": "BeastMode Dark Factory — turn intent into verified software",
5
5
  "type": "module",
6
6
  "bin": {