feat: implement channel runtime connectors
This commit is contained in:
@ -37,6 +37,8 @@ INSTANCES_ROOT="${INSTANCES_ROOT:-$INSTANCES_ROOT_DEFAULT}"
|
||||
REGISTRY_PATH="${REGISTRY_PATH:-$REGISTRY_PATH_DEFAULT}"
|
||||
NETWORK_NAME="${NETWORK_NAME:-}"
|
||||
HOST_BIND_IP="${HOST_BIND_IP:-127.0.0.1}"
|
||||
INITIAL_SKILLS_DIR="${INITIAL_SKILLS_DIR:-${SCRIPT_DIR}/../skills}"
|
||||
SEED_INITIAL_SKILLS=1
|
||||
FORCE_BUILD=0
|
||||
REPLACE=0
|
||||
|
||||
@ -78,6 +80,9 @@ Optional:
|
||||
--registry <path> Registry JSON path. Default: ./runtime/registry/instances.json
|
||||
--network <name> Optional docker network name.
|
||||
--host-bind-ip <ip> Host bind IP for published port. Default: 127.0.0.1
|
||||
--initial-skills-dir <path> Directory copied into workspace/skills on first create.
|
||||
Default: ../skills
|
||||
--skip-initial-skills Do not seed initial workspace skills.
|
||||
--build Force rebuild image before running.
|
||||
--replace Remove existing container with same name before running.
|
||||
--help Show this help.
|
||||
@ -225,6 +230,69 @@ data = {
|
||||
"name": os.environ["BACKEND_NAME"].strip(),
|
||||
"publicBaseUrl": os.environ["PUBLIC_URL"].strip(),
|
||||
},
|
||||
"channels": {
|
||||
"telegram-main": {
|
||||
"enabled": False,
|
||||
"kind": "telegram",
|
||||
"mode": "polling",
|
||||
"accountId": "bot-main",
|
||||
"displayName": "Telegram Main",
|
||||
"secrets": {
|
||||
"botToken": "",
|
||||
},
|
||||
"config": {
|
||||
"requireMentionInGroups": True,
|
||||
"maxMessageChars": 4096,
|
||||
},
|
||||
},
|
||||
"feishu-main": {
|
||||
"enabled": False,
|
||||
"kind": "feishu",
|
||||
"mode": "websocket",
|
||||
"accountId": "tenant-main",
|
||||
"displayName": "Feishu Main",
|
||||
"secrets": {
|
||||
"appId": "",
|
||||
"appSecret": "",
|
||||
},
|
||||
"config": {
|
||||
"domain": "feishu",
|
||||
"connectionMode": "websocket",
|
||||
"requireMentionInGroups": True,
|
||||
},
|
||||
},
|
||||
"qqbot-main": {
|
||||
"enabled": False,
|
||||
"kind": "qqbot",
|
||||
"mode": "websocket",
|
||||
"accountId": "qqbot-main",
|
||||
"displayName": "QQ Bot Main",
|
||||
"secrets": {
|
||||
"appId": "",
|
||||
"clientSecret": "",
|
||||
},
|
||||
"config": {
|
||||
"dmPolicy": "open",
|
||||
"groupPolicy": "allowlist",
|
||||
"markdownSupport": False,
|
||||
},
|
||||
},
|
||||
"weixin-main": {
|
||||
"enabled": False,
|
||||
"kind": "weixin",
|
||||
"mode": "polling",
|
||||
"accountId": "wx-main",
|
||||
"displayName": "Weixin Main",
|
||||
"secrets": {
|
||||
"token": "",
|
||||
},
|
||||
"config": {
|
||||
"dmPolicy": "open",
|
||||
"groupPolicy": "disabled",
|
||||
"textBatchDelaySeconds": 0.5,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
target.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
||||
@ -255,6 +323,66 @@ target.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n", encodin
|
||||
PY
|
||||
}
|
||||
|
||||
seed_initial_skills() {
|
||||
local workspace_path="$1"
|
||||
local initial_skills_dir="$2"
|
||||
local target_dir="${workspace_path}/skills"
|
||||
|
||||
if [[ "$SEED_INITIAL_SKILLS" -ne 1 ]]; then
|
||||
return
|
||||
fi
|
||||
if [[ ! -d "$initial_skills_dir" ]]; then
|
||||
log "initial skills directory not found, skipping: ${initial_skills_dir}"
|
||||
return
|
||||
fi
|
||||
|
||||
mkdir -p "$target_dir"
|
||||
INITIAL_SKILLS_DIR="$initial_skills_dir" TARGET_DIR="$target_dir" python3 - <<'PY'
|
||||
import json
|
||||
import shutil
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
initial = Path(os.environ["INITIAL_SKILLS_DIR"]).resolve()
|
||||
target = Path(os.environ["TARGET_DIR"]).resolve()
|
||||
|
||||
for child in sorted(initial.iterdir()):
|
||||
if child.name.startswith("."):
|
||||
continue
|
||||
destination = target / child.name
|
||||
if destination.exists():
|
||||
continue
|
||||
if child.is_dir():
|
||||
shutil.copytree(child, destination)
|
||||
elif child.is_file():
|
||||
shutil.copy2(child, destination)
|
||||
|
||||
for index_name in ("published", "disabled"):
|
||||
initial_index = initial / "_index" / f"{index_name}.json"
|
||||
target_index = target / "_index" / f"{index_name}.json"
|
||||
if not initial_index.exists():
|
||||
continue
|
||||
try:
|
||||
initial_items = json.loads(initial_index.read_text(encoding="utf-8")).get("items", [])
|
||||
except json.JSONDecodeError:
|
||||
initial_items = []
|
||||
if target_index.exists():
|
||||
try:
|
||||
target_items = json.loads(target_index.read_text(encoding="utf-8")).get("items", [])
|
||||
except json.JSONDecodeError:
|
||||
target_items = []
|
||||
else:
|
||||
target_items = []
|
||||
merged = []
|
||||
for item in [*target_items, *initial_items]:
|
||||
text = str(item).strip()
|
||||
if text and text not in merged:
|
||||
merged.append(text)
|
||||
target_index.parent.mkdir(parents=True, exist_ok=True)
|
||||
target_index.write_text(json.dumps({"items": merged}, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
||||
PY
|
||||
}
|
||||
|
||||
render_runtime_env_file() {
|
||||
local target_path="$1"
|
||||
|
||||
@ -428,6 +556,14 @@ while [[ $# -gt 0 ]]; do
|
||||
HOST_BIND_IP="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--initial-skills-dir)
|
||||
INITIAL_SKILLS_DIR="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--skip-initial-skills)
|
||||
SEED_INITIAL_SKILLS=0
|
||||
shift
|
||||
;;
|
||||
--build)
|
||||
FORCE_BUILD=1
|
||||
shift
|
||||
@ -531,6 +667,7 @@ mkdir -p "$BEAVER_HOME" "$WORKSPACE_PATH"
|
||||
render_config_json "$CONFIG_PATH"
|
||||
render_auth_users_json "$AUTH_USERS_PATH"
|
||||
render_runtime_env_file "$RUNTIME_ENV_PATH"
|
||||
seed_initial_skills "$WORKSPACE_PATH" "$INITIAL_SKILLS_DIR"
|
||||
|
||||
if [[ "$FORCE_BUILD" -eq 1 ]] || ! image_exists; then
|
||||
log "building image ${IMAGE_NAME}"
|
||||
@ -564,6 +701,7 @@ RUN_ARGS=(
|
||||
-e "APP_PUBLIC_PORT=8080"
|
||||
-e "APP_FRONTEND_PORT=3000"
|
||||
-e "APP_BACKEND_PORT=18080"
|
||||
-e "BEAVER_ENABLE_SELF_RESTART=1"
|
||||
-e "BEAVER_OUTLOOK_MCP_SERVER_ID=${OUTLOOK_MCP_SERVER_ID}"
|
||||
--label "beaver.instance.id=${INSTANCE_ID}"
|
||||
--label "beaver.instance.slug=${INSTANCE_SLUG}"
|
||||
|
||||
Reference in New Issue
Block a user