agents-relay 1.0.76 → 1.0.77
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.js +21 -5
- package/dist/markers.js +2 -0
- package/dist/store.js +9 -2
- package/package.json +1 -1
package/dist/dashboard.js
CHANGED
|
@@ -80,6 +80,7 @@ export const dashboardHtml = `<!doctype html>
|
|
|
80
80
|
.panel-actions { display:flex; align-items:center; gap:.45rem; }
|
|
81
81
|
.job-filters { display:flex; gap:.35rem; align-items:center; flex-wrap:wrap; }
|
|
82
82
|
.job-filter { max-width:145px; border:1px solid var(--border); border-radius:.35rem; background:var(--surface); color:var(--text); padding:.27rem .4rem; font:inherit; font-size:.72rem; }
|
|
83
|
+
.job-search { width:190px; max-width:28vw; border:1px solid var(--border); border-radius:.35rem; background:var(--surface); color:var(--text); padding:.27rem .45rem; font:inherit; font-size:.72rem; }
|
|
83
84
|
.quick-filter { border:1px solid var(--border); border-radius:.35rem; background:var(--surface); color:var(--muted); padding:.27rem .42rem; font:inherit; font-size:.7rem; font-weight:750; cursor:pointer; }
|
|
84
85
|
.quick-filter[aria-pressed="true"] { border-color:var(--accent); background:var(--accent2); color:var(--accent); }
|
|
85
86
|
.refresh-button { border:1px solid var(--border); border-radius:.35rem; background:var(--surface); color:var(--muted); padding:.28rem .55rem; font:inherit; font-size:.72rem; font-weight:700; cursor:pointer; }
|
|
@@ -125,6 +126,12 @@ export const dashboardHtml = `<!doctype html>
|
|
|
125
126
|
.task-row[aria-current="true"] { background:var(--accent2); }
|
|
126
127
|
.task-id { min-width:0; flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font: .77rem ui-monospace,SFMono-Regular,Menlo,monospace; }
|
|
127
128
|
.task-actions { display:flex; justify-content:flex-end; gap:.25rem; margin-bottom:.5rem; }
|
|
129
|
+
.task-link { color:var(--accent); font-weight:700; }
|
|
130
|
+
.agent-graph { display:grid; gap:.45rem; margin-top:.55rem; padding:.55rem; border:1px solid var(--border); border-radius:.4rem; background:var(--surface2); overflow:auto; }
|
|
131
|
+
.agent-wave { display:flex; align-items:center; gap:.4rem; min-width:max-content; }
|
|
132
|
+
.agent-node { padding:.32rem .48rem; border:1px solid var(--border); border-radius:.35rem; background:var(--surface); font:.73rem ui-monospace,SFMono-Regular,Menlo,monospace; }
|
|
133
|
+
.agent-arrow { color:var(--muted); font-weight:800; }
|
|
134
|
+
.agent-graph-source { margin:.1rem 0 0; padding:.45rem; border-radius:.35rem; background:var(--surface); color:var(--muted); font:.7rem/1.45 ui-monospace,SFMono-Regular,Menlo,monospace; white-space:pre; overflow:auto; }
|
|
128
135
|
.event-list { display:grid; gap:.45rem; }
|
|
129
136
|
.event-item { padding-bottom:.4rem; border-bottom:1px solid var(--border); }
|
|
130
137
|
.event-meta { display:flex; flex-wrap:wrap; gap:.4rem; color:var(--muted); font-size:.68rem; }
|
|
@@ -159,7 +166,7 @@ export const dashboardHtml = `<!doctype html>
|
|
|
159
166
|
</header>
|
|
160
167
|
<main>
|
|
161
168
|
<section id="jobs-view" role="tabpanel" aria-labelledby="jobs-tab" class="panel jobs-panel">
|
|
162
|
-
<div class="panel-head"><h2>Jobs</h2><div class="panel-actions"><div class="job-filters"><select id="quick-status" class="job-filter" aria-label="Filter jobs by derived status"><option value="">STATUS</option></select><button id="quick-autonomous" class="quick-filter" type="button" aria-pressed="false">AUTONOMOUS</button><button id="quick-draft" class="quick-filter" type="button" aria-pressed="false">DRAFT</button><select id="repo-filter" class="job-filter" aria-label="Filter jobs by repository"><option value="">All repos</option></select></div><button id="jobs-refresh" class="refresh-button" type="button">Refresh</button></div></div>
|
|
169
|
+
<div class="panel-head"><h2>Jobs</h2><div class="panel-actions"><div class="job-filters"><input id="job-search" class="job-search" type="search" placeholder="Search title or PR #" aria-label="Search jobs by title or PR number"><select id="quick-status" class="job-filter" aria-label="Filter jobs by derived status"><option value="">STATUS</option></select><button id="quick-autonomous" class="quick-filter" type="button" aria-pressed="false">AUTONOMOUS</button><button id="quick-draft" class="quick-filter" type="button" aria-pressed="false">DRAFT</button><select id="repo-filter" class="job-filter" aria-label="Filter jobs by repository"><option value="">All repos</option></select></div><button id="jobs-refresh" class="refresh-button" type="button">Refresh</button></div></div>
|
|
163
170
|
<div class="panel-body" aria-live="polite"><ul id="job-list" class="job-list"><li class="skeleton" aria-hidden="true"></li></ul></div>
|
|
164
171
|
</section>
|
|
165
172
|
<div class="workspace" id="jobs-workspace">
|
|
@@ -207,6 +214,7 @@ const usageView=document.querySelector('#usage-view');
|
|
|
207
214
|
const jobsWorkspace=document.querySelector('#jobs-workspace');
|
|
208
215
|
const jobList=document.querySelector('#job-list');
|
|
209
216
|
const jobsRefresh=document.querySelector('#jobs-refresh');
|
|
217
|
+
const jobSearch=document.querySelector('#job-search');
|
|
210
218
|
const repoFilter=document.querySelector('#repo-filter');
|
|
211
219
|
const quickStatus=document.querySelector('#quick-status');
|
|
212
220
|
const quickAutonomous=document.querySelector('#quick-autonomous');
|
|
@@ -229,6 +237,7 @@ let selectedTaskId=sessionStorage.getItem('agents-relay:selected-task');
|
|
|
229
237
|
let selectedRepo=sessionStorage.getItem('agents-relay:repo-filter')||'';
|
|
230
238
|
let quickMode=sessionStorage.getItem('agents-relay:quick-filter')||'jobs';
|
|
231
239
|
let selectedRuntimeStatus=sessionStorage.getItem('agents-relay:runtime-status-filter')||'';
|
|
240
|
+
let searchQuery=sessionStorage.getItem('agents-relay:job-search')||'';
|
|
232
241
|
let eventTaskId=selectedTaskId;
|
|
233
242
|
const displayedEventIds=new Set();
|
|
234
243
|
|
|
@@ -245,12 +254,17 @@ function relativeTime(timestamp){if(!timestamp)return '—';const parsed=Date.pa
|
|
|
245
254
|
function safePrUrl(repository,prNumber){if(typeof repository!=='string'||!Number.isInteger(prNumber)||prNumber<=0)return null;const parts=repository.split('/');if(parts.length!==2)return null;const [owner,repo]=parts;if(!owner||!repo||owner==='.'||owner==='..'||repo==='.'||repo==='..')return null;if(!/^[A-Za-z0-9-]+$/.test(owner)||!/^[A-Za-z0-9._-]+$/.test(repo))return null;return 'https://github.com/'+owner+'/'+repo+'/pull/'+prNumber;}
|
|
246
255
|
function prField(job){const box=element('div','detail-group');box.append(element('p','detail-label',text('GitHub PR')));const line=element('p','detail-value');const url=safePrUrl(job.repository,job.prNumber);if(url){const link=document.createElement('a');link.href=url;link.target='_blank';link.rel='noopener noreferrer';link.textContent='PR #'+job.prNumber;line.append(link);}else line.append(text('unavailable'));box.append(line);return box;}
|
|
247
256
|
function chatField(task){const box=element('div','detail-group');box.append(element('p','detail-label',text('Chat')));const line=element('p','detail-value');if(task.adapter==='chatgpt'&&typeof task.threadId==='string'&&task.threadId){const link=document.createElement('a');link.href='https://chatgpt.com/c/'+encodeURIComponent(task.threadId);link.target='_blank';link.rel='noopener noreferrer';link.textContent='Open chat';line.append(link);}else line.append(text('—'));box.append(line);return box;}
|
|
257
|
+
function taskCommentField(task){const box=element('div','detail-group');box.append(element('p','detail-label',text('PR comment')));const line=element('p','detail-value');if(typeof task.taskUrl==='string'&&task.taskUrl.startsWith('https://github.com/')){const link=document.createElement('a');link.className='task-link';link.href=task.taskUrl;link.target='_blank';link.rel='noopener noreferrer';link.textContent='Open task comment';line.append(link);}else line.append(text('—'));box.append(line);return box;}
|
|
258
|
+
function parseAgentGraphSource(source){if(typeof source!=='string'||!source.trim())return null;const agents=new Map(),edges=[];for(const raw of source.split(/\\r?\\n/)){const line=raw.trim();if(!line||line.startsWith('%%')||line.startsWith('flowchart '))continue;let match=line.match(/^([A-Za-z_][A-Za-z0-9_.-]*)(?:\\[agent:([A-Za-z_][A-Za-z0-9_.-]*)\\])?\\s*-->\\s*([A-Za-z_][A-Za-z0-9_.-]*)(?:\\[agent:([A-Za-z_][A-Za-z0-9_.-]*)\\])?$/);if(match){agents.set(match[1],match[2]||agents.get(match[1])||match[1]);agents.set(match[3],match[4]||agents.get(match[3])||match[3]);edges.push([match[1],match[3]]);continue;}match=line.match(/^([A-Za-z_][A-Za-z0-9_.-]*)\\[agent:([A-Za-z_][A-Za-z0-9_.-]*)\\]$/);if(match){agents.set(match[1],match[2]);continue;}return null;}const incoming=new Map([...agents.keys()].map(id=>[id,0]));for(const [,to] of edges)incoming.set(to,(incoming.get(to)||0)+1);const remaining=new Map(incoming),waves=[];let ready=[...remaining].filter(([,degree])=>degree===0).map(([id])=>id).sort();while(ready.length){waves.push(ready);const next=[];for(const id of ready)for(const [from,to] of edges)if(from===id){const degree=(remaining.get(to)||0)-1;remaining.set(to,degree);if(degree===0)next.push(to);}ready=[...new Set(next)].sort();}return {agents,waves};}
|
|
259
|
+
function agentGraphField(task){if(typeof task.agentGraph!=='string'||!task.agentGraph.trim())return null;const box=element('div','detail-group');box.style.gridColumn='1 / -1';box.append(element('p','detail-label',text('Task agent graph')));const graph=element('div','agent-graph');const parsed=parseAgentGraphSource(task.agentGraph);if(parsed&&parsed.waves.length){parsed.waves.forEach((wave,index)=>{const row=element('div','agent-wave');if(index>0)row.append(element('span','agent-arrow',text('↓')));for(const id of wave)row.append(element('span','agent-node',text(id+' · '+(parsed.agents.get(id)||id))));graph.append(row);});}const source=document.createElement('pre');source.className='agent-graph-source';source.textContent=task.agentGraph;graph.append(source);box.append(graph);return box;}
|
|
248
260
|
function clearEvents(){displayedEventIds.clear();eventFeed.replaceChildren(element('li','empty',text('Waiting for events.')));}
|
|
249
261
|
async function loadEventHistory(){if(!selectedJob){clearEvents();return;}const params=new URLSearchParams({job:selectedJob,limit:String(MAX_EVENTS)});if(eventTaskId)params.set('task',eventTaskId);try{const history=await loadJSON('/api/events?'+params.toString());clearEvents();for(const event of Array.isArray(history)?history.slice().reverse():[])addEvent(event);}catch{clearEvents();}}
|
|
250
262
|
function selectJob(id){selectedJob=id;selectedTaskId=null;eventTaskId=null;rememberSelection();clearEvents();location.hash=id?'#/jobs/'+encodeURIComponent(id):'#/jobs';renderAll();void loadEventHistory();}
|
|
251
263
|
function matchesQuickFilter(job){if(quickMode==='autonomous')return job.executionMode==='autonomous';if(quickMode==='draft')return job.draft||job.state==='DRAFT';if(quickMode==='status')return !!selectedRuntimeStatus&&(job.runtimeStatus||'IDLE')===selectedRuntimeStatus;return true;}
|
|
252
|
-
function
|
|
264
|
+
function matchesSearch(job){const query=searchQuery.trim().toLowerCase();if(!query)return true;const pr=String(job.prNumber||'');const normalized=query.replace(/^pr\s*#?/,'').replace(/^#/,'');return String(job.title||job.id||'').toLowerCase().includes(query)||pr.includes(normalized);}
|
|
265
|
+
function filteredJobs(){return jobs.filter(job=>(!selectedRepo||job.repository===selectedRepo)&&matchesQuickFilter(job)&&matchesSearch(job));}
|
|
253
266
|
function setQuickMode(mode,status=''){quickMode=mode;selectedRuntimeStatus=mode==='status'?status:'';sessionStorage.setItem('agents-relay:quick-filter',quickMode);if(selectedRuntimeStatus)sessionStorage.setItem('agents-relay:runtime-status-filter',selectedRuntimeStatus);else sessionStorage.removeItem('agents-relay:runtime-status-filter');syncFilters();renderJobList();}
|
|
267
|
+
function toggleQuickMode(mode){setQuickMode(quickMode===mode?'jobs':mode);}
|
|
254
268
|
function syncFilters(){
|
|
255
269
|
const repos=[...new Set(jobs.map(job=>job.repository).filter(Boolean))].sort();
|
|
256
270
|
const previousRepo=selectedRepo;
|
|
@@ -265,6 +279,7 @@ function syncFilters(){
|
|
|
265
279
|
quickStatus.value=quickMode==='status'?selectedRuntimeStatus:'';
|
|
266
280
|
quickAutonomous.setAttribute('aria-pressed',String(quickMode==='autonomous'));
|
|
267
281
|
quickDraft.setAttribute('aria-pressed',String(quickMode==='draft'));
|
|
282
|
+
jobSearch.value=searchQuery;
|
|
268
283
|
}
|
|
269
284
|
function actionEnabled(job,action){if(action==='start')return job.state==='DRAFT'&&job.githubState!=='CLOSED'&&job.githubState!=='MERGED';if(action==='reconcile')return job.state==='ACTIVE'&&job.githubState==='OPEN';if(action==='cancel')return job.state!=='COMPLETED'&&job.state!=='CANCELLED'&&job.githubState==='OPEN';return false;}
|
|
270
285
|
function actionButton(job,action,label,icon){const button=document.createElement('button');button.type='button';button.className='job-action';button.textContent=icon;button.title=label;button.setAttribute('aria-label',label);button.disabled=!actionEnabled(job,action);button.addEventListener('click',event=>{event.stopPropagation();void runJobAction(job,action,button);});return button;}
|
|
@@ -278,7 +293,7 @@ function attemptBudget(task){const attempt=Number(task?.attempt)||0;const max=Nu
|
|
|
278
293
|
function taskActionEnabled(task,action){if(action==='retry')return ['FAILED','BLOCKED'].includes(task.state);if(action==='cancel')return ['DRAFT','QUEUED','BLOCKED','FAILED'].includes(task.state);return false;}
|
|
279
294
|
function taskActionButton(task,action,label,icon){const button=document.createElement('button');button.type='button';button.className='job-action';button.textContent=icon;button.title=label;button.setAttribute('aria-label',label);button.disabled=!taskActionEnabled(task,action);button.addEventListener('click',()=>void runTaskAction(task,action,button));return button;}
|
|
280
295
|
async function runTaskAction(task,action,button){const job=selected();if(!job)return;button.disabled=true;try{const response=await fetch('/api/task-action?id='+encodeURIComponent(job.id)+'&task='+encodeURIComponent(task.id)+'&action='+encodeURIComponent(action),{method:'POST'});if(!response.ok)throw new Error(response.status+' '+response.statusText);const updated=await response.json();const index=jobs.findIndex(candidate=>candidate.id===job.id);if(index>=0)jobs[index]=updated;else jobs.push(updated);renderAll();void loadEventHistory();}catch(error){button.title=(action==='retry'?'Restart':'Cancel')+': '+error.message;}finally{renderAll();}}
|
|
281
|
-
function renderTaskDetail(task){taskDetailRoot.replaceChildren();if(!task){taskDetailRoot.append(element('p','empty',text('Select a task to inspect execution details.')));return;}const actions=element('div','task-actions');actions.append(taskActionButton(task,'retry','Restart task','↻'),taskActionButton(task,'cancel','Cancel task','×'));const routing=task.routing||{};const groups=element('div','detail-groups');groups.append(field('Task',task.id),field('Kind',task.kind||'work'),field('State',task.state),field('Planning decision',task.planningDecisionId||'—'),field('Planning source',task.planningSource||'—'),field('Planning round',task.planningRound===undefined?'—':String(task.planningRound)),field('Project / agent',value(task.projectName)+' / '+value(task.agentName)),field('Parent task',task.parentTaskId||'root'),field('Adapter',task.adapter),field('Provider / model',value(routing.provider)+' / '+value(routing.model)),field('Reasoning / profile',value(routing.reasoning||routing.profile)),field('Attempt',attemptBudget(task)),field('Dependencies',Array.isArray(task.dependencies)&&task.dependencies.length?task.dependencies.join(', '):'none'),chatField(task));if(task.plannerResult)groups.append(field('Objective status',task.plannerResult.objective_status),markdownField('Assessment',task.plannerResult.assessment));if(task.error)groups.append(field('Error',task.error));taskDetailRoot.append(actions,groups);}
|
|
296
|
+
function renderTaskDetail(task){taskDetailRoot.replaceChildren();if(!task){taskDetailRoot.append(element('p','empty',text('Select a task to inspect execution details.')));return;}const actions=element('div','task-actions');actions.append(taskActionButton(task,'retry','Restart task','↻'),taskActionButton(task,'cancel','Cancel task','×'));const routing=task.routing||{};const groups=element('div','detail-groups');groups.append(field('Task',task.id),field('Kind',task.kind||'work'),field('State',task.state),field('Planning decision',task.planningDecisionId||'—'),field('Planning source',task.planningSource||'—'),field('Planning round',task.planningRound===undefined?'—':String(task.planningRound)),field('Project / agent',value(task.projectName)+' / '+value(task.agentName)),field('Parent task',task.parentTaskId||'root'),field('Adapter',task.adapter),field('Provider / model',value(routing.provider)+' / '+value(routing.model)),field('Reasoning / profile',value(routing.reasoning||routing.profile)),field('Attempt',attemptBudget(task)),field('Dependencies',Array.isArray(task.dependencies)&&task.dependencies.length?task.dependencies.join(', '):'none'),taskCommentField(task),chatField(task));const graph=agentGraphField(task);if(graph)groups.append(graph);if(task.plannerResult)groups.append(field('Objective status',task.plannerResult.objective_status),markdownField('Assessment',task.plannerResult.assessment));if(task.error)groups.append(field('Error',task.error));taskDetailRoot.append(actions,groups);}
|
|
282
297
|
function reviewStatus(job){const review=job?.review;if(!review)return 'Not started';if(review.outcome==='approved'&&review.headSha===job.headSha)return 'MERGE READY · approved';const stale=Array.isArray(job?.tasks)&&job.tasks.some(task=>task.agentName==='reviewer'&&task.reviewHeadSha&&job.headSha&&task.reviewHeadSha!==job.headSha);if(stale)return 'STALE · awaiting review';return String(review.outcome||'pending').replace('_',' ').toUpperCase();}
|
|
283
298
|
function renderSelectedJob(){jobDetail.replaceChildren();const job=selected();if(!job){selectedJobTitle.textContent='';jobDetail.append(element('div','empty',text('No durable jobs discovered.')));buildTree([]);renderTaskDetail(null);return;}selectedJobTitle.textContent=value(job.title||job.id);if(job.id!==selectedJob){selectedJob=job.id;selectedTaskId=null;eventTaskId=null;clearEvents();rememberSelection();}const summary=element('div','job-summary');summary.append(field('PR state',job.githubState||'UNKNOWN'),field('Lifecycle state',job.state),field('Runtime status',job.runtimeStatus||'IDLE'),field('Execution mode',job.executionMode||'fixed'),field('Review',reviewStatus(job)),field('Review head SHA',job.review?.headSha||job.headSha||'—'),prField(job),field('Repository',job.repository||'—'),field('Last activity',relativeTime(job.updatedAt)),field('Task states',compactCounts(job.tasks)));jobDetail.append(summary);const selectedTask=(job.tasks||[]).find(task=>task.id===selectedTaskId)||null;if(selectedTaskId&&!selectedTask){selectedTaskId=null;eventTaskId=null;clearEvents();rememberSelection();}buildTree(job.tasks||[]);renderTaskDetail(selectedTask);renderEventScope();}
|
|
284
299
|
function renderAll(){jobs.sort(compareJobs);syncFilters();renderJobList();renderSelectedJob();}
|
|
@@ -309,8 +324,9 @@ jobsTab.addEventListener('click',()=>{location.hash=selectedJob?'#/jobs/'+encode
|
|
|
309
324
|
jobsRefresh.addEventListener('click',()=>void refresh(true));
|
|
310
325
|
eventsAllTasks.addEventListener('click',()=>{eventTaskId=null;clearEvents();renderEventScope();void loadEventHistory();});
|
|
311
326
|
repoFilter.addEventListener('change',()=>{selectedRepo=repoFilter.value;sessionStorage.setItem('agents-relay:repo-filter',selectedRepo);renderJobList();});
|
|
312
|
-
|
|
313
|
-
|
|
327
|
+
jobSearch.addEventListener('input',()=>{searchQuery=jobSearch.value;sessionStorage.setItem('agents-relay:job-search',searchQuery);renderJobList();});
|
|
328
|
+
quickAutonomous.addEventListener('click',()=>toggleQuickMode('autonomous'));
|
|
329
|
+
quickDraft.addEventListener('click',()=>toggleQuickMode('draft'));
|
|
314
330
|
quickStatus.addEventListener('change',()=>setQuickMode(quickStatus.value?'status':'jobs',quickStatus.value));
|
|
315
331
|
selectedJobRefresh.addEventListener('click',()=>void refreshSelected());
|
|
316
332
|
usageTab.addEventListener('click',()=>{location.hash='#/usage';showView('usage');});
|
package/dist/markers.js
CHANGED
|
@@ -56,12 +56,14 @@ ${JOB_OBJECTIVE_END}`;
|
|
|
56
56
|
}
|
|
57
57
|
export function renderTaskComment(task) {
|
|
58
58
|
const result = task.result?.summary?.trim() ? `\n\n### Result\n\n${task.result.summary.trim()}` : '';
|
|
59
|
+
const graph = task.agentGraph?.trim() ? `\n\n### Agent graph\n\n\`\`\`mermaid\n${task.agentGraph.trim()}\n\`\`\`` : '';
|
|
59
60
|
const { input: _input, ...metadata } = task;
|
|
60
61
|
return `${marker(TASK_MARKER, metadata)}
|
|
61
62
|
## Task: ${task.id}
|
|
62
63
|
|
|
63
64
|
- **State:** ${task.state}
|
|
64
65
|
- **Priority:** ${task.priority ?? 'P2'}
|
|
66
|
+
${graph}
|
|
65
67
|
|
|
66
68
|
### Work
|
|
67
69
|
|
package/dist/store.js
CHANGED
|
@@ -5,7 +5,7 @@ export async function loadManagedGitHubPullRequestGraphql(client, repository, pr
|
|
|
5
5
|
const [owner, name] = repository.split('/', 2);
|
|
6
6
|
if (!owner || !name || !Number.isInteger(prNumber) || prNumber <= 0 || !client.graphql)
|
|
7
7
|
return [];
|
|
8
|
-
const query = 'query($owner:String!,$name:String!,$number:Int!){repository(owner:$owner,name:$name){pullRequest(number:$number){state isDraft merged mergedAt closedAt body comments(last:100){nodes{author{login} body}}}}}';
|
|
8
|
+
const query = 'query($owner:String!,$name:String!,$number:Int!){repository(owner:$owner,name:$name){pullRequest(number:$number){state isDraft merged mergedAt closedAt body comments(last:100){nodes{databaseId url author{login} body}}}}}';
|
|
9
9
|
const data = await client.graphql(query, { owner, name, number: prNumber });
|
|
10
10
|
const repo = data.repository;
|
|
11
11
|
const pr = repo?.pullRequest;
|
|
@@ -20,7 +20,14 @@ export async function loadManagedGitHubPullRequestGraphql(client, repository, pr
|
|
|
20
20
|
const bodyJob = parseJob(typeof pr.body === 'string' ? pr.body : '');
|
|
21
21
|
const commentJobs = trusted.map(item => parseJob(String(item.body ?? ''))).filter((item) => item !== null);
|
|
22
22
|
const jobs = bodyJob ? [bodyJob] : commentJobs;
|
|
23
|
-
const tasks = trusted.map(item =>
|
|
23
|
+
const tasks = trusted.map(item => {
|
|
24
|
+
const row = item;
|
|
25
|
+
const task = parseTask(String(row.body ?? ''));
|
|
26
|
+
if (!task)
|
|
27
|
+
return null;
|
|
28
|
+
const taskUrl = typeof row.url === 'string' ? row.url : Number.isInteger(row.databaseId) ? `https://github.com/${repository}/pull/${prNumber}#issuecomment-${row.databaseId}` : undefined;
|
|
29
|
+
return taskUrl ? { ...task, taskUrl } : task;
|
|
30
|
+
}).filter((item) => item !== null);
|
|
24
31
|
const state = String(pr.state ?? '').toUpperCase();
|
|
25
32
|
const githubState = pr.merged === true ? 'MERGED' : state === 'CLOSED' ? 'CLOSED' : 'OPEN';
|
|
26
33
|
return jobs.map(job => {
|