feat(permissions): expose network permission state (#103)

This commit is contained in:
2026-07-25 23:05:55 +02:00
parent e8731834f4
commit 3047dc9195
31 changed files with 2080 additions and 94 deletions

View File

@ -39,6 +39,7 @@ import { FitAddon } from "@xterm/addon-fit";
import "@xterm/xterm/css/xterm.css";
import { useGateways } from "@/app/di";
import type { ResolvedAgentSystemPermissions } from "@/domain";
import type {
OpenTerminalOptions,
ReattachResult,
@ -113,6 +114,8 @@ interface TerminalViewProps {
* it never remounts/reopens the terminal.
*/
refitSignal?: number;
/** Optional resolved system permissions for this agent/cell. */
systemPermissions?: ResolvedAgentSystemPermissions | null;
}
/**
@ -144,6 +147,7 @@ export function TerminalView({
portal,
onReady,
refitSignal,
systemPermissions,
}: TerminalViewProps) {
const { terminal } = useGateways();
const containerRef = useRef<HTMLDivElement | null>(null);
@ -414,6 +418,15 @@ export function TerminalView({
refitRef.current?.();
}, [refitSignal]);
const showNetworkBanner =
systemPermissions != null &&
(systemPermissions.runtimeLock.state === "locked" ||
systemPermissions.effective === "deny");
const networkReason =
systemPermissions?.runtimeLock.reason ??
systemPermissions?.control.reason ??
"Le réseau est interdit pour cette cellule.";
return (
<div
data-testid="terminal-view"
@ -427,6 +440,34 @@ export function TerminalView({
{/* xterm mounts into this inner node; the error banner is a sibling so
React never fights xterm over the same subtree. */}
<div ref={containerRef} style={{ width: "100%", height: "100%" }} />
{showNetworkBanner && (
<div
role="status"
data-testid="terminal-network-banner"
style={{
position: "absolute",
top: 8,
left: 8,
right: 8,
padding: "0.5rem 0.75rem",
border: "1px solid rgba(245, 158, 11, 0.45)",
borderRadius: 6,
background: "rgba(24, 24, 27, 0.94)",
color: "var(--color-warning, #f59e0b)",
fontSize: 12,
fontFamily:
'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace',
whiteSpace: "pre-wrap",
wordBreak: "break-word",
zIndex: 2,
}}
>
{systemPermissions.runtimeLock.state === "locked"
? "Réseau verrouillé par le runtime."
: "Réseau interdit pour cet agent."}{" "}
{networkReason}
</div>
)}
{openError && (
<div
role="alert"