feat(clawdbot): add remote mode for macOS nodes - #587
Conversation
|
You do not have enough credits to review this pull request. Please purchase more credits to continue. |
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. 📝 WalkthroughSummary by CodeRabbitRelease Notes
✏️ Tip: You can customize this high-level summary in your review settings. WalkthroughAdds CLAWDBOT_GATEWAY_TOKEN and extraction; introduces host detection (isKyber/isGalactica/nodeName); makes Clawdbot use local gateway on Kyber and remote gateway with token on other hosts; propagates HOST in Makefile; converts a Fish alias to a function; adds Zellij config, LL M model entries, package reorderings, overlay clawdbot overrides, and removes install-skills.sh and its tests. Changes
Sequence DiagramsequenceDiagram
participant Client as Client (non-kyber / kyber)
participant RemoteGW as Remote Gateway
participant KyberHost as Kyber Host
participant LocalGW as Local Gateway
participant Browser as Chromium
Client->>RemoteGW: HTTPS request (mode=remote) + CLAWDBOT_GATEWAY_TOKEN
RemoteGW->>KyberHost: Forward request
KyberHost->>LocalGW: Deliver to local gateway service
LocalGW->>Browser: Spawn/headless Chromium to perform task
Browser-->>LocalGW: Return result
LocalGW-->>RemoteGW: Send response
RemoteGW-->>Client: Return final response
alt host.isKyber → mode=local
Client->>LocalGW: Direct LAN request (mode=local)
LocalGW->>Browser: Invoke Chromium locally
Browser-->>LocalGW: Return result
LocalGW-->>Client: Return response
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary of ChangesHello @shunkakinoki, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a significant architectural change to Clawdbot, shifting from a distributed gateway model to a centralized hub-and-spoke design. The primary goal is to enable macOS machines to operate as remote clients, connecting to a dedicated Linux gateway. This change simplifies deployment, centralizes resource management, and enhances the overall flexibility of the Clawdbot ecosystem by clearly defining roles for different operating systems. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
Mesa DescriptionTL;DRConfigured Clawdbot for a hub-and-spoke architecture, enabling macOS nodes to connect as remotes to a Linux (kyber) gateway over WebSocket with token authentication, while also improving developer ergonomics with new Zellij, LLM, and What changed?
Description generated by Mesa. Update settings |
There was a problem hiding this comment.
Pull request overview
This PR configures Clawdbot for a hub-and-spoke architecture where a Linux machine (kyber) runs the central gateway, and macOS machines connect to it as remote nodes via WebSocket.
Changes:
- macOS nodes now connect to a remote gateway instead of running a local one
- Linux gateway exposes a bridge on LAN for node connectivity and authentication
- Gateway token extraction added to support node authentication
- Fish shell alias updated to support both macOS and Linux platforms
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| home-manager/modules/clawdbot/default.nix | Configures platform-specific gateway modes: local for Linux (kyber) with bridge enabled, remote for macOS nodes connecting via WebSocket |
| home-manager/modules/clawdbot/extract-secrets.sh | Adds extraction of CLAWDBOT_GATEWAY_TOKEN from .env for node authentication |
| home-manager/programs/fish/default.nix | Updates cliproxyapi alias to work on both macOS and Linux with platform-specific commands |
| .env.example | Documents the new CLAWDBOT_GATEWAY_TOKEN environment variable |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| neofetch = "fastfetch"; | ||
|
|
||
| cliproxyapi = "cd ~/.cli-proxy-api && /opt/homebrew/bin/cliproxyapi -config config.yaml"; | ||
| cliproxyapi = "if test (uname) = Darwin; cd ~/.cli-proxy-api && /opt/homebrew/bin/cliproxyapi -config config.yaml; else; systemctl --user restart cliproxyapi && journalctl --user -u cliproxyapi -f; end"; |
There was a problem hiding this comment.
The Fish shell conditional syntax is incorrect. The if statement needs then before each command block, and should use ; correctly. The Darwin branch is missing the then keyword and uses ; incorrectly as a separator. The correct syntax should be: if test (uname) = Darwin; then cd ~/.cli-proxy-api && /opt/homebrew/bin/cliproxyapi -config config.yaml; else systemctl --user restart cliproxyapi && journalctl --user -u cliproxyapi -f; end
| cliproxyapi = "if test (uname) = Darwin; cd ~/.cli-proxy-api && /opt/homebrew/bin/cliproxyapi -config config.yaml; else; systemctl --user restart cliproxyapi && journalctl --user -u cliproxyapi -f; end"; | |
| cliproxyapi = "if test (uname) = Darwin; then cd ~/.cli-proxy-api && /opt/homebrew/bin/cliproxyapi -config config.yaml; else systemctl --user restart cliproxyapi && journalctl --user -u cliproxyapi -f; end"; |
| # - macOS: launchd disabled (remote mode - no local gateway) | ||
| launchd.enable = false; # macOS uses remote mode, no local gateway |
There was a problem hiding this comment.
Setting launchd.enable = false unconditionally will disable the service on macOS even though the configuration includes macOS-specific remote mode settings (lines 98-109). Since macOS nodes need to run in remote mode to connect to the gateway, this should be conditional: launchd.enable = pkgs.stdenv.isDarwin; to ensure the service runs on macOS nodes.
| # - macOS: launchd disabled (remote mode - no local gateway) | |
| launchd.enable = false; # macOS uses remote mode, no local gateway | |
| # - macOS: launchd runs the node in remote mode (no local gateway) | |
| launchd.enable = pkgs.stdenv.isDarwin; |
| # Telegram token (Linux gateway only) | ||
| @grep@ -E "^CLAWDBOT_TELEGRAM_TOKEN=" "$DOTFILES_ENV" 2>/dev/null | @cut@ -d= -f2- | @tr@ -d '"' >"$CLAWDBOT_DIR/telegram-token" || true | ||
|
|
||
| # Gateway token (for remote mode clients / nodes) |
There was a problem hiding this comment.
Comment uses 'clients / nodes' with inconsistent spacing. Should be 'clients/nodes' or 'remote mode nodes' for consistency.
| # Gateway token (for remote mode clients / nodes) | |
| # Gateway token (for remote mode clients/nodes) |
There was a problem hiding this comment.
Code Review
This pull request introduces a remote mode for Clawdbot on macOS, allowing nodes to connect to a central Linux gateway. The changes are well-structured, particularly the refactoring of the Nix configuration to handle platform-specific settings for Linux and macOS. I have a few suggestions to improve maintainability and robustness. Specifically, I recommend making the remote gateway URL configurable, refactoring duplicated logic in a shell script into a helper function, and removing a hardcoded path from a fish alias to make it more portable across different Mac architectures.
|
|
||
| # Remote gateway URL for non-kyber machines (macOS nodes connect here) | ||
| # Using Tailscale MagicDNS for direct connectivity (bridge is TCP, not HTTP) | ||
| remoteGatewayUrl = "ws://kyber.tail950b36.ts.net:18789"; |
There was a problem hiding this comment.
The remoteGatewayUrl is hardcoded with a specific Tailscale MagicDNS name. This makes the configuration less flexible and harder to maintain if the URL changes or for use in different environments. Consider making this a configurable module option with the current value as its default. This would allow it to be easily overridden when needed.
| configOverrides = | ||
| # Linux (kyber): Local gateway mode with browser + bridge for nodes | ||
| lib.optionalAttrs pkgs.stdenv.isLinux { | ||
| gateway = { | ||
| mode = "local"; | ||
| bind = "lan"; | ||
| }; | ||
| bridge = { | ||
| enabled = true; | ||
| bind = "lan"; # Allow nodes to connect from LAN/ingress | ||
| }; | ||
| browser = { | ||
| enabled = true; | ||
| headless = true; | ||
| executablePath = "${pkgs.chromium}/bin/chromium"; | ||
| noSandbox = true; # SUID sandbox requires root-owned binary with mode 4755 | ||
| }; | ||
| } | ||
| // lib.optionalAttrs pkgs.stdenv.isLinux { | ||
| executablePath = "${pkgs.chromium}/bin/chromium"; | ||
| noSandbox = true; # SUID sandbox requires root-owned binary with mode 4755 | ||
| }; | ||
| } | ||
| // lib.optionalAttrs pkgs.stdenv.isLinux { | ||
| gateway = { | ||
| bind = "lan"; | ||
| # macOS: Remote mode - connect to Linux gateway as a node | ||
| // lib.optionalAttrs pkgs.stdenv.isDarwin { | ||
| gateway = { | ||
| mode = "remote"; | ||
| url = remoteGatewayUrl; | ||
| # Auth token read from file (set via extract-secrets or manually) | ||
| tokenFile = "${clawdbotDir}/gateway-token"; | ||
| }; | ||
| browser = { | ||
| enabled = true; | ||
| headless = false; | ||
| }; | ||
| }; |
There was a problem hiding this comment.
The refactoring of configOverrides is a great improvement for clarity and platform separation. However, there's some duplication in the browser configuration. Both the Linux and macOS blocks define browser.enabled = true;. You could pull this common setting out and define it once before the platform-specific lib.optionalAttrs blocks, then merge it with the platform-specific overrides. This would make the configuration slightly more DRY.
configOverrides = {
browser = {
enabled = true;
};
} //
# Linux (kyber): Local gateway mode with browser + bridge for nodes
lib.optionalAttrs pkgs.stdenv.isLinux {
gateway = {
mode = "local";
bind = "lan";
};
bridge = {
enabled = true;
bind = "lan"; # Allow nodes to connect from LAN/ingress
};
browser = {
headless = true;
executablePath = "${pkgs.chromium}/bin/chromium";
noSandbox = true; # SUID sandbox requires root-owned binary with mode 4755
};
}
# macOS: Remote mode - connect to Linux gateway as a node
// lib.optionalAttrs pkgs.stdenv.isDarwin {
gateway = {
mode = "remote";
url = remoteGatewayUrl;
# Auth token read from file (set via extract-secrets or manually)
tokenFile = "${clawdbotDir}/gateway-token";
};
browser = {
headless = false;
};
};
| # Telegram token (Linux gateway only) | ||
| @grep@ -E "^CLAWDBOT_TELEGRAM_TOKEN=" "$DOTFILES_ENV" 2>/dev/null | @cut@ -d= -f2- | @tr@ -d '"' >"$CLAWDBOT_DIR/telegram-token" || true | ||
|
|
||
| # Gateway token (for remote mode clients / nodes) | ||
| @grep@ -E "^CLAWDBOT_GATEWAY_TOKEN=" "$DOTFILES_ENV" 2>/dev/null | @cut@ -d= -f2- | @tr@ -d '"' >"$CLAWDBOT_DIR/gateway-token" || true |
There was a problem hiding this comment.
The pipeline to extract secrets from the .env file is repeated here. This pattern is also used later in the script for CLAWDBOT_ANTHROPIC_KEY. To improve maintainability and reduce code duplication, you could define a helper function at the top of the script. For example:
extract_secret() {
local key="$1"
local outfile="$2"
@grep@ -E "^${key}=" "$DOTFILES_ENV" 2>/dev/null | @cut@ -d= -f2- | @tr@ -d '"' >"$outfile" || true
}Then you can replace the repeated pipelines with calls to this function.
| # Telegram token (Linux gateway only) | |
| @grep@ -E "^CLAWDBOT_TELEGRAM_TOKEN=" "$DOTFILES_ENV" 2>/dev/null | @cut@ -d= -f2- | @tr@ -d '"' >"$CLAWDBOT_DIR/telegram-token" || true | |
| # Gateway token (for remote mode clients / nodes) | |
| @grep@ -E "^CLAWDBOT_GATEWAY_TOKEN=" "$DOTFILES_ENV" 2>/dev/null | @cut@ -d= -f2- | @tr@ -d '"' >"$CLAWDBOT_DIR/gateway-token" || true | |
| # Telegram token (Linux gateway only) | |
| extract_secret "CLAWDBOT_TELEGRAM_TOKEN" "$CLAWDBOT_DIR/telegram-token" | |
| # Gateway token (for remote mode clients / nodes) | |
| extract_secret "CLAWDBOT_GATEWAY_TOKEN" "$CLAWDBOT_DIR/gateway-token" |
| neofetch = "fastfetch"; | ||
|
|
||
| cliproxyapi = "cd ~/.cli-proxy-api && /opt/homebrew/bin/cliproxyapi -config config.yaml"; | ||
| cliproxyapi = "if test (uname) = Darwin; cd ~/.cli-proxy-api && /opt/homebrew/bin/cliproxyapi -config config.yaml; else; systemctl --user restart cliproxyapi && journalctl --user -u cliproxyapi -f; end"; |
There was a problem hiding this comment.
The alias for cliproxyapi on Darwin uses a hardcoded path /opt/homebrew/bin/cliproxyapi. This is not robust as it's specific to Apple Silicon Macs. For Intel Macs, the Homebrew path is different (/usr/local/bin). Since your fish configuration adds the Homebrew bin directory to the PATH, you can rely on the shell to find the executable. Removing the hardcoded path will make the alias work correctly on any Mac architecture.
cliproxyapi = "if test (uname) = Darwin; cd ~/.cli-proxy-api && cliproxyapi -config config.yaml; else; systemctl --user restart cliproxyapi && journalctl --user -u cliproxyapi -f; end";
There was a problem hiding this comment.
1 issue found across 4 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="home-manager/modules/clawdbot/default.nix">
<violation number="1" location="home-manager/modules/clawdbot/default.nix:74">
P1: macOS service disabled: launchd is set to false, leaving macOS nodes with no daemon to connect to the remote gateway, breaking remote mode unless started manually</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @.env.example:
- Around line 12-13: Reorder the two env entries so CLAWDBOT_GATEWAY_TOKEN
appears before CLAWDBOT_TELEGRAM_TOKEN to satisfy dotenv-linter; locate the
CLAWDBOT_GATEWAY_TOKEN and CLAWDBOT_TELEGRAM_TOKEN keys in the .env example,
move the CLAWDBOT_GATEWAY_TOKEN block (including its comment) above the
CLAWDBOT_TELEGRAM_TOKEN entry, and keep surrounding formatting and comments
intact.
🧹 Nitpick comments (3)
home-manager/programs/fish/default.nix (1)
55-56: Add a short note for the new platform-specific behavior.The alias now restarts a service + tails logs on Linux but runs a local CLI on macOS. A brief comment here would make the behavior explicit for future edits.
📝 Suggested inline documentation
- cliproxyapi = "if test (uname) = Darwin; cd ~/.cli-proxy-api && /opt/homebrew/bin/cliproxyapi -config config.yaml; else; systemctl --user restart cliproxyapi && journalctl --user -u cliproxyapi -f; end"; + # Platform-aware: macOS runs local CLI; Linux restarts user service and tails logs + cliproxyapi = "if test (uname) = Darwin; cd ~/.cli-proxy-api && /opt/homebrew/bin/cliproxyapi -config config.yaml; else; systemctl --user restart cliproxyapi && journalctl --user -u cliproxyapi -f; end";Based on learnings, document all major changes in Nix configurations.
home-manager/modules/clawdbot/default.nix (2)
12-15: Consider makingremoteGatewayUrla configurable option.Hard-coding the gateway URL makes host overrides harder. Exposing it via
mkOption(with a default) would keep per-host customization out of the module body.As per coding guidelines, use
mkOptionfor configurable options in Nix modules.
90-95: Expand the inline comment to better document headless mode as a security control and the gateway's threat model.The existing comment correctly explains why the SUID sandbox is unavailable ("SUID sandbox requires root-owned binary with mode 4755"), which is a known Nix packaging constraint. However, the comment would be more helpful if it also noted that headless mode significantly reduces the attack surface compared to a GUI browser, and that this gateway runs in a private, controlled environment (accessed via Tailscale). Consider updating the comment to:
noSandbox = true; # SUID sandbox requires root-owned binary with mode 4755. # Headless mode + private gateway mitigate sandbox absence.This documents the architectural context—headless rendering eliminates JavaScript-in-GUI attacks, and the gateway is not exposed to arbitrary web content—making the risk assessment clearer to future maintainers.
| # Gateway token for remote mode clients (from kyber: cat ~/.config/clawdbot/gateway-token) | ||
| CLAWDBOT_GATEWAY_TOKEN=your-gateway-token-here |
There was a problem hiding this comment.
Align key order with dotenv-linter.
The linter expects CLAWDBOT_GATEWAY_TOKEN before CLAWDBOT_TELEGRAM_TOKEN.
💡 Suggested reorder
-# Get Telegram bot token from `@BotFather`: https://t.me/BotFather
-CLAWDBOT_TELEGRAM_TOKEN=123456789:ABCdefGHIjklMNOpqrsTUVwxyz
-# Get your Anthropic API key from: https://console.anthropic.com/
-CLAWDBOT_ANTHROPIC_KEY=sk-ant-api03-your-key-here
-# Gateway token for remote mode clients (from kyber: cat ~/.config/clawdbot/gateway-token)
-CLAWDBOT_GATEWAY_TOKEN=your-gateway-token-here
+# Gateway token for remote mode clients (from kyber: cat ~/.config/clawdbot/gateway-token)
+CLAWDBOT_GATEWAY_TOKEN=your-gateway-token-here
+# Get Telegram bot token from `@BotFather`: https://t.me/BotFather
+CLAWDBOT_TELEGRAM_TOKEN=123456789:ABCdefGHIjklMNOpqrsTUVwxyz
+# Get your Anthropic API key from: https://console.anthropic.com/
+CLAWDBOT_ANTHROPIC_KEY=sk-ant-api03-your-key-here🧰 Tools
🪛 dotenv-linter (4.0.0)
[warning] 13-13: [UnorderedKey] The CLAWDBOT_GATEWAY_TOKEN key should go before the CLAWDBOT_TELEGRAM_TOKEN key
(UnorderedKey)
🤖 Prompt for AI Agents
In @.env.example around lines 12 - 13, Reorder the two env entries so
CLAWDBOT_GATEWAY_TOKEN appears before CLAWDBOT_TELEGRAM_TOKEN to satisfy
dotenv-linter; locate the CLAWDBOT_GATEWAY_TOKEN and CLAWDBOT_TELEGRAM_TOKEN
keys in the .env example, move the CLAWDBOT_GATEWAY_TOKEN block (including its
comment) above the CLAWDBOT_TELEGRAM_TOKEN entry, and keep surrounding
formatting and comments intact.
2fcbd01 to
ceb652d
Compare
- Configure macOS as remote nodes connecting to kyber gateway via WebSocket - Add gateway token extraction from .env for node authentication - Enable bridge on Linux gateway (bind to LAN) for node connectivity - Fix cliproxyapi fish alias to work on both macOS and Linux
There was a problem hiding this comment.
1 issue found across 3 files (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="home-manager/programs/fish/functions/_cliproxyapi_function.fish">
<violation number="1" location="home-manager/programs/fish/functions/_cliproxyapi_function.fish:3">
P2: Mac helper hard-codes `/opt/homebrew/bin/cliproxyapi`, so it fails on Intel macOS or other brew prefixes; should probe PATH or include `/usr/local/bin` fallback.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| @@ -0,0 +1,7 @@ | |||
| function _cliproxyapi_function --description "Start cliproxyapi service" | |||
| if test (uname) = Darwin | |||
| cd ~/.cli-proxy-api && /opt/homebrew/bin/cliproxyapi -config config.yaml | |||
There was a problem hiding this comment.
P2: Mac helper hard-codes /opt/homebrew/bin/cliproxyapi, so it fails on Intel macOS or other brew prefixes; should probe PATH or include /usr/local/bin fallback.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At home-manager/programs/fish/functions/_cliproxyapi_function.fish, line 3:
<comment>Mac helper hard-codes `/opt/homebrew/bin/cliproxyapi`, so it fails on Intel macOS or other brew prefixes; should probe PATH or include `/usr/local/bin` fallback.</comment>
<file context>
@@ -0,0 +1,7 @@
+function _cliproxyapi_function --description "Start cliproxyapi service"
+ if test (uname) = Darwin
+ cd ~/.cli-proxy-api && /opt/homebrew/bin/cliproxyapi -config config.yaml
+ else
+ systemctl --user restart cliproxyapi && journalctl --user -u cliproxyapi -f
</file context>
…e-mode-nodes' into feat/clawdbot-remote-mode-nodes
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="config/llm/default.nix">
<violation number="1" location="config/llm/default.nix:4">
P1: home.file entries use unsupported `enable` option, causing Home‑Manager evaluation failure</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| { config, pkgs, ... }: | ||
| { | ||
| home.file."Library/Application Support/io.datasette.llm/extra-openai-models.yaml" = { | ||
| enable = pkgs.stdenv.isDarwin; |
There was a problem hiding this comment.
P1: home.file entries use unsupported enable option, causing Home‑Manager evaluation failure
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At config/llm/default.nix, line 4:
<comment>home.file entries use unsupported `enable` option, causing Home‑Manager evaluation failure</comment>
<file context>
@@ -1,6 +1,13 @@
+{ config, pkgs, ... }:
{
+ home.file."Library/Application Support/io.datasette.llm/extra-openai-models.yaml" = {
+ enable = pkgs.stdenv.isDarwin;
+ source = config.lib.file.mkOutOfStoreSymlink ./extra-openai-models.yaml;
+ force = true;
</file context>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@config/llm/extra-openai-models.yaml`:
- Around line 4-22: Update the claude-haiku model entry to use the correct
Anthropic model identifier by replacing model_name value for the entry with
model_id "claude-haiku" to "claude-3-5-haiku-20241022"; then reorder the keys
for every model block (entries with model_id values "cliproxyapi",
"claude-sonnet", "claude-opus", and "claude-haiku") so they are alphabetically
sorted as api_base, api_key_name, model_id, model_name.
♻️ Duplicate comments (1)
config/llm/default.nix (1)
3-13:home.filedoes not support theenableoption — uselib.mkIffor conditional file entries.The
enableattribute is not a valid option forhome.file.<name>in home-manager. This will cause an evaluation failure. Uselib.mkIfto conditionally define each file entry instead.🔧 Proposed fix using lib.mkIf
-{ config, pkgs, ... }: +{ config, lib, pkgs, ... }: { - home.file."Library/Application Support/io.datasette.llm/extra-openai-models.yaml" = { - enable = pkgs.stdenv.isDarwin; + home.file."Library/Application Support/io.datasette.llm/extra-openai-models.yaml" = lib.mkIf pkgs.stdenv.isDarwin { source = config.lib.file.mkOutOfStoreSymlink ./extra-openai-models.yaml; force = true; }; - home.file.".config/io.datasette.llm/extra-openai-models.yaml" = { - enable = pkgs.stdenv.isLinux; + home.file.".config/io.datasette.llm/extra-openai-models.yaml" = lib.mkIf pkgs.stdenv.isLinux { source = config.lib.file.mkOutOfStoreSymlink ./extra-openai-models.yaml; force = true; }; }
🧹 Nitpick comments (1)
lib/host.nix (1)
10-16: Consider reusingisKyberandisGalacticainnodeNameto reduce duplication.The
nodeNameattribute duplicates the hostname checks already defined inisKyberandisGalactica. Reusing these values improves maintainability.♻️ Proposed refactor
+let + isKyber = builtins.getEnv "HOSTNAME" == "kyber" || builtins.getEnv "HOST" == "kyber"; + isGalactica = builtins.getEnv "HOSTNAME" == "galactica" || builtins.getEnv "HOST" == "galactica"; +in { - # Detect if running on kyber (requires --impure flag, which Makefile already uses) - isKyber = builtins.getEnv "HOSTNAME" == "kyber" || builtins.getEnv "HOST" == "kyber"; - - # Detect if running on galactica (macOS node) - isGalactica = builtins.getEnv "HOSTNAME" == "galactica" || builtins.getEnv "HOST" == "galactica"; + # Detect if running on kyber (requires --impure flag, which Makefile already uses) + inherit isKyber; + + # Detect if running on galactica (macOS node) + inherit isGalactica; # Get the node name for clawdbot remote mode # Falls back to "unknown" if no hostname is detected nodeName = - if builtins.getEnv "HOSTNAME" == "kyber" || builtins.getEnv "HOST" == "kyber" then - "kyber" - else if builtins.getEnv "HOSTNAME" == "galactica" || builtins.getEnv "HOST" == "galactica" then - "galactica" + if isKyber then "kyber" + else if isGalactica then "galactica" else "unknown"; }
| - model_id: cliproxyapi | ||
| model_name: claude-sonnet-4-20250514 | ||
| api_base: http://localhost:8317/v1 | ||
| api_key_name: cliproxyapi | ||
|
|
||
| - model_id: claude-sonnet | ||
| model_name: claude-sonnet-4-20250514 | ||
| api_base: http://localhost:8317/v1 | ||
| api_key_name: cliproxyapi | ||
|
|
||
| - model_id: claude-opus | ||
| model_name: claude-opus-4-20250514 | ||
| api_base: http://localhost:8317/v1 | ||
| api_key_name: cliproxyapi | ||
|
|
||
| - model_id: claude-haiku | ||
| model_name: claude-haiku-4-20250514 | ||
| api_base: http://localhost:8317/v1 | ||
| api_key_name: cliproxyapi |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Anthropic Claude model identifiers 2026 latest available models
💡 Result:
Here are Anthropic’s latest Claude model identifiers (API model IDs) as of Jan 17, 2026:
- Claude Opus 4.1 — claude-opus-4-1-20250805 (alias: claude-opus-4-1). [1]
- Claude Opus 4 — claude-opus-4-20250514 (alias: claude-opus-4-0 / claude-opus-4). [1][2]
- Claude Sonnet 4 — claude-sonnet-4-20250514 (alias: claude-sonnet-4-0 / claude-sonnet-4). [1][2]
- Older/maintained models (examples): claude-3-7-sonnet-20250219 (alias: claude-3-7-sonnet-latest), claude-3-5-sonnet-20241022, claude-3-5-haiku-20241022. Use the Models list endpoint to see models available to your account. [1][2]
Recommendation: use the full snapshot ID (e.g., claude-opus-4-1-20250805) in production for stable behavior; aliases point to the latest snapshot. [1]
Sources: Anthropic docs (Models overview & API model examples). [1][2]
Fix invalid model identifier and sort configuration keys alphabetically.
The configuration has two issues:
-
Invalid model identifier:
claude-haiku-4-20250514is not a valid Anthropic model. The current Claude Haiku model isclaude-3-5-haiku-20241022. Update the haiku entry accordingly. -
Keys not alphabetically sorted: Per coding guidelines, configuration keys should be sorted alphabetically. Reorder each model entry to:
api_base,api_key_name,model_id,model_name.
The model identifiers for Sonnet and Opus are correct: claude-sonnet-4-20250514 and claude-opus-4-20250514 are valid Anthropic models as of January 2026.
🤖 Prompt for AI Agents
In `@config/llm/extra-openai-models.yaml` around lines 4 - 22, Update the
claude-haiku model entry to use the correct Anthropic model identifier by
replacing model_name value for the entry with model_id "claude-haiku" to
"claude-3-5-haiku-20241022"; then reorder the keys for every model block
(entries with model_id values "cliproxyapi", "claude-sonnet", "claude-opus", and
"claude-haiku") so they are alphabetically sorted as api_base, api_key_name,
model_id, model_name.
…me under 'remote' key
…me under 'remote' key
…de and fix app package version
… symlink installation script for macOS
…cture for Clawdbot.app
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
spec/coverage_spec.sh (1)
96-116: Add spec assertions for newly listed cliproxyapi scripts.Line 96 adds
backup.sh,hydrate.sh, andwrapper.shto the covered list, but the earlier “all required scripts have spec files” section doesn’t assert specs for them. That lets missing specs slip through, which contradicts the coverage intent. Please add corresponding spec checks (or remove them from the covered list if they’re exempt).Proposed update (adjust spec filenames as appropriate)
+It 'has spec file for home-manager/services/cliproxyapi/scripts/backup.sh' +The path "spec/cliproxyapi_backup_spec.sh" should be exist +End + +It 'has spec file for home-manager/services/cliproxyapi/scripts/hydrate.sh' +The path "spec/cliproxyapi_hydrate_spec.sh" should be exist +End + +It 'has spec file for home-manager/services/cliproxyapi/scripts/wrapper.sh' +The path "spec/cliproxyapi_wrapper_spec.sh" should be exist +End
🤖 Fix all issues with AI agents
In `@home-manager/modules/clawdbot/default.nix`:
- Around line 65-79: The launchd job launchd.agents.clawdbot-app currently
hardcodes ProgramArguments to
"/Applications/Clawdbot.app/Contents/MacOS/Clawdbot" which will cause repeated
launch attempts if the app isn't present; change the job to either add a
StartInterval (e.g., short retry delay) and/or point ProgramArguments to a small
wrapper script (create a wrapper that checks for the app binary existence and
exits quietly if missing, then execs the real binary when present) and update
Label/ProgramArguments to call that wrapper so launchd doesn't repeatedly spawn
a failing binary.
♻️ Duplicate comments (1)
home-manager/modules/clawdbot/default.nix (1)
13-16: Hardcoded Tailscale URL reduces flexibility.The
remoteGatewayUrlis hardcoded with a specific Tailscale MagicDNS hostname. Consider making this a module option with this value as the default, allowing easier overrides for different environments or network configurations.
🧹 Nitpick comments (2)
overlays/default.nix (1)
37-65: Consider adding error handling for thefindcommand edge case.The
findcommand with-quitis good for performance, but the error message could be more informative by listing what was found. Also,dontUnpack = trueis set butsrcis already fetched viafetchzip, which auto-unpacks — this is correct but the combination might be slightly confusing.♻️ Optional: More informative error message
app_path="$(find "$src" -maxdepth 2 -name '*.app' -print -quit)" if [ -z "$app_path" ]; then - echo "Clawdbot.app not found in $src" >&2 + echo "Clawdbot.app not found in $src. Contents:" >&2 + ls -la "$src" >&2 exit 1 fihome-manager/modules/clawdbot/default.nix (1)
48-63: Potential silent failure when config file doesn't exist initially.The activation script checks for both
TOKEN_FILEandCONFIG_FILE, but ifCONFIG_FILEdoesn't exist (e.g., first run before clawdbot creates it), the token injection silently skips. Consider logging when files are missing for debugging.♻️ Optional: Add debug logging for missing files
if [ -f "$TOKEN_FILE" ] && [ -f "$CONFIG_FILE" ]; then TOKEN=$(${pkgs.coreutils}/bin/cat "$TOKEN_FILE" | ${pkgs.coreutils}/bin/tr -d '\n') # Inject token into gateway.remote.token and remove tokenFile ${pkgs.jq}/bin/jq --arg token "$TOKEN" \ '.gateway.remote.token = $token | del(.gateway.remote.tokenFile)' \ "$CONFIG_FILE" > "$CONFIG_FILE.tmp" && \ ${pkgs.coreutils}/bin/mv "$CONFIG_FILE.tmp" "$CONFIG_FILE" echo "Injected gateway token into clawdbot config" + else + [ ! -f "$TOKEN_FILE" ] && echo "Skipping token injection: $TOKEN_FILE not found" + [ ! -f "$CONFIG_FILE" ] && echo "Skipping token injection: $CONFIG_FILE not found" fi
| # Auto-start Clawdbot.app on login (galactica only) | ||
| # App is installed to /Applications/Nix Apps/ via nix-darwin | ||
| launchd.agents.clawdbot-app = lib.mkIf (pkgs.stdenv.isDarwin && host.isGalactica) { | ||
| enable = true; | ||
| config = { | ||
| Label = "com.clawdbot.app"; | ||
| ProgramArguments = [ | ||
| "/Applications/Clawdbot.app/Contents/MacOS/Clawdbot" | ||
| ]; | ||
| RunAtLoad = true; | ||
| KeepAlive = false; | ||
| StandardOutPath = "/tmp/clawdbot-app.log"; | ||
| StandardErrorPath = "/tmp/clawdbot-app.error.log"; | ||
| }; | ||
| }; |
There was a problem hiding this comment.
Hardcoded app path assumes nix-darwin installation location.
The ProgramArguments uses /Applications/Clawdbot.app/... which depends on the nix-darwin post-activation script successfully copying the app. If that fails, launchd will repeatedly try to start a non-existent binary. Consider adding a StartInterval with a delay or a wrapper script that checks existence first.
🤖 Prompt for AI Agents
In `@home-manager/modules/clawdbot/default.nix` around lines 65 - 79, The launchd
job launchd.agents.clawdbot-app currently hardcodes ProgramArguments to
"/Applications/Clawdbot.app/Contents/MacOS/Clawdbot" which will cause repeated
launch attempts if the app isn't present; change the job to either add a
StartInterval (e.g., short retry delay) and/or point ProgramArguments to a small
wrapper script (create a wrapper that checks for the app binary existence and
exits quietly if missing, then execs the real binary when present) and update
Label/ProgramArguments to call that wrapper so launchd doesn't repeatedly spawn
a failing binary.
Summary
Configure Clawdbot for a hub-and-spoke architecture where Linux (kyber) runs the gateway and macOS machines connect as remote nodes.
Changes
Summary by cubic
Add remote mode so macOS nodes connect to the kyber gateway over WebSocket. Kyber runs the gateway with a LAN bridge and token auth.
New Features
Migration
Written for commit 218cd74. Summary will update on new commits.