feat: pos ai gemini — CLI + Telegram AI chat (ask, chat, models)

- bin/pos-ai-gemini: one-shot ask (stdout-only, pipe-friendly), interactive
  multi-turn chat REPL (q/Ctrl+C, /reset), models list; --model override,
  default gemini-2.5-flash; key via x-goog-api-key header, never printed.
- Config scope 'ai' (AI_GEMINI_API_KEY secret, AI_GEMINI_MODEL) in
  ~/.config/linux_post_install/ai.env via 'pos config ai'; config/ai.env
  template installed no-clobber by postinstall.
- Telegram listener: non-command text starting with 'ai ' (case-insensitive)
  is answered by Gemini via 'pos ai gemini ask'; owner-chat only, errors
  reply with the pos config ai hint. Future intents slot in as case arms.
- ai-gemini added to INTERACTIVE_CMDS (chat reads stdin).
- Docs: POS.md ai section + listener bridge, howto/ai.md, HOWTO/README
  index rows, bin/pos usage example; make gen refreshed context/completions.
This commit is contained in:
Your Name
2026-08-09 14:40:15 +00:00
parent f8b5c08ee5
commit 23eede637a
13 changed files with 378 additions and 23 deletions
+23 -17
View File
@@ -10,19 +10,19 @@
<!-- GEN:START docmap -->
| ## 1. Project Overview | 2843 |
| ## 2. Directory Structure | 44181 |
| ## 3. Installation Flow | 182234 |
| ## 4. The `pos` CLI System | 235297 |
| ## 5. Shared Library — `lib/common.sh` | 298329 |
| ## 6. Docker Compose / ScaleTail | 330372 |
| ## 7. Optional Apps (`apps/`) | 373402 |
| ## 8. Entertainment Module | 403416 |
| ## 9. Systemd Services | 417429 |
| ## 10. Configuration Files | 430455 |
| ## 11. Coding Conventions | 456488 |
| ## 12. Development Workflow | 489541 |
| ## 13. Key File Quick Reference | 542590 |
| ## 14. Common Tasks for Agents | 591615 |
| ## 2. Directory Structure | 44183 |
| ## 3. Installation Flow | 184236 |
| ## 4. The `pos` CLI System | 237300 |
| ## 5. Shared Library — `lib/common.sh` | 301332 |
| ## 6. Docker Compose / ScaleTail | 333375 |
| ## 7. Optional Apps (`apps/`) | 376405 |
| ## 8. Entertainment Module | 406419 |
| ## 9. Systemd Services | 420432 |
| ## 10. Configuration Files | 433459 |
| ## 11. Coding Conventions | 460492 |
| ## 12. Development Workflow | 493545 |
| ## 13. Key File Quick Reference | 546595 |
| ## 14. Common Tasks for Agents | 596621 |
<!-- GEN:END docmap -->
## 1. Project Overview
@@ -58,6 +58,7 @@ Linux_post_install/
├── bin/ # CLI tools — installed to /usr/local/bin/
│ ├── pos # Main dispatcher — smart arg matching to pos-* scripts
<!-- GEN:START tree -->
│ ├── pos-ai-gemini # Chat with Google Gemini (ask, chat, models)
│ ├── pos-communication-telegram-listener # Telegram bot listener: map /command → bash, run them on chat messages
│ ├── pos-communication-telegram-sender # Send Telegram messages/files/links/stickers via Bot API (send, test)
│ ├── pos-config # Interactive editor for the tools' runtime config (reads # POS_CONFIG: registry)
@@ -143,7 +144,8 @@ Linux_post_install/
├── config/
│ ├── authorized_keys # SSH public keys (gitignored)
── entertainment.env # Weather location template (auto-installed by postinstall)
── entertainment.env # Weather location template (auto-installed by postinstall)
│ └── ai.env # Gemini API key + model template (auto-installed by postinstall)
├── compose/
│ └── scale-tail/ # Git submodule → ScaleTail templates (119+ services)
@@ -251,6 +253,7 @@ All non-interactive `pos` commands log output to `~/.local/share/linux_post_inst
| Category | Command | Script | Description |
|----------|---------|--------|-------------|
<!-- GEN:START dispatch -->
| ai | gemini | `pos-ai-gemini` | Chat with Google Gemini (ask, chat, models) |
| communication | telegram-listener | `pos-communication-telegram-listener` | Telegram bot listener: map /command → bash, run them on chat messages |
| communication | telegram-sender | `pos-communication-telegram-sender` | Send Telegram messages/files/links/stickers via Bot API (send, test) |
| | config | `pos-config` | Interactive editor for the tools' runtime config (reads # POS_CONFIG: registry) |
@@ -440,6 +443,7 @@ All `.service` files in `systemd/` are automatically copied to `/etc/systemd/sys
- `~/.config/linux_post_install/entertainment.env` — entertainment plugin defaults: weather location + `ENABLED` auto-trigger list (`plugin, interval` pairs scheduled via `pos entertainment enable/disable`, systemd user timers); auto-installed from `config/entertainment.env` by `postinstall.sh` (no clobber, template printed)
- `~/.config/linux_post_install/system.env` — shared "system" tool settings (loaded by `pos system health` / `pos system backup` via `load_system_env()` in `lib/common.sh`; env already exported wins over the file); template `config/system.env`
- `~/.config/linux_post_install/notify.env` — alerting platform selection (`NOTIFY_PLATFORM=telegram,matrix`, comma-separated = fan out); read by `lib/notify.sh`; template `config/notify.env`
- `~/.config/linux_post_install/ai.env` — Google Gemini config (`AI_GEMINI_API_KEY` secret, `AI_GEMINI_MODEL` default `gemini-2.5-flash`); read by `pos ai gemini`; template `config/ai.env`, auto-installed by postinstall, edit with `pos config ai`
- `~/.bashrc` — Modified by postinstall (PATH, bash completion)
### Feature Flags
@@ -555,8 +559,9 @@ Use conventional prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `chore:`
| `bin/flag-clear` | 21 | Unset a flag |
| `features/autostart.sh` | 14 | Boot-time feature (moved from `bin/`, flag-gated service) |
<!-- GEN:START filetable -->
| `bin/pos` | 272 | CLI dispatcher with smart arg matching + logging + category help |
| `bin/pos-communication-telegram-listener` | 511 | Telegram bot listener: map /command → bash, run them on chat messages |
| `bin/pos` | 277 | CLI dispatcher with smart arg matching + logging + category help |
| `bin/pos-ai-gemini` | 197 | Chat with Google Gemini (ask, chat, models) |
| `bin/pos-communication-telegram-listener` | 526 | Telegram bot listener: map /command → bash, run them on chat messages |
| `bin/pos-communication-telegram-sender` | 220 | Send Telegram messages/files/links/stickers via Bot API (send, test) |
| `bin/pos-config` | 80 | Interactive editor for the tools' runtime config (reads # POS_CONFIG: registry) |
| `bin/pos-docker-compose` | 366 | Docker Compose service manager (ls/up/down/restart/logs/update/config) |
@@ -582,7 +587,7 @@ Use conventional prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `chore:`
| `bin/pos-system-nfs-server` | 134 | Manage the NFS kernel server (status, share/unshare exports, enable/disable) |
| `bin/pos-tree` | 110 | Show the pos CLI command tree: categories, commands, and subcommands |
| `bin/pos-usb-server` | 218 | USB Redirector server control (--ls, --share; prompts when args omitted) |
| `completions/pos.bash` | 280 | Dynamic bash completion |
| `completions/pos.bash` | 282 | Dynamic bash completion |
<!-- GEN:END filetable -->
| `apps/install.sh` | 171 | App install/uninstall picker/orchestrator |
@@ -608,6 +613,7 @@ Use conventional prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `chore:`
| Modify Docker health check | Edit `bin/pos-docker-health` |
| Modify vbox (Docker VM) logic | Edit `bin/pos-docker-vbox` |
| Modify USB forwarding logic | Edit `bin/pos-usb-server` |
| Modify AI/Gemini logic | Edit `bin/pos-ai-gemini` (config scope `ai` via `pos config ai`; `AI_GEMINI_API_KEY`/`AI_GEMINI_MODEL` in `~/.config/linux_post_install/ai.env`) |
| Modify UFW/firewall logic | Edit `bin/pos-system-firewall` |
| Modify pos logging | Edit log setup in `bin/pos` |
| Modify install phases/flags | Edit arg parsing in `install.sh` |
+2
View File
@@ -9,6 +9,7 @@ authoritative one-line reference (every command + flag), see
| Category | What you can do | Guide |
|----------|-----------------|-------|
| `pos ai` | Chat with Google Gemini from CLI or Telegram | [ai](howto/ai.md) |
| `pos network` | IP info, hotspot, scan, port check | [network](howto/network.md) |
| `pos docker` | Compose services, container dashboards, disposable VMs | [docker](howto/docker.md) |
| `pos media` | Download audio/video via yt-dlp | [media](howto/media.md) |
@@ -38,6 +39,7 @@ templates (without overwriting an existing file):
| `system.env` | `pos system health`, `pos system backup` | `BACKUP_SERVICE_ROOTS`, `HEALTH_BACKUP_MAX_AGE_DAYS` |
| `compose.env` | `pos docker compose` | `TS_AUTHKEY`, `TZ`, `DNS_SERVER`, `SERVICES_BASE` |
| `entertainment.env` | `pos entertainment *` | plugin keys (`WEATHER_LAT`…), `ENABLED` |
| `ai.env` | `pos ai gemini` | `AI_GEMINI_API_KEY`, `AI_GEMINI_MODEL` |
```bash
pos config telegram # set TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_ID
+27 -1
View File
@@ -5,6 +5,7 @@
- [The dispatcher — `bin/pos`](#the-dispatcher--binpos)
- [Logging behavior](#logging-behavior)
- [Commands](#commands)
- [ai](#ai)
- [network](#network)
- [docker](#docker)
- [media](#media)
@@ -52,6 +53,31 @@ Every non-interactive `pos` invocation logs to `~/.local/share/linux_post_instal
Category-less tools (`config`, `tree`) live outside any category and are documented in their own `###` sections below.
### ai
**File:** `bin/pos-ai-gemini`
**Purpose:** chat with Google Gemini via the REST API (`generativelanguage.googleapis.com`). One tool, three subcommands: `ask` (one-shot, scriptable), `chat` (interactive multi-turn REPL), and `models` (list `generateContent`-capable ids).
| Command | Behavior |
|---------|----------|
| `pos ai gemini ask "<prompt>"` | One-shot; POSTs `generateContent` and prints **only** the answer text to stdout (pipe/script/Telegram-friendly). The prompt may also be piped in via stdin when no argument is given |
| `pos ai gemini chat` | Interactive REPL with multi-turn history (the `contents[]` array is appended per turn); `q`/`quit`/`exit` or Ctrl+C quit, `/reset` clears the history, empty input re-prompts |
| `pos ai gemini models` | Lists models that support `generateContent` and flags the configured default |
| `pos ai gemini --model <id> …` | Overrides the model for one invocation |
`pos ai gemini` with no subcommand prints usage (never blocks on stdin). `ask`/`chat` time out after 60s per request; on a non-2xx response the API's `error.message` is shown and the tool exits non-zero.
**Configuration** (`~/.config/linux_post_install/ai.env`, edit with `pos config ai`):
| Key | Required | Default | Purpose |
|-----|----------|---------|---------|
| `AI_GEMINI_API_KEY` | yes | — | API key from aistudio.google.com (secret — masked in `pos config ai`) |
| `AI_GEMINI_MODEL` | no | `gemini-2.5-flash` | Model id used by `ask`/`chat`/`models` |
Precedence: `--model` flag > `AI_GEMINI_MODEL` env > config file > `gemini-2.5-flash`. `postinstall.sh` copies the repo's `config/ai.env` template to `~/.config/linux_post_install/ai.env` on install (no clobber). Dependencies: `curl` + `jq` (both in `preinstall.sh` PACKAGES).
**Telegram bridge:** the Telegram listener forwards non-command messages starting with `ai ` (case-insensitive) to `pos ai gemini ask` and replies with the model's answer — see [communication → listener](#communication).
### network
| Command | File | Purpose | Configuration |
@@ -228,7 +254,7 @@ The bot token is a secret — it is stored only in `~/.config/linux_post_install
| `pos communication telegram listener --sync-commands` | Push the mapped `/commands` to the bot's `/` menu (`setMyCommands`) — also run automatically after every map edit, on `--enable`, and at daemon start |
| `pos communication telegram listener --run` | Run the polling loop in the foreground (what the service executes) |
The map file is re-read for every message — edits apply without a restart. The listener only reacts to the owner chat (`TELEGRAM_CHAT_ID`); anyone else's message is ignored. `/help` lists mapped commands; an unmapped command replies "Unknown command". Commands run as your user via `timeout 60 bash -c "…"` (stdout + stderr are replied, truncated to ~3800 chars; empty output → `OK`), so `sudo` inside them needs a NOPASSWD rule. A map value prefixed with `@quiet ` runs the command but does NOT reply — for commands that already send their own notification (e.g. `/status=@quiet pos system health --send`), avoiding a double message. `--enable` warns if linger is off — the service stops when you log out unless you run `sudo loginctl enable-linger $(whoami)`.
The map file is re-read for every message — edits apply without a restart. The listener only reacts to the owner chat (`TELEGRAM_CHAT_ID`); anyone else's message is ignored. `/help` lists mapped commands; an unmapped command replies "Unknown command". Non-command text starting with `ai ` (case-insensitive, e.g. `ai what is Nvidia`) is forwarded to Gemini via `pos ai gemini ask` and the answer is replied verbatim; an AI failure replies the error plus a `pos config ai` hint. Commands run as your user via `timeout 60 bash -c "…"` (stdout + stderr are replied, truncated to ~3800 chars; empty output → `OK`), so `sudo` inside them needs a NOPASSWD rule. A map value prefixed with `@quiet ` runs the command but does NOT reply — for commands that already send their own notification (e.g. `/status=@quiet pos system health --send`), avoiding a double message. `--enable` warns if linger is off — the service stops when you log out unless you run `sudo loginctl enable-linger $(whoami)`.
Map entries may carry an optional **description** shown in the bot's `/` menu: `/cmd::short description=bash command` (the description falls back to the bash command, truncated to ~40 chars, when omitted). After every add/edit/remove the command list is pushed to the bot via `setMyCommands`, so the menu stays in sync; an empty map clears the menu. Telegram only registers lowercase `[a-z0-9_]` names (132 chars) — commands like `/Status` or `/my-cmd` are skipped from the menu with a warning but still resolve when typed.
+1 -1
View File
@@ -4,7 +4,7 @@ Everything in this folder is reference material for the `Linux_post_install` pro
| Document | What it covers |
|----------|----------------|
| [HOWTO.md](HOWTO.md) | Hands-on guides per category (network, docker, media, system, ssh, usb, communication, entertainment): flags, recipes, config, troubleshooting |
| [HOWTO.md](HOWTO.md) | Hands-on guides per category (ai, network, docker, media, system, ssh, usb, communication, entertainment): flags, recipes, config, troubleshooting |
| [SCRIPTS.md](SCRIPTS.md) | Core installer scripts: `install.sh`, `preinstall.sh`, `postinstall.sh`, `lib/common.sh`, `lib/flags.sh`, `lib/entertainment-lib.sh`, `features/autostart.sh` — purpose, how each works, configuration |
| [POS.md](POS.md) | The `pos` CLI: dispatcher, every `pos-*` command, Docker Compose / ScaleTail config, legacy wrappers, flag CLIs |
| [APPS.md](APPS.md) | Optional apps: `apps/install.sh` picker, installer conventions, full app catalog |
+92
View File
@@ -0,0 +1,92 @@
# How-To: `pos ai`
Chat with Google Gemini — from the terminal and through the Telegram bot.
Tools: `gemini` (`ask`, `chat`, `models`).
| Tool | What it does |
|------|--------------|
| `pos ai gemini ask "<prompt>"` | One-shot answer to stdout (scriptable) |
| `pos ai gemini chat` | Interactive multi-turn conversation |
| `pos ai gemini models` | List available model ids |
---
## First run — get a key, configure, chat
1. Get a free API key from https://aistudio.google.com/apikey (requires a Google
account).
2. Configure it (masked input):
```bash
pos config ai # enter AI_GEMINI_API_KEY
```
3. Test:
```bash
pos ai gemini ask "Explain DNS in one line"
pos ai gemini models # verify the default model id is live
pos ai gemini chat # multi-turn conversation
```
`ai.env` lives at `~/.config/linux_post_install/ai.env` (chmod 600); `pos config ai`
is the only place the key is written. The key is never printed by `pos`.
## From the Telegram bot
Once `pos ai gemini ask` works, any non-command message starting with `ai ` is
answered by the model — no bot map entry needed:
```
you: ai what is Nvidia
bot: NVIDIA is a company best known for GPUs...
```
The bridge lives in the Telegram listener's `handle_message` (it calls
`pos ai gemini ask`); only the owner chat is served, so your key stays private.
Set a different model per message:
```
you: ai --model gemini-2.5-flash explain a Raft consensus log
```
## Recipes
- **Answer from a file:** `pos ai gemini ask "$(cat notes.txt)"`
- **Pipe into it:** `echo "fix this: $(cat error.log)" | pos ai gemini ask`
- **Answer in a cron job:** `pos ai gemini ask "summarize today's git log" > /tmp/ai_digest.txt`
- **Change the default model:**
```bash
pos config ai # set AI_GEMINI_MODEL, or:
AI_GEMINI_MODEL=gemini-2.5-flash pos ai gemini ask "hi"
```
## How it works
- `ask` POSTs `contents:[{role:user, parts:[{text:"…"}]}]` to
`https://generativelanguage.googleapis.com/v1beta/models/<model>:generateContent`
with the key in the `x-goog-api-key` header, and prints
`.candidates[0].content.parts[].text` — nothing else.
- `chat` keeps the whole conversation in memory as a growing `contents[]` array,
so later turns have earlier context. `/reset` drops it.
- On a non-2xx response the API's `error.message` is shown and the exit code is
non-zero — so scripts can rely on `ask` failing loudly.
## Troubleshooting
- `ask` errors "No Gemini API key — run 'pos config ai'" → the key isn't set
(or `ai.env` isn't readable). Run `pos config ai`.
- `API error 400` → the model id is wrong or the prompt is too long for the
model's context window; check `pos ai gemini models`.
- `API error 429` → rate limit (free tier); wait and retry, or use a different
model.
- Nothing in Telegram for `ai …` → the listener daemon must be running
(`pos communication telegram listener --status`); the bot token and owner
chat id must match `pos config telegram`.
---
## Related
- Reference: [DOC/POS.md → ai](../POS.md#ai)
- Telegram bridge context: [communication.md](communication.md)