Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
c0e7856
feat(ssh-agent): add configuration for keychain integration on Linux
shunkakinoki Dec 16, 2025
0ad2cb4
fix(ssh-agent): remove Linux keychain configuration
shunkakinoki Dec 16, 2025
9aba5aa
fix(default.nix): reorder brewUpgrader import for consistency
shunkakinoki Dec 16, 2025
6a5b1e7
fix(default.nix): correct ssh-agent import to include pkgs inheritance
shunkakinoki Dec 16, 2025
abf72eb
fix(default.nix): correct yek configuration from programs to modules
shunkakinoki Dec 16, 2025
37f5b01
fix(services): update imports to include config and lib in ssh-agent
shunkakinoki Dec 16, 2025
75b33e7
fix(cliproxyapi): add Z.ai API configuration with models and keys
shunkakinoki Dec 16, 2025
4a3cd99
fix(opencode): add Z-AI model configuration and API key options
shunkakinoki Dec 16, 2025
afbcf7a
fix(cliproxyapi): enhance config generation by adding ZAI API key sub…
shunkakinoki Dec 16, 2025
3523468
chore: update
shunkakinoki Dec 19, 2025
dc1fb7b
Merge branch 'main' into fix-services-imports
shunkakinoki Dec 21, 2025
7c93d29
fix(pushover): improve hostname retrieval for notifications
shunkakinoki Dec 21, 2025
82dda83
fix(scripts): align AWS command formatting in backup and recovery scr…
shunkakinoki Dec 21, 2025
9a5171f
fix(scripts): quote variable expansion in directory checks for backup…
shunkakinoki Dec 21, 2025
4cf0463
feat(cliproxyapi): add backup and recovery scripts with corresponding…
shunkakinoki Dec 21, 2025
f984462
refactor(tests): simplify setup and cleanup for extension filtering a…
shunkakinoki Dec 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions config/claude/pushover.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ fi
# Read JSON input from stdin
input=$(cat)

# Get hostname for notifications (with fallback)
HOSTNAME=$(hostname -s 2>/dev/null || echo "unknown")
# Get Computer Name for notifications (macOS friendly name, fallback to hostname)
HOSTNAME=$(scutil --get ComputerName 2>/dev/null || hostname -s 2>/dev/null || echo "unknown")
if [ -z "$HOSTNAME" ]; then
HOSTNAME="unknown"
fi
Expand Down
6 changes: 6 additions & 0 deletions config/cliproxyapi/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ openai-compatibility:
- "__OPENROUTER_API_KEY__"
models:
- name: "z-ai/glm-4.6"
- name: "z-ai"
base-url: "https://api.z.ai/api/coding/paas/v4"
api-keys:
- "__ZAI_API_KEY__"
models:
- name: "glm-4.6"

# payload: # Optional payload configuration
# default: # Default rules only set parameters when they are missing in the payload.
Expand Down
13 changes: 13 additions & 0 deletions config/opencode/opencode.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,19 @@
}
}
}
},
"options": {
"apiKey": "{env:OPENROUTER_API_KEY}"
}
},
"z-ai": {
"models": {
"glm-4.6": {
"name": "GLM-4.6 (via Z-AI)",
}
},
"options": {
"apiKey": "{env:ZAI_API_KEY}",
}
}
},
Expand Down
4 changes: 2 additions & 2 deletions home-manager/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ let
sources = { };
};
services = import ./services {
inherit pkgs;
inherit config lib pkgs;
};
in
{
Expand All @@ -35,7 +35,7 @@ in
home.packages = packages;
home.stateVersion = "24.11";

programs.yek.enable = true;
modules.yek.enable = true;

accounts.email.accounts = {
Gmail = {
Expand Down
4 changes: 2 additions & 2 deletions home-manager/modules/yek/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
with lib;

let
cfg = config.programs.yek;
cfg = config.modules.yek;

# Determine the target platform string
target =
Expand Down Expand Up @@ -71,7 +71,7 @@ let
'';
in
{
options.programs.yek = {
options.modules.yek = {
enable = mkEnableOption "yek - serialize text files for LLM consumption";

package = mkOption {
Expand Down
40 changes: 40 additions & 0 deletions home-manager/services/cliproxyapi/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ let
inherit (pkgs) lib;
in
{
# Main cliproxyapi service
launchd.agents.cliproxyapi = lib.mkIf pkgs.stdenv.isDarwin {
enable = true;
config = {
Expand Down Expand Up @@ -41,4 +42,43 @@ in
WantedBy = [ "default.target" ];
};
};

# Backup/recovery service
launchd.agents.cliproxyapi-backup = lib.mkIf pkgs.stdenv.isDarwin {
enable = true;
config = {
ProgramArguments = [
"${pkgs.bash}/bin/bash"
"${./scripts/backup-and-recover.sh}"
];
StartInterval = 300; # Run every 5 minutes
RunAtLoad = true;
StandardOutPath = "/tmp/cliproxyapi-backup.log";
StandardErrorPath = "/tmp/cliproxyapi-backup.error.log";
};
};

systemd.user.timers.cliproxyapi-backup = lib.mkIf pkgs.stdenv.isLinux {
Unit = {
Description = "CLIProxyAPI auth backup and recovery timer";
};
Timer = {
OnBootSec = "1min";
OnUnitActiveSec = "5min";
Unit = "cliproxyapi-backup.service";
};
Install = {
WantedBy = [ "timers.target" ];
};
};

systemd.user.services.cliproxyapi-backup = lib.mkIf pkgs.stdenv.isLinux {
Unit = {
Description = "CLIProxyAPI auth backup and recovery";
};
Service = {
Type = "oneshot";
ExecStart = "${pkgs.bash}/bin/bash ${./scripts/backup-and-recover.sh}";
};
};
Comment on lines +61 to +83

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Add service dependencies to prevent coordination issues.

The backup service and timer lack explicit dependencies on the main cliproxyapi.service. This could lead to:

  • Both services accessing the same auth files simultaneously during startup
  • Race conditions if backup runs while the main service is modifying files
  • Unclear startup ordering

Based on learnings, service configurations should include proper service dependencies.

Recommended fix to add proper service ordering
   systemd.user.services.cliproxyapi-backup = lib.mkIf pkgs.stdenv.isLinux {
     Unit = {
       Description = "CLIProxyAPI auth backup and recovery";
+      After = [ "cliproxyapi.service" ];
     };
     Service = {
       Type = "oneshot";
       ExecStart = "${pkgs.bash}/bin/bash ${./scripts/backup-and-recover.sh}";
     };
   };

This ensures the backup service runs after the main service is active, reducing the risk of file access conflicts.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
systemd.user.timers.cliproxyapi-backup = lib.mkIf pkgs.stdenv.isLinux {
Unit = {
Description = "CLIProxyAPI auth backup and recovery timer";
};
Timer = {
OnBootSec = "1min";
OnUnitActiveSec = "5min";
Unit = "cliproxyapi-backup.service";
};
Install = {
WantedBy = [ "timers.target" ];
};
};
systemd.user.services.cliproxyapi-backup = lib.mkIf pkgs.stdenv.isLinux {
Unit = {
Description = "CLIProxyAPI auth backup and recovery";
};
Service = {
Type = "oneshot";
ExecStart = "${pkgs.bash}/bin/bash ${./scripts/backup-and-recover.sh}";
};
};
systemd.user.timers.cliproxyapi-backup = lib.mkIf pkgs.stdenv.isLinux {
Unit = {
Description = "CLIProxyAPI auth backup and recovery timer";
};
Timer = {
OnBootSec = "1min";
OnUnitActiveSec = "5min";
Unit = "cliproxyapi-backup.service";
};
Install = {
WantedBy = [ "timers.target" ];
};
};
systemd.user.services.cliproxyapi-backup = lib.mkIf pkgs.stdenv.isLinux {
Unit = {
Description = "CLIProxyAPI auth backup and recovery";
After = [ "cliproxyapi.service" ];
};
Service = {
Type = "oneshot";
ExecStart = "${pkgs.bash}/bin/bash ${./scripts/backup-and-recover.sh}";
};
};

}
25 changes: 25 additions & 0 deletions home-manager/services/cliproxyapi/scripts/backup-and-recover.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env bash
# Combined backup and recovery script
# Backs up auth files to R2, then recovers if missing

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

# Source .env for credentials
if [ -f "$HOME/dotfiles/.env" ]; then
set -a
# shellcheck source=/dev/null
source "$HOME/dotfiles/.env"
set +a
fi

# Run backup
echo "[$(date)] Starting backup..."
"$SCRIPT_DIR/backup-auth.sh"

# Run recovery if needed
echo "[$(date)] Checking for recovery..."
"$SCRIPT_DIR/recover-auth.sh"

echo "[$(date)] Backup/recovery cycle complete"
21 changes: 21 additions & 0 deletions home-manager/services/cliproxyapi/scripts/backup-auth.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env bash
# Backup auth files to R2 before service start
# Protects against race condition deletions

set -euo pipefail

CONFIG_DIR="$HOME/.cli-proxy-api"
BACKUP_DIR="s3://cliproxyapi/backup/auths/"
AUTH_DIR="$CONFIG_DIR/objectstore/auths"

# Check if auth directory has files
if [ -d "$AUTH_DIR" ] && [ -n "$(ls -A "$AUTH_DIR" 2>/dev/null)" ]; then
echo "Backing up auth files to R2 backup directory..." >&2
AWS_ACCESS_KEY_ID="${OBJECTSTORE_ACCESS_KEY}" \
AWS_SECRET_ACCESS_KEY="${OBJECTSTORE_SECRET_KEY}" \
aws s3 sync \
--endpoint-url="${OBJECTSTORE_ENDPOINT}" \
--no-progress \
"$AUTH_DIR/" \
"$BACKUP_DIR" 2>/dev/null || echo "⚠️ Backup failed (continuing anyway)" >&2
fi
22 changes: 22 additions & 0 deletions home-manager/services/cliproxyapi/scripts/recover-auth.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env bash
# Recover auth files from backup if missing
# Handles race condition where files get deleted during config reload

set -euo pipefail

CONFIG_DIR="$HOME/.cli-proxy-api"
BACKUP_DIR="s3://cliproxyapi/backup/auths/"
AUTH_DIR="$CONFIG_DIR/objectstore/auths"

# Check if auth directory is missing or empty
if [ ! -d "$AUTH_DIR" ] || [ -z "$(ls -A "$AUTH_DIR" 2>/dev/null)" ]; then
echo "Auth files missing, attempting recovery from R2 backup..." >&2
mkdir -p "$AUTH_DIR"
AWS_ACCESS_KEY_ID="${OBJECTSTORE_ACCESS_KEY}" \
AWS_SECRET_ACCESS_KEY="${OBJECTSTORE_SECRET_KEY}" \
aws s3 sync \
--endpoint-url="${OBJECTSTORE_ENDPOINT}" \
--no-progress \
"$BACKUP_DIR" \
"$AUTH_DIR/" 2>/dev/null && echo "✅ Recovered auth files from backup" >&2 || echo "⚠️ Recovery failed (no backup available?)" >&2
Comment on lines +13 to +21

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "recover-auth.sh" -type f

Repository: shunkakinoki/dotfiles

Length of output: 124


🏁 Script executed:

cat -n ./home-manager/services/cliproxyapi/scripts/recover-auth.sh

Repository: shunkakinoki/dotfiles

Length of output: 1050


Verify aws CLI availability before use.

The script uses the aws command on line 17 without checking if it exists in PATH. If aws is not available, the error gets suppressed by 2>/dev/null, leaving only a vague "Recovery failed (no backup available?)" message instead of indicating the actual cause. Add a check like command -v aws >/dev/null 2>&1 || { echo "aws CLI not found" >&2; exit 1; } before the sync command.

🤖 Prompt for AI Agents
In home-manager/services/cliproxyapi/scripts/recover-auth.sh around lines 13 to
21, the script calls the aws CLI without verifying it exists and then silences
errors, which hides the real cause when recovery fails; add a pre-check using
command -v aws >/dev/null 2>&1 and if it fails print "aws CLI not found" to
stderr and exit non-zero before attempting the aws s3 sync, so the script fails
fast with a clear message instead of a vague recovery-failed line.

fi
29 changes: 22 additions & 7 deletions home-manager/services/cliproxyapi/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,37 @@ fi
# Export management password for Management API (CLIProxyAPI requires MANAGEMENT_PASSWORD env var)
export MANAGEMENT_PASSWORD="${CLIPROXY_MANAGEMENT_PASSWORD:-}"

# Export S3-compatible object storage env vars (needed for backup/recovery)
export OBJECTSTORE_ENDPOINT="${OBJECTSTORE_ENDPOINT:-${AWS_S3_ENDPOINT:-}}"
export OBJECTSTORE_BUCKET="${OBJECTSTORE_BUCKET:-${AWS_S3_BUCKET:-}}"
export OBJECTSTORE_ACCESS_KEY="${OBJECTSTORE_ACCESS_KEY:-${AWS_ACCESS_KEY_ID:-}}"
export OBJECTSTORE_SECRET_KEY="${OBJECTSTORE_SECRET_KEY:-${AWS_SECRET_ACCESS_KEY:-}}"

# Backup auth files to R2 before starting service
# This protects against race condition deletions
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [ -x "$SCRIPT_DIR/scripts/backup-auth.sh" ]; then
bash "$SCRIPT_DIR/scripts/backup-auth.sh"
fi

# Generate config from template with secrets injected
if [ -f "$TEMPLATE" ]; then
sed -e "s|__OPENROUTER_API_KEY__|${OPENROUTER_API_KEY:-}|g" \
sed \
-e "s|__OPENROUTER_API_KEY__|${OPENROUTER_API_KEY:-}|g" \
-e "s|__CLIPROXY_MANAGEMENT_PASSWORD__|${CLIPROXY_MANAGEMENT_PASSWORD:-}|g" \
-e "s|__ZAI_API_KEY__|${ZAI_API_KEY:-}|g" \
"$TEMPLATE" >"$CONFIG"
fi

# Recover auth files from backup if they're missing
# This handles race condition where files get deleted during config reload
if [ -x "$SCRIPT_DIR/scripts/recover-auth.sh" ]; then
bash "$SCRIPT_DIR/scripts/recover-auth.sh"
fi

# Change to config dir so logs are created there
cd "$CONFIG_DIR"

# Export S3-compatible object storage env vars (for R2 or any S3-compatible storage)
export OBJECTSTORE_ENDPOINT="${OBJECTSTORE_ENDPOINT:-${AWS_S3_ENDPOINT:-}}"
export OBJECTSTORE_BUCKET="${OBJECTSTORE_BUCKET:-${AWS_S3_BUCKET:-}}"
export OBJECTSTORE_ACCESS_KEY="${OBJECTSTORE_ACCESS_KEY:-${AWS_ACCESS_KEY_ID:-}}"
export OBJECTSTORE_SECRET_KEY="${OBJECTSTORE_SECRET_KEY:-${AWS_SECRET_ACCESS_KEY:-}}"

# Find and exec cliproxyapi with config file
if [ -x /opt/homebrew/bin/cliproxyapi ]; then
exec /opt/homebrew/bin/cliproxyapi -config "$CONFIG" "$@"
Expand Down
13 changes: 10 additions & 3 deletions home-manager/services/default.nix
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
{ pkgs }:
{
config,
lib,
pkgs,
...
}:
let
brewUpgrader = import ./brew-upgrader { inherit pkgs; };
cliproxyapi = import ./cliproxyapi { inherit pkgs; };
codeSyncer = import ./code-syncer { inherit pkgs; };
dotfilesUpdater = import ./dotfiles-updater { inherit pkgs; };
neversslKeepalive = import ./neverssl-keepalive { inherit pkgs; };
ollama = import ./ollama { inherit pkgs; };
brewUpgrader = import ./brew-upgrader { inherit pkgs; };
sshAgent = import ./ssh-agent.nix;
sshAgent = import ./ssh-agent {
inherit config lib pkgs;
};
in
[
brewUpgrader
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,11 @@ in
# Load keys that exist
set -l keys

# Always try to load the default key (no passphrase)
# Always try to load the default key
if test -f ~/.ssh/id_ed25519
set -a keys ~/.ssh/id_ed25519
end
Comment on lines +21 to 24

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Search for references to id_ed25519_github in the codebase
rg "id_ed25519_github" -n --type-list | head -5
rg "id_ed25519_github" -n 2>/dev/null || echo "No references found"

Repository: shunkakinoki/dotfiles

Length of output: 2153


🏁 Script executed:

# Check for SSH identity configurations that might reference this key pattern
rg "IdentityFile|identity.*github|ssh.*github" -n -g "*.nix" -g "*.conf" -g "*.ssh" 2>/dev/null | head -20

Repository: shunkakinoki/dotfiles

Length of output: 941


🏁 Script executed:

# Get full context of the ssh-agent service configuration
if [ -f "home-manager/services/ssh-agent/default.nix" ]; then
  wc -l "home-manager/services/ssh-agent/default.nix"
  cat -n "home-manager/services/ssh-agent/default.nix"
fi

Repository: shunkakinoki/dotfiles

Length of output: 1325


This removal is a breaking change—verify user migration path or update documentation.

The removal of automatic ~/.ssh/id_ed25519_github loading is confirmed to impact multiple parts of the codebase: SSH client configuration (home-manager/programs/ssh/default.nix:41), a dedicated _ssh_add_github Fish function, and host-specific key setup in named-hosts/kyber/.

While an on-demand mechanism exists via the _ssh_add_github function, users who relied on automatic loading will need to manually invoke it or rename their key to the default. Ensure:

  1. Migration guide or release notes document this behavior change
  2. Users are directed to the _ssh_add_github function as the replacement for automatic loading
  3. Consider whether the default key should still support GitHub usage, or if the dedicated function is the intended pattern


# Load GitHub key if it exists (may have passphrase)
if test -f ~/.ssh/id_ed25519_github
set -a keys ~/.ssh/id_ed25519_github
end

# Initialize keychain with found keys
if test (count $keys) -gt 0
# Use --quiet to suppress most output, --eval to set environment variables
Expand Down
Loading
Loading