import { realExcluded } from "./error-map.mjs"; export const LIMITS = Object.freeze({ max_command_bytes: 65536, max_snapshot_bytes: 1048576, max_tick_step: 1000000, max_event_records: 256, max_workspace_path_bytes: 1024, max_workspace_file_bytes: 64 * 1024 * 1024, }); export const COMMANDS = Object.freeze([ "SESSION_ATTACH", "LOAD_WORKSPACE", "COMMAND", "SIM_STEP", "SNAPSHOT_REQUEST", "PROGRAM_LOAD", "PROGRAM_RUN", "PROGRAM_PAUSE", "PROGRAM_RESUME", "PROGRAM_ABORT", "PROGRAM_RESTART", "PROGRAM_STEP", "PROGRAM_SEEK", "MDI_EXECUTE", "CHECKPOINT", "RECOVER", "DESTROY", "POWER", "RESET", "SET_MODE", "SET_OVERRIDE", "JOG_START", "JOG_STOP", "HOME", "ESTOP", "IO_RECOVER", "LIMIT_SET", "FOLLOWING_ERROR", "SPINDLE_COMMAND", "SPINDLE_FAULT", "SPINDLE_INHIBIT", "SPINDLE_ORIENT", "SPINDLE_ORIENTED", "TOOL_PREPARE", "TOOL_PREPARED", "TOOL_CHANGE", "TOOL_CHANGED", "TOOL_ABORT", "TOOL_FAULT", "TOOL_CLEAR_FAULT", "PROBE_CONFIG", "ENCODER_SET", "COOLANT_COMMAND", "COOLANT_FAULT", "DOOR_SET", "INTERLOCK_SET", "INTERLOCK_RESET", "DEVICE_ESTOP", "DEVICE_ACK_ISSUE", "DEVICE_ACK", "DEVICE_ACK_ABORT", "CONFIGURATION_BATCH", ]); export const EXCLUSIONS = Object.freeze([ realExcluded("real_hardware", "browser_has_no_direct_machine_control", "digital_twin"), realExcluded("linux_realtime_kernel", "wasm_worker_uses_logical_ticks", "fixed_tick_scheduler"), realExcluded("host_device_access", "browser_sandbox_disallows_arbitrary_devices", "virtual_device_adapter"), ]); export function capabilities(contextId) { return { context_id: contextId, abi: 1, protocol: 1, snapshot: 1, event: 1, workspace: 1, commands: [...COMMANDS], worker_storage_commands: ["UI_STATE_SAVE", "UI_STATE_LOAD", "UI_STATE_CLEAR"], streams: ["STATE_SNAPSHOT", "EVENT", "ALARM"], storage: { owner: "dedicated-worker", ui_state: "opfs_temp_json_v1", power_off_checkpoint: "opfs_temp_json_v1", authority: "linuxcnc-wasm-remains-authoritative", }, limits: { ...LIMITS }, exclusions: EXCLUSIONS.map((entry) => ({ ...entry })), }; } export function validateLimit(name, value) { if (!(name in LIMITS)) return { status: "blocked", code: "capability_unknown_limit", message_key: "capability.unknown_limit", limit: name }; if (!Number.isSafeInteger(value) || value < 0) return { status: "blocked", code: "capability_invalid_value", message_key: "capability.invalid_value", limit: name, value }; if (value > LIMITS[name]) return { status: "blocked", code: "capability_limit_exceeded", message_key: "capability.limit_exceeded", limit: name, value, maximum: LIMITS[name] }; return { status: "accepted", limit: name, value, maximum: LIMITS[name] }; }