@yyy9527/openclaw-manager 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
6
+ ENV_FILE="${OPENCLAW_DOTENV_FILE:-${PROJECT_ROOT}/.env}"
7
+ CONFIG_FILE="${OPENCLAW_CONFIG_FILE:-${PROJECT_ROOT}/config/openclaw.env}"
8
+
9
+ log() {
10
+ printf '[check-env] %s\n' "$*"
11
+ }
12
+
13
+ warn() {
14
+ printf '[check-env][WARN] %s\n' "$*" >&2
15
+ }
16
+
17
+ if [ ! -f "${ENV_FILE}" ]; then
18
+ warn "未找到环境文件:${ENV_FILE}"
19
+ warn "请先从 .env.example 复制并填写,或通过 --env-file 指定其他文件。"
20
+ exit 1
21
+ fi
22
+
23
+ set -a
24
+ # shellcheck source=/dev/null
25
+ [ -f "${CONFIG_FILE}" ] && source "${CONFIG_FILE}"
26
+ # shellcheck source=/dev/null
27
+ source "${ENV_FILE}"
28
+ set +a
29
+
30
+ missing=()
31
+ require_var() {
32
+ local key="$1"
33
+ if [ -z "${!key:-}" ]; then
34
+ missing+=("${key}")
35
+ fi
36
+ }
37
+
38
+ # 基础协作能力必需项
39
+ require_var "GIT_TOKEN"
40
+
41
+ # 飞书开启时必需项
42
+ if [ "${FEISHU_ENABLED:-true}" = "true" ]; then
43
+ require_var "FEISHU_WEBHOOK_URL"
44
+ fi
45
+
46
+ if [ "${#missing[@]}" -gt 0 ]; then
47
+ warn "检测到缺失的环境变量(来源:${ENV_FILE}):"
48
+ for key in "${missing[@]}"; do
49
+ warn " - ${key}"
50
+ done
51
+ warn "请补齐后重试。"
52
+ exit 1
53
+ fi
54
+
55
+ log ".env 关键变量检查通过"
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
6
+ CONFIG_FILE="${OPENCLAW_CONFIG_FILE:-${PROJECT_ROOT}/config/openclaw.env}"
7
+ ENV_FILE="${OPENCLAW_DOTENV_FILE:-${PROJECT_ROOT}/.env}"
8
+
9
+ log() {
10
+ printf '[health-check] %s\n' "$*"
11
+ }
12
+
13
+ fail() {
14
+ printf '[health-check][ERROR] %s\n' "$*" >&2
15
+ exit 1
16
+ }
17
+
18
+ require_cmd() {
19
+ local cmd="$1"
20
+ command -v "${cmd}" >/dev/null 2>&1 || fail "缺少命令:${cmd}"
21
+ }
22
+
23
+ api_get() {
24
+ local url="$1"
25
+ curl --silent --show-error \
26
+ -H "Accept: application/json" \
27
+ "${url}"
28
+ }
29
+
30
+ load_env() {
31
+ [ -f "${CONFIG_FILE}" ] || fail "缺少配置文件:${CONFIG_FILE}"
32
+ [ -f "${ENV_FILE}" ] || fail "缺少环境文件:${ENV_FILE}"
33
+ set -a
34
+ # shellcheck source=/dev/null
35
+ source "${CONFIG_FILE}"
36
+ # shellcheck source=/dev/null
37
+ source "${ENV_FILE}"
38
+ set +a
39
+ }
40
+
41
+ check_docker() {
42
+ log "检查 Docker daemon 状态"
43
+ docker info >/dev/null 2>&1 || fail "Docker daemon 不可用,请先启动 Docker Desktop"
44
+ }
45
+
46
+ check_gitee() {
47
+ [ "${GIT_PLATFORM}" = "gitee" ] || fail "当前只支持 gitee 健康检查,检测到 GIT_PLATFORM=${GIT_PLATFORM}"
48
+ : "${GIT_API_BASE:?GIT_API_BASE 未配置}"
49
+ : "${GIT_REPO:?GIT_REPO 未配置}"
50
+ : "${GIT_TOKEN:?GIT_TOKEN 未配置}"
51
+
52
+ log "检查 Gitee 用户令牌有效性"
53
+ local user_json
54
+ user_json="$(api_get "${GIT_API_BASE}/user?access_token=${GIT_TOKEN}")"
55
+ local user_message
56
+ user_message="$(printf '%s' "${user_json}" | jq -r '.message // empty')"
57
+ [ -z "${user_message}" ] || fail "Gitee 用户鉴权失败:${user_message}"
58
+ local login
59
+ login="$(printf '%s' "${user_json}" | jq -r '.login // empty')"
60
+ [ -n "${login}" ] || fail "Gitee 令牌校验失败(未读取到 login)"
61
+ log "Gitee 用户鉴权通过:${login}"
62
+
63
+ log "检查 Gitee 仓库可访问性"
64
+ local repo_json
65
+ repo_json="$(api_get "${GIT_API_BASE}/repos/${GIT_REPO}?access_token=${GIT_TOKEN}")"
66
+ local repo_message
67
+ repo_message="$(printf '%s' "${repo_json}" | jq -r '.message // empty')"
68
+ if [ -n "${repo_message}" ]; then
69
+ fail "仓库访问失败:${repo_message}。请检查 GIT_REPO(应为 owner/repo)和 token 权限。"
70
+ fi
71
+ local full_name
72
+ full_name="$(printf '%s' "${repo_json}" | jq -r '.full_name // empty')"
73
+ [ -n "${full_name}" ] || fail "仓库访问失败,请检查 GIT_REPO 或 token 权限"
74
+ log "仓库可访问:${full_name}"
75
+
76
+ log "检查 Issue API 基础可用性"
77
+ local issues_json
78
+ issues_json="$(api_get "${GIT_API_BASE}/repos/${GIT_REPO}/issues?access_token=${GIT_TOKEN}&state=open&page=1&per_page=1")"
79
+ local issues_message
80
+ issues_message="$(printf '%s' "${issues_json}" | jq -r 'if type=="object" then .message // empty else empty end')"
81
+ if [ -n "${issues_message}" ]; then
82
+ fail "Issue API 调用失败:${issues_message}"
83
+ fi
84
+ printf '%s' "${issues_json}" | jq -e 'if type=="array" then true else false end' >/dev/null || fail "Issue API 返回异常"
85
+ log "Issue API 可用"
86
+ }
87
+
88
+ check_feishu_config() {
89
+ if [ "${FEISHU_ENABLED:-true}" = "true" ]; then
90
+ : "${FEISHU_WEBHOOK_URL:?FEISHU_WEBHOOK_URL 未配置}"
91
+ log "飞书通知已启用,Webhook 已配置"
92
+ else
93
+ log "飞书通知已禁用,跳过"
94
+ fi
95
+ }
96
+
97
+ main() {
98
+ require_cmd curl
99
+ require_cmd jq
100
+ require_cmd docker
101
+ load_env
102
+ check_docker
103
+ check_gitee
104
+ check_feishu_config
105
+ log "健康检查通过"
106
+ }
107
+
108
+ main "$@"
@@ -0,0 +1,119 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
6
+ CONFIG_FILE="${OPENCLAW_CONFIG_FILE:-${PROJECT_ROOT}/config/openclaw.env}"
7
+ ENV_FILE="${OPENCLAW_DOTENV_FILE:-${PROJECT_ROOT}/.env}"
8
+
9
+ log() {
10
+ printf '[install_openclaw] %s\n' "$*"
11
+ }
12
+
13
+ fail() {
14
+ printf '[install_openclaw][ERROR] %s\n' "$*" >&2
15
+ exit 1
16
+ }
17
+
18
+ ensure_file() {
19
+ local file="$1"
20
+ [ -f "${file}" ] || fail "缺少文件:${file}"
21
+ }
22
+
23
+ ensure_cmd() {
24
+ local cmd="$1"
25
+ command -v "${cmd}" >/dev/null 2>&1 || fail "缺少命令:${cmd}"
26
+ }
27
+
28
+ project_name_for_instance() {
29
+ local instance_id="$1"
30
+ printf '%s-%s' "${OPENCLAW_NAME_PREFIX}" "${instance_id}" | tr '[:upper:]' '[:lower:]'
31
+ }
32
+
33
+ load_env() {
34
+ set -a
35
+ # shellcheck source=/dev/null
36
+ source "${CONFIG_FILE}"
37
+ if [ -f "${ENV_FILE}" ]; then
38
+ # shellcheck source=/dev/null
39
+ source "${ENV_FILE}"
40
+ fi
41
+ set +a
42
+ }
43
+
44
+ check_required_vars() {
45
+ : "${OPENCLAW_IMAGE:?OPENCLAW_IMAGE 未配置}"
46
+ : "${OPENCLAW_VERSION:?OPENCLAW_VERSION 未配置}"
47
+ : "${OPENCLAW_INSTANCE_IDS:?OPENCLAW_INSTANCE_IDS 未配置}"
48
+ : "${OPENCLAW_BASE_HTTP_PORT:?OPENCLAW_BASE_HTTP_PORT 未配置}"
49
+ : "${OPENCLAW_CONTAINER_PORT:?OPENCLAW_CONTAINER_PORT 未配置}"
50
+ if [ -z "${OPENCLAW_HOME_ROOT:-}" ]; then
51
+ # 向后兼容旧变量名
52
+ OPENCLAW_HOME_ROOT="${OPENCLAW_DATA_ROOT:-}"
53
+ fi
54
+ : "${OPENCLAW_HOME_ROOT:?OPENCLAW_HOME_ROOT 未配置}"
55
+ OPENCLAW_SHARED_SKILLS_DIR="${OPENCLAW_SHARED_SKILLS_DIR:-${OPENCLAW_HOME_ROOT}/shared/skills}"
56
+ }
57
+
58
+ split_instances() {
59
+ local ids="${OPENCLAW_INSTANCE_IDS}"
60
+ IFS=',' read -r -a INSTANCE_LIST <<<"${ids}"
61
+ }
62
+
63
+ port_in_use() {
64
+ local port="$1"
65
+ lsof -iTCP:"${port}" -sTCP:LISTEN -n -P >/dev/null 2>&1
66
+ }
67
+
68
+ compose_up_for_instance() {
69
+ local instance_id="$1"
70
+ local ordinal="$2"
71
+ local http_port="$((OPENCLAW_BASE_HTTP_PORT + ordinal))"
72
+ local instance_home_dir="${OPENCLAW_HOME_ROOT}/${instance_id}"
73
+ local data_dir="${instance_home_dir}"
74
+ local logs_dir="${PROJECT_ROOT}/logs/${OPENCLAW_NAME_PREFIX}-${instance_id}"
75
+
76
+ if port_in_use "${http_port}"; then
77
+ fail "HTTP 端口已被占用:${http_port}(实例 ${instance_id})"
78
+ fi
79
+ mkdir -p "${instance_home_dir}" "${OPENCLAW_SHARED_SKILLS_DIR}" "${logs_dir}"
80
+ log "准备启动实例 ${instance_id}(host_http=${http_port}, container_port=${OPENCLAW_CONTAINER_PORT})"
81
+
82
+ OPENCLAW_INSTANCE_ID="${instance_id}" \
83
+ OPENCLAW_INSTANCE_ORDINAL="${ordinal}" \
84
+ OPENCLAW_HTTP_PORT="${http_port}" \
85
+ OPENCLAW_CONTAINER_PORT="${OPENCLAW_CONTAINER_PORT}" \
86
+ OPENCLAW_INSTANCE_HOME_DIR="${instance_home_dir}" \
87
+ OPENCLAW_INSTANCE_DATA_DIR="${data_dir}" \
88
+ OPENCLAW_INSTANCE_LOG_DIR="${logs_dir}" \
89
+ docker compose -p "$(project_name_for_instance "${instance_id}")" -f "${PROJECT_ROOT}/docker-compose.yml" up -d --remove-orphans
90
+ }
91
+
92
+ main() {
93
+ ensure_file "${CONFIG_FILE}"
94
+ ensure_file "${PROJECT_ROOT}/docker-compose.yml"
95
+ ensure_cmd docker
96
+
97
+ if ! docker info >/dev/null 2>&1; then
98
+ fail "Docker daemon 未就绪,请先启动 Docker Desktop。"
99
+ fi
100
+
101
+ load_env
102
+ check_required_vars
103
+ split_instances
104
+ "${PROJECT_ROOT}/scripts/skills_sync.sh"
105
+
106
+ local i=0
107
+ for id in "${INSTANCE_LIST[@]}"; do
108
+ compose_up_for_instance "${id}" "${i}"
109
+ i=$((i + 1))
110
+ done
111
+
112
+ if ! "${PROJECT_ROOT}/scripts/bootstrap_models.sh"; then
113
+ log "模型引导未完全成功,可稍后手动执行:npm run bootstrap-models"
114
+ fi
115
+
116
+ log "全部实例启动完成"
117
+ }
118
+
119
+ main "$@"
@@ -0,0 +1,323 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
6
+ DEFAULT_CONFIG_FILE="${PROJECT_ROOT}/config/openclaw.env"
7
+ DEFAULT_DOTENV_FILE="${PROJECT_ROOT}/.env"
8
+
9
+ OPENCLAW_CONFIG_FILE="${OPENCLAW_CONFIG_FILE:-${DEFAULT_CONFIG_FILE}}"
10
+ OPENCLAW_DOTENV_FILE="${OPENCLAW_DOTENV_FILE:-${DEFAULT_DOTENV_FILE}}"
11
+
12
+ usage() {
13
+ cat <<'EOF'
14
+ OpenClaw Manager CLI(多实例运维入口)
15
+
16
+ 用法:
17
+ ./scripts/openclaw_ctl.sh [--config <path>] [--env-file <path>] <command> [args]
18
+
19
+ 全局参数:
20
+ -h, --help 显示帮助
21
+ --config <path> 指定 openclaw 运行配置文件(默认 ./config/openclaw.env)
22
+ --env-file <path> 指定敏感变量文件(默认 ./.env)
23
+
24
+ 命令:
25
+ setup
26
+ install
27
+ start | stop | restart | status
28
+ logs [service]
29
+ list-claw
30
+ tui
31
+ chat-claw <name>
32
+ start-claw <name> | stop-claw <name> | status-claw <name> | logs-claw <name>
33
+ check-env
34
+ sync-skills
35
+ sync-model-configs
36
+ apply-config
37
+ bootstrap-models
38
+ set-qwen
39
+ health-check
40
+ self-test
41
+
42
+ 示例:
43
+ ./scripts/openclaw_ctl.sh --config ./config/openclaw.dev.env status
44
+ ./scripts/openclaw_ctl.sh --env-file ./.env.prod start-claw master
45
+ EOF
46
+ }
47
+
48
+ project_name_for_instance() {
49
+ local instance_id="$1"
50
+ printf '%s-%s' "${OPENCLAW_NAME_PREFIX}" "${instance_id}" | tr '[:upper:]' '[:lower:]'
51
+ }
52
+
53
+ run_install() {
54
+ "${PROJECT_ROOT}/scripts/install_openclaw.sh"
55
+ }
56
+
57
+ load_config() {
58
+ local config_file="${OPENCLAW_CONFIG_FILE}"
59
+ if [ ! -f "${config_file}" ]; then
60
+ echo "缺少 ${config_file}" >&2
61
+ exit 1
62
+ fi
63
+ set -a
64
+ # shellcheck source=/dev/null
65
+ source "${config_file}"
66
+ set +a
67
+
68
+ if [ -z "${OPENCLAW_HOME_ROOT:-}" ]; then
69
+ OPENCLAW_HOME_ROOT="${OPENCLAW_DATA_ROOT:-}"
70
+ fi
71
+ : "${OPENCLAW_HOME_ROOT:?OPENCLAW_HOME_ROOT 未配置}"
72
+ OPENCLAW_SHARED_SKILLS_DIR="${OPENCLAW_SHARED_SKILLS_DIR:-${OPENCLAW_HOME_ROOT}/shared/skills}"
73
+ }
74
+
75
+ ensure_instance_exists() {
76
+ local target="$1"
77
+ IFS=',' read -r -a instance_list <<<"${OPENCLAW_INSTANCE_IDS}"
78
+ for id in "${instance_list[@]}"; do
79
+ if [ "${id}" = "${target}" ]; then
80
+ return 0
81
+ fi
82
+ done
83
+ echo "未找到 claw 实例:${target}" >&2
84
+ exit 1
85
+ }
86
+
87
+ list_claws() {
88
+ load_config
89
+ IFS=',' read -r -a instance_list <<<"${OPENCLAW_INSTANCE_IDS}"
90
+ for id in "${instance_list[@]}"; do
91
+ printf '%s\n' "${id}"
92
+ done
93
+ }
94
+
95
+ run_compose_for_instance() {
96
+ local target="$1"
97
+ shift
98
+ load_config
99
+ ensure_instance_exists "${target}"
100
+
101
+ local ordinal=0
102
+ IFS=',' read -r -a instance_list <<<"${OPENCLAW_INSTANCE_IDS}"
103
+ for id in "${instance_list[@]}"; do
104
+ if [ "${id}" = "${target}" ]; then
105
+ OPENCLAW_INSTANCE_ID="${id}" \
106
+ OPENCLAW_INSTANCE_ORDINAL="${ordinal}" \
107
+ OPENCLAW_HTTP_PORT="$((OPENCLAW_BASE_HTTP_PORT + ordinal))" \
108
+ OPENCLAW_CONTAINER_PORT="${OPENCLAW_CONTAINER_PORT:-18789}" \
109
+ OPENCLAW_INSTANCE_HOME_DIR="${OPENCLAW_HOME_ROOT}/${id}" \
110
+ OPENCLAW_SHARED_SKILLS_DIR="${OPENCLAW_SHARED_SKILLS_DIR:-${OPENCLAW_HOME_ROOT}/shared/skills}" \
111
+ OPENCLAW_INSTANCE_DATA_DIR="${OPENCLAW_HOME_ROOT}/${id}" \
112
+ OPENCLAW_INSTANCE_LOG_DIR="${PROJECT_ROOT}/logs/${OPENCLAW_NAME_PREFIX}-${id}" \
113
+ docker compose -p "$(project_name_for_instance "${id}")" -f "${PROJECT_ROOT}/docker-compose.yml" "$@"
114
+ return 0
115
+ fi
116
+ ordinal=$((ordinal + 1))
117
+ done
118
+ }
119
+
120
+ chat_with_claw() {
121
+ local claw_name="$1"
122
+ local running_service
123
+ running_service="$(run_compose_for_instance "${claw_name}" ps --status running --services 2>/dev/null | awk 'NF{print $1; exit}')"
124
+ if [ "${running_service}" != "openclaw" ]; then
125
+ echo "实例 ${claw_name} 当前未运行,请先执行: npm run start-claw -- ${claw_name}" >&2
126
+ exit 1
127
+ fi
128
+
129
+ local max_wait=30
130
+ local waited=0
131
+ until run_compose_for_instance "${claw_name}" exec -T openclaw sh -lc 'openclaw health >/dev/null 2>&1'; do
132
+ waited=$((waited + 2))
133
+ if [ "${waited}" -ge "${max_wait}" ]; then
134
+ echo "实例 ${claw_name} 网关尚未就绪(等待 ${max_wait}s 超时)。可先执行: npm run apply-config" >&2
135
+ exit 1
136
+ fi
137
+ sleep 2
138
+ done
139
+
140
+ run_compose_for_instance "${claw_name}" exec openclaw sh -lc '
141
+ SESSION_KEY="chat-'${claw_name}'"
142
+ if command -v openclaw >/dev/null 2>&1; then
143
+ exec openclaw tui --session "${SESSION_KEY}"
144
+ elif command -v claw >/dev/null 2>&1; then
145
+ exec claw tui --session "${SESSION_KEY}"
146
+ else
147
+ echo "容器内未找到 openclaw/claw 命令,无法进入对话 TUI。"
148
+ exit 127
149
+ fi
150
+ '
151
+ }
152
+
153
+ compose_cmd_all_instances() {
154
+ load_config
155
+
156
+ IFS=',' read -r -a instance_list <<<"${OPENCLAW_INSTANCE_IDS}"
157
+ local i=0
158
+ for id in "${instance_list[@]}"; do
159
+ OPENCLAW_INSTANCE_ID="${id}" \
160
+ OPENCLAW_INSTANCE_ORDINAL="${i}" \
161
+ OPENCLAW_HTTP_PORT="$((OPENCLAW_BASE_HTTP_PORT + i))" \
162
+ OPENCLAW_CONTAINER_PORT="${OPENCLAW_CONTAINER_PORT:-18789}" \
163
+ OPENCLAW_INSTANCE_HOME_DIR="${OPENCLAW_HOME_ROOT}/${id}" \
164
+ OPENCLAW_SHARED_SKILLS_DIR="${OPENCLAW_SHARED_SKILLS_DIR:-${OPENCLAW_HOME_ROOT}/shared/skills}" \
165
+ OPENCLAW_INSTANCE_DATA_DIR="${OPENCLAW_HOME_ROOT}/${id}" \
166
+ OPENCLAW_INSTANCE_LOG_DIR="${PROJECT_ROOT}/logs/${OPENCLAW_NAME_PREFIX}-${id}" \
167
+ docker compose -p "$(project_name_for_instance "${id}")" -f "${PROJECT_ROOT}/docker-compose.yml" "$@"
168
+ i=$((i + 1))
169
+ done
170
+ }
171
+
172
+ run_self_test() {
173
+ echo "[self-test] 检查脚本可执行权限"
174
+ test -x "${PROJECT_ROOT}/scripts/setup_env.sh"
175
+ test -x "${PROJECT_ROOT}/scripts/install_openclaw.sh"
176
+ test -x "${PROJECT_ROOT}/scripts/openclaw_ctl.sh"
177
+ test -x "${PROJECT_ROOT}/scripts/check_env.sh"
178
+ test -x "${PROJECT_ROOT}/scripts/health_check.sh"
179
+ test -x "${PROJECT_ROOT}/scripts/skills_sync.sh"
180
+ test -x "${PROJECT_ROOT}/scripts/sync_model_configs.sh"
181
+ test -x "${PROJECT_ROOT}/scripts/bootstrap_models.sh"
182
+ test -x "${PROJECT_ROOT}/scripts/apply_config.sh"
183
+ test -x "${PROJECT_ROOT}/scripts/set_qwen_vendor_model.sh"
184
+ echo "[self-test] 检查关键文件存在"
185
+ test -f "${PROJECT_ROOT}/docker-compose.yml"
186
+ test -f "${PROJECT_ROOT}/config/openclaw.env.example"
187
+ test -f "${PROJECT_ROOT}/config/clawhub-skills.txt"
188
+ test -f "${PROJECT_ROOT}/.env.example"
189
+ echo "[self-test] 检查 Git 仓库状态(里程碑提交前置)"
190
+ if ! git -C "${PROJECT_ROOT}" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
191
+ echo "[self-test][WARN] 当前目录不是 Git 仓库。里程碑提交会被阻塞,请先执行 git init。"
192
+ fi
193
+ echo "[self-test] 基础检查通过"
194
+ }
195
+
196
+ POSITIONAL_ARGS=()
197
+ while [ "$#" -gt 0 ]; do
198
+ case "$1" in
199
+ -h|--help|help)
200
+ usage
201
+ exit 0
202
+ ;;
203
+ --config)
204
+ [ -n "${2:-}" ] || { echo "缺少 --config 的参数路径" >&2; exit 1; }
205
+ OPENCLAW_CONFIG_FILE="$2"
206
+ shift 2
207
+ ;;
208
+ --env-file)
209
+ [ -n "${2:-}" ] || { echo "缺少 --env-file 的参数路径" >&2; exit 1; }
210
+ OPENCLAW_DOTENV_FILE="$2"
211
+ shift 2
212
+ ;;
213
+ --)
214
+ shift
215
+ while [ "$#" -gt 0 ]; do
216
+ POSITIONAL_ARGS+=("$1")
217
+ shift
218
+ done
219
+ ;;
220
+ -*)
221
+ echo "未知参数: $1" >&2
222
+ usage
223
+ exit 1
224
+ ;;
225
+ *)
226
+ POSITIONAL_ARGS+=("$1")
227
+ shift
228
+ ;;
229
+ esac
230
+ done
231
+
232
+ export OPENCLAW_CONFIG_FILE
233
+ export OPENCLAW_DOTENV_FILE
234
+
235
+ set -- "${POSITIONAL_ARGS[@]}"
236
+ command="${1:-}"
237
+ case "${command}" in
238
+ setup)
239
+ "${PROJECT_ROOT}/scripts/setup_env.sh"
240
+ ;;
241
+ install)
242
+ run_install
243
+ ;;
244
+ start)
245
+ compose_cmd_all_instances up -d
246
+ ;;
247
+ stop)
248
+ compose_cmd_all_instances down
249
+ ;;
250
+ restart)
251
+ compose_cmd_all_instances restart
252
+ ;;
253
+ status)
254
+ compose_cmd_all_instances ps
255
+ ;;
256
+ logs)
257
+ service="${2:-}"
258
+ if [ -n "${service}" ]; then
259
+ compose_cmd_all_instances logs -f "${service}"
260
+ else
261
+ compose_cmd_all_instances logs --tail=200
262
+ fi
263
+ ;;
264
+ list-claw)
265
+ list_claws
266
+ ;;
267
+ tui)
268
+ node "${PROJECT_ROOT}/src/tui/index.js"
269
+ ;;
270
+ chat-claw)
271
+ claw_name="${2:-}"
272
+ [ -n "${claw_name}" ] || { echo "用法: ./scripts/openclaw_ctl.sh chat-claw <name>" >&2; exit 1; }
273
+ chat_with_claw "${claw_name}"
274
+ ;;
275
+ status-claw)
276
+ claw_name="${2:-}"
277
+ [ -n "${claw_name}" ] || { echo "用法: status-claw <name>" >&2; exit 1; }
278
+ run_compose_for_instance "${claw_name}" ps
279
+ ;;
280
+ start-claw)
281
+ claw_name="${2:-}"
282
+ [ -n "${claw_name}" ] || { echo "用法: start-claw <name>" >&2; exit 1; }
283
+ run_compose_for_instance "${claw_name}" up -d
284
+ ;;
285
+ stop-claw)
286
+ claw_name="${2:-}"
287
+ [ -n "${claw_name}" ] || { echo "用法: stop-claw <name>" >&2; exit 1; }
288
+ run_compose_for_instance "${claw_name}" down
289
+ ;;
290
+ logs-claw)
291
+ claw_name="${2:-}"
292
+ [ -n "${claw_name}" ] || { echo "用法: logs-claw <name>" >&2; exit 1; }
293
+ run_compose_for_instance "${claw_name}" logs --tail=200
294
+ ;;
295
+ self-test)
296
+ run_self_test
297
+ ;;
298
+ check-env)
299
+ "${PROJECT_ROOT}/scripts/check_env.sh"
300
+ ;;
301
+ sync-skills)
302
+ "${PROJECT_ROOT}/scripts/skills_sync.sh"
303
+ ;;
304
+ sync-model-configs)
305
+ "${PROJECT_ROOT}/scripts/sync_model_configs.sh"
306
+ ;;
307
+ apply-config)
308
+ "${PROJECT_ROOT}/scripts/apply_config.sh"
309
+ ;;
310
+ bootstrap-models)
311
+ "${PROJECT_ROOT}/scripts/bootstrap_models.sh"
312
+ ;;
313
+ set-qwen)
314
+ "${PROJECT_ROOT}/scripts/set_qwen_vendor_model.sh"
315
+ ;;
316
+ health-check)
317
+ "${PROJECT_ROOT}/scripts/health_check.sh"
318
+ ;;
319
+ *)
320
+ usage
321
+ exit 1
322
+ ;;
323
+ esac