以前OpenWorkを紹介しましたが、コード生成であればopencodeで十分です。そのopencodeを手軽にWebから利用できる、opencode webを一発で導入出来るスクリプトです。

LXDコンテナで実行
LXDコンテナ内での実行を想定しており、Tailscale ServeでHTTPS化しています。tailscaleが有効なLXDコンテナ内で、下記スクリプトを貼り付けるだけです。
#!/usr/bin/env bash
# =============================================================================
# opencode web + Tailscale Serve セットアップスクリプト
# LXDコンテナ内で実行。Tailscaleネットワーク経由でHTTPSアクセスを提供します。
#
# 使い方:
# bash setup-opencode-tailscale.sh
# ※ sudo は不要。スクリプト内で必要な箇所だけ sudo を使います。
# =============================================================================
set -euo pipefail
# ── 設定 ─────────────────────────────────────────────────────────────────────
OPENCODE_PORT=4096
OPENCODE_USERNAME="opencode"
OPENCODE_PASSWORD=""
WORK_DIR="${OPENCODE_WORK_DIR:-$HOME/workspace}"
SERVICE_FILE="/etc/systemd/system/opencode-web.service"
RUN_USER="${USER:-$(id -un)}"
RUN_HOME="$HOME"
# ── カラー出力 ────────────────────────────────────────────────────────────────
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
info() { echo -e "${CYAN}[INFO]${NC} $*"; }
success() { echo -e "${GREEN}[OK]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; exit 1; }
# ── ① 最初にパスワードを入力させる ───────────────────────────────────────────
prompt_password() {
echo ""
echo -e "${CYAN}╔══════════════════════════════════════════════════════╗${NC}"
echo -e "${CYAN}║ opencode web + Tailscale Serve セットアップ ║${NC}"
echo -e "${CYAN}╚══════════════════════════════════════════════════════╝${NC}"
echo ""
if [[ -n "${OPENCODE_SERVER_PASSWORD:-}" ]]; then
OPENCODE_PASSWORD="$OPENCODE_SERVER_PASSWORD"
info "OPENCODE_SERVER_PASSWORD 環境変数からパスワードを取得しました。"
return
fi
echo -e "${YELLOW}[WARN]${NC} Tailscaleネットワーク越しに公開するため、パスワード設定を推奨します。"
echo ""
while true; do
read -r -s -p "🔑 opencode web のパスワードを入力 (空Enterでスキップ): " OPENCODE_PASSWORD
echo ""
if [[ -z "$OPENCODE_PASSWORD" ]]; then
warn "パスワードなしで続行します。"
break
fi
read -r -s -p "🔑 もう一度入力してください: " OPENCODE_PASSWORD2
echo ""
if [[ "$OPENCODE_PASSWORD" == "$OPENCODE_PASSWORD2" ]]; then
success "パスワードを設定しました。"
break
else
warn "パスワードが一致しません。もう一度入力してください。"
fi
done
echo ""
}
# ── 前提チェック ──────────────────────────────────────────────────────────────
check_prerequisites() {
info "前提条件を確認しています..."
if ! command -v tailscale &>/dev/null; then
error "tailscale が見つかりません。先に Tailscale をインストール・ログインしてください。"
fi
if ! tailscale status &>/dev/null; then
error "Tailscale が接続されていません。'tailscale up' を実行してください。"
fi
success "Tailscale: 接続済み"
}
# ── opencode インストール ─────────────────────────────────────────────────────
install_opencode() {
# 公式スクリプトは ~/.opencode/bin/ にインストールするのでPATHを先に通す
export PATH="$HOME/.opencode/bin:$HOME/.local/bin:$HOME/bin:$PATH"
if command -v opencode &>/dev/null; then
CURRENT_VER=$(opencode --version 2>/dev/null || echo "unknown")
success "opencode は既にインストール済みです ($CURRENT_VER)。スキップします。"
return
fi
info "opencode をインストールしています..."
if command -v npm &>/dev/null; then
npm install -g opencode-ai@latest
success "opencode を npm でインストールしました。"
return
fi
if command -v curl &>/dev/null; then
curl -fsSL https://opencode.ai/install | bash
# インストール後にPATHを再読み込み
export PATH="$HOME/.opencode/bin:$HOME/.local/bin:$HOME/bin:$PATH"
success "opencode を公式スクリプトでインストールしました。"
return
fi
error "npm も curl も見つかりません。Node.js または curl をインストールしてください。"
}
# ── ワークディレクトリ・設定ファイル準備 ─────────────────────────────────────
setup_workdir() {
info "ワークディレクトリを準備しています: $WORK_DIR"
mkdir -p "$WORK_DIR"
OPENCODE_CONFIG_DIR="$RUN_HOME/.config/opencode"
mkdir -p "$OPENCODE_CONFIG_DIR"
cat > "$OPENCODE_CONFIG_DIR/opencode.json" <<EOF
{
"server": {
"port": ${OPENCODE_PORT},
"hostname": "127.0.0.1"
}
}
EOF
success "opencode 設定ファイル: $OPENCODE_CONFIG_DIR/opencode.json"
}
# ── systemd サービス登録 ──────────────────────────────────────────────────────
setup_systemd_service() {
info "systemd サービスを設定しています..."
# opencode バイナリのフルパスを解決
# 公式スクリプトは ~/.opencode/bin/ にインストールするため優先的に探す
OPENCODE_BIN=""
for candidate in \
"$RUN_HOME/.opencode/bin/opencode" \
"$RUN_HOME/.local/bin/opencode" \
"$RUN_HOME/bin/opencode" \
"/usr/local/bin/opencode"
do
if [[ -x "$candidate" ]]; then
OPENCODE_BIN="$candidate"
break
fi
done
# forループで見つからなければ PATH から探す
if [[ -z "$OPENCODE_BIN" ]]; then
OPENCODE_BIN=$(command -v opencode 2>/dev/null || true)
fi
if [[ -z "$OPENCODE_BIN" ]]; then
error "opencode バイナリが見つかりません。インストールを確認してください。"
fi
info "opencode バイナリ: $OPENCODE_BIN"
# パスワードが設定されている場合のみ Environment 行を追加
if [[ -n "$OPENCODE_PASSWORD" ]]; then
ENV_PASSWORD_LINE="Environment=\"OPENCODE_SERVER_PASSWORD=${OPENCODE_PASSWORD}\""
else
ENV_PASSWORD_LINE="# OPENCODE_SERVER_PASSWORD not set"
fi
sudo tee "$SERVICE_FILE" > /dev/null <<EOF
[Unit]
Description=OpenCode Web Server
After=network.target tailscaled.service
Wants=tailscaled.service
[Service]
Type=simple
User=${RUN_USER}
WorkingDirectory=${WORK_DIR}
Environment="HOME=${RUN_HOME}"
Environment="PATH=${RUN_HOME}/.opencode/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${RUN_HOME}/.local/bin:${RUN_HOME}/bin"
Environment="OPENCODE_SERVER_USERNAME=${OPENCODE_USERNAME}"
${ENV_PASSWORD_LINE}
ExecStart=${OPENCODE_BIN} web --port ${OPENCODE_PORT} --hostname 127.0.0.1
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=opencode-web
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable opencode-web.service
sudo systemctl restart opencode-web.service
sleep 4
if sudo systemctl is-active --quiet opencode-web.service; then
success "opencode-web サービスが起動しました。"
else
warn "サービスの起動に問題があります。直近のログ:"
echo ""
sudo journalctl -u opencode-web -n 20 --no-pager || true
echo ""
warn "詳細確認: journalctl -u opencode-web -n 50 --no-pager"
fi
}
# ── Tailscale serve 設定 ──────────────────────────────────────────────────────
setup_tailscale_serve() {
info "Tailscale serve を設定しています (HTTPS → localhost:${OPENCODE_PORT})..."
# 既存設定をリセット(冪等性確保)
sudo tailscale serve reset 2>/dev/null || true
# 新構文: tailscale serve --bg http://127.0.0.1:<PORT>
sudo tailscale serve --bg "http://127.0.0.1:${OPENCODE_PORT}"
success "Tailscale serve を設定しました。"
# Tailscale ホスト名を取得
TS_HOSTNAME=$(tailscale status --json 2>/dev/null | python3 -c "
import sys, json
try:
d = json.load(sys.stdin)
self = d.get('Self', {})
dns = self.get('DNSName', '').rstrip('.')
print(dns if dns else self.get('HostName', 'unknown'))
except:
print('unknown')
" 2>/dev/null || echo "unknown")
echo ""
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${GREEN} ✅ セットアップ完了!${NC}"
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
echo -e " 🌐 アクセスURL (Tailscaleネットワーク内から):"
echo -e " ${CYAN}https://${TS_HOSTNAME}${NC}"
echo ""
if [[ -n "$OPENCODE_PASSWORD" ]]; then
echo -e " 🔐 認証情報:"
echo -e " ユーザー名: ${OPENCODE_USERNAME}"
echo -e " パスワード: (入力した値)"
else
echo -e " ⚠️ 認証なし(パスワード未設定)"
fi
echo ""
echo -e " 📋 管理コマンド:"
echo -e " ログ確認: journalctl -u opencode-web -f"
echo -e " 再起動: sudo systemctl restart opencode-web"
echo -e " 停止: sudo systemctl stop opencode-web"
echo -e " serve 確認: tailscale serve status"
echo ""
}
# ── メイン ────────────────────────────────────────────────────────────────────
main() {
prompt_password # ① まずパスワードを聞く(sudo前)
check_prerequisites # ② Tailscale確認
install_opencode # ③ opencode インストール
setup_workdir # ④ 設定ファイル生成
setup_systemd_service # ⑤ systemd サービス登録(sudo)
setup_tailscale_serve # ⑥ Tailscale serve 設定(sudo)
}
main "$@"
モデルを指定
画面下でモデルを変更出来ます。


