mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-10-29 12:45:13 +08:00
💋 Sync 2026-04-08 20:31:38
This commit is contained in:
1 parent
d6e7b3ef08
commit
92a3fac9ca
29 files changed
+3458
-1106
No files matched your search
@@ -41,6 +41,25 @@ uci_get_list() {
|
||||
sed -n "s/^${UCI_NS}\\.main\\.${key}='\\(.*\\)'$/\\1/p"
|
||||
}
|
||||
|
||||
json_get() {
|
||||
local file="$1" expr="$2"
|
||||
[ -f "$file" ] || return 0
|
||||
command -v jsonfilter >/dev/null 2>&1 || return 0
|
||||
jsonfilter -i "$file" -e "$expr" 2>/dev/null || true
|
||||
}
|
||||
|
||||
load_gateway_runtime_from_json() {
|
||||
local cfg="${DATA_DIR}/.openclaw/openclaw.json"
|
||||
[ -f "$cfg" ] || return 0
|
||||
local json_port json_bind json_token
|
||||
json_port="$(json_get "$cfg" '@.gateway.port')"
|
||||
json_bind="$(json_get "$cfg" '@.gateway.bind')"
|
||||
json_token="$(json_get "$cfg" '@.gateway.auth.token')"
|
||||
[ -n "$json_port" ] && PORT="$json_port"
|
||||
[ -n "$json_bind" ] && BIND="$json_bind"
|
||||
[ -n "$json_token" ] && TOKEN="$json_token"
|
||||
}
|
||||
|
||||
fmt_elapsed() {
|
||||
local total="${1:-0}" h m s
|
||||
case "$total" in ''|*[!0-9]*) total=0 ;; esac
|
||||
@@ -254,6 +273,12 @@ gen_token() {
|
||||
(date +%s | md5sum 2>/dev/null | awk '{print $1}')
|
||||
}
|
||||
|
||||
ensure_token() {
|
||||
if [ -z "${TOKEN:-}" ]; then
|
||||
TOKEN="$(gen_token)"
|
||||
fi
|
||||
}
|
||||
|
||||
is_musl() {
|
||||
ldd --version 2>&1 | grep -qi musl && return 0
|
||||
[ -e /lib/ld-musl-*.so.1 ] 2>/dev/null && return 0
|
||||
@@ -573,46 +598,58 @@ ensure_gateway_config() {
|
||||
host_header_fallback="true"
|
||||
fi
|
||||
|
||||
local allow_insecure="false"
|
||||
if [ "${ALLOW_INSECURE_AUTH:-0}" = "1" ]; then
|
||||
allow_insecure="true"
|
||||
local allow_insecure=""
|
||||
if [ -n "${ALLOW_INSECURE_AUTH:-}" ]; then
|
||||
if [ "$ALLOW_INSECURE_AUTH" = "1" ]; then
|
||||
allow_insecure="true"
|
||||
else
|
||||
allow_insecure="false"
|
||||
fi
|
||||
fi
|
||||
|
||||
local disable_device_auth="false"
|
||||
if [ "${DISABLE_DEVICE_AUTH:-0}" = "1" ]; then
|
||||
disable_device_auth="true"
|
||||
local disable_device_auth=""
|
||||
if [ -n "${DISABLE_DEVICE_AUTH:-}" ]; then
|
||||
if [ "$DISABLE_DEVICE_AUTH" = "1" ]; then
|
||||
disable_device_auth="true"
|
||||
else
|
||||
disable_device_auth="false"
|
||||
fi
|
||||
fi
|
||||
|
||||
local allowed_origins="$ALLOWED_ORIGINS"
|
||||
if [ -z "$allowed_origins" ]; then
|
||||
allowed_origins="$(default_allowed_origins)"
|
||||
fi
|
||||
local selected_key selected_model selected_base_url override_base_url base_url_mode
|
||||
selected_key="$(agent_key_name)"
|
||||
selected_model="$(agent_default_model)"
|
||||
selected_base_url="$(agent_default_base_url)"
|
||||
selected_key=""
|
||||
selected_model=""
|
||||
selected_base_url=""
|
||||
override_base_url=""
|
||||
base_url_mode="default"
|
||||
if [ -n "${PROVIDER_BASE_URL:-}" ]; then
|
||||
if is_valid_http_url "$PROVIDER_BASE_URL"; then
|
||||
override_base_url="$PROVIDER_BASE_URL"
|
||||
base_url_mode="override"
|
||||
else
|
||||
write_installer_log "Ignoring invalid relay URL for ${DEFAULT_AGENT:-anthropic}: ${PROVIDER_BASE_URL}"
|
||||
base_url_mode="preserve"
|
||||
base_url_mode=""
|
||||
if [ -n "${DEFAULT_AGENT:-}" ]; then
|
||||
selected_key="$(agent_key_name)"
|
||||
selected_model="$(agent_default_model)"
|
||||
selected_base_url="$(agent_default_base_url)"
|
||||
base_url_mode="default"
|
||||
if [ -n "${PROVIDER_BASE_URL:-}" ]; then
|
||||
if is_valid_http_url "$PROVIDER_BASE_URL"; then
|
||||
override_base_url="$PROVIDER_BASE_URL"
|
||||
base_url_mode="override"
|
||||
else
|
||||
write_installer_log "Ignoring invalid relay URL for ${DEFAULT_AGENT}: ${PROVIDER_BASE_URL}"
|
||||
base_url_mode="preserve"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$cfg")" 2>/dev/null || true
|
||||
CFG_PATH="$cfg" \
|
||||
ALLOWED_ORIGINS_RAW="$allowed_origins" \
|
||||
DEFAULT_ALLOWED_ORIGINS_RAW="$(default_allowed_origins)" \
|
||||
GATEWAY_PORT="$PORT" \
|
||||
GATEWAY_BIND="$BIND" \
|
||||
GATEWAY_TOKEN="$TOKEN" \
|
||||
GATEWAY_ALLOW_INSECURE="$allow_insecure" \
|
||||
GATEWAY_DISABLE_DEVICE_AUTH="$disable_device_auth" \
|
||||
GATEWAY_HOST_HEADER_FALLBACK="$host_header_fallback" \
|
||||
DEFAULT_AGENT_NAME="${DEFAULT_AGENT:-anthropic}" \
|
||||
DEFAULT_AGENT_NAME="${DEFAULT_AGENT:-}" \
|
||||
DEFAULT_AGENT_KEY="$selected_key" \
|
||||
DEFAULT_AGENT_MODEL="$selected_model" \
|
||||
DEFAULT_AGENT_BASE_URL="$selected_base_url" \
|
||||
@@ -635,6 +672,25 @@ local function bool_env(name)
|
||||
return os.getenv(name) == "true"
|
||||
end
|
||||
|
||||
local function optional_env(name)
|
||||
local value = os.getenv(name)
|
||||
if value == nil or value == "" then
|
||||
return nil
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
local function optional_bool_env(name)
|
||||
local value = optional_env(name)
|
||||
if value == "true" then
|
||||
return true
|
||||
end
|
||||
if value == "false" then
|
||||
return false
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function split_lines(raw)
|
||||
local out = {}
|
||||
raw = raw or ""
|
||||
@@ -685,123 +741,166 @@ local gateway = ensure_table(cfg, "gateway")
|
||||
gateway.mode = "local"
|
||||
gateway.port = tonumber(os.getenv("GATEWAY_PORT")) or 18789
|
||||
gateway.bind = os.getenv("GATEWAY_BIND") or "lan"
|
||||
gateway.auth = { mode = "token", token = os.getenv("GATEWAY_TOKEN") or "" }
|
||||
local auth = ensure_table(gateway, "auth")
|
||||
auth.mode = "token"
|
||||
auth.token = os.getenv("GATEWAY_TOKEN") or ""
|
||||
|
||||
local control = ensure_table(gateway, "controlUi")
|
||||
control.enabled = true
|
||||
control.basePath = nil
|
||||
control.allowedOrigins = split_lines(os.getenv("ALLOWED_ORIGINS_RAW"))
|
||||
control.allowInsecureAuth = bool_env("GATEWAY_ALLOW_INSECURE")
|
||||
control.dangerouslyDisableDeviceAuth = bool_env("GATEWAY_DISABLE_DEVICE_AUTH")
|
||||
local configured_origins = optional_env("ALLOWED_ORIGINS_RAW")
|
||||
if configured_origins then
|
||||
control.allowedOrigins = split_lines(configured_origins)
|
||||
elseif type(control.allowedOrigins) ~= "table" or next(control.allowedOrigins) == nil then
|
||||
control.allowedOrigins = split_lines(os.getenv("DEFAULT_ALLOWED_ORIGINS_RAW") or "")
|
||||
end
|
||||
local allow_insecure = optional_bool_env("GATEWAY_ALLOW_INSECURE")
|
||||
if allow_insecure ~= nil then
|
||||
control.allowInsecureAuth = allow_insecure
|
||||
elseif type(control.allowInsecureAuth) ~= "boolean" then
|
||||
control.allowInsecureAuth = true
|
||||
end
|
||||
local disable_device_auth = optional_bool_env("GATEWAY_DISABLE_DEVICE_AUTH")
|
||||
if disable_device_auth ~= nil then
|
||||
control.dangerouslyDisableDeviceAuth = disable_device_auth
|
||||
elseif type(control.dangerouslyDisableDeviceAuth) ~= "boolean" then
|
||||
control.dangerouslyDisableDeviceAuth = true
|
||||
end
|
||||
control.dangerouslyAllowHostHeaderOriginFallback = bool_env("GATEWAY_HOST_HEADER_FALLBACK")
|
||||
|
||||
local env = {}
|
||||
local selected_env_key = os.getenv("DEFAULT_AGENT_KEY")
|
||||
local selected_api_key = os.getenv("DEFAULT_AGENT_API_KEY") or ""
|
||||
if selected_env_key and selected_env_key ~= "" and selected_api_key ~= "" then
|
||||
local env = ensure_table(cfg, "env")
|
||||
local selected_env_key = optional_env("DEFAULT_AGENT_KEY")
|
||||
local selected_api_key = optional_env("DEFAULT_AGENT_API_KEY")
|
||||
if selected_env_key and selected_api_key then
|
||||
env[selected_env_key] = selected_api_key
|
||||
end
|
||||
cfg.env = next(env) and env or nil
|
||||
|
||||
local function update_api_key(provider)
|
||||
if selected_api_key ~= nil then
|
||||
provider.apiKey = selected_api_key ~= "" and selected_api_key or nil
|
||||
end
|
||||
end
|
||||
|
||||
local function env_key_for_provider(id)
|
||||
if id == "openai" then return "OPENAI_API_KEY" end
|
||||
if id == "anthropic" then return "ANTHROPIC_API_KEY" end
|
||||
if id == "minimax-cn" then return "MINIMAX_API_KEY" end
|
||||
if id == "moonshot" then return "MOONSHOT_API_KEY" end
|
||||
return nil
|
||||
end
|
||||
|
||||
local models = ensure_table(cfg, "models")
|
||||
if type(models.mode) ~= "string" or models.mode == "" then
|
||||
models.mode = "merge"
|
||||
end
|
||||
local providers = ensure_table(models, "providers")
|
||||
|
||||
local current_provider_id = os.getenv("DEFAULT_AGENT_NAME") or "anthropic"
|
||||
local current_provider = ensure_table(providers, current_provider_id)
|
||||
local current_provider_id = optional_env("DEFAULT_AGENT_NAME")
|
||||
local primary_model = optional_env("DEFAULT_AGENT_MODEL")
|
||||
local current_primary = cfg.agents and cfg.agents.defaults and cfg.agents.defaults.model and cfg.agents.defaults.model.primary
|
||||
if not primary_model and type(current_primary) == "string" and current_primary ~= "" then
|
||||
primary_model = current_primary
|
||||
end
|
||||
if not current_provider_id and type(primary_model) == "string" and primary_model:match("^[^/]+/.+") then
|
||||
current_provider_id = primary_model:match("^([^/]+)/")
|
||||
end
|
||||
if not current_provider_id or current_provider_id == "" then
|
||||
current_provider_id = "anthropic"
|
||||
end
|
||||
if not primary_model or primary_model == "" then
|
||||
if current_provider_id == "openai" then
|
||||
primary_model = "openai/gpt-5.2"
|
||||
elseif current_provider_id == "anthropic" then
|
||||
primary_model = "anthropic/claude-sonnet-4-6"
|
||||
elseif current_provider_id == "minimax-cn" then
|
||||
primary_model = "minimax-cn/MiniMax-M2.5"
|
||||
elseif current_provider_id == "moonshot" then
|
||||
primary_model = "moonshot/kimi-k2.5"
|
||||
elseif current_provider_id == "custom-provider" then
|
||||
primary_model = "custom-provider/custom-model"
|
||||
local custom = models.providers and models.providers["custom-provider"]
|
||||
local first = custom and custom.models and custom.models[1]
|
||||
if type(first) == "table" and type(first.id) == "string" and first.id ~= "" then
|
||||
primary_model = "custom-provider/" .. first.id
|
||||
end
|
||||
else
|
||||
primary_model = "anthropic/claude-sonnet-4-6"
|
||||
end
|
||||
end
|
||||
local current_model_id = primary_model:match("^[^/]+/(.+)$") or primary_model
|
||||
if current_model_id == "" then
|
||||
current_model_id = "claude-sonnet-4-6"
|
||||
end
|
||||
|
||||
local old_providers = type(models.providers) == "table" and models.providers or {}
|
||||
local preserved_provider = type(old_providers[current_provider_id]) == "table" and old_providers[current_provider_id] or {}
|
||||
local providers = {}
|
||||
models.providers = providers
|
||||
local current_provider = preserved_provider
|
||||
providers[current_provider_id] = current_provider
|
||||
|
||||
if current_provider_id == "openai" then
|
||||
current_provider.api = "openai-completions"
|
||||
current_provider.baseUrl = "https://api.openai.com/v1"
|
||||
current_provider.apiKey = selected_api_key
|
||||
update_api_key(current_provider)
|
||||
current_provider.models = {
|
||||
{ id = "gpt-5.2", name = "GPT-5.2" },
|
||||
{ id = current_model_id, name = current_model_id },
|
||||
}
|
||||
elseif current_provider_id == "anthropic" then
|
||||
current_provider.api = "anthropic-messages"
|
||||
current_provider.baseUrl = "https://api.anthropic.com"
|
||||
current_provider.apiKey = selected_api_key
|
||||
update_api_key(current_provider)
|
||||
current_provider.models = {
|
||||
{ id = "claude-sonnet-4-6", name = "Claude Sonnet 4.6" },
|
||||
{ id = current_model_id, name = current_model_id },
|
||||
}
|
||||
elseif current_provider_id == "minimax-cn" then
|
||||
current_provider.api = "anthropic-messages"
|
||||
current_provider.baseUrl = "https://api.minimaxi.com/anthropic"
|
||||
current_provider.apiKey = selected_api_key
|
||||
update_api_key(current_provider)
|
||||
current_provider.authHeader = true
|
||||
current_provider.models = {
|
||||
{ id = "MiniMax-M2.5", name = "MiniMax M2.5" },
|
||||
{ id = current_model_id, name = current_model_id },
|
||||
}
|
||||
elseif current_provider_id == "moonshot" then
|
||||
current_provider.api = "openai-completions"
|
||||
current_provider.baseUrl = "https://api.moonshot.cn/v1"
|
||||
current_provider.apiKey = selected_api_key
|
||||
update_api_key(current_provider)
|
||||
current_provider.models = {
|
||||
{ id = "kimi-k2.5", name = "Kimi K2.5" },
|
||||
{ id = current_model_id, name = current_model_id },
|
||||
}
|
||||
elseif current_provider_id == "custom-provider" then
|
||||
local primary_model = os.getenv("DEFAULT_AGENT_MODEL") or "custom-provider/custom-model"
|
||||
local custom_model_id = primary_model:match("^[^/]+/(.+)$") or primary_model
|
||||
if custom_model_id == "" then
|
||||
custom_model_id = "custom-model"
|
||||
if current_model_id == "" then
|
||||
current_model_id = "custom-model"
|
||||
end
|
||||
current_provider.api = "openai-completions"
|
||||
current_provider.baseUrl = os.getenv("DEFAULT_AGENT_OVERRIDE_BASE_URL") or current_provider.baseUrl
|
||||
current_provider.apiKey = selected_api_key
|
||||
current_provider.baseUrl = optional_env("DEFAULT_AGENT_OVERRIDE_BASE_URL") or current_provider.baseUrl
|
||||
update_api_key(current_provider)
|
||||
current_provider.authHeader = nil
|
||||
current_provider.models = {
|
||||
{
|
||||
reasoning = false,
|
||||
name = custom_model_id .. " (Custom Provider)",
|
||||
cost = {
|
||||
input = 0,
|
||||
cacheRead = 0,
|
||||
cacheWrite = 0,
|
||||
output = 0,
|
||||
},
|
||||
id = custom_model_id,
|
||||
maxTokens = 4096,
|
||||
contextWindow = 16000,
|
||||
input = { "text" },
|
||||
},
|
||||
{ id = current_model_id, name = current_model_id },
|
||||
}
|
||||
end
|
||||
|
||||
local override_base = os.getenv("DEFAULT_AGENT_OVERRIDE_BASE_URL") or ""
|
||||
local base_url_mode = os.getenv("DEFAULT_AGENT_BASE_URL_MODE") or "default"
|
||||
local override_base = optional_env("DEFAULT_AGENT_OVERRIDE_BASE_URL") or ""
|
||||
local base_url_mode = optional_env("DEFAULT_AGENT_BASE_URL_MODE") or ""
|
||||
if current_provider_id == "custom-provider" and override_base ~= "" then
|
||||
current_provider.baseUrl = override_base
|
||||
elseif base_url_mode == "override" and override_base ~= "" then
|
||||
current_provider.baseUrl = override_base
|
||||
elseif base_url_mode == "default" then
|
||||
current_provider.baseUrl = os.getenv("DEFAULT_AGENT_BASE_URL") or current_provider.baseUrl
|
||||
end
|
||||
|
||||
-- luci.jsonc encodes empty Lua tables as JSON arrays ([]). To avoid producing
|
||||
-- invalid provider entries like {"deepseek/deepseek-chat":[]}, drop provider
|
||||
-- records that are empty or look like a provider/model string.
|
||||
do
|
||||
local to_del = {}
|
||||
for k, v in pairs(providers) do
|
||||
if type(k) == "string" and k:find("/", 1, true) then
|
||||
table.insert(to_del, k)
|
||||
elseif type(v) == "table" and next(v) == nil then
|
||||
table.insert(to_del, k)
|
||||
end
|
||||
end
|
||||
for _, k in ipairs(to_del) do
|
||||
providers[k] = nil
|
||||
end
|
||||
current_provider.baseUrl = optional_env("DEFAULT_AGENT_BASE_URL") or current_provider.baseUrl
|
||||
end
|
||||
|
||||
local agents = ensure_table(cfg, "agents")
|
||||
local defaults = ensure_table(agents, "defaults")
|
||||
local model = ensure_table(defaults, "model")
|
||||
model.primary = os.getenv("DEFAULT_AGENT_MODEL") or "anthropic/claude-sonnet-4-6"
|
||||
model.primary = primary_model or "anthropic/claude-sonnet-4-6"
|
||||
|
||||
prune_empty_tables(cfg)
|
||||
local current_env_key = env_key_for_provider(current_provider_id)
|
||||
local next_env = {}
|
||||
if current_env_key and type(current_provider.apiKey) == "string" and current_provider.apiKey ~= "" then
|
||||
next_env[current_env_key] = current_provider.apiKey
|
||||
end
|
||||
cfg.env = next(next_env) and next_env or nil
|
||||
|
||||
local encoded = json.stringify(cfg, true)
|
||||
if encoded then
|
||||
@@ -814,7 +913,7 @@ f:close()
|
||||
|
||||
local env_path = cfg_path:gsub("openclaw%.json$", "openclaw.env")
|
||||
local envf = assert(io.open(env_path, "w"))
|
||||
if selected_env_key and selected_env_key ~= "" and selected_api_key ~= "" then
|
||||
if selected_env_key and selected_env_key ~= "" and selected_api_key and selected_api_key ~= "" then
|
||||
local val = tostring(selected_api_key)
|
||||
val = val:gsub("\\", "\\\\"):gsub("\n", "\\n"):gsub('"', '\\"')
|
||||
envf:write(selected_env_key .. '="' .. val .. '"\n')
|
||||
@@ -1147,11 +1246,7 @@ upgrade_openclaw() {
|
||||
uci -q commit "$UCI_NS" >/dev/null 2>&1 || true
|
||||
|
||||
# Ensure token exists even if runtime is already installed (service start requires it).
|
||||
TOKEN="$(uci_get token)"
|
||||
if [ -z "$TOKEN" ]; then
|
||||
TOKEN="$(gen_token)"
|
||||
uci -q set "${UCI_NS}.main.token=$TOKEN" && uci -q commit "$UCI_NS" || true
|
||||
fi
|
||||
ensure_token
|
||||
ensure_gateway_config || true
|
||||
|
||||
if have_openclaw_runtime; then
|
||||
@@ -1233,6 +1328,7 @@ do_start() {
|
||||
write_installer_log "== start begin =="
|
||||
ensure_dirs
|
||||
fix_data_permissions || true
|
||||
ensure_token
|
||||
ensure_safe_port_for_start || exit 1
|
||||
/etc/init.d/openclawmgr enable >/dev/null 2>&1 || true
|
||||
uci -q set "${UCI_NS}.main.enabled=1" && uci -q commit "$UCI_NS" || true
|
||||
@@ -1255,6 +1351,7 @@ do_restart() {
|
||||
write_installer_log "== restart begin =="
|
||||
ensure_dirs
|
||||
fix_data_permissions || true
|
||||
ensure_token
|
||||
ensure_safe_port_for_start || exit 1
|
||||
ensure_gateway_config || true
|
||||
/etc/init.d/openclawmgr restart >/dev/null 2>&1 || true
|
||||
@@ -1266,6 +1363,7 @@ do_restart() {
|
||||
write_installer_log "== apply_config begin =="
|
||||
ensure_dirs
|
||||
fix_data_permissions || true
|
||||
ensure_token
|
||||
ensure_gateway_config || true
|
||||
|
||||
local pid=""
|
||||
@@ -1425,14 +1523,8 @@ PROVIDER_BASE_URL="$(uci_get provider_base_url)"
|
||||
[ -n "$BIND" ] || BIND="lan"
|
||||
[ -n "$ENABLED" ] || ENABLED="0"
|
||||
NODE_VERSION="24.14.0"
|
||||
[ -n "$ALLOW_INSECURE_AUTH" ] || ALLOW_INSECURE_AUTH="1"
|
||||
[ -n "$DISABLE_DEVICE_AUTH" ] || DISABLE_DEVICE_AUTH="1"
|
||||
[ -n "$DEFAULT_AGENT" ] || DEFAULT_AGENT="anthropic"
|
||||
[ -n "$DEFAULT_MODEL" ] || DEFAULT_MODEL=""
|
||||
[ -n "$INSTALL_ACCELERATED" ] || INSTALL_ACCELERATED="1"
|
||||
[ -n "$INSTALL_CHANNEL" ] || INSTALL_CHANNEL="stable"
|
||||
[ -n "$PROVIDER_API_KEY" ] || PROVIDER_API_KEY=""
|
||||
[ -n "$PROVIDER_BASE_URL" ] || PROVIDER_BASE_URL=""
|
||||
|
||||
case "$PORT" in
|
||||
''|*[!0-9]*) PORT="18789" ;;
|
||||
@@ -1449,14 +1541,17 @@ case "$ENABLED" in
|
||||
*) ENABLED="0" ;;
|
||||
esac
|
||||
case "$ALLOW_INSECURE_AUTH" in
|
||||
'') ;;
|
||||
1|true|yes|on) ALLOW_INSECURE_AUTH="1" ;;
|
||||
*) ALLOW_INSECURE_AUTH="0" ;;
|
||||
esac
|
||||
case "$DISABLE_DEVICE_AUTH" in
|
||||
'') ;;
|
||||
1|true|yes|on) DISABLE_DEVICE_AUTH="1" ;;
|
||||
*) DISABLE_DEVICE_AUTH="0" ;;
|
||||
esac
|
||||
case "$DEFAULT_AGENT" in
|
||||
'') ;;
|
||||
openai|anthropic|minimax-cn|moonshot|custom-provider) ;;
|
||||
*) DEFAULT_AGENT="anthropic" ;;
|
||||
esac
|
||||
@@ -1496,6 +1591,21 @@ NODE_DIR="${BASE_DIR}/node"
|
||||
GLOBAL_DIR="${BASE_DIR}/global"
|
||||
DATA_DIR="${BASE_DIR}/data"
|
||||
|
||||
if [ -n "$BASE_DIR" ]; then
|
||||
load_gateway_runtime_from_json
|
||||
fi
|
||||
|
||||
case "$PORT" in
|
||||
''|*[!0-9]*) PORT="18789" ;;
|
||||
esac
|
||||
if [ "$PORT" -lt 1 ] 2>/dev/null || [ "$PORT" -gt 65535 ] 2>/dev/null; then
|
||||
PORT="18789"
|
||||
fi
|
||||
case "$BIND" in
|
||||
loopback|lan|auto|tailnet|custom) ;;
|
||||
*) BIND="lan" ;;
|
||||
esac
|
||||
|
||||
NODE_BIN="${NODE_DIR}/bin/node"
|
||||
NPM_BIN="${NODE_DIR}/bin/npm"
|
||||
|
||||
|
||||
@@ -33,6 +33,45 @@ prompt_default() {
|
||||
|
||||
uci_get() { uci -q get "openclawmgr.main.$1" 2>/dev/null || true; }
|
||||
|
||||
is_musl() {
|
||||
ldd --version 2>&1 | grep -qi musl && return 0
|
||||
[ -e /lib/ld-musl-*.so.1 ] 2>/dev/null && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
init_git_transport() {
|
||||
command -v git >/dev/null 2>&1 || return 0
|
||||
mkdir -p "$DATA_DIR" 2>/dev/null || true
|
||||
git config --file "$DATA_DIR/.gitconfig" --add url."https://github.com/".insteadOf "ssh://git@github.com/" 2>/dev/null || true
|
||||
git config --file "$DATA_DIR/.gitconfig" --add url."https://github.com/".insteadOf "ssh://git@github.com" 2>/dev/null || true
|
||||
git config --file "$DATA_DIR/.gitconfig" --add url."https://github.com/".insteadOf "git@github.com:" 2>/dev/null || true
|
||||
}
|
||||
|
||||
init_npm_env() {
|
||||
INSTALL_ACCELERATED="${INSTALL_ACCELERATED:-$(uci_get install_accelerated)}"
|
||||
[ -n "$INSTALL_ACCELERATED" ] || INSTALL_ACCELERATED="1"
|
||||
case "$INSTALL_ACCELERATED" in
|
||||
1|true|yes|on) INSTALL_ACCELERATED="1" ;;
|
||||
*) INSTALL_ACCELERATED="0" ;;
|
||||
esac
|
||||
|
||||
NPM_CACHE_DIR="${BASE_DIR}/npm-cache"
|
||||
export npm_config_cache="$NPM_CACHE_DIR"
|
||||
export npm_config_prefix="$GLOBAL_DIR"
|
||||
export npm_config_audit="false"
|
||||
export npm_config_fund="false"
|
||||
export npm_config_progress="false"
|
||||
export npm_config_update_notifier="false"
|
||||
|
||||
if is_musl; then
|
||||
export npm_config_ignore_scripts="true"
|
||||
fi
|
||||
|
||||
if [ "$INSTALL_ACCELERATED" = "1" ]; then
|
||||
export npm_config_registry="https://registry.npmmirror.com"
|
||||
fi
|
||||
}
|
||||
|
||||
init_paths() {
|
||||
BASE_DIR="${BASE_DIR:-$(uci_get base_dir)}"
|
||||
if [ -z "$BASE_DIR" ]; then
|
||||
@@ -54,6 +93,8 @@ init_paths() {
|
||||
export OPENCLAW_STATE_DIR="${DATA_DIR}/.openclaw"
|
||||
export OPENCLAW_CONFIG_PATH="$CONFIG_FILE"
|
||||
export PATH="${NODE_DIR}/bin:${GLOBAL_DIR}/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
init_git_transport
|
||||
init_npm_env
|
||||
}
|
||||
|
||||
find_openclaw_entry() {
|
||||
@@ -81,6 +122,51 @@ openclaw_cmd() {
|
||||
return 127
|
||||
}
|
||||
|
||||
openclaw() {
|
||||
openclaw_cmd "$@"
|
||||
}
|
||||
|
||||
first_line_or_empty() {
|
||||
sed -n '1p' 2>/dev/null || true
|
||||
}
|
||||
|
||||
detect_openclaw_version() {
|
||||
local v=""
|
||||
v="$(openclaw_cmd --version 2>/dev/null | first_line_or_empty)"
|
||||
if [ -z "$v" ]; then
|
||||
v="$(openclaw_cmd version 2>/dev/null | first_line_or_empty)"
|
||||
fi
|
||||
printf '%s' "$v"
|
||||
}
|
||||
|
||||
print_cli_env_summary() {
|
||||
local node_v npm_v openclaw_v npm_registry musl_state ignore_scripts
|
||||
node_v="$(node -v 2>/dev/null | first_line_or_empty)"
|
||||
npm_v="$(npm -v 2>/dev/null | first_line_or_empty)"
|
||||
openclaw_v="$(detect_openclaw_version)"
|
||||
npm_registry="$(npm config get registry 2>/dev/null | tr -d '\r' | first_line_or_empty)"
|
||||
musl_state="no"
|
||||
ignore_scripts="${npm_config_ignore_scripts:-false}"
|
||||
is_musl && musl_state="yes"
|
||||
|
||||
[ -n "$node_v" ] || node_v="未安装"
|
||||
[ -n "$npm_v" ] || npm_v="未安装"
|
||||
[ -n "$openclaw_v" ] || openclaw_v="未安装"
|
||||
[ -n "$npm_registry" ] || npm_registry="unknown"
|
||||
|
||||
printf '%s\n' "${BOLD}环境摘要${NC}"
|
||||
printf '%s\n' " base_dir ${BASE_DIR}"
|
||||
printf '%s\n' " config ${CONFIG_FILE}"
|
||||
printf '%s\n' " node ${node_v}"
|
||||
printf '%s\n' " npm ${npm_v}"
|
||||
printf '%s\n' " npm prefix ${npm_config_prefix:-${GLOBAL_DIR}}"
|
||||
printf '%s\n' " npm cache ${npm_config_cache:-${NPM_CACHE_DIR:-}}"
|
||||
printf '%s\n' " npm registry ${npm_registry}"
|
||||
printf '%s\n' " musl ${musl_state}"
|
||||
printf '%s\n' " ignore scripts ${ignore_scripts}"
|
||||
printf '%s\n' " openclaw ${openclaw_v}"
|
||||
}
|
||||
|
||||
action_backup_config() {
|
||||
mkdir -p "$BACKUP_DIR" 2>/dev/null || true
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
@@ -117,15 +203,35 @@ action_configure() {
|
||||
openclaw_cmd configure || true
|
||||
}
|
||||
|
||||
action_cli_env() {
|
||||
printf '\n'
|
||||
info "=== OpenClaw CLI 环境 ==="
|
||||
printf '%s\n' "${DIM}已注入 BASE_DIR / NODE_DIR / GLOBAL_DIR / DATA_DIR / OPENCLAW_* / PATH / npm_config_*${NC}"
|
||||
printf '%s\n' "${DIM}可直接执行 openclaw doctor / openclaw configure / node -v / npm -v 等命令${NC}"
|
||||
printf '%s\n' "${DIM}此环境下 npm -g 默认安装到 ${GLOBAL_DIR},并复用 OpenClawMgr 的缓存/镜像配置${NC}"
|
||||
printf '%s\n' "${DIM}若系统为 musl,将自动启用 ignore-scripts,并预置 GitHub HTTPS 重写${NC}"
|
||||
printf '%s\n' "${DIM}输入 exit 可退出终端${NC}"
|
||||
printf '\n'
|
||||
print_cli_env_summary
|
||||
if cd "$DATA_DIR" 2>/dev/null; then
|
||||
printf '%s\n' "${DIM}当前目录: ${DATA_DIR}${NC}"
|
||||
else
|
||||
warn "无法切换到数据目录,保持当前目录不变。"
|
||||
fi
|
||||
printf '\n'
|
||||
exec sh -i
|
||||
}
|
||||
|
||||
main_menu() {
|
||||
while true; do
|
||||
printf '\n'
|
||||
printf '%s\n' "${BOLD}OpenClaw AI Gateway — CLI 配置入口(OpenClawMgr)${NC}"
|
||||
printf '%s\n' "${DIM}base_dir: ${BASE_DIR}${NC}"
|
||||
printf '\n'
|
||||
printf '%s\n' " ${CYAN}1)${NC} 🧭 官方配置向导 ${DIM}(openclaw configure)${NC}"
|
||||
printf '%s\n' " ${CYAN}2)${NC} 💾 备份配置"
|
||||
printf '%s\n' " ${CYAN}3)${NC} 📥 恢复配置"
|
||||
printf '%s\n' " ${CYAN}1)${NC} 💻 CLI环境 ${DIM}(仅注入环境,不执行 configure)${NC}"
|
||||
printf '%s\n' " ${CYAN}2)${NC} 🧭 官方配置向导 ${DIM}(openclaw configure)${NC}"
|
||||
printf '%s\n' " ${CYAN}3)${NC} 💾 备份配置"
|
||||
printf '%s\n' " ${CYAN}4)${NC} 📥 恢复配置"
|
||||
printf '\n'
|
||||
printf '%s\n' " ${CYAN}0)${NC} 退出"
|
||||
printf '\n'
|
||||
@@ -133,9 +239,10 @@ main_menu() {
|
||||
local c=""
|
||||
c="$(prompt_default "请选择" "1")"
|
||||
case "$c" in
|
||||
1) action_configure; pause_enter ;;
|
||||
2) action_backup_config || true; pause_enter ;;
|
||||
3) action_restore_config || true; pause_enter ;;
|
||||
1) action_cli_env ;;
|
||||
2) action_configure; pause_enter ;;
|
||||
3) action_backup_config || true; pause_enter ;;
|
||||
4) action_restore_config || true; pause_enter ;;
|
||||
0) ok "再见!"; exit 0 ;;
|
||||
*) warn "无效选择" ;;
|
||||
esac
|
||||
@@ -146,12 +253,13 @@ init_paths
|
||||
|
||||
case "${1:-}" in
|
||||
--help|-h)
|
||||
echo "Usage: openclawmgr-cli.sh [configure|backup|restore]"
|
||||
echo "Usage: openclawmgr-cli.sh [configure|cli-env|backup|restore]"
|
||||
;;
|
||||
*)
|
||||
case "${1:-menu}" in
|
||||
menu) main_menu ;;
|
||||
configure) action_configure; pause_enter ;;
|
||||
cli-env) action_cli_env ;;
|
||||
backup) action_backup_config || true; pause_enter ;;
|
||||
restore) action_restore_config || true; pause_enter ;;
|
||||
*) echo "Unknown command: ${1:-}" >&2; exit 2 ;;
|
||||
|
||||
Reference in new issue
Block a user