diff --git a/nix/checks/clawdbot-config-options.nix b/nix/checks/clawdbot-config-options.nix index 0a54cb3a..c40b8459 100644 --- a/nix/checks/clawdbot-config-options.nix +++ b/nix/checks/clawdbot-config-options.nix @@ -34,7 +34,7 @@ in stdenv.mkDerivation (finalAttrs: { pname = "clawdbot-config-options"; - version = "2026.1.8-2"; + version = "2026.1.16-2"; src = fetchFromGitHub sourceFetch; diff --git a/nix/checks/clawdbot-gateway-tests.nix b/nix/checks/clawdbot-gateway-tests.nix index 2deffd4e..d3ddad30 100644 --- a/nix/checks/clawdbot-gateway-tests.nix +++ b/nix/checks/clawdbot-gateway-tests.nix @@ -35,7 +35,7 @@ in stdenv.mkDerivation (finalAttrs: { pname = "clawdbot-gateway-tests"; - version = "2026.1.8-2"; + version = "2026.1.16-2"; src = fetchFromGitHub sourceFetch; diff --git a/nix/generated/clawdbot-config-options.nix b/nix/generated/clawdbot-config-options.nix index 5041202e..dd19fd43 100644 --- a/nix/generated/clawdbot-config-options.nix +++ b/nix/generated/clawdbot-config-options.nix @@ -194,15 +194,6 @@ in elevatedDefault = lib.mkOption { type = t.oneOf [ t.enum [ "off" ] t.enum [ "on" ] ]; }; - envelopeElapsed = lib.mkOption { - type = t.oneOf [ t.enum [ "on" ] t.enum [ "off" ] ]; - }; - envelopeTimestamp = lib.mkOption { - type = t.oneOf [ t.enum [ "on" ] t.enum [ "off" ] ]; - }; - envelopeTimezone = lib.mkOption { - type = t.str; - }; heartbeat = lib.mkOption { type = t.submodule { options = { ackMaxChars = lib.mkOption { @@ -259,16 +250,6 @@ in }; memorySearch = lib.mkOption { type = t.submodule { options = { - cache = lib.mkOption { - type = t.submodule { options = { - enabled = lib.mkOption { - type = t.bool; - }; - maxEntries = lib.mkOption { - type = t.int; - }; - }; }; - }; chunking = lib.mkOption { type = t.submodule { options = { overlap = lib.mkOption { @@ -282,15 +263,8 @@ in enabled = lib.mkOption { type = t.bool; }; - experimental = lib.mkOption { - type = t.submodule { options = { - sessionMemory = lib.mkOption { - type = t.bool; - }; - }; }; - }; fallback = lib.mkOption { - type = t.oneOf [ t.enum [ "openai" ] t.enum [ "gemini" ] t.enum [ "local" ] t.enum [ "none" ] ]; + type = t.oneOf [ t.enum [ "openai" ] t.enum [ "none" ] ]; }; local = lib.mkOption { type = t.submodule { options = { @@ -306,26 +280,10 @@ in type = t.str; }; provider = lib.mkOption { - type = t.oneOf [ t.enum [ "openai" ] t.enum [ "local" ] t.enum [ "gemini" ] ]; + type = t.oneOf [ t.enum [ "openai" ] t.enum [ "local" ] ]; }; query = lib.mkOption { type = t.submodule { options = { - hybrid = lib.mkOption { - type = t.submodule { options = { - candidateMultiplier = lib.mkOption { - type = t.int; - }; - enabled = lib.mkOption { - type = t.bool; - }; - textWeight = lib.mkOption { - type = t.number; - }; - vectorWeight = lib.mkOption { - type = t.number; - }; - }; }; - }; maxResults = lib.mkOption { type = t.int; }; @@ -342,33 +300,11 @@ in baseUrl = lib.mkOption { type = t.str; }; - batch = lib.mkOption { - type = t.submodule { options = { - concurrency = lib.mkOption { - type = t.int; - }; - enabled = lib.mkOption { - type = t.bool; - }; - pollIntervalMs = lib.mkOption { - type = t.int; - }; - timeoutMinutes = lib.mkOption { - type = t.int; - }; - wait = lib.mkOption { - type = t.bool; - }; - }; }; - }; headers = lib.mkOption { type = t.attrsOf (t.str); }; }; }; }; - sources = lib.mkOption { - type = t.listOf (t.oneOf [ t.enum [ "memory" ] t.enum [ "sessions" ] ]); - }; store = lib.mkOption { type = t.submodule { options = { driver = lib.mkOption { @@ -377,16 +313,6 @@ in path = lib.mkOption { type = t.str; }; - vector = lib.mkOption { - type = t.submodule { options = { - enabled = lib.mkOption { - type = t.bool; - }; - extensionPath = lib.mkOption { - type = t.str; - }; - }; }; - }; }; }; }; sync = lib.mkOption { @@ -702,16 +628,6 @@ in }; memorySearch = lib.mkOption { type = t.submodule { options = { - cache = lib.mkOption { - type = t.submodule { options = { - enabled = lib.mkOption { - type = t.bool; - }; - maxEntries = lib.mkOption { - type = t.int; - }; - }; }; - }; chunking = lib.mkOption { type = t.submodule { options = { overlap = lib.mkOption { @@ -725,15 +641,8 @@ in enabled = lib.mkOption { type = t.bool; }; - experimental = lib.mkOption { - type = t.submodule { options = { - sessionMemory = lib.mkOption { - type = t.bool; - }; - }; }; - }; fallback = lib.mkOption { - type = t.oneOf [ t.enum [ "openai" ] t.enum [ "gemini" ] t.enum [ "local" ] t.enum [ "none" ] ]; + type = t.oneOf [ t.enum [ "openai" ] t.enum [ "none" ] ]; }; local = lib.mkOption { type = t.submodule { options = { @@ -749,26 +658,10 @@ in type = t.str; }; provider = lib.mkOption { - type = t.oneOf [ t.enum [ "openai" ] t.enum [ "local" ] t.enum [ "gemini" ] ]; + type = t.oneOf [ t.enum [ "openai" ] t.enum [ "local" ] ]; }; query = lib.mkOption { type = t.submodule { options = { - hybrid = lib.mkOption { - type = t.submodule { options = { - candidateMultiplier = lib.mkOption { - type = t.int; - }; - enabled = lib.mkOption { - type = t.bool; - }; - textWeight = lib.mkOption { - type = t.number; - }; - vectorWeight = lib.mkOption { - type = t.number; - }; - }; }; - }; maxResults = lib.mkOption { type = t.int; }; @@ -785,33 +678,11 @@ in baseUrl = lib.mkOption { type = t.str; }; - batch = lib.mkOption { - type = t.submodule { options = { - concurrency = lib.mkOption { - type = t.int; - }; - enabled = lib.mkOption { - type = t.bool; - }; - pollIntervalMs = lib.mkOption { - type = t.int; - }; - timeoutMinutes = lib.mkOption { - type = t.int; - }; - wait = lib.mkOption { - type = t.bool; - }; - }; }; - }; headers = lib.mkOption { type = t.attrsOf (t.str); }; }; }; }; - sources = lib.mkOption { - type = t.listOf (t.oneOf [ t.enum [ "memory" ] t.enum [ "sessions" ] ]); - }; store = lib.mkOption { type = t.submodule { options = { driver = lib.mkOption { @@ -820,16 +691,6 @@ in path = lib.mkOption { type = t.str; }; - vector = lib.mkOption { - type = t.submodule { options = { - enabled = lib.mkOption { - type = t.bool; - }; - extensionPath = lib.mkOption { - type = t.str; - }; - }; }; - }; }; }; }; sync = lib.mkOption { @@ -1063,47 +924,6 @@ in }; }; }; }; - exec = lib.mkOption { - type = t.submodule { options = { - applyPatch = lib.mkOption { - type = t.submodule { options = { - allowModels = lib.mkOption { - type = t.listOf (t.str); - }; - enabled = lib.mkOption { - type = t.bool; - }; - }; }; - }; - ask = lib.mkOption { - type = t.enum [ "off" "on-miss" "always" ]; - }; - backgroundMs = lib.mkOption { - type = t.int; - }; - cleanupMs = lib.mkOption { - type = t.int; - }; - host = lib.mkOption { - type = t.enum [ "sandbox" "gateway" "node" ]; - }; - node = lib.mkOption { - type = t.str; - }; - notifyOnExit = lib.mkOption { - type = t.bool; - }; - pathPrepend = lib.mkOption { - type = t.listOf (t.str); - }; - security = lib.mkOption { - type = t.enum [ "deny" "allowlist" "full" ]; - }; - timeoutSec = lib.mkOption { - type = t.int; - }; - }; }; - }; profile = lib.mkOption { type = t.oneOf [ t.enum [ "minimal" ] t.enum [ "coding" ] t.enum [ "messaging" ] t.enum [ "full" ] ]; }; @@ -1217,6 +1037,39 @@ in }; }); }; + bridge = lib.mkOption { + type = t.submodule { options = { + bind = lib.mkOption { + type = t.oneOf [ t.enum [ "auto" ] t.enum [ "lan" ] t.enum [ "tailnet" ] t.enum [ "loopback" ] ]; + }; + enabled = lib.mkOption { + type = t.bool; + }; + port = lib.mkOption { + type = t.int; + }; + tls = lib.mkOption { + type = t.submodule { options = { + autoGenerate = lib.mkOption { + type = t.bool; + }; + caPath = lib.mkOption { + type = t.str; + }; + certPath = lib.mkOption { + type = t.str; + }; + enabled = lib.mkOption { + type = t.bool; + }; + keyPath = lib.mkOption { + type = t.str; + }; + }; }; + }; + }; }; + }; + broadcast = lib.mkOption { type = t.submodule { options = { strategy = lib.mkOption { @@ -1247,268 +1100,60 @@ in }; enabled = lib.mkOption { type = t.bool; - }; - executablePath = lib.mkOption { - type = t.str; - }; - headless = lib.mkOption { - type = t.bool; - }; - noSandbox = lib.mkOption { - type = t.bool; - }; - profiles = lib.mkOption { - type = t.attrsOf (t.submodule { options = { - cdpPort = lib.mkOption { - type = t.int; - }; - cdpUrl = lib.mkOption { - type = t.str; - }; - color = lib.mkOption { - type = t.str; - }; - driver = lib.mkOption { - type = t.oneOf [ t.enum [ "clawd" ] t.enum [ "extension" ] ]; - }; - }; }); - }; - remoteCdpHandshakeTimeoutMs = lib.mkOption { - type = t.int; - }; - remoteCdpTimeoutMs = lib.mkOption { - type = t.int; - }; - }; }; - }; - - canvasHost = lib.mkOption { - type = t.submodule { options = { - enabled = lib.mkOption { - type = t.bool; - }; - liveReload = lib.mkOption { - type = t.bool; - }; - port = lib.mkOption { - type = t.int; - }; - root = lib.mkOption { - type = t.str; - }; - }; }; - }; - - channels = lib.mkOption { - type = t.submodule { options = { - bluebubbles = lib.mkOption { - type = t.submodule { options = { - accounts = lib.mkOption { - type = t.attrsOf (t.submodule { options = { - allowFrom = lib.mkOption { - type = t.listOf (t.oneOf [ t.str t.number ]); - }; - blockStreaming = lib.mkOption { - type = t.bool; - }; - blockStreamingCoalesce = lib.mkOption { - type = t.submodule { options = { - idleMs = lib.mkOption { - type = t.int; - }; - maxChars = lib.mkOption { - type = t.int; - }; - minChars = lib.mkOption { - type = t.int; - }; - }; }; - }; - capabilities = lib.mkOption { - type = t.listOf (t.str); - }; - configWrites = lib.mkOption { - type = t.bool; - }; - dmHistoryLimit = lib.mkOption { - type = t.int; - }; - dmPolicy = lib.mkOption { - type = t.enum [ "pairing" "allowlist" "open" "disabled" ]; - }; - dms = lib.mkOption { - type = t.attrsOf (t.submodule { options = { - historyLimit = lib.mkOption { - type = t.int; - }; - }; }); - }; - enabled = lib.mkOption { - type = t.bool; - }; - groupAllowFrom = lib.mkOption { - type = t.listOf (t.oneOf [ t.str t.number ]); - }; - groupPolicy = lib.mkOption { - type = t.enum [ "open" "disabled" "allowlist" ]; - }; - groups = lib.mkOption { - type = t.attrsOf (t.submodule { options = { - requireMention = lib.mkOption { - type = t.bool; - }; - }; }); - }; - historyLimit = lib.mkOption { - type = t.int; - }; - mediaMaxMb = lib.mkOption { - type = t.int; - }; - name = lib.mkOption { - type = t.str; - }; - password = lib.mkOption { - type = t.str; - }; - sendReadReceipts = lib.mkOption { - type = t.bool; - }; - serverUrl = lib.mkOption { - type = t.str; - }; - textChunkLimit = lib.mkOption { - type = t.int; - }; - webhookPath = lib.mkOption { - type = t.str; - }; - }; }); - }; - actions = lib.mkOption { - type = t.submodule { options = { - addParticipant = lib.mkOption { - type = t.bool; - }; - edit = lib.mkOption { - type = t.bool; - }; - leaveGroup = lib.mkOption { - type = t.bool; - }; - reactions = lib.mkOption { - type = t.bool; - }; - removeParticipant = lib.mkOption { - type = t.bool; - }; - renameGroup = lib.mkOption { - type = t.bool; - }; - reply = lib.mkOption { - type = t.bool; - }; - sendAttachment = lib.mkOption { - type = t.bool; - }; - sendWithEffect = lib.mkOption { - type = t.bool; - }; - setGroupIcon = lib.mkOption { - type = t.bool; - }; - unsend = lib.mkOption { - type = t.bool; - }; - }; }; - }; - allowFrom = lib.mkOption { - type = t.listOf (t.oneOf [ t.str t.number ]); - }; - blockStreaming = lib.mkOption { - type = t.bool; - }; - blockStreamingCoalesce = lib.mkOption { - type = t.submodule { options = { - idleMs = lib.mkOption { - type = t.int; - }; - maxChars = lib.mkOption { - type = t.int; - }; - minChars = lib.mkOption { - type = t.int; - }; - }; }; - }; - capabilities = lib.mkOption { - type = t.listOf (t.str); - }; - configWrites = lib.mkOption { - type = t.bool; - }; - dmHistoryLimit = lib.mkOption { - type = t.int; - }; - dmPolicy = lib.mkOption { - type = t.enum [ "pairing" "allowlist" "open" "disabled" ]; - }; - dms = lib.mkOption { - type = t.attrsOf (t.submodule { options = { - historyLimit = lib.mkOption { - type = t.int; - }; - }; }); - }; - enabled = lib.mkOption { - type = t.bool; - }; - groupAllowFrom = lib.mkOption { - type = t.listOf (t.oneOf [ t.str t.number ]); - }; - groupPolicy = lib.mkOption { - type = t.enum [ "open" "disabled" "allowlist" ]; - }; - groups = lib.mkOption { - type = t.attrsOf (t.submodule { options = { - requireMention = lib.mkOption { - type = t.bool; - }; - }; }); - }; - historyLimit = lib.mkOption { - type = t.int; - }; - mediaMaxMb = lib.mkOption { + }; + executablePath = lib.mkOption { + type = t.str; + }; + headless = lib.mkOption { + type = t.bool; + }; + noSandbox = lib.mkOption { + type = t.bool; + }; + profiles = lib.mkOption { + type = t.attrsOf (t.submodule { options = { + cdpPort = lib.mkOption { type = t.int; }; - name = lib.mkOption { - type = t.str; - }; - password = lib.mkOption { + cdpUrl = lib.mkOption { type = t.str; }; - sendReadReceipts = lib.mkOption { - type = t.bool; - }; - serverUrl = lib.mkOption { + color = lib.mkOption { type = t.str; }; - textChunkLimit = lib.mkOption { - type = t.int; - }; - webhookPath = lib.mkOption { - type = t.str; + driver = lib.mkOption { + type = t.oneOf [ t.enum [ "clawd" ] t.enum [ "extension" ] ]; }; - }; }; + }; }); }; - defaults = lib.mkOption { - type = t.submodule { options = { - groupPolicy = lib.mkOption { - type = t.enum [ "open" "disabled" "allowlist" ]; - }; - }; }; + remoteCdpHandshakeTimeoutMs = lib.mkOption { + type = t.int; + }; + remoteCdpTimeoutMs = lib.mkOption { + type = t.int; + }; + }; }; + }; + + canvasHost = lib.mkOption { + type = t.submodule { options = { + enabled = lib.mkOption { + type = t.bool; + }; + liveReload = lib.mkOption { + type = t.bool; + }; + port = lib.mkOption { + type = t.int; }; + root = lib.mkOption { + type = t.str; + }; + }; }; + }; + + channels = lib.mkOption { + type = t.submodule { options = { discord = lib.mkOption { type = t.submodule { options = { accounts = lib.mkOption { @@ -2544,9 +2189,6 @@ in mediaMaxMb = lib.mkOption { type = t.number; }; - mode = lib.mkOption { - type = t.enum [ "socket" "http" ]; - }; name = lib.mkOption { type = t.str; }; @@ -2562,9 +2204,6 @@ in requireMention = lib.mkOption { type = t.bool; }; - signingSecret = lib.mkOption { - type = t.str; - }; slashCommand = lib.mkOption { type = t.submodule { options = { enabled = lib.mkOption { @@ -2600,9 +2239,6 @@ in userTokenReadOnly = lib.mkOption { type = t.bool; }; - webhookPath = lib.mkOption { - type = t.str; - }; }; }); }; actions = lib.mkOption { @@ -2740,9 +2376,6 @@ in mediaMaxMb = lib.mkOption { type = t.number; }; - mode = lib.mkOption { - type = t.enum [ "socket" "http" ]; - }; name = lib.mkOption { type = t.str; }; @@ -2758,9 +2391,6 @@ in requireMention = lib.mkOption { type = t.bool; }; - signingSecret = lib.mkOption { - type = t.str; - }; slashCommand = lib.mkOption { type = t.submodule { options = { enabled = lib.mkOption { @@ -2796,9 +2426,6 @@ in userTokenReadOnly = lib.mkOption { type = t.bool; }; - webhookPath = lib.mkOption { - type = t.str; - }; }; }; }; telegram = lib.mkOption { @@ -3450,48 +3077,6 @@ in }; }; }; - diagnostics = lib.mkOption { - type = t.submodule { options = { - enabled = lib.mkOption { - type = t.bool; - }; - otel = lib.mkOption { - type = t.submodule { options = { - enabled = lib.mkOption { - type = t.bool; - }; - endpoint = lib.mkOption { - type = t.str; - }; - flushIntervalMs = lib.mkOption { - type = t.int; - }; - headers = lib.mkOption { - type = t.attrsOf (t.str); - }; - logs = lib.mkOption { - type = t.bool; - }; - metrics = lib.mkOption { - type = t.bool; - }; - protocol = lib.mkOption { - type = t.oneOf [ t.enum [ "http/protobuf" ] t.enum [ "grpc" ] ]; - }; - sampleRate = lib.mkOption { - type = t.number; - }; - serviceName = lib.mkOption { - type = t.str; - }; - traces = lib.mkOption { - type = t.bool; - }; - }; }; - }; - }; }; - }; - discovery = lib.mkOption { type = t.submodule { options = { wideArea = lib.mkOption { @@ -3541,7 +3126,7 @@ in }; }; }; bind = lib.mkOption { - type = t.oneOf [ t.enum [ "auto" ] t.enum [ "lan" ] t.enum [ "loopback" ] t.enum [ "custom" ] ]; + type = t.oneOf [ t.enum [ "auto" ] t.enum [ "lan" ] t.enum [ "tailnet" ] t.enum [ "loopback" ] ]; }; controlUi = lib.mkOption { type = t.submodule { options = { @@ -3564,70 +3149,6 @@ in }; }; }; }; - responses = lib.mkOption { - type = t.submodule { options = { - enabled = lib.mkOption { - type = t.bool; - }; - files = lib.mkOption { - type = t.submodule { options = { - allowUrl = lib.mkOption { - type = t.bool; - }; - allowedMimes = lib.mkOption { - type = t.listOf (t.str); - }; - maxBytes = lib.mkOption { - type = t.int; - }; - maxChars = lib.mkOption { - type = t.int; - }; - maxRedirects = lib.mkOption { - type = t.int; - }; - pdf = lib.mkOption { - type = t.submodule { options = { - maxPages = lib.mkOption { - type = t.int; - }; - maxPixels = lib.mkOption { - type = t.int; - }; - minTextChars = lib.mkOption { - type = t.int; - }; - }; }; - }; - timeoutMs = lib.mkOption { - type = t.int; - }; - }; }; - }; - images = lib.mkOption { - type = t.submodule { options = { - allowUrl = lib.mkOption { - type = t.bool; - }; - allowedMimes = lib.mkOption { - type = t.listOf (t.str); - }; - maxBytes = lib.mkOption { - type = t.int; - }; - maxRedirects = lib.mkOption { - type = t.int; - }; - timeoutMs = lib.mkOption { - type = t.int; - }; - }; }; - }; - maxBodyBytes = lib.mkOption { - type = t.int; - }; - }; }; - }; }; }; }; }; }; @@ -3635,16 +3156,6 @@ in mode = lib.mkOption { type = t.oneOf [ t.enum [ "local" ] t.enum [ "remote" ] ]; }; - nodes = lib.mkOption { - type = t.submodule { options = { - allowCommands = lib.mkOption { - type = t.listOf (t.str); - }; - denyCommands = lib.mkOption { - type = t.listOf (t.str); - }; - }; }; - }; port = lib.mkOption { type = t.int; }; @@ -3669,9 +3180,6 @@ in sshTarget = lib.mkOption { type = t.str; }; - tlsFingerprint = lib.mkOption { - type = t.str; - }; token = lib.mkOption { type = t.str; }; @@ -3690,25 +3198,6 @@ in }; }; }; }; - tls = lib.mkOption { - type = t.submodule { options = { - autoGenerate = lib.mkOption { - type = t.bool; - }; - caPath = lib.mkOption { - type = t.str; - }; - certPath = lib.mkOption { - type = t.str; - }; - enabled = lib.mkOption { - type = t.bool; - }; - keyPath = lib.mkOption { - type = t.str; - }; - }; }; - }; }; }; }; @@ -4055,17 +3544,6 @@ in }; }; }; - meta = lib.mkOption { - type = t.submodule { options = { - lastTouchedAt = lib.mkOption { - type = t.str; - }; - lastTouchedVersion = lib.mkOption { - type = t.str; - }; - }; }; - }; - models = lib.mkOption { type = t.submodule { options = { mode = lib.mkOption { @@ -4074,14 +3552,11 @@ in providers = lib.mkOption { type = t.attrsOf (t.submodule { options = { api = lib.mkOption { - type = t.oneOf [ t.enum [ "openai-completions" ] t.enum [ "openai-responses" ] t.enum [ "anthropic-messages" ] t.enum [ "google-generative-ai" ] t.enum [ "github-copilot" ] t.enum [ "bedrock-converse-stream" ] ]; + type = t.oneOf [ t.enum [ "openai-completions" ] t.enum [ "openai-responses" ] t.enum [ "anthropic-messages" ] t.enum [ "google-generative-ai" ] t.enum [ "github-copilot" ] ]; }; apiKey = lib.mkOption { type = t.str; }; - auth = lib.mkOption { - type = t.oneOf [ t.enum [ "api-key" ] t.enum [ "aws-sdk" ] t.enum [ "oauth" ] t.enum [ "token" ] ]; - }; authHeader = lib.mkOption { type = t.bool; }; @@ -4094,7 +3569,7 @@ in models = lib.mkOption { type = t.listOf (t.submodule { options = { api = lib.mkOption { - type = t.oneOf [ t.enum [ "openai-completions" ] t.enum [ "openai-responses" ] t.enum [ "anthropic-messages" ] t.enum [ "google-generative-ai" ] t.enum [ "github-copilot" ] t.enum [ "bedrock-converse-stream" ] ]; + type = t.oneOf [ t.enum [ "openai-completions" ] t.enum [ "openai-responses" ] t.enum [ "anthropic-messages" ] t.enum [ "google-generative-ai" ] t.enum [ "github-copilot" ] ]; }; compat = lib.mkOption { type = t.submodule { options = { @@ -4206,13 +3681,6 @@ in }; }; }; }; - slots = lib.mkOption { - type = t.submodule { options = { - memory = lib.mkOption { - type = t.str; - }; - }; }; - }; }; }; }; @@ -4240,62 +3708,6 @@ in mainKey = lib.mkOption { type = t.str; }; - reset = lib.mkOption { - type = t.submodule { options = { - atHour = lib.mkOption { - type = t.int; - }; - idleMinutes = lib.mkOption { - type = t.int; - }; - mode = lib.mkOption { - type = t.oneOf [ t.enum [ "daily" ] t.enum [ "idle" ] ]; - }; - }; }; - }; - resetByType = lib.mkOption { - type = t.submodule { options = { - dm = lib.mkOption { - type = t.submodule { options = { - atHour = lib.mkOption { - type = t.int; - }; - idleMinutes = lib.mkOption { - type = t.int; - }; - mode = lib.mkOption { - type = t.oneOf [ t.enum [ "daily" ] t.enum [ "idle" ] ]; - }; - }; }; - }; - group = lib.mkOption { - type = t.submodule { options = { - atHour = lib.mkOption { - type = t.int; - }; - idleMinutes = lib.mkOption { - type = t.int; - }; - mode = lib.mkOption { - type = t.oneOf [ t.enum [ "daily" ] t.enum [ "idle" ] ]; - }; - }; }; - }; - thread = lib.mkOption { - type = t.submodule { options = { - atHour = lib.mkOption { - type = t.int; - }; - idleMinutes = lib.mkOption { - type = t.int; - }; - mode = lib.mkOption { - type = t.oneOf [ t.enum [ "daily" ] t.enum [ "idle" ] ]; - }; - }; }; - }; - }; }; - }; resetTriggers = lib.mkOption { type = t.listOf (t.str); }; @@ -4351,9 +3763,6 @@ in apiKey = lib.mkOption { type = t.str; }; - config = lib.mkOption { - type = t.attrsOf (t.anything); - }; enabled = lib.mkOption { type = t.bool; }; @@ -4464,30 +3873,15 @@ in }; }; }; }; - ask = lib.mkOption { - type = t.enum [ "off" "on-miss" "always" ]; - }; backgroundMs = lib.mkOption { type = t.int; }; cleanupMs = lib.mkOption { type = t.int; }; - host = lib.mkOption { - type = t.enum [ "sandbox" "gateway" "node" ]; - }; - node = lib.mkOption { - type = t.str; - }; notifyOnExit = lib.mkOption { type = t.bool; }; - pathPrepend = lib.mkOption { - type = t.listOf (t.str); - }; - security = lib.mkOption { - type = t.enum [ "deny" "allowlist" "full" ]; - }; timeoutSec = lib.mkOption { type = t.int; }; @@ -5115,21 +4509,8 @@ in maxResults = lib.mkOption { type = t.int; }; - perplexity = lib.mkOption { - type = t.submodule { options = { - apiKey = lib.mkOption { - type = t.str; - }; - baseUrl = lib.mkOption { - type = t.str; - }; - model = lib.mkOption { - type = t.str; - }; - }; }; - }; provider = lib.mkOption { - type = t.oneOf [ t.enum [ "brave" ] t.enum [ "perplexity" ] ]; + type = t.oneOf [ t.enum [ "brave" ] ]; }; timeoutSeconds = lib.mkOption { type = t.int; @@ -5152,7 +4533,7 @@ in update = lib.mkOption { type = t.submodule { options = { channel = lib.mkOption { - type = t.oneOf [ t.enum [ "stable" ] t.enum [ "beta" ] t.enum [ "dev" ] ]; + type = t.oneOf [ t.enum [ "stable" ] t.enum [ "beta" ] ]; }; checkOnStart = lib.mkOption { type = t.bool; diff --git a/nix/modules/home-manager/clawdbot.nix b/nix/modules/home-manager/clawdbot.nix index 1b59d8b3..aadfc079 100644 --- a/nix/modules/home-manager/clawdbot.nix +++ b/nix/modules/home-manager/clawdbot.nix @@ -36,11 +36,13 @@ let }; mkTelegramConfig = inst: lib.optionalAttrs inst.providers.telegram.enable { - telegram = { - enabled = true; - tokenFile = inst.providers.telegram.botTokenFile; - allowFrom = inst.providers.telegram.allowFrom; - groups = inst.providers.telegram.groups; + channels = { + telegram = { + enabled = true; + tokenFile = inst.providers.telegram.botTokenFile; + allowFrom = inst.providers.telegram.allowFrom; + groups = inst.providers.telegram.groups; + }; }; }; @@ -48,7 +50,6 @@ let messages = { queue = { mode = inst.routing.queue.mode; - byProvider = inst.routing.queue.byProvider; }; }; }; @@ -238,6 +239,29 @@ let description = "launchd label for this instance."; }; + launchd.useAppRunner = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Run the gateway via a minimal .app bundle instead of a raw script. + This allows granting Full Disk Access (FDA) to the gateway process, + which is required for iMessage support (imsg needs to read Messages.db). + + When enabled: + 1. A "Clawdbot Gateway Runner.app" is installed to ~/Applications/ + 2. The LaunchAgent runs the app's executable directly + 3. Grant FDA to the app in System Settings > Privacy & Security > Full Disk Access + ''; + }; + + launchd.appRunnerPath = lib.mkOption { + type = lib.types.str; + default = if name == "default" + then "${homeDir}/Applications/Clawdbot Gateway Runner.app" + else "${homeDir}/Applications/Clawdbot Gateway Runner (${name}).app"; + description = "Installation path for the gateway runner app (when useAppRunner is enabled)."; + }; + systemd.enable = lib.mkOption { type = lib.types.bool; default = true; @@ -300,11 +324,30 @@ let configPath = "${cfg.stateDir}/clawdbot.json"; logPath = "/tmp/clawdbot/clawdbot-gateway.log"; gatewayPort = 18789; - providers = cfg.providers; + gatewayPath = null; + gatewayPnpmDepsHash = lib.fakeHash; + providers = { + anthropic = cfg.providers.anthropic; + telegram = cfg.providers.telegram // { + groups = {}; + }; + }; routing = cfg.routing; - launchd = cfg.launchd; - systemd = cfg.systemd; + launchd = { + enable = cfg.launchd.enable; + label = "com.steipete.clawdbot.gateway"; + useAppRunner = cfg.launchd.useAppRunner or false; + appRunnerPath = cfg.launchd.appRunnerPath or "${homeDir}/Applications/Clawdbot Gateway Runner.app"; + }; + systemd = { + enable = cfg.systemd.enable; + unitName = "clawdbot-gateway"; + }; plugins = cfg.plugins; + agent = { + model = cfg.defaults.model; + thinkingDefault = cfg.defaults.thinkingDefault; + }; configOverrides = {}; config = {}; appDefaults = { @@ -758,6 +801,23 @@ let exec "${gatewayPackage}/bin/clawdbot" "$@" ''; + + # Build gateway runner .app for FDA support (when useAppRunner is enabled) + gatewayRunnerApp = lib.optionalAttrs (pkgs.stdenv.hostPlatform.isDarwin && inst.launchd.useAppRunner) ( + pkgs.callPackage ../../packages/clawdbot-gateway-runner.nix { + instanceName = name; + inherit gatewayWrapper homeDir; + stateDir = inst.stateDir; + logPath = inst.logPath; + gatewayPort = inst.gatewayPort; + configPath = inst.configPath; + } + ); + + appName = if name == "default" + then "Clawdbot Gateway Runner" + else "Clawdbot Gateway Runner (${name})"; + in { homeFile = { name = toRelative inst.configPath; @@ -773,32 +833,48 @@ let enable = true; config = { Label = inst.launchd.label; - ProgramArguments = [ - "${gatewayWrapper}/bin/clawdbot-gateway-${name}" - "gateway" - "--port" - "${toString inst.gatewayPort}" - ]; + ProgramArguments = + if inst.launchd.useAppRunner then [ + # Run the gateway runner app's executable directly + # This allows FDA to be granted to the .app bundle + "${inst.launchd.appRunnerPath}/Contents/MacOS/clawdbot-gateway-runner" + ] else [ + "${gatewayWrapper}/bin/clawdbot-gateway-${name}" + "gateway" + "--port" + "${toString inst.gatewayPort}" + ]; RunAtLoad = true; KeepAlive = true; WorkingDirectory = inst.stateDir; StandardOutPath = inst.logPath; StandardErrorPath = inst.logPath; - EnvironmentVariables = { - HOME = homeDir; - CLAWDBOT_CONFIG_PATH = inst.configPath; - CLAWDBOT_STATE_DIR = inst.stateDir; - CLAWDBOT_IMAGE_BACKEND = "sips"; - CLAWDBOT_NIX_MODE = "1"; - # Backward-compatible env names (gateway still uses CLAWDIS_* in some builds). - CLAWDIS_CONFIG_PATH = inst.configPath; - CLAWDIS_STATE_DIR = inst.stateDir; - CLAWDIS_IMAGE_BACKEND = "sips"; - CLAWDIS_NIX_MODE = "1"; + EnvironmentVariables = { + HOME = homeDir; + CLAWDBOT_CONFIG_PATH = inst.configPath; + CLAWDBOT_STATE_DIR = inst.stateDir; + CLAWDBOT_IMAGE_BACKEND = "sips"; + CLAWDBOT_NIX_MODE = "1"; + # Backward-compatible env names (gateway still uses CLAWDIS_* in some builds). + CLAWDIS_CONFIG_PATH = inst.configPath; + CLAWDIS_STATE_DIR = inst.stateDir; + CLAWDIS_IMAGE_BACKEND = "sips"; + CLAWDIS_NIX_MODE = "1"; + }; }; }; }; - }; + + # Gateway runner app source path (for activation script to copy) + # We use activation instead of home.file because home.file symlinks, + # and FDA requires the actual binary to be at the granted path, not a symlink. + appRunnerSource = lib.optionalString (pkgs.stdenv.hostPlatform.isDarwin && inst.launchd.useAppRunner) + "${gatewayRunnerApp}/Applications/${appName}.app"; + appRunnerDest = lib.optionalString (pkgs.stdenv.hostPlatform.isDarwin && inst.launchd.useAppRunner) + inst.launchd.appRunnerPath; + + # Kept for backwards compatibility but no longer used for app runner + appRunnerInstall = {}; systemdService = lib.optionalAttrs (pkgs.stdenv.hostPlatform.isLinux && inst.systemd.enable) { "${inst.systemd.unitName}" = { @@ -849,6 +925,12 @@ let instanceConfigs = lib.mapAttrsToList mkInstanceConfig enabledInstances; appInstalls = lib.filter (item: item != null) (map (item: item.appInstall) instanceConfigs); + appRunnerInstalls = lib.filter (item: item != {}) (map (item: item.appRunnerInstall) instanceConfigs); + + # Collect gateway runner app source/dest pairs for copy activation + appRunnerCopyPairs = lib.filter (pair: pair.source != "" && pair.dest != "") ( + map (item: { source = item.appRunnerSource; dest = item.appRunnerDest; }) instanceConfigs + ); appDefaults = lib.foldl' (acc: item: lib.recursiveUpdate acc item.appDefaults) {} instanceConfigs; @@ -1101,6 +1183,22 @@ in { description = "Run Clawdbot gateway via launchd (macOS)."; }; + launchd.useAppRunner = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Run the gateway via a minimal .app bundle instead of a raw script. + This allows granting Full Disk Access (FDA) to the gateway process, + which is required for iMessage support. + ''; + }; + + launchd.appRunnerPath = lib.mkOption { + type = lib.types.str; + default = "${homeDir}/Applications/Clawdbot Gateway Runner.app"; + description = "Installation path for the gateway runner app."; + }; + systemd.enable = lib.mkOption { type = lib.types.bool; default = true; @@ -1157,6 +1255,8 @@ in { }; }) // (lib.listToAttrs appInstalls) + # Note: Gateway runner app is installed via activation script (clawdbotGatewayRunnerCopy) + # to ensure it's copied (not symlinked) for FDA to work properly // documentsFiles // skillFiles // pluginSkillsFiles @@ -1203,6 +1303,20 @@ in { '' ); + # Copy gateway runner apps (not symlink) so FDA works properly + # FDA checks the actual binary path, not symlink targets + home.activation.clawdbotGatewayRunnerCopy = lib.mkIf (pkgs.stdenv.hostPlatform.isDarwin && appRunnerCopyPairs != []) ( + lib.hm.dag.entryAfter [ "writeBoundary" ] '' + ${lib.concatMapStringsSep "\n" (pair: '' + # Remove existing (might be a symlink from previous home-manager) + $DRY_RUN_CMD rm -rf "${pair.dest}" + # Copy the app bundle (not symlink, so FDA works) + $DRY_RUN_CMD cp -R "${pair.source}" "${pair.dest}" + $DRY_RUN_CMD chmod -R u+w "${pair.dest}" + '') appRunnerCopyPairs} + '' + ); + systemd.user.services = lib.mkIf pkgs.stdenv.hostPlatform.isLinux ( lib.mkMerge (map (item: item.systemdService) instanceConfigs) ); diff --git a/nix/packages/clawdbot-gateway-runner.nix b/nix/packages/clawdbot-gateway-runner.nix new file mode 100644 index 00000000..8c273733 --- /dev/null +++ b/nix/packages/clawdbot-gateway-runner.nix @@ -0,0 +1,123 @@ +{ lib +, stdenvNoCC +, writeShellScript +, instanceName ? "default" +, gatewayWrapper +, stateDir +, logPath +, gatewayPort ? 18789 +, homeDir ? "" +, configPath ? "" +}: + +# Creates a minimal .app bundle that runs the clawdbot gateway. +# This .app can be granted Full Disk Access in System Preferences, +# which is required for iMessage support (imsg needs to read Messages.db). +# +# The LaunchAgent runs this .app's executable directly, passing environment +# variables (CLAWDBOT_CONFIG_PATH, CLAWDBOT_STATE_DIR, etc.) and handling +# stdout/stderr logging. +# +# Usage: +# 1. Build and install this .app to ~/Applications/ +# 2. Grant FDA in System Settings > Privacy & Security > Full Disk Access +# 3. The LaunchAgent will run the app's executable with proper env vars + +let + appName = if instanceName == "default" + then "Clawdbot Gateway Runner" + else "Clawdbot Gateway Runner (${instanceName})"; + + bundleId = if instanceName == "default" + then "com.clawdbot.gateway-runner" + else "com.clawdbot.gateway-runner.${instanceName}"; + + # The actual script that runs inside the .app + # Environment variables (CLAWDBOT_CONFIG_PATH, etc.) are set by the LaunchAgent + # stdout/stderr are handled by the LaunchAgent's StandardOutPath/StandardErrorPath + runnerScript = writeShellScript "clawdbot-gateway-runner" '' + #!/bin/bash + set -euo pipefail + + # The LaunchAgent sets these env vars, but provide fallbacks just in case + export HOME="''${HOME:-${homeDir}}" + export CLAWDBOT_CONFIG_PATH="''${CLAWDBOT_CONFIG_PATH:-${configPath}}" + export CLAWDBOT_STATE_DIR="''${CLAWDBOT_STATE_DIR:-${stateDir}}" + export CLAWDBOT_IMAGE_BACKEND="''${CLAWDBOT_IMAGE_BACKEND:-sips}" + export CLAWDBOT_NIX_MODE="''${CLAWDBOT_NIX_MODE:-1}" + + # Run the gateway - stdout/stderr handled by LaunchAgent + exec "${gatewayWrapper}/bin/clawdbot-gateway-${instanceName}" \ + gateway \ + --port ${toString gatewayPort} + ''; + + infoPlist = '' + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + clawdbot-gateway-runner + CFBundleIconFile + AppIcon + CFBundleIdentifier + ${bundleId} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${appName} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0.0 + CFBundleVersion + 1 + LSMinimumSystemVersion + 11.0 + LSUIElement + + NSHighResolutionCapable + + + +''; + +in stdenvNoCC.mkDerivation { + pname = "clawdbot-gateway-runner"; + version = "1.0.0"; + + dontUnpack = true; + dontBuild = true; + + installPhase = '' + runHook preInstall + + APP_DIR="$out/Applications/${appName}.app" + mkdir -p "$APP_DIR/Contents/MacOS" + mkdir -p "$APP_DIR/Contents/Resources" + + # Write Info.plist + cat > "$APP_DIR/Contents/Info.plist" << 'PLIST_EOF' +${infoPlist} +PLIST_EOF + + # Copy the runner script as the executable + cp "${runnerScript}" "$APP_DIR/Contents/MacOS/clawdbot-gateway-runner" + chmod +x "$APP_DIR/Contents/MacOS/clawdbot-gateway-runner" + + # Create PkgInfo + echo -n "APPL????" > "$APP_DIR/Contents/PkgInfo" + + runHook postInstall + ''; + + meta = with lib; { + description = "Clawdbot Gateway Runner - minimal .app for FDA permissions"; + homepage = "https://github.com/clawdbot/clawdbot"; + license = licenses.mit; + platforms = platforms.darwin; + }; +} diff --git a/nix/packages/clawdbot-gateway.nix b/nix/packages/clawdbot-gateway.nix index 11c17b01..f13669eb 100644 --- a/nix/packages/clawdbot-gateway.nix +++ b/nix/packages/clawdbot-gateway.nix @@ -38,7 +38,7 @@ in stdenv.mkDerivation (finalAttrs: { pname = "clawdbot-gateway"; - version = "2026.1.8-2"; + version = "2026.1.23"; src = if gatewaySrc != null then gatewaySrc else fetchFromGitHub sourceFetch; diff --git a/nix/scripts/gateway-install.sh b/nix/scripts/gateway-install.sh index cd09ff94..552f75a8 100755 --- a/nix/scripts/gateway-install.sh +++ b/nix/scripts/gateway-install.sh @@ -4,6 +4,18 @@ mkdir -p "$out/lib/clawdbot" "$out/bin" cp -r dist node_modules package.json ui "$out/lib/clawdbot/" +# Copy extensions directory for channel plugins (telegram, discord, etc.) +# See: https://github.com/clawdbot/nix-clawdbot/issues/6 +if [ -d "extensions" ]; then + cp -r extensions "$out/lib/clawdbot/" +fi + +# Copy docs/reference/templates for workspace initialization +if [ -d "docs/reference/templates" ]; then + mkdir -p "$out/lib/clawdbot/docs/reference" + cp -r docs/reference/templates "$out/lib/clawdbot/docs/reference/" +fi + if [ -z "${STDENV_SETUP:-}" ]; then echo "STDENV_SETUP is not set" >&2 exit 1 diff --git a/nix/sources/clawdbot-source.nix b/nix/sources/clawdbot-source.nix index 1dab4cbc..7d59b055 100644 --- a/nix/sources/clawdbot-source.nix +++ b/nix/sources/clawdbot-source.nix @@ -2,7 +2,7 @@ { owner = "clawdbot"; repo = "clawdbot"; - rev = "e81ca7ab00ae8bf325691154cb3d8e2f410936ea"; - hash = "sha256-HQRCRuCCWUwXN/KJ2S+8UzmxSkfRVPlGl14MK2fgvoc="; - pnpmDepsHash = "sha256-Uo++PJBXKk7pN6OwezMnAYQXR77lCYJkru7J+t/KK5U="; + rev = "c9e98376b3e5d3a2f3a1639be53bd850f6d3acbf"; + hash = "sha256-egAHjt6CHz79fStSg42opVPHjquurAa6FcGpNkQ0UtA="; + pnpmDepsHash = "sha256-N0rAUNutQ/zox1ZL6Lt/lwvXoPc5mbmW5mw3f0fSuKw="; } diff --git a/scripts/update-pins.sh b/scripts/update-pins.sh index f683da0d..eea8854d 100755 --- a/scripts/update-pins.sh +++ b/scripts/update-pins.sh @@ -142,6 +142,18 @@ if [[ -z "$release_tag" ]]; then fi log "Latest app release tag with asset: $release_tag" +# Update version strings in gateway package and check derivations +gateway_version="${release_tag#v}" +log "Updating gateway version to: $gateway_version" + +gateway_file="$repo_root/nix/packages/clawdbot-gateway.nix" +tests_file="$repo_root/nix/checks/clawdbot-gateway-tests.nix" +options_file="$repo_root/nix/checks/clawdbot-config-options.nix" + +perl -0pi -e "s|version = \"[^\"]+\";|version = \"${gateway_version}\";|" "$gateway_file" +perl -0pi -e "s|version = \"[^\"]+\";|version = \"${gateway_version}\";|" "$tests_file" +perl -0pi -e "s|version = \"[^\"]+\";|version = \"${gateway_version}\";|" "$options_file" + app_url=$(printf '%s' "$release_json" | jq -r '[.[] | select([.assets[]?.name | (test("^(Clawdbot|Clawdis)-.*\\.zip$") and (test("dSYM") | not))] | any)][0].assets[] | select(.name | (test("^(Clawdbot|Clawdis)-.*\\.zip$") and (test("dSYM") | not))) | .browser_download_url' | head -n 1 || true) if [[ -z "$app_url" ]]; then echo "Failed to resolve Clawdbot app asset URL from latest release" >&2 @@ -227,7 +239,8 @@ if git diff --quiet; then fi log "Committing updated pins" -git add "$source_file" "$app_file" "$repo_root/nix/generated/clawdbot-config-options.nix" +git add "$source_file" "$app_file" "$gateway_file" "$tests_file" "$options_file" \ + "$repo_root/nix/generated/clawdbot-config-options.nix" git commit -F - <<'EOF' 🤖 codex: bump clawdbot pins (no-issue) @@ -235,6 +248,7 @@ What: - pin clawdbot source to latest upstream main - refresh macOS app pin to latest release asset - update source and app hashes +- update version strings in gateway and check derivations - regenerate config options from upstream schema Why: