mirror of
https://github.com/oqyude/nixos.git
synced 2026-10-09 21:47:12 +03:00
tty added and opencode fixed
This commit is contained in:
+44
-14
@@ -3,10 +3,17 @@
|
||||
# Mirrors ~/.config/opencode/ on the current workstation.
|
||||
# Imported by home/server.nix (sapphira). Auto-enables programs.opencode.
|
||||
#
|
||||
# Three files this module owns on disk (via xdg.configFile):
|
||||
# Files this module owns on disk:
|
||||
# ~/.config/opencode/opencode.json <- programs.opencode.settings
|
||||
# ~/.config/opencode/tui.json <- programs.opencode.tui
|
||||
# ~/.config/opencode/oh-my-openagent.json <- oh-my-openagent plugin config
|
||||
# ~/.omo/omo.jsonc <- oh-my-openagent plugin's PRIMARY
|
||||
# runtime config (>= v5.x reads
|
||||
# only this path; the legacy
|
||||
# ~/.config/opencode/oh-my-openagent.json
|
||||
# is read only by the migration shim).
|
||||
# ~/.config/opencode/oh-my-openagent.json <- legacy mirror, kept so omo doctor
|
||||
# and any downgrade that re-reads
|
||||
# the old path see the same content.
|
||||
#
|
||||
# Override any field in the importing module if needed.
|
||||
{
|
||||
@@ -17,10 +24,21 @@
|
||||
...
|
||||
}:
|
||||
let
|
||||
# Body of ~/.config/opencode/oh-my-openagent.json.
|
||||
# Body of ~/.omo/omo.jsonc (and the legacy mirror).
|
||||
# Loaded by the oh-my-openagent opencode plugin on startup.
|
||||
#
|
||||
# Plugins >= 5.x resolve their config from ~/.omo/omo.jsonc, NOT from
|
||||
# ~/.config/opencode/oh-my-openagent.json. Pinning _migrations here prevents
|
||||
# the 2026-07-opencode-config-unification migration from running on every
|
||||
# startup and re-backing-up the file (which would otherwise leave us with
|
||||
# an empty omo.jsonc that drops every agent override — see the journal entry
|
||||
# below).
|
||||
ohMyOpenagentConfig = {
|
||||
"$schema" = "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json";
|
||||
"$schema" = "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/omo.schema.json";
|
||||
_migrations = [
|
||||
"2026-07-opencode-config-unification"
|
||||
"2026-08-reasoning-unification"
|
||||
];
|
||||
|
||||
agents = {
|
||||
sisyphus = {
|
||||
@@ -270,17 +288,29 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
# ~/.config/opencode/oh-my-openagent.json — read by the plugin on startup.
|
||||
# ~/.omo/omo.jsonc — primary file the oh-my-openagent plugin reads at runtime
|
||||
# (>= v5.x). This path lives outside XDG_CONFIG_HOME (~/.config), so use
|
||||
# home.file rather than xdg.configFile.
|
||||
#
|
||||
# NOTE: the oh-my-openagent plugin runs a `2026-07-opencode-config-unification`
|
||||
# migration on every startup that backs up this file and tries to write its
|
||||
# consolidated form to ~/.omo/omo.jsonc. The backup directory name embeds the
|
||||
# source's content-hashed store path; because HM does not delete the previous
|
||||
# generation's store path until garbage collection, the same path is reused on
|
||||
# every retry and omo logs "Migration backup path already exists" forever.
|
||||
# Recovery: `rm -rf ~/.omo/migration-backup-*` and let omo retry; if the
|
||||
# migration keeps failing on the same backup path, the plugin/omo version
|
||||
# probably expects a new schema and this config needs updating.
|
||||
# ~/.config/opencode/oh-my-openagent.json is kept as a legacy mirror so
|
||||
# `omo doctor`, the migration shim, and any future downgrade that re-reads the
|
||||
# old path see the same content.
|
||||
#
|
||||
# MIGRATION TRAP (do not just point back at the legacy path):
|
||||
# The plugin runs a `2026-07-opencode-config-unification` migration on every
|
||||
# startup that backs up ~/.omo/omo.jsonc and tries to rewrite it from
|
||||
# ~/.config/opencode/oh-my-openagent.json. The backup directory name embeds
|
||||
# the source's content-hashed store path; because HM does not delete the
|
||||
# previous generation's store path until garbage collection, the same path
|
||||
# is reused on every retry and omo logs "Migration backup path already
|
||||
# exists" forever — meanwhile the user's agent overrides disappear and the
|
||||
# plugin's built-in fallback chain (which references providers like
|
||||
# kimi-for-coding that opencode's provider registry no longer ships) gets
|
||||
# picked instead, surfacing as `ProviderModelNotFoundError:
|
||||
# kimi-for-coding/kimi-for-coding-highspeed` on every subagent spawn.
|
||||
# Pinning _migrations above makes the migration a no-op; the omo.jsonc
|
||||
# below is the actual config the plugin sees.
|
||||
home.file."${config.home.homeDirectory}/.omo/omo.jsonc".text = builtins.toJSON ohMyOpenagentConfig;
|
||||
xdg.configFile."opencode/oh-my-openagent.json".text = builtins.toJSON ohMyOpenagentConfig;
|
||||
|
||||
# Same extras on the user's PATH too, so `omo doctor` and standalone invocations
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
./samba.nix
|
||||
./syncthing.nix
|
||||
./systemd.nix
|
||||
./ttyd.nix
|
||||
./uptime-kuma.nix
|
||||
# ../containers/remnawave.nix
|
||||
# ./coturn.nix
|
||||
|
||||
@@ -182,6 +182,50 @@ in
|
||||
};
|
||||
};
|
||||
};
|
||||
# tty.zeroq.su — web-shell (ttyd) behind Authelia forward-auth.
|
||||
# ttyd listens on 127.0.0.1:7681 only (modules/server/ttyd.nix), so
|
||||
# nginx is the only ingress. Same auth_request / 401→302 wiring as
|
||||
# vtimeline.zeroq.su above — the wildcard rule `*.zeroq.su` in
|
||||
# modules/server/authelia.nix already covers this subdomain under
|
||||
# `one_factor`, so no policy change is needed.
|
||||
"tty.zeroq.su" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
locations = {
|
||||
"/" = {
|
||||
proxyPass = "http://127.0.0.1:7681";
|
||||
proxyWebsockets = true;
|
||||
extraConfig = ''
|
||||
auth_request /authelia;
|
||||
auth_request_set $authelia_user $upstream_http_remote_user;
|
||||
# Same 401→302 trick as vtimeline.zeroq.su: a bare 302 from
|
||||
# Authelia surfaces to the client as a 500 ("auth request
|
||||
# unexpected status"), so we rewrite the response status to
|
||||
# a 302 pointing at the authelia login UI with an absolute
|
||||
# `$scheme://$host$request_uri` so the post-login `rd`
|
||||
# lands the user back on tty.zeroq.su, not on
|
||||
# authelia.zeroq.su/<path>.
|
||||
error_page 401 =302 https://authelia.zeroq.su/?rd=$scheme://$host$request_uri;
|
||||
'';
|
||||
};
|
||||
"= /authelia" = {
|
||||
extraConfig = ''
|
||||
internal;
|
||||
proxy_pass http://127.0.0.1:9091/api/authz/forward-auth;
|
||||
proxy_set_header X-Original-URL $request_uri;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Method $request_method;
|
||||
proxy_set_header X-Forwarded-Uri $request_uri;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
# Same Accept-forces-401 trick as vtimeline.zeroq.su — see
|
||||
# the comment there for why Authelia's default 302 is
|
||||
# harmful here.
|
||||
proxy_set_header Accept "application/json";
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
# Authelia login UI — same podman container on 127.0.0.1:9091 as the
|
||||
# forward-auth endpoint above, just exposed on a separate vhost so
|
||||
# Authelia has a stable absolute URL to redirect users to. Authelia
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
# ttyd — web-терминал на 127.0.0.1:7681 (loopback only), за
|
||||
# reverse-proxy vhost `tty.zeroq.su` (modules/server/nginx.nix).
|
||||
# Nginx + authelia — единственный ingress; ttyd снаружи недоступен.
|
||||
#
|
||||
# Рантайм-наблюдение (Oct 2026): первый прогон с
|
||||
# `entrypoint = [ pkgs.bashInteractive ]` поднимал bash на slave-pty
|
||||
# (/dev/pts/N создавался, fd 0/1/2 указывали туда), но bash НЕ входил
|
||||
# в interactive mode — wchan уходил в `poll_schedule_timeout`, PS1 не
|
||||
# рисовался, xterm.js канвас оставался пустым. Без явного `-i` и без
|
||||
# TERM в env systemd-юнита bash имеет право считать себя non-interactive
|
||||
# даже при isatty(0)=true. Лечится обоими сразу:
|
||||
# 1. entrypoint = login-shell пользователя + явный `-i`;
|
||||
# 2. systemd.services.ttyd.environment с TERM и HOME.
|
||||
#
|
||||
# Дополнительно: переключаемся с bash на zsh потому, что у `oqyude`
|
||||
# в /etc/passwd shell = /run/current-system/sw/bin/zsh. Подсовывать
|
||||
# bash шеллу, чей home настроен на zsh, значит терять dotfiles и
|
||||
# PATH-модификации, загружаемые при штатном login.
|
||||
#
|
||||
# `-l` (login-mode) подгружает полный login-env NixOS: /etc/zshenv,
|
||||
# /etc/zprofile, ~/.zprofile и через /etc/profile — все
|
||||
# /etc/profile.d/*.sh, где NixOS выставляет PATH, XDG_DATA_DIRS,
|
||||
# NIX_PATH и т.п. Без `-l` PATH остаётся тем узким, что в
|
||||
# systemd-Environment (coreutils/findutils/grep/sed/systemd) — без
|
||||
# nix/git/docker/man/…, что и было видно в первом прогоне.
|
||||
#
|
||||
# Замечание про PATH: NixOS раскладывает login-env по двум НЕСВЯЗАННЫМ
|
||||
# стекам — bash-стейку (/etc/profile → set-environment с PATH/XDG/
|
||||
# NIX_PATH/GTK_PATH/INFOPATH/…) и zsh-стейку (/etc/zprofile + /etc/zshrc).
|
||||
# Zsh сам по себе /etc/profile не source'ит — поэтому PATH внутри
|
||||
# ttyd-shell остаётся урезанным (только coreutils/findutils/grep/sed/
|
||||
# systemd от systemd.Environment). Это сознательно НЕ лечится здесь
|
||||
# (см. todo D2 в docs/arch/todo.md если файл существует): вопрос
|
||||
# глобальный, должен решаться или через environment.etc."zshrc.local"
|
||||
# в modules/essentials/, или через ~/.zshenv у пользователя. На этом
|
||||
# этапе ограничиваемся `zsh -i -l` — оно уже подгружает /etc/zprofile
|
||||
# (cd /etc/nixos + fastfetch) и /etc/zshrc (oh-my-zsh, aliases,
|
||||
# compinit, syntax-highlighting). Это то, что просили.
|
||||
#
|
||||
# Решения, отступающие от дефолтов upstream `services.ttyd`:
|
||||
#
|
||||
# user = "oqyude"
|
||||
# Default — root. С authelia `one_factor` (один пароль) это
|
||||
# эквивалент "root-shell за единым паролем". Идём от
|
||||
# пользователя-администратора (modules/users.nix: oqyude,
|
||||
# isNormalUser, wheel/disk/audio/networkmanager/libvirtd). Цена:
|
||||
# ttyd-юзер не правит systemd-юниты напрямую — для этого sudo.
|
||||
#
|
||||
# interface = "127.0.0.1"
|
||||
# Биндим исключительно на loopback. Роутер пробрасывает 443 (плюс
|
||||
# 80/22/8443/22000) на sapphira, но сам ttyd наружу выставлять
|
||||
# нельзя: это "SSH на порту 7681 без TLS". Доступ — ТОЛЬКО через
|
||||
# 127.0.0.1 + nginx-proxy.
|
||||
#
|
||||
# entrypoint = [ userShell "-i" "-l" ]
|
||||
# userShell — login-shell пользователя из users.users.<name>.shell
|
||||
# (fallback = /run/current-system/sw/bin/bash, если атрибут не
|
||||
# выставлен). `-i` форсит interactive mode — без него bash/zsh
|
||||
# могут стартовать non-interactive при наличии pty в fd 0.
|
||||
# `-l` (login-shell) подгружает /etc/zshenv, /etc/zprofile,
|
||||
# /etc/zshrc — login-env zsh-стейка NixOS: cd /etc/nixos +
|
||||
# fastfetch, oh-my-zsh, aliases, compinit, syntax-highlighting.
|
||||
# PATH остаётся урезанным — см. блок про PATH-замечание выше.
|
||||
#
|
||||
# writeable = true
|
||||
# Upstream-assertion требует явного значения.
|
||||
#
|
||||
# checkOrigin = true
|
||||
# Без него WebSocket-апгрейд от любого origin проходит — XSS на
|
||||
# любом *.zeroq.su vhost превращается в RCE через ttyd.
|
||||
#
|
||||
# maxClients = 0 (default)
|
||||
# Решение владельца: без лимита; ttyd разделяет TTY между всеми
|
||||
# WS-сессиями, состояние общее.
|
||||
#
|
||||
# Шрифт (clientOptions.fontFamily): НЕ задаём. Ttyd генерирует HTML
|
||||
# с fallback-стеком `courier-new, courier, monospace`, который на
|
||||
# клиенте отрисовывается через CSS-generic → monospace (Liberation Mono
|
||||
# на Linux, Menlo на macOS, Consolas/Cascadia на Windows). Fira Code
|
||||
# (предыдущее значение) требовал установленный на клиенте шрифт; если
|
||||
# его нет, xterm.js падает на generic mono, но в Canvas API без
|
||||
# CSS-стека рендеринг становится нестабильным. Явный `monospace` =
|
||||
# "всегда рисуется системным шрифтом". Если потом захочется Fira
|
||||
# Code / JetBrains Mono / другой — задать `fontFamily = "Fira Code,
|
||||
# monospace"` (с trailing monospace, чтобы Canvas нашёл fallback).
|
||||
#
|
||||
# systemd-окружение: TERM и HOME пробрасываются явно. systemd по
|
||||
# дефолту не выставляет TERM (User=oqyude даёт только euid), а
|
||||
# bash/zsh полагаются на TERM для history-substitution и line-editing.
|
||||
# HOME нужен zsh для определения `ZDOTDIR` (~/.zshrc и т.п.).
|
||||
let
|
||||
cfg = config.services.ttyd;
|
||||
# `users.users.<name>.shell` в NixOS 26.x — это пакет (derivation),
|
||||
# а в 24.x был путём. `lib.getExe` умеет оба: derivation → bin/<name>,
|
||||
# string → возвращает как есть.
|
||||
userShellExe =
|
||||
let shell = config.users.users.${cfg.user}.shell or "/run/current-system/sw/bin/bash";
|
||||
in if builtins.isString shell then shell else lib.getExe shell;
|
||||
in
|
||||
{
|
||||
services.ttyd = {
|
||||
enable = true;
|
||||
port = 7681;
|
||||
interface = "127.0.0.1";
|
||||
user = "oqyude";
|
||||
entrypoint = [ userShellExe "-i" "-l" ];
|
||||
writeable = true;
|
||||
checkOrigin = true;
|
||||
maxClients = 0;
|
||||
clientOptions = {
|
||||
fontSize = "14";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.ttyd.environment = {
|
||||
HOME = "/home/oqyude";
|
||||
TERM = "xterm-256color";
|
||||
USER = "oqyude";
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user