chore(release): v0.8.53 — Arcee support, telegram bridge, provider fixes

- Fix Rust syntax/clippy fallout in client.rs, cli/src/lib.rs, web_search.rs
- Fix 0.8.53 release metadata: changelog links, TUI changelog, npm wrapper
- Update visible help copy for multi-provider support
- Add telegram-bridge integration with deploy configs
- Add US remote VM quickstart doc
- Update Tencent Cloud deploy scripts and docs
- Bump npm wrapper to 0.8.53
This commit is contained in:
Hunter Bown
2026-06-03 16:12:38 -07:00
parent f884ceb6af
commit 772ec46c98
30 changed files with 2561 additions and 186 deletions
+46 -43
View File
@@ -6,15 +6,15 @@ if [[ "${EUID}" -ne 0 ]]; then
exit 1
fi
DEEPSEEK_USER="${DEEPSEEK_USER:-codewhale}"
DEEPSEEK_ROOT="${DEEPSEEK_ROOT:-/opt/codewhale}"
CODEWHALE_USER="${CODEWHALE_USER:-${DEEPSEEK_USER:-codewhale}}"
CODEWHALE_ROOT="${CODEWHALE_ROOT:-${DEEPSEEK_ROOT:-/opt/codewhale}}"
WHALEBRO_ROOT="${WHALEBRO_ROOT:-/opt/whalebro}"
REPO_URL="${DEEPSEEK_REPO_URL:-https://github.com/Hmbown/CodeWhale.git}"
REPO_URL="${CODEWHALE_REPO_URL:-${DEEPSEEK_REPO_URL:-https://github.com/Hmbown/CodeWhale.git}}"
WHALEBRO_EXTRA_REPOS="${WHALEBRO_EXTRA_REPOS:-}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SOURCE_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
SOURCE_BRANCH="$(git -C "${SOURCE_ROOT}" branch --show-current 2>/dev/null || true)"
REPO_BRANCH="${DEEPSEEK_REPO_BRANCH:-${SOURCE_BRANCH:-main}}"
REPO_BRANCH="${CODEWHALE_REPO_BRANCH:-${DEEPSEEK_REPO_BRANCH:-${SOURCE_BRANCH:-main}}}"
apt-get update
apt-get install -y \
@@ -35,22 +35,24 @@ apt-get install -y \
node_major="$(node -p "Number(process.versions.node.split('.')[0])")"
if (( node_major < 18 )); then
echo "Node.js 18+ is required for the Feishu bridge; install a newer Node.js before running install-services.sh." >&2
echo "Node.js 18+ is required for the phone bridges; install a newer Node.js before running install-services.sh." >&2
fi
if ! id -u "${DEEPSEEK_USER}" >/dev/null 2>&1; then
useradd --create-home --shell /bin/bash "${DEEPSEEK_USER}"
if ! id -u "${CODEWHALE_USER}" >/dev/null 2>&1; then
useradd --create-home --shell /bin/bash "${CODEWHALE_USER}"
fi
install -d -o "${DEEPSEEK_USER}" -g "${DEEPSEEK_USER}" "${DEEPSEEK_ROOT}"
install -d -o "${DEEPSEEK_USER}" -g "${DEEPSEEK_USER}" "${DEEPSEEK_ROOT}/bridge"
install -d -o "${DEEPSEEK_USER}" -g "${DEEPSEEK_USER}" "${WHALEBRO_ROOT}"
install -d -o "${DEEPSEEK_USER}" -g "${DEEPSEEK_USER}" "${WHALEBRO_ROOT}/worktrees"
install -d -m 0750 -o root -g "${DEEPSEEK_USER}" /etc/deepseek
install -d -m 0700 -o "${DEEPSEEK_USER}" -g "${DEEPSEEK_USER}" /var/lib/codewhale-feishu-bridge
install -d -o "${CODEWHALE_USER}" -g "${CODEWHALE_USER}" "${CODEWHALE_ROOT}"
install -d -o "${CODEWHALE_USER}" -g "${CODEWHALE_USER}" "${CODEWHALE_ROOT}/bridge"
install -d -o "${CODEWHALE_USER}" -g "${CODEWHALE_USER}" "${CODEWHALE_ROOT}/telegram-bridge"
install -d -o "${CODEWHALE_USER}" -g "${CODEWHALE_USER}" "${WHALEBRO_ROOT}"
install -d -o "${CODEWHALE_USER}" -g "${CODEWHALE_USER}" "${WHALEBRO_ROOT}/worktrees"
install -d -m 0750 -o root -g "${CODEWHALE_USER}" /etc/codewhale
install -d -m 0700 -o "${CODEWHALE_USER}" -g "${CODEWHALE_USER}" /var/lib/codewhale-feishu-bridge
install -d -m 0700 -o "${CODEWHALE_USER}" -g "${CODEWHALE_USER}" /var/lib/codewhale-telegram-bridge
if [[ ! -d "${WHALEBRO_ROOT}/codewhale/.git" ]]; then
sudo -u "${DEEPSEEK_USER}" git clone --branch "${REPO_BRANCH}" "${REPO_URL}" "${WHALEBRO_ROOT}/codewhale"
sudo -u "${CODEWHALE_USER}" git clone --branch "${REPO_BRANCH}" "${REPO_URL}" "${WHALEBRO_ROOT}/codewhale"
fi
for repo_spec in ${WHALEBRO_EXTRA_REPOS}; do
@@ -61,48 +63,49 @@ for repo_spec in ${WHALEBRO_EXTRA_REPOS}; do
continue
fi
if [[ ! -d "${WHALEBRO_ROOT}/${repo_name}/.git" ]]; then
sudo -u "${DEEPSEEK_USER}" git clone "${repo_url}" "${WHALEBRO_ROOT}/${repo_name}" || {
sudo -u "${CODEWHALE_USER}" git clone "${repo_url}" "${WHALEBRO_ROOT}/${repo_name}" || {
echo "Warning: failed to clone optional repo ${repo_name} from ${repo_url}" >&2
}
fi
done
if [[ ! -f /etc/deepseek/runtime.env ]]; then
cat >/etc/deepseek/runtime.env <<'EOF'
DEEPSEEK_RUNTIME_TOKEN=replace-with-long-random-token
DEEPSEEK_RUNTIME_PORT=7878
DEEPSEEK_RUNTIME_WORKERS=2
DEEPSEEK_API_KEY=replace-with-deepseek-platform-key
if [[ ! -f /etc/codewhale/runtime.env ]]; then
cat >/etc/codewhale/runtime.env <<'EOF'
CODEWHALE_RUNTIME_TOKEN=replace-with-long-random-token
CODEWHALE_RUNTIME_PORT=7878
CODEWHALE_RUNTIME_WORKERS=2
CODEWHALE_PROVIDER=deepseek
DEEPSEEK_API_KEY=replace-with-provider-key
RUST_LOG=info
EOF
chown root:"${DEEPSEEK_USER}" /etc/deepseek/runtime.env
chmod 0640 /etc/deepseek/runtime.env
chown root:"${CODEWHALE_USER}" /etc/codewhale/runtime.env
chmod 0640 /etc/codewhale/runtime.env
fi
if [[ ! -f /etc/deepseek/feishu-bridge.env ]]; then
cat >/etc/deepseek/feishu-bridge.env <<'EOF'
if [[ ! -f /etc/codewhale/feishu-bridge.env ]]; then
cat >/etc/codewhale/feishu-bridge.env <<'EOF'
FEISHU_APP_ID=cli_xxxxxxxxxxxxxxxx
FEISHU_APP_SECRET=replace-with-app-secret
FEISHU_DOMAIN=feishu
DEEPSEEK_RUNTIME_URL=http://127.0.0.1:7878
DEEPSEEK_RUNTIME_TOKEN=replace-with-same-token-as-runtime-env
DEEPSEEK_WORKSPACE=/opt/whalebro
DEEPSEEK_MODEL=auto
DEEPSEEK_MODE=agent
DEEPSEEK_ALLOW_SHELL=true
DEEPSEEK_TRUST_MODE=false
DEEPSEEK_AUTO_APPROVE=false
DEEPSEEK_CHAT_ALLOWLIST=
DEEPSEEK_ALLOW_UNLISTED=false
CODEWHALE_RUNTIME_URL=http://127.0.0.1:7878
CODEWHALE_RUNTIME_TOKEN=replace-with-same-token-as-runtime-env
CODEWHALE_WORKSPACE=/opt/whalebro
CODEWHALE_MODEL=auto
CODEWHALE_MODE=agent
CODEWHALE_ALLOW_SHELL=true
CODEWHALE_TRUST_MODE=false
CODEWHALE_AUTO_APPROVE=false
CODEWHALE_CHAT_ALLOWLIST=
CODEWHALE_ALLOW_UNLISTED=false
FEISHU_THREAD_MAP_PATH=/var/lib/codewhale-feishu-bridge/thread-map.json
FEISHU_ALLOW_GROUPS=false
FEISHU_REQUIRE_PREFIX_IN_GROUP=true
FEISHU_GROUP_PREFIX=/ds
FEISHU_GROUP_PREFIX=/cw
FEISHU_MAX_REPLY_CHARS=3500
DEEPSEEK_TURN_TIMEOUT_MS=900000
CODEWHALE_TURN_TIMEOUT_MS=900000
EOF
chown root:"${DEEPSEEK_USER}" /etc/deepseek/feishu-bridge.env
chmod 0640 /etc/deepseek/feishu-bridge.env
chown root:"${CODEWHALE_USER}" /etc/codewhale/feishu-bridge.env
chmod 0640 /etc/codewhale/feishu-bridge.env
fi
ufw allow OpenSSH
@@ -113,14 +116,14 @@ cat <<EOF
Base server setup complete.
Next:
1. Install Rust 1.88+ for ${DEEPSEEK_USER}; rustup is the usual path.
1. Install Rust 1.88+ for ${CODEWHALE_USER}; rustup is the usual path.
2. Build/install both binaries:
sudo -iu ${DEEPSEEK_USER}
sudo -iu ${CODEWHALE_USER}
cd ${WHALEBRO_ROOT}/codewhale
cargo install --path crates/cli --locked --force
cargo install --path crates/tui --locked --force
3. Copy integrations/feishu-bridge to ${DEEPSEEK_ROOT}/bridge and run npm install.
4. Edit /etc/deepseek/runtime.env and /etc/deepseek/feishu-bridge.env.
3. Copy integrations/feishu-bridge or integrations/telegram-bridge to ${CODEWHALE_ROOT} and run npm install.
4. Edit /etc/codewhale/runtime.env and the selected bridge env file.
5. Install systemd units with scripts/tencent-lighthouse/install-services.sh.
6. After the env files are edited and services are started, run:
sudo bash scripts/tencent-lighthouse/doctor.sh
+92 -32
View File
@@ -1,13 +1,49 @@
#!/usr/bin/env bash
set -euo pipefail
DEEPSEEK_USER="${DEEPSEEK_USER:-codewhale}"
DEEPSEEK_ROOT="${DEEPSEEK_ROOT:-/opt/codewhale}"
CODEWHALE_USER="${CODEWHALE_USER:-${DEEPSEEK_USER:-codewhale}}"
CODEWHALE_ROOT="${CODEWHALE_ROOT:-${DEEPSEEK_ROOT:-/opt/codewhale}}"
WHALEBRO_ROOT="${WHALEBRO_ROOT:-/opt/whalebro}"
RUNTIME_ENV="${RUNTIME_ENV:-/etc/deepseek/runtime.env}"
BRIDGE_ENV="${BRIDGE_ENV:-/etc/deepseek/feishu-bridge.env}"
BRIDGE_DIR="${BRIDGE_DIR:-${DEEPSEEK_ROOT}/bridge}"
if [[ -z "${RUNTIME_ENV:-}" ]]; then
if [[ -f /etc/codewhale/runtime.env || ! -f /etc/deepseek/runtime.env ]]; then
RUNTIME_ENV="/etc/codewhale/runtime.env"
else
RUNTIME_ENV="/etc/deepseek/runtime.env"
fi
fi
REPO_ROOT="${REPO_ROOT:-${WHALEBRO_ROOT}/codewhale}"
BRIDGE_KIND="${CODEWHALE_BRIDGE:-${DEEPSEEK_BRIDGE:-feishu}}"
case "${BRIDGE_KIND}" in
feishu|lark)
if [[ -z "${BRIDGE_ENV:-}" ]]; then
if [[ -f /etc/codewhale/feishu-bridge.env || ! -f /etc/deepseek/feishu-bridge.env ]]; then
BRIDGE_ENV="/etc/codewhale/feishu-bridge.env"
else
BRIDGE_ENV="/etc/deepseek/feishu-bridge.env"
fi
fi
BRIDGE_DIR="${BRIDGE_DIR:-${CODEWHALE_ROOT}/bridge}"
BRIDGE_UNIT="${BRIDGE_UNIT:-codewhale-feishu-bridge}"
BRIDGE_PACKAGE="${BRIDGE_PACKAGE:-integrations/feishu-bridge}"
;;
telegram)
if [[ -z "${BRIDGE_ENV:-}" ]]; then
if [[ -f /etc/codewhale/telegram-bridge.env || ! -f /etc/deepseek/telegram-bridge.env ]]; then
BRIDGE_ENV="/etc/codewhale/telegram-bridge.env"
else
BRIDGE_ENV="/etc/deepseek/telegram-bridge.env"
fi
fi
BRIDGE_DIR="${BRIDGE_DIR:-${CODEWHALE_ROOT}/telegram-bridge}"
BRIDGE_UNIT="${BRIDGE_UNIT:-codewhale-telegram-bridge}"
BRIDGE_PACKAGE="${BRIDGE_PACKAGE:-integrations/telegram-bridge}"
;;
*)
echo "Unknown bridge '${BRIDGE_KIND}'. Use CODEWHALE_BRIDGE=feishu or CODEWHALE_BRIDGE=telegram." >&2
exit 1
;;
esac
failures=0
warnings=0
@@ -44,6 +80,20 @@ env_value() {
|| true
}
env_value_any() {
local file="$1"
shift
local value
for key in "$@"; do
value="$(env_value "${file}" "${key}")"
if [[ -n "${value}" ]]; then
printf '%s\n' "${value}"
return 0
fi
done
return 0
}
is_placeholder() {
local value
value="$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')"
@@ -72,7 +122,7 @@ check_commands() {
check_node() {
section "Node"
if ! have_command node; then
fail "node is required for the Feishu bridge"
fail "node is required for the phone bridge"
return
fi
local major
@@ -98,7 +148,7 @@ check_workspace() {
check_binaries() {
section "CodeWhale binaries"
local cargo_bin="/home/${DEEPSEEK_USER}/.cargo/bin"
local cargo_bin="/home/${CODEWHALE_USER}/.cargo/bin"
local codewhale="${cargo_bin}/codewhale"
local tui="${cargo_bin}/codewhale-tui"
if [[ -x "${codewhale}" ]]; then
@@ -138,22 +188,28 @@ check_env() {
check_env_file "${RUNTIME_ENV}" "runtime"
check_env_file "${BRIDGE_ENV}" "bridge"
local runtime_token bridge_token api_key workspace domain allow_groups allow_unlisted
runtime_token="$(env_value "${RUNTIME_ENV}" DEEPSEEK_RUNTIME_TOKEN)"
bridge_token="$(env_value "${BRIDGE_ENV}" DEEPSEEK_RUNTIME_TOKEN)"
api_key="$(env_value "${RUNTIME_ENV}" DEEPSEEK_API_KEY)"
workspace="$(env_value "${BRIDGE_ENV}" DEEPSEEK_WORKSPACE)"
domain="$(env_value "${BRIDGE_ENV}" FEISHU_DOMAIN)"
allow_groups="$(env_value "${BRIDGE_ENV}" FEISHU_ALLOW_GROUPS)"
allow_unlisted="$(env_value "${BRIDGE_ENV}" DEEPSEEK_ALLOW_UNLISTED)"
local runtime_token bridge_token workspace domain allow_groups allow_unlisted provider
runtime_token="$(env_value_any "${RUNTIME_ENV}" CODEWHALE_RUNTIME_TOKEN DEEPSEEK_RUNTIME_TOKEN)"
bridge_token="$(env_value_any "${BRIDGE_ENV}" CODEWHALE_RUNTIME_TOKEN DEEPSEEK_RUNTIME_TOKEN)"
workspace="$(env_value_any "${BRIDGE_ENV}" CODEWHALE_WORKSPACE DEEPSEEK_WORKSPACE)"
provider="$(env_value_any "${RUNTIME_ENV}" CODEWHALE_PROVIDER DEEPSEEK_PROVIDER)"
if [[ "${BRIDGE_KIND}" == "telegram" ]]; then
allow_groups="$(env_value "${BRIDGE_ENV}" TELEGRAM_ALLOW_GROUPS)"
allow_unlisted="$(env_value_any "${BRIDGE_ENV}" TELEGRAM_ALLOW_UNLISTED CODEWHALE_ALLOW_UNLISTED DEEPSEEK_ALLOW_UNLISTED)"
else
domain="$(env_value "${BRIDGE_ENV}" FEISHU_DOMAIN)"
allow_groups="$(env_value "${BRIDGE_ENV}" FEISHU_ALLOW_GROUPS)"
allow_unlisted="$(env_value_any "${BRIDGE_ENV}" CODEWHALE_ALLOW_UNLISTED DEEPSEEK_ALLOW_UNLISTED)"
fi
if is_placeholder "${runtime_token}"; then
fail "runtime DEEPSEEK_RUNTIME_TOKEN is missing or still a placeholder"
fail "runtime token is missing or still a placeholder"
else
pass "runtime token is set"
fi
if is_placeholder "${bridge_token}"; then
fail "bridge DEEPSEEK_RUNTIME_TOKEN is missing or still a placeholder"
fail "bridge token is missing or still a placeholder"
else
pass "bridge token is set"
fi
@@ -162,19 +218,21 @@ check_env() {
elif [[ -n "${runtime_token}" && -n "${bridge_token}" ]]; then
pass "runtime and bridge tokens match"
fi
if is_placeholder "${api_key}"; then
warn "DEEPSEEK_API_KEY is missing or still a placeholder"
if is_placeholder "${provider}"; then
warn "runtime provider is missing or still a placeholder"
else
pass "DEEPSEEK_API_KEY is set"
pass "runtime provider is ${provider}"
fi
[[ "${workspace}" == "${WHALEBRO_ROOT}" || "${workspace}" == "${WHALEBRO_ROOT}/"* ]] \
&& pass "bridge workspace is under ${WHALEBRO_ROOT}" \
|| warn "bridge workspace is outside ${WHALEBRO_ROOT}: ${workspace:-unset}"
[[ "${domain:-feishu}" == "feishu" || "${domain:-feishu}" == "lark" || "${domain:-feishu}" == https://open.* ]] \
&& pass "FEISHU_DOMAIN is ${domain:-feishu}" \
|| fail "FEISHU_DOMAIN must be feishu, lark, or an https://open.* URL"
if [[ "${BRIDGE_KIND}" != "telegram" ]]; then
[[ "${domain:-feishu}" == "feishu" || "${domain:-feishu}" == "lark" || "${domain:-feishu}" == https://open.* ]] \
&& pass "FEISHU_DOMAIN is ${domain:-feishu}" \
|| fail "FEISHU_DOMAIN must be feishu, lark, or an https://open.* URL"
fi
[[ "${allow_groups:-false}" == "true" && "${allow_unlisted:-false}" == "true" ]] \
&& fail "group control cannot run with DEEPSEEK_ALLOW_UNLISTED=true" \
&& fail "group control cannot run with allow-unlisted=true" \
|| pass "group/unlisted mode is not openly combined"
}
@@ -182,15 +240,15 @@ check_validator() {
section "Bridge config validator"
local validator="${BRIDGE_DIR}/scripts/validate-config.mjs"
if [[ ! -f "${validator}" ]]; then
validator="${REPO_ROOT}/integrations/feishu-bridge/scripts/validate-config.mjs"
validator="${REPO_ROOT}/${BRIDGE_PACKAGE}/scripts/validate-config.mjs"
fi
if [[ ! -f "${validator}" ]]; then
warn "bridge config validator is not installed"
return
fi
local runner=(node)
if [[ "${EUID}" -eq 0 ]] && id -u "${DEEPSEEK_USER}" >/dev/null 2>&1 && have_command sudo; then
runner=(sudo -u "${DEEPSEEK_USER}" node)
if [[ "${EUID}" -eq 0 ]] && id -u "${CODEWHALE_USER}" >/dev/null 2>&1 && have_command sudo; then
runner=(sudo -u "${CODEWHALE_USER}" node)
fi
if "${runner[@]}" "${validator}" --env "${BRIDGE_ENV}" --runtime-env "${RUNTIME_ENV}" --workspace-root "${WHALEBRO_ROOT}" --check-filesystem; then
pass "bridge config validator passed"
@@ -205,7 +263,7 @@ check_systemd() {
warn "systemd is not available in this environment"
return
fi
for unit in codewhale-runtime codewhale-feishu-bridge; do
for unit in codewhale-runtime "${BRIDGE_UNIT}"; do
[[ -f "/etc/systemd/system/${unit}.service" ]] \
&& pass "${unit}.service is installed" \
|| fail "${unit}.service is missing"
@@ -222,7 +280,9 @@ check_bridge_install() {
section "Bridge install"
[[ -f "${BRIDGE_DIR}/package.json" ]] && pass "${BRIDGE_DIR}/package.json exists" || fail "bridge package.json is missing"
[[ -f "${BRIDGE_DIR}/src/index.mjs" ]] && pass "${BRIDGE_DIR}/src/index.mjs exists" || fail "bridge entrypoint is missing"
if [[ -d "${BRIDGE_DIR}/node_modules/@larksuiteoapi/node-sdk" ]]; then
if [[ "${BRIDGE_KIND}" == "telegram" ]]; then
pass "Telegram bridge has no required production npm dependencies"
elif [[ -d "${BRIDGE_DIR}/node_modules/@larksuiteoapi/node-sdk" ]]; then
pass "Lark SDK dependency is installed"
else
warn "Lark SDK dependency is not installed under ${BRIDGE_DIR}/node_modules"
@@ -232,9 +292,9 @@ check_bridge_install() {
check_localhost_health() {
section "Localhost health"
local port token
port="$(env_value "${RUNTIME_ENV}" DEEPSEEK_RUNTIME_PORT)"
port="$(env_value_any "${RUNTIME_ENV}" CODEWHALE_RUNTIME_PORT DEEPSEEK_RUNTIME_PORT)"
port="${port:-7878}"
token="$(env_value "${BRIDGE_ENV}" DEEPSEEK_RUNTIME_TOKEN)"
token="$(env_value_any "${BRIDGE_ENV}" CODEWHALE_RUNTIME_TOKEN DEEPSEEK_RUNTIME_TOKEN)"
if have_command ss; then
local listeners
@@ -287,7 +347,7 @@ check_localhost_health() {
}
main() {
printf 'Tencent Lighthouse DeepSeek doctor\n'
printf 'Tencent Lighthouse CodeWhale doctor (%s bridge)\n' "${BRIDGE_KIND}"
check_commands
check_node
check_workspace
+59 -18
View File
@@ -7,39 +7,80 @@ if [[ "${EUID}" -ne 0 ]]; then
fi
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
DEEPSEEK_USER="${DEEPSEEK_USER:-codewhale}"
DEEPSEEK_ROOT="${DEEPSEEK_ROOT:-/opt/codewhale}"
CODEWHALE_USER="${CODEWHALE_USER:-${DEEPSEEK_USER:-codewhale}}"
CODEWHALE_ROOT="${CODEWHALE_ROOT:-${DEEPSEEK_ROOT:-/opt/codewhale}}"
BRIDGE_KIND="${CODEWHALE_BRIDGE:-${DEEPSEEK_BRIDGE:-feishu}}"
install -d -o "${DEEPSEEK_USER}" -g "${DEEPSEEK_USER}" "${DEEPSEEK_ROOT}/bridge"
case "${BRIDGE_KIND}" in
feishu|lark)
BRIDGE_SRC="integrations/feishu-bridge"
BRIDGE_DST="${CODEWHALE_ROOT}/bridge"
BRIDGE_UNIT="codewhale-feishu-bridge.service"
BRIDGE_ENV="/etc/codewhale/feishu-bridge.env"
BRIDGE_ENV_EXAMPLE="deploy/tencent-lighthouse/examples/feishu-bridge.env.example"
BRIDGE_STATE_DIR="/var/lib/codewhale-feishu-bridge"
VALIDATOR="integrations/feishu-bridge/scripts/validate-config.mjs"
;;
telegram)
BRIDGE_SRC="integrations/telegram-bridge"
BRIDGE_DST="${CODEWHALE_ROOT}/telegram-bridge"
BRIDGE_UNIT="codewhale-telegram-bridge.service"
BRIDGE_ENV="/etc/codewhale/telegram-bridge.env"
BRIDGE_ENV_EXAMPLE="deploy/tencent-lighthouse/examples/telegram-bridge.env.example"
BRIDGE_STATE_DIR="/var/lib/codewhale-telegram-bridge"
VALIDATOR="integrations/telegram-bridge/scripts/validate-config.mjs"
;;
*)
echo "Unknown bridge '${BRIDGE_KIND}'. Use CODEWHALE_BRIDGE=feishu or CODEWHALE_BRIDGE=telegram." >&2
exit 1
;;
esac
install -d -m 0750 -o root -g "${CODEWHALE_USER}" /etc/codewhale
install -d -m 0700 -o "${CODEWHALE_USER}" -g "${CODEWHALE_USER}" "${BRIDGE_STATE_DIR}"
install -d -o "${CODEWHALE_USER}" -g "${CODEWHALE_USER}" "${BRIDGE_DST}"
if [[ ! -f /etc/codewhale/runtime.env && -f "${REPO_ROOT}/deploy/tencent-lighthouse/examples/runtime.env.example" ]]; then
install -m 0640 -o root -g "${CODEWHALE_USER}" \
"${REPO_ROOT}/deploy/tencent-lighthouse/examples/runtime.env.example" \
/etc/codewhale/runtime.env
fi
if [[ ! -f "${BRIDGE_ENV}" && -f "${REPO_ROOT}/${BRIDGE_ENV_EXAMPLE}" ]]; then
install -m 0640 -o root -g "${CODEWHALE_USER}" \
"${REPO_ROOT}/${BRIDGE_ENV_EXAMPLE}" \
"${BRIDGE_ENV}"
fi
rsync -a --delete \
--exclude node_modules \
"${REPO_ROOT}/integrations/feishu-bridge/" \
"${DEEPSEEK_ROOT}/bridge/"
chown -R "${DEEPSEEK_USER}:${DEEPSEEK_USER}" "${DEEPSEEK_ROOT}/bridge"
"${REPO_ROOT}/${BRIDGE_SRC}/" \
"${BRIDGE_DST}/"
chown -R "${CODEWHALE_USER}:${CODEWHALE_USER}" "${BRIDGE_DST}"
if [[ -f "${DEEPSEEK_ROOT}/bridge/package-lock.json" ]]; then
sudo -u "${DEEPSEEK_USER}" npm --prefix "${DEEPSEEK_ROOT}/bridge" ci --omit=dev
if [[ -f "${BRIDGE_DST}/package-lock.json" ]]; then
sudo -u "${CODEWHALE_USER}" npm --prefix "${BRIDGE_DST}" ci --omit=dev
else
sudo -u "${DEEPSEEK_USER}" npm --prefix "${DEEPSEEK_ROOT}/bridge" install --omit=dev
sudo -u "${CODEWHALE_USER}" npm --prefix "${BRIDGE_DST}" install --omit=dev
fi
install -m 0644 "${REPO_ROOT}/deploy/tencent-lighthouse/systemd/codewhale-runtime.service" /etc/systemd/system/codewhale-runtime.service
install -m 0644 "${REPO_ROOT}/deploy/tencent-lighthouse/systemd/codewhale-feishu-bridge.service" /etc/systemd/system/codewhale-feishu-bridge.service
install -m 0644 "${REPO_ROOT}/deploy/tencent-lighthouse/systemd/${BRIDGE_UNIT}" "/etc/systemd/system/${BRIDGE_UNIT}"
systemctl daemon-reload
systemctl enable codewhale-runtime codewhale-feishu-bridge
systemctl enable codewhale-runtime "${BRIDGE_UNIT}"
cat <<'EOF'
Services installed but not started.
Before starting, verify:
/etc/deepseek/runtime.env
/etc/deepseek/feishu-bridge.env
sudo -u codewhale node /opt/codewhale/bridge/scripts/validate-config.mjs --env /etc/deepseek/feishu-bridge.env --runtime-env /etc/deepseek/runtime.env --workspace-root /opt/whalebro --check-filesystem
/etc/codewhale/runtime.env
EOF
cat <<EOF
${BRIDGE_ENV}
sudo -u ${CODEWHALE_USER} node ${REPO_ROOT}/${VALIDATOR} --env ${BRIDGE_ENV} --runtime-env /etc/codewhale/runtime.env --workspace-root /opt/whalebro --check-filesystem
Then run:
sudo systemctl start codewhale-runtime
sudo systemctl start codewhale-feishu-bridge
sudo bash /opt/whalebro/codewhale/scripts/tencent-lighthouse/doctor.sh
sudo journalctl -u codewhale-feishu-bridge -f
sudo systemctl start ${BRIDGE_UNIT}
sudo CODEWHALE_BRIDGE=${BRIDGE_KIND} bash /opt/whalebro/codewhale/scripts/tencent-lighthouse/doctor.sh
sudo journalctl -u ${BRIDGE_UNIT} -f
EOF