Merge branch 'polecat/rust/hq-0h1p9m@ml2ugjq1': fix gastown statusline patch
All checks were successful
CI / check (push) Successful in 5m19s
All checks were successful
CI / check (push) Successful in 5m19s
Regenerated patch with correct hunk headers against locked rev 177094a2. Root cause was malformed patch format, not a flake.lock issue. Closes: hq-0h1p9m, x-bwld
This commit is contained in:
@@ -6,7 +6,7 @@
|
|||||||
# Issue prefix for this repository (used by bd init)
|
# Issue prefix for this repository (used by bd init)
|
||||||
# If not set, bd init will auto-detect from directory name
|
# If not set, bd init will auto-detect from directory name
|
||||||
# Example: issue-prefix: "myproject" creates issues like "myproject-1", "myproject-2", etc.
|
# Example: issue-prefix: "myproject" creates issues like "myproject-1", "myproject-2", etc.
|
||||||
# issue-prefix: ""
|
issue-prefix: "x"
|
||||||
|
|
||||||
# Use no-db mode: load from JSONL, no SQLite, write back after each command
|
# Use no-db mode: load from JSONL, no SQLite, write back after each command
|
||||||
# When true, bd will use .beads/issues.jsonl as the source of truth
|
# When true, bd will use .beads/issues.jsonl as the source of truth
|
||||||
|
|||||||
115
flake.lock
generated
115
flake.lock
generated
@@ -8,11 +8,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1769020852,
|
"lastModified": 1769405733,
|
||||||
"narHash": "sha256-MR6evuoa8w6mjYTesTAa3bsRH+c3IB7EOEDTCjsiAp8=",
|
"narHash": "sha256-WpROnW0dRi5ub0SlpKrMBs3pYlSBY4xw22hnTNvBMgI=",
|
||||||
"owner": "steveyegge",
|
"owner": "steveyegge",
|
||||||
"repo": "beads",
|
"repo": "beads",
|
||||||
"rev": "cb46db603d34c0190605eecb8724a6c581119f09",
|
"rev": "6e82d1e2eea121ce5dc0964d554879f8b0c08563",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -24,11 +24,11 @@
|
|||||||
"doomemacs": {
|
"doomemacs": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1767773143,
|
"lastModified": 1768984347,
|
||||||
"narHash": "sha256-QL/t9v2kFNxBDyNJb/s411o3mxujan+QX5IZglTdpTk=",
|
"narHash": "sha256-VvC4rgAAaFnYLCdcUoz7dTE3kuBNuHIc+GlXOrPCxpg=",
|
||||||
"owner": "doomemacs",
|
"owner": "doomemacs",
|
||||||
"repo": "doomemacs",
|
"repo": "doomemacs",
|
||||||
"rev": "3e15fb36d7f94f0a218bda977be4d3f5da983a71",
|
"rev": "57818a6da90fbef39ff80d62fab2cd319496c3b9",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -47,11 +47,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1768011937,
|
"lastModified": 1769329593,
|
||||||
"narHash": "sha256-SnU2XTo34vwVaijs+4VwcXTNwMWO4nwzzs08N39UagA=",
|
"narHash": "sha256-u5PSA+8TUYF/13ziBcnoE67nkDwpjAdecKh3srcJJm0=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "emacs-overlay",
|
"repo": "emacs-overlay",
|
||||||
"rev": "79abf71d9897cf3b5189f7175cda1b1102abc65c",
|
"rev": "776dc33d735af583a14cc56b406ea658398964a7",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -81,17 +81,17 @@
|
|||||||
"gastown": {
|
"gastown": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1769031452,
|
"lastModified": 1769538736,
|
||||||
"narHash": "sha256-tTvtLvTr38okqbpNnr5exfurI6VkVKNLcnM+A6O7DGY=",
|
"narHash": "sha256-A33gyS/ERUCFcaFG9PJdIHfIOafguqkRe+DuIZteH5s=",
|
||||||
"ref": "refs/heads/main",
|
"owner": "steveyegge",
|
||||||
"rev": "93e22595cd59802a24253b100dcfae98a6849428",
|
"repo": "gastown",
|
||||||
"revCount": 2938,
|
"rev": "177094a2335786d1d450fd9e14b935877291c004",
|
||||||
"type": "git",
|
"type": "github"
|
||||||
"url": "ssh://git@git.johnogle.info:2222/johno/gastown.git"
|
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"type": "git",
|
"owner": "steveyegge",
|
||||||
"url": "ssh://git@git.johnogle.info:2222/johno/gastown.git"
|
"repo": "gastown",
|
||||||
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"google-cookie-retrieval": {
|
"google-cookie-retrieval": {
|
||||||
@@ -101,11 +101,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1761423376,
|
"lastModified": 1768846578,
|
||||||
"narHash": "sha256-pMy3cnUFfue4vz/y0jx71BfcPGxZf+hk/DtnzWvfU0c=",
|
"narHash": "sha256-82f/+e8HAwmBukiLlr7I3HYvM/2GCd5SOc+BC+qzsOQ=",
|
||||||
"ref": "refs/heads/main",
|
"ref": "refs/heads/main",
|
||||||
"rev": "a1f695665771841a988afc965526cbf99160cd77",
|
"rev": "c11ff9d3c67372a843a0fa6bf23132e986bd6955",
|
||||||
"revCount": 11,
|
"revCount": 14,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.johnogle.info/johno/google-cookie-retrieval.git"
|
"url": "https://git.johnogle.info/johno/google-cookie-retrieval.git"
|
||||||
},
|
},
|
||||||
@@ -121,11 +121,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1767514898,
|
"lastModified": 1768949235,
|
||||||
"narHash": "sha256-ONYqnKrPzfKEEPChoJ9qPcfvBqW9ZgieDKD7UezWPg4=",
|
"narHash": "sha256-TtjKgXyg1lMfh374w5uxutd6Vx2P/hU81aEhTxrO2cg=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "7a06e8a2f844e128d3b210a000a62716b6040b7f",
|
"rev": "75ed713570ca17427119e7e204ab3590cc3bf2a5",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -142,11 +142,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1767556355,
|
"lastModified": 1769397130,
|
||||||
"narHash": "sha256-RDTUBDQBi9D4eD9iJQWtUDN/13MDLX+KmE+TwwNUp2s=",
|
"narHash": "sha256-TTM4KV9IHwa181X7afBRbhLJIrgynpDjAXJFMUOWfyU=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "f894bc4ffde179d178d8deb374fcf9855d1a82b7",
|
"rev": "c37679d37bdbecf11bbe3c5eb238d89ca4f60641",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -164,11 +164,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1767082077,
|
"lastModified": 1769273817,
|
||||||
"narHash": "sha256-2tL1mRb9uFJThUNfuDm/ehrnPvImL/QDtCxfn71IEz4=",
|
"narHash": "sha256-+iyLihi/ynJokMgJZMRXuMuI6DPGUQRajz5ztNCHgnI=",
|
||||||
"owner": "Jovian-Experiments",
|
"owner": "Jovian-Experiments",
|
||||||
"repo": "Jovian-NixOS",
|
"repo": "Jovian-NixOS",
|
||||||
"rev": "efd4b22e6fdc6d7fb4e186ae333a4b74e03da440",
|
"rev": "98f988ad46e31f9956c5f6874dfb3580a7ff3969",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -184,11 +184,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1765066094,
|
"lastModified": 1767634391,
|
||||||
"narHash": "sha256-0YSU35gfRFJzx/lTGgOt6ubP8K6LeW0vaywzNNqxkl4=",
|
"narHash": "sha256-owcSz2ICqTSvhBbhPP+1eWzi88e54rRZtfCNE5E/wwg=",
|
||||||
"owner": "nix-darwin",
|
"owner": "nix-darwin",
|
||||||
"repo": "nix-darwin",
|
"repo": "nix-darwin",
|
||||||
"rev": "688427b1aab9afb478ca07989dc754fa543e03d5",
|
"rev": "08585aacc3d6d6c280a02da195fdbd4b9cf083c2",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -206,11 +206,11 @@
|
|||||||
"systems": "systems_2"
|
"systems": "systems_2"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1768034604,
|
"lastModified": 1769330679,
|
||||||
"narHash": "sha256-62pIZMvGHhYJmMiiBsxHqZt/dFyENPcFHlJq5NJF3Sw=",
|
"narHash": "sha256-X7rw5ouiAYKmbbKLtkEc/Kqcg6DxKgOtgaftzuchy/M=",
|
||||||
"owner": "marienz",
|
"owner": "marienz",
|
||||||
"repo": "nix-doom-emacs-unstraightened",
|
"repo": "nix-doom-emacs-unstraightened",
|
||||||
"rev": "9b3b8044fe4ccdcbb2d6f733d7dbe4d5feea18bc",
|
"rev": "0c2d527055f448c8856129c6d063535e06aeff4d",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -243,11 +243,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1767480499,
|
"lastModified": 1769089682,
|
||||||
"narHash": "sha256-8IQQUorUGiSmFaPnLSo2+T+rjHtiNWc+OAzeHck7N48=",
|
"narHash": "sha256-9yA/LIuAVQq0lXelrZPjLuLVuZdm03p8tfmHhnDIkms=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "30a3c519afcf3f99e2c6df3b359aec5692054d92",
|
"rev": "078d69f03934859a181e81ba987c2bb033eebfc5",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -259,11 +259,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-unstable": {
|
"nixpkgs-unstable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1767379071,
|
"lastModified": 1769170682,
|
||||||
"narHash": "sha256-EgE0pxsrW9jp9YFMkHL9JMXxcqi/OoumPJYwf+Okucw=",
|
"narHash": "sha256-oMmN1lVQU0F0W2k6OI3bgdzp2YOHWYUAw79qzDSjenU=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "fb7944c166a3b630f177938e478f0378e64ce108",
|
"rev": "c5296fdd05cfa2c187990dd909864da9658df755",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -273,6 +273,22 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"perles": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1769460725,
|
||||||
|
"narHash": "sha256-zM2jw+emxe8+mNyR1ebMWkQiEx8uSmhoqqI0IxXLDgs=",
|
||||||
|
"owner": "zjrosen",
|
||||||
|
"repo": "perles",
|
||||||
|
"rev": "57b20413eea461452b59e13f5a4a367953b1f768",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "zjrosen",
|
||||||
|
"repo": "perles",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"plasma-manager": {
|
"plasma-manager": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"home-manager": [
|
"home-manager": [
|
||||||
@@ -283,11 +299,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1763909441,
|
"lastModified": 1767662275,
|
||||||
"narHash": "sha256-56LwV51TX/FhgX+5LCG6akQ5KrOWuKgcJa+eUsRMxsc=",
|
"narHash": "sha256-d5Q1GmQ+sW1Bt8cgDE0vOihzLaswsm8cSdg8124EqXE=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "plasma-manager",
|
"repo": "plasma-manager",
|
||||||
"rev": "b24ed4b272256dfc1cc2291f89a9821d5f9e14b4",
|
"rev": "51816be33a1ff0d4b22427de83222d5bfa96d30e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -306,11 +322,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1763909441,
|
"lastModified": 1767662275,
|
||||||
"narHash": "sha256-56LwV51TX/FhgX+5LCG6akQ5KrOWuKgcJa+eUsRMxsc=",
|
"narHash": "sha256-d5Q1GmQ+sW1Bt8cgDE0vOihzLaswsm8cSdg8124EqXE=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "plasma-manager",
|
"repo": "plasma-manager",
|
||||||
"rev": "b24ed4b272256dfc1cc2291f89a9821d5f9e14b4",
|
"rev": "51816be33a1ff0d4b22427de83222d5bfa96d30e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -331,6 +347,7 @@
|
|||||||
"nix-doom-emacs-unstraightened": "nix-doom-emacs-unstraightened",
|
"nix-doom-emacs-unstraightened": "nix-doom-emacs-unstraightened",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"nixpkgs-unstable": "nixpkgs-unstable",
|
"nixpkgs-unstable": "nixpkgs-unstable",
|
||||||
|
"perles": "perles",
|
||||||
"plasma-manager": "plasma-manager",
|
"plasma-manager": "plasma-manager",
|
||||||
"plasma-manager-unstable": "plasma-manager-unstable"
|
"plasma-manager-unstable": "plasma-manager-unstable"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,12 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
gastown = {
|
gastown = {
|
||||||
url = "git+ssh://git@git.johnogle.info:2222/johno/gastown.git";
|
url = "github:steveyegge/gastown";
|
||||||
|
flake = false; # No flake.nix upstream yet
|
||||||
|
};
|
||||||
|
|
||||||
|
perles = {
|
||||||
|
url = "github:zjrosen/perles";
|
||||||
flake = false; # No flake.nix upstream yet
|
flake = false; # No flake.nix upstream yet
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -210,7 +215,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
# Darwin/macOS configurations
|
# Darwin/macOS configurations
|
||||||
darwinConfigurations."blkfv4yf49kt7" = inputs.nix-darwin.lib.darwinSystem rec {
|
darwinConfigurations."BLKFV4YF49KT7" = inputs.nix-darwin.lib.darwinSystem rec {
|
||||||
system = "aarch64-darwin";
|
system = "aarch64-darwin";
|
||||||
modules = darwinModules ++ [
|
modules = darwinModules ++ [
|
||||||
./machines/johno-macbookpro/configuration.nix
|
./machines/johno-macbookpro/configuration.nix
|
||||||
|
|||||||
@@ -107,7 +107,7 @@
|
|||||||
aerospace = {
|
aerospace = {
|
||||||
enable = true;
|
enable = true;
|
||||||
leader = "cmd";
|
leader = "cmd";
|
||||||
ctrlShortcuts.enable = true;
|
ctrlShortcuts.enable = false;
|
||||||
sketchybar.enable = true;
|
sketchybar.enable = true;
|
||||||
# Optional: Add per-machine userSettings overrides
|
# Optional: Add per-machine userSettings overrides
|
||||||
# userSettings = {
|
# userSettings = {
|
||||||
|
|||||||
@@ -632,7 +632,9 @@ in
|
|||||||
text = ''
|
text = ''
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
DISK_USAGE=$(df -H / | grep -v Filesystem | awk '{print $5}')
|
# Monitor /System/Volumes/Data which contains user data on APFS
|
||||||
|
# The root / is a read-only snapshot with minimal usage
|
||||||
|
DISK_USAGE=$(df -H /System/Volumes/Data | grep -v Filesystem | awk '{print $5}')
|
||||||
|
|
||||||
${pkgs.sketchybar}/bin/sketchybar --set $NAME label="$DISK_USAGE"
|
${pkgs.sketchybar}/bin/sketchybar --set $NAME label="$DISK_USAGE"
|
||||||
'';
|
'';
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ in
|
|||||||
shellcheck
|
shellcheck
|
||||||
tmux
|
tmux
|
||||||
tree
|
tree
|
||||||
|
watch
|
||||||
];
|
];
|
||||||
|
|
||||||
# Automatic garbage collection for user profile (home-manager generations).
|
# Automatic garbage collection for user profile (home-manager generations).
|
||||||
|
|||||||
44
home/roles/development/beads-search-query-optimization.patch
Normal file
44
home/roles/development/beads-search-query-optimization.patch
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
diff --git a/internal/storage/dolt/queries.go b/internal/storage/dolt/queries.go
|
||||||
|
index 7d8214ee..8acdaae2 100644
|
||||||
|
--- a/internal/storage/dolt/queries.go
|
||||||
|
+++ b/internal/storage/dolt/queries.go
|
||||||
|
@@ -212,8 +212,21 @@ func (s *DoltStore) SearchIssues(ctx context.Context, query string, filter types
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint:gosec // G201: whereSQL contains column comparisons with ?, limitSQL is a safe integer
|
||||||
|
+ // Performance fix: SELECT all columns directly instead of id-only + WHERE IN (all_ids)
|
||||||
|
+ // See: hq-ihwsj - bd list uses inefficient WHERE IN (all_ids) query pattern
|
||||||
|
querySQL := fmt.Sprintf(`
|
||||||
|
- SELECT id FROM issues
|
||||||
|
+ SELECT id, content_hash, title, description, design, acceptance_criteria, notes,
|
||||||
|
+ status, priority, issue_type, assignee, estimated_minutes,
|
||||||
|
+ created_at, created_by, owner, updated_at, closed_at, external_ref,
|
||||||
|
+ compaction_level, compacted_at, compacted_at_commit, original_size, source_repo, close_reason,
|
||||||
|
+ deleted_at, deleted_by, delete_reason, original_type,
|
||||||
|
+ sender, ephemeral, pinned, is_template, crystallizes,
|
||||||
|
+ await_type, await_id, timeout_ns, waiters,
|
||||||
|
+ hook_bead, role_bead, agent_state, last_activity, role_type, rig, mol_type,
|
||||||
|
+ event_kind, actor, target, payload,
|
||||||
|
+ due_at, defer_until,
|
||||||
|
+ quality_score, work_type, source_system
|
||||||
|
+ FROM issues
|
||||||
|
%s
|
||||||
|
ORDER BY priority ASC, created_at DESC
|
||||||
|
%s
|
||||||
|
@@ -225,7 +238,15 @@ func (s *DoltStore) SearchIssues(ctx context.Context, query string, filter types
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
- return s.scanIssueIDs(ctx, rows)
|
||||||
|
+ var issues []*types.Issue
|
||||||
|
+ for rows.Next() {
|
||||||
|
+ issue, err := scanIssueRow(rows)
|
||||||
|
+ if err != nil {
|
||||||
|
+ return nil, err
|
||||||
|
+ }
|
||||||
|
+ issues = append(issues, issue)
|
||||||
|
+ }
|
||||||
|
+ return issues, rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetReadyWork returns issues that are ready to work on (not blocked)
|
||||||
@@ -9,17 +9,57 @@ let
|
|||||||
# Remove after upstream fix: https://github.com/steveyegge/beads/issues/XXX
|
# Remove after upstream fix: https://github.com/steveyegge/beads/issues/XXX
|
||||||
beadsPackage = globalInputs.beads.packages.${system}.default.overrideAttrs (old: {
|
beadsPackage = globalInputs.beads.packages.${system}.default.overrideAttrs (old: {
|
||||||
vendorHash = "sha256-YU+bRLVlWtHzJ1QPzcKJ70f+ynp8lMoIeFlm+29BNPE=";
|
vendorHash = "sha256-YU+bRLVlWtHzJ1QPzcKJ70f+ynp8lMoIeFlm+29BNPE=";
|
||||||
|
|
||||||
|
# Performance fix: avoid WHERE IN (8000+ IDs) query pattern that hammers Dolt CPU
|
||||||
|
# See: hq-ihwsj - bd list uses inefficient WHERE IN (all_ids) query pattern
|
||||||
|
# The fix changes SearchIssues to SELECT all columns directly instead of:
|
||||||
|
# 1. SELECT id FROM issues WHERE ... -> collect IDs
|
||||||
|
# 2. SELECT * FROM issues WHERE id IN (all_ids) -> 8000+ placeholder IN clause
|
||||||
|
patches = (old.patches or []) ++ [
|
||||||
|
./beads-search-query-optimization.patch
|
||||||
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
# Gastown - multi-agent workspace manager (no upstream flake.nix yet)
|
# Gastown - multi-agent workspace manager (no upstream flake.nix yet)
|
||||||
# Source is tracked via flake input for renovate updates
|
# Source is tracked via flake input for renovate updates
|
||||||
|
gastownRev = builtins.substring 0 8 (globalInputs.gastown.rev or "unknown");
|
||||||
gastownPackage = pkgs.buildGoModule {
|
gastownPackage = pkgs.buildGoModule {
|
||||||
pname = "gastown";
|
pname = "gastown";
|
||||||
version = "unstable-${builtins.substring 0 8 globalInputs.gastown.rev or "unknown"}";
|
version = "unstable-${gastownRev}";
|
||||||
src = globalInputs.gastown;
|
src = globalInputs.gastown;
|
||||||
vendorHash = "sha256-ripY9vrYgVW8bngAyMLh0LkU/Xx1UUaLgmAA7/EmWQU=";
|
vendorHash = "sha256-ripY9vrYgVW8bngAyMLh0LkU/Xx1UUaLgmAA7/EmWQU=";
|
||||||
subPackages = [ "cmd/gt" ];
|
subPackages = [ "cmd/gt" ];
|
||||||
doCheck = false;
|
doCheck = false;
|
||||||
|
|
||||||
|
# Must match ldflags from gastown Makefile - BuiltProperly=1 is required
|
||||||
|
# or gt will error with "This binary was built with 'go build' directly"
|
||||||
|
ldflags = [
|
||||||
|
"-X github.com/steveyegge/gastown/internal/cmd.Version=${gastownRev}"
|
||||||
|
"-X github.com/steveyegge/gastown/internal/cmd.Commit=${gastownRev}"
|
||||||
|
"-X github.com/steveyegge/gastown/internal/cmd.BuildTime=nix-build"
|
||||||
|
"-X github.com/steveyegge/gastown/internal/cmd.BuiltProperly=1"
|
||||||
|
];
|
||||||
|
|
||||||
|
# Bug fixes not yet merged upstream
|
||||||
|
# Each patch is stored in a separate file for clarity and maintainability
|
||||||
|
patches = [
|
||||||
|
# Fix validateRecipient bug: normalize addresses before comparison
|
||||||
|
./gastown-fix-validate-recipient.patch
|
||||||
|
# Fix agentBeadToAddress to use title field for hq- prefixed beads
|
||||||
|
./gastown-fix-agent-bead-address-title.patch
|
||||||
|
# Fix agentBeadToAddress to handle rig-specific prefixes (j-, sc-, etc.)
|
||||||
|
./gastown-fix-agent-bead-rig-prefix.patch
|
||||||
|
# Fix crew/polecat home paths: remove incorrect /rig suffix
|
||||||
|
./gastown-fix-role-home-paths.patch
|
||||||
|
# Fix town root detection: don't map to Mayor (causes spurious mismatch warnings)
|
||||||
|
./gastown-fix-town-root-detection.patch
|
||||||
|
# Fix copyDir to handle symlinks (broken symlinks cause "no such file" errors)
|
||||||
|
./gastown-fix-copydir-symlinks.patch
|
||||||
|
# Statusline optimization: skip expensive beads queries for detached sessions
|
||||||
|
# Reduces Dolt CPU from ~70% to ~20% by caching and early-exit
|
||||||
|
./gastown-statusline-optimization.patch
|
||||||
|
];
|
||||||
|
|
||||||
meta = with lib; {
|
meta = with lib; {
|
||||||
description = "Gas Town - multi-agent workspace manager by Steve Yegge";
|
description = "Gas Town - multi-agent workspace manager by Steve Yegge";
|
||||||
homepage = "https://github.com/steveyegge/gastown";
|
homepage = "https://github.com/steveyegge/gastown";
|
||||||
@@ -28,6 +68,28 @@ let
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Perles - TUI for beads issue tracking (no upstream flake.nix yet)
|
||||||
|
# Source is tracked via flake input for renovate updates
|
||||||
|
perlesRev = builtins.substring 0 8 (globalInputs.perles.rev or "unknown");
|
||||||
|
perlesPackage = pkgs.buildGoModule {
|
||||||
|
pname = "perles";
|
||||||
|
version = "unstable-${perlesRev}";
|
||||||
|
src = globalInputs.perles;
|
||||||
|
vendorHash = "sha256-JHERJDzbiqgjWXwRhXVjgDEiDQ3AUXRIONotfPF21B0=";
|
||||||
|
doCheck = false;
|
||||||
|
|
||||||
|
ldflags = [
|
||||||
|
"-X main.version=${perlesRev}"
|
||||||
|
];
|
||||||
|
|
||||||
|
meta = with lib; {
|
||||||
|
description = "Perles - Terminal UI for beads issue tracking";
|
||||||
|
homepage = "https://github.com/zjrosen/perles";
|
||||||
|
license = licenses.mit;
|
||||||
|
mainProgram = "perles";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
# Fetch the claude-plugins repository (for humanlayer commands/agents)
|
# Fetch the claude-plugins repository (for humanlayer commands/agents)
|
||||||
# Update the rev to get newer versions of the commands
|
# Update the rev to get newer versions of the commands
|
||||||
claudePluginsRepo = builtins.fetchGit {
|
claudePluginsRepo = builtins.fetchGit {
|
||||||
@@ -62,9 +124,11 @@ in
|
|||||||
home.packages = [
|
home.packages = [
|
||||||
beadsPackage
|
beadsPackage
|
||||||
gastownPackage
|
gastownPackage
|
||||||
|
perlesPackage
|
||||||
pkgs.unstable.claude-code
|
pkgs.unstable.claude-code
|
||||||
pkgs.unstable.claude-code-router
|
pkgs.unstable.claude-code-router
|
||||||
pkgs.unstable.codex
|
pkgs.unstable.codex
|
||||||
|
pkgs.dolt
|
||||||
pkgs.sqlite
|
pkgs.sqlite
|
||||||
|
|
||||||
# Custom packages
|
# Custom packages
|
||||||
@@ -86,12 +150,14 @@ in
|
|||||||
if [ -f "$file" ]; then
|
if [ -f "$file" ]; then
|
||||||
filename=$(basename "$file" .md)
|
filename=$(basename "$file" .md)
|
||||||
dest="$HOME/.claude/commands/humanlayer:''${filename}.md"
|
dest="$HOME/.claude/commands/humanlayer:''${filename}.md"
|
||||||
|
rm -f "$dest" 2>/dev/null || true
|
||||||
|
|
||||||
# Copy file and conditionally remove the "model:" line from frontmatter
|
# Copy file and conditionally remove the "model:" line from frontmatter
|
||||||
${if cfg.allowArbitraryClaudeCodeModelSelection
|
${if cfg.allowArbitraryClaudeCodeModelSelection
|
||||||
then "cp \"$file\" \"$dest\""
|
then "cp \"$file\" \"$dest\""
|
||||||
else "${pkgs.gnused}/bin/sed '/^model:/d' \"$file\" > \"$dest\""
|
else "${pkgs.gnused}/bin/sed '/^model:/d' \"$file\" > \"$dest\""
|
||||||
}
|
}
|
||||||
|
chmod u+w "$dest" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -100,12 +166,14 @@ in
|
|||||||
if [ -f "$file" ]; then
|
if [ -f "$file" ]; then
|
||||||
filename=$(basename "$file" .md)
|
filename=$(basename "$file" .md)
|
||||||
dest="$HOME/.claude/agents/humanlayer:''${filename}.md"
|
dest="$HOME/.claude/agents/humanlayer:''${filename}.md"
|
||||||
|
rm -f "$dest" 2>/dev/null || true
|
||||||
|
|
||||||
# Copy file and conditionally remove the "model:" line from frontmatter
|
# Copy file and conditionally remove the "model:" line from frontmatter
|
||||||
${if cfg.allowArbitraryClaudeCodeModelSelection
|
${if cfg.allowArbitraryClaudeCodeModelSelection
|
||||||
then "cp \"$file\" \"$dest\""
|
then "cp \"$file\" \"$dest\""
|
||||||
else "${pkgs.gnused}/bin/sed '/^model:/d' \"$file\" > \"$dest\""
|
else "${pkgs.gnused}/bin/sed '/^model:/d' \"$file\" > \"$dest\""
|
||||||
}
|
}
|
||||||
|
chmod u+w "$dest" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -120,6 +188,7 @@ in
|
|||||||
sleep 0.5
|
sleep 0.5
|
||||||
cp "$file" "$dest" || echo "Warning: Failed to copy $filename.md to commands"
|
cp "$file" "$dest" || echo "Warning: Failed to copy $filename.md to commands"
|
||||||
fi
|
fi
|
||||||
|
chmod u+w "$dest" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -134,13 +203,17 @@ in
|
|||||||
sleep 0.5
|
sleep 0.5
|
||||||
cp "$file" "$dest" || echo "Warning: Failed to copy $filename.md to skills"
|
cp "$file" "$dest" || echo "Warning: Failed to copy $filename.md to skills"
|
||||||
fi
|
fi
|
||||||
|
chmod u+w "$dest" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Copy micro-skills (compact reusable knowledge referenced by formulas)
|
# Copy micro-skills (compact reusable knowledge referenced by formulas)
|
||||||
for file in ${./skills/micro}/*.md; do
|
for file in ${./skills/micro}/*.md; do
|
||||||
if [ -f "$file" ]; then
|
if [ -f "$file" ]; then
|
||||||
cp "$file" "$HOME/.claude/commands/skills/$(basename "$file")"
|
dest="$HOME/.claude/commands/skills/$(basename "$file")"
|
||||||
|
rm -f "$dest" 2>/dev/null || true
|
||||||
|
cp "$file" "$dest"
|
||||||
|
chmod u+w "$dest" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -148,7 +221,10 @@ in
|
|||||||
mkdir -p ~/.beads/formulas
|
mkdir -p ~/.beads/formulas
|
||||||
for file in ${./formulas}/*.formula.toml; do
|
for file in ${./formulas}/*.formula.toml; do
|
||||||
if [ -f "$file" ]; then
|
if [ -f "$file" ]; then
|
||||||
cp "$file" "$HOME/.beads/formulas/$(basename "$file")"
|
dest="$HOME/.beads/formulas/$(basename "$file")"
|
||||||
|
rm -f "$dest" 2>/dev/null || true
|
||||||
|
cp "$file" "$dest"
|
||||||
|
chmod u+w "$dest" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
diff --git a/internal/mail/router.go b/internal/mail/router.go
|
||||||
|
--- a/internal/mail/router.go
|
||||||
|
+++ b/internal/mail/router.go
|
||||||
|
@@ -326,7 +326,10 @@ func agentBeadToAddress(bead *agentBead) string {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to parsing description for role_type and rig
|
||||||
|
- return parseAgentAddressFromDescription(bead.Description)
|
||||||
|
+ if bead.Title != "" && strings.Contains(bead.Title, "/") {
|
||||||
|
+ return bead.Title
|
||||||
|
+ }
|
||||||
|
+ return parseAgentAddressFromDescription(bead.Description)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle gt- prefixed IDs (legacy format)
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
diff --git a/internal/mail/router.go b/internal/mail/router.go
|
||||||
|
--- a/internal/mail/router.go
|
||||||
|
+++ b/internal/mail/router.go
|
||||||
|
@@ -330,8 +330,29 @@ func agentBeadToAddress(bead *agentBead) string {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle gt- prefixed IDs (legacy format)
|
||||||
|
- if !strings.HasPrefix(id, "gt-") {
|
||||||
|
- return "" // Not a valid agent bead ID
|
||||||
|
+ // Handle rig-specific prefixes: <prefix>-<rig>-<role>-<name>
|
||||||
|
+ // Examples: j-java-crew-americano -> java/crew/americano
|
||||||
|
+ idParts := strings.Split(id, "-")
|
||||||
|
+ if len(idParts) >= 3 {
|
||||||
|
+ for i, part := range idParts {
|
||||||
|
+ if part == "crew" || part == "polecat" || part == "polecats" {
|
||||||
|
+ if i >= 1 && i < len(idParts)-1 {
|
||||||
|
+ rig := idParts[i-1]
|
||||||
|
+ name := strings.Join(idParts[i+1:], "-")
|
||||||
|
+ return rig + "/" + part + "/" + name
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ if part == "witness" || part == "refinery" {
|
||||||
|
+ if i >= 1 {
|
||||||
|
+ return idParts[i-1] + "/" + part
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ // Handle gt- prefixed IDs (legacy format)
|
||||||
|
+ if !strings.HasPrefix(id, "gt-") {
|
||||||
|
+ return "" // Not a valid agent bead ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip prefix
|
||||||
25
home/roles/development/gastown-fix-copydir-symlinks.patch
Normal file
25
home/roles/development/gastown-fix-copydir-symlinks.patch
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
diff --git a/internal/git/git.go b/internal/git/git.go
|
||||||
|
--- a/internal/git/git.go
|
||||||
|
+++ b/internal/git/git.go
|
||||||
|
@@ -73,7 +73,19 @@ func copyDir(src, dest string) error {
|
||||||
|
srcPath := filepath.Join(src, entry.Name())
|
||||||
|
destPath := filepath.Join(dest, entry.Name())
|
||||||
|
|
||||||
|
- if entry.IsDir() {
|
||||||
|
+ // Handle symlinks (recreate them, do not follow)
|
||||||
|
+ if entry.Type()&os.ModeSymlink != 0 {
|
||||||
|
+ linkTarget, err := os.Readlink(srcPath)
|
||||||
|
+ if err != nil {
|
||||||
|
+ return err
|
||||||
|
+ }
|
||||||
|
+ if err := os.Symlink(linkTarget, destPath); err != nil {
|
||||||
|
+ return err
|
||||||
|
+ }
|
||||||
|
+ continue
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if entry.IsDir() {
|
||||||
|
if err := copyDir(srcPath, destPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
18
home/roles/development/gastown-fix-role-home-paths.patch
Normal file
18
home/roles/development/gastown-fix-role-home-paths.patch
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
diff --git a/internal/cmd/role.go b/internal/cmd/role.go
|
||||||
|
--- a/internal/cmd/role.go
|
||||||
|
+++ b/internal/cmd/role.go
|
||||||
|
@@ -326,11 +326,11 @@ func getRoleHome(role Role, rig, polecat, townRoot string) string {
|
||||||
|
if rig == "" || polecat == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
- return filepath.Join(townRoot, rig, "polecats", polecat, "rig")
|
||||||
|
+ return filepath.Join(townRoot, rig, "polecats", polecat)
|
||||||
|
case RoleCrew:
|
||||||
|
if rig == "" || polecat == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
- return filepath.Join(townRoot, rig, "crew", polecat, "rig")
|
||||||
|
+ return filepath.Join(townRoot, rig, "crew", polecat)
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
18
home/roles/development/gastown-fix-town-root-detection.patch
Normal file
18
home/roles/development/gastown-fix-town-root-detection.patch
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
diff --git a/internal/cmd/prime.go b/internal/cmd/prime.go
|
||||||
|
--- a/internal/cmd/prime.go
|
||||||
|
+++ b/internal/cmd/prime.go
|
||||||
|
@@ -276,11 +276,12 @@ func detectRole(cwd, townRoot string) RoleInfo {
|
||||||
|
|
||||||
|
// Check for mayor role
|
||||||
|
// At town root, or in mayor/ or mayor/rig/
|
||||||
|
if relPath == "." || relPath == "" {
|
||||||
|
- ctx.Role = RoleMayor
|
||||||
|
- return ctx
|
||||||
|
+ return ctx // RoleUnknown - town root is shared space
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ // Check for mayor role: mayor/ or mayor/rig/
|
||||||
|
if len(parts) >= 1 && parts[0] == "mayor" {
|
||||||
|
ctx.Role = RoleMayor
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
13
home/roles/development/gastown-fix-validate-recipient.patch
Normal file
13
home/roles/development/gastown-fix-validate-recipient.patch
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
diff --git a/internal/mail/router.go b/internal/mail/router.go
|
||||||
|
index b864c069..4b6a045b 100644
|
||||||
|
--- a/internal/mail/router.go
|
||||||
|
+++ b/internal/mail/router.go
|
||||||
|
@@ -646,7 +646,7 @@ func (r *Router) validateRecipient(identity string) error {
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, agent := range agents {
|
||||||
|
- if agentBeadToAddress(agent) == identity {
|
||||||
|
+ if AddressToIdentity(agentBeadToAddress(agent)) == AddressToIdentity(identity) {
|
||||||
|
return nil // Found matching agent
|
||||||
|
}
|
||||||
|
}
|
||||||
135
home/roles/development/gastown-statusline-optimization.patch
Normal file
135
home/roles/development/gastown-statusline-optimization.patch
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
diff --git a/internal/cmd/statusline.go b/internal/cmd/statusline.go
|
||||||
|
index 2edf1be8..00253eea 100644
|
||||||
|
--- a/internal/cmd/statusline.go
|
||||||
|
+++ b/internal/cmd/statusline.go
|
||||||
|
@@ -6,6 +6,7 @@ import (
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
+ "time"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/steveyegge/gastown/internal/beads"
|
||||||
|
@@ -14,6 +15,37 @@ import (
|
||||||
|
"github.com/steveyegge/gastown/internal/tmux"
|
||||||
|
"github.com/steveyegge/gastown/internal/workspace"
|
||||||
|
)
|
||||||
|
+// statusLineCacheTTL is how long cached status output remains valid.
|
||||||
|
+const statusLineCacheTTL = 10 * time.Second
|
||||||
|
+
|
||||||
|
+// statusLineCachePath returns the cache file path for a session.
|
||||||
|
+func statusLineCachePath(session string) string {
|
||||||
|
+ return filepath.Join(os.TempDir(), fmt.Sprintf("gt-status-%s", session))
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+// getStatusLineCache returns cached status if fresh, empty string otherwise.
|
||||||
|
+func getStatusLineCache(session string) string {
|
||||||
|
+ path := statusLineCachePath(session)
|
||||||
|
+ info, err := os.Stat(path)
|
||||||
|
+ if err != nil {
|
||||||
|
+ return ""
|
||||||
|
+ }
|
||||||
|
+ if time.Since(info.ModTime()) > statusLineCacheTTL {
|
||||||
|
+ return ""
|
||||||
|
+ }
|
||||||
|
+ data, err := os.ReadFile(path)
|
||||||
|
+ if err != nil {
|
||||||
|
+ return ""
|
||||||
|
+ }
|
||||||
|
+ return string(data)
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+// setStatusLineCache writes status to cache file.
|
||||||
|
+func setStatusLineCache(session, status string) {
|
||||||
|
+ path := statusLineCachePath(session)
|
||||||
|
+ _ = os.WriteFile(path, []byte(status), 0644)
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
|
||||||
|
var (
|
||||||
|
statusLineSession string
|
||||||
|
@@ -34,6 +66,19 @@ func init() {
|
||||||
|
func runStatusLine(cmd *cobra.Command, args []string) error {
|
||||||
|
t := tmux.NewTmux()
|
||||||
|
|
||||||
|
+ // Optimization: skip expensive beads queries for detached sessions
|
||||||
|
+ if statusLineSession != "" {
|
||||||
|
+ if !t.IsSessionAttached(statusLineSession) {
|
||||||
|
+ fmt.Print("○ |")
|
||||||
|
+ return nil
|
||||||
|
+ }
|
||||||
|
+ // Check cache for attached sessions too
|
||||||
|
+ if cached := getStatusLineCache(statusLineSession); cached != "" {
|
||||||
|
+ fmt.Print(cached)
|
||||||
|
+ return nil
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
// Get session environment
|
||||||
|
var rigName, polecat, crew, issue, role string
|
||||||
|
|
||||||
|
@@ -150,7 +195,11 @@ func runWorkerStatusLine(t *tmux.Tmux, session, rigName, polecat, crew, issue st
|
||||||
|
|
||||||
|
// Output
|
||||||
|
if len(parts) > 0 {
|
||||||
|
- fmt.Print(strings.Join(parts, " | ") + " |")
|
||||||
|
+ output := strings.Join(parts, " | ") + " |"
|
||||||
|
+ if statusLineSession != "" {
|
||||||
|
+ setStatusLineCache(statusLineSession, output)
|
||||||
|
+ }
|
||||||
|
+ fmt.Print(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
@@ -389,7 +438,11 @@ func runMayorStatusLine(t *tmux.Tmux) error {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- fmt.Print(strings.Join(parts, " | ") + " |")
|
||||||
|
+ output := strings.Join(parts, " | ") + " |"
|
||||||
|
+ if statusLineSession != "" {
|
||||||
|
+ setStatusLineCache(statusLineSession, output)
|
||||||
|
+ }
|
||||||
|
+ fmt.Print(output)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -458,7 +511,11 @@ func runDeaconStatusLine(t *tmux.Tmux) error {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- fmt.Print(strings.Join(parts, " | ") + " |")
|
||||||
|
+ output := strings.Join(parts, " | ") + " |"
|
||||||
|
+ if statusLineSession != "" {
|
||||||
|
+ setStatusLineCache(statusLineSession, output)
|
||||||
|
+ }
|
||||||
|
+ fmt.Print(output)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -526,7 +583,11 @@ func runWitnessStatusLine(t *tmux.Tmux, rigName string) error {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- fmt.Print(strings.Join(parts, " | ") + " |")
|
||||||
|
+ output := strings.Join(parts, " | ") + " |"
|
||||||
|
+ if statusLineSession != "" {
|
||||||
|
+ setStatusLineCache(statusLineSession, output)
|
||||||
|
+ }
|
||||||
|
+ fmt.Print(output)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -617,7 +678,11 @@ func runRefineryStatusLine(t *tmux.Tmux, rigName string) error {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- fmt.Print(strings.Join(parts, " | ") + " |")
|
||||||
|
+ output := strings.Join(parts, " | ") + " |"
|
||||||
|
+ if statusLineSession != "" {
|
||||||
|
+ setStatusLineCache(statusLineSession, output)
|
||||||
|
+ }
|
||||||
|
+ fmt.Print(output)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
@@ -53,6 +53,22 @@
|
|||||||
;; change `org-directory'. It must be set before org loads!
|
;; change `org-directory'. It must be set before org loads!
|
||||||
(setq org-directory "~/org/")
|
(setq org-directory "~/org/")
|
||||||
(after! org
|
(after! org
|
||||||
|
;; Skip recurring events past their CALDAV_UNTIL date
|
||||||
|
;; org-caldav ignores UNTIL from RRULE, so we store it as a property
|
||||||
|
;; and filter here in the agenda
|
||||||
|
(defun my/skip-if-past-until ()
|
||||||
|
"Return non-nil if entry has CALDAV_UNTIL and current date is past it."
|
||||||
|
(let ((until-str (org-entry-get nil "CALDAV_UNTIL")))
|
||||||
|
(when (and until-str
|
||||||
|
(string-match "^\\([0-9]\\{4\\}\\)\\([0-9]\\{2\\}\\)\\([0-9]\\{2\\}\\)" until-str))
|
||||||
|
(let* ((until-year (string-to-number (match-string 1 until-str)))
|
||||||
|
(until-month (string-to-number (match-string 2 until-str)))
|
||||||
|
(until-day (string-to-number (match-string 3 until-str)))
|
||||||
|
(until-time (encode-time 0 0 0 until-day until-month until-year))
|
||||||
|
(today (current-time)))
|
||||||
|
(when (time-less-p until-time today)
|
||||||
|
(org-end-of-subtree t))))))
|
||||||
|
|
||||||
(setq org-agenda-span 'week
|
(setq org-agenda-span 'week
|
||||||
org-agenda-start-with-log-mode t
|
org-agenda-start-with-log-mode t
|
||||||
my-agenda-dirs '("projects" "roam")
|
my-agenda-dirs '("projects" "roam")
|
||||||
@@ -61,6 +77,7 @@
|
|||||||
"\.org$"))
|
"\.org$"))
|
||||||
my-agenda-dirs))
|
my-agenda-dirs))
|
||||||
org-log-done 'time
|
org-log-done 'time
|
||||||
|
org-agenda-skip-function-global #'my/skip-if-past-until
|
||||||
org-agenda-custom-commands '(("n" "Agenda"
|
org-agenda-custom-commands '(("n" "Agenda"
|
||||||
((agenda "")
|
((agenda "")
|
||||||
(tags-todo "-someday-recurring")))
|
(tags-todo "-someday-recurring")))
|
||||||
@@ -83,25 +100,135 @@
|
|||||||
"d" #'org-agenda-day-view
|
"d" #'org-agenda-day-view
|
||||||
"w" #'org-agenda-week-view))
|
"w" #'org-agenda-week-view))
|
||||||
|
|
||||||
;; (use-package! org-caldav
|
;; org-caldav: Sync Org entries with Nextcloud CalDAV
|
||||||
;; :defer t
|
;; Setup requirements:
|
||||||
;; :config
|
;; 1. Create Nextcloud app password: Settings -> Security -> Devices & sessions
|
||||||
;; (setq org-caldav-url "https://nextcloud.johnogle.info/remote.php/dav/calendars/johno"
|
;; 2. Store in rbw: rbw add nextcloud-caldav (put app password as the secret)
|
||||||
;; org-caldav-calendar-id "personal"
|
;; 3. Run: doom sync
|
||||||
;; org-icalendar-timezone "America/Los_Angeles"
|
;; 4. Test: M-x my/org-caldav-sync-with-rbw (or SPC o a s)
|
||||||
;; org-caldav-inbox "~/org/calendar.org"
|
;;
|
||||||
;; org-caldav-files nil
|
;; Note: Conflict resolution is "Org always wins" - treat Org as source of truth
|
||||||
;; org-caldav-sync-direction 'cal->org))
|
;; for entries that originated in Org.
|
||||||
|
|
||||||
(defun my/get-rbw-password (alias)
|
;; Define sync wrapper before use-package (so keybinding works)
|
||||||
"Return the password for ALIAS via rbw, unlocking the vault only if needed."
|
(defun my/org-caldav-sync-with-rbw ()
|
||||||
(let* ((cmd (format "rbw get %s 2>&1" alias))
|
"Run org-caldav-sync with credentials from rbw embedded in URL."
|
||||||
(output (shell-command-to-string cmd)))
|
(interactive)
|
||||||
(string-trim output)))
|
(require 'org)
|
||||||
|
(require 'org-caldav)
|
||||||
|
(let* ((password (my/get-rbw-password "nextcloud-caldav"))
|
||||||
|
;; Embed credentials in URL (url-encode password in case of special chars)
|
||||||
|
(encoded-pass (url-hexify-string password)))
|
||||||
|
(setq org-caldav-url
|
||||||
|
(format "https://johno:%s@nextcloud.johnogle.info/remote.php/dav/calendars/johno"
|
||||||
|
encoded-pass))
|
||||||
|
(org-caldav-sync)))
|
||||||
|
|
||||||
|
(use-package! org-caldav
|
||||||
|
:after org
|
||||||
|
:commands (org-caldav-sync my/org-caldav-sync-with-rbw)
|
||||||
|
:init
|
||||||
|
(map! :leader
|
||||||
|
(:prefix ("o" . "open")
|
||||||
|
(:prefix ("a" . "agenda/calendar")
|
||||||
|
:desc "Sync CalDAV" "s" #'my/org-caldav-sync-with-rbw)))
|
||||||
|
:config
|
||||||
|
;; Nextcloud CalDAV base URL (credentials added dynamically by sync wrapper)
|
||||||
|
(setq org-caldav-url "https://nextcloud.johnogle.info/remote.php/dav/calendars/johno")
|
||||||
|
|
||||||
|
;; Timezone for iCalendar export
|
||||||
|
(setq org-icalendar-timezone "America/Los_Angeles")
|
||||||
|
|
||||||
|
;; Sync state storage (in org directory for multi-machine sync)
|
||||||
|
(setq org-caldav-save-directory (expand-file-name ".org-caldav/" org-directory))
|
||||||
|
|
||||||
|
;; Backup file for entries before modification
|
||||||
|
(setq org-caldav-backup-file (expand-file-name ".org-caldav/backup.org" org-directory))
|
||||||
|
|
||||||
|
;; Limit past events to 30 days (avoids uploading years of scheduled tasks)
|
||||||
|
(setq org-caldav-days-in-past 30)
|
||||||
|
|
||||||
|
;; Sync behavior: bidirectional by default
|
||||||
|
(setq org-caldav-sync-direction 'twoway)
|
||||||
|
|
||||||
|
;; What changes from calendar sync back to Org (conservative: title and timestamp only)
|
||||||
|
(setq org-caldav-sync-changes-to-org 'title-and-timestamp)
|
||||||
|
|
||||||
|
;; Deletion handling: never auto-delete to prevent accidental mass deletion
|
||||||
|
(setq org-caldav-delete-calendar-entries 'never)
|
||||||
|
(setq org-caldav-delete-org-entries 'never)
|
||||||
|
|
||||||
|
;; Enable TODO/VTODO sync
|
||||||
|
(setq org-icalendar-include-todo 'all)
|
||||||
|
(setq org-caldav-sync-todo t)
|
||||||
|
|
||||||
|
;; Map VTODO percent-complete to org-todo-keywords
|
||||||
|
;; Format: (PERCENT "KEYWORD") - percent thresholds map to states
|
||||||
|
(setq org-caldav-todo-percent-states
|
||||||
|
'((0 "TODO")
|
||||||
|
(25 "WAIT")
|
||||||
|
(50 "IN-PROGRESS")
|
||||||
|
(100 "DONE")
|
||||||
|
(100 "KILL")))
|
||||||
|
|
||||||
|
;; Allow export with broken links (mu4e links can't be resolved during export)
|
||||||
|
(setq org-export-with-broken-links 'mark)
|
||||||
|
|
||||||
|
;; Calendar-specific configuration
|
||||||
|
(setq org-caldav-calendars
|
||||||
|
'(;; Personal calendar: two-way sync with family-shared Nextcloud calendar
|
||||||
|
(:calendar-id "personal"
|
||||||
|
:inbox "~/org/personal-calendar.org"
|
||||||
|
:files ("~/org/personal-calendar.org"))
|
||||||
|
|
||||||
|
;; Tasks calendar: one-way sync (org → calendar only)
|
||||||
|
;; SCHEDULED/DEADLINE items from todo.org push to private Tasks calendar.
|
||||||
|
;; No inbox = no download from calendar (effectively one-way).
|
||||||
|
;; Note: Create 'tasks' calendar in Nextcloud first, keep it private.
|
||||||
|
(:calendar-id "tasks"
|
||||||
|
:files ("~/org/todo.org"))))
|
||||||
|
|
||||||
|
;; Handle UNTIL in recurring events
|
||||||
|
;; org-caldav ignores UNTIL from RRULE - events repeat forever.
|
||||||
|
;; This advice extracts UNTIL and stores it as a property for agenda filtering.
|
||||||
|
(defun my/org-caldav-add-until-property (orig-fun eventdata-alist)
|
||||||
|
"Advice to store CALDAV_UNTIL property for recurring events."
|
||||||
|
(let ((result (funcall orig-fun eventdata-alist)))
|
||||||
|
(let* ((rrule-props (alist-get 'rrule-props eventdata-alist))
|
||||||
|
(until-str (cadr (assoc 'UNTIL rrule-props)))
|
||||||
|
(summary (alist-get 'summary eventdata-alist)))
|
||||||
|
;; Debug: log what we're seeing
|
||||||
|
(message "CALDAV-DEBUG: %s | rrule-props: %S | until: %s"
|
||||||
|
(or summary "?") rrule-props until-str)
|
||||||
|
(when until-str
|
||||||
|
(save-excursion
|
||||||
|
(org-back-to-heading t)
|
||||||
|
(org-entry-put nil "CALDAV_UNTIL" until-str))))
|
||||||
|
result))
|
||||||
|
|
||||||
|
(advice-add 'org-caldav-insert-org-event-or-todo
|
||||||
|
:around #'my/org-caldav-add-until-property)
|
||||||
|
)
|
||||||
|
|
||||||
|
(defun my/get-rbw-password (alias &optional no-error)
|
||||||
|
"Return the password for ALIAS via rbw, unlocking the vault only if needed.
|
||||||
|
If NO-ERROR is non-nil, return nil instead of signaling an error when
|
||||||
|
rbw is unavailable or the entry is not found."
|
||||||
|
(if (not (executable-find "rbw"))
|
||||||
|
(if no-error
|
||||||
|
nil
|
||||||
|
(user-error "rbw: not installed or not in PATH"))
|
||||||
|
(let* ((cmd (format "rbw get %s 2>/dev/null" (shell-quote-argument alias)))
|
||||||
|
(output (string-trim (shell-command-to-string cmd))))
|
||||||
|
(if (string-empty-p output)
|
||||||
|
(if no-error
|
||||||
|
nil
|
||||||
|
(user-error "rbw: no entry found for '%s' - run: rbw add %s" alias alias))
|
||||||
|
output))))
|
||||||
|
|
||||||
(after! gptel
|
(after! gptel
|
||||||
:config
|
:config
|
||||||
(setq! gptel-api-key (my/get-rbw-password "openai-api-key-chatgpt-el")
|
(setq! gptel-api-key (my/get-rbw-password "openai-api-key-chatgpt-el" t)
|
||||||
gptel-default-mode 'org-mode
|
gptel-default-mode 'org-mode
|
||||||
gptel-use-tools t
|
gptel-use-tools t
|
||||||
gptel-confirm-tool-calls 'always
|
gptel-confirm-tool-calls 'always
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
;; ...Or *all* packages (NOT RECOMMENDED; will likely break things)
|
;; ...Or *all* packages (NOT RECOMMENDED; will likely break things)
|
||||||
;; (unpin! t)
|
;; (unpin! t)
|
||||||
|
|
||||||
;; (package! org-caldav)
|
(package! org-caldav)
|
||||||
|
|
||||||
;; Note: Packages with custom recipes must be pinned for nix-doom-emacs-unstraightened
|
;; Note: Packages with custom recipes must be pinned for nix-doom-emacs-unstraightened
|
||||||
;; to build deterministically. Update pins when upgrading packages.
|
;; to build deterministically. Update pins when upgrading packages.
|
||||||
|
|||||||
@@ -90,6 +90,8 @@ with lib;
|
|||||||
htop
|
htop
|
||||||
tmux
|
tmux
|
||||||
zfs
|
zfs
|
||||||
|
rclone
|
||||||
|
custom.rclone-torbox-setup # Helper script to set up TorBox credentials via rbw
|
||||||
];
|
];
|
||||||
|
|
||||||
# Enable SSH
|
# Enable SSH
|
||||||
@@ -126,6 +128,26 @@ with lib;
|
|||||||
|
|
||||||
roles.virtualisation.enable = true;
|
roles.virtualisation.enable = true;
|
||||||
|
|
||||||
|
# TorBox WebDAV mount for rdt-client and Jellyfin
|
||||||
|
roles.rclone-mount = {
|
||||||
|
enable = true;
|
||||||
|
mounts.torbox = {
|
||||||
|
webdavUrl = "https://webdav.torbox.app";
|
||||||
|
username = "john@ogle.fyi"; # TorBox account email
|
||||||
|
mountPoint = "/media/media/torbox-rclone";
|
||||||
|
environmentFile = "/etc/rclone/torbox.env";
|
||||||
|
vfsCacheMode = "full"; # Best for streaming media
|
||||||
|
dirCacheTime = "5m";
|
||||||
|
extraArgs = [
|
||||||
|
"--buffer-size=64M"
|
||||||
|
"--vfs-read-chunk-size=32M"
|
||||||
|
"--vfs-read-chunk-size-limit=off"
|
||||||
|
];
|
||||||
|
# Wait for ZFS media pool to be mounted before starting
|
||||||
|
requiresMountsFor = [ "/media" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
# Time zone
|
# Time zone
|
||||||
time.timeZone = "America/Los_Angeles"; # Adjust as needed
|
time.timeZone = "America/Los_Angeles"; # Adjust as needed
|
||||||
|
|
||||||
|
|||||||
@@ -23,12 +23,12 @@
|
|||||||
printing.enable = true;
|
printing.enable = true;
|
||||||
remote-build.builders = [
|
remote-build.builders = [
|
||||||
{
|
{
|
||||||
hostName = "zix790prors";
|
hostName = "zix790prors.oglehome";
|
||||||
maxJobs = 16;
|
maxJobs = 16;
|
||||||
speedFactor = 3;
|
speedFactor = 3;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
hostName = "john-endesktop";
|
hostName = "john-endesktop.oglehome";
|
||||||
maxJobs = 1;
|
maxJobs = 1;
|
||||||
speedFactor = 1;
|
speedFactor = 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,11 +19,18 @@
|
|||||||
desktopSession = "plasma";
|
desktopSession = "plasma";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
remote-build.builders = [{
|
remote-build.builders = [
|
||||||
hostName = "zix790prors";
|
{
|
||||||
maxJobs = 16;
|
hostName = "zix790prors.oglehome";
|
||||||
speedFactor = 4; # Prefer remote heavily on Steam Deck
|
maxJobs = 16;
|
||||||
}];
|
speedFactor = 4;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
hostName = "john-endesktop.oglehome";
|
||||||
|
maxJobs = 1;
|
||||||
|
speedFactor = 2;
|
||||||
|
}
|
||||||
|
];
|
||||||
users = {
|
users = {
|
||||||
enable = true;
|
enable = true;
|
||||||
extraGroups = [ "video" ];
|
extraGroups = [ "video" ];
|
||||||
|
|||||||
@@ -6,24 +6,24 @@
|
|||||||
}:
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
version = "2.1.12";
|
version = "2.1.19";
|
||||||
|
|
||||||
srcs = {
|
srcs = {
|
||||||
aarch64-darwin = {
|
aarch64-darwin = {
|
||||||
url = "https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases/${version}/darwin-arm64/claude";
|
url = "https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases/${version}/darwin-arm64/claude";
|
||||||
sha256 = "40be59519a84bd35eb1111aa46f72aa6b3443866d3f6336252a198fdcaefbbe5";
|
sha256 = "d386ac8f6d1479f85d31f369421c824135c10249c32087017d05a5f428852c41";
|
||||||
};
|
};
|
||||||
x86_64-darwin = {
|
x86_64-darwin = {
|
||||||
url = "https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases/${version}/darwin-x64/claude";
|
url = "https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases/${version}/darwin-x64/claude";
|
||||||
sha256 = "0eee4b46c91749480bf856f88e49b15a3e944faa9d346679c5f0c0d7fa6f2f54";
|
sha256 = "be266b3a952f483d8358ad141e2afe661170386506f479ead992319e4fdc38ac";
|
||||||
};
|
};
|
||||||
x86_64-linux = {
|
x86_64-linux = {
|
||||||
url = "https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases/${version}/linux-x64/claude";
|
url = "https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases/${version}/linux-x64/claude";
|
||||||
sha256 = "3fe979215489dc1b31463fadf95ed2d2d5473a9969447bb7a46431f4578847d4";
|
sha256 = "4e2a1c73871ecf3b133376b57ded03333a7a6387f2d2a3a6279bb90a07f7a944";
|
||||||
};
|
};
|
||||||
aarch64-linux = {
|
aarch64-linux = {
|
||||||
url = "https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases/${version}/linux-arm64/claude";
|
url = "https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases/${version}/linux-arm64/claude";
|
||||||
sha256 = "e214b1d3b5afd4cd2de9177359001d41a3eb98cb1e3665fe97edc592f5aa132f";
|
sha256 = "8c4b61b24ca760d6f7aa2f19727163d122e9fd0c3ce91f106a21b6918a7b1bbb";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,4 +4,5 @@
|
|||||||
app-launcher-server = pkgs.callPackage ./app-launcher-server {};
|
app-launcher-server = pkgs.callPackage ./app-launcher-server {};
|
||||||
claude-code = pkgs.callPackage ./claude-code {};
|
claude-code = pkgs.callPackage ./claude-code {};
|
||||||
mcrcon-rbw = pkgs.callPackage ./mcrcon-rbw {};
|
mcrcon-rbw = pkgs.callPackage ./mcrcon-rbw {};
|
||||||
|
rclone-torbox-setup = pkgs.callPackage ./rclone-torbox-setup {};
|
||||||
}
|
}
|
||||||
|
|||||||
98
packages/rclone-torbox-setup/default.nix
Normal file
98
packages/rclone-torbox-setup/default.nix
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
{ pkgs, ... }:
|
||||||
|
|
||||||
|
pkgs.writeShellScriptBin "rclone-torbox-setup" ''
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Default values
|
||||||
|
RBW_ENTRY="''${1:-torbox}"
|
||||||
|
ENV_FILE="''${2:-/etc/rclone/torbox.env}"
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "Usage: rclone-torbox-setup [rbw-entry] [env-file]"
|
||||||
|
echo ""
|
||||||
|
echo "Sets up rclone credentials for TorBox WebDAV mount."
|
||||||
|
echo "Retrieves password from rbw (Bitwarden), obscures it for rclone,"
|
||||||
|
echo "and writes it to the environment file for the systemd service."
|
||||||
|
echo ""
|
||||||
|
echo "Arguments:"
|
||||||
|
echo " rbw-entry Name of the Bitwarden entry containing the password (default: torbox)"
|
||||||
|
echo " env-file Path to write the environment file (default: /etc/rclone/torbox.env)"
|
||||||
|
echo ""
|
||||||
|
echo "The Bitwarden entry should contain your TorBox password as the password field."
|
||||||
|
echo ""
|
||||||
|
echo "Example:"
|
||||||
|
echo " rclone-torbox-setup torbox-password /etc/rclone/torbox.env"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "''${1:-}" == "-h" ]] || [[ "''${1:-}" == "--help" ]]; then
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "rclone TorBox credential setup"
|
||||||
|
echo "=============================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if rbw is available
|
||||||
|
if ! command -v rbw &> /dev/null; then
|
||||||
|
echo "Error: rbw is not available. Please ensure rbw is installed and configured."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if rclone is available
|
||||||
|
if ! command -v rclone &> /dev/null; then
|
||||||
|
echo "Error: rclone is not available. Please ensure rclone is installed."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Retrieving password from rbw entry: $RBW_ENTRY"
|
||||||
|
|
||||||
|
# Retrieve password from Bitwarden
|
||||||
|
if ! TORBOX_PASS=$(rbw get "$RBW_ENTRY" 2>/dev/null); then
|
||||||
|
echo ""
|
||||||
|
echo "Error: Failed to retrieve password from rbw entry '$RBW_ENTRY'"
|
||||||
|
echo ""
|
||||||
|
echo "Please ensure:"
|
||||||
|
echo " 1. The entry '$RBW_ENTRY' exists in Bitwarden"
|
||||||
|
echo " 2. rbw is unlocked: rbw unlock"
|
||||||
|
echo " 3. rbw is synced: rbw sync"
|
||||||
|
echo ""
|
||||||
|
echo "To create the entry in Bitwarden:"
|
||||||
|
echo " - Name: $RBW_ENTRY"
|
||||||
|
echo " - Password: Your TorBox password"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Password retrieved successfully"
|
||||||
|
|
||||||
|
# Obscure the password for rclone
|
||||||
|
echo "Obscuring password for rclone..."
|
||||||
|
if ! OBSCURED_PASS=$(echo -n "$TORBOX_PASS" | rclone obscure -); then
|
||||||
|
echo "Error: Failed to obscure password with rclone"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create the directory if needed (requires sudo)
|
||||||
|
ENV_DIR=$(dirname "$ENV_FILE")
|
||||||
|
if [[ ! -d "$ENV_DIR" ]]; then
|
||||||
|
echo "Creating directory $ENV_DIR (requires sudo)..."
|
||||||
|
sudo mkdir -p "$ENV_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Write the environment file
|
||||||
|
echo "Writing environment file to $ENV_FILE (requires sudo)..."
|
||||||
|
echo "RCLONE_WEBDAV_PASS=$OBSCURED_PASS" | sudo tee "$ENV_FILE" > /dev/null
|
||||||
|
sudo chmod 600 "$ENV_FILE"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Setup complete!"
|
||||||
|
echo ""
|
||||||
|
echo "The environment file has been created at: $ENV_FILE"
|
||||||
|
echo "The rclone-mount-torbox systemd service will use this file."
|
||||||
|
echo ""
|
||||||
|
echo "To activate the mount after NixOS rebuild:"
|
||||||
|
echo " sudo systemctl start rclone-mount-torbox"
|
||||||
|
echo ""
|
||||||
|
echo "To check status:"
|
||||||
|
echo " sudo systemctl status rclone-mount-torbox"
|
||||||
|
''
|
||||||
@@ -14,6 +14,7 @@ with lib;
|
|||||||
./nfs-mounts
|
./nfs-mounts
|
||||||
./nvidia
|
./nvidia
|
||||||
./printing
|
./printing
|
||||||
|
./rclone-mount
|
||||||
./remote-build
|
./remote-build
|
||||||
./spotifyd
|
./spotifyd
|
||||||
./users
|
./users
|
||||||
|
|||||||
149
roles/rclone-mount/default.nix
Normal file
149
roles/rclone-mount/default.nix
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.roles.rclone-mount;
|
||||||
|
|
||||||
|
# Generate systemd service for a single mount
|
||||||
|
mkMountService = name: mountCfg: {
|
||||||
|
description = "rclone mount for ${name}";
|
||||||
|
after = [ "network-online.target" ];
|
||||||
|
wants = [ "network-online.target" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
|
||||||
|
# Wait for parent mount points (e.g., ZFS pools) to be available
|
||||||
|
unitConfig = mkIf (mountCfg.requiresMountsFor != []) {
|
||||||
|
RequiresMountsFor = mountCfg.requiresMountsFor;
|
||||||
|
};
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "notify";
|
||||||
|
ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p ${mountCfg.mountPoint}";
|
||||||
|
ExecStart = concatStringsSep " " ([
|
||||||
|
"${pkgs.rclone}/bin/rclone mount"
|
||||||
|
":webdav:${mountCfg.remotePath}"
|
||||||
|
"${mountCfg.mountPoint}"
|
||||||
|
"--webdav-url=${mountCfg.webdavUrl}"
|
||||||
|
"--webdav-vendor=${mountCfg.webdavVendor}"
|
||||||
|
"--webdav-user=${mountCfg.username}"
|
||||||
|
"--allow-other"
|
||||||
|
"--vfs-cache-mode=${mountCfg.vfsCacheMode}"
|
||||||
|
"--dir-cache-time=${mountCfg.dirCacheTime}"
|
||||||
|
"--poll-interval=${mountCfg.pollInterval}"
|
||||||
|
"--log-level=${mountCfg.logLevel}"
|
||||||
|
] ++ mountCfg.extraArgs);
|
||||||
|
ExecStop = "${pkgs.fuse}/bin/fusermount -uz ${mountCfg.mountPoint}";
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = "10s";
|
||||||
|
EnvironmentFile = mountCfg.environmentFile;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.roles.rclone-mount = {
|
||||||
|
enable = mkEnableOption "Enable rclone WebDAV mounts";
|
||||||
|
|
||||||
|
mounts = mkOption {
|
||||||
|
type = types.attrsOf (types.submodule {
|
||||||
|
options = {
|
||||||
|
webdavUrl = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "WebDAV server URL (e.g., https://webdav.torbox.app)";
|
||||||
|
};
|
||||||
|
|
||||||
|
webdavVendor = mkOption {
|
||||||
|
type = types.enum [ "other" "nextcloud" "owncloud" "sharepoint" "sharepoint-ntlm" "fastmail" ];
|
||||||
|
default = "other";
|
||||||
|
description = "WebDAV server vendor for optimizations";
|
||||||
|
};
|
||||||
|
|
||||||
|
username = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "WebDAV username (often email address)";
|
||||||
|
};
|
||||||
|
|
||||||
|
environmentFile = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
description = ''
|
||||||
|
Path to environment file containing RCLONE_WEBDAV_PASS.
|
||||||
|
The password should be obscured using: rclone obscure <password>
|
||||||
|
File format: RCLONE_WEBDAV_PASS=<obscured_password>
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
mountPoint = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "Local mount point path";
|
||||||
|
};
|
||||||
|
|
||||||
|
remotePath = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "/";
|
||||||
|
description = "Remote path on WebDAV server to mount";
|
||||||
|
};
|
||||||
|
|
||||||
|
vfsCacheMode = mkOption {
|
||||||
|
type = types.enum [ "off" "minimal" "writes" "full" ];
|
||||||
|
default = "full";
|
||||||
|
description = ''
|
||||||
|
VFS cache mode. For streaming media, 'full' is recommended.
|
||||||
|
- off: No caching (direct reads/writes)
|
||||||
|
- minimal: Cache open files only
|
||||||
|
- writes: Cache writes and open files
|
||||||
|
- full: Full caching of all files
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
dirCacheTime = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "5m";
|
||||||
|
description = "Time to cache directory entries";
|
||||||
|
};
|
||||||
|
|
||||||
|
pollInterval = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "1m";
|
||||||
|
description = "Poll interval for remote changes";
|
||||||
|
};
|
||||||
|
|
||||||
|
logLevel = mkOption {
|
||||||
|
type = types.enum [ "DEBUG" "INFO" "NOTICE" "ERROR" ];
|
||||||
|
default = "INFO";
|
||||||
|
description = "rclone log level";
|
||||||
|
};
|
||||||
|
|
||||||
|
extraArgs = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
description = "Extra arguments to pass to rclone mount";
|
||||||
|
};
|
||||||
|
|
||||||
|
requiresMountsFor = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
description = ''
|
||||||
|
List of mount points that must be available before this service starts.
|
||||||
|
Use this when the mount point's parent is on a ZFS pool or other filesystem
|
||||||
|
that may not be mounted at boot time.
|
||||||
|
Example: [ "/media" ] to wait for the media ZFS pool to mount.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
default = {};
|
||||||
|
description = "Attribute set of rclone WebDAV mounts to configure";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
# Ensure FUSE is available
|
||||||
|
environment.systemPackages = [ pkgs.rclone pkgs.fuse ];
|
||||||
|
programs.fuse.userAllowOther = true;
|
||||||
|
|
||||||
|
# Create systemd services for each mount
|
||||||
|
systemd.services = mapAttrs' (name: mountCfg:
|
||||||
|
nameValuePair "rclone-mount-${name}" (mkMountService name mountCfg)
|
||||||
|
) cfg.mounts;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -35,12 +35,12 @@
|
|||||||
# a) Configure builders in configuration.nix:
|
# a) Configure builders in configuration.nix:
|
||||||
# roles.remote-build.builders = [
|
# roles.remote-build.builders = [
|
||||||
# {
|
# {
|
||||||
# hostName = "zix790prors";
|
# hostName = "zix790prors.oglehome";
|
||||||
# maxJobs = 16; # Number of parallel build jobs
|
# maxJobs = 16; # Number of parallel build jobs
|
||||||
# speedFactor = 3; # Higher = prefer this builder
|
# speedFactor = 3; # Higher = prefer this builder
|
||||||
# }
|
# }
|
||||||
# {
|
# {
|
||||||
# hostName = "john-endesktop";
|
# hostName = "john-endesktop.oglehome";
|
||||||
# maxJobs = 1; # Conservative for busy machines
|
# maxJobs = 1; # Conservative for busy machines
|
||||||
# speedFactor = 1;
|
# speedFactor = 1;
|
||||||
# }
|
# }
|
||||||
|
|||||||
Reference in New Issue
Block a user