diff --git a/config/codex/config.toml b/config/codex/config.toml index 7d6f158ae..6b436a083 100644 --- a/config/codex/config.toml +++ b/config/codex/config.toml @@ -5,6 +5,7 @@ notify = [ "notify", ] +suppress_unstable_features_warning = true web_search = "cached" [features] diff --git a/config/codex/default.nix b/config/codex/default.nix index 1f93cf834..da6b08e1e 100644 --- a/config/codex/default.nix +++ b/config/codex/default.nix @@ -1,7 +1,9 @@ -{ config, ... }: +{ config, lib, ... }: { - home.file.".codex/config.toml" = { - source = ./config.toml; - force = true; - }; + # Use activation script instead of home.file symlink + # Codex CLI uses atomic writes that break symlinks, so we force-copy on each switch + home.activation.codexConfig = lib.hm.dag.entryAfter [ "writeBoundary" ] '' + $DRY_RUN_CMD cp -f ${./config.toml} ~/.codex/config.toml + $DRY_RUN_CMD chmod 600 ~/.codex/config.toml + ''; } diff --git a/config/default.nix b/config/default.nix index 18708a6f5..093ad5335 100644 --- a/config/default.nix +++ b/config/default.nix @@ -16,6 +16,7 @@ ./k3s ./karabiner ./llm + ./openclaw ./opencode ./pi ./serena diff --git a/config/openclaw/default.nix b/config/openclaw/default.nix new file mode 100644 index 000000000..fea48f17b --- /dev/null +++ b/config/openclaw/default.nix @@ -0,0 +1,33 @@ +{ + config, + lib, + pkgs, + inputs, + ... +}: +let + inherit (inputs) host; + homeDir = config.home.homeDirectory; + + mode = if host.isKyber then "gateway" else "client"; + + hydrateScript = pkgs.replaceVars ./hydrate.sh ({ + sed = "${pkgs.gnused}/bin/sed"; + template = ./openclaw.template.json; + inherit mode; + } // (if host.isKyber then { + chromium = pkgs.chromium; + openclaw = "${homeDir}/.bun"; + } else { + chromium = "/unused"; + openclaw = "/unused"; + })); +in +{ + # Hydrate OpenClaw config from .env secrets + # Gateway mode on Kyber, client mode everywhere else + home.activation.hydrateOpenclawConfig = config.lib.dag.entryAfter [ "writeBoundary" ] '' + mkdir -p ${homeDir}/.openclaw + ${pkgs.bash}/bin/bash ${hydrateScript} || true + ''; +} diff --git a/config/openclaw/hydrate.sh b/config/openclaw/hydrate.sh index 62b230499..b666eb1ee 100644 --- a/config/openclaw/hydrate.sh +++ b/config/openclaw/hydrate.sh @@ -1,13 +1,14 @@ #!/usr/bin/env bash -# OpenClaw gateway start script with runtime secret injection +# OpenClaw config hydration with runtime secret injection +# Mode is set by nix: "gateway" for Kyber, "client" for macOS # shellcheck source=/dev/null set -euo pipefail +MODE="@mode@" STATE_DIR="${OPENCLAW_STATE_DIR:-${HOME}/.openclaw}" CONFIG="${OPENCLAW_CONFIG_PATH:-${STATE_DIR}/openclaw.json}" TEMPLATE="@template@" SECRETS_DIR="${HOME}/.config/openclaw" -LEGACY_SECRETS_DIR="${HOME}/.config/clawdbot" ENV_FILE="${HOME}/dotfiles/.env" # Source .env if it exists @@ -29,33 +30,53 @@ read_secret() { echo "" } -# Load secrets from files or environment -CLIPROXY_API_KEY="${OPENCLAW_CLIPROXY_API_KEY:-${CLIPROXY_API_KEY:-$(read_secret "${SECRETS_DIR}/cliproxy-key" "${LEGACY_SECRETS_DIR}/cliproxy-key")}}" -TELEGRAM_TOKEN="${OPENCLAW_TELEGRAM_TOKEN:-${TELEGRAM_TOKEN:-$(read_secret "${SECRETS_DIR}/telegram-token" "${LEGACY_SECRETS_DIR}/telegram-token")}}" -GATEWAY_TOKEN="${OPENCLAW_GATEWAY_TOKEN:-${GATEWAY_TOKEN:-$(read_secret "${SECRETS_DIR}/gateway-token" "${LEGACY_SECRETS_DIR}/gateway-token")}}" -ANTHROPIC_API_KEY="${OPENCLAW_ANTHROPIC_API_KEY:-${ANTHROPIC_API_KEY:-$(read_secret "${SECRETS_DIR}/anthropic-key" "${LEGACY_SECRETS_DIR}/anthropic-key")}}" +# Load gateway token (required for both modes) +GATEWAY_TOKEN="${OPENCLAW_GATEWAY_TOKEN:-${GATEWAY_TOKEN:-$(read_secret "${SECRETS_DIR}/gateway-token")}}" -# Chromium path (injected by nix) -CHROMIUM_PATH="@chromium@/bin/chromium" +if [ -z "${GATEWAY_TOKEN}" ]; then + echo "Warning: OPENCLAW_GATEWAY_TOKEN not set, skipping OpenClaw hydration" >&2 + exit 0 +fi -# Create state directory if needed mkdir -p "$STATE_DIR" -# Generate config from template with secret substitution -@sed@ \ - -e "s|__CLIPROXY_API_KEY__|${CLIPROXY_API_KEY}|g" \ - -e "s|__TELEGRAM_TOKEN__|${TELEGRAM_TOKEN}|g" \ - -e "s|__GATEWAY_TOKEN__|${GATEWAY_TOKEN}|g" \ - -e "s|__CHROMIUM_PATH__|${CHROMIUM_PATH}|g" \ - -e "s|__HOME__|${HOME}|g" \ - "$TEMPLATE" >"$CONFIG" +if [ "$MODE" = "gateway" ]; then + # Gateway mode (Kyber): hydrate full template and start gateway + CLIPROXY_API_KEY="${OPENCLAW_CLIPROXY_API_KEY:-${CLIPROXY_API_KEY:-$(read_secret "${SECRETS_DIR}/cliproxy-key")}}" + TELEGRAM_TOKEN="${OPENCLAW_TELEGRAM_TOKEN:-${TELEGRAM_TOKEN:-$(read_secret "${SECRETS_DIR}/telegram-token")}}" + ANTHROPIC_API_KEY="${OPENCLAW_ANTHROPIC_API_KEY:-${ANTHROPIC_API_KEY:-$(read_secret "${SECRETS_DIR}/anthropic-key")}}" + CHROMIUM_PATH="@chromium@/bin/chromium" -echo "Generated openclaw config at $CONFIG" >&2 + @sed@ \ + -e "s|__CLIPROXY_API_KEY__|${CLIPROXY_API_KEY}|g" \ + -e "s|__TELEGRAM_TOKEN__|${TELEGRAM_TOKEN}|g" \ + -e "s|__GATEWAY_TOKEN__|${GATEWAY_TOKEN}|g" \ + -e "s|__CHROMIUM_PATH__|${CHROMIUM_PATH}|g" \ + -e "s|__HOME__|${HOME}|g" \ + "$TEMPLATE" >"$CONFIG" -# Export Anthropic API key for OpenClaw -if [ -n "$ANTHROPIC_API_KEY" ]; then - export ANTHROPIC_API_KEY -fi + echo "Generated openclaw gateway config at $CONFIG" >&2 + + if [ -n "$ANTHROPIC_API_KEY" ]; then + export ANTHROPIC_API_KEY + fi + + exec @openclaw@/bin/openclaw gateway --port 18789 "$@" -# Start OpenClaw gateway -exec @openclaw@/bin/openclaw gateway --port 18789 "$@" +else + # Client mode (macOS): generate remote config with gateway token + cat >"$CONFIG" <&2 +fi diff --git a/dotagents b/dotagents index 565bbe58d..25b1492f4 160000 --- a/dotagents +++ b/dotagents @@ -1 +1 @@ -Subproject commit 565bbe58ded57d48484c1e81c9731085761e66b8 +Subproject commit 25b1492f463c3ea56609315e786f2530771a71ca diff --git a/home-manager/modules/openclaw/default.nix b/home-manager/modules/openclaw/default.nix index c0d0823ba..e7c52c1bf 100644 --- a/home-manager/modules/openclaw/default.nix +++ b/home-manager/modules/openclaw/default.nix @@ -11,7 +11,6 @@ in # Only enable on kyber (gateway host) lib.mkIf (host.isKyber) { # Ensure OpenClaw directories exist - # Note: Node symlink is managed by fnm module home.activation.openclawSetup = config.lib.dag.entryAfter [ "writeBoundary" ] '' mkdir -p /tmp/openclaw mkdir -p ${homeDir}/.openclaw diff --git a/nix-darwin/config/dock.nix b/nix-darwin/config/dock.nix index 10f71d3fc..395973c55 100644 --- a/nix-darwin/config/dock.nix +++ b/nix-darwin/config/dock.nix @@ -30,6 +30,7 @@ "/Applications/Ghostty.app" "/Applications/Linear.app" "/Applications/Tailscale.app" + "/Applications/Codex.app" "/Applications/Cursor.app" "/Applications/Visual Studio Code.app" "/Applications/Visual Studio Code - Insiders.app" diff --git a/spec/openclaw_hydrate_spec.sh b/spec/openclaw_hydrate_spec.sh index 0d0bd371b..429ef7f70 100644 --- a/spec/openclaw_hydrate_spec.sh +++ b/spec/openclaw_hydrate_spec.sh @@ -27,11 +27,6 @@ When run bash -c "grep 'SECRETS_DIR=' '$SCRIPT'" The output should include '.config/openclaw' End -It 'keeps legacy ~/.config/clawdbot fallback' -When run bash -c "grep 'LEGACY_SECRETS_DIR=' '$SCRIPT'" -The output should include '.config/clawdbot' -End - It 'reads from dotfiles .env file' When run bash -c "grep 'ENV_FILE=' '$SCRIPT'" The output should include 'dotfiles/.env'