cfg_read_secret is invoked via $() inside _cfg_edit_one, so the echo that
advanced the cursor after hidden input landed on the capture pipe instead of
the terminal — every secret value stored with pos config on a real TTY got a
leading newline (e.g. AI_GEMINI_API_KEY="\n<key>"), which neither cfg_value
nor the ai/telegram load_config parsers could read back: the menu showed
'(not set)' and pos ai gemini kept demanding a key.
Fix: send that newline to stderr (>&2). Defense in depth: cfg_write and
write_config_key now strip CR and truncate multi-line pastes, and the readers
(cfg_value, ai + telegram load_config) strip CR. Reproduced and verified on a
pseudo-TTY; piped-stdin tests never caught it because non-TTY input skips the
stty/echo path.
- Drop sender 'config'/'config set' subcommand — redundant with 'pos config
telegram' (same POS_CONFIG registry: masked token input/display, chat-id
validation, chmod 600); sender/listener error hints now point there.
- Listener: load_map now uses a \x1f delimiter, so mapped /commands
containing '|' are no longer truncated at the first pipe.
- entertainment send: forward extra plugin args (previously dropped); pass
'--' before the message so leading '-' plugin output isn't parsed as an
option.
- write_config_key/cfg_write: replace unescaped sed writes with grep-v+append
so values with &, |, \ round-trip safely.
- sync_systemd: daemon-reload after removing timer units.
- config-ui: 'digits' validation accepts negative group/supergroup chat ids.
Under 'set -u', 'local field=$1 key flags desc rest example' leaves the
unassigned locals UNSET rather than empty, so any POS_CONFIG key without
an '::example' hint (e.g. the whole system scope) hit 'example: unbound
variable' and aborted the scope listing. Initialize all locals explicitly.
POS_CONFIG fields gain an optional 'desc::example' value-format hint shown
in the editor ('e.g. weather,5m joke,10m' under ENABLED). Harden
entertainment-lib's plugin_marker/plugin_keys grep|sed pipelines with
'|| true': under set -o pipefail a grep miss exited 1 and set -e aborted
config_keys mid-scan, so installed layouts (/usr/local/bin mixes libs and
plugins) silently lost all plugin keys in pos config.
Add bin/pos-config + lib/config-ui.sh: reads the '# POS_CONFIG:' headers
across tools into a unified runtime config (~/.config/linux_post_install/
<scope>.env, chmod 600). Secret masking with show/hide, digits:/num:/url:
validation, '-' clears, blank keeps; '*plugins' expands entertainment
plugin vars from entertainment-lib.sh. pos config with no scope opens a
picker (scope list when not a TTY); completion offers scopes from the
registry at runtime.
gen-docs.sh now handles category-less tools (pos-config) instead of
emitting pos-config-config; fix set -e/pipefail abort that truncated the
header registry mid-read. install.sh ships config-ui.sh.