import { html, nothing } from "lit" ;
import { formatApprovalDisplayPath } from "../../../../src/infra/approval-display-paths.ts" ;
import type { AppViewState } from "../app-view-state.ts" ;
import type {
ExecApprovalRequest,
ExecApprovalRequestPayload,
} from "../controllers/exec-approval.ts" ;
function formatRemaining(ms: number): string {
const remaining = Math.max(0 , ms);
const totalSeconds = Math.floor(remaining / 1000 );
if (totalSeconds < 60 ) {
return `${totalSeconds}s`;
}
const minutes = Math.floor(totalSeconds / 60 );
if (minutes < 60 ) {
return `${minutes}m`;
}
const hours = Math.floor(minutes / 60 );
return `${hours}h`;
}
function renderMetaRow(label: string, value?: string | null , opts?: { path?: boolean }) {
if (!value) {
return nothing;
}
const displayValue = opts?.path ? formatApprovalDisplayPath(value) : value;
return html`<div class ="exec-approval-meta-row" >
<span>${label}</span><span>${displayValue}</span>
</div>`;
}
function renderExecBody(request: ExecApprovalRequestPayload) {
return html`
<div class ="exec-approval-command mono" >${request.command}</div>
<div class ="exec-approval-meta" >
${renderMetaRow("Host" , request.host)} ${renderMetaRow("Agent" , request.agentId)}
${renderMetaRow("Session" , request.sessionKey)}
${renderMetaRow("CWD" , request.cwd, {
path: true ,
})}
${renderMetaRow("Resolved" , request.resolvedPath, { path: true })}
${renderMetaRow("Security" , request.security)} ${renderMetaRow("Ask" , request.ask)}
</div>
`;
}
function renderPluginBody(active: ExecApprovalRequest) {
return html`
${active.pluginDescription
? html`<pre class ="exec-approval-command mono" style="white-space:pre-wrap" >
${active.pluginDescription}</pre
>`
: nothing}
<div class ="exec-approval-meta" >
${renderMetaRow("Severity" , active.pluginSeverity)}
${renderMetaRow("Plugin" , active.pluginId)} ${renderMetaRow("Agent" , active.request.agentId)}
${renderMetaRow("Session" , active.request.sessionKey)}
</div>
`;
}
export function renderExecApprovalPrompt(state: AppViewState) {
const active = state.execApprovalQueue[0 ];
if (!active) {
return nothing;
}
const request = active.request;
const remainingMs = active.expiresAtMs - Date.now();
const remaining = remainingMs > 0 ? `expires in ${formatRemaining(remainingMs)}` : "expired" ;
const queueCount = state.execApprovalQueue.length;
const isPlugin = active.kind === "plugin" ;
const title = isPlugin
? (active.pluginTitle ?? "Plugin approval needed" )
: "Exec approval needed" ;
return html`
<div class ="exec-approval-overlay" role="dialog" aria-live="polite" >
<div class ="exec-approval-card" >
<div class ="exec-approval-header" >
<div>
<div class ="exec-approval-title" >${title}</div>
<div class ="exec-approval-sub" >${remaining}</div>
</div>
${queueCount > 1
? html`<div class ="exec-approval-queue" >${queueCount} pending</div>`
: nothing}
</div>
${isPlugin ? renderPluginBody(active) : renderExecBody(request)}
${state.execApprovalError
? html`<div class ="exec-approval-error" >${state.execApprovalError}</div>`
: nothing}
<div class ="exec-approval-actions" >
<button
class ="btn primary"
?disabled=${state.execApprovalBusy}
@click=${() => state.handleExecApprovalDecision("allow-once" )}
>
Allow once
</button>
<button
class ="btn"
?disabled=${state.execApprovalBusy}
@click=${() => state.handleExecApprovalDecision("allow-always" )}
>
Always allow
</button>
<button
class ="btn danger"
?disabled=${state.execApprovalBusy}
@click=${() => state.handleExecApprovalDecision("deny" )}
>
Deny
</button>
</div>
</div>
</div>
`;
}
Messung V0.5 in Prozent C=99 H=98 G=98
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-06-07)
¤
*© Formatika GbR, Deutschland