`;
}
function setGeneratedSpaceLinksEnabled(links={},enabled=false){
const space=enabled?links.target_space_url:'';
const settings=enabled?(links.target_space_settings_url||links.target_space_url&&links.target_space_url+'/settings'):'';
for(const id of ['activeOpenSpace','openSpace','launchOpenSpace'])setLink(id,space);
for(const id of ['activeOpenSettings','openSettings','manualOpenSettings','launchOpenSettings'])setLink(id,settings);
}
function setQuickLinks(links={},context={}){links=normalizeLinks(links||{});const resolvedSpaceUrl=contextTargetSpaceUrl(context,links);if(resolvedSpaceUrl&&!links.target_space_url)links.target_space_url=resolvedSpaceUrl;if(!links.target_space_settings_url&&links.target_space_url)links.target_space_settings_url=links.target_space_url+'/settings';state.activeLinks=links;for(const id of ['activeOpenJob','openJob','launchOpenJob'])setLink(id,links.job_url);const runForLinks=links.run_id||context.run_id||context.summary?.run_id||state.runId;let spaceReady=Boolean(links.target_space_url&&contextIndicatesSpaceLinksReady(context,links));if(spaceReady&&links.target_space_url)state.spaceLinksUnlockedFor=runForLinks;if(!spaceReady&&state.spaceLinksUnlockedFor&&state.spaceLinksUnlockedFor===runForLinks&&links.target_space_url)spaceReady=true;setGeneratedSpaceLinksEnabled(links,spaceReady);for(const id of ['activeOpenArtifacts','openArtifacts','launchOpenArtifacts'])setLink(id,links.artifacts_url);renderManualAction({links:{...links,target_space_url:spaceReady?links.target_space_url:'',target_space_settings_url:spaceReady?links.target_space_settings_url:''}})}
let operationTimer=null;function toastDuration(type='info'){if(type==='loading')return 12000;if(type==='error')return 7000;if(type==='warning'||type==='warn')return 6000;return 3500}
function setOperation(message,type='info',opts={}){const el=$('operationStatus');if(!el)return;if(operationTimer){clearTimeout(operationTimer);operationTimer=null}if(!message){el.hidden=true;el.textContent='';el.className='operation-status floating-toast';return}el.hidden=false;el.className='operation-status floating-toast '+type;el.textContent=message;const duration=opts.duration??toastDuration(type);if(duration>0)operationTimer=setTimeout(()=>{setOperation('')},duration)}
function showMessage(msg,isError=false){const type=isError===true?'error':(typeof isError==='string'?isError:'info');setOperation(msg,type);const el=$('eventsPanel');if(el&&!state.runId)el.textContent=(isError?'Error: ':'')+msg}
function setBusy(isBusy,label='Working…'){state.busy=Boolean(isBusy);if(!state.busy&&$('operationStatus')?.className.includes('loading'))setOperation('');document.querySelectorAll('[data-action-button]').forEach(btn=>{btn.disabled=state.busy||btn.dataset.requiresBucket==='true'&&!state.bucketReady});if($('launchBuild'))setBuildLaunchEnabled();if($('launchValidate'))setValidationLaunchEnabled();if(state.busy)setOperation(label,'loading')}
function setButtonBusy(id,isBusy,busyLabel){const btn=$(id);if(!btn)return;if(isBusy){if(!btn.classList.contains('is-busy')&&!btn.dataset.oldText)btn.dataset.oldText=btn.textContent;btn.textContent=busyLabel||'Working…';btn.disabled=true;btn.classList.add('is-busy')}else{if(btn.dataset.oldText){btn.textContent=btn.dataset.oldText;delete btn.dataset.oldText}if(id==='launchBuild')setBuildLaunchEnabled();else btn.disabled=state.busy||btn.dataset.requiresBucket==='true'&&!state.bucketReady;btn.classList.remove('is-busy')}}
function bucketFeedbackSummary(message=''){
const text=String(message||'').trim();
if(!text)return'Create your private run bucket before launching builds.';
if(/Bucket Not Found|404 Client Error|Repository not found/i.test(text))return'Bucket not found. Create it to store runs and artifacts.';
if(/permission|authenticated|token|401|403/i.test(text))return'Bucket is private or inaccessible with this token.';
if(text.length>150)return text.slice(0,147)+'…';
return text;
}
function setBucketFeedback(mode='checking',message='Checking private bucket access…'){
const orb=$('bucketInlineOrb');
const text=$('bucketFeedbackText');
if(orb)orb.className='bucket-status-orb '+mode;
if(text){
text.textContent=bucketFeedbackSummary(message);
text.title=String(message||'');
}
}
function renderAnonymousEvalStatus(me){
const card=$('evalArchiveCard');
const title=$('evalArchiveTitle');
const text=$('anonymousEvalText');
const enable=$('enableEvalArchive');
const disable=$('disableEvalArchive');
const verify=$('verifyEvalArchive');
const flush=$('flushEvalArchive');
const stateBadge=$('evalArchiveStateBadge');
const cfg=me?.anonymous_eval||{};
const enabled=Boolean(cfg.enabled);
const canManage=Boolean(cfg.can_manage)&&cfg.env_override!==true;
const readonly=Boolean(cfg.readonly)||Boolean(enabled&&!canManage);
const canFlush=Boolean(cfg.can_flush)&&enabled;
const bucket=cfg.bucket_source||cfg.proposed_bucket_source||'configured bucket';
const path=cfg.bucket_path?'/'+cfg.bucket_path:(cfg.default_bucket_path?'/'+cfg.default_bucket_path:'');
const mount=cfg.job_mount_path||cfg.default_mount_path||'/evals';
if(card){
card.hidden=false;
card.className='eval-archive-panel '+(enabled?'on':'off')+(readonly?' readonly':'')+(canManage?' manageable':'');
}
if(title){
title.textContent=enabled?'Anonymous eval archive':'Anonymous eval archive';
}
if(stateBadge){
const unavailable=String(cfg.reason||'')==='status_unavailable';
stateBadge.textContent=enabled?'On':(unavailable?'Unknown':'Off');
stateBadge.className='badge '+(enabled?'success':unavailable?'warn':'neutral');
}
if(text){
text.className='eval-feedback '+(enabled?'on':'off');
if(enabled){
const manager=cfg.managed_by==='you'?'You manage this instance setting.':`Managed by ${cfg.managed_by||'instance owner'}.`;
text.textContent=`On · ${bucket}${path} → ${mount}`;
text.title=`Anonymous project eval archive enabled from ${cfg.source||'config'}: ${bucket}${path} mounted at ${mount}. ${manager} ASF backend publishes anonymized eval records after user Jobs write local run evals.`;
}else if(String(cfg.reason||'')==='status_unavailable'){
text.textContent='Status unavailable · could not verify eval archive configuration.';
text.title='Refresh to retry the public eval archive status check. Unknown is not treated as Off.';
}else if(me?.username){
text.textContent='Mount an operator eval bucket at /evals, then enable publishing.';
text.title='Recommended: private operator bucket, bucket path evals, mounted read-write at /evals on this ASF Space. Env vars still work as an admin override.';
}else{
text.textContent='Sign in as the instance owner to enable it.';
text.title='Anonymous eval archive can be enabled by the instance owner or configured eval admins.';
}
}
if(enable){
enable.hidden=enabled||!me?.username||cfg.env_override===true||cfg.can_manage===false;
enable.disabled=!me?.username||state.busy;
enable.title=`Enable anonymous eval archive using ${cfg.proposed_bucket_source||'your namespace bucket'} mounted at ${cfg.default_mount_path||'/evals'}.`;
}
if(disable){
disable.hidden=!enabled||!canManage;
disable.disabled=state.busy;
disable.title='Disable anonymous eval publishing for this ASF instance. Existing archived eval records are not deleted.';
}
if(verify){
verify.hidden=!enabled||!canManage;
verify.disabled=state.busy;
verify.title='Refresh and verify the eval archive configuration.';
}
if(flush){
flush.hidden=!canFlush;
flush.disabled=state.busy;
flush.title='Delete all anonymized eval records from the mounted operator archive. Config is preserved.';
}
}
function setBucketReady(ok,message){state.bucketReady=Boolean(ok);setBuildLaunchEnabled();const feedback=state.bucketReady?'Ready for builds, validations and artifacts.':(message||'Create your private run bucket before launching builds.');setText('buildGateText',state.bucketReady?'Bucket ready. You can launch builds.':bucketFeedbackSummary(feedback));setText('bucketInlineStatus',state.bucketReady?'Ready':'Setup needed');setBucketFeedback(state.bucketReady?'ready':'error',feedback);for(const id of ['bucketInlineDot','sidebarBucketDot']){const dot=$(id);if(dot)dot.className='status-dot '+(state.bucketReady?'green':'amber-dot')}const inline=$('bucketInlineStatus');if(inline)inline.className='badge '+(state.bucketReady?'success':'warn');document.querySelectorAll('[data-requires-bucket="true"]').forEach(btn=>btn.disabled=state.busy||!state.bucketReady);setBuildLaunchEnabled()}
function effectivePreferredHardware(){const preferred=formValue('preferredHardware')||'zero-a10g';const fallback=formValue('fallbackHardware')||'a10g-large';const tryZero=$('tryZeroGpu')?.checked??true;if(tryZero)return preferred;if(preferred==='zero-a10g'||preferred.startsWith('zero'))return fallback;return preferred}
function hardwarePlanLabel(){const tryZero=$('tryZeroGpu')?.checked??true;const preferred=effectivePreferredHardware();const fallback=formValue('fallbackHardware')||'a10g-large';const allow=$('allowFallback')?.checked??true;if(tryZero)return `ZeroGPU → ${allow?fallback:'no fixed fallback'}`;return `${preferred}${allow&&fallback!==preferred?' → '+fallback+' fallback':''}`}
function authLifetimeFromUser(me){
const lifetime=me?.auth_lifetime||{};
const expiresAt=lifetime.expires_at||me?.expires_at||null;
let seconds=Number.isFinite(Number(lifetime.seconds_until_expiry))?Number(lifetime.seconds_until_expiry):null;
if(seconds===null&&expiresAt){
const ts=Date.parse(expiresAt);
if(Number.isFinite(ts))seconds=Math.floor((ts-Date.now())/1000);
}
let status=String(lifetime.status||'').toLowerCase();
let severity=String(lifetime.severity||'').toLowerCase();
if(!status){
if(seconds===null)status='unknown';
else if(seconds<=0)status='expired';
else if(seconds<30*60)status='critical';
else if(seconds<90*60)status='warning';
else status='ok';
}
if(!severity){
severity=status==='ok'?'success':status==='warning'?'warn':(status==='critical'||status==='expired')?'error':'neutral';
}
return {status,severity,seconds,expiresAt,label:lifetime.label||'',recommendation:lifetime.recommendation||''};
}
function formatAuthRemaining(seconds){
if(seconds===null||seconds===undefined||!Number.isFinite(Number(seconds)))return'expiry unknown';
const total=Math.max(0,Math.floor(Number(seconds)));
const h=Math.floor(total/3600),m=Math.floor((total%3600)/60);
if(h)return`${h}h ${m}m left`;
if(m)return`${m}m left`;
return`${total}s left`;
}
function formatRecommendedMinutes(minutes){
const total=Math.max(0,Math.floor(Number(minutes)||0));
if(total>=60){const h=Math.floor(total/60),m=total%60;return`${h}h${m?` ${m}m`:''}`;}
return`${total}m`;
}
function compactList(items,limit=3){
const clean=(items||[]).filter(Boolean).map(x=>String(x).trim()).filter(Boolean);
return {visible:clean.slice(0,limit),hidden:clean.slice(limit),all:clean};
}
function renderPrescanDetails(summary,sections){
const safeSections=(sections||[]).filter(s=>s&&s.items&&s.items.length);
if(!safeSections.length)return'';
return `${escapeHtml(summary||'Show details')}${safeSections.map(section=>`
`;
}
function authAwareBuildGate(scan=state.modelScan,me=state.user){
const risk=scan?.build_risk||scan?.metadata?.build_risk||null;
const auth=authLifetimeFromUser(me);
if(!me?.username)return {blocking:false,severity:'neutral',message:'Sign in with Hugging Face to launch builds.',reason:'not_signed_in'};
if(auth.status==='expired')return {blocking:true,severity:'error',message:'HF session expired. Sign in again before launching a build.',reason:'auth_expired'};
if(!risk)return {blocking:false,severity:auth.severity||'neutral',message:auth.recommendation||'',reason:'no_model_risk'};
const level=String(risk.level||'low').toLowerCase();
const requiredMinutes=Number(risk.recommended_session_minutes||0);
const isHighRisk=['high','very_high'].includes(level);
const isRisky=['medium','high','very_high'].includes(level);
if(!isRisky||!requiredMinutes)return {blocking:false,severity:auth.severity||'neutral',message:auth.recommendation||'',reason:'low_model_risk'};
const requiredText=formatRecommendedMinutes(requiredMinutes);
if(auth.seconds===null||auth.seconds===undefined||!Number.isFinite(Number(auth.seconds))){
const msg=isHighRisk?`HF session expiry is unknown. This ${level.replace('_',' ')} model recommends ${requiredText}+ remaining; refresh sign-in before launch.`:'HF session expiry is unknown; refresh sign-in before long builds if this session is old.';
return {blocking:false,severity:isHighRisk?'warn':'neutral',message:msg,reason:'auth_unknown_for_risky_model',recommended_minutes:requiredMinutes,risk_level:level};
}
const remainingMinutes=Math.floor(Number(auth.seconds)/60);
if(remainingMinutesrenderAuthLifetimeBadge(state.user),60000);
}
function clearClientAuthState(){
if(state.poll){clearInterval(state.poll);state.poll=null;}
if(state.validationPoll){clearInterval(state.validationPoll);state.validationPoll=null;}
if(state.authLifetimeTimer){clearInterval(state.authLifetimeTimer);state.authLifetimeTimer=null;}
state.user=null;
state.bucketReady=false;
state.bucketSource=null;
state.runsCache=[];
state.runId=null;
state.validationRunId=null;
state.activeLinks={};
clearSavedRun();
}
function handleLogoutClick(){
clearClientAuthState();
}
function configureAuthAnchor(anchor,href,{newTab=false}={}){
if(!anchor)return;
if(href)anchor.href=href;
if(newTab){
anchor.target='_blank';
anchor.rel='noopener noreferrer';
}else{
anchor.removeAttribute('target');
anchor.removeAttribute('rel');
}
}
function authLoginUrl(){return '/oauth/huggingface/login'}
function authRefreshLoginUrl(){return '/auth/refresh-login'}
function authResetLocalUrl(){return '/auth/logout-local'}
function authLogoutUrl(){return '/auth/logout-local'}
function normalizeModelInput(value){return String(value||'').trim().replace('https://huggingface.co/','').split('?')[0].split('#')[0].replace(/^\/+|\/+$/g,'')}
function modelScanMatchesCurrent(){return state.modelScan?.ok&&normalizeModelInput(state.modelScan.model_id)===normalizeModelInput(formValue('modelId'))}
function scanVerdictClass(verdict){verdict=String(verdict||'').toLowerCase();if(verdict==='safe')return'success';if(verdict==='caution')return'warn';if(verdict==='risky'||verdict==='unsupported')return'error';if(verdict==='scanning')return'running';return'neutral'}
function outputTypeFromModelScan(scan={}){
const value=String(scan.expected_output_type||scan.metadata?.expected_output_type||'').toLowerCase();
return ['text','image','video','audio','any'].includes(value)?value:'';
}
function syncExpectedOutputFromModelScan(scan={}){
const inferred=outputTypeFromModelScan(scan);
const select=$('buildExpectedOutput');
if(!inferred||!select)return false;
const previous=select.value;
select.value=inferred;
select.dataset.prescanOutput=inferred;
select.dataset.prescanPreviousOutput=previous||'';
select.dispatchEvent(new Event('change',{bubbles:true}));
return previous!==inferred;
}
function normalizeBuildRiskLevel(level){
const clean=String(level||'low').toLowerCase().replace(/\s+/g,'_');
if(['very_high','high','medium','low'].includes(clean))return clean;
return 'low';
}
function buildRiskFromModelScan(scan={}){return scan?.build_risk||scan?.metadata?.build_risk||null}
function kernelStrategyFromModelScan(scan={}){return scan?.kernel_strategy||scan?.metadata?.kernel_strategy||null}
function buildPreScanTechnicalSections(scan={}){
const good=(scan.good_signals||[]).filter(Boolean);
const risk=(scan.risk_signals||[]).filter(Boolean);
const recs=(scan.recommendations||[]).filter(Boolean);
const buildRisk=buildRiskFromModelScan(scan);
const kernel=kernelStrategyFromModelScan(scan);
const sections=[
{title:'Checks passed',items:good},
{title:'Scan signals',items:risk},
{title:'Recommendations',items:recs},
];
if(buildRisk){
sections.push({title:'Build hints',items:[
buildRisk.label||'',
...(buildRisk.signals||[]),
...(buildRisk.mitigations||buildRisk.recommendations||[]),
].filter(Boolean)});
}
if(kernel&&kernel.native_kernel_risk){
sections.push({title:'Runtime hints',items:[
...(kernel.signals||[]),
...(kernel.candidate_backends||[]).map(x=>`Candidate backend: ${x}`),
kernel.policy||'Prefer runtime-compatible backends before source-built native packages.',
].filter(Boolean)});
}
return sections;
}
function buildPreScanViewModel(scan=null){
const model=normalizeModelInput(formValue('modelId'));
const isTransient=scan&&scan.transient===true;
const matches=scan&&((scan.ok&&normalizeModelInput(scan.model_id)===model)||isTransient);
if(!scan||!matches){
return {
mode:'required',tone:'neutral',badgeLabel:'Required',headline:'Pre-scan required',
summary:'Run a quick metadata check before launching. Pi will use the result to prepare an autonomous build plan.',
facts:[{icon:'↗',label:'Scan type',value:'Metadata only'},{icon:'◇',label:'Weights',value:'No download'}],
automationNote:'This keeps the launch gate lightweight while the worker performs deeper runtime checks during the Job.',
actionWarnings:[],technicalSections:[],isScanning:false,ctaLabel:'Scan model',empty:true,
};
}
const verdict=String(scan.verdict||'caution').toLowerCase();
const tone=scanVerdictClass(verdict);
const output=outputTypeFromModelScan(scan);
const buildRisk=buildRiskFromModelScan(scan);
const kernel=kernelStrategyFromModelScan(scan);
const level=normalizeBuildRiskLevel(buildRisk?.level);
const minutes=Number(buildRisk?.recommended_session_minutes||Math.ceil(Number(buildRisk?.recommended_session_seconds||0)/60)||0);
const longBuild=minutes>=60||['medium','high','very_high'].includes(level);
const score=scan.score!==undefined&&scan.score!==null&&verdict!=='scanning'&&verdict!=='error'?`Metadata score ${scan.score}`:'';
const auth=authAwareBuildGate(scan,state.user);
const manualSignals=[...(scan.risk_signals||[]),...(buildRisk?.signals||[])].map(x=>String(x).toLowerCase());
const mayNeedManualGpu=manualSignals.some(x=>x.includes('manual gpu')||x.includes('manual hardware')||x.includes('hardware selection')||x.includes('48gb')||x.includes('multi-gpu'));
const badgeMap={safe:'Ready',caution:'Review',risky:'Risky',unsupported:'Unsupported',scanning:'Scanning',error:'Scan failed'};
let headline='Review before build';
let summary=scan.summary||'Model metadata scan completed.';
if(verdict==='safe'){
headline=longBuild?'Ready, longer build expected':'Ready to build';
summary=longBuild?'This model looks compatible with an autonomous Space build, but the session may take longer than usual.':'This model looks compatible with an autonomous Space build.';
}else if(verdict==='caution'){
headline=mayNeedManualGpu?'Ready, hardware may need review':'Ready, review before launch';
summary='Pi can start the build, but review the visible launch expectations before continuing.';
}else if(verdict==='risky'){
headline='Review before build';
summary='This model may need manual setup or additional validation. Confirm the risk acknowledgement before launching.';
}else if(verdict==='unsupported'){
headline='Not recommended for autonomous build';
summary='This model is likely to need manual setup before a reliable autonomous build.';
}else if(verdict==='scanning'){
headline='Scanning model metadata';
summary='Analyzing model card, Hub metadata and small config files. No model weights are downloaded.';
}else if(verdict==='error'){
headline='Pre-scan could not complete';
summary='Try the scan again, or fix the issue before launching a Job.';
}
const estimate=minutes?`${level==='low'?'Quick':level==='medium'?'Medium':level==='high'?'Long':'Very long'} · ${formatRecommendedMinutes(minutes)}+ possible`:(longBuild?'Longer session possible':'Standard session');
const facts=[];
if(output)facts.push({icon:'↳',label:'Output',value:output.charAt(0).toUpperCase()+output.slice(1)});
if(scan.pipeline_tag)facts.push({icon:'◈',label:'Pipeline',value:scan.pipeline_tag});
facts.push({icon:'⌁',label:'Build estimate',value:verdict==='scanning'?'Checking…':estimate});
if(score)facts.push({icon:'✓',label:'Pre-scan',value:score});
const actionWarnings=[];
if(mayNeedManualGpu)actionWarnings.push('May require manual GPU or hardware review.');
if(auth?.message&&(auth.reason==='auth_short_for_model_risk'||auth.reason==='auth_unknown_for_risky_model'||auth.blocking))actionWarnings.push(auth.message);
if(verdict==='unsupported')actionWarnings.push('Autonomous launch is blocked for unsupported pre-scan verdicts.');
if(verdict==='error'&&scan.error)actionWarnings.push(scan.error);
const automationNote=verdict==='scanning'
?'Pi will use this signal to prepare the worker strategy.'
:'Pi will adapt runtime settings and fallback strategies during the build.';
return {
mode:verdict,tone,badgeLabel:badgeMap[verdict]||'Review',headline,summary,
facts:facts.slice(0,4),automationNote,actionWarnings,
technicalSections:buildPreScanTechnicalSections(scan),isScanning:verdict==='scanning',
ctaLabel:verdict==='scanning'?'Scanning…':(verdict==='error'?'Try scan again':'Scan again'),
};
}
function renderPreScanFactCards(facts=[]){
if(!facts.length)return'';
return `
`;
}
function renderPreScanWarnings(items=[]){
const clean=(items||[]).filter(Boolean);
if(!clean.length)return'';
return `
${clean.slice(0,3).map(item=>`
!${escapeHtml(item)}
`).join('')}
`;
}
function renderPreScanTechnicalDetails(sections=[]){
// Legacy labels intentionally remain searchable in this function: Show build-risk details, Show native-kernel details, Show scan signals.
return renderPrescanDetails('Show technical scan details',sections);
}
function renderPreScanSkeleton(){
return '
';
}
if(ackWrap)ackWrap.hidden=!(String(scan?.verdict||'').toLowerCase()==='risky');
const scanButton=$('scanModel');
if(scanButton&&!scanButton.disabled)scanButton.textContent=vm.ctaLabel||'Scan model';
renderAuthLifetimeBadge(state.user);
}
function resetModelPreScan(){state.modelScan=null;const ack=$('modelRiskAck');if(ack)ack.checked=false;renderModelPreScan(null);setBuildLaunchEnabled()}
function setBuildLaunchEnabled(){const btn=$('launchBuild');const err=validateBuildForm();const gate=authAwareBuildGate(state.modelScan,state.user);if(btn){btn.disabled=state.busy||Boolean(err);btn.title=err||((gate.reason==='auth_short_for_model_risk'||gate.reason==='auth_unknown_for_risky_model')?gate.message:'');}if($('buildGateText'))setText('buildGateText',err||((gate.reason==='auth_short_for_model_risk'||gate.reason==='auth_unknown_for_risky_model')?gate.message:'Bucket and model pre-scan ready. You can launch builds.'))}
async function scanModel(){
const model=formValue('modelId');
if(!model){showMessage('Enter a model card URL or model ID before scanning.',true);return}
setButtonBusy('scanModel',true,'Scanning…');
renderModelPreScan({ok:true,transient:true,model_id:model,verdict:'scanning',summary:'Reading the model card, Hub metadata and small config files. We do not download model weights.'});
try{
const result=await apiPost('/api/models/pre-scan',{model_id:model});
state.modelScan=result;
syncExpectedOutputFromModelScan(result);
const ack=$('modelRiskAck');if(ack)ack.checked=false;
renderModelPreScan(result);
setBuildLaunchEnabled();
showMessage(`Model pre-scan: ${result.verdict} (${result.score}/100)`,result.verdict==='safe'?'success':(result.verdict==='unsupported'||result.verdict==='risky'?true:'warning'));
}catch(e){state.modelScan=null;renderModelPreScan({ok:true,transient:true,model_id:model,verdict:'error',summary:'The model pre-scan could not complete. Fix the issue or try again before launching a Job.',error:e.message});showMessage(e.message,true)}
finally{setButtonBusy('scanModel',false);setBuildLaunchEnabled()}
}
function validateBuildForm(){if(!state.bucketReady)return'Create or check your private run bucket before launching a build.';if(!formValue('modelId'))return'Enter a model card URL or model ID.';if(!formValue('piModel'))return'Enter the Pi model to use.';if(!modelScanMatchesCurrent())return'Run the model pre-scan before launching this build.';const authGate=authAwareBuildGate(state.modelScan,state.user);if(authGate.blocking)return authGate.message||'Refresh HF sign-in before launching.';const verdict=String(state.modelScan?.verdict||'').toLowerCase();if(verdict==='unsupported')return'This model pre-scan marked the repo as unsupported for automatic build.';if(verdict==='risky'&&!$('modelRiskAck')?.checked)return'Review the model pre-scan risk signals and confirm before launching.';return''}
function parseJsonField(id,fallback){const raw=formValue(id);if(!raw)return fallback;return JSON.parse(raw)}
function prettyJson(value){return JSON.stringify(value,null,2)}
function validationTimeoutValue(){const raw=formValue('validateTimeout');if(!raw)return 60;const value=Number(raw);if(!Number.isFinite(value))return NaN;return Math.max(30,Math.min(3600,Math.round(value)))}
function validationPayloadSizeError(){const enc=typeof TextEncoder!=='undefined'?new TextEncoder():null;for(const [id,label] of [['testArgs','Test args'],['testKwargs','Test kwargs']]){const text=$(id)?.value||'';const bytes=enc?enc.encode(text).length:text.length;if(bytes>16000)return`${label} JSON is too large for this version (${bytes} bytes). Keep validation payloads under 16 KB and avoid pasted base64/files.`}return''}
function validateValidationForm(){
const resolved=resolveLinkedValidationPayload();
return resolved.launchable?'':(resolved.error||'Space Test is not ready to launch.');
}
function setValidationLaunchEnabled(){const btn=$('launchValidate');if(!btn)return;const running=state.validationMode==='running'||Boolean(state.validationRunId&&state.validationDetail&&validationCanonicalStatus(state.validationDetail)==='running');const error=validateValidationForm();btn.disabled=Boolean(state.busy||running||error);btn.title=error||'';renderSpaceTestReadiness();}
function validationIsTerminalStatus(status){const canonical=String(status||'').toLowerCase();return ['full_inference_success','partial_validation','manual_hardware_required','technical_blocker','technical_blocker_boot_only','failed','stale','stopped','auth_refresh_required'].includes(canonical)}
function validationIsNonTerminalStatus(status){const canonical=String(status||'').toLowerCase();return !canonical||['running','pending','waiting','started','queued','building','unknown'].includes(canonical)}
function validationDetailRunId(detail={}){return detail.run_id||detail.summary?.run_id||detail.state?.run_id||detail.launch?.run_id||''}
function rememberTerminalValidation(detail={}){const runId=validationDetailRunId(detail)||state.validationRunId;const canonical=validationCanonicalStatus(detail);if(runId&&validationIsTerminalStatus(canonical)){state.validationTerminalByRun=state.validationTerminalByRun||{};state.validationTerminalByRun[runId]={canonical,detail:{...detail,_terminalLocked:true}};}return canonical}
function terminalValidationForRun(runId=state.validationRunId){return runId&&state.validationTerminalByRun?state.validationTerminalByRun[runId]||null:null}
function mergeValidationWithTerminalLock(incoming={},runId=state.validationRunId){const terminal=terminalValidationForRun(runId);if(!terminal)return incoming;const incomingCanonical=validationCanonicalStatus(incoming);if(validationIsNonTerminalStatus(incomingCanonical)){return {...(terminal.detail||{}),_refreshing:true,_ignoredIncomingStatus:incomingCanonical};}return incoming;}
function shouldIgnoreValidationUpdate(incoming={},runId=state.validationRunId){const incomingCanonical=validationCanonicalStatus(incoming);const terminal=terminalValidationForRun(runId);if(terminal&&incomingCanonical==='running')return true;return Boolean(terminal&&validationIsNonTerminalStatus(incomingCanonical));}
function extractEffectiveValidationPayload(detail={}){const smoke=detail.generation_smoke||detail.summary?.generation_smoke||detail.state?.generation_smoke||{};const payload=detail.validation_payload||detail.tests?.validation_payload||detail.summary?.validation_payload||{};const args=firstArrayPath(detail.effective_args,smoke.effective_args,payload.effective_args,smoke.test_args,payload.test_args);const kwargs=firstObjectPath(detail.effective_kwargs,smoke.effective_kwargs,payload.effective_kwargs,smoke.test_kwargs,payload.test_kwargs);const source=detail.args_source||smoke.args_source||payload.args_source||'';return {args,kwargs,source,argsWereAutofilled:Boolean(detail.args_were_autofilled||smoke.args_were_autofilled||payload.args_were_autofilled||source==='gradio_schema_auto_fill')};}
function applyEffectiveValidationArgs(detail={}){const payload=extractEffectiveValidationPayload(detail);if(!payload.argsWereAutofilled||!Array.isArray(payload.args))return;if(state.validationArgsDirty)return;setInputValue('testArgs',prettyJson(payload.args));if(payload.kwargs&&typeof payload.kwargs==='object'&&!Array.isArray(payload.kwargs))setInputValue('testKwargs',prettyJson(payload.kwargs));const note=$('spaceTestArgsNote');if(note){note.hidden=false;note.textContent='Arguments filled from discovered Gradio schema.';} }
function setBuildModeNew(){const panel=$('buildModePanel');if(panel){panel.classList.remove('inspecting');panel.innerHTML='Preparing a new build
This form launches new builds. Existing runs can be inspected in the Runs Explorer on the right.
'}const launch=$('launchResultPanel');if(launch)launch.hidden=true;const status=$('progressStatus');if(status){status.textContent='Idle';status.className='badge neutral'}setText('progressTitle','No run selected')}
function setBuildModeInspecting(runId){const panel=$('buildModePanel');if(panel){panel.classList.add('inspecting');panel.innerHTML=`Inspecting selected run
You are viewing run ${escapeHtml(runId||'')}. The build form is unchanged and the active run panel is now tracking it.
`}}
function renderActiveRunEmptyState(){
const timeline=$('timeline');
if(timeline){timeline.dataset.signature='empty';timeline.className='timeline empty-timeline';timeline.innerHTML='
No active run selectedLaunch a build or select a run from Runs Explorer. The timeline will appear once a run starts.
';if(timeline.style?.removeProperty)timeline.style.removeProperty('--timeline-phase-count');}
const note=$('timelineTraceNote');if(note){note.hidden=true;note.innerHTML='';note.className='timeline-trace-note';}
const details=$('timelinePhaseDetails');if(details){details.hidden=true;details.innerHTML='';details.className='timeline-phase-details';}
}
function resetRunDetails(){
const cancel=$('cancelRun');if(cancel)cancel.disabled=true;
if(typeof elapsedClock!=='undefined')elapsedClock.isRunning=false;if(typeof resetProgressStability==='function')resetProgressStability(null);
if(typeof activityFeedState!=='undefined'){activityFeedState.lastItems=[];activityFeedState.runId=null}
for(const [id,value] of [
['detailRunId','—'],['detailJobStatus','—'],['detailGateStatus','—'],['detailSpace','—'],['detailHardware','—'],['detailTargetGpu','—'],['detailFallbackGpu','—'],['detailExpectedOutput','—'],['detailPiAssistant','—'],['detailTraces','—'],
['homeRunDetailStatus','No run'],['selectedModelId','—'],['progressTitle','No run selected'],['progressStatus','Idle'],
['currentStep','—'],['lastEvent','Select a run from the explorer or launch a new build.'],['elapsed','—'],['lastPolled','—'],
['progressPercent','0%']
])setText(id,value);
setText('activeLatencyValue','—');setText('activeZeroGpuDuration','—');setText('activeLatencySource','waiting for live smoke test');const latencyCard=$('activeLatencyCard');if(latencyCard){latencyCard.classList.remove('has-latency');latencyCard.classList.add('is-loading')}
const status=$('progressStatus');if(status)status.className='badge neutral';
const fill=$('progressFill');if(fill){fill.style.width='0%';fill.classList.remove('is-running','progress-bump','is-success','is-warn','is-error','is-running-state','is-stopped','is-neutral')}const cap=$('progressCaptionLabel');if(cap)cap.textContent='Overall progress';const sem=$('progressSemantics');if(sem){sem.hidden=true;sem.innerHTML=''}const bar=$('progressBar');if(bar)bar.setAttribute('aria-valuenow','0')
setQuickLinks({});
const full=$('reportPreview');if(full)full.textContent='No report selected.';
const manual=$('manualActionPanel');if(manual)manual.hidden=true;
const activity=$('activityFeed');if(activity){activity.dataset.signature='';activity.classList.remove('show-all');activity.innerHTML='
Run documents will appear as files are written to the bucket.
';
const blockers=$('selectedBlockers');if(blockers){blockers.hidden=true;blockers.innerHTML=''}
if(typeof clearEvalArchiveStatus==='function')clearEvalArchiveStatus('active-run-reset');
for(const id of ['deleteActiveRun','activePrefillSpaceTest','deleteValidationRun']){const btn=$(id);if(btn)btn.disabled=true}
// legacy anchor: renderTimeline(DEFAULT_TIMELINE,'idle')
renderActiveRunEmptyState();
} function prepareNewBuild(){if(state.poll){clearInterval(state.poll);state.poll=null}state.runId=null;state.activeRunProgress=null;state.selectedRunDetail=null;state.documentCache={};state.latencyCache={};state.activeLinks={};state.pollErrors=0;clearSavedRun();document.querySelectorAll('.run-row').forEach(r=>r.classList.remove('selected'));setBuildModeNew();resetRunDetails();setText('progressPercent','0%');const cap=$('progressCaptionLabel');if(cap)cap.textContent='Overall progress';const sem=$('progressSemantics');if(sem){sem.hidden=true;sem.innerHTML=''}const fill=$('progressFill');if(fill)fill.style.width='0%';setText('currentStep','Ready');setText('lastEvent','Choose a model card and launch a build.');setText('elapsed','—');if(typeof elapsedClock!=='undefined')elapsedClock.isRunning=false;setText('lastPolled','—');setText('hardwarePlan',hardwarePlanLabel());renderMetrics({});showMessage('New build mode ready.','info')}
function renderQuietLaunchState(result={}){setText('progressTitle','Starting build Job…');setText('progressStatus','Running');const statusEl=$('progressStatus');if(statusEl)statusEl.className='badge '+statusClass('running');setText('progressPercent','1%');const fill=$('progressFill');if(fill)fill.style.width='1%';setText('lastEvent','Build Job launched; waiting for the first canonical run snapshot.');setText('elapsed','—');setText('lastPolled','—');const timeline=$('timeline');if(timeline){timeline.innerHTML='
Waiting for first run snapshot…
';timeline.classList.remove('timeline-canonical');timeline.style.gridTemplateColumns=''}const note=$('timelineTraceNote');if(note){note.hidden=true;note.innerHTML=''}const details=$('timelinePhaseDetails');if(details){details.hidden=true;details.innerHTML='';details.className='timeline-phase-details'}}
function resetSpaceTestForNewBuild(runId=''){
if(typeof stopValidationPolling==='function')stopValidationPolling();
state.validationRequestSeq=(state.validationRequestSeq||0)+1;
state.validationPollToken=null;
state.validationRunId=null;
state.validationDetail=null;
state.validationMode='idle';
state.validationPollErrors=0;
state.validationPrefillDraft=null;
state.linkedValidationParent=null;
state.validationArgsDirty=false;
const note=$('spaceTestArgsNote');if(note)note.hidden=true;
const readiness=$('spaceTestLaunchReadiness');if(readiness){readiness.hidden=false;readiness.className='validation-args-note launch-readiness blocked';readiness.textContent='Space Test will be available after this build reaches a final Space state.';}
setInputValue('validateSpace','');
setInputValue('validateApi','');
setInputValue('testArgs',prettyJson(defaultValidationArgs(formValue('buildExpectedOutput')||'image')));
setInputValue('testKwargs','{}');
setText('spaceTestEndpoint','—');
setText('spaceTestOutput','Not launched');
setText('validationStartedAt','Not launched');
setText('spaceTestLatency','—');
setText('spaceTestVerdict',runId?`Waiting for Build Run ${runId} to finish.`:'Waiting for a Build Run to finish.');
const badge=$('spaceTestStatus');if(badge){badge.textContent='Blocked';badge.className='badge neutral';}
const tabDot=$('spaceTestTabDot');if(tabDot)tabDot.className='tab-status-dot neutral';
const timeline=$('spaceTestTimeline');if(timeline){timeline.hidden=true;timeline.innerHTML='';}
setValidationProgressVisible(false);
const deleteBtn=$('deleteValidationRun');if(deleteBtn)deleteBtn.disabled=true;
const launchBtn=$('launchValidate');if(launchBtn){launchBtn.textContent='Run linked validation';launchBtn.disabled=true;launchBtn.title='Available after the build reaches a final Space state.';}
const endpoints=$('spaceTestEndpoints');if(endpoints){endpoints.innerHTML='
Space TestWaiting for current Build Run to finish
';}
}
function clearValidationRunStateForBuildContext(parentRunId='',reason='build_selection'){
if(typeof stopValidationPolling==='function')stopValidationPolling();
state.validationRequestSeq=(state.validationRequestSeq||0)+1;
state.validationPollToken=null;
state.validationRunId=null;
state.validationDetail=null;
state.validationMode='idle';
state.validationPollErrors=0;
state.validationArgsDirty=false;
state.validationPrefillDraft=null;
state.linkedValidationParent=parentRunId||null;
const note=$('spaceTestArgsNote');if(note)note.hidden=true;
setText('spaceTestOutput','Not launched');
setText('validationStartedAt','Not launched');
setText('spaceTestLatency','—');
setText('spaceTestVerdict',parentRunId?`Linked to Build Run ${parentRunId}. No validation run launched yet.`:'Select a Build Run to prepare Space Test.');
setText('spaceTestEndpoint','—');
const timeline=$('spaceTestTimeline');if(timeline){timeline.hidden=true;timeline.innerHTML='';timeline.dataset.parentRunId=parentRunId||'';}
setValidationProgressVisible(false);
const deleteBtn=$('deleteValidationRun');if(deleteBtn){deleteBtn.disabled=true;deleteBtn.onclick=null;}
const tabDot=$('spaceTestTabDot');if(tabDot){tabDot.className='tab-status-dot neutral';tabDot.title='No active linked validation';}
const badge=$('spaceTestStatus');if(badge){badge.textContent='Preparing';badge.className='badge neutral';}
const card=$('spaceTestPreview');if(card){card.classList.remove('validation-running','validation-success','validation-error');card.classList.add('space-test-context-only');}
const readiness=$('spaceTestLaunchReadiness');if(readiness){readiness.hidden=false;readiness.className='validation-args-note launch-readiness blocked';readiness.textContent='Preparing Space Test context for the selected Build Run.';}
return true;
}
function requestSpaceTestContextResetForBuild(runId){
state.spaceTestContextResetForRun=runId||true;
}
function setActiveRun(result){if(state.poll){clearInterval(state.poll);state.poll=null}resetSpaceTestForNewBuild(result?.run_id||'');resetRunDetails();state.documentCache={};state.latencyCache={};state.runId=result.run_id;state.spaceLinksUnlockedFor=null;if(Array.isArray(state.runsCache)&&typeof sortRunsNewestFirst==='function'){state.runsCache=sortRunsNewestFirst([{...result,run_id:result.run_id,status:result.status||'running',created_at:result.created_at||new Date().toISOString()},...state.runsCache.filter(r=>r.run_id!==result.run_id)]);renderRunsFromCache();if(typeof renderRunStats==='function')renderRunStats();}if(result.bucket_source)state.bucketSource=result.bucket_source;setText('detailRunId',result.run_id);setText('detailJobStatus',result.status||'RUNNING');setText('detailGateStatus',result.kind||'running');setText('detailSpace',result.target_space||'—');setText('detailHardware',result.selected_hardware||result.preferred_space_hardware||'—');renderActiveRunMeta({summary:result,state:result,launch:result});renderQuietLaunchState(result);setText('homeRunDetailStatus',result.status||'Running');setBuildModeInspecting(result.run_id);setQuickLinks({...result.links,bucket_source:result.bucket_source,run_id:result.run_id,job_id:result.job_id,job_owner:state.user?.username,target_space_url:result.target_space_url},result);saveActiveRun();markPendingActiveFields();startPolling(result.run_id)}
function startPolling(runId,options={}){
if(!runId)return;
const sameRun=state.runId===runId;
if(sameRun&&state.poll)return;
if(!sameRun){
state.spaceLinksUnlockedFor=null;
const blockers=$('selectedBlockers');if(blockers){blockers.hidden=true;blockers.innerHTML=''}
}
state.runId=runId;
state.pollErrors=0;
state.lastJobLogPoll=0;
if(typeof resetProgressStability==='function')resetProgressStability(runId);
if(state.poll)clearInterval(state.poll);
saveActiveRun();
if(!options.deferFirstTick)refreshProgress();
else setTimeout(()=>{if(state.runId===runId)refreshProgress()},80);
state.poll=setInterval(refreshProgress,1500);
}
async function bootstrapRunRecovery(){return restoreActiveRun()}
async function restoreActiveRun(){try{const resumable=await apiGet(`/api/runs/resumable?bucket_name=${encodeURIComponent(state.bucketName)}`);if(resumable?.run?.run_id){state.runId=resumable.run.run_id;if(resumable.bucket_source)state.bucketSource=resumable.bucket_source;setBuildModeInspecting(state.runId);if(resumable.view)renderRunViewModel(resumable.view,{progress:0,events:[]});setQuickLinks(resumable.view?.links||{},resumable.view||{});startPolling(state.runId);return true}}catch(e){console.warn('resumable lookup failed',e)}try{const raw=localStorage.getItem('asf.activeRun');if(!raw)return false;const saved=JSON.parse(raw);if(saved?.bucketName)state.bucketName=saved.bucketName;if(saved?.runId){state.runId=saved.runId;setText('detailRunId',saved.runId);setQuickLinks(saved.links||{},{});setBuildModeInspecting(saved.runId);startPolling(saved.runId);return true}}catch(_){}return false}
async function refreshAnonymousEvalStatus(){
try{
const anonymous_eval=await apiGet('/api/eval-archive/status');
renderAnonymousEvalStatus({anonymous_eval});
return anonymous_eval;
}catch(e){
renderAnonymousEvalStatus({anonymous_eval:{enabled:false,reason:'status_unavailable',managed_by:'instance owner'}});
return null;
}
}
function isAuthExpiredError(error){return /expired|oauth_expired|session expired/i.test(String(error?.message||error||''))}
async function loadAppInfo(){setOperation('Checking sign-in and run storage…','loading');setBucketFeedback('checking','Checking private bucket access…');try{const me=await apiGet('/api/me');state.user=me;startAuthLifetimeTicker();renderAnonymousEvalStatus(me);setText('userChip','@'+me.username);configureAuthAnchor($('userChip'),`https://huggingface.co/${me.username}`);const logout=$('logoutLink');if(logout){logout.hidden=false;logout.textContent='Reset auth';configureAuthAnchor(logout,me.logout_url||authLogoutUrl(),{newTab:false})}state.bucketSource=me.username+'/'+state.bucketName;setText('bucketInlineLabel',state.bucketSource);renderAuthWarnings(me);renderBillingPanel(me);loadBillingStatus().catch(()=>{});await checkBucket(false);await loadRuns(true);clearSavedRun();if(!state.bucketReady)setOperation('Create your private run bucket before launching builds.','warning');else setOperation('')}catch(e){const expired=isAuthExpiredError(e);clearClientAuthState();await refreshAnonymousEvalStatus();setText('userChip',expired?'Refresh sign-in':'Sign in');configureAuthAnchor($('userChip'),expired?authRefreshLoginUrl():authLoginUrl(),{newTab:expired});const logout=$('logoutLink');if(logout){logout.hidden=false;logout.textContent='Reset auth';configureAuthAnchor(logout,authResetLocalUrl(),{newTab:false})}renderSignedOut({expired});renderBillingPanel(null);setBucketReady(false,expired?'HF OAuth expired. Refresh sign-in before continuing.':'Sign in with Hugging Face first.');showMessage(expired?'HF OAuth expired. Use Refresh sign-in to reconnect cleanly.':'Please sign in with Hugging Face to continue.',true)}}
function renderSignedOut({expired=false}={}){const auth=$('authPanel');if(!auth)return;auth.hidden=false;auth.className='auth-panel warning';if(expired){auth.innerHTML=`HF Auth expired
Your Hugging Face OAuth session expired or is stale. Refresh sign-in clears the local OAuth session first, then starts a fresh authorization. Use Reset auth if the browser is still stuck.
Use Hugging Face OAuth before launching Jobs or reading your Bucket.
Sign in with Hugging FaceReset local auth`}
function renderAuthWarnings(me){const auth=$('authPanel');if(!auth)return;const missing=me.missing_scopes||[];const warnings=[...(me.warnings||[])];const lifetime=authLifetimeFromUser(me);if(['warning','critical','expired'].includes(lifetime.status))warnings.push(lifetime.recommendation||authLifetimeLabel(lifetime));if(!missing.length&&!warnings.length){auth.hidden=true;auth.innerHTML='';return}auth.hidden=false;auth.className='auth-panel warning';const refresh=String(lifetime.status||'')==='expired'?authRefreshLoginUrl():authRefreshLoginUrl();auth.innerHTML=`OAuth session warning
`}
function renderEvents(events){const root=$('eventsPanel');if(!root)return;try{const rows=Array.isArray(events)?events:[];root.textContent=rows.slice(-30).map(e=>`${e?.ts||e?.time||''} ${e?.step||e?.phase||''} ${e?.status||''} ${e?.message||e?.title||''}`).join('\n')||'No events yet. The run may still be scheduling.'}catch(e){console.warn('renderEvents failed',e);root.textContent='Events could not be rendered.'}}
async function cancelActiveRun(){
if(!state.runId){showMessage('Select a running run before cancelling.', 'warning');return}
if(!confirm(`Cancel Job for run ${state.runId}?`))return;
setButtonBusy('cancelRun',true,'Cancelling…');
try{
const result=await apiPost(`/api/runs/${encodeURIComponent(state.runId)}/cancel?bucket_name=${encodeURIComponent(state.bucketName)}`,{});
showMessage(`Cancelled Job ${result.job_id||''}`,'success');
if(state.poll){clearInterval(state.poll);state.poll=null}
await refreshProgress();
await loadRuns(true);
}catch(e){
showMessage(e.message,true);
}finally{
setButtonBusy('cancelRun',false);
}
}
function switchView(view){state.currentView=view;document.querySelectorAll('.nav-item').forEach(b=>b.classList.toggle('active',b.dataset.view===view));document.querySelectorAll('[data-section]').forEach(s=>s.hidden=s.dataset.section!==view);setOperation('');if(view==='progress')loadRuns(false);if(view==='validate')switchCenterTab('validate');if(view==='active')switchCenterTab('active');const targetMap={build:'newBuildCard',progress:'runsExplorerPanel',validate:'spaceTestSection',active:'activeRunPanel'};const target=$(targetMap[view]||'');if(target)target.scrollIntoView({behavior:'smooth',block:'start'})}
function bindNavigation(){
bindAllOnce('.nav-item','click','navItem',e=>switchView(e.currentTarget.dataset.view||'build'));
bindAllOnce('[data-view-target]','click','viewTarget',e=>switchView(e.currentTarget.dataset.viewTarget||'build'));
bindAllOnce('[data-center-tab]','click','centerTab',e=>switchCenterTab(e.currentTarget.dataset.centerTab||'active',{source:'user'}));
}
function bindRunFilters(){
bindAllOnce('.filter[data-status]','click','runStatusFilter',e=>{
state.runStatusFilter=e.currentTarget.dataset.status||'all';
state.runPage=0;
renderRunsFromCache();
});
for(const id of ['runSearch','runSearchFull'])bindOnce($(id),'input',`runSearch${id}`,e=>{
state.runSearch=e.target.value||'';
state.runPage=0;
for(const other of ['runSearch','runSearchFull'])if($(other)&&$(other)!==e.target)$(other).value=state.runSearch;
clearTimeout(state.runSearchTimer);
state.runSearchTimer=setTimeout(renderRunsFromCache,80);
});
}
function bindValidationDraftInputs(){
for(const id of ['testArgs','testKwargs'])bindOnce($(id),'input',`validationArgsDirty${id}`,()=>{state.validationArgsDirty=true;const note=$('spaceTestArgsNote');if(note)note.hidden=true;setValidationLaunchEnabled();});
for(const id of ['validateSpace','validateApi','expectedOutput','validateTimeout'])bindOnce($(id),'input',`validationForm${id}`,setValidationLaunchEnabled);
}
function bindActions(){
$('launchBuild')?.setAttribute('data-requires-bucket','true');
$('launchBuild')?.setAttribute('data-action-button','true');
$('launchValidate')?.setAttribute('data-action-button','true');
bindOnce($('logoutLink'),'click','logoutClientState',handleLogoutClick);
const actions={
launchBuild:launchBuild,
scanModel:scanModel,
launchValidate:launchValidate,
prepareNewBuild:prepareNewBuild,
manualGoValidate:prepareValidationFromActiveRun,
activePrefillSpaceTest:prepareValidationFromActiveRun,
checkBucket:()=>checkBucket(true),
createBucket:createBucket,
enableEvalArchive:enableEvalArchive,
verifyEvalArchive:verifyEvalArchive,
disableEvalArchive:disableEvalArchive,
flushEvalArchive:flushEvalArchive,
refreshRuns:()=>loadRuns(true),
refreshRunsHome:()=>loadRuns(true),
cancelRun:cancelActiveRun,
};
Object.entries(actions).forEach(([id,handler])=>bindOnce($(id),'click',`action${id}`,handler));
bindOnce($('modelId'),'input','modelPreScanReset',resetModelPreScan);
bindOnce($('modelRiskAck'),'change','modelRiskAck',setBuildLaunchEnabled);
['tryZeroGpu','preferredHardware','fallbackHardware','allowFallback'].forEach(id=>bindOnce($(id),'change',`hardware${id}`,()=>setText('hardwarePlan',hardwarePlanLabel())));
bindOnce($('implementationMode'),'change','implementationModeHelp',updateImplementationModeHelp);
}
bindNavigation();bindActions();bindRunFilters();bindValidationDraftInputs();setText('hardwarePlan',hardwarePlanLabel());updateImplementationModeHelp();setBuildModeNew();renderActiveRunEmptyState();resetValidationRunView();loadAppInfo();
// Pass 47 intentionally removed automatic switchView('progress') after launch and run selection.
// Compatibility note for v108 regression: implementationMode')?.addEventListener('change',updateImplementationModeHelp is now bound through bindOnce().
// Live quotas, spend and compute usage remain in the HF Billing dashboard; Open HF Billing for numeric values.
// Legacy endpoint picker wording kept for tests: API name used for validation / Set API name.
// Legacy status copy kept for regression tests only, not displayed:
// 'Eval archive off · set env vars'
// Anonymous project eval archive enabled:
// Env override remains supported for backend publishing: ASF_EVAL_ENABLED=true, ASF_EVAL_BUCKET_SOURCE=/, ASF_EVAL_BUCKET_PATH=evals, ASF_EVAL_BUCKET_MOUNT=/evals.
/* v190.20 legacy invariant strings:
setText('spaceTestOutput',prefill.automaticSmokePassed?'Replay linked smoke test':'New linked validation')
parent_build_run_id:(state.validationPrefillDraft||{}).parentBuildRunId
*/
// v190.3 compatibility anchor: setText('spaceTestOutput',prefill.automaticSmokePassed?'Replay linked smoke test':'New linked validation') · Linked to Build Run
// v190.17 compatibility anchor: Linked to selected Build Run
// v190.24 compatibility anchors retained for release regression tests:
// Open a completed Build Run and use Prefill Space Test
// draft.requiresEndpointDiscovery?'':normalizeApiName
// live_timeout_seconds:validationTimeoutValue()
// v71 compatibility anchor: setText('detailJobStatus',p.status||p.state?.status
// v71 compatibility anchor: setText('homeRunDetailStatus',badgeLabel(p.status
// v186/v191 compatibility anchors for regression tests after v192.2 compacting:
// goodPreview=(verdict!=='safe'
// recommended session
// Refresh sign-in before launching
// v192.12 terminal reconciliation compatibility anchors:
// setTimeout(()=>refreshRunReportPreview(state.runId),120)
// refreshRunReportPreview(state.runId)
// v194 legacy regression anchors for older compact pre-scan tests:
// prescan-spinner prescan-decision-line allGood.length No risk signals Output',outputType prescan-meta Library