### Install Agent (Windows) Source: https://help.cftools.com/en/architect/moving-an-agent-installation Command to install or reinstall the Agent on Windows systems. This is performed after moving the Agent files to a new location and updating the configuration. It also handles tray installation. ```batch ArchitectAgent.exe --mode=install --tray-install ``` -------------------------------- ### Install Agent (Linux) Source: https://help.cftools.com/en/architect/moving-an-agent-installation Command to install or reinstall the Agent on Linux systems. This is performed after moving the Agent files to a new location and updating the configuration. ```bash ArchitectAgent --mode=install ``` -------------------------------- ### Configure Agent Directories (Configuration) Source: https://help.cftools.com/en/architect/moving-an-agent-installation Allows overriding default directory locations for Agent data. This is configured within the `config.toml` file and requires the Agent to be stopped and restarted after changes. Supported directories include database, logs, deployments, cache, certificates, and backups. ```toml # Internal folder structure # database_dir = "" # optional override for database directory, full path # log_dir = "" # optional override for agent logs directory, full path # deployments_dir = "" # optional override for deployments directory, full path # cache_dir = "" # optional override for cache directory, full path # certs_dir= "" # optional override for certs directory, full path # backups_dir= "" # optional override for backups directory, full path ``` -------------------------------- ### Configure Interactive Processes in config.toml (TOML) Source: https://help.cftools.com/en/architect/windows-interactive-processes This configuration snippet for `config.toml` allows the Architect Agent to start game server processes interactively. It requires setting `start_server_non_interactive` to `false` and specifying the `session_username` of the user under which the processes should run. The username is case-sensitive and requires the user to have logged in at least once. ```toml [windows] start_server_non_interactive = false session_username = "USERNAME" # case sensitive ``` -------------------------------- ### Default Architect Agent Configuration Source: https://help.cftools.com/en/architect/basic-configuration Provides the default TOML configuration structure for the Architect Agent. Users can modify these parameters to adjust agent behavior, including license key, security settings, logging verbosity, file system paths, network interfaces, and integration options for Steam, DayZ, and RCON. Changes require an agent restart. ```toml [authorization] # Replace with your license key license_key = "" # Option to disable "root" user access from remote networks (limits to localhost/127.0.0.1) # disable_remote_root = false # accepted values: true, false # Fail2Ban automatically blocks authentication attempts from ip addresses trying to authenticate with wrong credentials fail2ban_attempts = 5 # accepted values: numeric fail2ban_ban_time = 60 # accepted values: numeric, time in seconds [logging] # Allows you to enable console and debug logging verbose = true # accepted values: true, false # How long API audit logs are retained audit_logs_retention = 7 # accepted values: numeric, time in days [filesystem] # Insert the full path where you want to install the Agent agent_dir = "" # Limit maximum file size to be downloaded through read_file/download_file # max_manager_download_size = 32 # accepted values: numeric, size in mib # Internal folder structure # database_dir = "" # optional override for database directory, full path # log_dir = "" # optional override for agent logs directory, full path # deployments_dir = "" # optional override for deployments directory, full path # cache_dir = "" # optional override for cache directory, full path # certs_dir= "" # optional override for certs directory, full path # backups_dir= "" # optional override for backups directory, full path # Lifecycle hooks # python_path = "" # optional path to your python installation you want to use, omit for auto discovery [http_server] # Allows you to rebind Architect to a specific network interface address = "0.0.0.0" # 0.0.0.0 means that Architect will listen to all network interfaces and ip's # Port which Architect agent will listen on for Architect Manager and API connections port = 8090 # default: 8090 # Whether or not to use SSL for connections, turning this off is NOT recommended # You can exchange the SSL certificates with custom ones by replacing the self-signed ones in `certs` use_ssl = true [steam_servers] # Timeout for chunk download from Steam CDN chunk_download_timeout = 5 # accepted values: numeric, time in seconds # Allowed concurrent chunks # Higher values will increase your CPU/RAM usage # download_batch_size = 60 # accepted values: numeric # Maximum parallel downloads per Steam CDN server # The default is 8. Safe values are 8-16. Higher values will result in individual chunk downloads # failing due to rate limitations # max_downloads_per_cdn = 8 # accepted values: numeric # Option to disable caching of downloaded chunks in memory and instead write to disk as .part files # Useful for low memory machines # disable_buffered_download = false # accepted values: true, false [dayz] # Enable DZSALauncher queries dzsa_launcher_update_enable = true # accepted values: true, false # Interval for DZSALauncher queries dzsa_launcher_update = 60 # accepted values: numeric, time in seconds [rcon] # Disable RCon integration for Agent disable_rcon = false # accepted values: true, false # Optional local network ip address in case 127.0.0.1 does not resolve # rcon_ip_address = "" # accepted values: ipv4, ipv6 [windows] # Set an optional execution policy for Powershell scripts (lifecycle hooks) # powershell_execution_policy = "Bypass" # accepted values: str, a valid Windows Powershell Execution policy # Control if server should be pushed to a user session in order for the console to be displayed # start_server_non_interactive = false # accepted values: true, false # Control which session should be hijacked, falls back to non interactive if hijacked fails # When empty, uses a random active session # session_username = "" # accepted values: Windows username (Case Sensitive) ``` -------------------------------- ### Sending Commands via WebSocket API Source: https://help.cftools.com/en/architect/basics Explains the structure for sending commands as JSON payloads and how to retrieve a list of supported actions. ```APIDOC ## Sending Commands via WebSocket API ### Description Once connected, you can send commands to the Agent API using JSON formatted payloads. This includes sending specific actions and retrieving a list of all available actions. ### Method WebSocket Message (JSON Payload) ### Endpoint (Within an established WebSocket connection) ### Parameters #### Request Body (Command Structure) - **action** (string) - Required - The command to execute (e.g., `list_actions`). - **parameters** (map) - Required - A map of parameters required by the action. ### Request Example (Sending a Command) ```json { "action": "some_command", "parameters": { "param1": "value1", "param2": 123 } } ``` ### Request Example (Listing Actions) ```json { "action": "list_actions", "parameters": {} } ``` ### Response #### Success Response (Command Execution) - The response will contain the result of the executed command, including details about supported actions if `list_actions` was used. #### Response Example (List Actions) ```json { "action_results": { "list_actions": { "commands": [ { "name": "get_status", "parameters": {}, "returns": { "status": "string" } }, { "name": "set_config", "parameters": { "key": "string", "value": "string" }, "returns": {} } ] } } } ``` ``` -------------------------------- ### Connect and handle events with CFToolsArchitectAgentClient (Python) Source: https://help.cftools.com/en/architect/basics This Python snippet demonstrates how to initialize and use the CFToolsArchitectAgentClient. It sets up handlers for incoming events and successful connections, then connects to the agent. The client is kept running indefinitely until interrupted. ```python import asyncio from cftools.architect import CFToolsArchitectAgentClient async def handle_event(event: dict) -> None: print("event:", event) async def handle_connected() -> None: print('Connected.') print(await client.servers()) HOST = "your ip / hostname" PORT = 8090 USE_TLS = True USERNAME = "username" PASSWORD = "password" client = CFToolsArchitectAgentClient( host = HOST, port = PORT, use_tls = USE_TLS, username = USERNAME, password = PASSWORD, on_event=handle_event, on_connected=handle_connected ) async def main(): await client.connect() # Keep running; Ctrl-C to exit try: while True: await asyncio.sleep(3600) finally: await client.close() if __name__ == "__main__": import contextlib try: asyncio.run(main()) except KeyboardInterrupt: pass ``` -------------------------------- ### Agent API Command to List Supported Actions Source: https://help.cftools.com/en/architect/basics Illustrates the specific JSON payload to send to the Agent API to retrieve a list of all supported actions, their parameters, and data types. This is useful for discovering API capabilities. ```json { "action": "list_actions", "parameters": {} } ``` -------------------------------- ### Remove Agent Installation (Linux) Source: https://help.cftools.com/en/architect/moving-an-agent-installation Command to uninstall the Agent on Linux systems. This is a prerequisite step before moving the Agent to a new location or performing a reinstallation. ```bash ArchitectAgent --mode=remove ``` -------------------------------- ### Remove Agent Installation (Windows) Source: https://help.cftools.com/en/architect/moving-an-agent-installation Command to uninstall the Agent on Windows systems. This is a prerequisite step before moving the Agent to a new location or performing a reinstallation. It also handles tray uninstallation. ```batch ArchitectAgent.exe --mode=remove --tray-uninstall ``` -------------------------------- ### WebSocket API Connection and Authentication Source: https://help.cftools.com/en/architect/basics Details on establishing a WebSocket connection to the Agent API and authenticating using specific headers. ```APIDOC ## WebSocket API Connection and Authentication ### Description This section covers how to establish a WebSocket connection to the Architect Agent's API and authenticate your requests. ### Method WebSocket Connection ### Endpoint `ws://:` or `wss://:` ### Parameters #### Request Headers - **Sec-Websocket-Protocol** (string) - Required - Used for authentication. Format: `SMP1.0, {username}, {password}` ### Request Example ``` Sec-Websocket-Protocol: SMP1.0, myuser, mypassword ``` ### Response #### Success Response (Connection Established) - A successful WebSocket connection is established. #### Error Response (Authentication Failed) - Connection will be blocked if the authentication header is missing or invalid. ``` -------------------------------- ### Python 3: CFToolsArchitectAgentClient Class for Architect Agent Interaction Source: https://help.cftools.com/en/architect/basics This Python 3 class provides a client for interfacing with the Architect Agent programmatically. It handles WebSocket connections, sending requests, receiving responses, and dynamically creating methods for available actions. Dependencies include 'websockets', 're', 'ssl', 'json', 'uuid', 'types', 'asyncio', and 'typing'. It expects host, port, authentication credentials, and optional callbacks for events and connection status. ```python #!/usr/bin/env python3 """ This is a minimal class to interface with the Architect Agent programmatically. """ from __future__ import annotations import re import ssl import json import uuid import types import asyncio from typing import Callable, Awaitable, Any, Dict, Optional import websockets from websockets import WebSocketClientProtocol class CFToolsArchitectAgentClient: def __init__( self, host: str, port: int, *, use_tls: bool, username: str, password: str, on_event: Optional[Callable[[dict], Awaitable[None]]] = None, on_connected: Optional[Callable[[dict], Awaitable[None]]] = None, ) -> None: self._host = host self._port = port self._use_tls = use_tls self._username = username self._password = password self._on_event = on_event or (lambda _: asyncio.create_task(asyncio.sleep(0))) self._on_connected = on_connected proto = "wss" if self._use_tls else "ws" self._uri = f"{proto}://{self._host}:{self._port}/ws" self._subprotocols = ["SMP1.0", self._username, self._password] self._ws: WebSocketClientProtocol | None = None self._receiver_task: asyncio.Task[None] | None = None self._pending: Dict[str, asyncio.Future[dict]] = dict() self._actions: list = list() self._populated_actions: bool = False async def connect(self) -> None: """Establish the WebSocket and launch the passive receiver.""" ssl_ctx = ssl.create_default_context() ssl_ctx.check_hostname = False ssl_ctx.verify_mode = ssl.CERT_NONE self._ws = await websockets.connect( self._uri, ssl=ssl_ctx, subprotocols=self._subprotocols, ) print(f"Connected to {self._uri} (sub-protocol: {self._ws.subprotocol!r})") # Start the background listener self._receiver_task = asyncio.create_task(self._receiver()) async def close(self) -> None: if self._receiver_task: self._receiver_task.cancel() with contextlib.suppress(asyncio.CancelledError): await self._receiver_task if self._ws: await self._ws.close() print("Connection closed") async def send_request( self, payload: dict, *, timeout: float | None = 10.0 ) -> dict: """ Fire-and-await request/response conversation. - Adds an `idempotence` token. - Returns the first message that echoes that token. """ if not self._ws: raise RuntimeError("Not connected") token = str(uuid.uuid4()).replace('-', '') outgoing = dict(payload, idempotence=token) fut: asyncio.Future[dict] = asyncio.get_running_loop().create_future() self._pending[token] = fut await self._ws.send(json.dumps(outgoing)) try: return await asyncio.wait_for(fut, timeout=timeout) finally: # Clean up even on timeout/cancel self._pending.pop(token, None) async def _receiver(self) -> None: """Runs forever, routing every incoming frame.""" assert self._ws # no-connect guard asyncio.create_task(self._populate_actions()) try: async for raw in self._ws: try: msg = json.loads(raw) except json.JSONDecodeError: continue # ignore non-JSON frames token = msg.get("idempotence") if token and token in self._pending: # Wake the awaiting send_request() self._pending[token].set_result(msg) else: # Unsolicited event → callback asyncio.create_task(self._on_event(msg)) except websockets.ConnectionClosedOK: pass except Exception as exc: # Cancel all waiters on fatal connection error for fut in self._pending.values(): if not fut.done(): fut.set_exception(exc) raise async def _populate_actions(self) -> dict: await asyncio.sleep(1) catalogue: dict = await self.list_actions() def _make_stub(action: str, meta: dict, py_name: str): """Factory: returns a bound coroutine for an action.""" after_stub(self, **params): required = meta.get("required_parameters", []) if required is None: required = [] missing = [p for p in required if p not in params] if missing: raise TypeError( f"Missing {missing} for action '{action}'" ) payload = {"action": action, "parameters": params} return await self.send_request(payload) _stub.__name__ = py_name _stub.__doc__ = ( f"Dynamically generated wrapper for remote action '{action}'.\n" f"Remote description: {meta.get('description', 'n/a')}" ) return _stub for action_name, meta in catalogue.items(): if meta.get("cloud_controller_only"): continue py_name = action_name stub = _make_stub(action_name, meta, py_name) setattr(self, py_name, types.MethodType(stub, self)) self._actions.append(action_name) self._populated_actions = True if self._on_connected: asyncio.create_task(self._on_connected()) async def list_actions(self) -> dict: """Utility wrapper around the `list_actions` request.""" reply = await self.send_request({"action": "list_actions", "parameters": {}}) return reply["parameters"]["actions"] async def main() -> None: client: CFToolsArchitectAgentClient = None ``` -------------------------------- ### WebSocket Connection Header for Agent API Authentication Source: https://help.cftools.com/en/architect/basics This snippet shows the required header format to authenticate programmatically against an Agent's WebSocket API. Ensure you replace `{username}` and `{password}` with your actual credentials. This is crucial for any authenticated API interaction. ```text Sec-Websocket-Protocol: SMP1.0, {username}, {password} ```