feat: pos network download — aria2 RPC daemon + queue control (add/torrent/metalink, watch, limits)

Persistent aria2c as a systemd user service (pos-aria2.service, enable --now,
linger warning) on localhost:6800 with a generated RPC secret in
~/.config/linux_post_install/download.env (chmod 600, env override). 18
commands: start/stop/status, add/torrent/metalink (auto-start, --tmux live
view), list/info/files/peers, pause/resume/remove/purge/move, limit/set,
watch (2s live repoll). JSON built via jq -nc --arg, never string
interpolation. Deps: aria2 in preinstall PACKAGES, aria2c/jq/curl guards
before --help. Docs: POS.md + howto/network.md + indices + Common Tasks row.
This commit is contained in:
Your Name
2026-08-12 01:55:16 -04:00
parent ac44f972dc
commit c5dd6466ba
9 changed files with 657 additions and 18 deletions
+2
View File
@@ -57,6 +57,8 @@ summary (newest last).
- **2026-08-07** — `pos system health --send` notification-only; listener `@quiet` prefix (run mapped command without replying, for commands that self-notify). `/status=@quiet pos system health --send` = exactly one digest. - **2026-08-07** — `pos system health --send` notification-only; listener `@quiet` prefix (run mapped command without replying, for commands that self-notify). `/status=@quiet pos system health --send` = exactly one digest.
- **2026-08-12** — `pos network download` (`bin/pos-network-download`) — aria2 JSON-RPC daemon + queue control. **Daemon:** persistent `aria2c` as a systemd **user** service (`pos-aria2.service`, `${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user`, `enable --now` + linger warning on headless boxes), `--rpc-listen-port=6800`, generated `RPC_SECRET` in `~/.config/linux_post_install/download.env` (chmod 600, env override), unit flags `--continue=true --max-connection-per-server=16 --split=16 --seed-time=0 --dir=$HOME/Downloads`. **Commands (18):** `start`/`stop`/`status` (+ bare overview = status+list), `add <url…> [--dir --out --split --tmux]`, `torrent <file|magnet…> [--dir --seed --tmux]` (base64 `addTorrent`), `metalink <file|url> [--tmux]`, `list` (active/waiting/stopped table), `info`/`files`/`peers <gid>`, `pause|resume|remove [gid|all]` (`--force``force*`), `purge`, `move <gid> <pos>`, `limit [gid] <speed>` (`--upload`, 0=unlimited, `2M`/`512K`), `set <k=v…> [--gid]`, `watch [gid]` (2s live repoll; exits when that gid completes). `--tmux` opens a detached `dl-<name>` session running `watch <gid>` (name from `--out`/URL basename, sanitized, 40-char truncate, `-2` on collision; closes itself on completion). Deps: `aria2c`/`jq`/`curl` guards before `--help`; `aria2` added to preinstall PACKAGES. No stdin → not in `INTERACTIVE_CMDS`. JSON built with `jq -nc --arg` (never string interpolation — fixes JSON-quote bugs); `# POS_SUBCMDS:` (18) + `# POS_FLAGS:` → completions. Test seams `RPC_PORT`/`RPC_SECRET`/`DOWNLOAD_DIR`/`USER_SYSTEMD_DIR`/`ACTIVE_MARKER`; 76-case stub-PATH behavior suite green (unit content, secret 600, add→gid, tables, queue ops, error paths). Docs: POS.md network row+detail, howto/network.md section, HOWTO index, AGENT_Context Common Tasks row. `make gen && make check` green.
## Now ## Now
- (none — Tier 1 shipped: `pos system health`, `lib/notify.sh`, digest timer) - (none — Tier 1 shipped: `pos system health`, `lib/notify.sh`, digest timer)
+20 -16
View File
@@ -10,19 +10,19 @@
<!-- GEN:START docmap --> <!-- GEN:START docmap -->
| ## 1. Project Overview | 2843 | | ## 1. Project Overview | 2843 |
| ## 2. Directory Structure | 44188 | | ## 2. Directory Structure | 44189 |
| ## 3. Installation Flow | 189241 | | ## 3. Installation Flow | 190242 |
| ## 4. The `pos` CLI System | 242310 | | ## 4. The `pos` CLI System | 243312 |
| ## 5. Shared Library — `lib/common.sh` | 311342 | | ## 5. Shared Library — `lib/common.sh` | 313344 |
| ## 6. Docker Compose / ScaleTail | 343385 | | ## 6. Docker Compose / ScaleTail | 345387 |
| ## 7. Optional Apps (`apps/`) | 386415 | | ## 7. Optional Apps (`apps/`) | 388417 |
| ## 8. Entertainment Module | 416429 | | ## 8. Entertainment Module | 418431 |
| ## 9. Systemd Services | 430442 | | ## 9. Systemd Services | 432444 |
| ## 10. Configuration Files | 443469 | | ## 10. Configuration Files | 445471 |
| ## 11. Coding Conventions | 470502 | | ## 11. Coding Conventions | 472504 |
| ## 12. Development Workflow | 503555 | | ## 12. Development Workflow | 505557 |
| ## 13. Key File Quick Reference | 556611 | | ## 13. Key File Quick Reference | 558614 |
| ## 14. Common Tasks for Agents | 612639 | | ## 14. Common Tasks for Agents | 615643 |
<!-- GEN:END docmap --> <!-- GEN:END docmap -->
## 1. Project Overview ## 1. Project Overview
@@ -76,6 +76,7 @@ Linux_post_install/
│ ├── pos-media-mp3 # Download audio as MP3 (yt-dlp) │ ├── pos-media-mp3 # Download audio as MP3 (yt-dlp)
│ ├── pos-media-mp4 # Download video as MP4 (smart/interactive format select) │ ├── pos-media-mp4 # Download video as MP4 (smart/interactive format select)
│ ├── pos-network-checkport # Check TCP/UDP port reachability (nmap, or bash/nc fallback) + local interface view │ ├── pos-network-checkport # Check TCP/UDP port reachability (nmap, or bash/nc fallback) + local interface view
│ ├── pos-network-download # aria2 RPC daemon + queue control (add/torrent/metalink, watch, limits)
│ ├── pos-network-hotspot # Wi-Fi hotspot via create_ap + wihotspot-gui │ ├── pos-network-hotspot # Wi-Fi hotspot via create_ap + wihotspot-gui
│ ├── pos-network-ip # Show interfaces, routes, public IP + location │ ├── pos-network-ip # Show interfaces, routes, public IP + location
│ ├── pos-network-scan # Parallel ping sweep of CIDR │ ├── pos-network-scan # Parallel ping sweep of CIDR
@@ -276,6 +277,7 @@ All non-interactive `pos` commands log output to `~/.local/share/linux_post_inst
| media | mp3 | `pos-media-mp3` | Download audio as MP3 (yt-dlp) | | media | mp3 | `pos-media-mp3` | Download audio as MP3 (yt-dlp) |
| media | mp4 | `pos-media-mp4` | Download video as MP4 (smart/interactive format select) | | media | mp4 | `pos-media-mp4` | Download video as MP4 (smart/interactive format select) |
| network | checkport | `pos-network-checkport` | Check TCP/UDP port reachability (nmap, or bash/nc fallback) + local interface view | | network | checkport | `pos-network-checkport` | Check TCP/UDP port reachability (nmap, or bash/nc fallback) + local interface view |
| network | download | `pos-network-download` | aria2 RPC daemon + queue control (add/torrent/metalink, watch, limits) |
| network | hotspot | `pos-network-hotspot` | Wi-Fi hotspot via create_ap + wihotspot-gui | | network | hotspot | `pos-network-hotspot` | Wi-Fi hotspot via create_ap + wihotspot-gui |
| network | ip | `pos-network-ip` | Show interfaces, routes, public IP + location | | network | ip | `pos-network-ip` | Show interfaces, routes, public IP + location |
| network | scan | `pos-network-scan` | Parallel ping sweep of CIDR | | network | scan | `pos-network-scan` | Parallel ping sweep of CIDR |
@@ -558,7 +560,7 @@ Use conventional prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `chore:`
| File | Lines | Purpose | | File | Lines | Purpose |
|------|-------|---------| |------|-------|---------|
| `install.sh` | 206 | Main orchestrator — 4 phases with CLI flags, `--feature`, prebuilt arch bins | | `install.sh` | 206 | Main orchestrator — 4 phases with CLI flags, `--feature`, prebuilt arch bins |
| `preinstall.sh` | 55 | System packages + hotspot deps + yt-dlp + fail2ban | | `preinstall.sh` | 73 | System packages + hotspot deps + yt-dlp + fail2ban |
| `postinstall.sh` | 152 | fail2ban config, PATH, bash completion, systemd (flag-gated) | | `postinstall.sh` | 152 | fail2ban config, PATH, bash completion, systemd (flag-gated) |
| `lib/common.sh` | 144 | Shared library (log/warn/err/run/spawn, dry-run aware, `load_system_env`) | | `lib/common.sh` | 144 | Shared library (log/warn/err/run/spawn, dry-run aware, `load_system_env`) |
| `lib/flags.sh` | 60 | Feature flag store (set/clear/is_set/value/list/status) | | `lib/flags.sh` | 60 | Feature flag store (set/clear/is_set/value/list/status) |
@@ -570,7 +572,7 @@ Use conventional prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `chore:`
| `bin/flag-clear` | 21 | Unset a flag | | `bin/flag-clear` | 21 | Unset a flag |
| `features/autostart.sh` | 14 | Boot-time feature (moved from `bin/`, flag-gated service) | | `features/autostart.sh` | 14 | Boot-time feature (moved from `bin/`, flag-gated service) |
<!-- GEN:START filetable --> <!-- GEN:START filetable -->
| `bin/pos` | 290 | CLI dispatcher with smart arg matching + logging + category help | | `bin/pos` | 291 | CLI dispatcher with smart arg matching + logging + category help |
| `bin/pos-ai-gemini` | 311 | Chat with Google Gemini (ask, chat, models, sessions) | | `bin/pos-ai-gemini` | 311 | Chat with Google Gemini (ask, chat, models, sessions) |
| `bin/pos-communication-matrix-listener` | 565 | Matrix listener: map /command → bash, run them on room messages | | `bin/pos-communication-matrix-listener` | 565 | Matrix listener: map /command → bash, run them on room messages |
| `bin/pos-communication-matrix-sender` | 224 | Send messages to a Matrix room via the client-server API (send, test, login) | | `bin/pos-communication-matrix-sender` | 224 | Send messages to a Matrix room via the client-server API (send, test, login) |
@@ -589,6 +591,7 @@ Use conventional prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `chore:`
| `bin/pos-media-mp3` | 80 | Download audio as MP3 (yt-dlp) | | `bin/pos-media-mp3` | 80 | Download audio as MP3 (yt-dlp) |
| `bin/pos-media-mp4` | 126 | Download video as MP4 (smart/interactive format select) | | `bin/pos-media-mp4` | 126 | Download video as MP4 (smart/interactive format select) |
| `bin/pos-network-checkport` | 496 | Check TCP/UDP port reachability (nmap, or bash/nc fallback) + local interface view | | `bin/pos-network-checkport` | 496 | Check TCP/UDP port reachability (nmap, or bash/nc fallback) + local interface view |
| `bin/pos-network-download` | 558 | aria2 RPC daemon + queue control (add/torrent/metalink, watch, limits) |
| `bin/pos-network-hotspot` | 93 | Wi-Fi hotspot via create_ap + wihotspot-gui | | `bin/pos-network-hotspot` | 93 | Wi-Fi hotspot via create_ap + wihotspot-gui |
| `bin/pos-network-ip` | 69 | Show interfaces, routes, public IP + location | | `bin/pos-network-ip` | 69 | Show interfaces, routes, public IP + location |
| `bin/pos-network-scan` | 271 | Parallel ping sweep of CIDR | | `bin/pos-network-scan` | 271 | Parallel ping sweep of CIDR |
@@ -603,7 +606,7 @@ Use conventional prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `chore:`
| `bin/pos-system-firewall` | 291 | Interactive UFW management | | `bin/pos-system-firewall` | 291 | Interactive UFW management |
| `bin/pos-system-health` | 209 | Host health dashboard (disk, RAM, services, backup age, fail2ban, docker); exit 1 if any FAIL | | `bin/pos-system-health` | 209 | Host health dashboard (disk, RAM, services, backup age, fail2ban, docker); exit 1 if any FAIL |
| `bin/pos-tree` | 112 | Show the pos CLI command tree: categories, commands, and subcommands | | `bin/pos-tree` | 112 | Show the pos CLI command tree: categories, commands, and subcommands |
| `completions/pos.bash` | 289 | Dynamic bash completion | | `completions/pos.bash` | 291 | Dynamic bash completion |
<!-- GEN:END filetable --> <!-- GEN:END filetable -->
| `apps/install.sh` | 171 | App install/uninstall picker/orchestrator | | `apps/install.sh` | 171 | App install/uninstall picker/orchestrator |
@@ -629,6 +632,7 @@ Use conventional prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `chore:`
| Modify Docker health check | Edit `bin/pos-docker-health` | | Modify Docker health check | Edit `bin/pos-docker-health` |
| Modify vbox (Docker VM) logic | Edit `bin/pos-docker-vbox` | | Modify vbox (Docker VM) logic | Edit `bin/pos-docker-vbox` |
| Modify USB forwarding logic | Edit `bin/pos-share-usb-server` | | Modify USB forwarding logic | Edit `bin/pos-share-usb-server` |
| Modify aria2 download daemon / queue logic | Edit `bin/pos-network-download` |
| Modify NFS share logic | Edit `bin/pos-share-nfs-server` / `bin/pos-share-nfs-client` | | Modify NFS share logic | Edit `bin/pos-share-nfs-server` / `bin/pos-share-nfs-client` |
| Modify SMB share logic | Edit `bin/pos-share-smb-server` / `bin/pos-share-smb-client` | | Modify SMB share logic | Edit `bin/pos-share-smb-server` / `bin/pos-share-smb-client` |
| 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 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`) |
+1 -1
View File
@@ -10,7 +10,7 @@ authoritative one-line reference (every command + flag), see
| Category | What you can do | Guide | | Category | What you can do | Guide |
|----------|-----------------|-------| |----------|-----------------|-------|
| `pos ai` | Chat with Google Gemini from CLI or Telegram | [ai](howto/ai.md) | | `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 network` | IP info, hotspot, scan, port check, aria2 download daemon | [network](howto/network.md) |
| `pos docker` | Compose services, container dashboards, disposable VMs | [docker](howto/docker.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) | | `pos media` | Download audio/video via yt-dlp | [media](howto/media.md) |
| `pos system` | Backups, firewall, health dashboard | [system](howto/system.md) | | `pos system` | Backups, firewall, health dashboard | [system](howto/system.md) |
+25
View File
@@ -86,6 +86,7 @@ Precedence: `--model` flag > `AI_GEMINI_MODEL` env > config file > `gemini-2.5-f
| `pos network checkport <ip:port>` | `bin/pos-network-checkport` | Check if a TCP port is open | None. Uses `/dev/tcp` with a 2s timeout; exit 0/1 via OPEN/CLOSED | | `pos network checkport <ip:port>` | `bin/pos-network-checkport` | Check if a TCP port is open | None. Uses `/dev/tcp` with a 2s timeout; exit 0/1 via OPEN/CLOSED |
| `pos network scan <cidr> [--full] [--retries N]` | `bin/pos-network-scan` | Two-phase nmap scan | See below | | `pos network scan <cidr> [--full] [--retries N]` | `bin/pos-network-scan` | Two-phase nmap scan | See below |
| `pos network hotspot [cmd]` | `bin/pos-network-hotspot` | Wi-Fi hotspot via `create_ap` (CLI) or `wihotspot-gui` (GUI) | Uses the precompiled binaries from `x64_bin/`; see below | | `pos network hotspot [cmd]` | `bin/pos-network-hotspot` | Wi-Fi hotspot via `create_ap` (CLI) or `wihotspot-gui` (GUI) | Uses the precompiled binaries from `x64_bin/`; see below |
| `pos network download <cmd>` | `bin/pos-network-download` | aria2 RPC daemon + queue control (add/torrent/metalink, watch, limits) | `aria2c`/`jq`/`curl`; daemon = systemd user service; secret in `~/.config/linux_post_install/download.env`; see below |
**`pos network scan` in detail:** **`pos network scan` in detail:**
@@ -107,6 +108,30 @@ Backed by the precompiled binaries shipped in `x64_bin/` (see [SCRIPTS.md → x6
| `pos network hotspot stop [<id>]` | Stops the running access point via `create_ap --stop`; `<id>` is an interface name or PID, auto-detected if omitted | | `pos network hotspot stop [<id>]` | Stops the running access point via `create_ap --stop`; `<id>` is an interface name or PID, auto-detected if omitted |
| `pos network hotspot status` | Runs `create_ap --list-running` | | `pos network hotspot status` | Runs `create_ap --list-running` |
**`pos network download` in detail:**
Runs a persistent `aria2c` JSON-RPC daemon (`localhost:6800`) as a **systemd user service** (`pos-aria2.service`, enabled with `systemctl --user enable --now`; prints a linger warning on headless boxes). `start` installs the unit and generates a random `RPC_SECRET` into `~/.config/linux_post_install/download.env` (chmod 600); the secret is also respected as the `RPC_SECRET` env var. Unit flags: `--continue=true --max-connection-per-server=16 --split=16 --seed-time=0 --dir=$HOME/Downloads`.
| Command | Behavior |
|---------|----------|
| `pos network download start` / `stop` | Install+enable the systemd user service / stop and remove it |
| `pos network download status` | Daemon health + global transfer stats (`getGlobalStat`) |
| `pos network download add <url>...` | Enqueue HTTP/FTP downloads (auto-starts the daemon); options `--dir`, `--out`, `--split`, `--tmux` |
| `pos network download torrent <file|magnet>...` | Enqueue `.torrent` files (base64 via `addTorrent`) or magnet links; `--seed` keeps seeding (default `--seed-time=0`), `--dir`, `--tmux` |
| `pos network download metalink <file|url>...` | Enqueue `.metalink` files or URLs; `--tmux` |
| `pos network download list` | Table of active / waiting / finished downloads (GID, status, %, dl/up speeds, name) |
| `pos network download info <gid>` | Full `tellStatus` dump (status, progress, speeds, ETA, error) |
| `pos network download files <gid>` / `peers <gid>` | Files of a download / peers of a torrent |
| `pos network download pause\|resume [gid\|all]` | Pause/resume one or all (default `all`) |
| `pos network download remove [gid\|all]` | Remove one or all; `--force` = `forceRemove` (kills immediately) |
| `pos network download purge` | Clear finished/error history |
| `pos network download move <gid> <pos>` | Reorder the waiting queue (`changePosition`) |
| `pos network download limit [gid] <speed>` | Speed limit, global or per-download (`--upload` = upload speed; `0` = unlimited); accepts `2M`/`512K` |
| `pos network download set <k=v>...` | Set global aria2 options (`--gid <gid>` = per-download) |
| `pos network download watch [gid]` | Live table, 2 s refresh; with a GID it exits when that download completes |
**`--tmux`:** after enqueueing, `add`/`torrent`/`metalink` open a detached tmux session `dl-<name>` running `watch <gid>` (name from `--out` or the URL basename, sanitized and truncated to 40 chars; `-2` suffix on collision). The session closes itself when the download finishes — attach with `tmux attach -t dl-<name>`.
### docker ### docker
| Command | File | Purpose | Configuration | | Command | File | Purpose | Configuration |
+47
View File
@@ -109,6 +109,53 @@ local firewall — check `sudo pos system firewall` rules and service binds
--- ---
## `pos network download` — aria2 download daemon + queue
Runs a persistent `aria2c` JSON-RPC daemon as a systemd **user** service
(`pos-aria2.service`, enabled via `systemctl --user enable --now`; a linger
warning is printed when no session is active, so the daemon survives logout).
The RPC secret lives in `~/.config/linux_post_install/download.env` (chmod 600).
Downloads land in `~/Downloads` by default.
```bash
# enqueue one or many files (auto-starts the daemon if needed)
pos network download add https://example.com/ubuntu.iso
pos network download add https://example.com/a.bin https://example.com/b.bin --dir /mnt/hdd/dl
# torrents / magnets — keep seeding with --seed (default: stop at 100%)
pos network download torrent debian.torrent --seed
pos network download torrent "magnet:?xt=urn:btih:…"
# watch progress live (Ctrl+C detaches); with a GID it exits when that one finishes
pos network download watch
pos network download watch a1b2c3d4e5f60718
# the queue
pos network download list
pos network download pause a1b2c3d4e5f60718 # resume / remove work the same
pos network download remove --force all # kill everything immediately
pos network download limit 2M # global speed cap (0 = unlimited)
pos network download move a1b2c3d4e5f60718 0 # jump to the front of the queue
```
**Recipe:** grab a big ISO in the background and keep an eye on it from a
terminal:
```bash
pos network download add https://example.com/ubuntu.iso --tmux
tmux attach -t dl-ubuntu.iso # live progress; session closes itself when done
```
`--tmux` opens a detached `dl-<name>` session running `watch <gid>` (name from
`--out` or the URL basename, sanitized; `-2` suffix on collision).
**Troubleshooting:** "RPC failed — is the daemon running?" → `pos network
download status`; if the unit failed, check the journal with
`journalctl --user -u pos-aria2 -n 50`. Seed/limit knobs live on `add`/`torrent`
(`--seed`, `--split`) and on `limit`/`set` for the running queue.
---
## Related ## Related
- Reference tables: [DOC/POS.md → network](../POS.md) - Reference tables: [DOC/POS.md → network](../POS.md)
+1
View File
@@ -142,6 +142,7 @@ EXAMPLES
pos network checkport 10.0.0.1:80 Check if a TCP port is open pos network checkport 10.0.0.1:80 Check if a TCP port is open
pos network scan 192.168.1.0/24 Fast parallel ping sweep pos network scan 192.168.1.0/24 Fast parallel ping sweep
pos network hotspot Launch wihotspot-gui pos network hotspot Launch wihotspot-gui
pos network download add <url> Enqueue an aria2 download (--tmux = live view)
pos docker ps List containers (health, IPs, ports) pos docker ps List containers (health, IPs, ports)
pos docker compose ls List available ScaleTail services pos docker compose ls List available ScaleTail services
+558
View File
@@ -0,0 +1,558 @@
#!/usr/bin/env bash
set -euo pipefail
# POS: network download — aria2 RPC daemon + queue control (add/torrent/metalink, watch, limits)
# POS_SUBCMDS: start stop status add torrent metalink list info files peers pause resume remove purge move limit set watch
# POS_FLAGS: --dir --out --split --seed --force --upload --gid --tmux
source "$(dirname "$0")/../lib/common.sh" 2>/dev/null || source "$(dirname "$0")/common.sh"
command -v aria2c &>/dev/null || err "aria2c not found (install aria2)"
command -v jq &>/dev/null || err "jq not found (install jq)"
command -v curl &>/dev/null || err "curl not found (install curl)"
# ── Config / seams (env overrides for tests) ───────────────────
RPC_PORT="${RPC_PORT:-6800}"
RPC_URL="http://127.0.0.1:$RPC_PORT/jsonrpc"
CONFIG_DIR="${CONFIG_DIR:-$HOME/.config/linux_post_install}"
CONFIG_FILE="$CONFIG_DIR/download.env"
DOWNLOAD_DIR="${DOWNLOAD_DIR:-$HOME/Downloads}"
USER_SYSTEMD_DIR="${USER_SYSTEMD_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user}"
SERVICE="pos-aria2.service"
load_secret() {
if [ -z "${RPC_SECRET:-}" ] && [ -f "$CONFIG_FILE" ]; then
RPC_SECRET=$(grep -E '^RPC_SECRET=' "$CONFIG_FILE" 2>/dev/null | head -1 | cut -d= -f2-)
fi
RPC_SECRET="${RPC_SECRET:-}"
}
load_secret
# Shared awk helpers: hs = human speed, et = human eta (seconds).
ROW_AWK='
function hs(s){ if(s>=1073741824)return sprintf("%.1fG/s", s/1073741824); if(s>=1048576)return sprintf("%.1fM/s", s/1048576); if(s>=1024)return sprintf("%.1fK/s", s/1024); return sprintf("%dB/s", s) }
function et(s){ s=s+0; if(s<=0)return "—"; if(s>=86400)return sprintf("%dd%dh", int(s/86400), int(s%86400/3600)); if(s>=3600)return sprintf("%dh%dm", int(s/3600), int(s%3600/60)); if(s>=60)return sprintf("%dm%ds", int(s/60), s%60); return sprintf("%ds", s) }
'
usage() {
cat <<EOF
Usage: pos network download <command> [args]
aria2 download daemon + queue control. Runs a persistent aria2c with JSON-RPC
(systemd user service on localhost:$RPC_PORT) and drives it via the RPC API.
Commands:
start Start the daemon (installs the systemd user service, generates RPC secret)
stop Stop the daemon and remove the service
status Daemon health + global transfer stats
add <url> [<url>...] Enqueue HTTP/FTP downloads (auto-starts the daemon)
torrent <file|magnet>... Enqueue .torrent files or magnet links
metalink <file|url> Enqueue a .metalink file or URL
list Show active / waiting / finished downloads
info <gid> Full status of one download
files <gid> Files of a download
peers <gid> Peers of a torrent download
pause|resume [gid|all] Pause / resume one or all downloads (default all)
remove [gid|all] Remove one or all (--force kills immediately)
purge Clear finished/error download history
move <gid> <pos> Reorder the waiting queue
limit [gid] <speed> Speed limit, global or per-download (--upload = up; 0 = unlimited)
set <key=value>... Set global aria2 options (--gid <gid> = per-download)
watch [gid] Live progress, 2s refresh (Ctrl+C to detach)
Options (add / torrent / metalink):
--dir <path> download directory (default: $DOWNLOAD_DIR)
--out <name> output file name (add only)
--split <n> connections per download (default: 16)
--seed torrent: keep seeding after download (torrent only)
--tmux show live progress in a tmux session named after the file
(attach with: tmux attach -t dl-<name>)
Examples:
pos network download add https://example.com/ubuntu.iso --tmux
pos network download add https://example.com/a.bin https://example.com/b.bin --dir /mnt/hdd/dl
pos network download torrent file.torrent --seed
pos network download list
pos network download limit 2M
pos network download watch
Config: RPC secret in $CONFIG_FILE (chmod 600).
Env: RPC_PORT, RPC_SECRET, DOWNLOAD_DIR override defaults (test seams).
EOF
exit 0
}
# ── JSON helpers ────────────────────────────────────────────────
json_arr() { jq -nc '$ARGS.positional' --args "$@"; }
json_str() { jq -nc --arg s "$1" '$s'; }
opts_json() { # key=value... → JSON object
local kv k v o="{}"
for kv in "$@"; do
k="${kv%%=*}"; v="${kv#*=}"
o=$(jq -nc --argjson o "$o" --arg k "$k" --arg v "$v" '$o + {($k): $v}')
done
printf '%s' "$o"
}
# ── RPC client ─────────────────────────────────────────────────
rpc() { # rpc <method> [json-args...]
local method="$1"; shift
local params="" a
for a in "$@"; do params="$params,$a"; done
local body
body=$(curl -fsS --noproxy '*' -m 60 -H 'Content-Type: application/json' \
-d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"$method\",\"params\":[\"token:${RPC_SECRET}\"${params}]}" \
"$RPC_URL" 2>/dev/null) \
|| err "RPC failed — is the daemon running? (pos network download start)"
local ecode
ecode=$(printf '%s' "$body" | jq -r '.error.code // empty' 2>/dev/null || true)
if [ -n "$ecode" ]; then
err "aria2 RPC error $ecode: $(printf '%s' "$body" | jq -r '.error.message // "?"')"
fi
printf '%s' "$body"
}
# ── Daemon lifecycle (systemd user service) ────────────────────
daemon_active() { systemctl --user is-active --quiet "$SERVICE" 2>/dev/null; }
cmd_start() {
if [ -z "$RPC_SECRET" ] && [ -f "$CONFIG_FILE" ]; then
RPC_SECRET=$(grep -E '^RPC_SECRET=' "$CONFIG_FILE" 2>/dev/null | head -1 | cut -d= -f2-)
fi
if [ -z "$RPC_SECRET" ]; then
RPC_SECRET=$(head -c 24 /dev/urandom | od -An -tx1 | tr -d ' \n')
if [ "${DRY_RUN:-0}" -eq 1 ]; then
log "(dry-run) write RPC secret to $CONFIG_FILE"
else
mkdir -p "$CONFIG_DIR"
printf 'RPC_SECRET=%s\n' "$RPC_SECRET" > "$CONFIG_FILE"
chmod 600 "$CONFIG_FILE"
log "rpc secret written: $CONFIG_FILE"
fi
fi
mkdir -p "$DOWNLOAD_DIR" 2>/dev/null || warn "cannot create download dir: $DOWNLOAD_DIR"
if [ "${DRY_RUN:-0}" -eq 1 ]; then
log "(dry-run) install unit $USER_SYSTEMD_DIR/$SERVICE"
log "(dry-run) systemctl --user daemon-reload && enable --now $SERVICE"
else
mkdir -p "$USER_SYSTEMD_DIR"
cat > "$USER_SYSTEMD_DIR/$SERVICE" <<EOF
[Unit]
Description=pos aria2 RPC daemon (linux-post-install)
[Service]
Type=simple
ExecStart=/usr/bin/aria2c --enable-rpc --rpc-listen-port=$RPC_PORT --rpc-secret=$RPC_SECRET --rpc-listen-all=false "--dir=$DOWNLOAD_DIR" --continue=true --max-connection-per-server=16 --split=16 --seed-time=0 --console-log-level=warn
Restart=on-failure
RestartSec=2
[Install]
WantedBy=default.target
EOF
chmod 644 "$USER_SYSTEMD_DIR/$SERVICE"
systemctl --user daemon-reload
systemctl --user enable --now "$SERVICE"
sleep 1
fi
log "aria2 daemon running on http://127.0.0.1:$RPC_PORT (dir: $DOWNLOAD_DIR)"
if command -v loginctl >/dev/null 2>&1; then
if ! loginctl show-user "$(id -un)" 2>/dev/null | grep -q '^Linger=yes'; then
warn "enable linger so the daemon survives logout: sudo loginctl enable-linger $(id -un)"
fi
fi
}
cmd_stop() {
if [ ! -f "$USER_SYSTEMD_DIR/$SERVICE" ]; then
warn "no aria2 daemon service installed ($SERVICE)"
exit 0
fi
if [ "${DRY_RUN:-0}" -eq 1 ]; then
log "(dry-run) systemctl --user disable --now $SERVICE; remove unit"
else
systemctl --user disable --now "$SERVICE" 2>/dev/null || true
rm -f "$USER_SYSTEMD_DIR/$SERVICE"
systemctl --user daemon-reload
fi
log "aria2 daemon stopped and removed"
}
cmd_status() {
if daemon_active; then echo "daemon: running"; else echo "daemon: not running"; fi
if systemctl --user is-enabled "$SERVICE" >/dev/null 2>&1; then
echo "autostart: enabled (starts on login)"
else
echo "autostart: disabled"
fi
echo "rpc: http://127.0.0.1:$RPC_PORT"
[ -f "$CONFIG_FILE" ] && echo "secret: $CONFIG_FILE"
echo "dir: $DOWNLOAD_DIR"
if daemon_active; then
rpc aria2.getGlobalStat | jq -r '"active: \(.result.numActive) waiting: \(.result.numWaiting) stopped: \(.result.numStopped) (history: \(.result.numStoppedTotal))"'
echo "aria2: $(rpc aria2.getVersion | jq -r '.result.version')"
fi
}
ensure_daemon() {
if ! daemon_active; then
log "daemon not running — starting it"
cmd_start
fi
}
# ── Submit ─────────────────────────────────────────────────────
basename_url() {
local u="$1" b
b="${u##*/}"; b="${b%%\?*}"; b="${b%%\#*}"
[ -n "$b" ] || b="download"
printf '%s' "$b"
}
cmd_add() {
local dir="$DOWNLOAD_DIR" out="" split="" tmux=0
local urls=()
while [ $# -gt 0 ]; do
case "$1" in
--dir) dir="${2:-$DOWNLOAD_DIR}"; shift 2 ;;
--dir=*) dir="${1#*=}"; shift ;;
--out) out="${2:-}"; shift 2 ;;
--out=*) out="${1#*=}"; shift ;;
--split) split="${2:-}"; shift 2 ;;
--split=*) split="${1#*=}"; shift ;;
--tmux) tmux=1; shift ;;
-h|--help) usage ;;
-*) err "add: unknown option: $1" ;;
*) urls+=("$1"); shift ;;
esac
done
[ ${#urls[@]} -gt 0 ] || err "add: no URL given"
[ "$tmux" -eq 1 ] && { command -v tmux &>/dev/null || err "tmux not found (install tmux) — needed for --tmux"; }
dir="${dir/#\~/$HOME}"
ensure_daemon
local oa=()
[ -n "$dir" ] && oa+=("dir=$dir")
[ -n "$out" ] && oa+=("out=$out")
[ -n "$split" ] && oa+=("split=$split")
local resp gid
resp=$(rpc aria2.addUri "$(json_arr "${urls[@]}")" "$(opts_json "${oa[@]}")")
gid=$(printf '%s' "$resp" | jq -r '.result')
log "added $gid: ${urls[*]}"
if [ "$tmux" -eq 1 ]; then tmux_watch "$gid" "${out:-$(basename_url "${urls[0]}")}"; fi
}
cmd_torrent() {
local dir="$DOWNLOAD_DIR" seed=0 tmux=0
local items=()
while [ $# -gt 0 ]; do
case "$1" in
--dir) dir="${2:-$DOWNLOAD_DIR}"; shift 2 ;;
--dir=*) dir="${1#*=}"; shift ;;
--seed) seed=1; shift ;;
--tmux) tmux=1; shift ;;
-h|--help) usage ;;
-*) err "torrent: unknown option: $1" ;;
*) items+=("$1"); shift ;;
esac
done
[ ${#items[@]} -gt 0 ] || err "torrent: need a .torrent file or magnet link"
[ "$tmux" -eq 1 ] && { command -v tmux &>/dev/null || err "tmux not found (install tmux) — needed for --tmux"; }
dir="${dir/#\~/$HOME}"
ensure_daemon
local item
for item in "${items[@]}"; do
local resp gid name
if [[ "$item" =~ ^magnet: ]]; then
resp=$(rpc aria2.addUri "$(json_arr "$item")" "$(opts_json "dir=$dir")")
name="magnet"
else
[ -f "$item" ] || err "torrent: no such file: $item"
name=$(basename "$item"); name="${name%.torrent}"
resp=$(rpc aria2.addTorrent "$(json_str "$(base64 -w0 "$item")")" "[]" "$(opts_json "dir=$dir")")
fi
gid=$(printf '%s' "$resp" | jq -r '.result')
if [ "$seed" -eq 1 ]; then
rpc aria2.changeOption "$(json_str "$gid")" "$(opts_json "seed-ratio=0")" >/dev/null
log "added torrent $gid: $item (seeding until stopped)"
else
log "added torrent $gid: $item"
fi
if [ "$tmux" -eq 1 ]; then tmux_watch "$gid" "$name"; fi
done
}
cmd_metalink() {
local dir="$DOWNLOAD_DIR" tmux=0 item=""
while [ $# -gt 0 ]; do
case "$1" in
--dir) dir="${2:-$DOWNLOAD_DIR}"; shift 2 ;;
--dir=*) dir="${1#*=}"; shift ;;
--tmux) tmux=1; shift ;;
-h|--help) usage ;;
-*) err "metalink: unknown option: $1" ;;
*) item="$1"; shift ;;
esac
done
[ -n "$item" ] || err "metalink: need a .metalink file or URL"
[ "$tmux" -eq 1 ] && { command -v tmux &>/dev/null || err "tmux not found (install tmux) — needed for --tmux"; }
dir="${dir/#\~/$HOME}"
ensure_daemon
local src="$item" name
if [[ "$item" =~ ^https?:// ]]; then
src=$(mktemp)
trap 'rm -f "$src"' EXIT
curl -fsSL --noproxy '*' -m 120 -o "$src" "$item" || err "metalink: failed to fetch $item"
name=$(basename_url "$item")
else
[ -f "$item" ] || err "metalink: no such file: $item"
name=$(basename "$item")
fi
local resp gid
resp=$(rpc aria2.addMetalink "$(json_str "$(base64 -w0 "$src")")" "{}")
gid=$(printf '%s' "$resp" | jq -r '.result')
if [[ "$item" =~ ^https?:// ]]; then
trap - EXIT
rm -f "$src"
fi
log "added metalink $gid: $item"
if [ "$tmux" -eq 1 ]; then tmux_watch "$gid" "$name"; fi
}
# ── Listing / inspection ───────────────────────────────────────
LIST_JQ='.result[] | [.gid[0:8], .status,
(if ((.totalLength|tonumber) > 0) then (100*(.completedLength|tonumber)/(.totalLength|tonumber)) else 0 end),
(.downloadSpeed|tonumber), (.uploadSpeed|tonumber),
(.bittorrent.info.name // ((.files[0].path // "")|split("/")|last))] | @tsv'
fmt_list() {
awk -F'\t' "$ROW_AWK"'{ printf " %-8s %-9s %6.1f%% %9s %9s %s\n", $1, $2, $3, hs($4), hs($5), $6 }'
}
cmd_list() {
local act wait stp n
act=$(rpc aria2.tellActive)
wait=$(rpc aria2.tellWaiting 0 100)
stp=$(rpc aria2.tellStopped 0 50)
n=$(printf '%s' "$act" | jq '.result | length')
printf 'ACTIVE (%s)\n' "$n"
printf ' %-8s %-9s %7s %9s %9s %s\n' GID STATUS PCT DL UL NAME
printf '%s' "$act" | jq -r "$LIST_JQ" | fmt_list
n=$(printf '%s' "$wait" | jq '.result | length')
printf 'WAITING (%s)\n' "$n"
printf ' %-8s %-9s %7s %9s %9s %s\n' GID STATUS PCT DL UL NAME
printf '%s' "$wait" | jq -r "$LIST_JQ" | fmt_list
n=$(printf '%s' "$stp" | jq '.result | length')
printf 'STOPPED (last %s)\n' "$n"
printf ' %-8s %-9s %7s %9s %9s %s\n' GID STATUS PCT DL UL NAME
printf '%s' "$stp" | jq -r "$LIST_JQ" | fmt_list
}
cmd_info() {
local gid="${1:-}"
[ -n "$gid" ] || err "info: gid required (see 'pos network download list')"
rpc aria2.tellStatus "$(json_str "$gid")" | jq -r '
.result as $r |
"gid: \($r.gid)",
"status: \($r.status)",
(if (($r.totalLength|tonumber) > 0) then "progress: \(100*($r.completedLength|tonumber)/($r.totalLength|tonumber)|floor)% \($r.completedLength)/\($r.totalLength) bytes" else "progress: unknown (metadata still downloading)" end),
"dl speed: \($r.downloadSpeed) B/s",
"up speed: \($r.uploadSpeed) B/s",
"connections: \($r.connections)",
(if ($r.numSeeders // 0 | tonumber) > 0 then "seeders: \($r.numSeeders)" else empty end),
(if ($r.errorCode | tonumber) > 0 then "error: \($r.errorCode) — \($r.errorMessage // "?")" else empty end),
(if $r.bittorrent then "torrent: \($r.bittorrent.info.name // "?")" + "\n" + "info-hash: \($r.infoHash)" else empty end),
"dir: \($r.dir)",
"files:",
($r.files[] | " [\(.index)] \(.path) \(.completedLength)/\(.length) bytes")'
}
cmd_files() {
local gid="${1:-}"
[ -n "$gid" ] || err "files: gid required"
rpc aria2.getFiles "$(json_str "$gid")" | jq -r '.result[] | [.index, .path, .length, .completedLength] | @tsv' \
| awk -F'\t' '{ printf " [%s] %s %s/%s bytes\n", $1, $2, $4, $3 }'
}
cmd_peers() {
local gid="${1:-}"
[ -n "$gid" ] || err "peers: gid required"
rpc aria2.getPeers "$(json_str "$gid")" | jq -r '.result[] | [.peerId[0:12], .ip, (.port|tostring), .downloadSpeed, .uploadSpeed, ((.progress|tonumber) * 100)] | @tsv' \
| awk -F'\t' "$ROW_AWK"'{ printf " %-12s %-16s %6s dl %s up %s %5.1f%%\n", $1, $2, $3, hs($4), hs($5), $6 }'
}
# ── Queue control ──────────────────────────────────────────────
queue_cmd() {
local single="$1" all="$2" verb="$3" target="${4:-all}"
if [ "$target" = "all" ]; then
rpc "$all" >/dev/null
else
rpc "$single" "$(json_str "$target")" >/dev/null
fi
log "$verb: $target"
}
cmd_pause() { queue_cmd aria2.pause aria2.pauseAll "paused" "$@"; }
cmd_resume() { queue_cmd aria2.unpause aria2.unpauseAll "resumed" "$@"; }
cmd_remove() {
local force=0 target="${1:-all}"
if [ "${1:-}" = "--force" ]; then force=1; target="${2:-all}"; fi
if [ "$target" = "all" ]; then
if [ "$force" -eq 1 ]; then rpc aria2.forceRemoveAll >/dev/null; else rpc aria2.removeAll >/dev/null; fi
else
if [ "$force" -eq 1 ]; then rpc aria2.forceRemove "$(json_str "$target")" >/dev/null; else rpc aria2.remove "$(json_str "$target")" >/dev/null; fi
fi
log "removed: $target"
}
cmd_purge() { rpc aria2.purgeDownloadResult >/dev/null; log "download history purged"; }
cmd_move() {
local gid="${1:-}" pos="${2:-}"
[ -n "$gid" ] && [ -n "$pos" ] || err "move: usage: pos network download move <gid> <position>"
local newpos
newpos=$(rpc aria2.changePosition "$(json_str "$gid")" "$(json_str "$pos")" "$(json_str "POS_SET")" | jq -r '.result')
log "moved $gid to position $newpos"
}
# ── Tuning ─────────────────────────────────────────────────────
cmd_limit() {
local upload=0 gid="" speed=""
while [ $# -gt 0 ]; do
case "$1" in
--upload) upload=1; shift ;;
-h|--help) usage ;;
-*) err "limit: unknown option: $1" ;;
*) if [ -z "$speed" ]; then speed="$1"; else gid="$speed"; speed="$1"; fi; shift ;;
esac
done
[ -n "$speed" ] || err "limit: speed required (0 = unlimited; e.g. 2M)"
local key="max-overall-download-limit"
if [ "$upload" -eq 1 ]; then key="max-overall-upload-limit"; fi
if [ -n "$gid" ]; then
key="max-download-limit"; [ "$upload" -eq 1 ] && key="max-upload-limit"
rpc aria2.changeOption "$(json_str "$gid")" "$(opts_json "$key=$speed")" >/dev/null
log "limit: $gid $key=$speed"
else
rpc aria2.changeGlobalOption "$(opts_json "$key=$speed")" >/dev/null
log "limit: global $key=$speed"
fi
}
cmd_set() {
local gid="" kvs=()
while [ $# -gt 0 ]; do
case "$1" in
--gid) gid="${2:-}"; shift 2 ;;
--gid=*) gid="${1#*=}"; shift ;;
-h|--help) usage ;;
-*) err "set: unknown option: $1" ;;
*) kvs+=("$1"); shift ;;
esac
done
[ ${#kvs[@]} -gt 0 ] || err "set: need key=value (see: aria2c --help=#rpc-options)"
local opts
opts=$(opts_json "${kvs[@]}")
if [ -n "$gid" ]; then
rpc aria2.changeOption "$gid" "$opts" >/dev/null
log "set: gid $gid — ${kvs[*]}"
else
rpc aria2.changeGlobalOption "$opts" >/dev/null
log "set: global — ${kvs[*]}"
fi
}
# ── Live view ──────────────────────────────────────────────────
cmd_watch() {
local gid="${1:-}"
printf '\033[?25l'
trap 'printf "\033[?25h"; exit 0' INT TERM EXIT
if [ -n "$gid" ]; then
while :; do
local raw row st
raw=$(rpc aria2.tellStatus "$(json_str "$gid")")
row=$(printf '%s' "$raw" | jq -r '
.result as $r |
[$r.status,
(if (($r.totalLength|tonumber) > 0) then (100*($r.completedLength|tonumber)/($r.totalLength|tonumber)) else 0 end),
$r.completedLength, $r.totalLength,
($r.downloadSpeed|tonumber), ($r.uploadSpeed|tonumber),
($r.connections|tonumber),
(if (($r.downloadSpeed|tonumber) > 0) then (((($r.totalLength|tonumber) - ($r.completedLength|tonumber)) / ($r.downloadSpeed|tonumber))) else 0 end)]
| @tsv' 2>/dev/null || true)
st=$(printf '%s' "$row" | cut -f1)
printf '\r\033[J'
if [ -n "$row" ]; then
printf '%s' "$row" | awk -F'\t' "$ROW_AWK"'{ printf " %-9s %6.1f%% %s / %s dl %s up %s conn %s eta %s", $1, $2, $3, $4, hs($5), hs($6), $7, et($8) }'
fi
case "$st" in
complete|error|removed) printf '\n'; exit 0 ;;
esac
sleep 2
done
else
while :; do
local act g n
act=$(rpc aria2.tellActive)
g=$(rpc aria2.getGlobalStat)
n=$(printf '%s' "$act" | jq '.result | length')
printf '\r\033[J'
if [ "$n" -eq 0 ]; then
printf 'no active downloads\n'
exit 0
fi
printf '%s' "$g" | jq -r '[.result.numActive, (.result.downloadSpeed|tonumber), (.result.uploadSpeed|tonumber)] | @tsv' | awk -F'\t' "$ROW_AWK"'{ printf "active %s dl %s up %s", $1, hs($2), hs($3) }'
printf ' %-8s %-9s %7s %9s %9s %s\n' GID STATUS PCT DL UL NAME
printf '%s' "$act" | jq -r "$LIST_JQ" | fmt_list
sleep 2
done
fi
}
tmux_watch() {
local gid="$1" name="$2"
name=$(printf '%s' "$name" | tr -cs 'A-Za-z0-9._-' '-' | cut -c1-40)
name="${name%-}"
[ -n "$name" ] || name="download"
local base sname n=2
base="dl-$name"
sname="$base"
while tmux has-session -t "$sname" 2>/dev/null; do
sname="${base}-${n}"; n=$((n+1))
done
local self
self="$(cd "$(dirname "$0")" && pwd)/$(basename "$0")"
if [ "${DRY_RUN:-0}" -eq 1 ]; then
log "(dry-run) tmux new-session -d -s $sname -- $self watch $gid"
else
TERM="${TERM:-xterm-256color}" tmux new-session -d -s "$sname" "$self watch $gid"
fi
log "tmux session '$sname' started — attach: tmux attach -t '$sname'"
}
# ── Dispatch ───────────────────────────────────────────────────
overview() {
cmd_status
if daemon_active; then echo; cmd_list; fi
}
main() {
local cmd="${1:-}"
shift 2>/dev/null || true
case "$cmd" in
"") overview ;;
-h|--help) usage ;;
start) cmd_start ;;
stop) cmd_stop ;;
status) cmd_status ;;
add) cmd_add "$@" ;;
torrent) cmd_torrent "$@" ;;
metalink) cmd_metalink "$@" ;;
list) cmd_list ;;
info|files|peers|pause|resume|remove|move|limit|set|watch)
"cmd_$cmd" "$@" ;;
purge) cmd_purge ;;
*) err "unknown command: $cmd (see 'pos network download --help')" ;;
esac
}
main "$@"
+2
View File
@@ -11,6 +11,7 @@ _pos_flags[entertainment-send]="--print --markdown"
_pos_flags[media-mp3]="--output --no-playlist --cookies --by-artist --dry-run" _pos_flags[media-mp3]="--output --no-playlist --cookies --by-artist --dry-run"
_pos_flags[media-mp4]="--format --best --worst --output --no-playlist --cookies --dry-run" _pos_flags[media-mp4]="--format --best --worst --output --no-playlist --cookies --dry-run"
_pos_flags[network-checkport]="--tcp --udp --ping --no-banner --versions --timeout" _pos_flags[network-checkport]="--tcp --udp --ping --no-banner --versions --timeout"
_pos_flags[network-download]="--dir --out --split --seed --force --upload --gid --tmux"
_pos_flags[network-hotspot]="--foreground" _pos_flags[network-hotspot]="--foreground"
_pos_flags[share-usb-server]="--ls --ls-shared --share --unshare --auto-share --callback --close-callback --auto-connect --disconnect --nickname --timeout --port --info --version" _pos_flags[share-usb-server]="--ls --ls-shared --share --unshare --auto-share --callback --close-callback --auto-connect --disconnect --nickname --timeout --port --info --version"
_pos_flags[system-backup]="--service" _pos_flags[system-backup]="--service"
@@ -24,6 +25,7 @@ _pos_subcmds[communication-matrix-sender]="send test login"
_pos_subcmds[communication-telegram-sender]="send test" _pos_subcmds[communication-telegram-sender]="send test"
_pos_subcmds[docker-compose]="ls installed up down restart logs update config" _pos_subcmds[docker-compose]="ls installed up down restart logs update config"
_pos_subcmds[docker-vbox]="create enter stop start rm ls" _pos_subcmds[docker-vbox]="create enter stop start rm ls"
_pos_subcmds[network-download]="start stop status add torrent metalink list info files peers pause resume remove purge move limit set watch"
_pos_subcmds[system-event-trigger]="run config list enable disable status" _pos_subcmds[system-event-trigger]="run config list enable disable status"
# GEN:END possubcmds # GEN:END possubcmds
# GEN:START posconfigscopes # GEN:START posconfigscopes
+1 -1
View File
@@ -26,7 +26,7 @@ while [[ $# -gt 0 ]]; do
done done
PACKAGES=( PACKAGES=(
git curl wget vim nano tmux tree jq git curl wget aria2 vim nano tmux tree jq
unzip zip rsync htop btop telnet unzip zip rsync htop btop telnet
net-tools iputils-ping traceroute tcpdump nmap net-tools iputils-ping traceroute tcpdump nmap
openssh-client openssh-server ufw fail2ban openssh-client openssh-server ufw fail2ban