Compare commits
119 Commits
8e11fe06de
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 379fe5c290 | |||
| ea36629461 | |||
| 0f85723755 | |||
| 4db8c17f70 | |||
| 36f2abc1a9 | |||
| 4cd5d1ba62 | |||
| 73b2bbc973 | |||
| bc95e479d6 | |||
| d15e610337 | |||
| f15765d5a7 | |||
| 881f8fe816 | |||
| 9d8ec6d7d2 | |||
| fd9695a754 | |||
| 5cf4b93072 | |||
| c359a26d3f | |||
| fbeb040914 | |||
| e18aee716a | |||
| b4e1afd012 | |||
| 6dd5e38d9b | |||
| f4a213de8e | |||
| 68cb7ea3d5 | |||
| 7842c9cd96 | |||
| 285fe99461 | |||
| 544ab6f781 | |||
| 332713f8fc | |||
| b4fffef1d7 | |||
| a3e080b2f2 | |||
| 1b43af76b1 | |||
| a2298fcfae | |||
| 79884e8a77 | |||
| a7941db240 | |||
| 964b0dd2a1 | |||
| 81ffe67cce | |||
| 5b2111c7e8 | |||
| 62f3ddc218 | |||
| 0f9513ec26 | |||
| 1623f8d14a | |||
| 719f2798e5 | |||
| 24dd67a0de | |||
| 89e2161ff4 | |||
| 3b4f54b2b1 | |||
| 4e2d03ae89 | |||
| eaf307db23 | |||
| b16d816a18 | |||
| 18e8a39ee3 | |||
| 328bb6e1db | |||
| a01f9e34ee | |||
| 9824728ccb | |||
| 9ec2d61fcc | |||
| 1879e98ebc | |||
| 4df32ad273 | |||
| ecad94aab3 | |||
| 187c717383 | |||
| 352e99c732 | |||
| 6fff658f9d | |||
| 885fe8517d | |||
| 2133786985 | |||
| b41e9f2a84 | |||
| b25a933dd0 | |||
| 37b0fae7e2 | |||
| 02410568dc | |||
| 2f29008a2a | |||
| 87cdabbef4 | |||
| 18a4e46bcb | |||
| f3cc67b17d | |||
| d142c5ff7e | |||
| fea5cc887d | |||
| f6f17831bf | |||
| 5ded87b24a | |||
| d3ccbda958 | |||
| 3bccdc6382 | |||
| 0e3658615a | |||
| 3095515963 | |||
| 4701a97a91 | |||
| 6e20baf883 | |||
| f19894b45f | |||
| d72b563276 | |||
| d1f17a18b4 | |||
| 193b431681 | |||
| 3117a3569f | |||
| 3cc4649979 | |||
| 7c1519881a | |||
| f00edb620c | |||
| 8d45977154 | |||
| 40114f438f | |||
| ba30222962 | |||
| e4d40d89d9 | |||
| 43a1d66e6b | |||
| 1283b7cdef | |||
| 09fdff4908 | |||
| 88308602c8 | |||
| 1812d2ea03 | |||
| ab63211a75 | |||
| 561f10d2a7 | |||
| a3b2efa5bb | |||
| 74ff71803b | |||
| 75eba8703f | |||
| 3d55b6e675 | |||
| 990b6a4392 | |||
| bcba8f6b60 | |||
| 93e2247a30 | |||
| 31363f5f8d | |||
| 976edab339 | |||
| d1d3f3c1a3 | |||
| a242461139 | |||
| ca8d2a38ed | |||
| eef4d78cb3 | |||
| 50719469da | |||
| f349b24b5d | |||
| f110a9743a | |||
| b85b01bcaa | |||
| 5a5aeb592e | |||
| 04296e282c | |||
| 005ba2244b | |||
| 412b503c7a | |||
| a38a725bb9 | |||
| ad11dbdf2a | |||
| a39a314674 | |||
| e8bc4e4da7 |
@@ -12,7 +12,7 @@ If the user provides only a **package name** (no version), look up the latest ve
|
|||||||
## Hard Rules — Read First
|
## Hard Rules — Read First
|
||||||
|
|
||||||
1. **Never run `nix build .#<pkg>`** or `.#packages.<system>.<pkg>`. That compiles the package. Only realise **FOD sub-attributes** (`.src`, `.goModules`, `.npmDeps`, `.cargoDeps`) — those are pure downloads, not builds.
|
1. **Never run `nix build .#<pkg>`** or `.#packages.<system>.<pkg>`. That compiles the package. Only realise **FOD sub-attributes** (`.src`, `.goModules`, `.npmDeps`, `.cargoDeps`) — those are pure downloads, not builds.
|
||||||
2. **Never** use `nix-prefetch-git`, `nix-prefetch-url`, `nix hash path`, `git clone` + manual hashing, `builtins.fetchGit`, or any other ad-hoc method to compute hashes. They produce hashes in formats that don't match what `fetchgit`/`fetchFromGitHub`/etc. expect, and you will waste time chasing mismatches.
|
2. **Never** use `nix-prefetch-git`, `nix-prefetch-github`, `nix-prefetch-url`, `nix hash path`, `nix hash file` (on a raw patch/tarball), `git clone` + manual hashing, `builtins.fetchGit`, or any other ad-hoc method to compute hashes. They produce hashes in formats that don't match what `fetchgit`/`fetchFromGitHub`/`fetchpatch` expect (notably: `fetchFromGitHub { leaveDotGit = true; }` is non-deterministic across machines, and `fetchpatch` normalizes patches — strips `index abc..def`, `From <sha>`, signatures — so its hash ≠ `nix hash file` of the raw `.patch`).
|
||||||
3. There are exactly **two** correct ways to get a hash, both listed below. If neither fits, stop and ask the user — don't improvise.
|
3. There are exactly **two** correct ways to get a hash, both listed below. If neither fits, stop and ask the user — don't improvise.
|
||||||
|
|
||||||
## The Only Two Methods
|
## The Only Two Methods
|
||||||
@@ -29,20 +29,81 @@ Copy the `hash = "sha256-..."` line from the output into the package's `src` blo
|
|||||||
|
|
||||||
### Method B — FOD mismatch trick (for everything else)
|
### Method B — FOD mismatch trick (for everything else)
|
||||||
|
|
||||||
For `vendorHash`, `npmDepsHash`, `cargoHash`, `cargoLock.outputHashes.<crate>`, or any `src` using a custom fetcher (`leaveDotGit`, `postFetch`, `fetchSubmodules`, etc. — applies to `llama-cpp` and `llama-swap`), realise the **specific FOD sub-attribute** and read the `got:` line from the error.
|
For `vendorHash`, `npmDepsHash`, `cargoHash`, `cargoLock.outputHashes.<crate>`, `fetchpatch` hashes, or any `src` using a custom fetcher (`leaveDotGit`, `postFetch`, `fetchSubmodules`, etc. — applies to `llama-cpp` and `llama-swap`), realise the **specific FOD sub-attribute** and read the `got:` line from the error.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
nix build .#<name>.src --no-link 2>&1 | tee /tmp/hash.log # for src
|
nix build .#<name>.src --no-link 2>&1 | tee /tmp/hash.log # for src
|
||||||
nix build .#<name>.goModules --no-link 2>&1 | tee /tmp/hash.log # for vendorHash
|
nix build .#<name>.goModules --no-link 2>&1 | tee /tmp/hash.log # for vendorHash
|
||||||
nix build .#<name>.npmDeps --no-link 2>&1 | tee /tmp/hash.log # for npmDepsHash
|
nix build .#<name>.npmDeps --no-link 2>&1 | tee /tmp/hash.log # for npmDepsHash
|
||||||
nix build .#<name>.cargoDeps --no-link 2>&1 | tee /tmp/hash.log # for cargoHash
|
nix build .#<name>.cargoDeps --no-link 2>&1 | tee /tmp/hash.log # for cargoHash
|
||||||
|
nix build .#<name> --no-link 2>&1 | tee /tmp/hash.log # for fetchpatch / other input FODs (see note)
|
||||||
grep -E '^[[:space:]]*got:' /tmp/hash.log | tail -1 | awk '{print $2}'
|
grep -E '^[[:space:]]*got:' /tmp/hash.log | tail -1 | awk '{print $2}'
|
||||||
```
|
```
|
||||||
|
|
||||||
Setting the hash to `sha256-AAAA...` (44 A's) or leaving the old one in place both work — the build will fail at the FOD with `got: sha256-...` which is the correct value.
|
**`fetchpatch` note:** patches don't have a dedicated sub-attribute, so you must target the package itself. This is safe *only* when the patch hash is wrong (e.g. `lib.fakeHash`) — Nix realizes the patch FOD before compilation starts, so a hash mismatch aborts with `0 built (1 failed)` and zero compile work. If you accidentally fix all FODs correctly, `nix build .#<name>` will start compiling. To guard against this: always start patch hashes as `lib.fakeHash`, run the build, copy `got:`, paste, and only then re-verify with `.src` / sub-attribute builds (never re-run `.#<name>` to confirm).
|
||||||
|
|
||||||
|
**GitHub PR patches — `.patch` vs `.diff`:** When fetching a patch from a GitHub pull request, prefer the `.diff` endpoint over `.patch`.
|
||||||
|
|
||||||
|
- `https://github.com/<owner>/<repo>/pull/<N>.patch` — a `git format-patch` **mbox** containing each commit in the PR separately. `git apply` (which `fetchpatch` and the Nix `patchPhase` use) does **not** replay commit history; it applies hunks against the working tree. PRs that create a file in one commit and delete/rename it in a later commit will fail with errors like `The next patch would delete the file X, which does not exist`.
|
||||||
|
- `https://github.com/<owner>/<repo>/pull/<N>.diff` — a **squashed** unified diff of the PR's net change. Applies cleanly against any base the PR is mergeable against.
|
||||||
|
|
||||||
|
Default to `.diff`. Only fall back to `.patch` if you specifically need authorship metadata (rare for Nix patching). If a previously-working `.patch` URL suddenly fails to apply, switching to `.diff` is the first thing to try.
|
||||||
|
|
||||||
|
Always use `lib.fakeHash` (or `pkgs.lib.fakeHash` if only `pkgs` is in scope). This is the only reliable way to set a bogus hash — never write a literal `sha256-...` placeholder string. The build will fail at the FOD with `got: sha256-...` which is the correct value.
|
||||||
|
|
||||||
**Note:** `.src`, `.goModules`, etc. are sub-attributes of the derivation. They download but do not compile. `nix build .#<name>` (without the `.src` suffix) compiles — never do that.
|
**Note:** `.src`, `.goModules`, etc. are sub-attributes of the derivation. They download but do not compile. `nix build .#<name>` (without the `.src` suffix) compiles — never do that.
|
||||||
|
|
||||||
|
### Example — Package With `src`, `npmDepsHash`, and `vendorHash`
|
||||||
|
|
||||||
|
Use the same pattern for packages that combine a custom `src` fetcher, Go dependencies, and a nested npm UI derivation. `llama-swap` is a concrete example because it uses `leaveDotGit` + `postFetch`, `vendorHash`, and a UI package exposed under `passthru.ui`.
|
||||||
|
|
||||||
|
1. Set stale hashes to `lib.fakeHash` where possible:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
src.hash = lib.fakeHash;
|
||||||
|
vendorHash = lib.fakeHash;
|
||||||
|
passthru.npmDepsHash = lib.fakeHash;
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Resolve the custom `src` hash first; downstream FODs depend on it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix build .#llama-swap.src --no-link 2>&1 | tee /tmp/llama-swap-src.log
|
||||||
|
grep -E '^[[:space:]]*got:' /tmp/llama-swap-src.log | tail -1 | awk '{print $2}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Put the `got:` value into `src.hash` before resolving dependency hashes.
|
||||||
|
|
||||||
|
3. Resolve the UI npm dependency hash from the nested UI derivation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix build .#llama-swap.passthru.ui.npmDeps --no-link 2>&1 | tee /tmp/llama-swap-npm.log
|
||||||
|
grep -E '^[[:space:]]*got:' /tmp/llama-swap-npm.log | tail -1 | awk '{print $2}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Put the `got:` value into `passthru.npmDepsHash`.
|
||||||
|
|
||||||
|
4. Resolve the Go `vendorHash`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix build .#llama-swap.goModules --no-link 2>&1 | tee /tmp/llama-swap-go.log
|
||||||
|
grep -E '^[[:space:]]*got:' /tmp/llama-swap-go.log | tail -1 | awk '{print $2}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Put the `got:` value into `vendorHash`.
|
||||||
|
|
||||||
|
For other packages, adapt the flake attribute path to where the FOD is exposed (for example, `.#<name>.npmDeps`, `.#<name>.passthru.ui.npmDeps`, or `.#<name>.goModules`).
|
||||||
|
|
||||||
|
5. Re-run the FOD sub-attribute builds to confirm they realise successfully:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix build .#llama-swap.src --no-link
|
||||||
|
nix build .#llama-swap.passthru.ui.npmDeps --no-link
|
||||||
|
nix build .#llama-swap.goModules --no-link
|
||||||
|
```
|
||||||
|
|
||||||
|
If a dependency FOD fails before a hash mismatch (for example, `go.mod requires go >= ...`), fix the package inputs minimally (for example, switch to the matching `buildGo126Module`) and re-run the same FOD sub-attribute. Do not build `.#llama-swap`.
|
||||||
|
|
||||||
## Lookup Latest Version
|
## Lookup Latest Version
|
||||||
|
|
||||||
When the user asks to update a package but doesn't specify a version:
|
When the user asks to update a package but doesn't specify a version:
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ show_main_head() {
|
|||||||
line=$(git ls-remote "$url" "refs/heads/$branch" 2>/dev/null | head -1)
|
line=$(git ls-remote "$url" "refs/heads/$branch" 2>/dev/null | head -1)
|
||||||
if [[ -n "$line" ]]; then
|
if [[ -n "$line" ]]; then
|
||||||
local full_rev="${line%% *}"
|
local full_rev="${line%% *}"
|
||||||
echo " main: ${full_rev:0:12}"
|
echo " main: $full_rev"
|
||||||
echo
|
echo
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
@@ -120,13 +120,13 @@ cmd_releases() {
|
|||||||
| grep -v '\^{}' \
|
| grep -v '\^{}' \
|
||||||
| sed -E 's/^[a-f0-9]+[[:space:]]+refs\/tags\/([^\/]+)[[:space:]]*$/\1/' \
|
| sed -E 's/^[a-f0-9]+[[:space:]]+refs\/tags\/([^\/]+)[[:space:]]*$/\1/' \
|
||||||
| sort -V \
|
| sort -V \
|
||||||
| tail -5)
|
| tail -5 || true)
|
||||||
else
|
else
|
||||||
tags=$(git ls-remote --tags "$url" 2>&1 \
|
tags=$(git ls-remote --tags "$url" 2>&1 \
|
||||||
| grep -v '\^{}' \
|
| grep -v '\^{}' \
|
||||||
| sed -E 's/^[a-f0-9]+[[:space:]]+refs\/tags\/([^\/]+)[[:space:]]*$/\1/' \
|
| sed -E 's/^[a-f0-9]+[[:space:]]+refs\/tags\/([^\/]+)[[:space:]]*$/\1/' \
|
||||||
| sort -V \
|
| sort -V \
|
||||||
| tail -5)
|
| tail -5 || true)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -z "$tags" ]]; then
|
if [[ -z "$tags" ]]; then
|
||||||
@@ -135,13 +135,13 @@ cmd_releases() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
printf " %-30s %s\n" "TAG" "COMMIT"
|
printf " %-30s %s\n" "TAG" "COMMIT"
|
||||||
echo " $(printf '%.0s-' {1..45})"
|
echo " $(printf '%.0s-' {1..72})"
|
||||||
|
|
||||||
echo "$tags" | tac | while IFS= read -r tag_name; do
|
echo "$tags" | tac | while IFS= read -r tag_name; do
|
||||||
local full_rev
|
local full_rev
|
||||||
full_rev=$(git ls-remote "$url" "refs/tags/$tag_name" 2>/dev/null | head -1 | cut -f1)
|
full_rev=$(git ls-remote "$url" "refs/tags/$tag_name" 2>/dev/null | head -1 | cut -f1)
|
||||||
if [[ -n "$full_rev" ]]; then
|
if [[ -n "$full_rev" ]]; then
|
||||||
printf " %-30s %s\n" "$tag_name" "${full_rev:0:12}"
|
printf " %-30s %s\n" "$tag_name" "$full_rev"
|
||||||
else
|
else
|
||||||
printf " %-30s %s\n" "$tag_name" "(not found)"
|
printf " %-30s %s\n" "$tag_name" "(not found)"
|
||||||
fi
|
fi
|
||||||
|
|||||||
71
.agents/skills/update-vllm-3090-configs/SKILL.md
Normal file
71
.agents/skills/update-vllm-3090-configs/SKILL.md
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
---
|
||||||
|
name: update-vllm-3090-configs
|
||||||
|
description: Update only the qwen3.6-27b vLLM 3090 llama-swap configs from club-3090 refs; compare diffs, present a plan, and require approval before editing.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Update vLLM 3090 Configs
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
Use only for Qwen3.6 27B vLLM 3090 configs in `modules/nixos/services/llama-swap/`.
|
||||||
|
Do not use this skill for other models, other Qwen sizes, non-vLLM configs, or package bumps.
|
||||||
|
|
||||||
|
Local files:
|
||||||
|
- `modules/nixos/services/llama-swap/config.nix`
|
||||||
|
- `modules/nixos/services/llama-swap/setup-qwen36-vllm.sh`
|
||||||
|
|
||||||
|
Local config keys:
|
||||||
|
- `vllm-qwen3.6-27b-tools-text`
|
||||||
|
- `vllm-qwen3.6-27b-long-text`
|
||||||
|
- `vllm-qwen3.6-27b-long-vision`
|
||||||
|
|
||||||
|
## Hash Tracking
|
||||||
|
|
||||||
|
Each config entry stores an upstream commit hash comment:
|
||||||
|
`# Upstream: club-3090 <hash> (<date>) - <compose-file>`
|
||||||
|
|
||||||
|
When comparing, first extract stored hashes. If a config's hash matches
|
||||||
|
upstream HEAD, skip it (report "already synced"). Only full-diff configs
|
||||||
|
whose hash differs. Update the hash comment when edits are applied.
|
||||||
|
|
||||||
|
## Upstream References
|
||||||
|
|
||||||
|
Compare against `club-3090` master:
|
||||||
|
- `models/qwen3.6-27b/vllm/compose/single/tools-text.yml`
|
||||||
|
- `models/qwen3.6-27b/vllm/compose/single/long-text.yml`
|
||||||
|
- `models/qwen3.6-27b/vllm/compose/single/long-vision.yml`
|
||||||
|
- `scripts/setup.sh` for the current `GENESIS_PIN="${GENESIS_PIN:-...}"`
|
||||||
|
|
||||||
|
Use raw URLs or a temp clone under `_scratch/club-3090`. Prefer a temp clone when checking broad changes:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p _scratch
|
||||||
|
git clone https://github.com/noonghunna/club-3090 _scratch/club-3090 2>/dev/null || git -C _scratch/club-3090 pull --ff-only
|
||||||
|
```
|
||||||
|
|
||||||
|
## Required Workflow
|
||||||
|
|
||||||
|
1. Fetch/update upstream refs under `_scratch/club-3090` or fetch the raw files.
|
||||||
|
2. Extract stored upstream hashes from `# Upstream: club-3090 ...` comments in config.nix. Skip any config whose hash matches upstream HEAD (report "already synced").
|
||||||
|
3. Compare upstream compose files to the remaining local llama-swap entries. Translate docker-compose semantics into the existing `docker run`/llama-swap format.
|
||||||
|
4. Compare upstream `scripts/setup.sh` Genesis pin to local `GENESIS_PIN` in `setup-qwen36-vllm.sh`.
|
||||||
|
5. Check upstream compose volumes/entrypoint for sidecar patches. If patches are added, removed, renamed, or invoked differently, update both:
|
||||||
|
- runtime mounts and `python3 /patches/...` calls in `config.nix`
|
||||||
|
- download/install logic and summary in `setup-qwen36-vllm.sh`
|
||||||
|
6. Ignore these diffs unless the user explicitly asks otherwise:
|
||||||
|
- `shm_size` / shm-related compose settings
|
||||||
|
- local timing patch `patch_timings_07351e088.py` and its mount/invocation
|
||||||
|
- model served-name differences caused by llama-swap `${MODEL_ID}`
|
||||||
|
- `HUGGING_FACE_HUB_TOKEN`; keep local CUDA device/env choices
|
||||||
|
- upstream relative paths vs local `/mnt/ssd/vLLM/...` paths
|
||||||
|
- docker-compose format vs local llama-swap/Nix format
|
||||||
|
7. Before editing, present:
|
||||||
|
- upstream files/commit checked
|
||||||
|
- meaningful diffs found
|
||||||
|
- ignored diffs
|
||||||
|
- exact planned local changes
|
||||||
|
Then wait for explicit user approval.
|
||||||
|
8. After approval, edit minimally and update the `# Upstream: club-3090 ...` hash comments. Validate:
|
||||||
|
- `bash -n modules/nixos/services/llama-swap/setup-qwen36-vllm.sh`
|
||||||
|
- `nix-instantiate --parse modules/nixos/services/llama-swap/config.nix`
|
||||||
|
9. Summarize changed files and any remaining upstream differences.
|
||||||
19
.sops.yaml
19
.sops.yaml
@@ -5,14 +5,16 @@ keys:
|
|||||||
# User SSH Derived
|
# User SSH Derived
|
||||||
- &user_lin-va-desktop age15hdlen5dgjvdfgg2j0uzvchs5vs3xuptkhsw9xeuatcuk6uwrvcsz7hcsg
|
- &user_lin-va-desktop age15hdlen5dgjvdfgg2j0uzvchs5vs3xuptkhsw9xeuatcuk6uwrvcsz7hcsg
|
||||||
- &user_lin-va-mbp-personal age17ayje4uv2mhwehhp9jr3u9l0ds07396kt7ef40sufx89vm7cgfjq6d5d4y
|
- &user_lin-va-mbp-personal age17ayje4uv2mhwehhp9jr3u9l0ds07396kt7ef40sufx89vm7cgfjq6d5d4y
|
||||||
|
- &user_lin-va-mbp-work-vm age1mar507c9mxmwalg486chs5kfh0mya38rv5w64ypfwnwlawewrpnswerpg8
|
||||||
|
- &user_lin-va-terminal age1w6avj7gd4f5frk90lsyh4e2k5am6z92hzlr0vpgrm767muyj59qsnuah62
|
||||||
- &user_lin-va-thinkpad age1avlhszrryt4gf4ya536jhzm7qwt9xfttm8x4sns6h9w2tahzqp8sspz9y5
|
- &user_lin-va-thinkpad age1avlhszrryt4gf4ya536jhzm7qwt9xfttm8x4sns6h9w2tahzqp8sspz9y5
|
||||||
- &user_mac-va-mbp-personal age1dccte7xtwswgef089nd80dutp96xnezx5lrqnneh9cusegsnda8sj3dj6c
|
- &user_mac-va-mbp-personal age1dccte7xtwswgef089nd80dutp96xnezx5lrqnneh9cusegsnda8sj3dj6c
|
||||||
- &user_mac-va-mbp-work age1jf7yuycuajc5m8vupgrndjvw8knekr2v9979j68xc5ykvcxag4lss454au
|
- &user_mac-va-mbp-work age1ped3hpugq06908ex8kgama33qckqe03rmac5pa6th87vks5d249qhshvqu
|
||||||
- &user_lin-va-terminal age1w6avj7gd4f5frk90lsyh4e2k5am6z92hzlr0vpgrm767muyj59qsnuah62
|
|
||||||
|
|
||||||
# System SSH Derived
|
# System SSH Derived
|
||||||
- &system_lin-va-desktop age1mxjrvjxkn69kfn2np3wpd73g44fuhsgykw7l5ss9rx30em5jfp2scnrq32
|
- &system_lin-va-desktop age1mxjrvjxkn69kfn2np3wpd73g44fuhsgykw7l5ss9rx30em5jfp2scnrq32
|
||||||
- &system_lin-va-thinkpad age13gymlygyac9z2slecl53jp8spq7e8n4zkan86n0gmnm3nrj4muxqa5ullm
|
- &system_lin-va-thinkpad age13gymlygyac9z2slecl53jp8spq7e8n4zkan86n0gmnm3nrj4muxqa5ullm
|
||||||
|
- &system_lin-va-terminal age1rfgh40p6wmaam4ttwgv9xfukgxjruskhfvm0q3ud6lvxtlqc2y8snhwjft
|
||||||
creation_rules:
|
creation_rules:
|
||||||
- path_regex: secrets/[^/]+\.(yaml|json|env|ini)$
|
- path_regex: secrets/[^/]+\.(yaml|json|env|ini)$
|
||||||
key_groups:
|
key_groups:
|
||||||
@@ -24,11 +26,24 @@ creation_rules:
|
|||||||
- *admin_reichard
|
- *admin_reichard
|
||||||
- *system_lin-va-desktop
|
- *system_lin-va-desktop
|
||||||
- *system_lin-va-thinkpad
|
- *system_lin-va-thinkpad
|
||||||
|
- *system_lin-va-terminal
|
||||||
|
- path_regex: secrets/common/llama-swap.yaml
|
||||||
|
key_groups:
|
||||||
|
- age:
|
||||||
|
- *admin_reichard
|
||||||
|
- *system_lin-va-desktop
|
||||||
|
- *user_lin-va-mbp-personal
|
||||||
|
- *user_lin-va-mbp-work-vm
|
||||||
|
- *user_lin-va-terminal
|
||||||
|
- *user_lin-va-thinkpad
|
||||||
|
- *user_mac-va-mbp-personal
|
||||||
|
- *user_mac-va-mbp-work
|
||||||
- path_regex: secrets/common/evanreichard.yaml
|
- path_regex: secrets/common/evanreichard.yaml
|
||||||
key_groups:
|
key_groups:
|
||||||
- age:
|
- age:
|
||||||
- *admin_reichard
|
- *admin_reichard
|
||||||
- *user_lin-va-mbp-personal
|
- *user_lin-va-mbp-personal
|
||||||
|
- *user_lin-va-mbp-work-vm
|
||||||
- *user_lin-va-terminal
|
- *user_lin-va-terminal
|
||||||
- *user_lin-va-thinkpad
|
- *user_lin-va-thinkpad
|
||||||
- *user_mac-va-mbp-personal
|
- *user_mac-va-mbp-personal
|
||||||
|
|||||||
110
flake.lock
generated
110
flake.lock
generated
@@ -8,11 +8,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776147994,
|
"lastModified": 1778234684,
|
||||||
"narHash": "sha256-c5F8jYiB0fjWsP4j/yeszqszA3laflzDj6/pmoJTeG4=",
|
"narHash": "sha256-usIHfvSt7aXvMvRGtcbsue3rA13Z+9TW/7I3WBzLqFY=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "nixos-apple-silicon",
|
"repo": "nixos-apple-silicon",
|
||||||
"rev": "81439a7fb8067ab43641efd79c84607701da1ccd",
|
"rev": "3d7fe422ef6162154830209b9e50bf69e150cff7",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -28,16 +28,16 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1772129556,
|
"lastModified": 1779036909,
|
||||||
"narHash": "sha256-Utk0zd8STPsUJPyjabhzPc5BpPodLTXrwkpXBHYnpeg=",
|
"narHash": "sha256-zXcwYQGCT6pzinK+1dBB2ekTVtfxGZAapb3Evdcu4fY=",
|
||||||
"owner": "nix-darwin",
|
"owner": "nix-darwin",
|
||||||
"repo": "nix-darwin",
|
"repo": "nix-darwin",
|
||||||
"rev": "ebec37af18215214173c98cf6356d0aca24a2585",
|
"rev": "56c666e108467d87d13508936aade6d567f2a501",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "nix-darwin",
|
"owner": "nix-darwin",
|
||||||
"ref": "nix-darwin-25.11",
|
"ref": "nix-darwin-26.05",
|
||||||
"repo": "nix-darwin",
|
"repo": "nix-darwin",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
@@ -51,11 +51,11 @@
|
|||||||
"nixpkgs": "nixpkgs_2"
|
"nixpkgs": "nixpkgs_2"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1775584659,
|
"lastModified": 1778179392,
|
||||||
"narHash": "sha256-NA5oZRunqxD+4LNdU7ZKJHqwuazKyAmBjO4OHXL14X4=",
|
"narHash": "sha256-W6zorvjBYbzMNvqKIqCdpDF4rq3gj50Xximl56YM9/I=",
|
||||||
"owner": "determinatesystems",
|
"owner": "determinatesystems",
|
||||||
"repo": "determinate",
|
"repo": "determinate",
|
||||||
"rev": "21dcaa011d3d35cf42a04e988eaac9b28c97a707",
|
"rev": "efd54faa68be8cd777b5c28cab11e638998a0853",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -67,37 +67,37 @@
|
|||||||
"determinate-nixd-aarch64-darwin": {
|
"determinate-nixd-aarch64-darwin": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"narHash": "sha256-qLWfYk9qkb21wKCDWnhMfqBFjcdBBJkNUKBlvdHSLgA=",
|
"narHash": "sha256-z4mCqKI3Qd6weuHrlfzGccJG0giym/VJhKv20ijRSs0=",
|
||||||
"type": "file",
|
"type": "file",
|
||||||
"url": "https://install.determinate.systems/determinate-nixd/tag/v3.17.3/macOS"
|
"url": "https://install.determinate.systems/determinate-nixd/tag/v3.20.0/macOS"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"type": "file",
|
"type": "file",
|
||||||
"url": "https://install.determinate.systems/determinate-nixd/tag/v3.17.3/macOS"
|
"url": "https://install.determinate.systems/determinate-nixd/tag/v3.20.0/macOS"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"determinate-nixd-aarch64-linux": {
|
"determinate-nixd-aarch64-linux": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"narHash": "sha256-0BmprPIRTopvJ2QdImOMP+TujAPVgRdl0bUL3vhqGIY=",
|
"narHash": "sha256-yW+VNepSRytzfanSssPMJPvwioCcmlZYaBX8++UFkAk=",
|
||||||
"type": "file",
|
"type": "file",
|
||||||
"url": "https://install.determinate.systems/determinate-nixd/tag/v3.17.3/aarch64-linux"
|
"url": "https://install.determinate.systems/determinate-nixd/tag/v3.20.0/aarch64-linux"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"type": "file",
|
"type": "file",
|
||||||
"url": "https://install.determinate.systems/determinate-nixd/tag/v3.17.3/aarch64-linux"
|
"url": "https://install.determinate.systems/determinate-nixd/tag/v3.20.0/aarch64-linux"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"determinate-nixd-x86_64-linux": {
|
"determinate-nixd-x86_64-linux": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"narHash": "sha256-+Q85cySxr0FB/cr97hk/WWYgeJY+iC4OH+FjGYygIbU=",
|
"narHash": "sha256-+L102C3Hhkd1GlXmRm2eLTLsZKBxEvooiQZFqQRlBf0=",
|
||||||
"type": "file",
|
"type": "file",
|
||||||
"url": "https://install.determinate.systems/determinate-nixd/tag/v3.17.3/x86_64-linux"
|
"url": "https://install.determinate.systems/determinate-nixd/tag/v3.20.0/x86_64-linux"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"type": "file",
|
"type": "file",
|
||||||
"url": "https://install.determinate.systems/determinate-nixd/tag/v3.17.3/x86_64-linux"
|
"url": "https://install.determinate.systems/determinate-nixd/tag/v3.20.0/x86_64-linux"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"disko": {
|
"disko": {
|
||||||
@@ -105,11 +105,11 @@
|
|||||||
"nixpkgs": "nixpkgs_3"
|
"nixpkgs": "nixpkgs_3"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1773889306,
|
"lastModified": 1777713215,
|
||||||
"narHash": "sha256-PAqwnsBSI9SVC2QugvQ3xeYCB0otOwCacB1ueQj2tgw=",
|
"narHash": "sha256-8GzXDOXckDWwST8TY5DbwYFjdvQLlP7K9CLSVx6iTTo=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "disko",
|
"repo": "disko",
|
||||||
"rev": "5ad85c82cc52264f4beddc934ba57f3789f28347",
|
"rev": "63b4e7e6cf75307c1d26ac3762b886b5b0247267",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -126,11 +126,11 @@
|
|||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"dir": "pkgs/firefox-addons",
|
"dir": "pkgs/firefox-addons",
|
||||||
"lastModified": 1776199335,
|
"lastModified": 1778385775,
|
||||||
"narHash": "sha256-ImihxU7ReZZuNdrASq8qzOmmO/UQtkuqQ9V9KKb1dD0=",
|
"narHash": "sha256-n0MUvWA2SML/qBB4hpShQ7i+i961MX4oPtaQfYo0+uU=",
|
||||||
"owner": "rycee",
|
"owner": "rycee",
|
||||||
"repo": "nur-expressions",
|
"repo": "nur-expressions",
|
||||||
"rev": "95066e56aaa948f170747f57a20c99511a953eed",
|
"rev": "268324916742a48cd03b94fd63f2822d6b66d519",
|
||||||
"type": "gitlab"
|
"type": "gitlab"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -278,16 +278,16 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1775425411,
|
"lastModified": 1780361225,
|
||||||
"narHash": "sha256-KY6HsebJHEe5nHOWP7ur09mb0drGxYSzE3rQxy62rJo=",
|
"narHash": "sha256-wnV9ttf4fPWNonBIQmvlrSlNpQYgx5HgWWd007mwIFA=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "0d02ec1d0a05f88ef9e74b516842900c41f0f2fe",
|
"rev": "e28654b71096e08c019d4861ca26acb646f583d8",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"ref": "release-25.11",
|
"ref": "release-26.05",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
@@ -301,12 +301,12 @@
|
|||||||
"nixpkgs-regression": "nixpkgs-regression"
|
"nixpkgs-regression": "nixpkgs-regression"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1775583600,
|
"lastModified": 1778177425,
|
||||||
"narHash": "sha256-/shs/3GA4R3rxhhqpPbEMnDZKbCvf3VpwnHB75nkTcI=",
|
"narHash": "sha256-oyHvP5HDRe59opmjTrq2ED9lh+R9FrHyaCGPPNfBqWM=",
|
||||||
"rev": "e9b4735be7b90cf49767faf5c36f770ac1bdc586",
|
"rev": "f0ccb960d3ad5bff28acd9cabf8bdef885b5d52f",
|
||||||
"revCount": 24880,
|
"revCount": 25858,
|
||||||
"type": "tarball",
|
"type": "tarball",
|
||||||
"url": "https://api.flakehub.com/f/pinned/DeterminateSystems/nix-src/3.17.3/019d6913-e8c2-7128-ba76-3dc4f6b58158/source.tar.gz"
|
"url": "https://api.flakehub.com/f/pinned/DeterminateSystems/nix-src/3.20.0/019e03bc-3f83-7833-aba3-b691ef4956c7/source.tar.gz"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"type": "tarball",
|
"type": "tarball",
|
||||||
@@ -351,16 +351,16 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1761597516,
|
"lastModified": 1773222311,
|
||||||
"narHash": "sha256-wxX7u6D2rpkJLWkZ2E932SIvDJW8+ON/0Yy8+a5vsDU=",
|
"narHash": "sha256-BHoB/XpbqoZkVYZCfXJXfkR+GXFqwb/4zbWnOr2cRcU=",
|
||||||
"rev": "daf6dc47aa4b44791372d6139ab7b25269184d55",
|
"rev": "0590cd39f728e129122770c029970378a79d076a",
|
||||||
"revCount": 811874,
|
"revCount": 909248,
|
||||||
"type": "tarball",
|
"type": "tarball",
|
||||||
"url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.2505.811874%2Brev-daf6dc47aa4b44791372d6139ab7b25269184d55/019a3494-3498-707e-9086-1fb81badc7fe/source.tar.gz"
|
"url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.2511.909248%2Brev-0590cd39f728e129122770c029970378a79d076a/019ce32b-8ace-7339-b129-cceaa8dd10c6/source.tar.gz"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"type": "tarball",
|
"type": "tarball",
|
||||||
"url": "https://flakehub.com/f/NixOS/nixpkgs/0.2505"
|
"url": "https://flakehub.com/f/NixOS/nixpkgs/0.2511"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs-23-11": {
|
"nixpkgs-23-11": {
|
||||||
@@ -397,11 +397,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-unstable": {
|
"nixpkgs-unstable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1775710090,
|
"lastModified": 1777954456,
|
||||||
"narHash": "sha256-ar3rofg+awPB8QXDaFJhJ2jJhu+KqN/PRCXeyuXR76E=",
|
"narHash": "sha256-hGdgeU2Nk87RAuZyYjyDjFL6LK7dAZN5RE9+hrDTkDU=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "4c1018dae018162ec878d42fec712642d214fdfa",
|
"rev": "549bd84d6279f9852cae6225e372cc67fb91a4c1",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -413,12 +413,12 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1775464765,
|
"lastModified": 1777826146,
|
||||||
"narHash": "sha256-nex6TL2x1/sVHCyDWcvl1t/dbTedb9bAGC4DLf/pmYk=",
|
"narHash": "sha256-wQ/iN5Zp5VIa3ebBibijPnLyKhor+xEbDy4d0goa9Zs=",
|
||||||
"rev": "83e29f2b8791f6dec20804382fcd9a666d744c07",
|
"rev": "73c703c22422b8951895a960959dbbaca7296492",
|
||||||
"revCount": 975711,
|
"revCount": 991389,
|
||||||
"type": "tarball",
|
"type": "tarball",
|
||||||
"url": "https://api.flakehub.com/f/pinned/DeterminateSystems/nixpkgs-weekly/0.1.975711%2Brev-83e29f2b8791f6dec20804382fcd9a666d744c07/019d6689-cde2-7061-b044-e0ef61ade488/source.tar.gz"
|
"url": "https://api.flakehub.com/f/pinned/DeterminateSystems/nixpkgs-weekly/0.1.991389%2Brev-73c703c22422b8951895a960959dbbaca7296492/019df6c8-934b-7d40-b402-027bb5def30f/source.tar.gz"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"type": "tarball",
|
"type": "tarball",
|
||||||
@@ -443,16 +443,16 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_4": {
|
"nixpkgs_4": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776067740,
|
"lastModified": 1780203844,
|
||||||
"narHash": "sha256-B35lpsqnSZwn1Lmz06BpwF7atPgFmUgw1l8KAV3zpVQ=",
|
"narHash": "sha256-K5sT4jTpGs15ADhviMKNBH38REpPf5Q6mM1+N6cArVE=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "7e495b747b51f95ae15e74377c5ce1fe69c1765f",
|
"rev": "b51242d7d43689db2f3be91bd05d5b24fbb469c4",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"ref": "nixos-25.11",
|
"ref": "nixos-26.05",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
@@ -501,11 +501,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776119890,
|
"lastModified": 1777944972,
|
||||||
"narHash": "sha256-Zm6bxLNnEOYuS/SzrAGsYuXSwk3cbkRQZY0fJnk8a5M=",
|
"narHash": "sha256-VfGRo1qTBKOe3s2gOv8LSoA6Fk19PvBlwQ1ECN0Evn8=",
|
||||||
"owner": "Mic92",
|
"owner": "Mic92",
|
||||||
"repo": "sops-nix",
|
"repo": "sops-nix",
|
||||||
"rev": "d4971dd58c6627bfee52a1ad4237637c0a2fb0cd",
|
"rev": "c591bf665727040c6cc5cb409079acb22dcce33c",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
description = "NixOS Hosts";
|
description = "NixOS Hosts";
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
|
||||||
nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
|
nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
disko.url = "github:nix-community/disko";
|
disko.url = "github:nix-community/disko";
|
||||||
determinate.url = "github:determinatesystems/determinate";
|
determinate.url = "github:determinatesystems/determinate";
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
home-manager = {
|
home-manager = {
|
||||||
url = "github:nix-community/home-manager/release-25.11";
|
url = "github:nix-community/home-manager/release-26.05";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
apple-silicon = {
|
apple-silicon = {
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
darwin = {
|
darwin = {
|
||||||
url = "github:nix-darwin/nix-darwin/nix-darwin-25.11";
|
url = "github:nix-darwin/nix-darwin/nix-darwin-26.05";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ let
|
|||||||
inherit (lib.${namespace}) enabled;
|
inherit (lib.${namespace}) enabled;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
home.stateVersion = "25.11";
|
home.stateVersion = "26.05";
|
||||||
|
|
||||||
reichard = {
|
reichard = {
|
||||||
user = {
|
user = {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ let
|
|||||||
inherit (lib.${namespace}) enabled;
|
inherit (lib.${namespace}) enabled;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
home.stateVersion = "25.11";
|
home.stateVersion = "26.05";
|
||||||
|
|
||||||
reichard = {
|
reichard = {
|
||||||
user = {
|
user = {
|
||||||
@@ -34,15 +34,14 @@ in
|
|||||||
git = enabled;
|
git = enabled;
|
||||||
k9s = enabled;
|
k9s = enabled;
|
||||||
nvim = enabled;
|
nvim = enabled;
|
||||||
opencode = enabled;
|
|
||||||
pi = enabled;
|
pi = enabled;
|
||||||
zk = enabled;
|
zk = enabled;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
services = {
|
services = {
|
||||||
# nunc = enabled;
|
|
||||||
sketchybar = enabled;
|
sketchybar = enabled;
|
||||||
|
open-proxy.server = enabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
security = {
|
security = {
|
||||||
@@ -54,8 +53,6 @@ in
|
|||||||
programs.jq = enabled;
|
programs.jq = enabled;
|
||||||
programs.pandoc = enabled;
|
programs.pandoc = enabled;
|
||||||
home.packages = with pkgs; [
|
home.packages = with pkgs; [
|
||||||
# colima
|
|
||||||
docker
|
|
||||||
keycastr
|
keycastr
|
||||||
reichard.slack-cli
|
reichard.slack-cli
|
||||||
_1password-cli
|
_1password-cli
|
||||||
|
|||||||
@@ -33,6 +33,16 @@ else
|
|||||||
echo " [✓] VM SOCKS Proxy Already Running"
|
echo " [✓] VM SOCKS Proxy Already Running"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Reverse tunnel so the VM's `open`/`xdg-open` reach open-proxy serve on this host.
|
||||||
|
if ! pgrep -f "ssh -N -R 7777:127.0.0.1:7777 adios-cs" > /dev/null; then
|
||||||
|
echo " [*] VM Open Proxy Starting..."
|
||||||
|
ssh -N -R 7777:127.0.0.1:7777 adios-cs &> /dev/null &
|
||||||
|
disown
|
||||||
|
echo " [✓] VM Open Proxy Started"
|
||||||
|
else
|
||||||
|
echo " [✓] VM Open Proxy Already Running"
|
||||||
|
fi
|
||||||
|
|
||||||
echo -e " [*] Connecting..."
|
echo -e " [*] Connecting..."
|
||||||
|
|
||||||
# Connect to VM
|
# Connect to VM
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ let
|
|||||||
inherit (lib.${namespace}) enabled;
|
inherit (lib.${namespace}) enabled;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
home.stateVersion = "25.11";
|
home.stateVersion = "26.05";
|
||||||
|
|
||||||
reichard = {
|
reichard = {
|
||||||
user = {
|
user = {
|
||||||
@@ -20,7 +20,7 @@ in
|
|||||||
services = {
|
services = {
|
||||||
ssh-agent = enabled;
|
ssh-agent = enabled;
|
||||||
fusuma = enabled;
|
fusuma = enabled;
|
||||||
swww = enabled;
|
awww = enabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
security = {
|
security = {
|
||||||
@@ -58,6 +58,7 @@ in
|
|||||||
|
|
||||||
home.packages = with pkgs; [
|
home.packages = with pkgs; [
|
||||||
orca-slicer
|
orca-slicer
|
||||||
|
reichard.tuxguitar
|
||||||
];
|
];
|
||||||
|
|
||||||
dconf = {
|
dconf = {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ let
|
|||||||
inherit (lib.${namespace}) enabled;
|
inherit (lib.${namespace}) enabled;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
home.stateVersion = "25.11";
|
home.stateVersion = "26.05";
|
||||||
|
|
||||||
reichard = {
|
reichard = {
|
||||||
user = {
|
user = {
|
||||||
@@ -17,6 +17,12 @@ in
|
|||||||
|
|
||||||
services = {
|
services = {
|
||||||
ssh-agent = enabled;
|
ssh-agent = enabled;
|
||||||
|
open-proxy.client = enabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
security = {
|
||||||
|
pass-keyring = enabled;
|
||||||
|
sops = enabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
programs = {
|
programs = {
|
||||||
@@ -25,6 +31,7 @@ in
|
|||||||
enable = true;
|
enable = true;
|
||||||
customFastFetchLogo = ./prophet.txt;
|
customFastFetchLogo = ./prophet.txt;
|
||||||
};
|
};
|
||||||
|
conduit = enabled;
|
||||||
btop = enabled;
|
btop = enabled;
|
||||||
claude-code = enabled;
|
claude-code = enabled;
|
||||||
direnv = enabled;
|
direnv = enabled;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ let
|
|||||||
inherit (lib.${namespace}) enabled;
|
inherit (lib.${namespace}) enabled;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
home.stateVersion = "25.11";
|
home.stateVersion = "26.05";
|
||||||
|
|
||||||
reichard = {
|
reichard = {
|
||||||
user = {
|
user = {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ let
|
|||||||
inherit (lib.${namespace}) enabled;
|
inherit (lib.${namespace}) enabled;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
home.stateVersion = "25.11";
|
home.stateVersion = "26.05";
|
||||||
|
|
||||||
reichard = {
|
reichard = {
|
||||||
user = {
|
user = {
|
||||||
@@ -25,6 +25,7 @@ in
|
|||||||
btop = enabled;
|
btop = enabled;
|
||||||
direnv = enabled;
|
direnv = enabled;
|
||||||
tmux = enabled;
|
tmux = enabled;
|
||||||
|
git = enabled;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
{ lib
|
{ lib
|
||||||
, config
|
, config
|
||||||
, namespace
|
, namespace
|
||||||
|
, osConfig
|
||||||
, ...
|
, ...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
inherit (lib.${namespace}) enabled;
|
inherit (lib.${namespace}) enabled;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
home.stateVersion = "25.11";
|
home.stateVersion = "26.05";
|
||||||
|
|
||||||
reichard = {
|
reichard = {
|
||||||
user = {
|
user = {
|
||||||
@@ -27,6 +28,7 @@ in
|
|||||||
terminal = {
|
terminal = {
|
||||||
bash = enabled;
|
bash = enabled;
|
||||||
btop = enabled;
|
btop = enabled;
|
||||||
|
conduit = enabled;
|
||||||
direnv = enabled;
|
direnv = enabled;
|
||||||
git = enabled;
|
git = enabled;
|
||||||
k9s = enabled;
|
k9s = enabled;
|
||||||
@@ -37,4 +39,11 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Kubernetes Secrets
|
||||||
|
sops.secrets = lib.mkIf osConfig.${namespace}.security.sops.enable {
|
||||||
|
rke2_kubeconfig = {
|
||||||
|
path = "${config.home.homeDirectory}/.kube/lin-va-kube";
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ let
|
|||||||
inherit (lib.${namespace}) enabled;
|
inherit (lib.${namespace}) enabled;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
home.stateVersion = "25.11";
|
home.stateVersion = "26.05";
|
||||||
|
|
||||||
reichard = {
|
reichard = {
|
||||||
user = {
|
user = {
|
||||||
@@ -20,7 +20,7 @@ in
|
|||||||
services = {
|
services = {
|
||||||
ssh-agent = enabled;
|
ssh-agent = enabled;
|
||||||
fusuma = enabled;
|
fusuma = enabled;
|
||||||
swww = enabled;
|
awww = enabled;
|
||||||
poweralertd = enabled;
|
poweralertd = enabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -30,7 +30,10 @@ in
|
|||||||
|
|
||||||
programs = {
|
programs = {
|
||||||
graphical = {
|
graphical = {
|
||||||
wms.hyprland = enabled;
|
wms.hyprland = {
|
||||||
|
enable = true;
|
||||||
|
menuMod = "ALT";
|
||||||
|
};
|
||||||
ghostty = enabled;
|
ghostty = enabled;
|
||||||
strawberry = enabled;
|
strawberry = enabled;
|
||||||
gimp = enabled;
|
gimp = enabled;
|
||||||
@@ -50,7 +53,7 @@ in
|
|||||||
git = enabled;
|
git = enabled;
|
||||||
k9s = enabled;
|
k9s = enabled;
|
||||||
nvim = enabled;
|
nvim = enabled;
|
||||||
opencode = enabled;
|
pi = enabled;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -65,6 +68,10 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
home.packages = with pkgs; [
|
||||||
|
orca-slicer
|
||||||
|
];
|
||||||
|
|
||||||
home.pointerCursor = {
|
home.pointerCursor = {
|
||||||
gtk.enable = true;
|
gtk.enable = true;
|
||||||
name = "catppuccin-macchiato-mauve-cursors";
|
name = "catppuccin-macchiato-mauve-cursors";
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ in
|
|||||||
services = {
|
services = {
|
||||||
ssh-agent = enabled;
|
ssh-agent = enabled;
|
||||||
fusuma = enabled;
|
fusuma = enabled;
|
||||||
swww = enabled;
|
awww = enabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
security = {
|
security = {
|
||||||
|
|||||||
@@ -2,11 +2,13 @@
|
|||||||
{
|
{
|
||||||
|
|
||||||
home.packages = with pkgs; [
|
home.packages = with pkgs; [
|
||||||
sqlite-interactive
|
|
||||||
jnv
|
jnv
|
||||||
jq
|
jq
|
||||||
|
mosh
|
||||||
ncdu
|
ncdu
|
||||||
ripgrep
|
|
||||||
reichard.codexis
|
reichard.codexis
|
||||||
|
ripgrep
|
||||||
|
sqlite-interactive
|
||||||
|
unzip
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
{ config, lib, pkgs, namespace, ... }:
|
{ config
|
||||||
|
, lib
|
||||||
|
, pkgs
|
||||||
|
, namespace
|
||||||
|
, ...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
inherit (lib)
|
inherit (lib)
|
||||||
types
|
types
|
||||||
@@ -62,40 +67,47 @@ in
|
|||||||
extensions.packages = mkOpt (with lib.types; listOf package)
|
extensions.packages = mkOpt (with lib.types; listOf package)
|
||||||
(with pkgs.firefox-addons; [
|
(with pkgs.firefox-addons; [
|
||||||
bitwarden
|
bitwarden
|
||||||
|
pkgs.firefox-addons."ctrl-number-to-switch-tabs"
|
||||||
darkreader
|
darkreader
|
||||||
gruvbox-dark-theme
|
gruvbox-dark-theme
|
||||||
kagi-search
|
kagi-search
|
||||||
sponsorblock
|
sponsorblock
|
||||||
ublock-origin
|
ublock-origin
|
||||||
|
|
||||||
# bypass-paywalls-clean
|
|
||||||
]) "Extensions to install";
|
]) "Extensions to install";
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
programs.firefox = {
|
programs.firefox = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
configPath = ".mozilla/firefox";
|
||||||
|
|
||||||
inherit (cfg) policies;
|
inherit (cfg) policies;
|
||||||
|
|
||||||
profiles = {
|
profiles = {
|
||||||
${config.${namespace}.user.name} = {
|
${config.${namespace}.user.name} = {
|
||||||
inherit (cfg) extraConfig extensions;
|
inherit (cfg) extraConfig;
|
||||||
inherit (config.${namespace}.user) name;
|
inherit (config.${namespace}.user) name;
|
||||||
|
|
||||||
|
extensions = {
|
||||||
|
packages = cfg.extensions.packages;
|
||||||
|
force = true;
|
||||||
|
};
|
||||||
|
|
||||||
id = 0;
|
id = 0;
|
||||||
|
|
||||||
settings = mkMerge [
|
settings = mkMerge [
|
||||||
cfg.settings
|
cfg.settings
|
||||||
{
|
{
|
||||||
"browser.aboutConfig.showWarning" = false;
|
"browser.aboutConfig.showWarning" = false;
|
||||||
|
"extensions.autoDisableScopes" = 0;
|
||||||
|
"extensions.activeThemeID" = "{eb8c4a94-e603-49ef-8e81-73d3c4cc04ff}";
|
||||||
"browser.aboutwelcome.enabled" = false;
|
"browser.aboutwelcome.enabled" = false;
|
||||||
"browser.sessionstore.warnOnQuit" = true;
|
"browser.sessionstore.warnOnQuit" = true;
|
||||||
"browser.newtabpage.activity-stream.showSponsoredTopSites" = false;
|
"browser.newtabpage.activity-stream.showSponsoredTopSites" = false;
|
||||||
"browser.shell.checkDefaultBrowser" = false;
|
"browser.shell.checkDefaultBrowser" = false;
|
||||||
"general.smoothScroll.msdPhysics.enabled" = true;
|
"general.smoothScroll.msdPhysics.enabled" = true;
|
||||||
"intl.accept_languages" = "en-US,en";
|
"intl.accept_languages" = "en-US,en";
|
||||||
"ui.key.accelKey" = "224";
|
"ui.key.accelKey" = 91;
|
||||||
|
|
||||||
# "devtools.chrome.enabled" = true;
|
# "devtools.chrome.enabled" = true;
|
||||||
# "xpinstall.signatures.required" = false;
|
# "xpinstall.signatures.required" = false;
|
||||||
|
|||||||
@@ -1,154 +0,0 @@
|
|||||||
exec-once = uwsm app -- waybar
|
|
||||||
exec-once = uwsm app -- $terminal
|
|
||||||
exec-once = uwsm app -- firefox
|
|
||||||
|
|
||||||
general {
|
|
||||||
gaps_in = 5
|
|
||||||
gaps_out = 12
|
|
||||||
|
|
||||||
border_size = 2
|
|
||||||
|
|
||||||
# https://wiki.hyprland.org/Configuring/Variables/#variable-types for info about colors
|
|
||||||
col.active_border = rgba(33ccffee) rgba(00ff99ee) 45deg
|
|
||||||
col.inactive_border = rgba(595959aa)
|
|
||||||
|
|
||||||
# Set to true enable resizing windows by clicking and dragging on borders and gaps
|
|
||||||
resize_on_border = false
|
|
||||||
|
|
||||||
# Please see https://wiki.hyprland.org/Configuring/Tearing/ before you turn this on
|
|
||||||
allow_tearing = false
|
|
||||||
|
|
||||||
layout = dwindle
|
|
||||||
}
|
|
||||||
|
|
||||||
# https://wiki.hyprland.org/Configuring/Variables/#decoration
|
|
||||||
decoration {
|
|
||||||
rounding = 10
|
|
||||||
|
|
||||||
active_opacity = 1.0
|
|
||||||
inactive_opacity = 1.0
|
|
||||||
|
|
||||||
shadow {
|
|
||||||
enabled = true
|
|
||||||
range = 4
|
|
||||||
render_power = 3
|
|
||||||
color = rgba(1a1a1aee)
|
|
||||||
}
|
|
||||||
|
|
||||||
blur {
|
|
||||||
enabled = true
|
|
||||||
size = 3
|
|
||||||
passes = 1
|
|
||||||
vibrancy = 0.1696
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# https://wiki.hyprland.org/Configuring/Variables/#animations
|
|
||||||
animations {
|
|
||||||
enabled = yes, please :)
|
|
||||||
|
|
||||||
|
|
||||||
bezier = easeOutQuint,0.23,1,0.32,1
|
|
||||||
bezier = easeInOutCubic,0.65,0.05,0.36,1
|
|
||||||
bezier = linear,0,0,1,1
|
|
||||||
bezier = almostLinear,0.5,0.5,0.75,1.0
|
|
||||||
bezier = quick,0.15,0,0.1,1
|
|
||||||
|
|
||||||
animation = global, 1, 10, default
|
|
||||||
animation = border, 1, 5.39, easeOutQuint
|
|
||||||
animation = windows, 1, 4.79, easeOutQuint
|
|
||||||
animation = windowsIn, 1, 4.1, easeOutQuint, popin 87%
|
|
||||||
animation = windowsOut, 1, 1.49, linear, popin 87%
|
|
||||||
animation = fadeIn, 1, 1.73, almostLinear
|
|
||||||
animation = fadeOut, 1, 1.46, almostLinear
|
|
||||||
animation = fade, 1, 3.03, quick
|
|
||||||
animation = layers, 1, 3.81, easeOutQuint
|
|
||||||
animation = layersIn, 1, 4, easeOutQuint, fade
|
|
||||||
animation = layersOut, 1, 1.5, linear, fade
|
|
||||||
animation = fadeLayersIn, 1, 1.79, almostLinear
|
|
||||||
animation = fadeLayersOut, 1, 1.39, almostLinear
|
|
||||||
animation = workspaces, 1, 1.94, almostLinear, fade
|
|
||||||
animation = workspacesIn, 1, 1.21, almostLinear, fade
|
|
||||||
animation = workspacesOut, 1, 1.94, almostLinear, fade
|
|
||||||
}
|
|
||||||
|
|
||||||
# Ref https://wiki.hyprland.org/Configuring/Workspace-Rules/
|
|
||||||
# "Smart gaps" / "No gaps when only"
|
|
||||||
# uncomment all if you wish to use that.
|
|
||||||
# workspace = w[tv1], gapsout:0, gapsin:0
|
|
||||||
# workspace = f[1], gapsout:0, gapsin:0
|
|
||||||
# windowrulev2 = bordersize 0, floating:0, onworkspace:w[tv1]
|
|
||||||
# windowrulev2 = rounding 0, floating:0, onworkspace:w[tv1]
|
|
||||||
# windowrulev2 = bordersize 0, floating:0, onworkspace:f[1]
|
|
||||||
# windowrulev2 = rounding 0, floating:0, onworkspace:f[1]
|
|
||||||
|
|
||||||
# See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more
|
|
||||||
dwindle {
|
|
||||||
pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below
|
|
||||||
preserve_split = true # You probably want this
|
|
||||||
}
|
|
||||||
|
|
||||||
# See https://wiki.hyprland.org/Configuring/Master-Layout/ for more
|
|
||||||
master {
|
|
||||||
new_status = master
|
|
||||||
}
|
|
||||||
|
|
||||||
# https://wiki.hyprland.org/Configuring/Variables/#misc
|
|
||||||
misc {
|
|
||||||
force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers
|
|
||||||
disable_hyprland_logo = false # If true disables the random hyprland logo / anime girl background. :(
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#############
|
|
||||||
### INPUT ###
|
|
||||||
#############
|
|
||||||
|
|
||||||
# https://wiki.hyprland.org/Configuring/Variables/#input
|
|
||||||
input {
|
|
||||||
kb_layout = us
|
|
||||||
kb_variant =
|
|
||||||
kb_model =
|
|
||||||
kb_options =
|
|
||||||
kb_rules =
|
|
||||||
|
|
||||||
follow_mouse = 1
|
|
||||||
|
|
||||||
sensitivity = 0.0 # -1.0 - 1.0, 0 means no modification.
|
|
||||||
|
|
||||||
touchpad {
|
|
||||||
scroll_factor = 0.5
|
|
||||||
disable_while_typing = true
|
|
||||||
natural_scroll = true
|
|
||||||
clickfinger_behavior = true
|
|
||||||
tap-to-click = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# https://wiki.hyprland.org/Configuring/Variables/#gestures
|
|
||||||
gesture = 4, horizontal, workspace, invert
|
|
||||||
|
|
||||||
# Thinkpad Trackpoint
|
|
||||||
device {
|
|
||||||
name = tpps/2-elan-trackpoint
|
|
||||||
sensitivity = -0.3
|
|
||||||
}
|
|
||||||
|
|
||||||
##############################
|
|
||||||
### WINDOWS AND WORKSPACES ###
|
|
||||||
##############################
|
|
||||||
|
|
||||||
# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
|
|
||||||
# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules
|
|
||||||
|
|
||||||
# Example windowrule v1
|
|
||||||
# windowrule = float, ^(kitty)$
|
|
||||||
|
|
||||||
# Example windowrule v2
|
|
||||||
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
|
|
||||||
|
|
||||||
# Ignore maximize requests from apps. You'll probably like this.
|
|
||||||
windowrulev2 = suppressevent maximize, class:.*
|
|
||||||
|
|
||||||
# Fix some dragging issues with XWayland
|
|
||||||
windowrulev2 = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
|
|
||||||
217
modules/home/programs/graphical/wms/hyprland/config/hyprland.lua
Normal file
217
modules/home/programs/graphical/wms/hyprland/config/hyprland.lua
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
-- Hyprland config (lua backend, Hyprland 0.55+).
|
||||||
|
-- `mainMod`, `menuMod`, and the monitor(s) are injected by Nix above this file.
|
||||||
|
-- See https://wiki.hypr.land/Configuring/Start/
|
||||||
|
|
||||||
|
local terminal = "ghostty"
|
||||||
|
local menu = "wofi --show drun"
|
||||||
|
|
||||||
|
-------------------
|
||||||
|
---- AUTOSTART ----
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
hl.on("hyprland.start", function()
|
||||||
|
hl.exec_cmd("uwsm app -- waybar")
|
||||||
|
hl.exec_cmd("uwsm app -- " .. terminal)
|
||||||
|
hl.exec_cmd("uwsm app -- firefox")
|
||||||
|
end)
|
||||||
|
|
||||||
|
-----------------------
|
||||||
|
---- LOOK AND FEEL ----
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
hl.config({
|
||||||
|
general = {
|
||||||
|
gaps_in = 5,
|
||||||
|
gaps_out = 12,
|
||||||
|
|
||||||
|
border_size = 2,
|
||||||
|
|
||||||
|
col = {
|
||||||
|
active_border = { colors = { "rgba(33ccffee)", "rgba(00ff99ee)" }, angle = 45 },
|
||||||
|
inactive_border = "rgba(595959aa)",
|
||||||
|
},
|
||||||
|
|
||||||
|
resize_on_border = false,
|
||||||
|
allow_tearing = false,
|
||||||
|
|
||||||
|
layout = "dwindle",
|
||||||
|
},
|
||||||
|
|
||||||
|
decoration = {
|
||||||
|
rounding = 10,
|
||||||
|
|
||||||
|
active_opacity = 1.0,
|
||||||
|
inactive_opacity = 1.0,
|
||||||
|
|
||||||
|
shadow = {
|
||||||
|
enabled = true,
|
||||||
|
range = 4,
|
||||||
|
render_power = 3,
|
||||||
|
color = 0xee1a1a1a,
|
||||||
|
},
|
||||||
|
|
||||||
|
blur = {
|
||||||
|
enabled = true,
|
||||||
|
size = 3,
|
||||||
|
passes = 1,
|
||||||
|
vibrancy = 0.1696,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
animations = {
|
||||||
|
enabled = true,
|
||||||
|
},
|
||||||
|
|
||||||
|
dwindle = {
|
||||||
|
preserve_split = true,
|
||||||
|
},
|
||||||
|
|
||||||
|
master = {
|
||||||
|
new_status = "master",
|
||||||
|
},
|
||||||
|
|
||||||
|
misc = {
|
||||||
|
force_default_wallpaper = -1,
|
||||||
|
disable_hyprland_logo = false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
----------------------
|
||||||
|
---- ANIMATIONS ------
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
hl.curve("easeOutQuint", { type = "bezier", points = { { 0.23, 1 }, { 0.32, 1 } } })
|
||||||
|
hl.curve("easeInOutCubic", { type = "bezier", points = { { 0.65, 0.05 }, { 0.36, 1 } } })
|
||||||
|
hl.curve("linear", { type = "bezier", points = { { 0, 0 }, { 1, 1 } } })
|
||||||
|
hl.curve("almostLinear", { type = "bezier", points = { { 0.5, 0.5 }, { 0.75, 1 } } })
|
||||||
|
hl.curve("quick", { type = "bezier", points = { { 0.15, 0 }, { 0.1, 1 } } })
|
||||||
|
|
||||||
|
hl.animation({ leaf = "global", enabled = true, speed = 10, bezier = "default" })
|
||||||
|
hl.animation({ leaf = "border", enabled = true, speed = 5.39, bezier = "easeOutQuint" })
|
||||||
|
hl.animation({ leaf = "windows", enabled = true, speed = 4.79, bezier = "easeOutQuint" })
|
||||||
|
hl.animation({ leaf = "windowsIn", enabled = true, speed = 4.1, bezier = "easeOutQuint", style = "popin 87%" })
|
||||||
|
hl.animation({ leaf = "windowsOut", enabled = true, speed = 1.49, bezier = "linear", style = "popin 87%" })
|
||||||
|
hl.animation({ leaf = "fadeIn", enabled = true, speed = 1.73, bezier = "almostLinear" })
|
||||||
|
hl.animation({ leaf = "fadeOut", enabled = true, speed = 1.46, bezier = "almostLinear" })
|
||||||
|
hl.animation({ leaf = "fade", enabled = true, speed = 3.03, bezier = "quick" })
|
||||||
|
hl.animation({ leaf = "layers", enabled = true, speed = 3.81, bezier = "easeOutQuint" })
|
||||||
|
hl.animation({ leaf = "layersIn", enabled = true, speed = 4, bezier = "easeOutQuint", style = "fade" })
|
||||||
|
hl.animation({ leaf = "layersOut", enabled = true, speed = 1.5, bezier = "linear", style = "fade" })
|
||||||
|
hl.animation({ leaf = "fadeLayersIn", enabled = true, speed = 1.79, bezier = "almostLinear" })
|
||||||
|
hl.animation({ leaf = "fadeLayersOut", enabled = true, speed = 1.39, bezier = "almostLinear" })
|
||||||
|
hl.animation({ leaf = "workspaces", enabled = true, speed = 1.94, bezier = "almostLinear", style = "fade" })
|
||||||
|
hl.animation({ leaf = "workspacesIn", enabled = true, speed = 1.21, bezier = "almostLinear", style = "fade" })
|
||||||
|
hl.animation({ leaf = "workspacesOut", enabled = true, speed = 1.94, bezier = "almostLinear", style = "fade" })
|
||||||
|
|
||||||
|
---------------
|
||||||
|
---- INPUT ----
|
||||||
|
---------------
|
||||||
|
|
||||||
|
hl.config({
|
||||||
|
input = {
|
||||||
|
kb_layout = "us",
|
||||||
|
kb_variant = "",
|
||||||
|
kb_model = "",
|
||||||
|
kb_options = "",
|
||||||
|
kb_rules = "",
|
||||||
|
|
||||||
|
follow_mouse = 1,
|
||||||
|
sensitivity = 0.0,
|
||||||
|
|
||||||
|
touchpad = {
|
||||||
|
scroll_factor = 0.5,
|
||||||
|
disable_while_typing = true,
|
||||||
|
natural_scroll = true,
|
||||||
|
clickfinger_behavior = true,
|
||||||
|
tap_to_click = false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
-- 4-finger horizontal swipe to switch workspaces. The old `invert` modifier was
|
||||||
|
-- removed in the 0.51 gesture rework; flip the physical swipe direction if needed.
|
||||||
|
hl.gesture({ fingers = 4, direction = "horizontal", action = "workspace" })
|
||||||
|
|
||||||
|
-- Thinkpad Trackpoint
|
||||||
|
hl.device({ name = "tpps/2-elan-trackpoint", sensitivity = -0.3 })
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
---- KEYBINDINGS ----
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
-- Menu Mod Bindings (macOS Transition - Spotlight & Screenshots)
|
||||||
|
hl.bind(menuMod .. " + SPACE", hl.dsp.exec_cmd(menu))
|
||||||
|
hl.bind(menuMod .. " + SHIFT + 1", hl.dsp.exec_cmd("hyprshot -m output"))
|
||||||
|
hl.bind(menuMod .. " + SHIFT + 2", hl.dsp.exec_cmd("hyprshot -m window"))
|
||||||
|
hl.bind(menuMod .. " + SHIFT + 3", hl.dsp.exec_cmd("hyprshot -m region"))
|
||||||
|
hl.bind(menuMod .. " + Q", hl.dsp.window.close())
|
||||||
|
|
||||||
|
-- Primary Bindings
|
||||||
|
hl.bind(mainMod .. " + RETURN", hl.dsp.exec_cmd(terminal))
|
||||||
|
hl.bind(mainMod .. " + M", hl.dsp.exec_cmd("uwsm stop"))
|
||||||
|
hl.bind(mainMod .. " + V", hl.dsp.window.float({ action = "toggle" }))
|
||||||
|
hl.bind(mainMod .. " + P", hl.dsp.window.pin())
|
||||||
|
hl.bind(mainMod .. " + J", hl.dsp.layout("togglesplit"))
|
||||||
|
hl.bind(mainMod .. " + S", hl.dsp.workspace.toggle_special("magic"))
|
||||||
|
hl.bind(mainMod .. " + SHIFT + S", hl.dsp.window.move({ workspace = "special:magic" }))
|
||||||
|
|
||||||
|
-- Window Focus
|
||||||
|
hl.bind(mainMod .. " + left", hl.dsp.focus({ direction = "left" }))
|
||||||
|
hl.bind(mainMod .. " + right", hl.dsp.focus({ direction = "right" }))
|
||||||
|
hl.bind(mainMod .. " + up", hl.dsp.focus({ direction = "up" }))
|
||||||
|
hl.bind(mainMod .. " + down", hl.dsp.focus({ direction = "down" }))
|
||||||
|
|
||||||
|
-- Workspace switch + move active window to workspace (1-9, 0 -> 10)
|
||||||
|
for i = 1, 10 do
|
||||||
|
local key = i % 10
|
||||||
|
hl.bind(mainMod .. " + " .. key, hl.dsp.focus({ workspace = i }))
|
||||||
|
hl.bind(mainMod .. " + SHIFT + " .. key, hl.dsp.window.move({ workspace = i }))
|
||||||
|
end
|
||||||
|
|
||||||
|
hl.bind(mainMod .. " + SHIFT + right", hl.dsp.focus({ workspace = "+1" }))
|
||||||
|
hl.bind(mainMod .. " + SHIFT + left", hl.dsp.focus({ workspace = "-1" }))
|
||||||
|
|
||||||
|
-- Window move/resize with mouse
|
||||||
|
hl.bind(mainMod .. " + mouse:272", hl.dsp.window.drag(), { mouse = true })
|
||||||
|
hl.bind(mainMod .. " + mouse:273", hl.dsp.window.resize(), { mouse = true })
|
||||||
|
|
||||||
|
-- Multimedia & Brightness Keys
|
||||||
|
hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+"), { locked = true, repeating = true })
|
||||||
|
hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"), { locked = true, repeating = true })
|
||||||
|
hl.bind("XF86AudioMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"), { locked = true, repeating = true })
|
||||||
|
hl.bind("XF86AudioMicMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"), { locked = true, repeating = true })
|
||||||
|
hl.bind("XF86MonBrightnessUp", hl.dsp.exec_cmd("brightnessctl s 4%+"), { locked = true, repeating = true })
|
||||||
|
hl.bind("XF86MonBrightnessDown", hl.dsp.exec_cmd("brightnessctl s 5%-"), { locked = true, repeating = true })
|
||||||
|
|
||||||
|
-- macOS Keyboard Brightness
|
||||||
|
hl.bind(menuMod .. " + XF86MonBrightnessUp", hl.dsp.exec_cmd("brightnessctl -d kbd_backlight s 10%+"), { locked = true, repeating = true })
|
||||||
|
hl.bind(menuMod .. " + XF86MonBrightnessDown", hl.dsp.exec_cmd("brightnessctl -d kbd_backlight s 10%-"), { locked = true, repeating = true })
|
||||||
|
|
||||||
|
-- Player Controls
|
||||||
|
hl.bind("XF86AudioNext", hl.dsp.exec_cmd("playerctl next"), { locked = true })
|
||||||
|
hl.bind("XF86AudioPause", hl.dsp.exec_cmd("playerctl play-pause"), { locked = true })
|
||||||
|
hl.bind("XF86AudioPlay", hl.dsp.exec_cmd("playerctl play-pause"), { locked = true })
|
||||||
|
hl.bind("XF86AudioPrev", hl.dsp.exec_cmd("playerctl previous"), { locked = true })
|
||||||
|
|
||||||
|
--------------------------------
|
||||||
|
---- WINDOWS AND WORKSPACES ----
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
hl.window_rule({
|
||||||
|
name = "suppress-maximize-events",
|
||||||
|
match = { class = ".*" },
|
||||||
|
suppress_event = "maximize",
|
||||||
|
})
|
||||||
|
|
||||||
|
hl.window_rule({
|
||||||
|
name = "fix-xwayland-drags",
|
||||||
|
match = {
|
||||||
|
class = "^$",
|
||||||
|
title = "^$",
|
||||||
|
xwayland = true,
|
||||||
|
float = true,
|
||||||
|
fullscreen = false,
|
||||||
|
pin = false,
|
||||||
|
},
|
||||||
|
no_focus = true,
|
||||||
|
})
|
||||||
@@ -24,90 +24,27 @@ in
|
|||||||
|
|
||||||
wayland.windowManager.hyprland = {
|
wayland.windowManager.hyprland = {
|
||||||
enable = true;
|
enable = true;
|
||||||
extraConfig = builtins.readFile ./config/hyprland.conf;
|
# Lua Backend - Hyprland 0.55 deprecated hyprlang and home-manager 26.05 defaults configType to "lua".
|
||||||
settings = {
|
configType = "lua";
|
||||||
"$mainMod" = cfg.mainMod;
|
extraConfig =
|
||||||
"$menuMod" = cfg.menuMod;
|
let
|
||||||
"$terminal" = "ghostty";
|
# Quote unless the value is numeric, so scale can be `2` or `"auto"`.
|
||||||
"$menu" = "wofi --show drun";
|
luaScalar = v: if builtins.match "[0-9]+(\\.[0-9]+)?" v != null then v else ''"${v}"'';
|
||||||
|
mkMonitor =
|
||||||
|
s:
|
||||||
|
let
|
||||||
|
parts = map lib.trim (lib.splitString "," s);
|
||||||
|
field = i: if builtins.length parts > i then builtins.elemAt parts i else "";
|
||||||
|
in
|
||||||
|
''hl.monitor({ output = "${field 0}", mode = "${field 1}", position = "${field 2}", scale = ${luaScalar (field 3)} })'';
|
||||||
|
in
|
||||||
|
''
|
||||||
|
local mainMod = "${cfg.mainMod}"
|
||||||
|
local menuMod = "${cfg.menuMod}"
|
||||||
|
|
||||||
monitor = cfg.monitors;
|
${lib.concatMapStringsSep "\n" mkMonitor cfg.monitors}
|
||||||
|
''
|
||||||
bind = [
|
+ builtins.readFile ./config/hyprland.lua;
|
||||||
# Menu Mod Bindings (macOS Transition - Spotlight & Screenshots)
|
|
||||||
"$menuMod, SPACE, exec, $menu"
|
|
||||||
"$menuMod SHIFT, 1, exec, hyprshot -m output"
|
|
||||||
"$menuMod SHIFT, 2, exec, hyprshot -m window"
|
|
||||||
"$menuMod SHIFT, 3, exec, hyprshot -m region"
|
|
||||||
"$menuMod, Q, killactive"
|
|
||||||
|
|
||||||
# Primary Bindings
|
|
||||||
"$mainMod, RETURN, exec, $terminal"
|
|
||||||
"$mainMod, M, exit"
|
|
||||||
"$mainMod, V, togglefloating"
|
|
||||||
"$mainMod, P, pin"
|
|
||||||
"$mainMod, J, togglesplit"
|
|
||||||
"$mainMod, S, togglespecialworkspace, magic"
|
|
||||||
"$mainMod SHIFT, S, movetoworkspace, special:magic"
|
|
||||||
|
|
||||||
# Window Focus
|
|
||||||
"$mainMod, left, movefocus, l"
|
|
||||||
"$mainMod, right, movefocus, r"
|
|
||||||
"$mainMod, up, movefocus, u"
|
|
||||||
"$mainMod, down, movefocus, d"
|
|
||||||
|
|
||||||
# Workspace Switch
|
|
||||||
"$mainMod, 1, workspace, 1"
|
|
||||||
"$mainMod, 2, workspace, 2"
|
|
||||||
"$mainMod, 3, workspace, 3"
|
|
||||||
"$mainMod, 4, workspace, 4"
|
|
||||||
"$mainMod, 5, workspace, 5"
|
|
||||||
"$mainMod, 6, workspace, 6"
|
|
||||||
"$mainMod, 7, workspace, 7"
|
|
||||||
"$mainMod, 8, workspace, 8"
|
|
||||||
"$mainMod, 9, workspace, 9"
|
|
||||||
"$mainMod, 0, workspace, 10"
|
|
||||||
|
|
||||||
# Window Workspace Move
|
|
||||||
"$mainMod SHIFT, 1, movetoworkspace, 1"
|
|
||||||
"$mainMod SHIFT, 2, movetoworkspace, 2"
|
|
||||||
"$mainMod SHIFT, 3, movetoworkspace, 3"
|
|
||||||
"$mainMod SHIFT, 4, movetoworkspace, 4"
|
|
||||||
"$mainMod SHIFT, 5, movetoworkspace, 5"
|
|
||||||
"$mainMod SHIFT, 6, movetoworkspace, 6"
|
|
||||||
"$mainMod SHIFT, 7, movetoworkspace, 7"
|
|
||||||
"$mainMod SHIFT, 8, movetoworkspace, 8"
|
|
||||||
"$mainMod SHIFT, 9, movetoworkspace, 9"
|
|
||||||
"$mainMod SHIFT, 0, movetoworkspace, 10"
|
|
||||||
"$mainMod SHIFT, right, workspace, +1"
|
|
||||||
"$mainMod SHIFT, left, workspace, -1"
|
|
||||||
];
|
|
||||||
bindm = [
|
|
||||||
# Window Resizing
|
|
||||||
"$mainMod, mouse:272, movewindow"
|
|
||||||
"$mainMod, mouse:273, resizewindow"
|
|
||||||
];
|
|
||||||
bindel = [
|
|
||||||
# Multimedia & Brightness Keys
|
|
||||||
",XF86AudioRaiseVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+"
|
|
||||||
",XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"
|
|
||||||
",XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"
|
|
||||||
",XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"
|
|
||||||
",XF86MonBrightnessUp, exec, brightnessctl s 4%+"
|
|
||||||
",XF86MonBrightnessDown, exec, brightnessctl s 5%-"
|
|
||||||
|
|
||||||
# macOS Keyboard Brightness
|
|
||||||
"$menuMod, XF86MonBrightnessUp, exec, brightnessctl -d kbd_backlight s 10%+"
|
|
||||||
"$menuMod, XF86MonBrightnessDown, exec, brightnessctl -d kbd_backlight s 10%-"
|
|
||||||
];
|
|
||||||
bindl = [
|
|
||||||
# Player Controls
|
|
||||||
", XF86AudioNext, exec, playerctl next"
|
|
||||||
", XF86AudioPause, exec, playerctl play-pause"
|
|
||||||
", XF86AudioPlay, exec, playerctl play-pause"
|
|
||||||
", XF86AudioPrev, exec, playerctl previous"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
programs.waybar = {
|
programs.waybar = {
|
||||||
@@ -117,9 +54,9 @@ in
|
|||||||
{
|
{
|
||||||
layer = "top";
|
layer = "top";
|
||||||
position = "top";
|
position = "top";
|
||||||
mod = "dock";
|
mode = "dock";
|
||||||
exclusive = true;
|
exclusive = true;
|
||||||
passtrough = false;
|
passthrough = false;
|
||||||
gtk-layer-shell = true;
|
gtk-layer-shell = true;
|
||||||
height = 0;
|
height = 0;
|
||||||
modules-left = [
|
modules-left = [
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
MODEL="qwen3-coder-next-80b-instruct"
|
MODEL="qwen3.6-27b-vllm-180k-cuda0"
|
||||||
SYSTEM_PROMPT="You are a shell command expert. Given a natural language query, generate a single shell command that accomplishes the task."
|
SYSTEM_PROMPT="You are a shell command expert. Given a natural language query, generate a single shell command that accomplishes the task."
|
||||||
|
|
||||||
# Colors
|
# Colors
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ in
|
|||||||
profileExtra = ''
|
profileExtra = ''
|
||||||
export COLORTERM=truecolor
|
export COLORTERM=truecolor
|
||||||
SHELL="$BASH"
|
SHELL="$BASH"
|
||||||
PATH=~/.bin:$PATH
|
PATH=~/.local/bin:$PATH
|
||||||
bind "set show-mode-in-prompt on"
|
bind "set show-mode-in-prompt on"
|
||||||
|
|
||||||
set -o vi || true
|
set -o vi || true
|
||||||
|
|||||||
38
modules/home/programs/terminal/conduit/default.nix
Normal file
38
modules/home/programs/terminal/conduit/default.nix
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{ lib
|
||||||
|
, pkgs
|
||||||
|
, config
|
||||||
|
, namespace
|
||||||
|
, ...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
inherit (lib) mkIf;
|
||||||
|
cfg = config.${namespace}.programs.terminal.conduit;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.${namespace}.programs.terminal.conduit = {
|
||||||
|
enable = lib.mkEnableOption "conduit";
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
# Add Conduit
|
||||||
|
home.packages = with pkgs; [
|
||||||
|
reichard.conduit
|
||||||
|
];
|
||||||
|
|
||||||
|
# Define Conduit Configuration
|
||||||
|
sops = {
|
||||||
|
secrets.conduit_apikey = {
|
||||||
|
sopsFile = lib.snowfall.fs.get-file "secrets/common/evanreichard.yaml";
|
||||||
|
};
|
||||||
|
templates."conduit.json" = {
|
||||||
|
path = "${config.xdg.configHome}/conduit/config.json";
|
||||||
|
content = builtins.toJSON {
|
||||||
|
server = "https://conduit.va.reichard.io";
|
||||||
|
api_key = "${config.sops.placeholder.conduit_apikey}";
|
||||||
|
log_level = "info";
|
||||||
|
log_format = "text";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -2,3 +2,5 @@ _scratch
|
|||||||
.direnv
|
.direnv
|
||||||
.envrc
|
.envrc
|
||||||
.agents
|
.agents
|
||||||
|
.pi
|
||||||
|
.nvim.lua
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ in
|
|||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
programs.git = {
|
programs.git = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
lfs.enable = true;
|
||||||
settings = {
|
settings = {
|
||||||
aliases = {
|
aliases = {
|
||||||
lg = "log --graph --abbrev-commit --decorate --date=relative --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all -n 15";
|
lg = "log --graph --abbrev-commit --decorate --date=relative --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all -n 15";
|
||||||
|
|||||||
36
modules/home/programs/terminal/glimpse/default.nix
Executable file
36
modules/home/programs/terminal/glimpse/default.nix
Executable file
@@ -0,0 +1,36 @@
|
|||||||
|
{ lib
|
||||||
|
, pkgs
|
||||||
|
, config
|
||||||
|
, namespace
|
||||||
|
, ...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
inherit (lib) mkIf;
|
||||||
|
cfg = config.${namespace}.programs.terminal.glimpse;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.${namespace}.programs.terminal.glimpse = {
|
||||||
|
enable = lib.mkEnableOption "glimpse";
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
# Add Glimpse
|
||||||
|
home.packages = with pkgs; [
|
||||||
|
reichard.glimpse
|
||||||
|
];
|
||||||
|
|
||||||
|
# Define Glimpse Configuration
|
||||||
|
sops = {
|
||||||
|
secrets.kagi_token = {
|
||||||
|
sopsFile = lib.snowfall.fs.get-file "secrets/common/evanreichard.yaml";
|
||||||
|
};
|
||||||
|
templates."glimpse.json" = {
|
||||||
|
path = "${config.home.homeDirectory}/.config/glimpse/config.json";
|
||||||
|
content = builtins.toJSON {
|
||||||
|
search.provider = "kagi";
|
||||||
|
providers.kagi.token = "${config.sops.placeholder.kagi_token}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -2,11 +2,7 @@
|
|||||||
local diagnostics_active = true
|
local diagnostics_active = true
|
||||||
local toggle_diagnostics = function()
|
local toggle_diagnostics = function()
|
||||||
diagnostics_active = not diagnostics_active
|
diagnostics_active = not diagnostics_active
|
||||||
if diagnostics_active then
|
vim.diagnostic.enable(diagnostics_active)
|
||||||
vim.diagnostic.enable()
|
|
||||||
else
|
|
||||||
vim.diagnostic.disable()
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local diagnostics_loclist_active = false
|
local diagnostics_loclist_active = false
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
local llm_endpoint = "https://llm-api.va.reichard.io"
|
local llm_endpoint = "https://llm-api.va.reichard.io"
|
||||||
local llm_assistant_model = "qwen3.5-27b-thinking"
|
local llm_assistant_model = "qwen3.6-27b-vllm-75k-cuda0"
|
||||||
local llm_infill_model = llm_assistant_model
|
local llm_infill_model = "qwen3.5-4b-cuda1"
|
||||||
local current_fim = "copilot"
|
local current_fim = "llama"
|
||||||
|
|
||||||
-- Copilot Configuration
|
-- Copilot Configuration
|
||||||
vim.g.copilot_no_tab_map = true
|
vim.g.copilot_no_tab_map = true
|
||||||
|
|||||||
@@ -241,7 +241,12 @@ setup_lsp("gopls", {
|
|||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
filetypes = { "go" },
|
filetypes = { "go" },
|
||||||
cmd = { "gopls", "-remote=auto" },
|
cmd = function(dispatchers, config)
|
||||||
|
return vim.lsp.rpc.start({ "gopls", "-remote=auto" }, dispatchers, {
|
||||||
|
cwd = config.root_dir,
|
||||||
|
env = { GOMEMLIMIT = "6GiB" },
|
||||||
|
})
|
||||||
|
end,
|
||||||
settings = {
|
settings = {
|
||||||
gopls = {
|
gopls = {
|
||||||
buildFlags = { "-tags=e2e" },
|
buildFlags = { "-tags=e2e" },
|
||||||
@@ -304,3 +309,30 @@ none_ls.setup({
|
|||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
------------------------------------------------------
|
||||||
|
---------------------- EXRC LSP ----------------------
|
||||||
|
------------------------------------------------------
|
||||||
|
|
||||||
|
vim.o.exrc = true -- native path: <cwd>/.nvim.lua on startup
|
||||||
|
|
||||||
|
local loaded = {} -- absolute path -> true
|
||||||
|
local function load_project_config(buf)
|
||||||
|
local fname = vim.api.nvim_buf_get_name(buf)
|
||||||
|
if fname == "" then return end
|
||||||
|
local found = vim.fs.find(".nvim.lua", {
|
||||||
|
upward = true,
|
||||||
|
path = vim.fs.dirname(fname),
|
||||||
|
})[1]
|
||||||
|
if not found or loaded[found] then return end
|
||||||
|
local content = vim.secure.read(found)
|
||||||
|
if content then
|
||||||
|
loaded[found] = true
|
||||||
|
local chunk, err = loadfile(found)
|
||||||
|
if chunk then chunk() else vim.notify("project config: " .. err, vim.log.levels.ERROR) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
vim.api.nvim_create_autocmd({ "BufReadPost", "BufNewFile" }, {
|
||||||
|
callback = function(args) load_project_config(args.buf) end,
|
||||||
|
})
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ local function pr_status()
|
|||||||
end
|
end
|
||||||
|
|
||||||
require("lualine").setup({
|
require("lualine").setup({
|
||||||
options = { theme = "catppuccin" },
|
options = { theme = "catppuccin-mocha" },
|
||||||
sections = {
|
sections = {
|
||||||
lualine_c = { { pr_status } },
|
lualine_c = { { pr_status } },
|
||||||
-- lualine_z = { require("opencode").statusline }
|
-- lualine_z = { require("opencode").statusline }
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
require("nvim-treesitter.configs").setup({
|
|
||||||
highlight = { enable = true, additional_vim_regex_highlighting = false },
|
|
||||||
})
|
|
||||||
vim.treesitter.language.register("markdown", "octo")
|
vim.treesitter.language.register("markdown", "octo")
|
||||||
|
|
||||||
|
vim.api.nvim_create_autocmd("FileType", {
|
||||||
|
callback = function(args)
|
||||||
|
pcall(vim.treesitter.start, args.buf)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|||||||
@@ -135,18 +135,18 @@ in
|
|||||||
golangci-lint-langserver
|
golangci-lint-langserver
|
||||||
lua-language-server
|
lua-language-server
|
||||||
nil
|
nil
|
||||||
nodePackages.eslint
|
eslint
|
||||||
nodePackages.svelte-language-server
|
svelte-language-server
|
||||||
nodePackages.typescript
|
typescript
|
||||||
nodePackages.typescript-language-server
|
typescript-language-server
|
||||||
nodePackages.vscode-langservers-extracted
|
vscode-langservers-extracted
|
||||||
pyright
|
pyright
|
||||||
python312Packages.autopep8
|
python312Packages.autopep8
|
||||||
|
|
||||||
# Formatters
|
# Formatters
|
||||||
luaformatter
|
luaformatter
|
||||||
nixpkgs-fmt
|
nixpkgs-fmt
|
||||||
nodePackages.prettier
|
prettier
|
||||||
stylua
|
stylua
|
||||||
sql-formatter
|
sql-formatter
|
||||||
|
|
||||||
@@ -177,9 +177,9 @@ in
|
|||||||
clangd = "${pkgs.clang-tools}/bin/clangd",
|
clangd = "${pkgs.clang-tools}/bin/clangd",
|
||||||
golintls = "${pkgs.golangci-lint-langserver}/bin/golangci-lint-langserver",
|
golintls = "${pkgs.golangci-lint-langserver}/bin/golangci-lint-langserver",
|
||||||
luals = "${pkgs.lua-language-server}/bin/lua-language-server",
|
luals = "${pkgs.lua-language-server}/bin/lua-language-server",
|
||||||
sveltels = "${pkgs.nodePackages.svelte-language-server}/bin/svelteserver",
|
sveltels = "${pkgs.svelte-language-server}/bin/svelteserver",
|
||||||
tsls = "${pkgs.nodePackages.typescript-language-server}/bin/typescript-language-server",
|
tsls = "${pkgs.typescript-language-server}/bin/typescript-language-server",
|
||||||
vscls = "${pkgs.nodePackages.vscode-langservers-extracted}",
|
vscls = "${pkgs.vscode-langservers-extracted}",
|
||||||
sqls = "${pkgs.sqls}/bin/sqls",
|
sqls = "${pkgs.sqls}/bin/sqls",
|
||||||
}
|
}
|
||||||
return nix_vars
|
return nix_vars
|
||||||
|
|||||||
@@ -1,63 +1,81 @@
|
|||||||
# AI Agent Guidelines
|
# AI Agent Guidelines
|
||||||
|
|
||||||
## Important Rules
|
Be cognizant of context use; this file is loaded for all LLMs. Keep guidance concise and high-signal.
|
||||||
|
|
||||||
1. **Timeout for bash tool**: The `bash` tool MUST have a timeout specified. Without a timeout, the tool will hang indefinitely and cause the task to fail.
|
## Critical Rules
|
||||||
|
|
||||||
2. **File writing**: Do NOT use `cat` with heredocs to write files. Use the `write` tool instead (or `edit` for modifications).
|
1. **Bash timeouts**: Every `bash` tool call MUST specify a timeout.
|
||||||
|
```bash
|
||||||
|
bash(command="some command", timeout=30)
|
||||||
|
```
|
||||||
|
|
||||||
## Example of Correct Usage
|
2. **File writing**: Do NOT use `cat` with heredocs to write files. Use `write` for new/rewritten files and `edit` for targeted modifications.
|
||||||
|
|
||||||
### Incorrect (will hang):
|
3. **Scratch files**: Put temporary scripts, plans, notes, and reusable exploration artifacts in `_scratch/`. It is gitignored.
|
||||||
|
|
||||||
```bash
|
4. **Missing commands**: If a tool is not installed, prefer `nix run` instead of installing it.
|
||||||
bash(command="some long-running command")
|
```bash
|
||||||
```
|
nix run nixpkgs#python3 -- script.py
|
||||||
|
```
|
||||||
|
|
||||||
### Correct (with timeout):
|
## Asking Questions
|
||||||
|
|
||||||
```bash
|
If a task is ambiguous, underspecified, or you foresee a non-obvious tradeoff during implementation, **surface it before coding** rather than guessing and producing rework. Treat this as always-on; an explicit "any questions?" is never required.
|
||||||
bash(command="some command", timeout=30)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Incorrect (file writing):
|
## Context Discipline
|
||||||
|
|
||||||
```bash
|
Prefer a **search → targeted read** pattern:
|
||||||
bash(command="cat > file.txt << 'EOF'\ncontent\nEOF")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Correct (file writing):
|
1. Search with `rg -n` / `grep -n` to find relevant line numbers.
|
||||||
|
2. Read only the needed range with `read(path, offset, limit)`.
|
||||||
|
|
||||||
```bash
|
Full-file reads are fine when genuinely needed, but avoid them as the default reflex.
|
||||||
write(path="file.txt", content="content")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Principles
|
## Principles
|
||||||
|
|
||||||
1. **KISS / YAGNI** - Keep solutions simple and straightforward. Don't introduce abstractions, generics, or indirection unless there is a concrete, immediate need. Prefer obvious code over clever code.
|
1. **Priority order**: When goals conflict, optimize in this order:
|
||||||
|
1. **Correctness** — solve the actual use case, including the realistic failure modes (not just the happy path).
|
||||||
|
2. **Maintainability / readability** — non-negotiable. Code is read far more than it is written; clarity wins over cleverness.
|
||||||
|
3. **Abstraction & polish** — only after the above are solid, and only when a concrete need justifies it.
|
||||||
|
|
||||||
2. **Maintain AGENTS.md** - If the project has an `AGENTS.md`, keep it up to date as conventions or architecture evolve. However, follow the **BLUF** (Bottom Line Up Front) principle: keep it concise, actionable, and context-size conscious. Don't overload it with information that belongs in code comments or external docs.
|
All three matter, but never sacrifice (2) for (3). Prefer obvious, boring code over slick code that requires a paragraph to explain.
|
||||||
|
|
||||||
|
2. **KISS / YAGNI**: Avoid abstractions, generics, or indirection unless there is a concrete, present need. Speculative flexibility is a maintainability tax.
|
||||||
|
|
||||||
|
3. **Maintain AGENTS.md**: Keep project guidance up to date, but BLUF: concise, actionable, and context-size conscious.
|
||||||
|
|
||||||
|
4. **Rephrase over append**: When extending existing content (docs, comments, prose, code), prefer rephrasing to capture the new intent over tacking on more verbosity.
|
||||||
|
|
||||||
|
5. **Positive framing over prohibition**: State what _to_ do, not what _not_ to do. Default to omitting an instruction entirely rather than adding a "don't do X" rule — omission costs less context and avoids the failure mode where deleting a prohibition gets inverted into a mandate. Reserve explicit prohibitions for cases where the wrong behavior is a likely default that positive guidance alone can't redirect.
|
||||||
|
|
||||||
|
6. **Knowledge Capture Check**: Before the final response, ask whether the task revealed a non-obvious convention, pitfall, repeatable workflow, or missing helper. If yes, briefly recommend exactly where to capture it: package/project AGENTS.md, global AGENTS.md, a skill, or a helper script. Skip this note when there is nothing meaningful.
|
||||||
|
|
||||||
## Style
|
## Style
|
||||||
|
|
||||||
### Comment Style
|
### Comment Style
|
||||||
|
|
||||||
A logical "block" of code (doesn't have to be a scope, but a cohesive group of statements responsible for something) should have a comment above it with a short "title". The title must be in **Title Case**. For example:
|
Default to **no comment**. Code should be self-explanatory through naming and structure. Only add a comment when it earns its place by explaining something the code cannot.
|
||||||
|
|
||||||
|
Write a comment when, and only when:
|
||||||
|
|
||||||
|
1. The _why_ is non-obvious (intent, constraint, workaround, surprising invariant).
|
||||||
|
2. A reader familiar with the language/codebase would otherwise stop and ask "why?".
|
||||||
|
|
||||||
|
Do not narrate _what_ the code does. Do not add Title Case section headers over logical blocks just to label them.
|
||||||
|
|
||||||
|
When a comment _is_ warranted, use a short Title Case label, a dash, and the _why_:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// Map Component Results
|
// Map Component Results - Downstream consumers expect a name-keyed lookup.
|
||||||
for _, comp := range components {
|
for _, comp := range components {
|
||||||
results[comp.Name] = comp.Result
|
results[comp.Name] = comp.Result
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If the block is more complicated or non-obvious, explain _why_ it does what it does after the title:
|
Rules for the explanation after the dash:
|
||||||
|
|
||||||
```go
|
- Keep it to **2–3 sentences max**. Never a paragraph.
|
||||||
// Map Component Results - This is needed because downstream consumers
|
- State the _why_ directly. Do not restate what the code does, recap prior context, or hedge.
|
||||||
// expect a name-keyed lookup. Without it, the renderer would fall back
|
- Do **not** hard-wrap comments at 80 columns. Up to ~120 is fine.
|
||||||
// to O(n) scans on every frame.
|
|
||||||
for _, comp := range components {
|
If a block is complex enough that it needs a heading just to be navigable, that is usually a signal to extract a well-named function instead.
|
||||||
results[comp.Name] = comp.Result
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
||||||
|
|
||||||
export default function replacePiWithClaudeCodeExtension(pi: ExtensionAPI) {
|
export default function replacePiWithClaudeCodeExtension(pi: ExtensionAPI) {
|
||||||
pi.on("before_agent_start", async (event) => {
|
pi.on("before_agent_start", async (event, ctx) => {
|
||||||
// Replace "pi" With "claude code" - Exclude Literal ".pi" (e.g. Paths)
|
// Anthropic-Only Guard - Other providers (e.g. OpenAI, Google) should
|
||||||
|
// see the unmodified pi system prompt.
|
||||||
|
if (ctx.model?.provider !== "anthropic") {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace "pi" With "claude code"
|
||||||
const transformedSystemPrompt = event.systemPrompt.replace(
|
const transformedSystemPrompt = event.systemPrompt.replace(
|
||||||
/(?<!\.)pi/gi,
|
/(^|\s)pi(?![\w-])/gi,
|
||||||
"claude code",
|
"$1claude code",
|
||||||
);
|
);
|
||||||
|
|
||||||
if (transformedSystemPrompt === event.systemPrompt) {
|
if (transformedSystemPrompt === event.systemPrompt) {
|
||||||
|
|||||||
@@ -1,19 +1,34 @@
|
|||||||
---
|
---
|
||||||
name: address-gh-review
|
name: address-gh-review
|
||||||
description: 'Fetch and address unresolved GitHub PR review comments. Use when user asks to handle PR reviews, address review feedback, mentions "/address-gh-review", or wants to see what reviewers requested. Fetches unresolved threads, presents an actionable summary, and lets the user select which items to address.'
|
description: 'Fetch and address unresolved GitHub PR review comments. Use when user asks to handle PR reviews, address review feedback, mentions "/address-gh-review", or wants to see what reviewers requested. Fetches unresolved threads, presents an actionable summary, lets the user select which items to address, and resolves threads on GitHub.'
|
||||||
---
|
---
|
||||||
|
|
||||||
# GitHub PR Review
|
# GitHub PR Review
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Fetch unresolved review threads from the current PR, consolidate them into actionable items, and address selected items — each as a separate commit.
|
Fetch unresolved review threads from the current PR, consolidate them into actionable items, address selected items (each as a separate commit), and resolve threads on GitHub with reactions or explanatory comments.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
- `gh` CLI authenticated and in a repo with an open PR on the current branch
|
- `gh` CLI authenticated and in a repo with an open PR on the current branch
|
||||||
- The `git-commit` skill available for committing changes
|
- The `git-commit` skill available for committing changes
|
||||||
|
|
||||||
|
## Scripts
|
||||||
|
|
||||||
|
- `gh_review.sh` — Fetches unresolved threads, prints human-readable summary, and appends a compact thread mapping block at the end with item numbers, thread IDs, and comment IDs for downstream resolution.
|
||||||
|
- `gh_resolve_thread.sh` — Resolves a GitHub review thread, optionally adding reactions or a comment first.
|
||||||
|
|
||||||
|
### `gh_resolve_thread.sh` Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Thumbs-up on comments, then resolve
|
||||||
|
bash gh_resolve_thread.sh thumbsup <thread-id> [comment-id1 comment-id2 ...]
|
||||||
|
|
||||||
|
# Post a reply explaining why, then resolve
|
||||||
|
bash gh_resolve_thread.sh comment "reason" <thread-id>
|
||||||
|
```
|
||||||
|
|
||||||
## Workflow
|
## Workflow
|
||||||
|
|
||||||
### 1. Fetch Review Comments
|
### 1. Fetch Review Comments
|
||||||
@@ -26,6 +41,16 @@ bash gh_review.sh
|
|||||||
|
|
||||||
If the script fails (no PR, not authenticated, etc.), report the error and stop.
|
If the script fails (no PR, not authenticated, etc.), report the error and stop.
|
||||||
|
|
||||||
|
The script appends a thread mapping block at the end of its output:
|
||||||
|
|
||||||
|
```
|
||||||
|
# Thread Mapping (item_num | thread_id | comment_ids)
|
||||||
|
1 PRRT_kwDOKhRdjM5-yKNp PRRC_kwDOKhRdjM6849mq PRRC_kwDOKhRdjM6849mr
|
||||||
|
2 PRRT_kwDOKhRdjM5-yKOJ PRRC_kwDOKhRdjM6849nZ
|
||||||
|
```
|
||||||
|
|
||||||
|
Capture these IDs from the script output — they are needed in step 5.
|
||||||
|
|
||||||
### 2. Consolidate into Actionable Items
|
### 2. Consolidate into Actionable Items
|
||||||
|
|
||||||
Parse the output and group into actionable items. Combine threads that ask for the same change (e.g. multiple reviewers commenting on the same function about the same concern). Keep items separate when they require distinct code changes.
|
Parse the output and group into actionable items. Combine threads that ask for the same change (e.g. multiple reviewers commenting on the same function about the same concern). Keep items separate when they require distinct code changes.
|
||||||
@@ -59,6 +84,15 @@ For each selected item, in order:
|
|||||||
3. Run relevant linting/tests if applicable (e.g. `quicklint`)
|
3. Run relevant linting/tests if applicable (e.g. `quicklint`)
|
||||||
4. Commit using the `git-commit` skill — **one commit per item**
|
4. Commit using the `git-commit` skill — **one commit per item**
|
||||||
|
|
||||||
### 5. Summary
|
### 5. Resolve Threads on GitHub
|
||||||
|
|
||||||
After all selected items are addressed, print a brief summary of what was done.
|
After all code changes are committed, resolve the corresponding threads on GitHub using the thread IDs and comment IDs from the script output in step 1:
|
||||||
|
|
||||||
|
1. For **addressed items**: run `bash gh_resolve_thread.sh thumbsup <thread-id> <comment-ids...>` — adds 👍 to each comment, then resolves the thread.
|
||||||
|
2. For **skipped items**: ask the user for a brief reason, then run `bash gh_resolve_thread.sh comment "<reason>" <thread-id>` — posts an explanation, then resolves.
|
||||||
|
|
||||||
|
Only resolve threads for items the user explicitly selected (addressed or skipped). Leave untouched items unresolved.
|
||||||
|
|
||||||
|
### 6. Summary
|
||||||
|
|
||||||
|
After all selected items are addressed and threads resolved, print a brief summary of what was done (code changes + thread resolutions).
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Resolve a GitHub PR review thread, optionally adding reactions or a comment.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# gh_resolve_thread.sh thumbsup <thread-id> [comment-id1 comment-id2 ...]
|
||||||
|
# gh_resolve_thread.sh comment "reason" <thread-id>
|
||||||
|
#
|
||||||
|
# - thumbsup: adds 👍 to each supplied comment ID, then resolves the thread
|
||||||
|
# - comment: posts a reply explaining why the thread is being resolved, then resolves
|
||||||
|
#
|
||||||
|
# Requires: gh CLI authenticated, running on a branch with an open PR.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
err() { echo "error: $1" >&2; exit 1; }
|
||||||
|
|
||||||
|
command -v gh >/dev/null 2>&1 || err "'gh' CLI not found"
|
||||||
|
|
||||||
|
if [[ $# -lt 1 ]]; then
|
||||||
|
err "usage: $0 {thumbsup|comment} ..."
|
||||||
|
fi
|
||||||
|
ACTION="$1"
|
||||||
|
shift
|
||||||
|
|
||||||
|
case "$ACTION" in
|
||||||
|
thumbsup)
|
||||||
|
[[ $# -lt 1 ]] && err "usage: $0 thumbsup <thread-id> [comment-id ...]"
|
||||||
|
THREAD_ID="$1"
|
||||||
|
shift
|
||||||
|
COMMENT_IDS=("$@")
|
||||||
|
|
||||||
|
# Add thumbs-up reaction to each comment
|
||||||
|
for cid in "${COMMENT_IDS[@]+"${COMMENT_IDS[@]}"}"; do
|
||||||
|
[[ -z "$cid" ]] && continue
|
||||||
|
# shellcheck disable=SC2016 # GraphQL uses -f flags, not shell expansion
|
||||||
|
gh api graphql -f query='
|
||||||
|
mutation($subjectId: ID!, $content: ReactionContent!) {
|
||||||
|
addReaction(input: { subjectId: $subjectId, content: $content }) {
|
||||||
|
reaction { content }
|
||||||
|
}
|
||||||
|
}' -f subjectId="$cid" -f content=THUMBS_UP --jq '.data.addReaction.reaction.content' >/dev/null 2>&1 || true
|
||||||
|
done
|
||||||
|
|
||||||
|
# Resolve the thread
|
||||||
|
# shellcheck disable=SC2016 # GraphQL uses -f flags, not shell expansion
|
||||||
|
gh api graphql -f query='
|
||||||
|
mutation($threadId: ID!) {
|
||||||
|
resolveReviewThread(input: { threadId: $threadId }) {
|
||||||
|
clientMutationId
|
||||||
|
}
|
||||||
|
}' -f threadId="$THREAD_ID" --jq '.data.resolveReviewThread.clientMutationId' >/dev/null 2>&1 || err "failed to resolve thread $THREAD_ID"
|
||||||
|
|
||||||
|
echo "Resolved thread $THREAD_ID (thumbs-up on ${#COMMENT_IDS[@]} comment(s))"
|
||||||
|
;;
|
||||||
|
|
||||||
|
comment)
|
||||||
|
[[ $# -lt 2 ]] && err "usage: $0 comment \"reason\" <thread-id>"
|
||||||
|
REASON="$1"
|
||||||
|
THREAD_ID="$2"
|
||||||
|
|
||||||
|
# Post a reply on the thread
|
||||||
|
# shellcheck disable=SC2016 # GraphQL uses -f flags, not shell expansion
|
||||||
|
gh api graphql -f query='
|
||||||
|
mutation($threadId: ID!, $body: String!) {
|
||||||
|
addPullRequestReviewThreadReply(input: { pullRequestReviewThreadId: $threadId, body: $body }) {
|
||||||
|
comment { id }
|
||||||
|
}
|
||||||
|
}' -f threadId="$THREAD_ID" -f body="$REASON" --jq '.data.addPullRequestReviewThreadReply.comment.id' >/dev/null 2>&1 || err "failed to post reply on thread $THREAD_ID"
|
||||||
|
|
||||||
|
# Resolve the thread
|
||||||
|
# shellcheck disable=SC2016 # GraphQL uses -f flags, not shell expansion
|
||||||
|
gh api graphql -f query='
|
||||||
|
mutation($threadId: ID!) {
|
||||||
|
resolveReviewThread(input: { threadId: $threadId }) {
|
||||||
|
clientMutationId
|
||||||
|
}
|
||||||
|
}' -f threadId="$THREAD_ID" --jq '.data.resolveReviewThread.clientMutationId' >/dev/null 2>&1 || err "failed to resolve thread $THREAD_ID"
|
||||||
|
|
||||||
|
echo "Resolved thread $THREAD_ID (commented: $REASON)"
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
err "unknown action: $ACTION (expected thumbsup|comment)"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
# Fetch unresolved PR review threads, formatted for LLM consumption.
|
# Fetch unresolved PR review threads, formatted for LLM consumption.
|
||||||
# Omits diff hunks (the LLM can read files from the repo directly).
|
# Omits diff hunks (the LLM can read files from the repo directly).
|
||||||
# Groups comments into threads so conversation context is preserved.
|
# Groups comments into threads so conversation context is preserved.
|
||||||
|
# Appends a compact thread mapping block at the end with item numbers,
|
||||||
|
# thread IDs, and comment IDs for downstream resolution.
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
@@ -24,16 +26,18 @@ ME=$(gh api user -q .login 2>/dev/null) || err "failed to fetch GitHub user - ar
|
|||||||
OWNER=$(gh repo view --json owner -q .owner.login 2>/dev/null) || err "failed to determine repository owner"
|
OWNER=$(gh repo view --json owner -q .owner.login 2>/dev/null) || err "failed to determine repository owner"
|
||||||
REPO=$(gh repo view --json name -q .name 2>/dev/null) || err "failed to determine repository name"
|
REPO=$(gh repo view --json name -q .name 2>/dev/null) || err "failed to determine repository name"
|
||||||
|
|
||||||
# Fetch unresolved review threads via GraphQL
|
# Fetch Unresolved Review Threads via GraphQL
|
||||||
OUTPUT=$(gh api graphql -f query='
|
QUERY=$(cat <<EOF
|
||||||
query {
|
query {
|
||||||
repository(owner: "'"$OWNER"'", name: "'"$REPO"'") {
|
repository(owner: "${OWNER}", name: "${REPO}") {
|
||||||
pullRequest(number: '"$PR_NUM"') {
|
pullRequest(number: ${PR_NUM}) {
|
||||||
reviewThreads(first: 100) {
|
reviewThreads(first: 100) {
|
||||||
nodes {
|
nodes {
|
||||||
|
id
|
||||||
isResolved
|
isResolved
|
||||||
comments(first: 100) {
|
comments(first: 100) {
|
||||||
nodes {
|
nodes {
|
||||||
|
id
|
||||||
author { login }
|
author { login }
|
||||||
body
|
body
|
||||||
path
|
path
|
||||||
@@ -44,28 +48,53 @@ query {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}' --jq '
|
}
|
||||||
[
|
EOF
|
||||||
.data.repository.pullRequest.reviewThreads.nodes[]
|
)
|
||||||
| select(.isResolved == false)
|
|
||||||
| {
|
RAW_JSON=$(gh api graphql -f query="$QUERY" 2>/dev/null | jq -c '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false)') || err "GraphQL query failed - check your permissions and token scopes"
|
||||||
file: .comments.nodes[0].path,
|
|
||||||
line: .comments.nodes[0].line,
|
# Build human-readable output + thread mapping block
|
||||||
comments: [
|
OUTPUT=""
|
||||||
.comments.nodes[]
|
MAPPING=""
|
||||||
| select(.author.login != "'"$ME"'")
|
ITEM_NUM=0
|
||||||
| {author: .author.login, body: .body}
|
|
||||||
]
|
while IFS= read -r thread; do
|
||||||
}
|
[[ -z "$thread" ]] && continue
|
||||||
| select(.comments | length > 0)
|
|
||||||
]
|
THREAD_ID=$(echo "$thread" | jq -r '.id')
|
||||||
| .[]
|
FILE=$(echo "$thread" | jq -r '.comments.nodes[0].path')
|
||||||
| "## \(.file):\(.line)\n\(.comments | map("- **\(.author)**: \(.body)") | join("\n"))\n"
|
LINE=$(echo "$thread" | jq -r '.comments.nodes[0].line')
|
||||||
' 2>/dev/null) || err "GraphQL query failed - check your permissions and token scopes"
|
|
||||||
|
# Collect comment IDs (for reactions) — exclude own comments
|
||||||
|
COMMENT_IDS=$(echo "$thread" | jq -r --arg me "$ME" '[.comments.nodes[] | select(.author.login != $me) | .id] | join(" ")')
|
||||||
|
|
||||||
|
# Build human-readable comments (exclude own comments)
|
||||||
|
COMMENTS=$(echo "$thread" | jq -r --arg me "$ME" '[.comments.nodes[] | select(.author.login != $me) | "- **\(.author.login)**: \(.body)"] | join("\n")')
|
||||||
|
|
||||||
|
if [[ -z "$COMMENTS" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
ITEM_NUM=$((ITEM_NUM + 1))
|
||||||
|
|
||||||
|
# Append human-readable block
|
||||||
|
OUTPUT+="## ${FILE}:${LINE}
|
||||||
|
${COMMENTS}
|
||||||
|
|
||||||
|
"
|
||||||
|
|
||||||
|
# Append mapping line
|
||||||
|
MAPPING+="${ITEM_NUM} ${THREAD_ID} ${COMMENT_IDS}
|
||||||
|
"
|
||||||
|
done <<< "$RAW_JSON"
|
||||||
|
|
||||||
# Format output
|
# Format output
|
||||||
if [[ -z "$OUTPUT" ]]; then
|
if [[ -z "$OUTPUT" ]]; then
|
||||||
echo "No unresolved review comments found."
|
echo "No unresolved review comments found."
|
||||||
else
|
else
|
||||||
echo "$OUTPUT" | sed 's/\\n/\n/g'
|
echo "$OUTPUT"
|
||||||
|
echo "---"
|
||||||
|
echo "# Thread Mapping (item_num | thread_id | comment_ids)"
|
||||||
|
echo "$MAPPING"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
---
|
||||||
|
name: create-skill
|
||||||
|
description: 'Create a new skill in the skills directory. Use when user asks to add a skill, create a skill, or mentions "/create-skill". Produces a SKILL.md with frontmatter and optional helper scripts, following conventions from existing skills.'
|
||||||
|
---
|
||||||
|
|
||||||
|
# Create Skill
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Scaffold a new skill directory with a `SKILL.md` and optional helper scripts under the project's skills directory.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
### 1. Gather Requirements
|
||||||
|
|
||||||
|
Ask the user:
|
||||||
|
|
||||||
|
- **What does the skill do?** (trigger conditions, purpose)
|
||||||
|
- **Are there repeatable commands?** (if yes, these become scripts)
|
||||||
|
|
||||||
|
If the user already provided enough detail, skip straight to drafting.
|
||||||
|
|
||||||
|
### 2. Draft the Skill
|
||||||
|
|
||||||
|
Create `skills/<skill-name>/SKILL.md` with this structure:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
name: <skill-name>
|
||||||
|
description: "<One-liner: what it does and when to trigger. Keep under ~200 chars.>"
|
||||||
|
---
|
||||||
|
|
||||||
|
# <Skill Title>
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
[1-2 sentences on purpose and scope]
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
[Numbered steps the agent follows]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Guidelines:**
|
||||||
|
|
||||||
|
- **Be concise.** Skills are injected into agent context — every line costs tokens. Aim for the minimum needed to reliably guide the agent.
|
||||||
|
- **Use scripts for repeatable logic.** If a step involves a multi-line shell command, `jq` pipeline, or API call that won't change between runs, put it in a `.sh` file next to `SKILL.md` and reference it from the workflow. See `address-gh-review/` for an example.
|
||||||
|
- **Needs configurable values (paths, identifiers, etc.; not secrets — values are stored as plaintext files)?** Copy `assets/variable.sh` into the new skill's `scripts/` dir as-is. Callers use `variable.sh --get NAME [--require-exec RELPATH]`; the helper prints self-explaining `--set` instructions on "unset" or "set-but-invalid" and exits non-zero, so callers just propagate. The helper self-ignores its `.vars/` store on first `--set`, so no `.gitignore` setup is needed.
|
||||||
|
- **Frontmatter is required.** `name` and `description` fields. The description is what the agent uses to decide whether to load the skill, so make it specific about trigger conditions.
|
||||||
|
- **Don't over-specify.** Trust the agent to fill gaps. Document the _what_ and _when_, not every micro-step.
|
||||||
|
- **Frame positively; omit rather than prohibit.** Write what the agent _should_ do. Prefer leaving a rule out over adding "don't do X" (see AGENTS.md principle: _Positive framing over prohibition_).
|
||||||
|
- **Split workflow from reference when the reference surface grows.** If a skill accumulates lookup tables, mapping rules, or capability references that the workflow consults, move them into a sibling `<skill>/<category>/` directory (e.g. `mappings/`, `references/`) with one sub-doc per category and an index `README.md`. Keep `SKILL.md` focused on the hot path — workflow, hard rules, and a short table pointing at the sub-docs. Include a brief style guide in the index README covering (a) defer to authoritative sources (stubs, schemas, generated docs) whenever possible, (b) row/entry formatting conventions, (c) when to create a new sub-doc vs. extend an existing one.
|
||||||
|
|
||||||
|
### 3. Present for Review
|
||||||
|
|
||||||
|
Show the user the draft and wait for approval before finalizing. Apply any requested changes.
|
||||||
|
|
||||||
|
### 4. Git Add
|
||||||
|
|
||||||
|
Run `git add` on the new skill directory so the flake/tooling can discover it.
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Skill-local variable store. Values live in <skill-dir>/.vars/<NAME>.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# variable.sh --get NAME # prints value to stdout, exits 0
|
||||||
|
# # or prints a self-explaining hint to
|
||||||
|
# # stderr and exits 2 if unset.
|
||||||
|
# variable.sh --set NAME VALUE # writes value, exits 0.
|
||||||
|
#
|
||||||
|
# Callers should treat a non-zero exit as fatal; the stderr message tells
|
||||||
|
# the caller (agent or user) exactly how to populate the missing value.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
SKILL_DIR="$(dirname "$SCRIPT_DIR")"
|
||||||
|
STORE="$SKILL_DIR/.vars"
|
||||||
|
SELF="$0"
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat >&2 <<EOF
|
||||||
|
Usage:
|
||||||
|
$SELF --get NAME
|
||||||
|
$SELF --set NAME VALUE
|
||||||
|
EOF
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
case "${1:-}" in
|
||||||
|
--get)
|
||||||
|
[[ $# -eq 2 ]] || usage
|
||||||
|
name="$2"
|
||||||
|
file="$STORE/$name"
|
||||||
|
if [[ ! -f "$file" ]]; then
|
||||||
|
cat >&2 <<EOF
|
||||||
|
$SELF: $name is not set.
|
||||||
|
Ask the user for the value, then set it:
|
||||||
|
$SELF --set $name <value>
|
||||||
|
EOF
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
cat "$file"
|
||||||
|
;;
|
||||||
|
--set)
|
||||||
|
[[ $# -eq 3 ]] || usage
|
||||||
|
name="$2"; value="$3"
|
||||||
|
[[ "$name" =~ ^[A-Z][A-Z0-9_]*$ ]] || {
|
||||||
|
echo >&2 "$SELF: invalid name '$name' (must match [A-Z][A-Z0-9_]*)"
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
mkdir -p "$STORE"
|
||||||
|
# Self-ignore the store so values never get committed, even if the
|
||||||
|
# skill root lacks a .gitignore entry for .vars/.
|
||||||
|
[[ -f "$STORE/.gitignore" ]] || printf '*\n' > "$STORE/.gitignore"
|
||||||
|
printf '%s' "$value" > "$STORE/$name"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
esac
|
||||||
@@ -126,6 +126,8 @@ EOF
|
|||||||
- Imperative mood: "fix bug" not "fixes bug"
|
- Imperative mood: "fix bug" not "fixes bug"
|
||||||
- Reference issues: `Closes #123`, `Refs #456`
|
- Reference issues: `Closes #123`, `Refs #456`
|
||||||
- Keep description under 72 characters
|
- Keep description under 72 characters
|
||||||
|
- Keep commit bodies short: 4 sentences max.
|
||||||
|
- If you are unsure whether a body is useful, omit it entirely.
|
||||||
|
|
||||||
## Git Safety Protocol
|
## Git Safety Protocol
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
---
|
||||||
|
name: planning
|
||||||
|
description: 'Create a structured plan before implementing broad or non-trivial changes. Use when user requests a new feature spanning multiple areas, significant refactoring, or explicitly asks to "plan this out". Produces a plan document in _scratch/, waits for user approval, then tracks progress as tasks are completed.'
|
||||||
|
---
|
||||||
|
|
||||||
|
# Planning Workflow Skill
|
||||||
|
|
||||||
|
## When to Use
|
||||||
|
|
||||||
|
Trigger this workflow when:
|
||||||
|
- A user requests a **broad or non-trivial change** (e.g. new feature spanning frontend/backend, significant refactoring)
|
||||||
|
- A user says things like "plan this out", "create a plan", or "do this properly"
|
||||||
|
|
||||||
|
Don't trigger for routine multi-file changes like renames, simple bug fixes, or mechanical refactors.
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
### 1. Always Start with a Plan
|
||||||
|
|
||||||
|
**Before touching any code**, create a markdown plan in `_scratch/plan-[short-name].md` with:
|
||||||
|
- A clear title and status
|
||||||
|
- Clarifications section (if any unknowns)
|
||||||
|
- Phased tasks as checkboxes (`- [ ]`)
|
||||||
|
|
||||||
|
### 2. Ask Clarifying Questions First
|
||||||
|
|
||||||
|
When a requirement is **ambiguous or has multiple valid interpretations**:
|
||||||
|
- List your interpretations
|
||||||
|
- Ask the user to confirm
|
||||||
|
- Document answers in the plan's "Clarifications" section
|
||||||
|
|
||||||
|
### 3. Wait for User Approval
|
||||||
|
|
||||||
|
Present the plan and wait for the user to confirm before implementing. If the user requests changes, update the plan first.
|
||||||
|
|
||||||
|
### 4. Update the Plan as You Work
|
||||||
|
|
||||||
|
- Change `- [ ]` to `- [x]` for completed items
|
||||||
|
- Note any deviations from the original plan
|
||||||
|
- If you discover new tasks, add them with a note explaining why
|
||||||
|
|
||||||
|
### 5. Plan Document Format
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Plan: [Feature Name]
|
||||||
|
|
||||||
|
> **Status**: ⏳ In Progress | ✅ Complete | ❌ Blocked
|
||||||
|
|
||||||
|
## Clarifications
|
||||||
|
|
||||||
|
- ~~Question?~~ **Answer**
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
### Phase 1: [Name]
|
||||||
|
- [ ] Task 1
|
||||||
|
- [ ] Task 2
|
||||||
|
|
||||||
|
### Phase 2: [Name]
|
||||||
|
- [ ] Task 3
|
||||||
|
|
||||||
|
## Rollback Plan (if destructive/irreversible)
|
||||||
|
[How to undo if needed]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Anti-Patterns
|
||||||
|
|
||||||
|
- ❌ Coding before the user approves the plan
|
||||||
|
- ❌ Making assumptions about ambiguous requirements
|
||||||
|
- ❌ Plans that are too vague ("update code")
|
||||||
|
- ❌ Forgetting to update the plan as you work
|
||||||
10
modules/home/programs/terminal/pi/config/subagents/scout.md
Normal file
10
modules/home/programs/terminal/pi/config/subagents/scout.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
name: scout
|
||||||
|
description: Fast codebase reconnaissance. Reads and searches files, then returns concise findings with paths.
|
||||||
|
approved_tools:
|
||||||
|
- read
|
||||||
|
- bash
|
||||||
|
---
|
||||||
|
You are a focused codebase scout running as a subagent with isolated context.
|
||||||
|
|
||||||
|
Find the information requested by the task and return a concise report. Prefer exact file paths, symbol names, and line numbers. Do not modify files.
|
||||||
@@ -11,6 +11,74 @@ let
|
|||||||
llamaSwapConfig = import ./../../../../nixos/services/llama-swap/config.nix { inherit pkgs; };
|
llamaSwapConfig = import ./../../../../nixos/services/llama-swap/config.nix { inherit pkgs; };
|
||||||
|
|
||||||
cfg = config.${namespace}.programs.terminal.pi;
|
cfg = config.${namespace}.programs.terminal.pi;
|
||||||
|
|
||||||
|
# Nix-Owned Plugin List - Source of truth for `packages` in settings.json.
|
||||||
|
# Merged into the (mutable) settings.json on activation so pi can keep
|
||||||
|
# writing other fields (current model, etc.) without us clobbering them.
|
||||||
|
piPackages = [
|
||||||
|
"https://gitea.va.reichard.io/evan/pi-lsp.git@main"
|
||||||
|
"https://gitea.va.reichard.io/evan/pi-web.git@main"
|
||||||
|
"https://gitea.va.reichard.io/evan/pi-subagents.git@main"
|
||||||
|
"https://gitea.va.reichard.io/evan/pi-statusline.git@main"
|
||||||
|
];
|
||||||
|
|
||||||
|
piPackagesJson = pkgs.writeText "pi-packages.json" (builtins.toJSON piPackages);
|
||||||
|
|
||||||
|
# Pi Auth Api Keys - These are merged into mutable auth.json on activation.
|
||||||
|
# Add another entry here to manage another top-level provider auth key.
|
||||||
|
piAuthApiKeys = [
|
||||||
|
{
|
||||||
|
provider = "zai";
|
||||||
|
secretName = "zai_apikey";
|
||||||
|
jqVar = "zai";
|
||||||
|
sopsFile = lib.snowfall.fs.get-file "secrets/common/evanreichard.yaml";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
piAuthJqRawfiles = lib.concatStringsSep " \\\n " (
|
||||||
|
map
|
||||||
|
(
|
||||||
|
auth: ''--rawfile ${auth.jqVar} "${config.sops.secrets.${auth.secretName}.path}"''
|
||||||
|
)
|
||||||
|
piAuthApiKeys
|
||||||
|
);
|
||||||
|
|
||||||
|
piAuthJqFilter = lib.concatStringsSep " | " (
|
||||||
|
map
|
||||||
|
(
|
||||||
|
auth: ''.["${auth.provider}"] = { type: "api_key", key: ($'' + auth.jqVar + ''| rtrimstr("\n")) }''
|
||||||
|
)
|
||||||
|
piAuthApiKeys
|
||||||
|
);
|
||||||
|
|
||||||
|
piAuthMergeScript = pkgs.writeShellScript "pi-auth-merge" ''
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
PI_AUTH="$HOME/.pi/agent/auth.json"
|
||||||
|
mkdir -p "$(dirname "$PI_AUTH")"
|
||||||
|
|
||||||
|
if [ -L "$PI_AUTH" ]; then
|
||||||
|
rm "$PI_AUTH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
for secret in ${
|
||||||
|
lib.concatStringsSep " " (
|
||||||
|
map (auth: ''"${config.sops.secrets.${auth.secretName}.path}"'') piAuthApiKeys
|
||||||
|
)
|
||||||
|
}; do
|
||||||
|
if [ ! -e "$secret" ]; then
|
||||||
|
echo "Skipping pi auth merge; missing sops secret: $secret" >&2
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
[ -s "$PI_AUTH" ] || echo '{}' > "$PI_AUTH"
|
||||||
|
tmp=$(mktemp)
|
||||||
|
${pkgs.jq}/bin/jq ${piAuthJqRawfiles} \
|
||||||
|
'${piAuthJqFilter}' "$PI_AUTH" > "$tmp"
|
||||||
|
mv "$tmp" "$PI_AUTH"
|
||||||
|
chmod 600 "$PI_AUTH"
|
||||||
|
'';
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.${namespace}.programs.terminal.pi = {
|
options.${namespace}.programs.terminal.pi = {
|
||||||
@@ -18,25 +86,14 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
|
# Enable Glimpse
|
||||||
|
${namespace}.programs.terminal.glimpse.enable = true;
|
||||||
|
|
||||||
# Add Pi Coding Agent to Home Packages
|
# Add Pi Coding Agent to Home Packages
|
||||||
home.packages = with pkgs; [
|
home.packages = with pkgs; [ reichard.pi-coding-agent ];
|
||||||
reichard.pi-coding-agent
|
|
||||||
];
|
|
||||||
|
|
||||||
# Define Pi Configuration
|
# Define Pi Configuration
|
||||||
home.file = {
|
home.file = {
|
||||||
".pi/agent/models.json" = {
|
|
||||||
text = builtins.toJSON {
|
|
||||||
providers = {
|
|
||||||
"llama-swap" = {
|
|
||||||
baseUrl = "https://llm-api.va.reichard.io/v1";
|
|
||||||
api = "openai-completions";
|
|
||||||
apiKey = "none";
|
|
||||||
models = helpers.toPiModels llamaSwapConfig;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
".pi/agent/AGENTS.md" = {
|
".pi/agent/AGENTS.md" = {
|
||||||
source = ./config/AGENTS.md;
|
source = ./config/AGENTS.md;
|
||||||
};
|
};
|
||||||
@@ -44,6 +101,10 @@ in
|
|||||||
source = ./config/skills;
|
source = ./config/skills;
|
||||||
recursive = true;
|
recursive = true;
|
||||||
};
|
};
|
||||||
|
".pi/agent/subagents" = {
|
||||||
|
source = ./config/subagents;
|
||||||
|
recursive = true;
|
||||||
|
};
|
||||||
".pi/agent/prompts" = {
|
".pi/agent/prompts" = {
|
||||||
source = ./config/prompts;
|
source = ./config/prompts;
|
||||||
recursive = true;
|
recursive = true;
|
||||||
@@ -53,5 +114,86 @@ in
|
|||||||
recursive = true;
|
recursive = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Pi Models Config - Inject llama-swap API key from sops into models.json
|
||||||
|
# so pi can authenticate against the llm-api endpoint.
|
||||||
|
sops = lib.mkIf config.${namespace}.security.sops.enable {
|
||||||
|
secrets = {
|
||||||
|
"llama_swap_api_keys/pi" = {
|
||||||
|
sopsFile = lib.snowfall.fs.get-file "secrets/common/llama-swap.yaml";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// lib.listToAttrs (
|
||||||
|
map
|
||||||
|
(auth: {
|
||||||
|
name = auth.secretName;
|
||||||
|
value.sopsFile = auth.sopsFile;
|
||||||
|
})
|
||||||
|
piAuthApiKeys
|
||||||
|
);
|
||||||
|
# Pi Web Config - Sops template so the kagi token (declared by the
|
||||||
|
# glimpse module, which pi enables above) can be embedded alongside
|
||||||
|
# the non-secret searxng base URL.
|
||||||
|
templates."pi-web.json" = {
|
||||||
|
path = "${config.home.homeDirectory}/.pi/pi-web/config.json";
|
||||||
|
content = builtins.toJSON {
|
||||||
|
provider = "searxng";
|
||||||
|
kagi.token = "${config.sops.placeholder.kagi_token}";
|
||||||
|
searxng.baseUrl = "https://search.va.reichard.io";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
templates."pi-models.json" = {
|
||||||
|
path = "${config.home.homeDirectory}/.pi/agent/models.json";
|
||||||
|
content = builtins.toJSON {
|
||||||
|
providers = {
|
||||||
|
"llama-swap" = {
|
||||||
|
baseUrl = "https://llm-api.va.reichard.io/v1";
|
||||||
|
api = "openai-completions";
|
||||||
|
apiKey = config.sops.placeholder."llama_swap_api_keys/pi";
|
||||||
|
models = helpers.toPiModels llamaSwapConfig;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Merge Nix-Defined Plugins Into Mutable settings.json - We can't symlink
|
||||||
|
# this file into the nix store because pi rewrites it at runtime (e.g. to
|
||||||
|
# persist the last-used model). Instead, on every activation we use jq to
|
||||||
|
# set `.packages` from Nix while preserving every other field.
|
||||||
|
home.activation.piSettingsMerge = config.lib.dag.entryAfter [ "writeBoundary" ] ''
|
||||||
|
PI_SETTINGS="$HOME/.pi/agent/settings.json"
|
||||||
|
mkdir -p "$(dirname "$PI_SETTINGS")"
|
||||||
|
[ -s "$PI_SETTINGS" ] || echo '{}' > "$PI_SETTINGS"
|
||||||
|
tmp=$(mktemp)
|
||||||
|
${pkgs.jq}/bin/jq --slurpfile pkgs ${piPackagesJson} \
|
||||||
|
'.packages = $pkgs[0]' "$PI_SETTINGS" > "$tmp"
|
||||||
|
mv "$tmp" "$PI_SETTINGS"
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Merge Api Key Auth Into Mutable auth.json - Pi needs auth.json to stay
|
||||||
|
# writable, so merge sops-managed API keys instead of symlinking the whole
|
||||||
|
# file. Existing provider auth entries are preserved.
|
||||||
|
home.activation.piAuthMerge = lib.mkIf config.${namespace}.security.sops.enable (
|
||||||
|
config.lib.dag.entryAfter [ "sops-nix" "writeBoundary" ] ''
|
||||||
|
${piAuthMergeScript}
|
||||||
|
''
|
||||||
|
);
|
||||||
|
|
||||||
|
# Run Pi Auth Merge After Sops - During NixOS system activation, sops-nix
|
||||||
|
# can be restarted asynchronously and secrets may not exist yet. This user
|
||||||
|
# service retries the merge in the normal user systemd graph after sops-nix.
|
||||||
|
systemd.user.services.pi-auth-merge = lib.mkIf config.${namespace}.security.sops.enable {
|
||||||
|
Unit = {
|
||||||
|
Description = "Merge sops-managed Pi auth entries";
|
||||||
|
After = [ "sops-nix.service" ];
|
||||||
|
Requires = [ "sops-nix.service" ];
|
||||||
|
};
|
||||||
|
Service = {
|
||||||
|
Type = "oneshot";
|
||||||
|
ExecStart = piAuthMergeScript;
|
||||||
|
};
|
||||||
|
Install.WantedBy = [ "default.target" ];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ in
|
|||||||
toPiModels =
|
toPiModels =
|
||||||
llamaSwapConfig:
|
llamaSwapConfig:
|
||||||
let
|
let
|
||||||
textGenModels = filterAttrs (name: model: any (t: t == "coding") (model.metadata.type or [ ])) (
|
hasType = type: model: any (t: t == type) (model.metadata.type or [ ]);
|
||||||
llamaSwapConfig.models or { }
|
|
||||||
);
|
codingModels = filterAttrs (_name: model: hasType "coding" model) (llamaSwapConfig.models or { });
|
||||||
|
|
||||||
localModels = mapAttrs
|
localModels = mapAttrs
|
||||||
(
|
(
|
||||||
@@ -32,8 +32,19 @@ in
|
|||||||
else
|
else
|
||||||
{ }
|
{ }
|
||||||
)
|
)
|
||||||
|
// (
|
||||||
|
if hasType "vision" model then
|
||||||
|
{
|
||||||
|
input = [
|
||||||
|
"text"
|
||||||
|
"image"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ }
|
||||||
|
)
|
||||||
)
|
)
|
||||||
textGenModels;
|
codingModels;
|
||||||
|
|
||||||
peerModels = listToAttrs (
|
peerModels = listToAttrs (
|
||||||
flatten (
|
flatten (
|
||||||
|
|||||||
40
modules/home/security/pass-keyring/default.nix
Normal file
40
modules/home/security/pass-keyring/default.nix
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
{ config
|
||||||
|
, lib
|
||||||
|
, namespace
|
||||||
|
, pkgs
|
||||||
|
, ...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
inherit (lib) mkIf mkEnableOption;
|
||||||
|
|
||||||
|
cfg = config.${namespace}.security.pass-keyring;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.${namespace}.security.pass-keyring = {
|
||||||
|
enable = mkEnableOption "Enable pass-backed keyring";
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
home.packages = [ pkgs.pass ];
|
||||||
|
|
||||||
|
# GPG + Pass Keyring - Provides credential storage for CLI
|
||||||
|
# tools (e.g. python keyring) via pass (GPG-backed). The
|
||||||
|
# keyringrc.cfg forces keyring to use the pass backend instead
|
||||||
|
# of SecretService (which requires a working D-Bus provider).
|
||||||
|
programs.gpg.enable = true;
|
||||||
|
services.gpg-agent = {
|
||||||
|
enable = true;
|
||||||
|
enableBashIntegration = true;
|
||||||
|
pinentry.package = pkgs.pinentry-curses;
|
||||||
|
defaultCacheTtl = 86400; # 24 hours
|
||||||
|
maxCacheTtl = 604800; # 7 days
|
||||||
|
};
|
||||||
|
|
||||||
|
# Keyring Backend Config - Forces keyring to use the pass
|
||||||
|
# backend instead of SecretService (broken on headless Linux).
|
||||||
|
xdg.configFile."python_keyring/keyringrc.cfg".text = ''
|
||||||
|
[backend]
|
||||||
|
default-keyring=keyring_pass.PasswordStoreBackend
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -5,23 +5,23 @@
|
|||||||
, ...
|
, ...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
cfg = config.${namespace}.services.swww;
|
cfg = config.${namespace}.services.awww;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.${namespace}.services.swww = {
|
options.${namespace}.services.awww = {
|
||||||
enable = lib.mkEnableOption "swww wallpaper service";
|
enable = lib.mkEnableOption "awww wallpaper service";
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
home.packages = with pkgs; [
|
home.packages = with pkgs; [
|
||||||
swww
|
awww
|
||||||
];
|
];
|
||||||
|
|
||||||
systemd.user = {
|
systemd.user = {
|
||||||
services = {
|
services = {
|
||||||
swww-daemon = {
|
awww-daemon = {
|
||||||
Unit = {
|
Unit = {
|
||||||
Description = "SWWW Wallpaper Daemon";
|
Description = "AWWW Wallpaper Daemon";
|
||||||
After = [ "graphical-session.target" ];
|
After = [ "graphical-session.target" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ in
|
|||||||
|
|
||||||
Service = {
|
Service = {
|
||||||
Type = "simple";
|
Type = "simple";
|
||||||
ExecStart = "${pkgs.swww}/bin/swww-daemon";
|
ExecStart = "${pkgs.awww}/bin/awww-daemon";
|
||||||
Restart = "on-failure";
|
Restart = "on-failure";
|
||||||
RestartSec = 5;
|
RestartSec = 5;
|
||||||
};
|
};
|
||||||
@@ -39,28 +39,28 @@ in
|
|||||||
|
|
||||||
change-wallpaper = {
|
change-wallpaper = {
|
||||||
Unit = {
|
Unit = {
|
||||||
Description = "SWWW Wallpaper Changer";
|
Description = "AWWW Wallpaper Changer";
|
||||||
After = [ "swww-daemon.service" ];
|
After = [ "awww-daemon.service" ];
|
||||||
Requires = [ "swww-daemon.service" ];
|
Requires = [ "awww-daemon.service" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
Install = {
|
Install = {
|
||||||
WantedBy = [ "swww-daemon.service" ];
|
WantedBy = [ "awww-daemon.service" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
Service = {
|
Service = {
|
||||||
Type = "oneshot";
|
Type = "oneshot";
|
||||||
ExecStart = "${pkgs.writeShellScript "change-wallpaper-script" ''
|
ExecStart = "${pkgs.writeShellScript "change-wallpaper-script" ''
|
||||||
WALLPAPER=$(${pkgs.findutils}/bin/find $HOME/Wallpapers -type f | ${pkgs.coreutils}/bin/shuf -n 1)
|
WALLPAPER=$(${pkgs.findutils}/bin/find $HOME/Wallpapers -type f | ${pkgs.coreutils}/bin/shuf -n 1)
|
||||||
${pkgs.swww}/bin/swww img "$WALLPAPER" --transition-type random
|
${pkgs.awww}/bin/awww img "$WALLPAPER" --transition-type random
|
||||||
''}";
|
''}";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
timers.swww-schedule = {
|
timers.awww-schedule = {
|
||||||
Unit = {
|
Unit = {
|
||||||
Description = "SWWW Wallpaper Schedule";
|
Description = "AWWW Wallpaper Schedule";
|
||||||
};
|
};
|
||||||
|
|
||||||
Install = {
|
Install = {
|
||||||
76
modules/home/services/open-proxy/default.nix
Normal file
76
modules/home/services/open-proxy/default.nix
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
{ config
|
||||||
|
, lib
|
||||||
|
, pkgs
|
||||||
|
, namespace
|
||||||
|
, ...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
inherit (lib) mkIf mkEnableOption;
|
||||||
|
cfg = config.${namespace}.services.open-proxy;
|
||||||
|
package = pkgs.reichard.open-proxy;
|
||||||
|
secretName = "open_proxy_token";
|
||||||
|
tokenPath = config.sops.secrets.${secretName}.path;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.${namespace}.services.open-proxy = {
|
||||||
|
server.enable = mkEnableOption "open-proxy host server (opens forwarded URLs/files on this machine)";
|
||||||
|
client.enable = mkEnableOption "open-proxy client (shadows open/xdg-open to forward to the host)";
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkMerge [
|
||||||
|
(mkIf (cfg.server.enable || cfg.client.enable) {
|
||||||
|
sops.secrets.${secretName} = {
|
||||||
|
sopsFile = lib.snowfall.fs.get-file "secrets/common/evanreichard.yaml";
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
|
(mkIf cfg.server.enable {
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = pkgs.stdenv.isDarwin;
|
||||||
|
message = "reichard.services.open-proxy.server is only supported on macOS (Darwin).";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
launchd.agents.open-proxy = {
|
||||||
|
enable = true;
|
||||||
|
config = {
|
||||||
|
Label = "io.reichard.open-proxy";
|
||||||
|
ProgramArguments = [ "${package}/bin/open-proxy" "serve" ];
|
||||||
|
RunAtLoad = true;
|
||||||
|
KeepAlive = true;
|
||||||
|
EnvironmentVariables = {
|
||||||
|
OPEN_PROXY_TOKEN_FILE = tokenPath;
|
||||||
|
# open(1) lives in /usr/bin; launchd agents don't inherit a login PATH.
|
||||||
|
PATH = "/usr/bin:/bin:/usr/sbin:/sbin";
|
||||||
|
};
|
||||||
|
StandardOutPath = "${config.home.homeDirectory}/Library/Logs/open-proxy/open-proxy.out.log";
|
||||||
|
StandardErrorPath = "${config.home.homeDirectory}/Library/Logs/open-proxy/open-proxy.err.log";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
|
(mkIf cfg.client.enable {
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = pkgs.stdenv.isLinux;
|
||||||
|
message = "reichard.services.open-proxy.client is only supported on Linux.";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
# Shadow the openers via ~/.local/bin (prepended to PATH below). open-proxy
|
||||||
|
# keys off argv[0], so these symlinks run in client mode and fall back to
|
||||||
|
# any real opener further down PATH when the host is unreachable.
|
||||||
|
home.file = {
|
||||||
|
".local/bin/open".source = "${package}/bin/open-proxy";
|
||||||
|
".local/bin/xdg-open".source = "${package}/bin/open-proxy";
|
||||||
|
};
|
||||||
|
|
||||||
|
home.sessionPath = [ "$HOME/.local/bin" ];
|
||||||
|
home.sessionVariables = {
|
||||||
|
BROWSER = "open";
|
||||||
|
OPEN_PROXY_TOKEN_FILE = tokenPath;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -16,6 +16,12 @@ in
|
|||||||
enable32Bit = mkBoolOpt false "enable 32-bit";
|
enable32Bit = mkBoolOpt false "enable 32-bit";
|
||||||
enableIntel = mkBoolOpt false "support for intel";
|
enableIntel = mkBoolOpt false "support for intel";
|
||||||
enableNvidia = mkBoolOpt false "support for nvidia";
|
enableNvidia = mkBoolOpt false "support for nvidia";
|
||||||
|
nvidiaPackage = lib.mkOption {
|
||||||
|
type = lib.types.package;
|
||||||
|
default = config.boot.kernelPackages.nvidiaPackages.stable;
|
||||||
|
defaultText = "config.boot.kernelPackages.nvidiaPackages.stable";
|
||||||
|
description = "nvidia driver package; pin to legacy_580 for Pascal (GTX 10xx) and older";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
@@ -32,7 +38,7 @@ in
|
|||||||
|
|
||||||
# Enable Nvidia Hardware
|
# Enable Nvidia Hardware
|
||||||
hardware.nvidia = mkIf cfg.enableNvidia {
|
hardware.nvidia = mkIf cfg.enableNvidia {
|
||||||
package = config.boot.kernelPackages.nvidiaPackages.stable;
|
package = cfg.nvidiaPackage;
|
||||||
modesetting.enable = true;
|
modesetting.enable = true;
|
||||||
powerManagement.enable = true;
|
powerManagement.enable = true;
|
||||||
open = false;
|
open = false;
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ in
|
|||||||
sshUser = "evanreichard";
|
sshUser = "evanreichard";
|
||||||
protocol = "ssh";
|
protocol = "ssh";
|
||||||
sshKey = config.sops.secrets.builder_ssh_key.path;
|
sshKey = config.sops.secrets.builder_ssh_key.path;
|
||||||
|
publicHostKey = "c3NoLWVkMjU1MTkgQUFBQUMzTnphQzFsWkRJMU5URTVBQUFBSUdscEMwcm9yQVRLeks4bUxNS2dDWXFNNU4yTi9HZ1MydDRNMTNjd25BT1M=";
|
||||||
supportedFeatures = [
|
supportedFeatures = [
|
||||||
"benchmark"
|
"benchmark"
|
||||||
"big-parallel"
|
"big-parallel"
|
||||||
|
|||||||
45
modules/nixos/services/llama-swap/AGENTS.md
Normal file
45
modules/nixos/services/llama-swap/AGENTS.md
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# llama-swap Module — Agent Guide
|
||||||
|
|
||||||
|
## Model ID Convention
|
||||||
|
|
||||||
|
Use `<family>-<size>[-backend/variant][-context][-vl]-<placement>`. Omit `thinking` from IDs, use `vl` for vision-language models, and keep placement as the final suffix (`cuda0`, `cuda1`, or `dual`). Keep quantization and richer behavior details in the display `name` unless they are needed to distinguish two active configs for the same family/placement.
|
||||||
|
|
||||||
|
## Syncing vLLM Configs from club-3090
|
||||||
|
|
||||||
|
The three vLLM model configs in `config.nix` (`qwen3.6-27b-vllm-180k-cuda0`, `qwen3.6-27b-vllm-145k-vl-cuda0`, `qwen3.6-27b-vllm-75k-cuda0`) are derived from the club-3090 repo's Docker Compose files. Each config block has a `Synced from:` comment with the commit hash it was last aligned to.
|
||||||
|
|
||||||
|
### Source Files
|
||||||
|
|
||||||
|
The upstream compose files live at https://github.com/noonghunna/club-3090 under `models/qwen3.6-27b/vllm/compose/`:
|
||||||
|
|
||||||
|
| config.nix model ID | Compose file |
|
||||||
|
|------------------------------------|-------------------------------------|
|
||||||
|
| `qwen3.6-27b-vllm-180k-cuda0` | `docker-compose.long-text.yml` |
|
||||||
|
| `qwen3.6-27b-vllm-145k-vl-cuda0` | `docker-compose.long-vision.yml` |
|
||||||
|
| `qwen3.6-27b-vllm-75k-cuda0` | `docker-compose.tools-text.yml` |
|
||||||
|
|
||||||
|
### Sync Process
|
||||||
|
|
||||||
|
1. **Fetch the latest compose files** from https://github.com/noonghunna/club-3090 (master branch) and note the HEAD commit hash.
|
||||||
|
2. **Diff each compose file** against the current config.nix block. The mapping is:
|
||||||
|
- Compose `command:` args → Nix `vllmCmd` string (the `exec vllm serve ...` block)
|
||||||
|
- Compose `environment:` → Nix docker `-e` flags
|
||||||
|
- Compose `volumes:` → Nix docker `-v` flags
|
||||||
|
- Compose `image:` → Nix docker image tag at the end of the `docker run` command
|
||||||
|
- Compose `entrypoint:` → Nix `vllmCmd` preamble (the `set -e; pip install ...; python3 ...` lines before `exec vllm serve`)
|
||||||
|
3. **Apply changes** to `config.nix`. Key things to watch:
|
||||||
|
- `--max-model-len` and `--gpu-memory-utilization` — these change across versions
|
||||||
|
- Genesis env vars — the full set grows frequently; add new ones, remove deprecated ones
|
||||||
|
- Sidecar patches — old patches get absorbed into Genesis; drop them from entrypoint + volume mounts
|
||||||
|
- Docker image tag — update when the compose files move to a new nightly
|
||||||
|
4. **Keep `patch_timings_1acd67a.py`** — this is our own patch, not from club-3090. Always retain it in the entrypoint and volume mounts.
|
||||||
|
5. **Update the `Synced from:` comment** on each config block with the new commit hash and date.
|
||||||
|
6. **Update `setup-qwen36-vllm.sh`** if the upstream `patches/` directory changed (new patches added, old ones removed). The setup script downloads sidecar patches and creates cache directories.
|
||||||
|
7. **Verify syntax**: `nix-instantiate --parse config.nix`
|
||||||
|
|
||||||
|
### Structural Notes
|
||||||
|
|
||||||
|
- `config.nix` uses Nix string interpolation. Newlines in `vllmCmd` are flattened to spaces via `builtins.replaceStrings` before passing to `docker run -c`.
|
||||||
|
- We pin `CUDA_VISIBLE_DEVICES=0` and `CUDA_DEVICE_ORDER=PCI_BUS_ID` (not in compose files) because the host has multiple GPUs and llama-swap's concurrency matrix manages GPU assignment.
|
||||||
|
- Volume mounts use `/mnt/ssd/vLLM/` paths (Models, Patches, Cache) — these match what `setup-qwen36-vllm.sh` creates.
|
||||||
|
- The `patches/` subdirectory in this module contains our custom timings patch and its source `.patch` file — unrelated to club-3090's `patches/` dir.
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -5,10 +5,37 @@
|
|||||||
, ...
|
, ...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
inherit (lib) mkIf mkEnableOption recursiveUpdate;
|
inherit (lib) mkIf mkEnableOption recursiveUpdate listToAttrs;
|
||||||
|
|
||||||
|
apiKeys = [ "evan" "pi" "aethera" ];
|
||||||
cfg = config.${namespace}.services.llama-swap;
|
cfg = config.${namespace}.services.llama-swap;
|
||||||
|
|
||||||
llama-swap = pkgs.reichard.llama-swap;
|
llama-swap = pkgs.reichard.llama-swap;
|
||||||
|
llamaCppPresets =
|
||||||
|
let
|
||||||
|
models = (import ./config.nix { inherit pkgs; }).models;
|
||||||
|
llamaCppModels = lib.filterAttrs (_: model: lib.hasInfix "/bin/llama-server" (model.cmd or "")) models;
|
||||||
|
in
|
||||||
|
builtins.mapAttrs (_: model: {
|
||||||
|
inherit (model) cmd;
|
||||||
|
name = model.name or "";
|
||||||
|
env = model.env or [ ];
|
||||||
|
}) llamaCppModels;
|
||||||
|
llamaCppPresetFile = pkgs.writeText "llama-cpp-presets.json" (builtins.toJSON llamaCppPresets);
|
||||||
|
llama-cpp-bisect-context = pkgs.writeShellApplication {
|
||||||
|
name = "llama-cpp-bisect-context";
|
||||||
|
runtimeInputs = with pkgs; [
|
||||||
|
coreutils
|
||||||
|
curl
|
||||||
|
gnused
|
||||||
|
python3
|
||||||
|
util-linux
|
||||||
|
];
|
||||||
|
text = builtins.replaceStrings
|
||||||
|
[ "__LLAMA_CPP_PRESETS__" ]
|
||||||
|
[ "${llamaCppPresetFile}" ]
|
||||||
|
(builtins.readFile ./scripts/llama-cpp-bisect-context);
|
||||||
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.${namespace}.services.llama-swap = {
|
options.${namespace}.services.llama-swap = {
|
||||||
@@ -27,6 +54,7 @@ in
|
|||||||
users.users.llama-swap = {
|
users.users.llama-swap = {
|
||||||
isSystemUser = true;
|
isSystemUser = true;
|
||||||
group = "llama-swap";
|
group = "llama-swap";
|
||||||
|
extraGroups = [ "podman" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
# Create Service
|
# Create Service
|
||||||
@@ -34,7 +62,6 @@ in
|
|||||||
description = "Model swapping for LLaMA C++ Server (or any local OpenAPI compatible server)";
|
description = "Model swapping for LLaMA C++ Server (or any local OpenAPI compatible server)";
|
||||||
after = [ "network.target" ];
|
after = [ "network.target" ];
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
Type = "exec";
|
Type = "exec";
|
||||||
ExecStart = "${lib.getExe llama-swap} --listen :8080 --config ${
|
ExecStart = "${lib.getExe llama-swap} --listen :8080 --config ${
|
||||||
@@ -87,21 +114,27 @@ in
|
|||||||
|
|
||||||
# Create Config
|
# Create Config
|
||||||
sops = {
|
sops = {
|
||||||
secrets.synthetic_apikey = {
|
secrets = listToAttrs (map (name: {
|
||||||
sopsFile = lib.snowfall.fs.get-file "secrets/common/systems.yaml";
|
name = "llama_swap_api_keys/${name}";
|
||||||
};
|
value = {
|
||||||
|
sopsFile = lib.snowfall.fs.get-file "secrets/common/llama-swap.yaml";
|
||||||
|
};
|
||||||
|
}) apiKeys);
|
||||||
templates."llama-swap.json" = {
|
templates."llama-swap.json" = {
|
||||||
|
restartUnits = [ "llama-swap.service" ];
|
||||||
owner = "llama-swap";
|
owner = "llama-swap";
|
||||||
group = "llama-swap";
|
group = "llama-swap";
|
||||||
mode = "0400";
|
mode = "0400";
|
||||||
content = builtins.toJSON (
|
content = builtins.toJSON (
|
||||||
recursiveUpdate cfg.config {
|
recursiveUpdate cfg.config {
|
||||||
peers.synthetic.apiKey = config.sops.placeholder.synthetic_apikey;
|
apiKeys = map (name: config.sops.placeholder."llama_swap_api_keys/${name}") apiKeys;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
environment.systemPackages = [ llama-cpp-bisect-context ];
|
||||||
|
|
||||||
networking.firewall.allowedTCPPorts = [ 8080 ];
|
networking.firewall.allowedTCPPorts = [ 8080 ];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
1
modules/nixos/services/llama-swap/patches/.gitignore
vendored
Normal file
1
modules/nixos/services/llama-swap/patches/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
__pycache__
|
||||||
69
modules/nixos/services/llama-swap/patches/README.md
Normal file
69
modules/nixos/services/llama-swap/patches/README.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# vLLM Timings Patch
|
||||||
|
|
||||||
|
This directory contains the custom timings patch for the current vLLM Docker image used by the llama-swap module:
|
||||||
|
|
||||||
|
```text
|
||||||
|
vllm/vllm-openai:nightly-1acd67a795ebccdf9b9db7697ae9082058301657
|
||||||
|
```
|
||||||
|
|
||||||
|
The patch adds a top-level llama.cpp-compatible `timings` object to OpenAI-compatible responses so llama-swap can populate cached tokens, prompt processing speed, and generation speed.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
- `patch_timings_1acd67a.py` — idempotent boot-time disk-edit patch script for the vLLM Docker container.
|
||||||
|
- `vllm-timings-1acd67a.patch` — equivalent standard unified git patch against the current image's vLLM source.
|
||||||
|
|
||||||
|
## Runtime Script
|
||||||
|
|
||||||
|
Deploy the script under `/mnt/ssd/vLLM/Patches/` and mount it into the container:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
-v /mnt/ssd/vLLM/Patches/patch_timings_1acd67a.py:/patches/patch_timings_1acd67a.py:ro \
|
||||||
|
```
|
||||||
|
|
||||||
|
Run it before `exec vllm serve`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 /patches/patch_timings_1acd67a.py;
|
||||||
|
exec vllm serve ...
|
||||||
|
```
|
||||||
|
|
||||||
|
The script is idempotent. Re-running it skips files that already contain `# [patch_timings]`.
|
||||||
|
|
||||||
|
## Standard Patch
|
||||||
|
|
||||||
|
For a source checkout at commit `1acd67a795ebccdf9b9db7697ae9082058301657`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git apply --check /path/to/vllm-timings-1acd67a.patch
|
||||||
|
git apply /path/to/vllm-timings-1acd67a.patch
|
||||||
|
```
|
||||||
|
|
||||||
|
At container runtime, applying the `.patch` directly is possible if the image has `patch` or `git` installed:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /usr/local/lib/python3.12/dist-packages
|
||||||
|
patch -p1 < /patches/vllm-timings-1acd67a.patch
|
||||||
|
```
|
||||||
|
|
||||||
|
The Python script remains the safer boot-time option because it is idempotent and does not depend on external patch tools being present in the Docker image.
|
||||||
|
|
||||||
|
## Timings Fields
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"prompt_n": 123,
|
||||||
|
"prompt_ms": 456.7,
|
||||||
|
"prompt_per_second": 269.3,
|
||||||
|
"predicted_n": 50,
|
||||||
|
"predicted_ms": 1000.0,
|
||||||
|
"predicted_per_second": 50.0,
|
||||||
|
"cache_n": 100
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Data comes from vLLM's existing internal `RequestStateStats` and `RequestOutput.num_cached_tokens`:
|
||||||
|
|
||||||
|
- prompt/prefill time: `first_token_ts - scheduled_ts`
|
||||||
|
- generation/decode time: `last_token_ts - first_token_ts`
|
||||||
|
- cached tokens: `num_cached_tokens`
|
||||||
@@ -0,0 +1,269 @@
|
|||||||
|
"""
|
||||||
|
Disk-edit patch for vLLM nightly-1acd67a795ebccdf9b9db7697ae9082058301657:
|
||||||
|
inject llama.cpp-compatible `timings` into chat/completion API responses.
|
||||||
|
|
||||||
|
Adds `timings` to:
|
||||||
|
- /v1/chat/completions non-streaming responses
|
||||||
|
- /v1/chat/completions streaming final usage chunk
|
||||||
|
- /v1/completions non-streaming responses
|
||||||
|
- /v1/completions streaming final usage chunk
|
||||||
|
|
||||||
|
The `timings` object matches llama.cpp fields consumed by llama-swap:
|
||||||
|
prompt_n, prompt_ms, prompt_per_second,
|
||||||
|
predicted_n, predicted_ms, predicted_per_second, cache_n
|
||||||
|
|
||||||
|
Usage, before `exec vllm serve`:
|
||||||
|
python3 /patches/patch_timings_1acd67a.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
log = logging.getLogger("patch_timings")
|
||||||
|
log.setLevel(logging.INFO)
|
||||||
|
if not log.handlers:
|
||||||
|
log.addHandler(logging.StreamHandler())
|
||||||
|
|
||||||
|
PATCH_TAG = "# [patch_timings]"
|
||||||
|
|
||||||
|
TIMINGS_HELPER = f'''
|
||||||
|
{PATCH_TAG}
|
||||||
|
def _compute_timings(metrics, num_prompt, num_gen, num_cached=None):
|
||||||
|
"""Compute llama.cpp-compatible timings from RequestStateStats."""
|
||||||
|
t = {{
|
||||||
|
"prompt_n": num_prompt,
|
||||||
|
"prompt_ms": 0.0,
|
||||||
|
"prompt_per_second": 0.0,
|
||||||
|
"predicted_n": num_gen,
|
||||||
|
"predicted_ms": 0.0,
|
||||||
|
"predicted_per_second": 0.0,
|
||||||
|
"cache_n": num_cached if num_cached is not None else -1,
|
||||||
|
}}
|
||||||
|
if metrics is None:
|
||||||
|
return t
|
||||||
|
if metrics.first_token_ts > 0 and metrics.scheduled_ts > 0:
|
||||||
|
ps = metrics.first_token_ts - metrics.scheduled_ts
|
||||||
|
if ps > 0:
|
||||||
|
t["prompt_ms"] = ps * 1000.0
|
||||||
|
t["prompt_per_second"] = num_prompt / ps
|
||||||
|
if metrics.last_token_ts > 0 and metrics.first_token_ts > 0:
|
||||||
|
ds = metrics.last_token_ts - metrics.first_token_ts
|
||||||
|
if ds > 0:
|
||||||
|
t["predicted_ms"] = ds * 1000.0
|
||||||
|
t["predicted_per_second"] = num_gen / ds
|
||||||
|
return t
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def _find_vllm_dir():
|
||||||
|
"""Auto-discover vLLM install directory."""
|
||||||
|
try:
|
||||||
|
import vllm
|
||||||
|
return os.path.dirname(vllm.__file__)
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
for path in [
|
||||||
|
"/usr/local/lib/python3.12/dist-packages/vllm",
|
||||||
|
"/usr/lib/python3.12/site-packages/vllm",
|
||||||
|
]:
|
||||||
|
if os.path.isdir(path):
|
||||||
|
return path
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _read(path):
|
||||||
|
with open(path, "r") as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
|
||||||
|
def _write(path, content):
|
||||||
|
with open(path, "w") as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
|
||||||
|
def _replace_once(content, old, new, label):
|
||||||
|
count = content.count(old)
|
||||||
|
if count != 1:
|
||||||
|
raise RuntimeError(f"{label}: anchor matched {count} times")
|
||||||
|
return content.replace(old, new, 1)
|
||||||
|
|
||||||
|
|
||||||
|
def _patch_protocol(path, label, replacements):
|
||||||
|
if not os.path.exists(path):
|
||||||
|
log.error(" %s: file not found: %s", label, path)
|
||||||
|
return False
|
||||||
|
|
||||||
|
content = _read(path)
|
||||||
|
if PATCH_TAG in content:
|
||||||
|
log.info(" %s: already patched, skipping", label)
|
||||||
|
return True
|
||||||
|
|
||||||
|
try:
|
||||||
|
for old, new in replacements:
|
||||||
|
content = _replace_once(content, old, new, label)
|
||||||
|
except RuntimeError as e:
|
||||||
|
log.error(" %s", e)
|
||||||
|
return False
|
||||||
|
|
||||||
|
_write(path, content)
|
||||||
|
log.info(" %s: patched successfully", label)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _patch_chat_protocol(vllm_dir):
|
||||||
|
path = os.path.join(vllm_dir, "entrypoints/openai/chat_completion/protocol.py")
|
||||||
|
return _patch_protocol(path, "chat_completion/protocol.py", [
|
||||||
|
(
|
||||||
|
''' kv_transfer_params: dict[str, Any] | None = Field(\n default=None, description="KVTransfer parameters."\n )\n''',
|
||||||
|
''' kv_transfer_params: dict[str, Any] | None = Field(\n default=None, description="KVTransfer parameters."\n )\n\n # llama.cpp-compatible per-request timings # [patch_timings]\n timings: dict[str, Any] | None = None\n''',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
''' # not part of the OpenAI spec but for tracing the tokens\n prompt_token_ids: list[int] | None = None\n''',
|
||||||
|
''' # not part of the OpenAI spec but for tracing the tokens\n prompt_token_ids: list[int] | None = None\n\n # llama.cpp-compatible per-request timings # [patch_timings]\n timings: dict[str, Any] | None = None\n''',
|
||||||
|
),
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def _patch_completion_protocol(vllm_dir):
|
||||||
|
path = os.path.join(vllm_dir, "entrypoints/openai/completion/protocol.py")
|
||||||
|
return _patch_protocol(path, "completion/protocol.py", [
|
||||||
|
(
|
||||||
|
''' kv_transfer_params: dict[str, Any] | None = Field(\n default=None, description="KVTransfer parameters."\n )\n''',
|
||||||
|
''' kv_transfer_params: dict[str, Any] | None = Field(\n default=None, description="KVTransfer parameters."\n )\n\n # llama.cpp-compatible per-request timings # [patch_timings]\n timings: dict[str, Any] | None = None\n''',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'''class CompletionStreamResponse(OpenAIBaseModel):\n id: str = Field(default_factory=lambda: f"cmpl-{random_uuid()}")\n object: str = "text_completion"\n created: int = Field(default_factory=lambda: int(time.time()))\n model: str\n choices: list[CompletionResponseStreamChoice]\n usage: UsageInfo | None = Field(default=None)\n''',
|
||||||
|
'''class CompletionStreamResponse(OpenAIBaseModel):\n id: str = Field(default_factory=lambda: f"cmpl-{random_uuid()}")\n object: str = "text_completion"\n created: int = Field(default_factory=lambda: int(time.time()))\n model: str\n choices: list[CompletionResponseStreamChoice]\n usage: UsageInfo | None = Field(default=None)\n\n # llama.cpp-compatible per-request timings # [patch_timings]\n timings: dict[str, Any] | None = None\n''',
|
||||||
|
),
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def _patch_chat_serving(vllm_dir):
|
||||||
|
path = os.path.join(vllm_dir, "entrypoints/openai/chat_completion/serving.py")
|
||||||
|
label = "chat_completion/serving.py"
|
||||||
|
if not os.path.exists(path):
|
||||||
|
log.error(" %s: file not found: %s", label, path)
|
||||||
|
return False
|
||||||
|
|
||||||
|
content = _read(path)
|
||||||
|
if PATCH_TAG in content:
|
||||||
|
log.info(" %s: already patched, skipping", label)
|
||||||
|
return True
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Helper Function
|
||||||
|
content = _replace_once(
|
||||||
|
content,
|
||||||
|
"class OpenAIServingChat(OpenAIServing):",
|
||||||
|
TIMINGS_HELPER + "\n\nclass OpenAIServingChat(OpenAIServing):",
|
||||||
|
label,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Streaming Last Result Capture - first streaming loop only.
|
||||||
|
content = _replace_once(
|
||||||
|
content,
|
||||||
|
" async for res in result_generator:\n if res.prompt_token_ids is not None:",
|
||||||
|
f" async for res in result_generator:\n _last_stream_res = res {PATCH_TAG}\n if res.prompt_token_ids is not None:",
|
||||||
|
label,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Streaming Final Usage Chunk
|
||||||
|
content = _replace_once(
|
||||||
|
content,
|
||||||
|
''' final_usage_chunk = ChatCompletionStreamResponse(\n id=request_id,\n object=chunk_object_type,\n created=created_time,\n choices=[],\n model=model_name,\n usage=final_usage,\n system_fingerprint=self.system_fingerprint,\n )\n''',
|
||||||
|
f''' final_usage_chunk = ChatCompletionStreamResponse(\n id=request_id,\n object=chunk_object_type,\n created=created_time,\n choices=[],\n model=model_name,\n usage=final_usage,\n system_fingerprint=self.system_fingerprint,\n )\n # Inject Timings {PATCH_TAG}\n try:\n _s_cached = _last_stream_res.num_cached_tokens\n final_usage_chunk.timings = _compute_timings(\n _last_stream_res.metrics,\n num_prompt_tokens, completion_tokens, _s_cached,\n )\n except NameError:\n pass\n''',
|
||||||
|
label,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Non-Streaming Response
|
||||||
|
content = _replace_once(
|
||||||
|
content,
|
||||||
|
''' response = ChatCompletionResponse(\n id=request_id,\n created=created_time,\n model=model_name,\n choices=choices,\n usage=usage,\n system_fingerprint=self.system_fingerprint,\n prompt_logprobs=clamp_prompt_logprobs(final_res.prompt_logprobs),\n prompt_token_ids=(\n final_res.prompt_token_ids if request.return_token_ids else None\n ),\n kv_transfer_params=final_res.kv_transfer_params,\n prompt_routed_experts=prompt_routed_experts,\n )\n''',
|
||||||
|
f''' response = ChatCompletionResponse(\n id=request_id,\n created=created_time,\n model=model_name,\n choices=choices,\n usage=usage,\n system_fingerprint=self.system_fingerprint,\n prompt_logprobs=clamp_prompt_logprobs(final_res.prompt_logprobs),\n prompt_token_ids=(\n final_res.prompt_token_ids if request.return_token_ids else None\n ),\n kv_transfer_params=final_res.kv_transfer_params,\n prompt_routed_experts=prompt_routed_experts,\n )\n\n # Inject Timings {PATCH_TAG}\n _cached = final_res.num_cached_tokens\n response.timings = _compute_timings(\n final_res.metrics, num_prompt_tokens, num_generated_tokens,\n _cached,\n )\n''',
|
||||||
|
label,
|
||||||
|
)
|
||||||
|
except RuntimeError as e:
|
||||||
|
log.error(" %s", e)
|
||||||
|
return False
|
||||||
|
|
||||||
|
_write(path, content)
|
||||||
|
log.info(" %s: patched successfully", label)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _patch_completion_serving(vllm_dir):
|
||||||
|
path = os.path.join(vllm_dir, "entrypoints/openai/completion/serving.py")
|
||||||
|
label = "completion/serving.py"
|
||||||
|
if not os.path.exists(path):
|
||||||
|
log.error(" %s: file not found: %s", label, path)
|
||||||
|
return False
|
||||||
|
|
||||||
|
content = _read(path)
|
||||||
|
if PATCH_TAG in content:
|
||||||
|
log.info(" %s: already patched, skipping", label)
|
||||||
|
return True
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Helper Function
|
||||||
|
content = _replace_once(
|
||||||
|
content,
|
||||||
|
"class OpenAIServingCompletion(OpenAIServing):",
|
||||||
|
TIMINGS_HELPER + "\n\nclass OpenAIServingCompletion(OpenAIServing):",
|
||||||
|
label,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Streaming Last Result Capture.
|
||||||
|
content = _replace_once(
|
||||||
|
content,
|
||||||
|
" async for prompt_idx, res in result_generator:\n prompt_token_ids = res.prompt_token_ids",
|
||||||
|
f" async for prompt_idx, res in result_generator:\n _last_comp_res = res {PATCH_TAG}\n prompt_token_ids = res.prompt_token_ids",
|
||||||
|
label,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Streaming Final Usage Chunk
|
||||||
|
content = _replace_once(
|
||||||
|
content,
|
||||||
|
''' final_usage_chunk = CompletionStreamResponse(\n id=request_id,\n created=created_time,\n model=model_name,\n choices=[],\n usage=final_usage_info,\n system_fingerprint=self.system_fingerprint,\n )\n''',
|
||||||
|
f''' final_usage_chunk = CompletionStreamResponse(\n id=request_id,\n created=created_time,\n model=model_name,\n choices=[],\n usage=final_usage_info,\n system_fingerprint=self.system_fingerprint,\n )\n # Inject Timings {PATCH_TAG}\n try:\n _sc_cached = _last_comp_res.num_cached_tokens\n final_usage_chunk.timings = _compute_timings(\n _last_comp_res.metrics,\n total_prompt_tokens, total_completion_tokens,\n _sc_cached,\n )\n except NameError:\n pass\n''',
|
||||||
|
label,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Non-Streaming Response
|
||||||
|
content = _replace_once(
|
||||||
|
content,
|
||||||
|
''' return CompletionResponse(\n id=request_id,\n created=created_time,\n model=model_name,\n choices=choices,\n usage=usage,\n system_fingerprint=self.system_fingerprint,\n kv_transfer_params=kv_transfer_params,\n prompt_routed_experts=prompt_routed_experts,\n )\n''',
|
||||||
|
f''' _comp_response = CompletionResponse( {PATCH_TAG}\n id=request_id,\n created=created_time,\n model=model_name,\n choices=choices,\n usage=usage,\n system_fingerprint=self.system_fingerprint,\n kv_transfer_params=kv_transfer_params,\n prompt_routed_experts=prompt_routed_experts,\n )\n # Inject Timings {PATCH_TAG}\n if last_final_res is not None:\n _comp_cached = last_final_res.num_cached_tokens\n _comp_response.timings = _compute_timings(\n last_final_res.metrics, num_prompt_tokens,\n num_generated_tokens, _comp_cached,\n )\n return _comp_response\n''',
|
||||||
|
label,
|
||||||
|
)
|
||||||
|
except RuntimeError as e:
|
||||||
|
log.error(" %s", e)
|
||||||
|
return False
|
||||||
|
|
||||||
|
_write(path, content)
|
||||||
|
log.info(" %s: patched successfully", label)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def apply():
|
||||||
|
vllm_dir = _find_vllm_dir()
|
||||||
|
if vllm_dir is None:
|
||||||
|
log.error("Could not find vLLM installation directory")
|
||||||
|
return False
|
||||||
|
|
||||||
|
log.info("Found vLLM at: %s", vllm_dir)
|
||||||
|
ok = True
|
||||||
|
ok &= _patch_chat_protocol(vllm_dir)
|
||||||
|
ok &= _patch_chat_serving(vllm_dir)
|
||||||
|
ok &= _patch_completion_protocol(vllm_dir)
|
||||||
|
ok &= _patch_completion_serving(vllm_dir)
|
||||||
|
return ok
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if apply():
|
||||||
|
print("patch_timings: All patches applied successfully")
|
||||||
|
else:
|
||||||
|
print("patch_timings: Some patches failed!", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
@@ -0,0 +1,213 @@
|
|||||||
|
diff --git a/vllm/entrypoints/openai/chat_completion/protocol.py b/vllm/entrypoints/openai/chat_completion/protocol.py
|
||||||
|
index 742f9cc..ade939f 100644
|
||||||
|
--- a/vllm/entrypoints/openai/chat_completion/protocol.py
|
||||||
|
+++ b/vllm/entrypoints/openai/chat_completion/protocol.py
|
||||||
|
@@ -115,6 +115,9 @@ class ChatCompletionResponse(OpenAIBaseModel):
|
||||||
|
default=None, description="KVTransfer parameters."
|
||||||
|
)
|
||||||
|
|
||||||
|
+ # llama.cpp-compatible per-request timings # [patch_timings]
|
||||||
|
+ timings: dict[str, Any] | None = None
|
||||||
|
+
|
||||||
|
|
||||||
|
class ChatCompletionResponseStreamChoice(OpenAIBaseModel):
|
||||||
|
index: int
|
||||||
|
@@ -139,6 +142,9 @@ class ChatCompletionStreamResponse(OpenAIBaseModel):
|
||||||
|
# not part of the OpenAI spec but for tracing the tokens
|
||||||
|
prompt_token_ids: list[int] | None = None
|
||||||
|
|
||||||
|
+ # llama.cpp-compatible per-request timings # [patch_timings]
|
||||||
|
+ timings: dict[str, Any] | None = None
|
||||||
|
+
|
||||||
|
|
||||||
|
class ChatCompletionToolsParam(OpenAIBaseModel):
|
||||||
|
type: Literal["function"] = "function"
|
||||||
|
diff --git a/vllm/entrypoints/openai/chat_completion/serving.py b/vllm/entrypoints/openai/chat_completion/serving.py
|
||||||
|
index 1026e0a..a9c5708 100644
|
||||||
|
--- a/vllm/entrypoints/openai/chat_completion/serving.py
|
||||||
|
+++ b/vllm/entrypoints/openai/chat_completion/serving.py
|
||||||
|
@@ -79,6 +79,34 @@ if TYPE_CHECKING:
|
||||||
|
logger = init_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
+
|
||||||
|
+# [patch_timings]
|
||||||
|
+def _compute_timings(metrics, num_prompt, num_gen, num_cached=None):
|
||||||
|
+ """Compute llama.cpp-compatible timings from RequestStateStats."""
|
||||||
|
+ t = {
|
||||||
|
+ "prompt_n": num_prompt,
|
||||||
|
+ "prompt_ms": 0.0,
|
||||||
|
+ "prompt_per_second": 0.0,
|
||||||
|
+ "predicted_n": num_gen,
|
||||||
|
+ "predicted_ms": 0.0,
|
||||||
|
+ "predicted_per_second": 0.0,
|
||||||
|
+ "cache_n": num_cached if num_cached is not None else -1,
|
||||||
|
+ }
|
||||||
|
+ if metrics is None:
|
||||||
|
+ return t
|
||||||
|
+ if metrics.first_token_ts > 0 and metrics.scheduled_ts > 0:
|
||||||
|
+ ps = metrics.first_token_ts - metrics.scheduled_ts
|
||||||
|
+ if ps > 0:
|
||||||
|
+ t["prompt_ms"] = ps * 1000.0
|
||||||
|
+ t["prompt_per_second"] = num_prompt / ps
|
||||||
|
+ if metrics.last_token_ts > 0 and metrics.first_token_ts > 0:
|
||||||
|
+ ds = metrics.last_token_ts - metrics.first_token_ts
|
||||||
|
+ if ds > 0:
|
||||||
|
+ t["predicted_ms"] = ds * 1000.0
|
||||||
|
+ t["predicted_per_second"] = num_gen / ds
|
||||||
|
+ return t
|
||||||
|
+
|
||||||
|
+
|
||||||
|
class OpenAIServingChat(OpenAIServing):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
@@ -485,6 +513,7 @@ class OpenAIServingChat(OpenAIServing):
|
||||||
|
|
||||||
|
try:
|
||||||
|
async for res in result_generator:
|
||||||
|
+ _last_stream_res = res # [patch_timings]
|
||||||
|
if res.prompt_token_ids is not None:
|
||||||
|
num_prompt_tokens = len(res.prompt_token_ids)
|
||||||
|
if res.encoder_prompt_token_ids is not None:
|
||||||
|
@@ -947,6 +976,15 @@ class OpenAIServingChat(OpenAIServing):
|
||||||
|
usage=final_usage,
|
||||||
|
system_fingerprint=self.system_fingerprint,
|
||||||
|
)
|
||||||
|
+ # Inject Timings # [patch_timings]
|
||||||
|
+ try:
|
||||||
|
+ _s_cached = _last_stream_res.num_cached_tokens
|
||||||
|
+ final_usage_chunk.timings = _compute_timings(
|
||||||
|
+ _last_stream_res.metrics,
|
||||||
|
+ num_prompt_tokens, completion_tokens, _s_cached,
|
||||||
|
+ )
|
||||||
|
+ except NameError:
|
||||||
|
+ pass
|
||||||
|
final_usage_data = final_usage_chunk.model_dump_json(
|
||||||
|
exclude_unset=True, exclude_none=True
|
||||||
|
)
|
||||||
|
@@ -1377,6 +1415,13 @@ class OpenAIServingChat(OpenAIServing):
|
||||||
|
prompt_routed_experts=prompt_routed_experts,
|
||||||
|
)
|
||||||
|
|
||||||
|
+ # Inject Timings # [patch_timings]
|
||||||
|
+ _cached = final_res.num_cached_tokens
|
||||||
|
+ response.timings = _compute_timings(
|
||||||
|
+ final_res.metrics, num_prompt_tokens, num_generated_tokens,
|
||||||
|
+ _cached,
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
# Log complete response if output logging is enabled
|
||||||
|
if self.enable_log_outputs and self.request_logger:
|
||||||
|
for choice in choices:
|
||||||
|
diff --git a/vllm/entrypoints/openai/completion/protocol.py b/vllm/entrypoints/openai/completion/protocol.py
|
||||||
|
index 7bb3c8d..8487e93 100644
|
||||||
|
--- a/vllm/entrypoints/openai/completion/protocol.py
|
||||||
|
+++ b/vllm/entrypoints/openai/completion/protocol.py
|
||||||
|
@@ -489,6 +489,9 @@ class CompletionResponse(OpenAIBaseModel):
|
||||||
|
default=None, description="KVTransfer parameters."
|
||||||
|
)
|
||||||
|
|
||||||
|
+ # llama.cpp-compatible per-request timings # [patch_timings]
|
||||||
|
+ timings: dict[str, Any] | None = None
|
||||||
|
+
|
||||||
|
|
||||||
|
class CompletionResponseStreamChoice(OpenAIBaseModel):
|
||||||
|
index: int
|
||||||
|
@@ -516,6 +519,9 @@ class CompletionStreamResponse(OpenAIBaseModel):
|
||||||
|
model: str
|
||||||
|
choices: list[CompletionResponseStreamChoice]
|
||||||
|
usage: UsageInfo | None = Field(default=None)
|
||||||
|
+
|
||||||
|
+ # llama.cpp-compatible per-request timings # [patch_timings]
|
||||||
|
+ timings: dict[str, Any] | None = None
|
||||||
|
# Set only on the final chunk of a stream to mirror non-streaming responses
|
||||||
|
# without the per-chunk serialization overhead.
|
||||||
|
system_fingerprint: str | None = None
|
||||||
|
diff --git a/vllm/entrypoints/openai/completion/serving.py b/vllm/entrypoints/openai/completion/serving.py
|
||||||
|
index ee4ca9f..8b27011 100644
|
||||||
|
--- a/vllm/entrypoints/openai/completion/serving.py
|
||||||
|
+++ b/vllm/entrypoints/openai/completion/serving.py
|
||||||
|
@@ -48,6 +48,34 @@ if TYPE_CHECKING:
|
||||||
|
logger = init_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
+
|
||||||
|
+# [patch_timings]
|
||||||
|
+def _compute_timings(metrics, num_prompt, num_gen, num_cached=None):
|
||||||
|
+ """Compute llama.cpp-compatible timings from RequestStateStats."""
|
||||||
|
+ t = {
|
||||||
|
+ "prompt_n": num_prompt,
|
||||||
|
+ "prompt_ms": 0.0,
|
||||||
|
+ "prompt_per_second": 0.0,
|
||||||
|
+ "predicted_n": num_gen,
|
||||||
|
+ "predicted_ms": 0.0,
|
||||||
|
+ "predicted_per_second": 0.0,
|
||||||
|
+ "cache_n": num_cached if num_cached is not None else -1,
|
||||||
|
+ }
|
||||||
|
+ if metrics is None:
|
||||||
|
+ return t
|
||||||
|
+ if metrics.first_token_ts > 0 and metrics.scheduled_ts > 0:
|
||||||
|
+ ps = metrics.first_token_ts - metrics.scheduled_ts
|
||||||
|
+ if ps > 0:
|
||||||
|
+ t["prompt_ms"] = ps * 1000.0
|
||||||
|
+ t["prompt_per_second"] = num_prompt / ps
|
||||||
|
+ if metrics.last_token_ts > 0 and metrics.first_token_ts > 0:
|
||||||
|
+ ds = metrics.last_token_ts - metrics.first_token_ts
|
||||||
|
+ if ds > 0:
|
||||||
|
+ t["predicted_ms"] = ds * 1000.0
|
||||||
|
+ t["predicted_per_second"] = num_gen / ds
|
||||||
|
+ return t
|
||||||
|
+
|
||||||
|
+
|
||||||
|
class OpenAIServingCompletion(OpenAIServing):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
@@ -291,6 +319,7 @@ class OpenAIServingCompletion(OpenAIServing):
|
||||||
|
|
||||||
|
try:
|
||||||
|
async for prompt_idx, res in result_generator:
|
||||||
|
+ _last_comp_res = res # [patch_timings]
|
||||||
|
prompt_token_ids = res.prompt_token_ids
|
||||||
|
prompt_logprobs = res.prompt_logprobs
|
||||||
|
|
||||||
|
@@ -445,6 +474,16 @@ class OpenAIServingCompletion(OpenAIServing):
|
||||||
|
usage=final_usage_info,
|
||||||
|
system_fingerprint=self.system_fingerprint,
|
||||||
|
)
|
||||||
|
+ # Inject Timings # [patch_timings]
|
||||||
|
+ try:
|
||||||
|
+ _sc_cached = _last_comp_res.num_cached_tokens
|
||||||
|
+ final_usage_chunk.timings = _compute_timings(
|
||||||
|
+ _last_comp_res.metrics,
|
||||||
|
+ total_prompt_tokens, total_completion_tokens,
|
||||||
|
+ _sc_cached,
|
||||||
|
+ )
|
||||||
|
+ except NameError:
|
||||||
|
+ pass
|
||||||
|
final_usage_data = final_usage_chunk.model_dump_json(
|
||||||
|
exclude_unset=False, exclude_none=True
|
||||||
|
)
|
||||||
|
@@ -577,7 +616,7 @@ class OpenAIServingCompletion(OpenAIServing):
|
||||||
|
if pre is not None:
|
||||||
|
prompt_routed_experts = pre.tolist()
|
||||||
|
|
||||||
|
- return CompletionResponse(
|
||||||
|
+ _comp_response = CompletionResponse( # [patch_timings]
|
||||||
|
id=request_id,
|
||||||
|
created=created_time,
|
||||||
|
model=model_name,
|
||||||
|
@@ -587,6 +626,14 @@ class OpenAIServingCompletion(OpenAIServing):
|
||||||
|
kv_transfer_params=kv_transfer_params,
|
||||||
|
prompt_routed_experts=prompt_routed_experts,
|
||||||
|
)
|
||||||
|
+ # Inject Timings # [patch_timings]
|
||||||
|
+ if last_final_res is not None:
|
||||||
|
+ _comp_cached = last_final_res.num_cached_tokens
|
||||||
|
+ _comp_response.timings = _compute_timings(
|
||||||
|
+ last_final_res.metrics, num_prompt_tokens,
|
||||||
|
+ num_generated_tokens, _comp_cached,
|
||||||
|
+ )
|
||||||
|
+ return _comp_response
|
||||||
|
|
||||||
|
def _create_completion_logprobs(
|
||||||
|
self,
|
||||||
464
modules/nixos/services/llama-swap/scripts/llama-cpp-bisect-context
Executable file
464
modules/nixos/services/llama-swap/scripts/llama-cpp-bisect-context
Executable file
@@ -0,0 +1,464 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage:
|
||||||
|
llama-cpp-bisect-context MODEL --low N --high N [options]
|
||||||
|
llama-cpp-bisect-context --cmd-template CMD --low N --high N [options]
|
||||||
|
llama-cpp-bisect-context --cmd-file FILE --low N --high N [options]
|
||||||
|
|
||||||
|
Bisect the largest llama.cpp llama-server context that can start and complete
|
||||||
|
a near-context prompt without OOMing. Startup-only mode is available for isolating the first cliff.
|
||||||
|
|
||||||
|
MODEL is a llama.cpp preset generated from the Nix llama-swap config.
|
||||||
|
|
||||||
|
Command templates are evaluated with these environment variables:
|
||||||
|
PORT random listen port for this trial
|
||||||
|
CTX candidate context size
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--cmd-template CMD llama-server command, e.g. 'llama-server --port ${PORT} -c ${CTX} ...'
|
||||||
|
--cmd-file FILE executable or shell snippet using $PORT and $CTX
|
||||||
|
--preset-file FILE preset JSON file (default: Nix-generated presets)
|
||||||
|
--list-presets list available Nix-generated presets and exit
|
||||||
|
--low N known/assumed lower context bound
|
||||||
|
--high N upper context bound to test
|
||||||
|
--step N stop when high-low <= N (default: 1024)
|
||||||
|
--prompt-ratio PCT prompt fill target as percent of CTX (default: 90)
|
||||||
|
--chars-per-token N rough prompt sizing ratio (default: 4)
|
||||||
|
--prompt-turns N split the prompt across N user/assistant turns (default: 4)
|
||||||
|
--max-tokens N generated tokens for prompt test (default: 32)
|
||||||
|
--startup-timeout SEC seconds to wait for /health readiness (default: 300)
|
||||||
|
--request-timeout SEC seconds to wait for prompt response (default: 600)
|
||||||
|
--cooldown SEC seconds to sleep after stopping server (default: 5)
|
||||||
|
--startup-only only test server startup, not prompt/runtime OOM
|
||||||
|
--verbose print llama-server logs for each failed trial
|
||||||
|
--keep-logs keep trial logs after a successful run too
|
||||||
|
-h, --help show this help
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
llama-cpp-bisect-context \
|
||||||
|
--cmd-template 'llama-server --port ${PORT} -m model.gguf -c ${CTX} -ngl 99' \
|
||||||
|
--low 32768 --high 196608
|
||||||
|
|
||||||
|
llama-cpp-bisect-context qwen3.6-27b-ik-cuda0 --low 32768 --high 180000
|
||||||
|
llama-cpp-bisect-context --cmd-file ./server-command.sh --low 32768 --high 196608
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
preset_model=""
|
||||||
|
preset_file="__LLAMA_CPP_PRESETS__"
|
||||||
|
list_presets=0
|
||||||
|
cmd_template=""
|
||||||
|
cmd_file=""
|
||||||
|
low=""
|
||||||
|
high=""
|
||||||
|
step=1024
|
||||||
|
prompt_ratio=90
|
||||||
|
chars_per_token=4
|
||||||
|
prompt_turns=4
|
||||||
|
max_tokens=32
|
||||||
|
startup_timeout=300
|
||||||
|
request_timeout=600
|
||||||
|
cooldown=5
|
||||||
|
startup_only=0
|
||||||
|
verbose=0
|
||||||
|
keep_logs=0
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--cmd-template) cmd_template="${2:-}"; shift 2 ;;
|
||||||
|
--cmd-file) cmd_file="${2:-}"; shift 2 ;;
|
||||||
|
--preset-file) preset_file="${2:-}"; shift 2 ;;
|
||||||
|
--list-presets) list_presets=1; shift ;;
|
||||||
|
--low) low="${2:-}"; shift 2 ;;
|
||||||
|
--high) high="${2:-}"; shift 2 ;;
|
||||||
|
--step) step="${2:-}"; shift 2 ;;
|
||||||
|
--prompt-ratio) prompt_ratio="${2:-}"; shift 2 ;;
|
||||||
|
--chars-per-token) chars_per_token="${2:-}"; shift 2 ;;
|
||||||
|
--prompt-turns) prompt_turns="${2:-}"; shift 2 ;;
|
||||||
|
--max-tokens) max_tokens="${2:-}"; shift 2 ;;
|
||||||
|
--startup-timeout) startup_timeout="${2:-}"; shift 2 ;;
|
||||||
|
--request-timeout) request_timeout="${2:-}"; shift 2 ;;
|
||||||
|
--cooldown) cooldown="${2:-}"; shift 2 ;;
|
||||||
|
--startup-only) startup_only=1; shift ;;
|
||||||
|
--verbose) verbose=1; shift ;;
|
||||||
|
--keep-logs) keep_logs=1; shift ;;
|
||||||
|
-h|--help) usage; exit 0 ;;
|
||||||
|
--*) echo "unknown argument: $1" >&2; usage >&2; exit 2 ;;
|
||||||
|
*)
|
||||||
|
if [[ -n "$preset_model" ]]; then
|
||||||
|
echo "unexpected positional argument: $1" >&2
|
||||||
|
usage >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
preset_model="$1"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
list_presets_json() {
|
||||||
|
python3 - "$preset_file" <<'PY'
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
with open(sys.argv[1]) as f:
|
||||||
|
presets = json.load(f)
|
||||||
|
for key in sorted(presets):
|
||||||
|
name = presets[key].get("name", "")
|
||||||
|
print(f"{key}\t{name}" if name else key)
|
||||||
|
PY
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if (( list_presets )); then
|
||||||
|
list_presets_json
|
||||||
|
fi
|
||||||
|
|
||||||
|
load_preset() {
|
||||||
|
local command_file="$tmpdir/preset-command.sh"
|
||||||
|
python3 - "$preset_file" "$preset_model" "$command_file" <<'PY'
|
||||||
|
import json
|
||||||
|
import shlex
|
||||||
|
import sys
|
||||||
|
|
||||||
|
preset_file, model_id, command_file = sys.argv[1:]
|
||||||
|
with open(preset_file) as f:
|
||||||
|
presets = json.load(f)
|
||||||
|
try:
|
||||||
|
preset = presets[model_id]
|
||||||
|
except KeyError:
|
||||||
|
print(f"unknown preset: {model_id}", file=sys.stderr)
|
||||||
|
print("available presets:", file=sys.stderr)
|
||||||
|
for key in sorted(presets):
|
||||||
|
print(f" {key}", file=sys.stderr)
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
cmd = preset["cmd"].replace("${ctx}", "${CTX}").replace("$ctx", "${CTX}")
|
||||||
|
env = preset.get("env", [])
|
||||||
|
with open(command_file, "w") as f:
|
||||||
|
f.write("set -e\n")
|
||||||
|
for item in env:
|
||||||
|
key, sep, value = item.partition("=")
|
||||||
|
if not sep or not key:
|
||||||
|
continue
|
||||||
|
f.write(f"export {key}={shlex.quote(value)}\n")
|
||||||
|
f.write(cmd)
|
||||||
|
if not cmd.endswith("\n"):
|
||||||
|
f.write("\n")
|
||||||
|
PY
|
||||||
|
cmd_file="$command_file"
|
||||||
|
}
|
||||||
|
|
||||||
|
require_int() {
|
||||||
|
local name="$1" value="$2"
|
||||||
|
if [[ ! "$value" =~ ^[0-9]+$ ]]; then
|
||||||
|
echo "$name must be a positive integer" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
mode_count=0
|
||||||
|
[[ -n "$preset_model" ]] && mode_count=$((mode_count + 1))
|
||||||
|
[[ -n "$cmd_template" ]] && mode_count=$((mode_count + 1))
|
||||||
|
[[ -n "$cmd_file" ]] && mode_count=$((mode_count + 1))
|
||||||
|
if (( mode_count != 1 )); then
|
||||||
|
echo "use exactly one of MODEL, --cmd-template, or --cmd-file" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
if [[ -z "$low" || -z "$high" ]]; then
|
||||||
|
echo "missing --low or --high" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
for pair in \
|
||||||
|
"--low:$low" \
|
||||||
|
"--high:$high" \
|
||||||
|
"--step:$step" \
|
||||||
|
"--prompt-ratio:$prompt_ratio" \
|
||||||
|
"--chars-per-token:$chars_per_token" \
|
||||||
|
"--prompt-turns:$prompt_turns" \
|
||||||
|
"--max-tokens:$max_tokens" \
|
||||||
|
"--startup-timeout:$startup_timeout" \
|
||||||
|
"--request-timeout:$request_timeout" \
|
||||||
|
"--cooldown:$cooldown"; do
|
||||||
|
require_int "${pair%%:*}" "${pair#*:}"
|
||||||
|
done
|
||||||
|
|
||||||
|
if (( low <= 0 || high <= low || step <= 0 || prompt_ratio <= 0 || chars_per_token <= 0 || prompt_turns <= 0 )); then
|
||||||
|
echo "invalid numeric bounds/options" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$cmd_file" && ! -f "$cmd_file" ]]; then
|
||||||
|
echo "cmd file not found: $cmd_file" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
for dep in curl python3; do
|
||||||
|
if ! command -v "$dep" >/dev/null 2>&1; then
|
||||||
|
echo "missing required command: $dep" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
tmpdir="$(mktemp -d)"
|
||||||
|
server_pid=""
|
||||||
|
log_file=""
|
||||||
|
|
||||||
|
terminate_server() {
|
||||||
|
if [[ -z "${server_pid:-}" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
kill -- "-${server_pid}" >/dev/null 2>&1 || true
|
||||||
|
kill "$server_pid" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
local waited=0
|
||||||
|
while kill -0 "$server_pid" >/dev/null 2>&1 && (( waited < 30 )); do
|
||||||
|
sleep 1
|
||||||
|
waited=$((waited + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
if kill -0 "$server_pid" >/dev/null 2>&1; then
|
||||||
|
kill -9 -- "-${server_pid}" >/dev/null 2>&1 || true
|
||||||
|
kill -9 "$server_pid" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
wait "$server_pid" >/dev/null 2>&1 || true
|
||||||
|
server_pid=""
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
local status=$?
|
||||||
|
trap - EXIT INT TERM HUP
|
||||||
|
terminate_server
|
||||||
|
if (( keep_logs || status != 0 )); then
|
||||||
|
echo "logs kept in: $tmpdir" >&2
|
||||||
|
else
|
||||||
|
rm -rf "$tmpdir"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
interrupt() {
|
||||||
|
echo "interrupted; stopping llama-server" >&2
|
||||||
|
exit 130
|
||||||
|
}
|
||||||
|
|
||||||
|
trap cleanup EXIT
|
||||||
|
trap interrupt INT TERM HUP
|
||||||
|
|
||||||
|
if [[ -n "$preset_model" ]]; then
|
||||||
|
load_preset
|
||||||
|
fi
|
||||||
|
|
||||||
|
free_port() {
|
||||||
|
python3 - <<'PY'
|
||||||
|
import socket
|
||||||
|
with socket.socket() as s:
|
||||||
|
s.bind(("127.0.0.1", 0))
|
||||||
|
print(s.getsockname()[1])
|
||||||
|
PY
|
||||||
|
}
|
||||||
|
|
||||||
|
start_server() {
|
||||||
|
local ctx="$1"
|
||||||
|
PORT="$(free_port)"
|
||||||
|
CTX="$ctx"
|
||||||
|
export PORT CTX
|
||||||
|
log_file="$tmpdir/llama-server-${ctx}.log"
|
||||||
|
{
|
||||||
|
printf 'CTX=%s\n' "$CTX"
|
||||||
|
printf 'PORT=%s\n' "$PORT"
|
||||||
|
if [[ -n "$cmd_file" ]]; then
|
||||||
|
printf 'CMD_FILE=%s\n' "$cmd_file"
|
||||||
|
else
|
||||||
|
printf 'CMD_TEMPLATE=%s\n' "$cmd_template"
|
||||||
|
fi
|
||||||
|
printf -- '--- llama-server output ---\n'
|
||||||
|
} >"$log_file"
|
||||||
|
|
||||||
|
if [[ -n "$cmd_file" ]]; then
|
||||||
|
setsid bash "$cmd_file" >>"$log_file" 2>&1 &
|
||||||
|
else
|
||||||
|
setsid bash -c "$cmd_template" >>"$log_file" 2>&1 &
|
||||||
|
fi
|
||||||
|
server_pid="$!"
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_server() {
|
||||||
|
terminate_server
|
||||||
|
sleep "$cooldown"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_failure_log() {
|
||||||
|
local label="$1" ctx="$2"
|
||||||
|
echo "[$label] ctx=$ctx failed; log: $log_file" >&2
|
||||||
|
if (( verbose )) && [[ -f "$log_file" ]]; then
|
||||||
|
sed -n '1,220p' "$log_file" >&2 || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_ready() {
|
||||||
|
local deadline=$((SECONDS + startup_timeout))
|
||||||
|
while (( SECONDS < deadline )); do
|
||||||
|
if [[ -n "${server_pid:-}" ]] && ! kill -0 "$server_pid" >/dev/null 2>&1; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if curl -fsS --max-time 5 "http://127.0.0.1:${PORT}/health" >/dev/null 2>&1; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if curl -fsS --max-time 5 "http://127.0.0.1:${PORT}/v1/models" >/dev/null 2>&1; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
make_prompt_json() {
|
||||||
|
local ctx="$1"
|
||||||
|
local approx_tokens=$(( ctx * prompt_ratio / 100 ))
|
||||||
|
local chars=$(( approx_tokens * chars_per_token ))
|
||||||
|
python3 - "$chars" "$max_tokens" "$prompt_turns" <<'PY'
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
chars = int(sys.argv[1])
|
||||||
|
max_tokens = int(sys.argv[2])
|
||||||
|
prompt_turns = int(sys.argv[3])
|
||||||
|
seed = (
|
||||||
|
"This is deterministic context filler for memory testing. "
|
||||||
|
"It uses normal words so token estimates are closer to real prompts. "
|
||||||
|
)
|
||||||
|
messages = []
|
||||||
|
remaining = chars
|
||||||
|
for turn in range(prompt_turns):
|
||||||
|
turns_left = prompt_turns - turn
|
||||||
|
chunk_chars = max(1, remaining // turns_left)
|
||||||
|
content = (seed * ((chunk_chars // len(seed)) + 1))[:chunk_chars]
|
||||||
|
messages.append({"role": "user", "content": content})
|
||||||
|
remaining -= chunk_chars
|
||||||
|
if turn != prompt_turns - 1:
|
||||||
|
messages.append({"role": "assistant", "content": "Acknowledged."})
|
||||||
|
|
||||||
|
print(json.dumps({
|
||||||
|
"messages": messages,
|
||||||
|
"max_tokens": max_tokens,
|
||||||
|
"temperature": 0,
|
||||||
|
"stream": False,
|
||||||
|
}))
|
||||||
|
PY
|
||||||
|
}
|
||||||
|
|
||||||
|
run_prompt() {
|
||||||
|
local ctx="$1"
|
||||||
|
local payload="$tmpdir/prompt-${ctx}.json"
|
||||||
|
make_prompt_json "$ctx" >"$payload"
|
||||||
|
curl -fsS \
|
||||||
|
--max-time "$request_timeout" \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d "@$payload" \
|
||||||
|
"http://127.0.0.1:${PORT}/v1/chat/completions" \
|
||||||
|
>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
test_startup() {
|
||||||
|
local ctx="$1"
|
||||||
|
echo "[startup] testing ctx=$ctx" >&2
|
||||||
|
start_server "$ctx"
|
||||||
|
if wait_ready; then
|
||||||
|
stop_server
|
||||||
|
echo "[startup] ctx=$ctx PASS" >&2
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
print_failure_log startup "$ctx"
|
||||||
|
stop_server
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
test_qualified_context() {
|
||||||
|
local ctx="$1"
|
||||||
|
echo "[ctx] testing ctx=$ctx with prompt_ratio=${prompt_ratio}% prompt_turns=${prompt_turns}" >&2
|
||||||
|
start_server "$ctx"
|
||||||
|
if ! wait_ready; then
|
||||||
|
print_failure_log ctx-startup "$ctx"
|
||||||
|
stop_server
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if run_prompt "$ctx"; then
|
||||||
|
stop_server
|
||||||
|
echo "[ctx] ctx=$ctx PASS" >&2
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
print_failure_log ctx-prompt "$ctx"
|
||||||
|
stop_server
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
bisect_max() {
|
||||||
|
local label="$1" pass="$2" fail="$3" fn="$4"
|
||||||
|
while (( fail - pass > step )); do
|
||||||
|
local mid=$(( (pass + fail) / 2 ))
|
||||||
|
if "$fn" "$mid"; then
|
||||||
|
pass="$mid"
|
||||||
|
else
|
||||||
|
fail="$mid"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
printf '%s:%s:%s\n' "$label" "$pass" "$fail"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (( startup_only )); then
|
||||||
|
if ! test_startup "$low"; then
|
||||||
|
echo "low bound does not pass startup: $low" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
result="$(bisect_max startup "$low" "$high" test_startup)"
|
||||||
|
pass="$(cut -d: -f2 <<<"$result")"
|
||||||
|
fail="$(cut -d: -f3 <<<"$result")"
|
||||||
|
|
||||||
|
printf '\nResult:\n'
|
||||||
|
printf ' startup max passing ctx: %s\n' "$pass"
|
||||||
|
printf ' startup min failing ctx: %s\n' "$fail"
|
||||||
|
python3 - "$pass" "$fail" <<'PY'
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
max_passing, min_failing = map(int, sys.argv[1:])
|
||||||
|
print(json.dumps({"startup": {"maxPassingCtx": max_passing, "minFailingCtx": min_failing}}, indent=2))
|
||||||
|
PY
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! test_qualified_context "$low"; then
|
||||||
|
echo "low bound does not pass qualified context test: $low" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
result="$(bisect_max context "$low" "$high" test_qualified_context)"
|
||||||
|
pass="$(cut -d: -f2 <<<"$result")"
|
||||||
|
fail="$(cut -d: -f3 <<<"$result")"
|
||||||
|
|
||||||
|
printf '\nResult:\n'
|
||||||
|
printf ' context max passing ctx: %s\n' "$pass"
|
||||||
|
printf ' context min failing ctx: %s\n' "$fail"
|
||||||
|
printf ' prompt ratio: %s%%\n' "$prompt_ratio"
|
||||||
|
printf ' prompt turns: %s\n' "$prompt_turns"
|
||||||
|
|
||||||
|
python3 - "$pass" "$fail" "$prompt_ratio" "$prompt_turns" <<'PY'
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
max_passing = int(sys.argv[1])
|
||||||
|
min_failing = int(sys.argv[2])
|
||||||
|
prompt_ratio = int(sys.argv[3])
|
||||||
|
prompt_turns = int(sys.argv[4])
|
||||||
|
print(json.dumps({
|
||||||
|
"context": {
|
||||||
|
"maxPassingCtx": max_passing,
|
||||||
|
"minFailingCtx": min_failing,
|
||||||
|
"promptRatio": prompt_ratio,
|
||||||
|
"promptTurns": prompt_turns,
|
||||||
|
}
|
||||||
|
}, indent=2))
|
||||||
|
PY
|
||||||
112
modules/nixos/services/llama-swap/setup-qwen36-vllm.sh
Executable file
112
modules/nixos/services/llama-swap/setup-qwen36-vllm.sh
Executable file
@@ -0,0 +1,112 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Setup script for vLLM Qwen3.6-27B on a single 3090.
|
||||||
|
#
|
||||||
|
# Idempotent - safe to re-run; skips steps already completed.
|
||||||
|
#
|
||||||
|
# Prerequisites: git (with git-lfs), docker
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Model Directories
|
||||||
|
MODEL_DIR="/mnt/ssd/vLLM/Models"
|
||||||
|
MODEL_SUBDIR="qwen3.6-27b-autoround-int4"
|
||||||
|
PATCHES_DIR="/mnt/ssd/vLLM/Patches"
|
||||||
|
TEMPLATES_DIR="/mnt/ssd/vLLM/Templates"
|
||||||
|
CACHE_DIR="/mnt/ssd/vLLM/Cache"
|
||||||
|
GENESIS_DIR="${PATCHES_DIR}/genesis"
|
||||||
|
GENESIS_PIN="${GENESIS_PIN:-7b9fd319}"
|
||||||
|
|
||||||
|
# Timings Patch
|
||||||
|
TIMINGS_PATCH="${PATCHES_DIR}/patch_timings_1acd67a.py"
|
||||||
|
TIMINGS_PATCH_URL="${TIMINGS_PATCH_URL:-https://gitea.va.reichard.io/evan/nix/raw/branch/master/modules/nixos/services/llama-swap/patches/patch_timings_1acd67a.py}"
|
||||||
|
|
||||||
|
# Template
|
||||||
|
TEMPLATE="${TEMPLATES_DIR}/chat_template-v11.jinja"
|
||||||
|
TEMPLATE_URL="https://huggingface.co/froggeric/Qwen-Fixed-Chat-Templates/resolve/main/qwen3.6/chat_template-v11.jinja"
|
||||||
|
|
||||||
|
# ---------- Preflight Checks ----------
|
||||||
|
for cmd in git git-lfs curl; do
|
||||||
|
if ! command -v "$cmd" &>/dev/null; then
|
||||||
|
echo "ERROR: '$cmd' not found in PATH." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# ---------- Create Directories ----------
|
||||||
|
echo "Creating directories..."
|
||||||
|
mkdir -p "${TEMPLATES_DIR}" "${MODEL_DIR}" "${PATCHES_DIR}" "${CACHE_DIR}/torch_compile" "${CACHE_DIR}/triton"
|
||||||
|
|
||||||
|
# ---------- Download Model ----------
|
||||||
|
if [ -d "${MODEL_DIR}/${MODEL_SUBDIR}/.git" ]; then
|
||||||
|
echo "Model already cloned at ${MODEL_DIR}/${MODEL_SUBDIR}, skipping."
|
||||||
|
else
|
||||||
|
echo "Cloning Lorbus/Qwen3.6-27B-int4-AutoRound (with LFS)..."
|
||||||
|
git clone https://huggingface.co/Lorbus/Qwen3.6-27B-int4-AutoRound \
|
||||||
|
"${MODEL_DIR}/${MODEL_SUBDIR}"
|
||||||
|
echo "Model cloned."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------- Clone / Pin Genesis Patches ----------
|
||||||
|
if [ -d "${GENESIS_DIR}/.git" ]; then
|
||||||
|
echo "Genesis already cloned - fetching + checking out ${GENESIS_PIN} ..."
|
||||||
|
(cd "${GENESIS_DIR}" && git fetch origin && git checkout "${GENESIS_PIN}" 2>&1 | tail -3)
|
||||||
|
else
|
||||||
|
echo "Cloning Genesis patches at ${GENESIS_PIN} ..."
|
||||||
|
git clone https://github.com/Sandermage/genesis-vllm-patches "${GENESIS_DIR}"
|
||||||
|
(cd "${GENESIS_DIR}" && git checkout "${GENESIS_PIN}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Sanity Check
|
||||||
|
if [[ ! -d "${GENESIS_DIR}/vllm/_genesis" ]]; then
|
||||||
|
echo "ERROR: genesis tree at ${GENESIS_PIN} missing vllm/_genesis package." >&2
|
||||||
|
echo " Re-run with GENESIS_PIN=<other-ref> to try a different version." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Genesis pinned to ${GENESIS_PIN} ($(cd "${GENESIS_DIR}" && git rev-parse --short HEAD))"
|
||||||
|
|
||||||
|
# ---------- Download URL Patch ----------
|
||||||
|
install_via_url() {
|
||||||
|
local name="$1"
|
||||||
|
local url="$2"
|
||||||
|
local dest="$3"
|
||||||
|
local tmp_patch
|
||||||
|
tmp_patch="$(mktemp)"
|
||||||
|
|
||||||
|
echo "Downloading ${name}..."
|
||||||
|
curl -fsSL "${url}" -o "${tmp_patch}"
|
||||||
|
|
||||||
|
if [ -f "${dest}" ] && cmp -s "${tmp_patch}" "${dest}"; then
|
||||||
|
echo "${name} already current at ${dest}, skipping."
|
||||||
|
else
|
||||||
|
echo "Installing ${name} to ${dest}..."
|
||||||
|
install -m 0644 "${tmp_patch}" "${dest}"
|
||||||
|
echo "${name} installed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "${tmp_patch}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------- Download Assets ----------
|
||||||
|
install_via_url "patch_timings_1acd67a.py" "${TIMINGS_PATCH_URL}" "${TIMINGS_PATCH}"
|
||||||
|
install_via_url "chat_template-v11.jinja" "${TEMPLATE_URL}" "${TEMPLATE}"
|
||||||
|
|
||||||
|
# ---------- Summary ----------
|
||||||
|
echo ""
|
||||||
|
echo "=== Setup Complete ==="
|
||||||
|
echo " Model: ${MODEL_DIR}/${MODEL_SUBDIR}"
|
||||||
|
echo " Cache: ${CACHE_DIR}/{torch_compile,triton}"
|
||||||
|
echo " Genesis: ${GENESIS_DIR} (pinned: ${GENESIS_PIN})"
|
||||||
|
echo ""
|
||||||
|
echo "Expected layout:"
|
||||||
|
echo " /mnt/ssd/vLLM/"
|
||||||
|
echo " ├── Models/"
|
||||||
|
echo " │ └── qwen3.6-27b-autoround-int4/ (model weights)"
|
||||||
|
echo " ├── Templates/"
|
||||||
|
echo " │ └── chat_template-v11.jinja (chat template)"
|
||||||
|
echo " ├── Cache/"
|
||||||
|
echo " │ ├── torch_compile/ (torch.compile cache)"
|
||||||
|
echo " │ └── triton/ (Triton kernel cache)"
|
||||||
|
echo " └── Patches/"
|
||||||
|
echo " ├── genesis/ (Genesis @ ${GENESIS_PIN})"
|
||||||
|
echo " │ └── vllm/_genesis/ (mounted into container)"
|
||||||
|
echo " └── patch_timings_1acd67a.py (boot-time: llama.cpp-compatible timings)"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
{ inputs, ... }:
|
{ inputs, ... }:
|
||||||
final: _prev: {
|
final: _prev: {
|
||||||
firefox-addons = inputs.firefox-addons.packages.${final.system};
|
firefox-addons = inputs.firefox-addons.packages.${final.stdenv.hostPlatform.system};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
# Workaround for aarch64-darwin codesigning bug (nixpkgs#208951 / #507531):
|
|
||||||
# fish binaries from the binary cache occasionally have invalid ad-hoc
|
|
||||||
# signatures on Apple Silicon. Forcing a local rebuild ensures codesigning
|
|
||||||
# is applied on this machine with a valid signature.
|
|
||||||
{ inputs, ... }:
|
|
||||||
final: prev: {
|
|
||||||
fish = prev.fish.overrideAttrs (_old: {
|
|
||||||
# Bust the cache key so fish is always built locally rather than
|
|
||||||
# substituted from the binary cache where the signature may be stale.
|
|
||||||
NIX_FORCE_LOCAL_REBUILD = "darwin-codesign-fix";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
25
packages/conduit/default.nix
Normal file
25
packages/conduit/default.nix
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{ lib
|
||||||
|
, buildGoModule
|
||||||
|
, fetchgit
|
||||||
|
}:
|
||||||
|
|
||||||
|
buildGoModule rec {
|
||||||
|
pname = "conduit";
|
||||||
|
version = "unstable-2026-05-15";
|
||||||
|
|
||||||
|
src = fetchgit {
|
||||||
|
url = "https://gitea.va.reichard.io/evan/conduit.git";
|
||||||
|
rev = "8dfb14f1e7f952bee92cad29703dba55fb156f0c";
|
||||||
|
hash = "sha256-Fc0FHLCNBbEpOFFD0bHSDo1E5AsOzL2fJzHufleKBIo=";
|
||||||
|
};
|
||||||
|
|
||||||
|
vendorHash = "sha256-LOFT8eCNRm5Q2tVl7ifu4dB5cr828B/E2NJW5WiW0LI=";
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
description = "Self-hosted tunneling service";
|
||||||
|
homepage = "https://gitea.va.reichard.io/evan/conduit";
|
||||||
|
license = lib.licenses.mit;
|
||||||
|
maintainers = with lib.maintainers; [ evanreichard ];
|
||||||
|
mainProgram = "conduit";
|
||||||
|
};
|
||||||
|
}
|
||||||
41
packages/glimpse/default.nix
Normal file
41
packages/glimpse/default.nix
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{ lib
|
||||||
|
, buildNpmPackage
|
||||||
|
, fetchgit
|
||||||
|
, firefox
|
||||||
|
, geckodriver
|
||||||
|
, makeWrapper
|
||||||
|
,
|
||||||
|
}:
|
||||||
|
|
||||||
|
buildNpmPackage rec {
|
||||||
|
pname = "glimpse";
|
||||||
|
version = "unstable-2026-05-02";
|
||||||
|
|
||||||
|
src = fetchgit {
|
||||||
|
url = "https://gitea.va.reichard.io/evan/glimpse.git";
|
||||||
|
rev = "e3d7c28820ed9bd838e96f2419de946685ab8d23";
|
||||||
|
hash = "sha256-LtrwD7mkh3wUXr2do3IeKiljHgpxCL8drZrJBI32Bu0=";
|
||||||
|
};
|
||||||
|
|
||||||
|
npmDepsHash = "sha256-ycAjPZZqI3ZMIUubJbWy8G6X6LaXDcgdZGswikfkQj8=";
|
||||||
|
|
||||||
|
npmBuildScript = "build";
|
||||||
|
|
||||||
|
nativeBuildInputs = [ makeWrapper ];
|
||||||
|
|
||||||
|
postInstall = ''
|
||||||
|
wrapProgram $out/bin/glimpse \
|
||||||
|
--prefix PATH : ${lib.makeBinPath [
|
||||||
|
firefox
|
||||||
|
geckodriver
|
||||||
|
]}
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
description = "Browser automation CLI for inspecting web pages";
|
||||||
|
homepage = "https://gitea.va.reichard.io/evan/glimpse";
|
||||||
|
license = lib.licenses.isc;
|
||||||
|
maintainers = with lib.maintainers; [ evanreichard ];
|
||||||
|
mainProgram = "glimpse";
|
||||||
|
};
|
||||||
|
}
|
||||||
43
packages/ik-llama-cpp/default.nix
Normal file
43
packages/ik-llama-cpp/default.nix
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
{ pkgs }:
|
||||||
|
let
|
||||||
|
rev = "f9a93c37e2fc021760c3c1aa99cf74c73b7591a7";
|
||||||
|
src = pkgs.fetchFromGitHub {
|
||||||
|
owner = "ikawrakow";
|
||||||
|
repo = "ik_llama.cpp";
|
||||||
|
inherit rev;
|
||||||
|
hash = "sha256-vBVosqBi8FyrllWGJOYsOYaNYAKoTTq6bn+i0Y32pu4=";
|
||||||
|
leaveDotGit = true;
|
||||||
|
postFetch = ''
|
||||||
|
git -C "$out" rev-parse --short HEAD > $out/COMMIT
|
||||||
|
find "$out" -name .git -print0 | xargs -0 rm -rf
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in
|
||||||
|
(pkgs.callPackage "${src}/.devops/nix/package.nix" {
|
||||||
|
useCuda = true;
|
||||||
|
useVulkan = true;
|
||||||
|
useBlas = true;
|
||||||
|
useRocm = false;
|
||||||
|
useMetalKit = false;
|
||||||
|
}).overrideAttrs
|
||||||
|
(oldAttrs: {
|
||||||
|
inherit src;
|
||||||
|
|
||||||
|
# Add SPIR-V Headers for Vulkan Backend
|
||||||
|
# Newer ggml requires spirv/unified1/spirv.hpp which isn't pulled in by
|
||||||
|
# vulkan-headers alone.
|
||||||
|
buildInputs = (oldAttrs.buildInputs or [ ]) ++ [ pkgs.spirv-headers ];
|
||||||
|
|
||||||
|
# Auto CPU Optimizations + CUDA Arches
|
||||||
|
# Appended after upstream's flags so CMAKE_CUDA_ARCHITECTURES wins.
|
||||||
|
cmakeFlags = (oldAttrs.cmakeFlags or [ ]) ++ [
|
||||||
|
"-DGGML_CUDA_ENABLE_UNIFIED_MEMORY=1"
|
||||||
|
"-DCMAKE_CUDA_ARCHITECTURES=61;86" # GTX 1070 / GTX 1080ti / RTX 3090
|
||||||
|
];
|
||||||
|
|
||||||
|
# Disable Nix's march=native Stripping
|
||||||
|
preConfigure = ''
|
||||||
|
export NIX_ENFORCE_NO_NATIVE=0
|
||||||
|
${oldAttrs.preConfigure or ""}
|
||||||
|
'';
|
||||||
|
})
|
||||||
35
packages/llama-cpp/AGENTS.md
Normal file
35
packages/llama-cpp/AGENTS.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# llama-cpp — Agent Notes
|
||||||
|
|
||||||
|
Override of `pkgs.llama-cpp` with CUDA + Vulkan + BLAS, custom CMake flags, and an optional fork pin.
|
||||||
|
|
||||||
|
## Pitfalls
|
||||||
|
|
||||||
|
### `version` must be numeric
|
||||||
|
|
||||||
|
Upstream `pkgs/by-name/ll/llama-cpp/package.nix` passes `version` straight through as a C integer via:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
(cmakeFeature "LLAMA_BUILD_NUMBER" finalAttrs.version)
|
||||||
|
```
|
||||||
|
|
||||||
|
`build-info.cpp` then emits `int LLAMA_BUILD_NUMBER = <version>;`. A non-numeric `version` (e.g. `"mtp-clean-08b1474"`) breaks the build with:
|
||||||
|
|
||||||
|
```
|
||||||
|
error: '<value>' was not declared in this scope
|
||||||
|
int LLAMA_BUILD_NUMBER = <value>;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Convention:**
|
||||||
|
- Upstream tag pins: use the bare build number, e.g. `version = "9048";` with `tag = "b${version}";`.
|
||||||
|
- Fork / arbitrary commit pins: use a `YYYYMMDD` date derived from the commit's author/commit date (`gh api repos/<owner>/<repo>/commits/<sha>` → `.commit.committer.date`).
|
||||||
|
|
||||||
|
### `leaveDotGit` + `postFetch`
|
||||||
|
|
||||||
|
We keep `.git` only long enough to record the short SHA into `$out/COMMIT`, then strip it. Preserve this pattern when changing `src` so downstream tooling that reads `COMMIT` keeps working.
|
||||||
|
|
||||||
|
## Refreshing the pinned commit (fork)
|
||||||
|
|
||||||
|
1. `git ls-remote https://github.com/<owner>/llama.cpp refs/heads/<branch>` → get the full SHA.
|
||||||
|
2. `nix run nixpkgs#nix-prefetch-github -- <owner> llama.cpp --rev <sha> --leave-dot-git` → get the hash.
|
||||||
|
3. Look up the commit date: `curl -s https://api.github.com/repos/<owner>/llama.cpp/commits/<sha> | jq -r '.commit.committer.date'`.
|
||||||
|
4. Update `src.{owner,rev,hash}` and set `version = "YYYYMMDD"`.
|
||||||
@@ -1,4 +1,22 @@
|
|||||||
{ pkgs }:
|
{ pkgs }:
|
||||||
|
let
|
||||||
|
# Version MUST be an integer string.
|
||||||
|
# For tagged releases use the tag number (e.g. "9222").
|
||||||
|
# For HEAD builds use YYYYMMDD (e.g. "20260519").
|
||||||
|
version = "9496";
|
||||||
|
|
||||||
|
src = pkgs.fetchFromGitHub {
|
||||||
|
owner = "ggml-org";
|
||||||
|
repo = "llama.cpp";
|
||||||
|
rev = "94a220cd6745e6e3f8de62870b66fd5b9bc92700";
|
||||||
|
hash = "sha256-1jAowfGVzrrHDwWWzKESY7aV82whnuIg1N37fmtcgyw=";
|
||||||
|
leaveDotGit = true;
|
||||||
|
postFetch = ''
|
||||||
|
git -C "$out" rev-parse --short HEAD > $out/COMMIT
|
||||||
|
find "$out" -name .git -print0 | xargs -0 rm -rf
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in
|
||||||
(pkgs.llama-cpp.override {
|
(pkgs.llama-cpp.override {
|
||||||
cudaSupport = true;
|
cudaSupport = true;
|
||||||
blasSupport = true;
|
blasSupport = true;
|
||||||
@@ -6,19 +24,13 @@
|
|||||||
metalSupport = false;
|
metalSupport = false;
|
||||||
vulkanSupport = true;
|
vulkanSupport = true;
|
||||||
}).overrideAttrs
|
}).overrideAttrs
|
||||||
(oldAttrs: rec {
|
(oldAttrs: {
|
||||||
version = "8914";
|
inherit version src;
|
||||||
src = pkgs.fetchFromGitHub {
|
|
||||||
owner = "ggml-org";
|
# WebUI npm deps hash for our pinned src. Upstream nixpkgs builds the WebUI
|
||||||
repo = "llama.cpp";
|
# from tools/ui via `npm run build` in preConfigure (offline, using these
|
||||||
tag = "b${version}";
|
# deps), so no custom webui derivation / HF-bucket workaround is needed.
|
||||||
hash = "sha256-mgoJD4dxWKGnv4UzeBt3V1PrFb4gap8sF8JwI/FK72k=";
|
npmDepsHash = "sha256-1iM0LGeI9e+gZEHk46lkBe51DxIhiimfAm9o3Z3m9Ik=";
|
||||||
leaveDotGit = true;
|
|
||||||
postFetch = ''
|
|
||||||
git -C "$out" rev-parse --short HEAD > $out/COMMIT
|
|
||||||
find "$out" -name .git -print0 | xargs -0 rm -rf
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
# Add SPIR-V Headers for Vulkan Backend
|
# Add SPIR-V Headers for Vulkan Backend
|
||||||
# Newer llama.cpp requires spirv/unified1/spirv.hpp which isn't
|
# Newer llama.cpp requires spirv/unified1/spirv.hpp which isn't
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{ lib
|
{ lib
|
||||||
, stdenv
|
, stdenv
|
||||||
, buildGoModule
|
, buildGo126Module
|
||||||
, fetchFromGitHub
|
, fetchFromGitHub
|
||||||
, versionCheckHook
|
, versionCheckHook
|
||||||
, callPackage
|
, callPackage
|
||||||
@@ -11,15 +11,15 @@
|
|||||||
let
|
let
|
||||||
canExecute = stdenv.buildPlatform.canExecute stdenv.hostPlatform;
|
canExecute = stdenv.buildPlatform.canExecute stdenv.hostPlatform;
|
||||||
in
|
in
|
||||||
buildGoModule (finalAttrs: {
|
buildGo126Module (finalAttrs: {
|
||||||
pname = "llama-swap";
|
pname = "llama-swap";
|
||||||
version = "197";
|
version = "216";
|
||||||
|
|
||||||
src = fetchFromGitHub {
|
src = fetchFromGitHub {
|
||||||
owner = "mostlygeek";
|
owner = "mostlygeek";
|
||||||
repo = "llama-swap";
|
repo = "llama-swap";
|
||||||
tag = "v${finalAttrs.version}";
|
tag = "v${finalAttrs.version}";
|
||||||
hash = "sha256-EXgyYmpbN/zzr6KeSpvFEB+FS7gDIZFinNMv70v5boY=";
|
hash = "sha256-PHSY4z2h406xL+EcIYyrzr4s28txO7SCsWm8hrXf+2U=";
|
||||||
# populate values that require us to use git. By doing this in postFetch we
|
# populate values that require us to use git. By doing this in postFetch we
|
||||||
# can delete .git afterwards and maintain better reproducibility of the src.
|
# can delete .git afterwards and maintain better reproducibility of the src.
|
||||||
leaveDotGit = true;
|
leaveDotGit = true;
|
||||||
@@ -32,10 +32,10 @@ buildGoModule (finalAttrs: {
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
vendorHash = "sha256-XiDYlw/byu8CWvg4KSPC7m8PGCZXtp08Y1velx4BR8U=";
|
vendorHash = "sha256-QysQ7YdwJcLTziwL25j73n3tQVvzVQIFxN4GkTU8JZg=";
|
||||||
|
|
||||||
passthru.ui = callPackage ./ui.nix { llama-swap = finalAttrs.finalPackage; };
|
passthru.ui = callPackage ./ui.nix { llama-swap = finalAttrs.finalPackage; };
|
||||||
passthru.npmDepsHash = "sha256-Fs7+JKE8YBp2Xj8bVBlwmT+UwuD642VeUHiPx+fv94c=";
|
passthru.npmDepsHash = "sha256-NJqEJ+XTdpPFtJJxP4CGu+JDUW7lKDcFgsixQJ3SXtQ=";
|
||||||
|
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
versionCheckHook
|
versionCheckHook
|
||||||
|
|||||||
25
packages/open-proxy/default.nix
Normal file
25
packages/open-proxy/default.nix
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{ lib
|
||||||
|
, buildGoModule
|
||||||
|
, fetchgit
|
||||||
|
}:
|
||||||
|
|
||||||
|
buildGoModule rec {
|
||||||
|
pname = "open-proxy";
|
||||||
|
version = "unstable-2026-06-16";
|
||||||
|
|
||||||
|
src = fetchgit {
|
||||||
|
url = "https://gitea.va.reichard.io/evan/open-proxy.git";
|
||||||
|
rev = "a589341214a1e035b6ce2b2d79870e591a25ccca";
|
||||||
|
hash = "sha256-onfvxOl4TdeRrVLD1oJWcnhEDzKFYU/V0qxV1+NpQrg=";
|
||||||
|
};
|
||||||
|
|
||||||
|
vendorHash = null;
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
description = "Forward `open`/`xdg-open` from a remote VM to the host machine";
|
||||||
|
homepage = "https://gitea.va.reichard.io/evan/open-proxy";
|
||||||
|
license = lib.licenses.mit;
|
||||||
|
maintainers = with lib.maintainers; [ evanreichard ];
|
||||||
|
mainProgram = "open-proxy";
|
||||||
|
};
|
||||||
|
}
|
||||||
23
packages/pi-coding-agent/AGENTS.md
Normal file
23
packages/pi-coding-agent/AGENTS.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# pi-coding-agent Packaging Notes
|
||||||
|
|
||||||
|
`pi-coding-agent` is built from the `earendil-works/pi-mono` monorepo with `buildNpmPackage`.
|
||||||
|
|
||||||
|
## Lockfile Metadata
|
||||||
|
|
||||||
|
Upstream `package-lock.json` may omit `resolved` / `integrity` metadata that npm can recover online, but Nix needs for its offline npm cache. Keep a package-local enriched lockfile at `packages/pi-coding-agent/package-lock.json` and copy it in during `prePatch` before `npmConfigHook` validates/generates `npmDeps`.
|
||||||
|
|
||||||
|
After bumping `version` in `default.nix`, regenerate it with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node packages/pi-coding-agent/update-lockfile.mjs
|
||||||
|
# or explicitly:
|
||||||
|
node packages/pi-coding-agent/update-lockfile.mjs 0.74.0
|
||||||
|
```
|
||||||
|
|
||||||
|
Then refresh `npmDepsHash` from the FOD mismatch:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix build .#packages.aarch64-linux.pi-coding-agent.npmDeps --no-link
|
||||||
|
```
|
||||||
|
|
||||||
|
Remember: new files must be `git add`ed before the flake can see them.
|
||||||
@@ -2,6 +2,10 @@
|
|||||||
, buildNpmPackage
|
, buildNpmPackage
|
||||||
, fetchFromGitHub
|
, fetchFromGitHub
|
||||||
, nodejs
|
, nodejs
|
||||||
|
, nodejs_22
|
||||||
|
, firefox
|
||||||
|
, geckodriver
|
||||||
|
, makeWrapper
|
||||||
, pkg-config
|
, pkg-config
|
||||||
, pixman
|
, pixman
|
||||||
, cairo
|
, cairo
|
||||||
@@ -14,18 +18,23 @@
|
|||||||
|
|
||||||
buildNpmPackage rec {
|
buildNpmPackage rec {
|
||||||
pname = "pi-coding-agent";
|
pname = "pi-coding-agent";
|
||||||
version = "0.70.2";
|
version = "0.78.1";
|
||||||
|
|
||||||
src = fetchFromGitHub {
|
src = fetchFromGitHub {
|
||||||
owner = "badlogic";
|
owner = "earendil-works";
|
||||||
repo = "pi-mono";
|
repo = "pi-mono";
|
||||||
rev = "v${version}";
|
rev = "v${version}";
|
||||||
hash = "sha256-qqmJloTp3mWuZBGgpwoyoFyXx6QD8xhJEwCZb7xFabM=";
|
hash = "sha256-K5+reVdi9LPwUHxFgM1iFWojuj6M/m25ymhkDOQdBE4=";
|
||||||
};
|
};
|
||||||
|
|
||||||
npmDepsHash = "sha256-ImDvTC0Nm+IGYJuqjwUUfnOtA65uJvjlpP4h2Xt/2vE=";
|
npmDepsHash = "sha256-PknwCOAr61Fq2Mhl6jd79Rdsje1OXFts2MDLM/gIEYE=";
|
||||||
|
|
||||||
nativeBuildInputs = [ pkg-config ];
|
nativeBuildInputs = [ pkg-config makeWrapper ];
|
||||||
|
|
||||||
|
# Restore NPM Metadata - upstream lockfile omits resolved/integrity entries needed by buildNpmPackage.
|
||||||
|
prePatch = ''
|
||||||
|
cp ${./package-lock.json} package-lock.json
|
||||||
|
'';
|
||||||
|
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
pixman
|
pixman
|
||||||
@@ -39,7 +48,7 @@ buildNpmPackage rec {
|
|||||||
# Skip generate-models in ai package (models.generated.ts already in repo)
|
# Skip generate-models in ai package (models.generated.ts already in repo)
|
||||||
preBuild = ''
|
preBuild = ''
|
||||||
substituteInPlace packages/ai/package.json \
|
substituteInPlace packages/ai/package.json \
|
||||||
--replace-fail '"build": "npm run generate-models && tsgo -p tsconfig.build.json"' \
|
--replace-fail '"build": "npm run generate-models && npm run generate-image-models && tsgo -p tsconfig.build.json"' \
|
||||||
'"build": "tsgo -p tsconfig.build.json"'
|
'"build": "tsgo -p tsconfig.build.json"'
|
||||||
'';
|
'';
|
||||||
|
|
||||||
@@ -70,14 +79,22 @@ buildNpmPackage rec {
|
|||||||
EOF
|
EOF
|
||||||
chmod +x $out/bin/pi
|
chmod +x $out/bin/pi
|
||||||
|
|
||||||
|
wrapProgram $out/bin/pi \
|
||||||
|
--prefix PATH : ${lib.makeBinPath [
|
||||||
|
nodejs_22
|
||||||
|
# evan/pi-web - Browser automation tools are needed for web-fetch support.
|
||||||
|
firefox
|
||||||
|
geckodriver
|
||||||
|
]}
|
||||||
|
|
||||||
runHook postInstall
|
runHook postInstall
|
||||||
'';
|
'';
|
||||||
|
|
||||||
meta = {
|
meta = {
|
||||||
description = "Coding agent CLI with read, bash, edit, write tools and session management";
|
description = "Coding agent CLI with read, bash, edit, write tools and session management";
|
||||||
homepage = "https://github.com/badlogic/pi-mono";
|
homepage = "https://github.com/earendil-works/pi-mono";
|
||||||
downloadPage = "https://www.npmjs.com/package/@mariozechner/pi-coding-agent";
|
downloadPage = "https://www.npmjs.com/package/@earendil-works/pi-coding-agent";
|
||||||
changelog = "https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/CHANGELOG.md";
|
changelog = "https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/CHANGELOG.md";
|
||||||
license = lib.licenses.mit;
|
license = lib.licenses.mit;
|
||||||
maintainers = with lib.maintainers; [ evanreichard ];
|
maintainers = with lib.maintainers; [ evanreichard ];
|
||||||
mainProgram = "pi";
|
mainProgram = "pi";
|
||||||
|
|||||||
6375
packages/pi-coding-agent/package-lock.json
generated
Normal file
6375
packages/pi-coding-agent/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
92
packages/pi-coding-agent/update-lockfile.mjs
Executable file
92
packages/pi-coding-agent/update-lockfile.mjs
Executable file
@@ -0,0 +1,92 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
import fs from 'node:fs/promises';
|
||||||
|
import path from 'node:path';
|
||||||
|
import process from 'node:process';
|
||||||
|
|
||||||
|
const repoRoot = new URL('../..', import.meta.url);
|
||||||
|
const packageDir = new URL('.', import.meta.url);
|
||||||
|
const defaultNixPath = new URL('default.nix', packageDir);
|
||||||
|
const lockfilePath = new URL('package-lock.json', packageDir);
|
||||||
|
const registryCache = new Map();
|
||||||
|
|
||||||
|
// Version Selection
|
||||||
|
async function getVersion() {
|
||||||
|
const argVersion = process.argv[2];
|
||||||
|
if (argVersion) return argVersion.replace(/^v/, '');
|
||||||
|
|
||||||
|
const defaultNix = await fs.readFile(defaultNixPath, 'utf8');
|
||||||
|
const match = defaultNix.match(/version\s*=\s*"([^"]+)";/);
|
||||||
|
if (!match) throw new Error(`Unable to find version in ${defaultNixPath.pathname}`);
|
||||||
|
return match[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Package Name Extraction
|
||||||
|
function packageNameFromLockPath(lockPath) {
|
||||||
|
const parts = lockPath.split('/');
|
||||||
|
const idx = parts.lastIndexOf('node_modules');
|
||||||
|
if (idx < 0 || idx + 1 >= parts.length) return null;
|
||||||
|
|
||||||
|
const first = parts[idx + 1];
|
||||||
|
if (first.startsWith('@')) return `${first}/${parts[idx + 2]}`;
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registry Fetching
|
||||||
|
function registryUrl(name, version) {
|
||||||
|
const encodedName = name.startsWith('@') ? name.replace('/', '%2f') : name;
|
||||||
|
return `https://registry.npmjs.org/${encodedName}/${version}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchPackageMetadata(name, version) {
|
||||||
|
const key = `${name}@${version}`;
|
||||||
|
if (registryCache.has(key)) return registryCache.get(key);
|
||||||
|
|
||||||
|
const response = await fetch(registryUrl(name, version), {
|
||||||
|
headers: { accept: 'application/json' },
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to fetch ${key}: HTTP ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const metadata = await response.json();
|
||||||
|
const dist = metadata.dist;
|
||||||
|
if (!dist?.tarball || !dist?.integrity) {
|
||||||
|
throw new Error(`Missing dist.tarball/dist.integrity for ${key}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
registryCache.set(key, dist);
|
||||||
|
return dist;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lockfile Enrichment
|
||||||
|
async function enrichLockfile(lock) {
|
||||||
|
let updated = 0;
|
||||||
|
|
||||||
|
for (const [lockPath, entry] of Object.entries(lock.packages ?? {})) {
|
||||||
|
if (!lockPath || entry.link || !entry.version || entry.resolved) continue;
|
||||||
|
if (!lockPath.includes('node_modules')) continue;
|
||||||
|
|
||||||
|
const name = packageNameFromLockPath(lockPath);
|
||||||
|
if (!name) continue;
|
||||||
|
|
||||||
|
const dist = await fetchPackageMetadata(name, entry.version);
|
||||||
|
entry.resolved = dist.tarball;
|
||||||
|
entry.integrity = dist.integrity;
|
||||||
|
updated += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main
|
||||||
|
const version = await getVersion();
|
||||||
|
const lockUrl = `https://raw.githubusercontent.com/earendil-works/pi-mono/v${version}/package-lock.json`;
|
||||||
|
const response = await fetch(lockUrl, { headers: { accept: 'application/json' } });
|
||||||
|
if (!response.ok) throw new Error(`Failed to fetch ${lockUrl}: HTTP ${response.status}`);
|
||||||
|
|
||||||
|
const lock = await response.json();
|
||||||
|
const updated = await enrichLockfile(lock);
|
||||||
|
await fs.writeFile(lockfilePath, JSON.stringify(lock, null, 2) + '\n');
|
||||||
|
|
||||||
|
const displayPath = path.relative(repoRoot.pathname, lockfilePath.pathname);
|
||||||
|
console.error(`Wrote ${displayPath} for v${version}; restored metadata for ${updated} entries.`);
|
||||||
@@ -11,16 +11,18 @@
|
|||||||
}:
|
}:
|
||||||
buildNpmPackage (finalAttrs: {
|
buildNpmPackage (finalAttrs: {
|
||||||
pname = "qwen-code";
|
pname = "qwen-code";
|
||||||
version = "0.4.0-nightly.20251209.a6a57233";
|
version = "0.16.0-preview.0";
|
||||||
|
|
||||||
src = fetchFromGitHub {
|
src = fetchFromGitHub {
|
||||||
owner = "QwenLM";
|
owner = "QwenLM";
|
||||||
repo = "qwen-code";
|
repo = "qwen-code";
|
||||||
tag = "v${finalAttrs.version}";
|
tag = "v${finalAttrs.version}";
|
||||||
hash = "sha256-s9m1IN6jDDbNPr/vI/UcrauYPiyQTDODarLP3EvnG3Y=";
|
hash = "sha256-UAJNw1RjHRoZqtgIWJ1dOTWnE9LoBpfJCAM0Jay+VPI=";
|
||||||
};
|
};
|
||||||
|
|
||||||
npmDepsHash = "sha256-ngAjCCoHLPZ+GgBRmAKbRYaF7l+RK3YGf1kEkwFbyQg=";
|
npmDepsHash = "sha256-uJtOeNnhbGE7EzTwkNbg2EHLonjHCbdPH5rcV2bgQUw=";
|
||||||
|
makeCacheWritable = true;
|
||||||
|
npmFlags = [ "--legacy-peer-deps" ];
|
||||||
|
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
jq
|
jq
|
||||||
@@ -34,8 +36,13 @@ buildNpmPackage (finalAttrs: {
|
|||||||
libsecret
|
libsecret
|
||||||
];
|
];
|
||||||
|
|
||||||
postPatch = ''
|
prePatch = ''
|
||||||
|
${jq}/bin/jq '.dependencies."iconv-lite" = "^0.7.0"' \
|
||||||
|
packages/core/package.json > packages/core/package.json.tmp
|
||||||
|
mv packages/core/package.json.tmp packages/core/package.json
|
||||||
|
|
||||||
${jq}/bin/jq '
|
${jq}/bin/jq '
|
||||||
|
.packages."packages/core".dependencies."iconv-lite" = "^0.7.0" |
|
||||||
del(.packages."node_modules/node-pty") |
|
del(.packages."node_modules/node-pty") |
|
||||||
del(.packages."node_modules/@lydell/node-pty") |
|
del(.packages."node_modules/@lydell/node-pty") |
|
||||||
del(.packages."node_modules/@lydell/node-pty-darwin-arm64") |
|
del(.packages."node_modules/@lydell/node-pty-darwin-arm64") |
|
||||||
@@ -62,9 +69,26 @@ buildNpmPackage (finalAttrs: {
|
|||||||
' package-lock.json > package-lock.json.tmp && mv package-lock.json.tmp package-lock.json
|
' package-lock.json > package-lock.json.tmp && mv package-lock.json.tmp package-lock.json
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
preBuild = ''
|
||||||
|
mkdir -p node_modules/@lydell/node-pty
|
||||||
|
printf '%s\n' \
|
||||||
|
'export interface IPty {' \
|
||||||
|
' pid: number;' \
|
||||||
|
' onData(callback: (data: string) => void): void;' \
|
||||||
|
' onExit(callback: (event: { exitCode: number; signal?: number }) => void): void;' \
|
||||||
|
' kill(signal?: string): void;' \
|
||||||
|
' write(data: string): void;' \
|
||||||
|
' resize(columns: number, rows: number): void;' \
|
||||||
|
' removeListener(event: string, listener: (...args: unknown[]) => void): void;' \
|
||||||
|
' exitCode?: number;' \
|
||||||
|
'}' \
|
||||||
|
> node_modules/@lydell/node-pty/node-pty.d.ts
|
||||||
|
'';
|
||||||
|
|
||||||
buildPhase = ''
|
buildPhase = ''
|
||||||
runHook preBuild
|
runHook preBuild
|
||||||
npm run generate
|
npm run generate
|
||||||
|
npm run build
|
||||||
npm run bundle
|
npm run bundle
|
||||||
runHook postBuild
|
runHook postBuild
|
||||||
'';
|
'';
|
||||||
@@ -75,6 +99,14 @@ buildNpmPackage (finalAttrs: {
|
|||||||
cp -r dist/* $out/share/qwen-code/
|
cp -r dist/* $out/share/qwen-code/
|
||||||
npm prune --production
|
npm prune --production
|
||||||
cp -r node_modules $out/share/qwen-code/
|
cp -r node_modules $out/share/qwen-code/
|
||||||
|
if [ -d $out/share/qwen-code/vendor/ripgrep ]; then
|
||||||
|
find $out/share/qwen-code/vendor/ripgrep -type f -name rg -exec sh -c '
|
||||||
|
for rg; do
|
||||||
|
rm "$rg"
|
||||||
|
ln -s ${ripgrep}/bin/rg "$rg"
|
||||||
|
done
|
||||||
|
' sh {} +
|
||||||
|
fi
|
||||||
find $out/share/qwen-code/node_modules -type l -delete || true
|
find $out/share/qwen-code/node_modules -type l -delete || true
|
||||||
patchShebangs $out/share/qwen-code
|
patchShebangs $out/share/qwen-code
|
||||||
ln -s $out/share/qwen-code/cli.js $out/bin/qwen
|
ln -s $out/share/qwen-code/cli.js $out/bin/qwen
|
||||||
|
|||||||
114
packages/tuxguitar/default.nix
Normal file
114
packages/tuxguitar/default.nix
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
{ lib
|
||||||
|
, stdenv
|
||||||
|
, maven
|
||||||
|
, fetchFromGitHub
|
||||||
|
, jdk17
|
||||||
|
, jre
|
||||||
|
, swt
|
||||||
|
, makeWrapper
|
||||||
|
, wrapGAppsHook3
|
||||||
|
, pkg-config
|
||||||
|
, alsa-lib
|
||||||
|
, jack2
|
||||||
|
, fluidsynth
|
||||||
|
, libpulseaudio
|
||||||
|
, lilv
|
||||||
|
, suil
|
||||||
|
, qt5
|
||||||
|
, which
|
||||||
|
}:
|
||||||
|
|
||||||
|
maven.buildMavenPackage rec {
|
||||||
|
pname = "tuxguitar";
|
||||||
|
version = "2.0.1";
|
||||||
|
|
||||||
|
src = fetchFromGitHub {
|
||||||
|
owner = "helge17";
|
||||||
|
repo = "tuxguitar";
|
||||||
|
rev = version;
|
||||||
|
hash = "sha256-USdYj8ebosXkiZpDqyN5J+g1kjyWm225iQlx/szXmLA=";
|
||||||
|
};
|
||||||
|
|
||||||
|
mvnHash = "sha256-XTODH8SG7iwhACJT4AbIokORUe00r6theV18TEXbrIs=";
|
||||||
|
|
||||||
|
doCheck = false;
|
||||||
|
|
||||||
|
mvnJdk = jdk17;
|
||||||
|
|
||||||
|
nativeBuildInputs = [
|
||||||
|
makeWrapper
|
||||||
|
pkg-config
|
||||||
|
wrapGAppsHook3
|
||||||
|
];
|
||||||
|
|
||||||
|
buildInputs = [
|
||||||
|
alsa-lib
|
||||||
|
fluidsynth
|
||||||
|
jack2
|
||||||
|
lilv
|
||||||
|
qt5.qtbase
|
||||||
|
suil
|
||||||
|
];
|
||||||
|
|
||||||
|
mvnFetchExtraArgs = {
|
||||||
|
inherit buildInputs;
|
||||||
|
dontWrapQtApps = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
postPatch = ''
|
||||||
|
substituteInPlace desktop/build-scripts/native-modules/tuxguitar-synth-lv2-linux/pom.xml \
|
||||||
|
--replace-fail /usr/include/lilv-0/lilv ${lib.getDev lilv}/include/lilv-0/lilv \
|
||||||
|
--replace-fail /usr/include/suil-0/suil ${lib.getDev suil}/include/suil-0/suil
|
||||||
|
|
||||||
|
if [[ "$name" == maven-deps-* ]]; then
|
||||||
|
mvn install:install-file \
|
||||||
|
-Dfile=${swt}/jars/swt.jar \
|
||||||
|
-DgroupId=org.eclipse.swt \
|
||||||
|
-DartifactId=org.eclipse.swt.gtk.linux \
|
||||||
|
-Dpackaging=jar \
|
||||||
|
-Dversion=4.36 \
|
||||||
|
-Dmaven.repo.local=$out/.m2
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
|
||||||
|
mvnParameters = "-f desktop/build-scripts/tuxguitar-linux-swt/pom.xml verify -P native-modules";
|
||||||
|
|
||||||
|
dontWrapGApps = true;
|
||||||
|
dontWrapQtApps = true;
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
|
||||||
|
mkdir -p $out/bin
|
||||||
|
cp -r desktop/build-scripts/tuxguitar-linux-swt/target/tuxguitar-*-linux-swt/{dist,lib,share,tuxguitar.sh} $out/
|
||||||
|
ln -sf ${swt}/jars/swt.jar $out/lib/swt.jar
|
||||||
|
ln -s ../tuxguitar.sh $out/bin/tuxguitar
|
||||||
|
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
|
||||||
|
postFixup = ''
|
||||||
|
wrapProgram $out/tuxguitar.sh \
|
||||||
|
"''${gappsWrapperArgs[@]}" \
|
||||||
|
--prefix PATH : ${lib.makeBinPath [ jre which ]} \
|
||||||
|
--prefix LD_LIBRARY_PATH : "$out/lib:${lib.makeLibraryPath [
|
||||||
|
swt
|
||||||
|
alsa-lib
|
||||||
|
fluidsynth
|
||||||
|
jack2
|
||||||
|
libpulseaudio
|
||||||
|
lilv
|
||||||
|
qt5.qtbase
|
||||||
|
suil
|
||||||
|
]}"
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
description = "Multitrack guitar tablature editor";
|
||||||
|
homepage = "https://github.com/helge17/tuxguitar";
|
||||||
|
license = lib.licenses.lgpl2;
|
||||||
|
maintainers = with lib.maintainers; [ evanreichard ];
|
||||||
|
mainProgram = "tuxguitar";
|
||||||
|
platforms = lib.platforms.linux;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,58 +1,75 @@
|
|||||||
|
open_proxy_token: ENC[AES256_GCM,data:LxC0dR2EQ8XPmw4fwKnKJD3usqImMKH+81I9RSTNsjg=,iv:LQmf+kxtwgAMxcHvIe6y3Qw/oxXvdWGbyV/kdwPpKw4=,tag:TUQbM8sIK6KB7eOsYfHuqw==,type:str]
|
||||||
|
conduit_apikey: ENC[AES256_GCM,data:4mjvEI00V7nAhPkDa97eOfLCqItxoRALFe8OdxzUiUc=,iv:2mtSu1LDUvaneTnqs3Z4GVAj+HuAY2+VXrpIITSg/64=,tag:trkgxyX0ssCjyKMB42bFGg==,type:str]
|
||||||
context7_apikey: ENC[AES256_GCM,data:K8/OoJMWBhN3ufmTa/tAiD3iMergDZQ1OBucUtLsrg+L26DXDPAko9D41w==,iv:/IVpaaPivUTn2rbIAPIwyN5nb7TmtDh05YlMdOlBkhE=,tag:0XJfoNlDelBwMXMAAqKjtQ==,type:str]
|
context7_apikey: ENC[AES256_GCM,data:K8/OoJMWBhN3ufmTa/tAiD3iMergDZQ1OBucUtLsrg+L26DXDPAko9D41w==,iv:/IVpaaPivUTn2rbIAPIwyN5nb7TmtDh05YlMdOlBkhE=,tag:0XJfoNlDelBwMXMAAqKjtQ==,type:str]
|
||||||
rke2_kubeconfig: ENC[AES256_GCM,data:Rfls+wsKywqic5ZphpnmQ2GO/Pum+FC6XqhojYVS+AGwbsCNP3P4HzERa2RcDewXNEMUKro2Rr8dVMZIN4IAoMN/02gvHfBE0L9EmnZAZoaYCpbbazk7YHjFdPg+/3mDCH5TvZTp/jP9A3hy0KB6r5ksQaKJVE1RYD1I+WSPkLiLKy0gH/hJI5xUao7Vd5QhAINILCxFXK9qh68RIYa5faa1nEPk6e28wwNo7cOXsdHbYfg0m1ZwG7s11OVxAR0pDgJGhuPPD5DVqVTtaZtTOh5OqtH5zaCjkpK+c4JxcK6rpr0g1d/+aulPy1HYUEBIjNTrlV4aToy7m6wx4xbK/oCYqgUQVQyiJe5Ojm63UDBf8dSxAMVgu7Lmer++ADnICUBFaogvo3yYXKdhf6C3wbO6Z+4XqBqw+u4RJeCQNmaVL+AJSm+BjZlYA0I98kahmZ1iv3iEHltsCotSmKuBRPFInvyVbTlz1Q//bQC8ZwCD/Sf1WGfVJTnzFuMnROYrvt1qVaqj8Fl38+1dmdlpSE/rCBUM7Kg4IHakz5DxEMLflojo266DX46AnXBR7RRE8X32/RZcDYWeE14bWUZLFV3fhsCvQXX7H3q7jnr127UOQi0h4HjPS5SHCIxscaVjz0DZUzgo3WAMRW6vuD1S0XOAusD/B13mYku1sadQxxQGaeZyfcMvVN4Fb/udRhpOQEZLFunkrGF0GByh2JsYvyW22vkNoOc+Vclw2nWIt4B7DcTH7LJN47kACywR6U75d6AUmzEtPnXDJwikg+NE/HomGhmY1w7OJK+IiN1AhQ58jCEOsC+jlvN0qfr3LnoXt3mWJzyicoI1v7lY9mEpBcCP6/nTgHOazKlPP90Gm4G6jWRQIBGKbnQxe2Gpkf30e3hymPJkgSI2CexLQQjBwyEa5ozE2ri9xtWBSyPtSulKb+VU7vahGyV4ETgdwE86ixyjR8HRYNQX2sBGK0BtLAz3/MnUuhJcHv+RVJ3dbdR3XCu8s+oHC5Y+VC46i6vJ9RJnRjw9rH43kUinRSuZVfFQdipVEI/kiyuqpgOm7RcCDRFYb0aJAV4BCIKQqzwVpOtCMLPzuHtzuFmcbOWiViwh1zhAyZLdWv3WmDwQpCmEDfGvgwiw7eg646EuzhHhtenP9LNReTrxy9bqJUrYfE2BogpTzEuWGqNZvv+HlJzPhG0Ze4sYC1viyiuJehsv647pa/ywHmrca1X+yGLf7Qu0nKbVbTcOD6TePB7l14bT9nUrXEEuf8TDhvsNeO8tfPi3AG2+LdyQ9zi7DBxnaofxoF6XquSbvIlvwSvGSZD2cjd9W2DxeUWB7r+6cLhMHyldNgxThAQRoKhnEDb35ma4fgZfeh3bYc0eVsp1qJpHPfAU7M9BAShnIMUp+smOk04i4aIAtqrl6sZm0qDB96ouF+aCUKSzmCTopomxL487ItZ9X5CpYEfUoGeTiEiH7RWYB0qBx24yBj3vSN3n4JRR8+c/Dc9SKtRUJHVbyCD3bGtc9cGxDeQgmXizRaUFajCbvIMW7G4Lp1lBxcLSmUnE+eRPqKsbRt703jmDnv9wMEA5SZpZnFeF5p7Klm5orZnsADQE71VnDeqHFb3wrUnDZnzMbUVBLu66kTQ2nZKCF63N8/QyxGJmWB2aGsCB80foi/yxqXYxvuW9pAX88hyAHztSQISZ9pANq6Nils0A1+u8qwcPepZnMwNtn8PHaWs0KHOGYVL5v2FRLjMKmPP22uvXL/tCpi9E/nNP/qinkyt+dV6mCkkKRgDj9rrqGd8qoQHsHMb+XUCtaESzebPJmL77tIJqHvgozbdQnKPmbQRUoGobP3NS9p2i6KrbTW4NnYsNFH2jlOaWI9n352wVz2nNOZgWmsc3FDyyN6wMJNMI87iwili21lu6SUMpPq0mq+Mvd4vH0fHur0li68JHQK/ytrzaB2X82LmNqR47ETwmkHw2WYfhElRET4HUAVjmx4ei4WilnsUfvgWaBs3oBy6zwmDMTEnn/JI1IbEIOC2nasUlUwabpYRNQ6U+zRs1OgSs9aiPx4gIIoGTQZ1MKuh1RFGN4SmdW0IOWbCeMcX0Mipb0GyhXZeYuMy3GOVWp/xATkntKiwrAOg11Wv6ah8aNPHmhbu/eI5gTkCSw7kNLtGf4EdZHAKPOl79wys7MtmmyH+CRgWBvXqN9xYBxoIVP+oMfRk9Y4uz6mLK0XmC0knT+bz6KSXpsrAL0gMZlOKSMtQ8nX0/1kr99Uh6mIPMHPH6Mxy4OWklBLnLVTFxVt4Nl6wkVq6EBsF7RvQ2vC4gKs7ARF6JjZ2/KG1TYUn0BQ9h78EDwO7WdhGYqbsGy+wxtEDgUbIigpQ63X8hNxQZO6QC6Ij1bxlyGDlLwcX3erMtmDzf8A78jECMyQVSnxUNU7z5/oAkjm/qnmPMtZm082kNITOFU/idAS754EGmc2IXVlMsM49fS534J57hjibl2OhruBgkvSrJNZhmaOrs4LLI2SD9nZryH+bmEcgZ05xuVM6DkfvylTDtvC8AuzVeD7TgOlHef68KgsS8QI4CjiuZgD3dpakFhJr5LFagi+xUniLoTthQ3sLab8pamtq55GrQmtxkJAlVU5hp2/B5ap3hZxrknqTqE4XNGUl0JSfwi7QWCpVfu17wAai7JQpvUi9e2sVYl27+OF3siwWeY6WqXB7fMCSCPoZl0MWZFitzX3pquVYjTdBbsuolg/qeKQP5NLA3yLvjs9Iekqtk0AXk2CLqEOa49tc+FozV82JYGwvzBHEUqFyUqotUQTisbVMUsZu9TgR4ZVK6DVv4n1RpZzSsILTBKgBYD5Xv+mBboLKh3gyn16bsS0atkk4b+zciqbQkTBv6vffPvQS46vhi0AkUC9tcO3kw39ztl6m21HHTxG+rnH+wyY5hAoSiOxgZ6qb5Aaku3ZjfI0bebhQoYUdVAoe5rqe64h2s5LzDmfqj73SziTSliX5W5QQ+tP6GFmJDysGkEgSbZbQ2JkjRubgLaXL+RDH1kirWMtKgIJ0pA/7t2/MYl6wgwMB8pXNP3k/TlgPmFc0QyavEZijNsTQUkyBlL4fHASp7J3G0VAOfoUcS0el7C0B6q4RCCA4CnSCzj4Uj1ZvwK0dmnSky+CpG4Shwd7/36Rc35379f5UKB7ixwReXqGngdAwJ88BUUeMUfC7ApDKb/SaJICEE3l0Uc32mHzHW7prmgifaaeheYa3intxj+hN7EdBZam9/62LnKXaad1DJnqqSbJLxKh/8Nbx8ZXasAbWwVkIlaOvAjSD2Eh+DtAsfAB9F8mYvCoLUXChKeJ1MBKT9fqVSWUqeK5+ePp1idY4RVW1ELhV0Ew/QmHkW6dvoIlhNl/f7+q65wCoIj7zbfx8/Bw4B1PZnpe8WIHYAXrw/1si1jalmCKA1gvJTZaBGzz/96p3cr+DJNOAhoLzXnKMWfh2WQLVpcaYFiVBMxXGEFHTOzZMaE9pe9MIrOr1zI9HwEPrEFon1qmo+gnd0vsuZsOpIL6V7vB0z/Dw3+Wc3RBggNYYzMPSa+gY/Lq2WSMBrN1/k4qGm3cU/7g3Kf1xkptOHCM+CIAipA2Ji8A8I6xsxid1+HYjpaAAB01EZGhBQ5MiS+5HmkzSOcvlGQ9hvNvNtFedYX2mBBUcB9RmrR+OiU1jx01L56WFR61mARQHfUu3YM5L4vO7nylPACZfnb1MCfQL4+zv5gclHco8lhyPJACfC6T0LuVk2/3D0p/9Qcyhj1IzKZFSFW3d6DVI+JOW9CK4kn2+0M1mLP6ardJCjm1YUO3nL+d5XVkcsz0iSO+8TmowqM9EvJLg6jQ4mBPYqw2tBZhV0dDVdGmON+qo5WjSw7aqkufVKEI9d8Z9euyc+M4yBK8/6WCMfoR5q+u/EQX78smAMnPfKbbNJczPe,iv:p+HEgeqmySS8uG2yk7Ar1Bkr/LY2e2dMu4DSF0YdIyQ=,tag:0/+Z2ojLmKeSeAeHc7JzaQ==,type:str]
|
zai_apikey: ENC[AES256_GCM,data:eNgIfEqs8JGM7Qo6D5KMMqRF8fd1qLakYQ9F5oEDUvLqPJ+TAktz8GMVuSndwW5BxA==,iv:eR8IR/MDmhk2JUoT2chCwRYOJGfxEBFGARf1CI7EG8Q=,tag:3fmRWA5eof304WSWKntDFg==,type:str]
|
||||||
|
kagi_token: ENC[AES256_GCM,data:6pxxMMQ3RCy6sdUFiuAy8rUzsIMMiBgPzphpgTVMfiHC98ejgVolvFszR8SHwEgTxMzzc8wMs3Eun8PzkZQt+7lqIKeNWeauiXJAHIsMZiaBJVDrxXVW,iv:zz48rUwbxAGV5+eff0Sg5Q5Pm1lGvOScUwo+g0t02z8=,tag:XgvUbgmOnXxYBZbXe3/Wkg==,type:str]
|
||||||
|
rke2_kubeconfig: ENC[AES256_GCM,data:DmmaV5bSnSSbLfenT7/xsv9qq5V1s2b9mzdeOe5JbhXLcvC9RRX3z1TkAwdC9IEAtr0cIiPigJS2fUCo0/baYSZ+lKTZ6pUmuPwX0x1g2O8Vdfe7jTTnTDnZ/A8+CIrS79uhsNxlmQNpEOCCSAOQ4+XAnFbPbLh/0QhV2M3a19ocJBQnFyNpYCxverRvNIfgHOoMskvwn3MEsmp6foOGnwPsbeQ1RRiIyCmf7c6jJQH7O5qDLcTIFNYNKiorr8veRhI5av0eX+5/rM8wWgBVNo/lf4TJnX+ufIUZQYCIz4vpfaw8N1jcpiAJiUFGdlKX+AR9b3ti8owa5+JmQkLNp4GBEI+I0tdMp15K6RjKqkKrkPujtUFntxXC07r+eQ37oUUvS9qilIMrkX0TxWoooShgOgQfVUEAEdtb2o830TL1FFZHTiy5RBkeRQxol4yAW/M0B0S4iIj/W07UHNdp5tBaPotsdyj9QQrumYS67GwWolVW007pG8nvD/lvP55nAndsLZpHAYSFI6z1N5ayx0N4I8OP+dT5ElaQv/tt/KO69EQYEwJetgRLnMQ34WKfAr3akLYja6QxkrhEnhfa60mXP9QLynEWGsfYdMUjPioIiImvdRi+5FkyvQ7aZyVzCRsMNGL3I5f1dXWz2wS+B1oB9yimOpfz4wr2794w64EKO1gF5dso17ebVEBuT8myeOenZREVUJCEunYcFPsMDD8bI+VD/VJDwQI/aWmukBWW9dztySiAJA0RWOb69LeApgx2SUwcPnx1yLerb5FWjA8hzY6GKGKyO8cNMRbH/l6QjAL/oMg3cgi7dH/7o1dGSphvGpTAOmcb82ZiT4gMSHhKIrxdLKyZclGu4Rf/mSjadGzLrEA6qj5r07wJOxZHu2bcMafWnoUZBuo7yE/ogVkruW1vI9c4lNBsOIUescE4sE5qjRncJkPEh9pcwWWLFnRQVCxVSpp72VyeJTxvo9gEBHuZGFF3J7R3YsTq1YhcXAR5+PIseIY46rdxqIh2WmVfG4W/iteuQh+JEcspvHNMB7a7j3yTEOHQ7ILaslLYDDnma4qo6SPuTzhx3Tbkx1WN5FelVkw1INV6qSjL83ghfk5nOVumbYurrTXviqqWg5ikCJ/Ewy2nrpNMbeVMs0x9Wcdpe7xi405IdJm6ry5Ipo9ZMKNJxRDP+ebUBgfiB3WzVI68AvvTpePz3KxGDwzh2aWu3Ei7CJoBrCrkEk2DPxoGvBinOvslZYuGhvUqL5XuNoDjLuxNCY1Dt67dvLi3ydiekZU8mNQ1qraMSFBg4KXH9e718X1zjuAGv86TVUfllxiXeoo6L/Sgn2iO1YW5w6igO5qkuIYEIi1rpx0jFOrbgvZeU6qjBHQmFwEw7h03IVw54s62E2dCy1wSq1BcUu1jUR5iJ0mPJ8ajGhb4D7MRO2wanAQxrzKJhSJ1OAdCzrebprLJRoo1v4YySiQkZ5cD57YnABST+i3/u0aWcS3xDi8Z/NKr0TMyvf2rWpvlOYUfIDgZLQiHBiph0UZNk/XNpvH23e6lEHG/ztmIu8CbcAuAbAy7Qwf664UGq9cK82gklvebO6lo5vCUGpx8mLOQYLIOefdoDJEei3DoTeZvtOpLkNXnRlwSlCY4geCNOioU3H6mtF4JdlLSFM7QMt/4CpMGEEzXDVCu7GI2Gem7VmBuLOBhGYxiF1zG+D6ZKUxOx1rmo7f2flgdfEtlkpQrIbeZfVEnqgb5z9Vw8bziW+Kc5CJyo9iV09BK0aeZWstnR6SKIWwuImWrGM0zSQBHd8QdrgicuR416PnFuElT3dkrF3TedLTKWKasWhlGOYeILMzCz/dnwy85ihp5zc10AbIpLISAvHSaMtEgdiwIc2n2Ti94ntnwfIB3AG20X3/yljDZKezn95+SZV8jOhMk/OcrpGH3UTB7ezHyf6gVD0qLXM6xUgi5vehhsO8ihFFTSNHf0881fimokRHQPjMJ9NC3J26JLhqJEs0Zfwvx2+7NrGq0pnRQ0W0FExy14dWWx270/EF8L91YRFohCJidJYgD5oTEab3eG1itM+OAAIA5xG+g5N6Re+34yO3JDMVUfuxGYpxKxfF8eVvAXB46+CH/lXQH6cPO8plWmlrzT8TS3rPj8MtvXrNaCVHuoWQ3oNu0cSGtsgcX/kJ8P8rOV8wNEdj2EpA8Fq8o741OapgFxpc5fMN3gKc6n7uwnKBHMMaQsZ2ymWeEn+qa6f/Me0DlyOkVLQFTYqAKibOoeyCtNILMf/NPMFMLe7Oktl6f9dvsMtu1zwCSYAV83/Ti8sZdnPFLET4OttQB+Bk8IX7BeTndAc3uMrVuOBwJC4sh2uGvRIJPEYbfw6p2amcn8mylN1o+l6sntpMrudEFo+oAz6M4UCEoNpotRIIMBB+uZo8T876TxSbENz31IDrOJ8ka4hCE0dK/gxZQsnoa16VvouTSc92se52n3RdELu+q9Oeeubd4htUJHhqxpbSmEQGVTWH2kJT7c76NZu7B634aUtoYI5eYmcLB3zsqVwZeG9fpzNeHMS2C6qzT4uKjxmbTW8eicDYSZSyJg4rpEjxf9GZn8f4898JtGvKF+esthhOlaxCEFcVoPQLC4pOAzY/TN+XTPk55bEPQ5LP3cByTfIv1UnZvSjXpAz2m+DlbkGfEqiICHr4HkISJS297CKI05tjpQNfO1Ylp89uL1hqwcddRee6+34kb+1XjSYwKMooZvSf/pasjj5xlpbCtxkiEIHp996H4MNXaySjZ7QU49Yy3EAKjulbc7xQXpkSUZb5Rh8yge/KpCK/5gK/fwlYIJ3tR4mIMN/b7HmfulBmktekK0G2fxagoKFot3DOwY1OnwIJhNzo0fCS3qFe9r2Ixs76C78gXo+DChKxYcYS7N5wmQy1PxKOsBpxeiWyaHCIC8Ey5dPSEoYi0zr+1A6wbkPaCrKI/C9Bwu8f04ySCBdbQQa52uCdWM2ctIsPHZNzkWNcXCfz9XLQZYZG/bQJtj+lDXgKKd8AiF0+WKzinGWHkKQbZPVYJI0s0jLXfH3G+kg5QztkfXedHNY6y0F5hh15jRYWsLAn6Ls2NJVP9SmCvl+pj6wHRKqDV7SbHz2bqCwLzzD68Wvv7b44OLFZEAH8F1TW8D205Dw7YFDiLMnWjeWv8ZX5CNUi7tkiCeORdQGiiv6n3CItvqWQ00tKnO5h+4/E97PtsSgfqG+Pgyjjscz4z02stB1XuaGbfPKe604CeYcmhjf9mV8gjfewOYTs+E2j6aoSKJbOtdiWfIHNngn92D/tOLB2UsuErYd3ZGLoH2yBOVL1rLnkY1QG4JxzXCwpnauQTzHRmg/WpJXurETbVyzFkzkF9YnX9iuJFt1mZglZuaO0JtC93xhdjMqjJyau4Oba7ZwwzyvM4TkgvffbPZit5nNAAZO0HO4r3t5nMIJeS1uBin66pjUWEuZ8ptjA5iYuAJwAg2VOPUuDKc9HBYmQjJ+Yx+KhO0aDmJd2pV/R6TOKua6jAnmny2FFqbyC3znPTfJZHOnVPZZ0V1D0NJ8dP62EDBebTnoFScE+93xyB9ETE3ubrBACAPTMuCQlqAY5ix2F1MpCCT7c0ovMyRlDaUJyFXS93UfJf4MmQg1+yCHuGX9E/bqUXsXC6SdJU9PdJ/3QdhN7iIcy8rPxOfYjJSwFrdUZn9sJcrVsbbPuObOM9HPOtp/E2xTOlYkfIeValLFxBb2lEBZghYXT05X82fSoe9ZBZdJfIMmLnuh/bP++bBVU7/y/aCYFcRCOafDdLQrTOpJ62SIDP0zN0YkmRy41qNlSaMp0ZZpxiK45ihqGP4aqWBmGC822dTsY4og1V07rRnfdG2wQcW+VHLldg72vRoc2brhfRZAOvwHQQCy0NxMD5pCgoSXJD4qsJrOFBcsmbs8YZ2omHzkVf+9ybnUD4WNCjVIvM,iv:CMrKYb+2QZVKEJMjW51rbiYW/cN6ATDzgwfBdSi9B10=,tag:qVqO5byXdj7DZdaHNx7S9A==,type:str]
|
||||||
sops:
|
sops:
|
||||||
kms: []
|
|
||||||
gcp_kms: []
|
|
||||||
azure_kv: []
|
|
||||||
hc_vault: []
|
|
||||||
age:
|
age:
|
||||||
- recipient: age1sac93wpnjcv62s7583jv6a4yspndh6k0r25g3qx3k7gq748uvafst6nz4w
|
- enc: |
|
||||||
enc: |
|
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnaEYrY1ZsRGgvZmxhQ1ZU
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBrY0pseTB5R290aXAzdnB3
|
||||||
dEdpd1lyTzExZzU1SVgwTllQL0RGdzBoYnl3Ckt5Rm01UkxDTndQSGY3Tysza2hE
|
aTVMS2tBdnEvSjR5K241dElEa1Z1Tmt2a0ZJCnRSbVBsZ3lNVlNzdnFlY3VvaWpB
|
||||||
RmszeDFHcXFtR29UOUZrbGxaYzd4MEkKLS0tIExkZjFaUERnRDZlejE1Nm1ncFV3
|
aTd1ODFianpQR3RSMlk0Qk1IRE1jYlkKLS0tIENzVGgyUUFBcloweFdOMlE5cGNn
|
||||||
aE1ZdGt5MUd0am9oK2xQQWs2bHdWTG8K9L1SP4HuONB0ems8Cacze455sLnBveY2
|
S3hOa3p1MHVLek5zaUxhWXFiV2Z3R0UKxSrnYSoN6KcuFdg5K6qwcwh9/j9lI0HB
|
||||||
sOx9riAGvi8QiR/FTfI+O9BHbgIhTVlRQMLF33FLfV+BIwBl+yQZXg==
|
HqujumuIfWkcctNk38AMn4beeesmXsbJQcUPHUVOZQw6Ov4jXaGz/Q==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age17ayje4uv2mhwehhp9jr3u9l0ds07396kt7ef40sufx89vm7cgfjq6d5d4y
|
recipient: age1sac93wpnjcv62s7583jv6a4yspndh6k0r25g3qx3k7gq748uvafst6nz4w
|
||||||
enc: |
|
- enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpME5hVk4vZnl1Q1FNR0Ev
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBMazJ5VmNXQ2FRYzFuejBU
|
||||||
d2pWcGpQNUlLcVJReGNUYjhVeDYxRjVLZ1hBCjVLRjdKMTBHUzFZaVR1ekN1aENw
|
bWNPWnI2a2R3NVBpY0Y2N0lXd1RXbUdmTkc0ClZDTSs4bklpeVEyMDNrVXJXRHRF
|
||||||
bldhdW5ERngwc1M5ZjJtWFlLanU5YlEKLS0tICs1Q1RKYUs4WFE1TUQxVmREbWpO
|
UjIvRnd3VnFtbi9TMjVvd3UybTEvakEKLS0tIHpoRGtZb2VyUkJkd3hWcnZNWlln
|
||||||
LzlSR0NneVYvOG1OekJKejExMFFuTjAKGkWw4s5DU7uEPg+c2pXXfHymHHJHFNlD
|
a3dPbEZtL0tsOWpWSEtPblZSYk9NVTgKM6TfK7VX6v059FXpRjpAlgX+ab4f6vq2
|
||||||
zselZ73dBQly6GrTXomNYFlINE3s7J78rbVxo3aA098Nu1vAOKkU/Q==
|
jH8jyO33YxQYI1kSgXJ5AR8evCoV3FzbZ5rzIy2PRmCOwFV8Im2bRg==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1avlhszrryt4gf4ya536jhzm7qwt9xfttm8x4sns6h9w2tahzqp8sspz9y5
|
recipient: age17ayje4uv2mhwehhp9jr3u9l0ds07396kt7ef40sufx89vm7cgfjq6d5d4y
|
||||||
enc: |
|
- enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBycGhUNTV0UCt6N3R6NHV3
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4S0NrbGF4WXN5KzY4S0lt
|
||||||
TE5VRi9VR3hpWHRjNERJYUJkWjJSc01BOXdFCnlGeWY0bThzWkVjM2ZUbEtzRStp
|
ZW1PMWE5NUF6NEIyWmp6RWY4MzNYbE0wQnpFCkdOaGtIWmtJT08zNmZNNzcvQldp
|
||||||
Uk1paVEzc0hjQ0dXSmxuV0pwazAzdjQKLS0tIExTWFlWVEZsL29sTlE2cWtpS1c2
|
bDFzdUcrRjcvaWdhWENGTlFPSUdWTkEKLS0tIENJeWRKenY1SVBpYXNwck5TSUwr
|
||||||
cmZTdjhPNERqdlVCY1dIdGpxMDQxQkEKhw0y9SMDVFuCfQqGh44ZCepFuLPBrn0r
|
VFJrQWNnNTJ4azVpMElHbmsrZnJPM2MKx/7XxkZfd1tPMck9FmoM6g28dp5JeXQ5
|
||||||
6BXPFJoExcoMiIEAAA+5s6Cwzg7cn2Q0ZDWYmQMu/Pb9YE5lXZAEhg==
|
OdiOLlKc2If1f6dLKkjDmmscMui6aLMQ8RJ8dLK7FKlYy+95VsHVrw==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1dccte7xtwswgef089nd80dutp96xnezx5lrqnneh9cusegsnda8sj3dj6c
|
recipient: age1mar507c9mxmwalg486chs5kfh0mya38rv5w64ypfwnwlawewrpnswerpg8
|
||||||
enc: |
|
- enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4NUxJNGpOeS9oQ0dobDlt
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnZFpKSVpWMCt6Z3M5UGUz
|
||||||
c28zQmJSRDgyK3kzU2dqUFoydzFDSThBSmdBClZ0UUEvQkpkWllHQ0IwcDRreHdX
|
YVI4TG85UVh1eXYwVStQWk5xdEhhSjdNTzFJCk1mNnBtSVV0VFB0TnFIeVdocWFP
|
||||||
ZmdqTFRrZnRxSUV3TDNvdmhhUnl3WGcKLS0tIEpFQXFaMjBkZHAwSlo3eTNhRjBw
|
VzVKekdjUWYrbUVsRDJ3ZmdhaGhERDQKLS0tIE9rR3hpZmM2TWJDeEZxczlyRlFj
|
||||||
SVVQV1lKRjFUZE5oNktUZVFnL3RTcW8KoMSLJTM7BFrpjAYSYgblowWBpz9hDL3B
|
MzNuSHlWWnNMYkJMMHZkcnh0cXZaREkKE+j0yWV/zK8lz8vRa0cywpLL2DiAFsgi
|
||||||
et4snoEatB4x4p09Gm1IjpiCPp7RjRNETR3IaicpttNXdoKDjaj5pA==
|
fgCdeysSacrQLxB8iBWbusJ31ktyJMYLrsWFAdPkl0WN6HjaR3k3CQ==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1jf7yuycuajc5m8vupgrndjvw8knekr2v9979j68xc5ykvcxag4lss454au
|
recipient: age1w6avj7gd4f5frk90lsyh4e2k5am6z92hzlr0vpgrm767muyj59qsnuah62
|
||||||
enc: |
|
- enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBEZHpTR3JRbTdwN3ljS0NN
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBDUFYyTzdpQlJ4VVJJbDF5
|
||||||
NlJqUVpsZGJ1b2cvZ3VwRTFROENHQlZVb0dBCnlZMHlTTjdoREQrWTIyTUJOTDk4
|
cEhIQ1NSRVcwRUFBUzdaeXdkOWhPVjFIdGtBCk5qVmx1L09kVGhiNnlaQ0xoeTVz
|
||||||
dWFQeW9Wd3Y0RHA4b0JoOHkxeUVIcGsKLS0tIGdVZnc0Mk00QVFYRlVmeEtleFZy
|
SEpTSjRLU2FkeGl5Q0hETStoR093VFEKLS0tIGIwblBVd2hKTGQyOENOMDR4Y1dP
|
||||||
ZjVDNHNKajArSy9CYitnUXJFdkxMRTAKO0e842Rsq1w6pCF7mnhOXnAy0BHyRsIw
|
VmFINkwvZEFuWnkyb2JpTCtmRUVBbUkKKzbifH6Ue84MkpaLHrwDvJu8uvjY7yOY
|
||||||
JUNuMKYS7gsimPgmxt9pXsRuOvb+fgXWoh/+72wN9XgsnVf8bZqk/g==
|
+qYg0rOqFuZAx9YiOjDR7JVeGpfHM+7pO9ZjSNTPH0f1NC3XwsNp1A==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
lastmodified: "2026-01-19T18:14:58Z"
|
recipient: age1avlhszrryt4gf4ya536jhzm7qwt9xfttm8x4sns6h9w2tahzqp8sspz9y5
|
||||||
mac: ENC[AES256_GCM,data:mui6kiEDHvk8+Nh2/DToiGbAEu3rdWADkWDNI3Cy4eSrys7pFaeUx7o7RYHWOToFGAHLRVEGiZPg75mVmdcOtajvYytAMA/XE0TyVJ/dRkOk/R4Lbc0FQrKuExCla+TFuTAJSkvKHjeVu3EqFXuO3jHbCViEj+iRVrhv+UO+ZIM=,iv:E/Qkk1XC1oh9z7sx3ejfmO3gKc23XIANSwcjFxPyq+k=,tag:fO4Gp2uN9DVGh1BfCndraw==,type:str]
|
- enc: |
|
||||||
pgp: []
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBBSS9CQlE0azMwbFo4V0Ez
|
||||||
|
V3pKL2QvY0NNdmoxZXhQRUU1cG9Temk5M3pZCjBZUHJFRmZ1bTZzQ2RGU2F3WXF5
|
||||||
|
UGpPMGc5SmdiamlOUmpuZ3QrTkhhd3MKLS0tIDlaOUNhcmE0eUFaYTNIWWFoRXFx
|
||||||
|
Zm1ReDlMWW8wak5Jb0VncFV2bFJROWsKWi9DTgveMgGG8eK4qNeAGGG/gfiJS96G
|
||||||
|
232Tgf94Pb8eAU2zbF77pLWMaqTBbYPz1tggcMTfrAeDohq+/0sU8g==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
recipient: age1dccte7xtwswgef089nd80dutp96xnezx5lrqnneh9cusegsnda8sj3dj6c
|
||||||
|
- enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqS0p2TVhWd2VMQTNNR0or
|
||||||
|
amhNdTh5bmY3WWh1QkgveWZEMEtxSCt3Tng4CnVEKzhDMFVzRktndmI5OFhSei94
|
||||||
|
dysxVnBUSXdnVDJOdHV1eFZDVzdXVlEKLS0tIDIwMnhyaUZPZWI2NnRmellnZjZI
|
||||||
|
TC91cmtZWm03dzYwS2E3dkorNkdFY2sKj5OZHOtKx1NGPSGKsWjC/8+seUAhvmxb
|
||||||
|
wQ0iuPAq6yDLhYV69n7Jx4G9fKoidLIQxq+Ia+tLcYt58UDX7aixJQ==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
recipient: age1ped3hpugq06908ex8kgama33qckqe03rmac5pa6th87vks5d249qhshvqu
|
||||||
|
lastmodified: "2026-06-16T18:49:15Z"
|
||||||
|
mac: ENC[AES256_GCM,data:Q51p1A317BYzKvXSpkx2HBduGLGvxdQFi/BCfUtKWV9uAJGlQDp//eGJ7kJEG6DIO6sWUddf75fBLYAQcqm3iogIetTLUuQl3OhHSpAPvGpUDC6Hh87sAZy/ebaN2cy7BhJy5cjPJ9JAkfHqLCFRP+cVIwS/eb87GzwvWdSGZbc=,iv:yTTwhHCB09Qb6a437VENFiWQPp8CHwd0TelFj4ugO3U=,tag:JXN58pSy2I04O44Hg4pQcw==,type:str]
|
||||||
unencrypted_suffix: _unencrypted
|
unencrypted_suffix: _unencrypted
|
||||||
version: 3.11.0
|
version: 3.13.1
|
||||||
|
|||||||
83
secrets/common/llama-swap.yaml
Normal file
83
secrets/common/llama-swap.yaml
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
#ENC[AES256_GCM,data:GdmmcWLHlE3LJvl9VfzbuEgZyGGqlKcrtNa+78/FFKO5coPf0n27eKwfo6UGuhf3ln++ePv37Eg=,iv:M+DWl7AZeQXJ0z4l6LHJBYrI/jW5NFY6b2tW9QnL9jM=,tag:fdy4feWIvKPCHbAcNZ6mmQ==,type:comment]
|
||||||
|
llama_swap_api_keys:
|
||||||
|
pi: ENC[AES256_GCM,data:7Cw7RPQemcf5/zO7uazjA+dzpQu2MQo/Nbe3K3/CJ+OeQR90SJx4Z0TZudFugZoIHWR+sPEGQxUk8ne5xcfY6GSHJA==,iv:B5fX93BtSNwIDUdWTXr3ZhBQ4AuUqDHjeeVbkcCk7HI=,tag:6RMyFEF5872waHzxUCUh0Q==,type:str]
|
||||||
|
aethera: ENC[AES256_GCM,data:IcVya8MVZ/tzFchSWp8mkaLJfUqMgDsWQL4gZsZZmppXZ0+xOTRf5vjMc3sGNuRvOixU5nLaeTaESqoWfoq5gDNMfQ==,iv:WYFbAaNiSrHxJ4e8PU1hEyKGYKgWdFg72CSAQJmXaHw=,tag:X45Y7h0ME1e7Yryl6ES9SQ==,type:str]
|
||||||
|
evan: ENC[AES256_GCM,data:QKoFxv0gnDd1TZn9a+hFxu/J,iv:rje8Pk4ko8kjt1za/LOiLkoid4mmR5NtHCk0QX6rakg=,tag:MYTTE3KfWvfv2i98rTZUhQ==,type:str]
|
||||||
|
sops:
|
||||||
|
age:
|
||||||
|
- recipient: age1sac93wpnjcv62s7583jv6a4yspndh6k0r25g3qx3k7gq748uvafst6nz4w
|
||||||
|
enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBmZm9hY3BOa1Fmd1A0cGlz
|
||||||
|
Z0JWQ0Z4UnZGdUN0Q0JYLzBzczdsNi95d2tZCndYM2pLdENZTW53YmZUMy9GMUx3
|
||||||
|
TzIraGY0NTM0bmtUWWd3MXhPWGNDNVUKLS0tIFd4SlMrdWxLeDd6K2ZVM2hPai9q
|
||||||
|
djVIQldUaU96Vk9xUWptR3RxV3o4dEUKY4ti95K2052gWXpTJo4zw82pYSPhY53D
|
||||||
|
tKeeTBrfBs1PQ8bAYJ63exWam5HOz+fp0qu5HqO3reNairkgcH3Mhg==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
- recipient: age1mxjrvjxkn69kfn2np3wpd73g44fuhsgykw7l5ss9rx30em5jfp2scnrq32
|
||||||
|
enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1b0JTTUtoTDJRdGNHaHJj
|
||||||
|
SUNieDhhRUM3VkQzelV1ZDZTSVZqNy9oVjFnCjllWWFhcVprZ3BzamFEMmZXNzVo
|
||||||
|
NzdrVC9lcXpkYWpuR2lWdmtHQmx2UzgKLS0tIEtGTGI4M2RNNFRSSFRyaWRNNDhS
|
||||||
|
K2lsb3ZrYjB4RzRLTWpEVVpsQkoyQ0EK2nxzVyMoPI7P269oUVPUQfFbZIPPwjJy
|
||||||
|
IP9s6BvX8dkEssk2QDhqAKhugEHQ9czzutqcg9ZzepyrfIZde6bbYA==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
- recipient: age17ayje4uv2mhwehhp9jr3u9l0ds07396kt7ef40sufx89vm7cgfjq6d5d4y
|
||||||
|
enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBTRVdtRnBXbFdKemtZclZ2
|
||||||
|
R3RDc2NOeDFLU0NuT2pRM1pDaDFGbDd0d2p3CkZkL0lYdlhhWE5iS2hNN2hoelc1
|
||||||
|
NXN4U1M5azk1ZHA4cEtNUnA5ZWhNaEEKLS0tIGdobWlDTnY4SzIwZDNWSTc0UjR4
|
||||||
|
NEJrT3JScC9XV2RoRW8xTEhwR01RaDgKuGGUk0GTJiBH/9r9EImZqhVoc8czS+qb
|
||||||
|
mdUQTzHt9c19R1a6BU99owQqPUShuoZh+4TR9ntEejODmo/wLDQUsw==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
- recipient: age1mar507c9mxmwalg486chs5kfh0mya38rv5w64ypfwnwlawewrpnswerpg8
|
||||||
|
enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFMHRrekI4ZWZvS0ZkOVFx
|
||||||
|
OHl1aFhNWTQ2US8waVRET3NjMXpWQXJtRGprCldyMXdNMnF0S1RaV2FoL1M3cVpz
|
||||||
|
bmVNaWNSRHJKa0xrUTJJdndtUTB6YTgKLS0tIHFDWEd0VGxzT1I3cnhJV2VHaDlN
|
||||||
|
M050QzkyQVIwa2sxc0h1TktxMDdlY28KWfobSicg0Z85yN7JZfKPiysHL8UvVucj
|
||||||
|
M27JLgbuvRw+tRKtuPdurfiSXQnHBXl1KKp7/HFeRWYYIf1CbjleXg==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
- recipient: age1w6avj7gd4f5frk90lsyh4e2k5am6z92hzlr0vpgrm767muyj59qsnuah62
|
||||||
|
enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtMjRzS3ZQWkJEZXkrWmJ6
|
||||||
|
Zi84OWhFYnVuQWJLaFN1OS9uUW9GVTdtTjBvClVpZkYvc0tKTmxjbnR0eXpzeFZF
|
||||||
|
UDNnU3ZLQ0R1Nm4wS3ZoQm1pUCtuelkKLS0tIGlpbEtLNFpPU21SWWhhWEpxMUJO
|
||||||
|
elV3Z1RuM2RNemtEN1dTejNZWVZWTDAKP8DLC1+yoIWeVFa/m9ZE9VWi5kfU6NS9
|
||||||
|
918BrKOp3+Fzxf2/L+u3txmyDDKjAkax2ngNtcSCrF9FcW4oQMjOMQ==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
- recipient: age1avlhszrryt4gf4ya536jhzm7qwt9xfttm8x4sns6h9w2tahzqp8sspz9y5
|
||||||
|
enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBPcXpOb05mTWhHYUJQSXB6
|
||||||
|
Zzh6QWNyRUgvcVhtNU4zclBnVHo2Y3czZFFRCklkWmlWaXRWZHhvME5XcTBDTTVM
|
||||||
|
clQxZW0xWFl0UFlvOEtSdjVLc0VzYlUKLS0tIEpIQU9uRkJ6MHdSWU0vc0V5R3Vj
|
||||||
|
bDE3SS91Mk5LdHd4QnhQS0xXS0c3MWMKQJ+dKG5TFo/K0Ds2Tuf4xpKUmJS+bri1
|
||||||
|
HnXCB5MTyLO8OrTsT8eiiBsNWjOyny7f8SHI2gGpQpvOjsh9IsmzjA==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
- recipient: age1dccte7xtwswgef089nd80dutp96xnezx5lrqnneh9cusegsnda8sj3dj6c
|
||||||
|
enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB0UkdSRnFnRjlTOGFGc05S
|
||||||
|
SWNXQnNHL1VUSE8xS0RFN1FLcEpOcnl0VHdVCkpOTFZVYW5Wb3RLc2NMWnZLSkRI
|
||||||
|
QTRWQ2l1WUt0SXZWWVdzN2IxSEtzNEUKLS0tIHhZc056Z3pCL2lvQmQ0dWxZV21Q
|
||||||
|
RnR2UVVCSkMvRllzcVU4bmpndmpWNncKd6tk1HLfofPc714enTIiLGgQyQoEgnUH
|
||||||
|
PQSx9ipnMAnl4N0lVogn6P79xs37q3+DRPLoZi0XUeSpQnqLgBdQ7A==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
- recipient: age1ped3hpugq06908ex8kgama33qckqe03rmac5pa6th87vks5d249qhshvqu
|
||||||
|
enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBMZm5hUkJBKzNTK3pydHRM
|
||||||
|
eTE5YVY5SjluUEh0U2o1amFoNG0yQkpld0Q4CjBxNllmL3BublZUdzF0bWdQbmxn
|
||||||
|
ZiszY3l0L2RMb1gvbTFWU2V6MHE1SVkKLS0tIENBdmRxc24wMFF6akNRQjRBSXUw
|
||||||
|
SmpMYnNBTWVYTENWSUQvWXMrZXVqbncK6KtP4pOEBDM8gK26uYp3a/WRP4TrkyWV
|
||||||
|
4ugL2Y7sGkVrWz0Cvr3Jp9QDuPh3xs4jZyEvB8RbxQDMFJzdOEBv2A==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
lastmodified: "2026-05-02T19:41:27Z"
|
||||||
|
mac: ENC[AES256_GCM,data:uL+15e31xhsZ3p1h3HqkWxjnEbwI3NuV9g+Apt/lw/1Q9IB29ViNl9H11qAptcJNz9gjTi9k2J9ITLgOqUkP2+3Saz1fK+QLJ11W91cQxAeLVSWdDwpJKrTL6tbWwH1+Ri/p5odyC1tqwwUX3AHy7WMYNq3f20+SJKbCBNc3k3I=,iv:H74r14tlmpJZzaFRGIkoM9UVMSDnyWgrhPpDAw5/rIQ=,tag:O67E2OZ2+HLrlEkvfTrQyA==,type:str]
|
||||||
|
unencrypted_suffix: _unencrypted
|
||||||
|
version: 3.12.1
|
||||||
@@ -5,29 +5,38 @@ sops:
|
|||||||
- recipient: age1sac93wpnjcv62s7583jv6a4yspndh6k0r25g3qx3k7gq748uvafst6nz4w
|
- recipient: age1sac93wpnjcv62s7583jv6a4yspndh6k0r25g3qx3k7gq748uvafst6nz4w
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtNzNQMlF1MGI2TkQxZFl1
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQeXRGUldvcVpFNEtHS05a
|
||||||
WGV2d3lVTVFRSmQwamx5eG9NRi92WWF4U0JrClF2SWdaNStwK1UraUF5Z2RpQ0dQ
|
QjhlZGhQZFlaSjRKSWFNRHNrQldNMTRWdUFNCm1pRE04SndFNG9iMkZWRlVIaXh2
|
||||||
TGs4angrM1lrWkZzVm9EU2xoV1hieWcKLS0tIFVHN3hlVFFnSElpcTJvUDRwdVlU
|
TXpYcldYUk9sVGlBcWxiWTJTdWtPS00KLS0tIHhSN2hCZjllajVialhXYktyRTIz
|
||||||
OVNDc0VpbDVmUmlwS3lHTlFBaGZ0UEkKMhxvuNH2lw2rn31G26u9ur8ShHRCZQHg
|
Q0hTbExTRTl3VjFSek5Ga0dGRGs0UUEKHICfAk9HISav51w5jkAZcQptpwpz9L4m
|
||||||
PXPPBxMmbuoU4t5g1ongWqERG85YgOAOMO3werVw0Iw49AtQQzGE8w==
|
4O+gPPW8vk+TSP9pzGfLoxj0n5qu8qYQ2071zzpMfbaYTZrI8RYY/Q==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1mxjrvjxkn69kfn2np3wpd73g44fuhsgykw7l5ss9rx30em5jfp2scnrq32
|
- recipient: age1mxjrvjxkn69kfn2np3wpd73g44fuhsgykw7l5ss9rx30em5jfp2scnrq32
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5YTZUMUNUeElqbmFKanc2
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBOWGh5RVVjeFRZSHFDSlpy
|
||||||
TklFbk8yTU93YUFTUW4vM3BwREgzditnSFMwCk1nNW1XWnBBWXBTb3k4enpwZlVQ
|
Z0UzWU14eWVsc1VMYW1yZVFZbEtFNVN5NENrCmM2TEhqUVcwS0x1bXR5NW5NQmJ0
|
||||||
bFVwNkNWOHZ3MTZUSjN4SWZYaDFzak0KLS0tIEtLYUhvNFVkOUp0QzVOei9XTm9C
|
RGp0eEtNelpiNjBDdDBrdXhCSXFabUkKLS0tIERiaUtLOFZaSjF6RUp3UCsvSXJP
|
||||||
ZVNmVktSNDYxdGFvRUpmYnlJbGFHQTgKf7ovzPU3Vo84gwGTKU/SNCy+76WY88ve
|
TDMyK0Y0ZXNUdEd1WjZYbnhNbEhzY2sKxuDAWIwRw+mUyPiQuUqTM9l6rkT0yWSA
|
||||||
ZPkJ29D8BeaEwFCbNcDOygwiKGSFYV31a+2zYnTP4j5pf01d2it2eQ==
|
cgHaWeWLgrLAmJ7vbjo24aDld1RVV8MG6ofAPBGpon7zZgmzFxSQyQ==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age13gymlygyac9z2slecl53jp8spq7e8n4zkan86n0gmnm3nrj4muxqa5ullm
|
- recipient: age13gymlygyac9z2slecl53jp8spq7e8n4zkan86n0gmnm3nrj4muxqa5ullm
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA2c2dEWVRDbzYxeWkyRmdo
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA4UDJJWGluaEJoRlhRbHpF
|
||||||
RzdBcm9UbXE5Q01zeHRxMisxajlDMnMvZW5vCm9pMEVlU3pEUGpoNmFlRlV4OXJ4
|
UUZhRVRmejFpREkyYllhV1IxTjJWYjM0bGowCmM2K2FKczNpSmU1SlgyWmJDa3Rh
|
||||||
QXg5ZTZSVkMzcTlFc2cvNzVQR2ZwelEKLS0tIDhhQmtGYTZjcEZwMXJoMjdMNVFt
|
MlpQUWZRTVhqcHF0b3VlK3YrWUo5dXMKLS0tIG5RTnhZTWlFOVAxV0IwcTJEYWxp
|
||||||
aHc4a3UvZUFRNzRtQTc2NTloWE0zdkUKL5FRH7D8MlR8ofvIieFqIStwEXQUvu2w
|
OThzTW82YXRYMGt4VjlLQkNSbTBweVkKyJhjsDc1hf/1StbOybX3ccNp8jPC9kU8
|
||||||
+/SHKsi3lt9/1Vkk/Jlm1aymglp3ZdGVzTS/cxpM43VDDx+E3HYOQQ==
|
InhF+++apu7vocTyuTxRfSB7BU3o8ZAog+qvoz+34PbVHSfxXufcuQ==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
- recipient: age1rfgh40p6wmaam4ttwgv9xfukgxjruskhfvm0q3ud6lvxtlqc2y8snhwjft
|
||||||
|
enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6aEF6QmY4OXBJeVVNSngy
|
||||||
|
VHlsMm8yalFkaktGYlpGYmd0Umk3RjF3TFhVCkNSOTIwYXRqRU5lMnNqek1zYUts
|
||||||
|
TEdEcXNZY3hwMUh4RThpYXhJWXhNWG8KLS0tIFpXL0RhQWVma3p1dG5MRDF1SGo5
|
||||||
|
bWkxUDhBL24xMFJDUnR4eXNVUXE5N3cKyHtp6HAOh/qc4vkWhGiT+9kLlN6YROtE
|
||||||
|
y2RZTVC840ABTA2XsCWpSiBviT6StJGhK4vl6vNzF5gbO9mVB9pbDg==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
lastmodified: "2026-01-12T02:56:42Z"
|
lastmodified: "2026-01-12T02:56:42Z"
|
||||||
mac: ENC[AES256_GCM,data:R6s3ErVrw2nvRhkCdiaa6FCmIxBKZGQggQX5bYe1xmhIXuujsl9NZ9aqlzlS1XvVDICJEIbryfoEnOqSCrY/vAmdlKNfzakZqLZRrkfOZed6PWFWjk3SX6HmuMR9dQSQgLRlDZINZcKMNE0kuLL+mx4bo8lV84VoqMHGHtkwAJI=,iv:NCh3zDMEiYcrYxPxP5lfGWYwWLl1/yylq7+gTEHyWF4=,tag:t7MOwGHejUFotIBi7kfecw==,type:str]
|
mac: ENC[AES256_GCM,data:R6s3ErVrw2nvRhkCdiaa6FCmIxBKZGQggQX5bYe1xmhIXuujsl9NZ9aqlzlS1XvVDICJEIbryfoEnOqSCrY/vAmdlKNfzakZqLZRrkfOZed6PWFWjk3SX6HmuMR9dQSQgLRlDZINZcKMNE0kuLL+mx4bo8lV84VoqMHGHtkwAJI=,iv:NCh3zDMEiYcrYxPxP5lfGWYwWLl1/yylq7+gTEHyWF4=,tag:t7MOwGHejUFotIBi7kfecw==,type:str]
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
let
|
let
|
||||||
sync-repo = pkgs.writeShellScriptBin "sync-repo" ''
|
sync-repo = pkgs.writeShellScriptBin "sync-repo" ''
|
||||||
|
# Navigate to repo root so rsync copies the entire repository
|
||||||
|
cd "$(git rev-parse --show-toplevel)"
|
||||||
|
|
||||||
if [ -z "$1" ]; then
|
if [ -z "$1" ]; then
|
||||||
echo "Usage: sync-repo <ip-address>"
|
echo "Usage: sync-repo <ip-address>"
|
||||||
echo "Example: sync-repo 23.29.118.42"
|
echo "Example: sync-repo 23.29.118.42"
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ in
|
|||||||
(modulesPath + "/profiles/qemu-guest.nix")
|
(modulesPath + "/profiles/qemu-guest.nix")
|
||||||
];
|
];
|
||||||
|
|
||||||
system.stateVersion = "25.11";
|
system.stateVersion = "26.05";
|
||||||
time.timeZone = "UTC";
|
time.timeZone = "UTC";
|
||||||
|
|
||||||
boot.loader.grub = {
|
boot.loader.grub = {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ in
|
|||||||
(modulesPath + "/profiles/qemu-guest.nix")
|
(modulesPath + "/profiles/qemu-guest.nix")
|
||||||
];
|
];
|
||||||
|
|
||||||
system.stateVersion = "25.11";
|
system.stateVersion = "26.05";
|
||||||
time.timeZone = "UTC";
|
time.timeZone = "UTC";
|
||||||
|
|
||||||
boot.loader.grub = {
|
boot.loader.grub = {
|
||||||
|
|||||||
@@ -11,14 +11,24 @@ in
|
|||||||
./hardware-configuration.nix
|
./hardware-configuration.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
system.stateVersion = "25.11";
|
system.stateVersion = "26.05";
|
||||||
time.timeZone = "America/New_York";
|
time.timeZone = "America/New_York";
|
||||||
|
|
||||||
programs.firejail.enable = true;
|
programs.firejail.enable = true;
|
||||||
|
programs.nix-ld.enable = true;
|
||||||
|
|
||||||
|
# Asahi Wi-Fi Resume Bug - The Broadcom driver can fail to reconnect after suspend on this MacBook.
|
||||||
|
powerManagement.resumeCommands = ''
|
||||||
|
${pkgs.kmod}/bin/modprobe -r brcmfmac_wcc 2>/dev/null || true
|
||||||
|
${pkgs.kmod}/bin/modprobe -r brcmfmac 2>/dev/null || true
|
||||||
|
${pkgs.kmod}/bin/modprobe brcmfmac
|
||||||
|
${pkgs.systemd}/bin/systemctl restart NetworkManager.service
|
||||||
|
'';
|
||||||
|
|
||||||
# System Config
|
# System Config
|
||||||
reichard = {
|
reichard = {
|
||||||
nix = enabled;
|
nix = enabled;
|
||||||
|
user.extraGroups = [ "dialout" ];
|
||||||
|
|
||||||
system = {
|
system = {
|
||||||
boot = {
|
boot = {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ in
|
|||||||
(modulesPath + "/profiles/qemu-guest.nix")
|
(modulesPath + "/profiles/qemu-guest.nix")
|
||||||
];
|
];
|
||||||
|
|
||||||
system.stateVersion = "25.11";
|
system.stateVersion = "26.05";
|
||||||
time.timeZone = "America/New_York";
|
time.timeZone = "America/New_York";
|
||||||
|
|
||||||
programs.nix-ld.enable = true;
|
programs.nix-ld.enable = true;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ let
|
|||||||
inherit (lib.${namespace}) enabled;
|
inherit (lib.${namespace}) enabled;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
system.stateVersion = "25.11";
|
system.stateVersion = "26.05";
|
||||||
time.timeZone = "America/New_York";
|
time.timeZone = "America/New_York";
|
||||||
|
|
||||||
# Config Boot
|
# Config Boot
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ in
|
|||||||
(modulesPath + "/profiles/qemu-guest.nix")
|
(modulesPath + "/profiles/qemu-guest.nix")
|
||||||
];
|
];
|
||||||
|
|
||||||
system.stateVersion = "25.11";
|
system.stateVersion = "26.05";
|
||||||
time.timeZone = "UTC";
|
time.timeZone = "UTC";
|
||||||
|
|
||||||
boot.loader.grub = {
|
boot.loader.grub = {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ in
|
|||||||
(modulesPath + "/profiles/qemu-guest.nix")
|
(modulesPath + "/profiles/qemu-guest.nix")
|
||||||
];
|
];
|
||||||
|
|
||||||
system.stateVersion = "25.11";
|
system.stateVersion = "26.05";
|
||||||
time.timeZone = "UTC";
|
time.timeZone = "UTC";
|
||||||
|
|
||||||
networking.firewall.allowedTCPPorts = [ 443 ];
|
networking.firewall.allowedTCPPorts = [ 443 ];
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ in
|
|||||||
(modulesPath + "/profiles/qemu-guest.nix")
|
(modulesPath + "/profiles/qemu-guest.nix")
|
||||||
];
|
];
|
||||||
|
|
||||||
system.stateVersion = "25.11";
|
system.stateVersion = "26.05";
|
||||||
time.timeZone = "UTC";
|
time.timeZone = "UTC";
|
||||||
|
|
||||||
networking.firewall.allowedTCPPorts = [ 443 ];
|
networking.firewall.allowedTCPPorts = [ 443 ];
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{ namespace
|
{ namespace
|
||||||
|
, config
|
||||||
, pkgs
|
, pkgs
|
||||||
, lib
|
, lib
|
||||||
, ...
|
, ...
|
||||||
@@ -6,13 +7,15 @@
|
|||||||
let
|
let
|
||||||
inherit (lib.${namespace}) enabled;
|
inherit (lib.${namespace}) enabled;
|
||||||
|
|
||||||
|
nvidia-smi = "${config.hardware.nvidia.package.bin}/bin/nvidia-smi";
|
||||||
llama-cpp = pkgs.reichard.llama-cpp;
|
llama-cpp = pkgs.reichard.llama-cpp;
|
||||||
|
ik-llama-cpp = pkgs.reichard.ik-llama-cpp;
|
||||||
stable-diffusion-cpp = pkgs.reichard.stable-diffusion-cpp.override {
|
stable-diffusion-cpp = pkgs.reichard.stable-diffusion-cpp.override {
|
||||||
cudaSupport = true;
|
cudaSupport = true;
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
system.stateVersion = "25.11";
|
system.stateVersion = "26.05";
|
||||||
time.timeZone = "America/New_York";
|
time.timeZone = "America/New_York";
|
||||||
nixpkgs.config.allowUnfree = true;
|
nixpkgs.config.allowUnfree = true;
|
||||||
hardware.nvidia-container-toolkit.enable = true;
|
hardware.nvidia-container-toolkit.enable = true;
|
||||||
@@ -46,6 +49,27 @@ in
|
|||||||
allowedTCPPorts = [ 8081 ];
|
allowedTCPPorts = [ 8081 ];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# NVIDIA GPU Power Limit
|
||||||
|
systemd.services = {
|
||||||
|
nvidia-persistence-mode = {
|
||||||
|
description = "Enable NVIDIA GPU Persistence Mode";
|
||||||
|
after = [ "nvidia-modules-load.service" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
serviceConfig.Type = "oneshot";
|
||||||
|
serviceConfig.RemainAfterExit = true;
|
||||||
|
script = "${nvidia-smi} -pm 1";
|
||||||
|
};
|
||||||
|
|
||||||
|
nvidia-power-limit = {
|
||||||
|
description = "Set NVIDIA GPU Power Limit";
|
||||||
|
after = [ "nvidia-persistence-mode.service" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
serviceConfig.Type = "oneshot";
|
||||||
|
serviceConfig.RemainAfterExit = true;
|
||||||
|
script = "${nvidia-smi} -i 0 -pl 290";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
# System Config
|
# System Config
|
||||||
reichard = {
|
reichard = {
|
||||||
nix = enabled;
|
nix = enabled;
|
||||||
@@ -76,6 +100,8 @@ in
|
|||||||
opengl = {
|
opengl = {
|
||||||
enable = true;
|
enable = true;
|
||||||
enableNvidia = true;
|
enableNvidia = true;
|
||||||
|
# GTX 1080 Ti is Pascal; NVIDIA 590+ (nixpkgs stable = 595) dropped Pascal support.
|
||||||
|
nvidiaPackage = config.boot.kernelPackages.nvidiaPackages.legacy_580;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -106,6 +132,7 @@ in
|
|||||||
|
|
||||||
# Local Packages
|
# Local Packages
|
||||||
llama-cpp
|
llama-cpp
|
||||||
|
ik-llama-cpp
|
||||||
stable-diffusion-cpp
|
stable-diffusion-cpp
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
}:
|
}:
|
||||||
{
|
{
|
||||||
time.timeZone = "America/New_York";
|
time.timeZone = "America/New_York";
|
||||||
system.stateVersion = "25.11";
|
system.stateVersion = "26.05";
|
||||||
|
|
||||||
reichard = {
|
reichard = {
|
||||||
system = {
|
system = {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ let
|
|||||||
cfg = config.${namespace}.user;
|
cfg = config.${namespace}.user;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
system.stateVersion = "25.11";
|
system.stateVersion = "26.05";
|
||||||
time.timeZone = "America/New_York";
|
time.timeZone = "America/New_York";
|
||||||
|
|
||||||
nixpkgs.config.allowUnfree = true;
|
nixpkgs.config.allowUnfree = true;
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ let
|
|||||||
inherit (lib.${namespace}) enabled;
|
inherit (lib.${namespace}) enabled;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
system.stateVersion = "25.11";
|
system.stateVersion = "26.05";
|
||||||
time.timeZone = "America/New_York";
|
time.timeZone = "America/New_York";
|
||||||
|
|
||||||
|
programs.nix-ld.enable = true;
|
||||||
|
|
||||||
reichard = {
|
reichard = {
|
||||||
nix = enabled;
|
nix = enabled;
|
||||||
|
|
||||||
|
|||||||
@@ -7,15 +7,10 @@ let
|
|||||||
inherit (lib.${namespace}) enabled;
|
inherit (lib.${namespace}) enabled;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
system.stateVersion = "25.11";
|
system.stateVersion = "26.05";
|
||||||
time.timeZone = "America/New_York";
|
time.timeZone = "America/New_York";
|
||||||
|
|
||||||
boot = {
|
programs.nix-ld.enable = true;
|
||||||
kernelParams = [
|
|
||||||
# Mask GPE03 (EC wakeup events) to allow hibernation without spurious CPU wakeups
|
|
||||||
"acpi_mask_gpe=0x03"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
hardware = {
|
hardware = {
|
||||||
enableRedistributableFirmware = true;
|
enableRedistributableFirmware = true;
|
||||||
@@ -57,6 +52,7 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
services = {
|
services = {
|
||||||
|
printing = enabled;
|
||||||
openssh = enabled;
|
openssh = enabled;
|
||||||
tailscale = enabled;
|
tailscale = enabled;
|
||||||
avahi = enabled;
|
avahi = enabled;
|
||||||
@@ -82,14 +78,4 @@ in
|
|||||||
sops = enabled;
|
sops = enabled;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# Additional System Packages
|
|
||||||
environment.systemPackages = with pkgs; [
|
|
||||||
dool
|
|
||||||
jq
|
|
||||||
mosh
|
|
||||||
rclone
|
|
||||||
sqlite-interactive
|
|
||||||
unzip
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ in
|
|||||||
|
|
||||||
config = {
|
config = {
|
||||||
# Basic System
|
# Basic System
|
||||||
system.stateVersion = "25.11";
|
system.stateVersion = "26.05";
|
||||||
time.timeZone = "UTC";
|
time.timeZone = "UTC";
|
||||||
|
|
||||||
reichard = {
|
reichard = {
|
||||||
|
|||||||
Reference in New Issue
Block a user