from typing import Literal from pydantic import BaseModel, ConfigDict, Field class Model(BaseModel): model_config = ConfigDict(extra="forbid") class ToolResult(Model): ok: bool data: dict | None = None error: dict | None = None change: dict | None = None result: dict | None = None class WriteOptions(Model): request_id: str = Field(min_length=1, max_length=128, description="Unique operation key. Reuse when retrying a timed-out call.") if_revision: str | None = Field(default=None, description="Revision returned by get/overview; required when changing existing configuration.") dry_run: bool = False background: bool = False confirm_within_seconds: int | None = Field(default=None, ge=5, le=3600, description="Automatically roll back unless fgx_change_confirm arrives before this deadline.") class AddressSpec(Model): address_id: str | None = None ip_int: str = Field(description="IPv4/IPv6 address, CIDR or interface name such as eth0") port: int = Field(ge=1, le=65535) edge: Literal["tcp", "tls", "udp", "quic"] | None = None target_port: int | None = Field(default=None, ge=1, le=65535) upstream: Literal["same", "tcp", "tls"] | None = None proxy_ip: str | None = None proxy_port: int | None = Field(default=None, ge=1, le=65535) class ServiceSpec(Model): name: str | None = Field(default=None, min_length=1, max_length=100) proto: Literal["tcp", "udp", "tls", "quic", "http"] | None = None transport: Literal["proxy", "nfqueue", "external"] | None = None addresses: list[AddressSpec] | None = Field(default=None, min_length=1, max_length=100, description="Complete replacement address list when supplied; omitted means unchanged.") fail_open: bool | None = None max_connections: int | None = Field(default=None, ge=0) over_limit_forwards: bool | None = None first_byte_timeout: int | None = Field(default=None, ge=0) tls_cert: str | None = Field(default=None, max_length=65536) tls_key: str | None = Field(default=None, max_length=65536) class PatternSpec(Model): regex_id: str | None = None pattern: str = Field(min_length=1, max_length=65536) direction: Literal["client_to_server", "server_to_client", "both"] = "both" case_sensitive: bool = True active: bool = True class FilterSpec(Model): kind: Literal["regex", "pyfilter"] | None = None name: str | None = Field(default=None, min_length=1, max_length=100) active: bool | None = None position: int | None = Field(default=None, ge=0) patterns: list[PatternSpec] | None = Field(default=None, max_length=500, description="Complete replacement pattern list; omitted means unchanged.") code: str | None = Field(default=None, max_length=524288) functions: dict[str, bool] | None = Field(default=None, description="Patch function enabled states by Python function name.") class Sample(Model): name: str = "sample" data: str = Field(max_length=524288) encoding: Literal["utf8", "base64", "hex"] = "utf8" class FirewallRule(Model): id: str | None = Field(default=None, description="Stable MCP rule ID from fgx_firewall_get; absent for a new rule.") name: str = Field(min_length=1, max_length=100) active: bool = True proto: Literal["tcp", "udp", "both", "any"] = "tcp" table: Literal["filter", "mangle"] = "filter" mode: Literal["in", "out", "forward"] = "in" src: str = "" dst: str = "" port_src_from: int = Field(default=1, ge=1, le=65535) port_src_to: int = Field(default=65535, ge=1, le=65535) port_dst_from: int = Field(default=1, ge=1, le=65535) port_dst_to: int = Field(default=65535, ge=1, le=65535) action: Literal["accept", "drop", "reject"] = "drop" class FirewallPatch(Model): rules: list[FirewallRule] | None = Field(default=None, max_length=1000, description="Complete ordered ruleset; omitted keeps current rules.") policy: Literal["accept", "drop", "reject"] | None = None settings: dict[Literal["keep_rules", "allow_loopback", "allow_established", "allow_icmp", "multicast_dns", "allow_upnp", "drop_invalid", "allow_dhcp", "allow_dnat"], bool] | None = None