移动设备远程vipecoding解决方案参考

移动设备远程vipecoding解决方案参考
移动设备远程vipecoding解决方案参考

前置要求

本方案刚需一台开放ssh端口且可以安装各类tui agent(claude code等)的设备。
同时确认你知道用于连接的 ip地址/域名,ssh端口号(可以用于远程访问的端口号),你需要登陆的用户的用户名,该用户的密码。
组网方案可以使用tailscale等虚拟局域网方案,或是直接frp内网穿透(TCP隧道)

为什么用Termux

之前在用Termius,效果其实也不错,只是这个在我这里没法滑动屏幕滚屏 :smiling_face_with_tear:,含泪舍弃,后来发现有个叫Termux的东西,感觉很有意思,且在使用ssh时可以正常划屏,所以开始研究。没试过其他客户端,如果有其他好用的客户端推荐,欢迎回帖。

整体思路

使用软件 Termux 在移动设备用pkg包管理器安装 openssh 和 sshpass,并持久化本地ssh config,从而实现快捷远程ssh
Termux官方网址: Termux | The main termux site and help pages.
github仓库: GitHub - termux/termux-app: Termux - a terminal emulator application for Android OS extendible by variety of packages. · GitHub
F-Droid发布页: Termux | F-Droid - Free and Open Source Android App Repository
也可以直接使用谷歌版,方法通用

整体步骤

首先安装openssh和sshpass,然后在本地创建ssh目录,之后创建密钥对,用sshpass将密钥对上传到需要连接的ssh主机,期间需要提供所登录用户的密码,并定义config。之后就可以用 ssh <你的自定义名称> 登录到ssh主机。
具体流程可以参考我让ai帮我写的脚本

#!/usr/bin/env bash
set -euo pipefail
#此处内容注意修改,改为你自己的主机相关内容
HOST_ALIAS="你自己起的名称"
REMOTE_HOST="ip地址/域名,不带端口号"
REMOTE_PORT="可以远程连接到的ssh端口"
REMOTE_USER="需要登录的用户"

KEY_FILE="$HOME/.ssh/${HOST_ALIAS}_ed25519"
CONFIG_FILE="$HOME/.ssh/config"

echo "==> 检查 pkg 包管理器"

if ! command -v pkg >/dev/null 2>&1; then
  echo "ERROR: 未找到 pkg 包管理器。"
  echo "此脚本面向 Termux 环境,请确认你正在 Termux 中运行。"
  exit 1
fi

echo "==> 安装 openssh 和 sshpass"

pkg update -y
pkg install -y openssh sshpass

echo "==> 检查 ssh / ssh-keygen / sshpass"

for cmd in ssh ssh-keygen sshpass; do
  if ! command -v "$cmd" >/dev/null 2>&1; then
    echo "ERROR: 缺少命令:$cmd"
    echo "请检查 openssh / sshpass 是否安装成功。"
    exit 1
  fi
done

echo "==> 准备 SSH 目录"

mkdir -p "$HOME/.ssh"
chmod 700 "$HOME/.ssh"

echo "==> 生成 SSH 密钥对"

if [ -f "$KEY_FILE" ]; then
  echo "已存在私钥:$KEY_FILE"
  echo "将复用现有密钥。"
else
  ssh-keygen -t ed25519 \
    -f "$KEY_FILE" \
    -C "${HOST_ALIAS}-${REMOTE_USER}@${REMOTE_HOST}" \
    -N ""

  chmod 600 "$KEY_FILE"
  chmod 644 "$KEY_FILE.pub"

  echo "已生成密钥:$KEY_FILE"
fi

echo "==> 读取远程密码"

if [ -z "${SSH_PASSWORD:-}" ]; then
  read -rsp "请输入 ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PORT} 的 SSH 密码: " SSH_PASSWORD
  echo ""
fi

echo "==> 复制公钥到远程主机"

cat "$KEY_FILE.pub" | sshpass -p "$SSH_PASSWORD" ssh \
  -p "$REMOTE_PORT" \
  -o StrictHostKeyChecking=accept-new \
  -o PreferredAuthentications=password \
  -o PubkeyAuthentication=no \
  "${REMOTE_USER}@${REMOTE_HOST}" \
  'umask 077
   mkdir -p ~/.ssh
   touch ~/.ssh/authorized_keys
   chmod 700 ~/.ssh
   chmod 600 ~/.ssh/authorized_keys
   read -r PUBKEY
   grep -qxF -- "$PUBKEY" ~/.ssh/authorized_keys || printf "%s\n" "$PUBKEY" >> ~/.ssh/authorized_keys
  '

echo "==> 备份并更新本地 SSH config"

touch "$CONFIG_FILE"
chmod 600 "$CONFIG_FILE"

BACKUP_FILE="${CONFIG_FILE}.bak.$(date +%Y%m%d%H%M%S)"
cp "$CONFIG_FILE" "$BACKUP_FILE"

TMP_FILE="$(mktemp)"

# 删除旧的 devssh 配置块,避免重复
awk -v host="$HOST_ALIAS" '
  BEGIN { skip = 0 }

  /^[[:space:]]*Host[[:space:]]+/ {
    if ($2 == host) {
      skip = 1
      next
    } else {
      skip = 0
    }
  }

  skip == 0 { print }
' "$CONFIG_FILE" > "$TMP_FILE"

cat >> "$TMP_FILE" <<EOF

Host ${HOST_ALIAS}
  HostName ${REMOTE_HOST}
  Port ${REMOTE_PORT}
  User ${REMOTE_USER}
  IdentityFile ${KEY_FILE}
  IdentitiesOnly yes
  ServerAliveInterval 60
  ServerAliveCountMax 3
EOF

mv "$TMP_FILE" "$CONFIG_FILE"
chmod 600 "$CONFIG_FILE"

echo "==> 测试免密登录"

ssh -o BatchMode=yes "$HOST_ALIAS" "echo 'SSH key login success: \$(hostname)'"

echo ""
echo "完成。现在可以直接连接:"
echo ""
echo "  ssh ${HOST_ALIAS}"
echo ""
echo "私钥位置:"
echo "  ${KEY_FILE}"
echo ""
echo "配置文件:"
echo "  ${CONFIG_FILE}"
echo ""
echo "原配置备份:"
echo "  ${BACKUP_FILE}"

使用该脚本时注意修改脚本开头的 HOST_ALIAS、REMOTE_HOST、REMOTE_PORT、REMOTE_USER

实操展示

1.安装并打开Termux,长摁屏幕即可进行粘贴 :squinting_face_with_tongue:

de0a22942d9b422e740bec6681beca6c
2.复制完毕之后点击回车按键,执行脚本,等出现“读取远程密码”的提示之后,输入你所登录账户的密码 :open_mouth:
cd359ced394b04f0509ea66ccf059bd4
3.完成之后直接尝试登录,我之前填写的名称为 devssh,那么我这里就是用 ssh devssh 这个命令进行远程ssh登录,远程登录我的ubuntu主机 :face_savoring_food:
50fbc123338a3fbec533230fcb50dab4
4.打开你最喜欢的tui coding agent,开始愉快编码 :heart_eyes:
72ac04a70f9fea176f70db4744e27df6

后续优化

在之前的图片中可以看到,Termux 默认的字体样式并不适合用来进行coding,整体看上去比较杂乱,此时可以对字体进行修改。
具体参考以下脚本 ,使用的字体是 “Maple Mono NF CN”,如果你想要其他字体,可以让ai帮你改 :melting_face:

bash <<'EOF'
set -euo pipefail

REPO="subframe7536/maple-font"
FONT_ALIAS="Maple Mono NF CN"
ASSET_REGEX="${ASSET_REGEX:-MapleMono.*NF.*CN.*\.zip$}"

TERMUX_PREFIX="/data/data/com.termux/files/usr"
TERMUX_FONT_DIR="$HOME/.termux"
TERMUX_FONT_FILE="$TERMUX_FONT_DIR/font.ttf"

TMP_DIR=""

log() {
  printf '==> %s\n' "$*" >&2
}

warn() {
  printf 'WARN: %s\n' "$*" >&2
}

die() {
  printf 'ERROR: %s\n' "$*" >&2
  exit 1
}

cleanup() {
  if [ -n "${TMP_DIR:-}" ] && [ -d "$TMP_DIR" ]; then
    rm -rf "$TMP_DIR"
  fi
}

check_termux() {
  if [ ! -d "$TERMUX_PREFIX" ]; then
    die "当前环境看起来不是 Termux,请在 Termux 中运行。"
  fi
}

install_deps() {
  log "更新软件源并安装必要依赖..."

  export DEBIAN_FRONTEND=noninteractive

  pkg update -y

  apt-get install -y \
    -o Dpkg::Options::="--force-confdef" \
    -o Dpkg::Options::="--force-confold" \
    wget jq unzip tar ca-certificates
}

get_latest_asset() {
  local work_dir="$1"
  local api_url="https://api.github.com/repos/${REPO}/releases/latest"
  local json_file="$work_dir/latest.json"

  log "获取 ${FONT_ALIAS} 最新版本信息..."

  wget -qO "$json_file" "$api_url" \
    || die "无法访问 GitHub API:$api_url"

  if ! jq -e '.assets and (.assets | type == "array")' "$json_file" >/dev/null; then
    cat "$json_file" >&2 || true
    die "GitHub API 返回异常,未找到 release assets。"
  fi

  local tag_name
  tag_name="$(jq -r '.tag_name // empty' "$json_file")"

  if [ -n "$tag_name" ]; then
    log "最新版本:$tag_name"
  fi

  local asset_name
  local asset_url

  asset_name="$(
    jq -r --arg re "$ASSET_REGEX" '
      [.assets[] | select(.name | test($re; "i")) | .name][0] // empty
    ' "$json_file"
  )"

  asset_url="$(
    jq -r --arg re "$ASSET_REGEX" '
      [.assets[] | select(.name | test($re; "i")) | .browser_download_url][0] // empty
    ' "$json_file"
  )"

  if [ -z "$asset_url" ]; then
    warn "没有找到匹配的下载文件。"
    warn "当前匹配规则:$ASSET_REGEX"
    warn ""
    warn "可用 release assets:"
    jq -r '.assets[].name' "$json_file" >&2 || true
    die "未找到 ${FONT_ALIAS} 对应压缩包。"
  fi

  log "匹配到文件:$asset_name"

  printf '%s\n' "$asset_url"
}

download_asset() {
  local asset_url="$1"
  local output_file="$2"

  case "$asset_url" in
    http://*|https://*) ;;
    *) die "下载地址不是有效 URL:$asset_url" ;;
  esac

  log "下载字体压缩包..."
  log "下载地址:$asset_url"

  wget -O "$output_file" "$asset_url" \
    || die "下载失败:$asset_url"
}

extract_asset() {
  local asset_file="$1"
  local extract_dir="$2"

  log "解压字体文件..."

  mkdir -p "$extract_dir"

  case "$asset_file" in
    *.zip)
      unzip -q "$asset_file" -d "$extract_dir"
      ;;
    *.tar.gz|*.tgz)
      tar -xzf "$asset_file" -C "$extract_dir"
      ;;
    *.tar.xz)
      tar -xJf "$asset_file" -C "$extract_dir"
      ;;
    *.ttf|*.otf)
      cp "$asset_file" "$extract_dir/"
      ;;
    *)
      die "暂不支持的文件格式:$asset_file"
      ;;
  esac
}

find_font_file() {
  local extract_dir="$1"
  local font_file=""

  log "查找 Maple Mono NF CN Regular 字体文件..."

  font_file="$(
    find "$extract_dir" -type f \( -iname "*.ttf" -o -iname "*.otf" \) \
      | grep -Ei 'Maple.*Mono.*NF.*CN.*Regular.*\.ttf$' \
      | grep -Eiv 'italic|bold|light|medium|semibold|extrabold|thin|black' \
      | sort \
      | head -n 1 || true
  )"

  if [ -z "$font_file" ]; then
    font_file="$(
      find "$extract_dir" -type f \( -iname "*.ttf" -o -iname "*.otf" \) \
        | grep -Ei 'Maple.*Mono.*NF.*CN.*Regular.*\.(ttf|otf)$' \
        | grep -Eiv 'italic|bold|light|medium|semibold|extrabold|thin|black' \
        | sort \
        | head -n 1 || true
    )"
  fi

  if [ -z "$font_file" ]; then
    font_file="$(
      find "$extract_dir" -type f \( -iname "*.ttf" -o -iname "*.otf" \) \
        | grep -Ei 'Regular.*\.(ttf|otf)$' \
        | grep -Eiv 'italic|bold|light|medium|semibold|extrabold|thin|black' \
        | sort \
        | head -n 1 || true
    )"
  fi

  if [ -z "$font_file" ]; then
    font_file="$(
      find "$extract_dir" -type f \( -iname "*.ttf" -o -iname "*.otf" \) \
        | grep -Ei 'Maple|NF|CN' \
        | grep -Eiv 'italic|bold|light|medium|semibold|extrabold|thin|black' \
        | sort \
        | head -n 1 || true
    )"
  fi

  if [ -z "$font_file" ]; then
    font_file="$(
      find "$extract_dir" -type f \( -iname "*.ttf" -o -iname "*.otf" \) \
        | sort \
        | head -n 1 || true
    )"
  fi

  if [ -z "$font_file" ]; then
    die "解压后没有找到 .ttf 或 .otf 字体文件。"
  fi

  printf '%s\n' "$font_file"
}

install_font_to_termux() {
  local font_file="$1"

  log "安装字体到 Termux..."

  if [ ! -f "$font_file" ]; then
    die "字体文件不存在:$font_file"
  fi

  mkdir -p "$TERMUX_FONT_DIR"

  if [ -f "$TERMUX_FONT_FILE" ]; then
    local backup_file="${TERMUX_FONT_FILE}.bak.$(date +%Y%m%d%H%M%S)"
    cp "$TERMUX_FONT_FILE" "$backup_file"
    log "已备份原字体:$backup_file"
  fi

  cp "$font_file" "$TERMUX_FONT_FILE"
  chmod 644 "$TERMUX_FONT_FILE"

  log "已安装字体:$TERMUX_FONT_FILE"
  log "字体来源:$font_file"
}

reload_termux_settings() {
  log "刷新 Termux 设置..."

  if command -v termux-reload-settings >/dev/null 2>&1; then
    termux-reload-settings || warn "刷新失败,请手动重启 Termux。"
  else
    warn "未找到 termux-reload-settings,请手动重启 Termux 应用。"
  fi
}

main() {
  check_termux
  install_deps

  TMP_DIR="$(mktemp -d)"
  trap cleanup EXIT

  local asset_url
  local asset_url_lc
  local asset_file
  local extract_dir
  local font_file

  asset_url="$(get_latest_asset "$TMP_DIR")"
  asset_url_lc="$(printf '%s' "$asset_url" | tr '[:upper:]' '[:lower:]')"

  log "最终下载 URL:$asset_url"

  case "$asset_url_lc" in
    *.zip)
      asset_file="$TMP_DIR/font.zip"
      ;;
    *.tar.gz|*.tgz)
      asset_file="$TMP_DIR/font.tar.gz"
      ;;
    *.tar.xz)
      asset_file="$TMP_DIR/font.tar.xz"
      ;;
    *.ttf)
      asset_file="$TMP_DIR/font.ttf"
      ;;
    *.otf)
      asset_file="$TMP_DIR/font.otf"
      ;;
    *)
      asset_file="$TMP_DIR/font.zip"
      warn "无法从 URL 判断格式,默认按 zip 处理。"
      ;;
  esac

  extract_dir="$TMP_DIR/extracted"

  download_asset "$asset_url" "$asset_file"
  extract_asset "$asset_file" "$extract_dir"

  font_file="$(find_font_file "$extract_dir")"

  log "选中的字体文件:$font_file"

  install_font_to_termux "$font_file"
  reload_termux_settings

  log "安装完成。"
  log "如果字体没有立即生效,请完全关闭并重新打开 Termux。"
}

main "$@"
EOF

更新字体之后的展示,可以看到显示效果明显比之前好。

a02f5661430f8213624b7f7081aac985

已经测试过 F-Droid版本和google play版本,均可以正常使用

可以配合tmux进行使用,实现断开连接ai依然在跑的效果 :face_savoring_food:

发现一个新的问题,在连接到物理键盘时,termux似乎无法输入中文

通过脚本 解决了该问题,谷歌版似乎无法用这个方案解决,必须用F-Droid /Github版

mkdir -p ~/.termux
echo 'enforce-char-based-input=true' >> ~/.termux/termux.properties
termux-reload-settings

第一次写这种文章,如有写得不好的地方请见谅 :smiling_face_with_tear:

1 个帖子 - 1 位参与者

阅读完整话题

来源: LinuxDo 最新话题查看原文