From 422647e7898b8df67cd11f8fb060aec23dec84aa Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Wed, 14 Oct 2026 22:20:15 +0200 Subject: feat(cli): meshbay-cli, a command-line client for members Signs in to the hub, lists the account's groups and their folders, and downloads and uploads files directly with each group's node, end-to-end encrypted. With no argument it opens a two-pane interactive mode (Textual). Session and device keys are kept encrypted under the system's key storage, or a 0600 key file where there is none; node keys are pinned at first contact; a new machine links as a device. Packaged on its own on Linux (deb and rpm, on meshbay-common's venv, with a man page generated from the parser) and frozen next to meshbay-node in the full Windows installer, not the light one, with a self-test the build runs. client/keys.py ports keyderive.js and is LGPL like it. Co-Authored-By: Claude Opus 5.5 --- packaging/build/build-cli.sh | 82 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100755 packaging/build/build-cli.sh (limited to 'packaging/build/build-cli.sh') diff --git a/packaging/build/build-cli.sh b/packaging/build/build-cli.sh new file mode 100755 index 0000000..b697f10 --- /dev/null +++ b/packaging/build/build-cli.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +# Build the meshbay-cli package: the command-line client, and the Python +# packages only it needs, on top of meshbay-common. +# +# Expects build-common.sh to have run first: it installed the CLI last, after +# listing the servers' packages in servers-site.txt and servers-bin.txt, so +# everything not in those lists is the CLI's and moves here. +# +# Usage: ./build-cli.sh [staging-dir] +set -euo pipefail + +REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +STAGING="${1:-/tmp/meshbay-build}" +COMMON_ROOT="$STAGING/meshbay-common-root" +ROOT="$STAGING/meshbay-cli-root" + +VENV="$COMMON_ROOT/opt/meshbay-common/venv" +PYVER=$(basename "$VENV"/lib/python3.*) +SITE="$VENV/lib/$PYVER/site-packages" + +VERSION=$(python3 -c " +import tomllib, pathlib +p = pathlib.Path('$REPO/packages/meshbay-cli/pyproject.toml') +print(tomllib.loads(p.read_text())['project']['version']) +") +echo "==> meshbay-cli $VERSION" + +[ -d "$VENV" ] || { echo "!! shared venv not found — run build-common.sh first" >&2; exit 1; } +[ -f "$STAGING/servers-site.txt" ] || { + echo "!! $STAGING/servers-site.txt missing — run build-common.sh first" >&2; exit 1; } +[ -d "$SITE/meshbay_cli" ] || { + echo "!! meshbay_cli not in the shared venv — was build-cli.sh already run?" >&2; exit 1; } + +rm -rf "$ROOT" +mkdir -p "$ROOT/opt/meshbay-cli" + +# Run from the staged venv with no bytecode written: whatever it imports +# would otherwise leave __pycache__ in files other packages own. +run_venv() { PYTHONDONTWRITEBYTECODE=1 "$VENV/bin/python" "$@"; } + +# --- Before moving anything: the notices and the man page ----------------- +# Both need the CLI importable, which it is only where build-common.sh put it. +echo " writing THIRD-PARTY-NOTICES.txt" +run_venv "$REPO/packaging/third_party_notices.py" \ + -o "$ROOT/opt/meshbay-cli/THIRD-PARTY-NOTICES.txt" meshbay-cli + +echo " generating the man page" +mkdir -p "$ROOT/usr/share/man/man1" +run_venv -m meshbay_cli.manpage | gzip -9n > "$ROOT/usr/share/man/man1/meshbay-cli.1.gz" +[ "$(gzip -dc "$ROOT/usr/share/man/man1/meshbay-cli.1.gz" | head -c 3)" = ".TH" ] || { + echo "!! the man page did not generate" >&2; exit 1; } + +# --- The CLI and its own dependencies, out of the common tree --------------- +# MOVE (not copy), as build-hub.sh and build-node.sh do: dpkg/rpm then know +# these files belong to meshbay-cli. The list is kept for the maintainer +# scripts, which remove the bytecode compiled next to these files. +CLI_SITE="$ROOT/opt/meshbay-common/venv/lib/$PYVER/site-packages" +CLI_BIN="$ROOT/opt/meshbay-common/venv/bin" +mkdir -p "$CLI_SITE" "$CLI_BIN" +: > "$ROOT/opt/meshbay-cli/site-packages.list" +while IFS= read -r entry; do + mv "$SITE/$entry" "$CLI_SITE/" + echo "$entry" >> "$ROOT/opt/meshbay-cli/site-packages.list" +done < <(LC_ALL=C comm -13 "$STAGING/servers-site.txt" <(LC_ALL=C ls -1A "$SITE")) +while IFS= read -r entry; do + mv "$VENV/bin/$entry" "$CLI_BIN/" +done < <(LC_ALL=C comm -13 "$STAGING/servers-bin.txt" <(LC_ALL=C ls -1A "$VENV/bin")) +echo " moved: $(tr '\n' ' ' < "$ROOT/opt/meshbay-cli/site-packages.list")" + +# Only the client is on PATH; the dependencies' own scripts (pygmentize, +# keyring, ...) stay inside the venv. +mkdir -p "$ROOT/usr/bin" +ln -sf /opt/meshbay-common/venv/bin/meshbay-cli "$ROOT/usr/bin/meshbay-cli" + +find "$ROOT" -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true + +# What the venv holds of this package, for the rpm spec's %files: the entries +# differ with every dependency upgrade, so no list is kept by hand. +( cd "$ROOT" && find opt/meshbay-common/venv/lib/*/site-packages opt/meshbay-common/venv/bin \ + -mindepth 1 -maxdepth 1 | LC_ALL=C sort | sed 's|^|/|' ) > "$ROOT.files" + +echo "==> meshbay-cli staging ready at $ROOT" -- cgit v1.2.3