agents-relay 2.0.135 → 2.0.137
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/dashboard-html.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { dashboardStyles } from './dashboard-style.js';
|
|
2
|
-
import { workflowPresentation } from './dashboard-workflow-presentation.js';
|
|
3
2
|
import { createRequire } from 'node:module';
|
|
4
3
|
import { execFileSync } from 'node:child_process';
|
|
5
4
|
import { fileURLToPath } from 'node:url';
|
|
@@ -170,8 +169,6 @@ let selectedRuntimeStatus=sessionStorage.getItem('agents-relay:runtime-status-fi
|
|
|
170
169
|
let searchQuery=sessionStorage.getItem('agents-relay:job-search')||'';
|
|
171
170
|
let workflowGraphs=[];
|
|
172
171
|
let selectedGraphId='entry';
|
|
173
|
-
const GRAPH_ENTRY_SOURCE=${JSON.stringify(workflowPresentation.entrySource)};
|
|
174
|
-
const GRAPH_NAV=${JSON.stringify(workflowPresentation.navigation)};
|
|
175
172
|
const WORKFLOW_SOURCE_REF=${JSON.stringify(dashboardSourceRevision())};
|
|
176
173
|
let eventTaskId=selectedTaskId;
|
|
177
174
|
const collapsedTasks=new Set();
|
|
@@ -297,16 +294,17 @@ function githubUsageCard(effective,status){
|
|
|
297
294
|
}
|
|
298
295
|
async function drawUsage(){try{const overview=await loadJSON('/api/usage');usageRoot.replaceChildren();usageMeta.textContent='Real account telemetry when available';const providers=Array.isArray(overview.providers)?overview.providers:[];const credits=Array.isArray(overview.credits)?overview.credits:[];const grid=element('div','usage-providers');if(overview.githubEffective||overview.githubStatus)grid.append(githubUsageCard(overview.githubEffective,overview.githubStatus));for(const provider of providers)grid.append(usageProvider(provider));for(const provider of credits)grid.append(creditProvider(provider));if(!grid.children.length){usageRoot.append(element('p','empty',text('Usage telemetry is unavailable.')));return;}usageRoot.append(grid);}catch(error){usageMeta.textContent='Unavailable';usageRoot.replaceChildren(element('p','error-state',text('Usage unavailable: '+error.message)));}}
|
|
299
296
|
function selectedWorkflowGraph(){return workflowGraphs.find(graph=>graph.id===selectedGraphId)||null;}
|
|
300
|
-
function
|
|
301
|
-
function
|
|
297
|
+
function graphLinks(source){const links=new Map();for(const line of source.split('\\n')){const match=/^\\s*click\\s+([A-Za-z0-9_]+)\\s+href\\s+"([a-z0-9-]+\\.mmd)"\\s*$/.exec(line);if(!match)continue;const id=match[2].slice(0,-4);if(workflowGraphs.some(graph=>graph.id===id))links.set(match[1],id);}return links;}
|
|
298
|
+
function graphTargets(id){const graph=selectedWorkflowGraph();return graph?[...new Set(graphLinks(graph.source).values())]:[];}
|
|
299
|
+
function graphFromHash(){if(location.hash==='#/graphs'||location.hash==='#/graphs/entry')return 'main';if(!location.hash.startsWith('#/graphs/'))return null;const id=location.hash.slice(9);return /^[a-z0-9-]+$/.test(id)?id:null;}
|
|
302
300
|
const WORKFLOW_NAV_KEY='agents-relay:workflow-navigation';
|
|
303
301
|
function graphHistory(){try{const value=JSON.parse(sessionStorage.getItem(WORKFLOW_NAV_KEY)||'null');if(value&&Array.isArray(value.entries)&&value.entries.every(id=>typeof id==='string'&&/^[a-z0-9-]+$/.test(id)))return value;}catch{}return {entries:[]};}
|
|
304
302
|
function saveGraphHistory(data){sessionStorage.setItem(WORKFLOW_NAV_KEY,JSON.stringify(data));}
|
|
305
303
|
function graphHistoryIndex(){const n=history.state?.workflowIndex;return Number.isInteger(n)&&n>=0?n:null;}
|
|
306
|
-
function initializeGraphHistory(){const route=graphFromHash();if(route===null)return;let data=graphHistory();const index=graphHistoryIndex();if(index!==null&&data.entries[index]===route)return;data={entries:[route]};saveGraphHistory(data);history.replaceState({...history.state,workflowIndex:0,workflowPath:route==='
|
|
307
|
-
function navigateGraph(id){if(id!=='entry'&&!workflowGraphs.some(graph=>graph.id===id))return;if(graphFromHash()===null)return;initializeGraphHistory();const data=graphHistory();const current=graphHistoryIndex()??0;const path=history.state?.workflowPath;const valid=Array.isArray(path)&&path.at(-1)===graphFromHash()&&path[0]==='
|
|
304
|
+
function initializeGraphHistory(){const route=graphFromHash();if(route===null)return;let data=graphHistory();const index=graphHistoryIndex();if(index!==null&&data.entries[index]===route)return;data={entries:[route]};saveGraphHistory(data);history.replaceState({...history.state,workflowIndex:0,workflowPath:route==='main'?['main']:['main',route]},'');}
|
|
305
|
+
function navigateGraph(id){if(id!=='entry'&&!workflowGraphs.some(graph=>graph.id===id))return;if(graphFromHash()===null)return;initializeGraphHistory();const data=graphHistory();const current=graphHistoryIndex()??0;const path=history.state?.workflowPath;const valid=Array.isArray(path)&&path.at(-1)===graphFromHash()&&path[0]==='main';const nextPath=id==='main'?['main']:[...(valid?path:['main']),id];data.entries=data.entries.slice(0,current+1);data.entries.push(id);saveGraphHistory(data);history.pushState({workflowIndex:current+1,workflowPath:nextPath},'','#/graphs/'+id);syncGraphRoute();}
|
|
308
306
|
function syncGraphRoute(){const route=graphFromHash();if(route===null)return;initializeGraphHistory();selectedGraphId=route;renderGraphList();void renderSelectedGraph();}
|
|
309
|
-
function renderGraphNavigation(){const graph=selectedWorkflowGraph();initializeGraphHistory();const data=graphHistory();const index=graphHistoryIndex()??0;document.querySelector('#graph-back').disabled=index<=0;document.querySelector('#graph-forward').disabled=index>=data.entries.length-1;const path=history.state?.workflowPath;const names=Array.isArray(path)&&path.at(-1)===selectedGraphId?path:[selectedGraphId==='
|
|
307
|
+
function renderGraphNavigation(){const graph=selectedWorkflowGraph();initializeGraphHistory();const data=graphHistory();const index=graphHistoryIndex()??0;document.querySelector('#graph-back').disabled=index<=0;document.querySelector('#graph-forward').disabled=index>=data.entries.length-1;const path=history.state?.workflowPath;const names=Array.isArray(path)&&path.at(-1)===selectedGraphId?path:[selectedGraphId==='main'?'main':'main',...(selectedGraphId==='main'?[]:[selectedGraphId])];document.querySelector('#graph-breadcrumb').textContent=names.map(id=>id==='main'?'Main (conceptual)':workflowGraphs.find(g=>g.id===id)?.name||id).join(' / ');const links=document.querySelector('#graph-links');links.replaceChildren();for(const id of graphTargets(selectedGraphId)){if(!workflowGraphs.some(g=>g.id===id))continue;const button=element('button','refresh-button',text('Open '+id));button.type='button';button.addEventListener('click',()=>navigateGraph(id));links.append(button);}}
|
|
310
308
|
function renderGraphList(){graphList.replaceChildren();if(!workflowGraphs.length){graphList.append(element('p','empty',text('No workflow graphs found.')));return;}for(const graph of workflowGraphs){const button=document.createElement('button');button.type='button';button.className='graph-button';button.setAttribute('aria-current',String(graph.id===selectedGraphId));button.append(element('strong','',text(graph.name)),element('span','',text(graph.path)));button.addEventListener('click',()=>navigateGraph(graph.id));graphList.append(button);}}
|
|
311
309
|
async function graphDetail(kind,name){const panel=document.querySelector('#graph-detail');panel.hidden=false;panel.replaceChildren(element('strong','',text(kind+': '+name)),element('p','subtle',text('Loading authoritative references…')));try{const params=new URLSearchParams({graph:selectedGraphId,kind,name});const result=await loadJSON('/api/graph-symbol?'+params);panel.replaceChildren(element('strong','',text(kind+': '+name)));const sections=[['Graph',[result.graph]],['Canonical contract',result.contracts],['Implementation',result.implementation],['Tests',result.tests]];for(const [heading,refs] of sections){panel.append(element('p','detail-label',text(heading)));if(!refs?.length){panel.append(element('p','subtle',text('No exact symbol reference found.')));continue;}for(const ref of refs){const link=element('a','',text(ref.path+(ref.line>0?':'+ref.line:'')+' ['+(ref.evidence==='exact-token'?'verified graph token':'text-search suggestion')+']'));if(WORKFLOW_SOURCE_REF){link.href='https://github.com/lalalic/agents-relay/blob/'+WORKFLOW_SOURCE_REF+'/'+ref.path+(ref.line>0?'#L'+ref.line:'');link.target='_blank';link.rel='noopener noreferrer';}else{link.removeAttribute('href');link.title='Source revision unavailable';}link.target='_blank';link.rel='noopener noreferrer';panel.append(link,document.createTextNode(' '));}}panel.scrollIntoView({block:'nearest'});}catch(error){panel.append(element('p','error-state',text('Reference lookup unavailable: '+error.message)));}}
|
|
312
310
|
function interactiveNode(node,callback,label){node.classList.add('drillable');node.setAttribute('tabindex','0');node.setAttribute('role','button');node.setAttribute('aria-label',label);node.addEventListener('click',callback);node.addEventListener('keydown',event=>{if(event.key==='Enter'||event.key===' '){event.preventDefault();callback();}});}
|
|
@@ -314,8 +312,8 @@ function graphNodeDefinitions(source){const result=new Map();for(const line of s
|
|
|
314
312
|
function addNodeLabel(node,label){const ns='http://www.w3.org/2000/svg';const old=node.querySelector('g.label');if(old)old.remove();const textNode=document.createElementNS(ns,'text');textNode.setAttribute('x','0');textNode.setAttribute('y','0');textNode.setAttribute('text-anchor','middle');textNode.setAttribute('dominant-baseline','middle');textNode.setAttribute('fill','#182536');textNode.setAttribute('font-size','13');const rect=node.querySelector('rect.label-container,rect.basic,rect');const width=Math.max(65,Number(rect?.getAttribute('width')||135)-14);const maxChars=Math.max(9,Math.floor(width/7.2));const words=label.split(/\\s+/);const lines=[];let current='';for(const word of words){if(current&&(current+' '+word).length>maxChars){lines.push(current);current=word;}else current=current?current+' '+word:word;}if(current)lines.push(current);for(let i=0;i<lines.length;i++){const tspan=document.createElementNS(ns,'tspan');tspan.setAttribute('x','0');tspan.setAttribute('dy',i===0?String(-((lines.length-1)*7)):'14');tspan.textContent=lines[i];textNode.append(tspan);}node.append(textNode);}
|
|
315
313
|
function sanitizeGraphSvg(markup){const parsed=new DOMParser().parseFromString(markup,'image/svg+xml');const source=parsed.documentElement;if(source.localName!=='svg'||parsed.querySelector('parsererror'))throw new Error('Invalid SVG');const ns='http://www.w3.org/2000/svg';const elements=new Set(['svg','g','path','rect','circle','ellipse','line','polyline','polygon','text','tspan','defs','marker','title','desc','clipPath']);const attributes=new Set(['id','class','viewBox','width','height','x','y','x1','y1','x2','y2','cx','cy','r','rx','ry','d','points','transform','fill','stroke','stroke-width','stroke-dasharray','stroke-linecap','stroke-linejoin','opacity','fill-opacity','stroke-opacity','font-size','font-family','font-weight','text-anchor','dominant-baseline','markerWidth','markerHeight','markerUnits','refX','refY','orient','preserveAspectRatio','clip-path','marker-start','marker-mid','marker-end','role','aria-label']);const clean=node=>{if(node.nodeType===3)return document.createTextNode(node.textContent||'');if(node.nodeType!==1||node.namespaceURI!==ns||!elements.has(node.localName))return null;const out=document.createElementNS(ns,node.localName);for(const attr of node.attributes){if(!attr.namespaceURI&&attr.name==='style'){for(const declaration of attr.value.split(';')){const split=declaration.indexOf(':');if(split<0)continue;const property=declaration.slice(0,split).trim();const cssValue=declaration.slice(split+1).trim();if((property==='stroke'||property==='fill')&&/^(?:#[0-9a-fA-F]{3,8}|rgb\\([0-9.,%\\s]+\\)|none|currentColor)$/.test(cssValue))out.setAttribute(property,cssValue);else if(property==='stroke-width'&&/^[0-9]+(?:\\.[0-9]+)?(?:px)?$/.test(cssValue))out.setAttribute(property,cssValue);}continue;}if(attr.namespaceURI||!attributes.has(attr.name))continue;if(['marker-start','marker-mid','marker-end','clip-path'].includes(attr.name)){if(!attr.value.startsWith('url(#')||!attr.value.endsWith(')')||!(/^[a-zA-Z0-9_-]+$/.test(attr.value.slice(5,-1))))continue;}else if(attr.value.toLowerCase().includes('url(')||attr.value.includes('<')||attr.value.includes('>'))continue;out.setAttribute(attr.name,attr.value);}for(const child of node.childNodes){const safe=clean(child);if(safe)out.appendChild(safe);}return out;};return clean(source);}
|
|
316
314
|
function applyGraphPalette(svg){const ns='http://www.w3.org/2000/svg';for(const path of svg.querySelectorAll('path')){if(path.closest('marker')){if(!path.hasAttribute('fill'))path.setAttribute('fill','#65768a');continue;}if(!path.hasAttribute('fill'))path.setAttribute('fill','none');if(!path.hasAttribute('stroke'))path.setAttribute('stroke','#65768a');}for(const node of svg.querySelectorAll('g.node')){const error=node.classList.contains('failure');for(const shape of node.querySelectorAll('rect,polygon,circle,ellipse')){shape.setAttribute('fill',error?'#ffe4e4':'#f9fbff');shape.setAttribute('stroke',error?'#be3030':'#657f9e');shape.setAttribute('stroke-width','1.3');}for(const label of node.querySelectorAll('text'))label.setAttribute('fill',error?'#7c1818':'#182536');}for(const rect of svg.querySelectorAll('rect')){if(!rect.closest('g.node')&&!rect.hasAttribute('fill'))rect.setAttribute('fill','#ffffff');}for(const label of svg.querySelectorAll('text'))if(!label.hasAttribute('fill'))label.setAttribute('fill','#182536');}
|
|
317
|
-
async function renderSelectedGraph(){const graph=selectedWorkflowGraph();renderGraphNavigation();document.querySelector('#graph-detail').hidden=true;if(!graph){graphTitle.textContent='Graph not found';graphPath.textContent='';graphSource.textContent='';graphCanvas.replaceChildren(element('p','error-state',text('Graph not found: '+selectedGraphId+'. Select Entry or another graph.')));return;}graphTitle.textContent=graph.name;graphPath.textContent=graph.path;graphSource.textContent=graph.source;graphCanvas.replaceChildren(element('p','subtle',text('Rendering…')));try{if(!window.mermaid)throw new Error('Mermaid renderer unavailable');if(!window.__relayMermaidReady){window.mermaid.initialize({startOnLoad:false,securityLevel:'strict',flowchart:{htmlLabels:false}});window.__relayMermaidReady=true;}const rendered=await window.mermaid.render('relay-graph-'+graph.id.replace(/[^A-Za-z0-9_-]/g,'-'),graph.source);if(graph.id!==selectedGraphId)return;const svg=sanitizeGraphSvg(rendered.svg);graphCanvas.replaceChildren(svg);if(graph.id!=='entry'){const events=[...new Set([...graph.source.matchAll(/\\|on:\\s*([A-Za-z0-9_.-]+)\\|/g)].map(m=>m[1]))];const links=document.querySelector('#graph-links');for(const event of events){const button=element('button','refresh-button',text('Event: '+event));button.addEventListener('click',()=>graphDetail('Event',event));links.append(button);}}const definitions=graphNodeDefinitions(graph.source);for(const node of svg.querySelectorAll('g.node')){const entry=Array.from(definitions.entries()).find(([key])=>node.id.includes('-flowchart-'+key+'-'));const label=entry?.[1]||'';if(label)addNodeLabel(node,label);
|
|
318
|
-
async function drawGraphs(){try{const graphs=await loadJSON('/api/graphs');workflowGraphs=
|
|
315
|
+
async function renderSelectedGraph(){const graph=selectedWorkflowGraph();renderGraphNavigation();document.querySelector('#graph-detail').hidden=true;if(!graph){graphTitle.textContent='Graph not found';graphPath.textContent='';graphSource.textContent='';graphCanvas.replaceChildren(element('p','error-state',text('Graph not found: '+selectedGraphId+'. Select Entry or another graph.')));return;}graphTitle.textContent=graph.name;graphPath.textContent=graph.path;graphSource.textContent=graph.source;graphCanvas.replaceChildren(element('p','subtle',text('Rendering…')));try{if(!window.mermaid)throw new Error('Mermaid renderer unavailable');if(!window.__relayMermaidReady){window.mermaid.initialize({startOnLoad:false,securityLevel:'strict',flowchart:{htmlLabels:false}});window.__relayMermaidReady=true;}const rendered=await window.mermaid.render('relay-graph-'+graph.id.replace(/[^A-Za-z0-9_-]/g,'-'),graph.source);if(graph.id!==selectedGraphId)return;const svg=sanitizeGraphSvg(rendered.svg.replace(/<a\\b[^>]*>/g,'<g>').replace(/<\\/a>/g,'</g>'));graphCanvas.replaceChildren(svg);if(graph.id!=='entry'){const events=[...new Set([...graph.source.matchAll(/\\|on:\\s*([A-Za-z0-9_.-]+)\\|/g)].map(m=>m[1]))];const links=document.querySelector('#graph-links');for(const event of events){const button=element('button','refresh-button',text('Event: '+event));button.addEventListener('click',()=>graphDetail('Event',event));links.append(button);}}const definitions=graphNodeDefinitions(graph.source);for(const node of svg.querySelectorAll('g.node')){const entry=Array.from(definitions.entries()).find(([key])=>node.id.includes('-flowchart-'+key+'-'));const label=entry?.[1]||'';if(label)addNodeLabel(node,label);const target=entry?.[0];const next=target?graphLinks(graph.source).get(target):null;if(next){interactiveNode(node,()=>navigateGraph(next),'Explore '+target);}else{const match=/^(state|guard|action):\\s*([A-Za-z0-9_.-]+)/.exec(label);if(match)interactiveNode(node,()=>graphDetail(match[1]==='guard'?'Guard':match[1]==='action'?'Action':'State',match[2]),'Inspect '+match[1]+' '+match[2]);}}applyGraphPalette(svg);}catch(error){graphCanvas.replaceChildren(element('p','error-state',text('Visual preview unavailable: '+error.message+'. Mermaid source remains available below.')));}}
|
|
316
|
+
async function drawGraphs(){try{const graphs=await loadJSON('/api/graphs');workflowGraphs=Array.isArray(graphs)?graphs:[];const requested=graphFromHash();selectedGraphId=requested||'main';graphsMeta.textContent=(workflowGraphs.length-1)+' executable workflow graphs · main is conceptual';renderGraphList();await renderSelectedGraph();}catch(error){graphsMeta.textContent='Unavailable';workflowGraphs=[];selectedGraphId='main';renderGraphList();await renderSelectedGraph();graphPath.textContent='Executable graphs unavailable: '+error.message;}}
|
|
319
317
|
function renderAudit(records){auditLog.replaceChildren();if(!Array.isArray(records)||!records.length){auditLog.append(element('p','empty',text('No schema v5 routing decisions yet.')));return;}for(const record of records){const card=element('article','audit-entry');const head=element('div','audit-entry-head');head.append(element('strong','',text(routeWorker(record)+' · '+value(record?.selected?.model?.model))),element('span','subtle',text(new Date(record.timestamp).toLocaleString())));card.append(head);const correlation=record.correlation||{};card.append(element('div','subtle',text([correlation.job_id,correlation.task_id].filter(Boolean).join(' / ')||'uncorrelated')));const probabilities=record.decision_evidence?.probabilities||{};const gates=(record.gate_results||[]).filter(item=>!item.eligible).map(item=>item.candidate_id+': '+item.rejection_reasons.join(', '));const nl=String.fromCharCode(10);const details=document.createElement('pre');details.textContent='fallback: '+String(Boolean(record.decision_evidence?.fallback))+(Object.keys(probabilities).length?nl+'probabilities: '+JSON.stringify(probabilities,null,2):'')+(gates.length?nl+'rejected: '+gates.join(nl):'');card.append(details);auditLog.append(card);}}
|
|
320
318
|
async function drawAudit(){auditRefresh.disabled=true;try{renderAudit(await loadJSON('/api/decision-audit'));}catch(error){auditLog.replaceChildren(element('p','error-state',text('Audit unavailable: '+error.message)));}finally{auditRefresh.disabled=false;}}
|
|
321
319
|
async function drawRules(){rulesStatus.textContent='';try{const rules=await loadJSON('/api/execution-router-rules');rulesText.value=typeof rules.text==='string'?rules.text:'';rulesMeta.textContent=rules.hash&&rules.hash!=='none'?'Loaded · '+rules.hash.slice(0,12):'No custom rules saved';}catch(error){rulesMeta.textContent='Unavailable';rulesStatus.textContent='Could not load rules: '+error.message;}void drawAudit();}
|
|
@@ -338,7 +336,7 @@ mobileJobs.addEventListener('change',syncMobileJobsView);
|
|
|
338
336
|
window.addEventListener('hashchange',syncMobileJobsView);
|
|
339
337
|
usageTab.addEventListener('click',()=>{location.hash='#/usage';showView('usage');});
|
|
340
338
|
graphsTab.addEventListener('click',()=>{location.hash='#/graphs/entry';showView('graphs');});
|
|
341
|
-
document.querySelector('#graph-entry').addEventListener('click',()=>navigateGraph('
|
|
339
|
+
document.querySelector('#graph-entry').addEventListener('click',()=>navigateGraph('main'));
|
|
342
340
|
const graphDialog=document.querySelector('#graph-dialog');const graphViewport=document.querySelector('#graph-dialog-viewport');let graphZoom=1;const graphCanvasHome=graphCanvas.parentNode;function setGraphZoom(value){graphZoom=Math.min(4,Math.max(.35,value));const svg=graphCanvas.querySelector('svg');if(svg){svg.style.width=(Number(svg.getAttribute('width'))||Math.max(900,svg.viewBox.baseVal.width))*graphZoom+'px';svg.style.maxWidth='none';}document.querySelector('#graph-zoom-level').textContent=Math.round(graphZoom*100)+'%';}document.querySelector('#graph-maximize').addEventListener('click',()=>{graphDialog.showModal();graphViewport.append(graphCanvas);document.querySelector('#graph-dialog-title').textContent=graphTitle.textContent;setGraphZoom(1);});function closeGraphViewer(){graphCanvasHome.insertBefore(graphCanvas,graphCanvasHome.querySelector('.graph-source-details'));setGraphZoom(1);}graphDialog.addEventListener('close',closeGraphViewer);document.querySelector('#graph-dialog-close').addEventListener('click',()=>graphDialog.close());document.querySelector('#graph-zoom-in').addEventListener('click',()=>setGraphZoom(graphZoom*1.25));document.querySelector('#graph-zoom-out').addEventListener('click',()=>setGraphZoom(graphZoom/1.25));document.querySelector('#graph-zoom-reset').addEventListener('click',()=>setGraphZoom(1));graphViewport.addEventListener('wheel',event=>{if(!event.ctrlKey&&!event.metaKey)return;event.preventDefault();setGraphZoom(graphZoom*(event.deltaY<0?1.15:1/1.15));},{passive:false});let drag=null;graphViewport.addEventListener('pointerdown',event=>{if(event.target.closest('.drillable'))return;drag={x:event.clientX,y:event.clientY,left:graphViewport.scrollLeft,top:graphViewport.scrollTop};graphViewport.setPointerCapture(event.pointerId);});graphViewport.addEventListener('pointermove',event=>{if(!drag)return;graphViewport.scrollLeft=drag.left-(event.clientX-drag.x);graphViewport.scrollTop=drag.top-(event.clientY-drag.y);});graphViewport.addEventListener('pointerup',()=>drag=null);graphViewport.addEventListener('pointercancel',()=>drag=null);
|
|
343
341
|
document.querySelector('#graph-back').addEventListener('click',()=>history.back());
|
|
344
342
|
document.querySelector('#graph-forward').addEventListener('click',()=>history.forward());
|
package/dist/dashboard-style.js
CHANGED
|
@@ -99,6 +99,7 @@ export const dashboardStyles = ` <style>
|
|
|
99
99
|
.graph-preview-head h3 { margin:0; font-size:.95rem; }
|
|
100
100
|
.graph-canvas { min-height:24rem; display:grid; place-items:center; overflow:auto; padding:1rem; border:1px solid var(--border); border-radius:.45rem; background:var(--surface2); }
|
|
101
101
|
.graph-canvas svg { max-width:none; height:auto; }
|
|
102
|
+
.graph-canvas svg rect.background { display:none; }
|
|
102
103
|
.graph-navigation { display:flex; flex-wrap:wrap; align-items:center; gap:.5rem; margin-bottom:.7rem; }
|
|
103
104
|
.graph-canvas .node.drillable { cursor:pointer; }
|
|
104
105
|
.graph-dialog { width:96vw; height:94vh; max-width:none; max-height:none; padding:.7rem; border:1px solid var(--border); border-radius:.5rem; background:var(--surface); color:var(--text); }
|
package/dist/dashboard.js
CHANGED
|
@@ -17,7 +17,7 @@ export async function dashboardWorkflowGraphs() {
|
|
|
17
17
|
const names = (await readdir(WORKFLOW_DIR)).filter(name => name.endsWith('.mmd')).sort();
|
|
18
18
|
return Promise.all(names.map(async (name) => ({
|
|
19
19
|
id: name.replace(/\.mmd$/, ''),
|
|
20
|
-
name: name.replace(/\.mmd$/, '').split('-').map(part => part.charAt(0).toUpperCase() + part.slice(1)).join(' '),
|
|
20
|
+
name: name === 'main.mmd' ? 'Main / Happy Path' : name.replace(/\.mmd$/, '').split('-').map(part => part.charAt(0).toUpperCase() + part.slice(1)).join(' '),
|
|
21
21
|
path: `workflow/${name}`,
|
|
22
22
|
source: await readFile(join(WORKFLOW_DIR, name), 'utf8'),
|
|
23
23
|
})));
|
package/package.json
CHANGED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
%% dashboard: presentation-only; not an executable workflow
|
|
2
|
+
flowchart LR
|
|
3
|
+
objective["START: Job Objective"] --> planner["Planner"] --> worker["Worker"] --> reviewer["Reviewer"] --> merge["Merge / Delivery"] --> achieved["Objective Achieved"] --> completed["Job Completed"]
|
|
4
|
+
reviewer -->|Changes Required| fix["Fix Task"] --> worker
|
|
5
|
+
planner -->|Failed| troubleshooter["Troubleshooter"]
|
|
6
|
+
worker -->|Failed| troubleshooter
|
|
7
|
+
reviewer -->|Failed| troubleshooter
|
|
8
|
+
merge -->|Failed| troubleshooter
|
|
9
|
+
troubleshooter --> resolution["Resolution"]
|
|
10
|
+
resolution -->|Recovered| planner
|
|
11
|
+
resolution -->|Recovered| worker
|
|
12
|
+
resolution -->|Recovered| reviewer
|
|
13
|
+
resolution -->|Recovered| merge
|
|
14
|
+
resolution -->|Needs human action| escalator["Escalator workflow"] -->|Resolved| resolution
|
|
15
|
+
classDef failure fill:#ffe4e4,stroke:#be3030,color:#7c1818
|
|
16
|
+
class troubleshooter,resolution,escalator failure
|
|
17
|
+
linkStyle 8,9,10,11,12,13,14,15,16,17,18 stroke:#be3030,stroke-width:2px
|
|
18
|
+
|
|
19
|
+
click objective href "job-lifecycle.mmd"
|
|
20
|
+
click planner href "planner-orchestration.mmd"
|
|
21
|
+
click worker href "task-lifecycle.mmd"
|
|
22
|
+
click reviewer href "review-lifecycle.mmd"
|
|
23
|
+
click merge href "job-lifecycle.mmd"
|
|
24
|
+
click achieved href "job-lifecycle.mmd"
|
|
25
|
+
click completed href "job-lifecycle.mmd"
|
|
26
|
+
click fix href "review-lifecycle.mmd"
|
|
27
|
+
click troubleshooter href "troubleshooter.mmd"
|
|
28
|
+
click resolution href "blocker-resolution.mmd"
|
|
29
|
+
click escalator href "escalation.mmd"
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/** Conceptual dashboard-only overview; workflow/*.mmd remains the transition authority. */
|
|
2
|
-
export const workflowPresentation = {
|
|
3
|
-
entrySource: [
|
|
4
|
-
'flowchart LR',
|
|
5
|
-
'objective["START: Job Objective"] --> planner["Planner"] --> worker["Worker"] --> reviewer["Reviewer"] --> merge["Merge / Delivery"] --> achieved["Objective Achieved"] --> completed["Job Completed"]',
|
|
6
|
-
'reviewer -->|Changes Required| fix["Fix Task"] --> worker',
|
|
7
|
-
'planner -->|Failed| troubleshooter["Troubleshooter"]',
|
|
8
|
-
'worker -->|Failed| troubleshooter',
|
|
9
|
-
'reviewer -->|Failed| troubleshooter',
|
|
10
|
-
'merge -->|Failed| troubleshooter',
|
|
11
|
-
'troubleshooter --> resolution["Resolution"]',
|
|
12
|
-
'resolution -->|Recovered| planner',
|
|
13
|
-
'resolution -->|Recovered| worker',
|
|
14
|
-
'resolution -->|Recovered| reviewer',
|
|
15
|
-
'resolution -->|Recovered| merge',
|
|
16
|
-
'resolution -->|Needs human action| escalator["Escalator workflow"] -->|Resolved| resolution',
|
|
17
|
-
'classDef failure fill:#ffe4e4,stroke:#be3030,color:#7c1818',
|
|
18
|
-
'class troubleshooter,resolution,escalator failure',
|
|
19
|
-
'linkStyle 8,9,10,11,12,13,14,15,16,17,18 stroke:#be3030,stroke-width:2px'
|
|
20
|
-
].join('\n'),
|
|
21
|
-
navigation: { objective: ['job-lifecycle'], planner: ['planner-orchestration', 'planner-trigger'], worker: ['task-lifecycle'], reviewer: ['review-lifecycle'], merge: ['job-lifecycle'], achieved: ['job-lifecycle'], completed: ['job-lifecycle'], fix: ['review-lifecycle', 'task-lifecycle'], troubleshooter: ['troubleshooter', 'task-resolution'], resolution: ['blocker-resolution', 'task-resolution', 'authoritative-unblock'], escalator: ['escalation'] }
|
|
22
|
-
};
|