feat(installer): add --install/--uninstall with recorded full rollback
This commit is contained in:
@@ -10,6 +10,9 @@
|
|||||||
- Added helper scripts for smoke/dev lifecycle and rule validation
|
- Added helper scripts for smoke/dev lifecycle and rule validation
|
||||||
- Added no-touch config rendering and integration docs
|
- Added no-touch config rendering and integration docs
|
||||||
- Added installer script with rollback (`scripts/install-whispergate-openclaw.sh`)
|
- Added installer script with rollback (`scripts/install-whispergate-openclaw.sh`)
|
||||||
|
- supports `--install` / `--uninstall`
|
||||||
|
- uninstall restores all recorded changes
|
||||||
|
- writes install/uninstall records under `~/.openclaw/whispergate-install-records/`
|
||||||
- Added discord-control-api with:
|
- Added discord-control-api with:
|
||||||
- `channel-private-create` (create private channel for allowlist)
|
- `channel-private-create` (create private channel for allowlist)
|
||||||
- `channel-private-update` (update allowlist/overwrites for existing channel)
|
- `channel-private-update` (update allowlist/overwrites for existing channel)
|
||||||
|
|||||||
@@ -32,7 +32,16 @@ You can merge this snippet manually into your `openclaw.json`.
|
|||||||
For production-like install with automatic rollback on error:
|
For production-like install with automatic rollback on error:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./scripts/install-whispergate-openclaw.sh
|
./scripts/install-whispergate-openclaw.sh --install
|
||||||
|
```
|
||||||
|
|
||||||
|
Uninstall (revert all recorded config changes):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/install-whispergate-openclaw.sh --uninstall
|
||||||
|
# or specify a record explicitly
|
||||||
|
# RECORD_FILE=~/.openclaw/whispergate-install-records/whispergate-YYYYmmddHHMMSS.json \
|
||||||
|
# ./scripts/install-whispergate-openclaw.sh --uninstall
|
||||||
```
|
```
|
||||||
|
|
||||||
Environment overrides:
|
Environment overrides:
|
||||||
@@ -48,7 +57,10 @@ Environment overrides:
|
|||||||
The script:
|
The script:
|
||||||
- writes via `openclaw config set ... --json`
|
- writes via `openclaw config set ... --json`
|
||||||
- creates config backup first
|
- creates config backup first
|
||||||
- restores backup automatically if any step fails
|
- restores backup automatically if any install step fails
|
||||||
|
- writes a change record for every install/uninstall:
|
||||||
|
- directory: `~/.openclaw/whispergate-install-records/`
|
||||||
|
- latest pointer: `~/.openclaw/whispergate-install-record-latest.json`
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
# Install WhisperGate config into OpenClaw safely.
|
# WhisperGate installer/uninstaller for OpenClaw
|
||||||
# - uses `openclaw config set ... --json` for writes
|
# Requirements:
|
||||||
# - creates full config backup
|
# - all writes via `openclaw config set ... --json`
|
||||||
# - rolls back automatically on failure
|
# - install supports rollback on failure
|
||||||
|
# - uninstall reverts ALL recorded changes
|
||||||
|
# - every install writes a change record
|
||||||
|
|
||||||
OPENCLAW_CONFIG_PATH="${OPENCLAW_CONFIG_PATH:-$HOME/.openclaw/openclaw.json}"
|
OPENCLAW_CONFIG_PATH="${OPENCLAW_CONFIG_PATH:-$HOME/.openclaw/openclaw.json}"
|
||||||
PLUGIN_PATH="${PLUGIN_PATH:-/root/.openclaw/workspace-operator/WhisperGate/dist/plugin}"
|
PLUGIN_PATH="${PLUGIN_PATH:-/root/.openclaw/workspace-operator/WhisperGate/dist/plugin}"
|
||||||
@@ -15,74 +17,141 @@ NO_REPLY_API_KEY="${NO_REPLY_API_KEY:-wg-local-test-token}"
|
|||||||
BYPASS_USER_IDS_JSON="${BYPASS_USER_IDS_JSON:-["561921120408698910","1474088632750047324"]}"
|
BYPASS_USER_IDS_JSON="${BYPASS_USER_IDS_JSON:-["561921120408698910","1474088632750047324"]}"
|
||||||
END_SYMBOLS_JSON="${END_SYMBOLS_JSON:-["🔚"]}"
|
END_SYMBOLS_JSON="${END_SYMBOLS_JSON:-["🔚"]}"
|
||||||
|
|
||||||
BACKUP_PATH="${OPENCLAW_CONFIG_PATH}.bak-whispergate-install-$(date +%Y%m%d%H%M%S)"
|
STATE_DIR="${STATE_DIR:-$HOME/.openclaw/whispergate-install-records}"
|
||||||
|
LATEST_RECORD_LINK="${LATEST_RECORD_LINK:-$HOME/.openclaw/whispergate-install-record-latest.json}"
|
||||||
|
|
||||||
|
MODE=""
|
||||||
|
if [[ "${1:-}" == "--install" ]]; then
|
||||||
|
MODE="install"
|
||||||
|
elif [[ "${1:-}" == "--uninstall" ]]; then
|
||||||
|
MODE="uninstall"
|
||||||
|
else
|
||||||
|
echo "Usage: $0 --install | --uninstall"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
TIMESTAMP="$(date +%Y%m%d%H%M%S)"
|
||||||
|
BACKUP_PATH="${OPENCLAW_CONFIG_PATH}.bak-whispergate-${MODE}-${TIMESTAMP}"
|
||||||
|
RECORD_PATH="${STATE_DIR}/whispergate-${TIMESTAMP}.json"
|
||||||
|
|
||||||
|
PROVIDER_PATH="models.providers[\"${NO_REPLY_PROVIDER_ID}\"]"
|
||||||
|
PATH_PLUGINS_LOAD="plugins.load.paths"
|
||||||
|
PATH_PLUGIN_ENTRY="plugins.entries.whispergate"
|
||||||
|
|
||||||
INSTALLED_OK=0
|
INSTALLED_OK=0
|
||||||
|
|
||||||
rollback() {
|
|
||||||
local ec=$?
|
|
||||||
if [[ $INSTALLED_OK -eq 1 ]]; then
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
echo "[whispergate-install] ERROR (exit=$ec). Rolling back config from backup..."
|
|
||||||
if [[ -f "$BACKUP_PATH" ]]; then
|
|
||||||
cp -f "$BACKUP_PATH" "$OPENCLAW_CONFIG_PATH"
|
|
||||||
echo "[whispergate-install] rollback complete: $OPENCLAW_CONFIG_PATH restored"
|
|
||||||
else
|
|
||||||
echo "[whispergate-install] WARNING: backup file not found, cannot auto-rollback"
|
|
||||||
fi
|
|
||||||
exit "$ec"
|
|
||||||
}
|
|
||||||
|
|
||||||
trap rollback ERR
|
|
||||||
|
|
||||||
require_cmd() {
|
require_cmd() {
|
||||||
command -v "$1" >/dev/null 2>&1 || {
|
command -v "$1" >/dev/null 2>&1 || {
|
||||||
echo "[whispergate-install] missing command: $1" >&2
|
echo "[whispergate] missing command: $1" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oc_get_json_or_missing() {
|
||||||
|
local path="$1"
|
||||||
|
if openclaw config get "$path" --json >/tmp/wg_get.json 2>/dev/null; then
|
||||||
|
printf '{"exists":true,"value":%s}\n' "$(cat /tmp/wg_get.json)"
|
||||||
|
else
|
||||||
|
printf '{"exists":false}'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
oc_set_json() {
|
oc_set_json() {
|
||||||
local path="$1"
|
local path="$1"
|
||||||
local json="$2"
|
local json="$2"
|
||||||
openclaw config set "$path" "$json" --json >/dev/null
|
openclaw config set "$path" "$json" --json >/dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
echo "[whispergate-install] checking prerequisites..."
|
oc_unset() {
|
||||||
require_cmd openclaw
|
local path="$1"
|
||||||
require_cmd python3
|
openclaw config unset "$path" >/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
if [[ ! -f "$OPENCLAW_CONFIG_PATH" ]]; then
|
write_record() {
|
||||||
echo "[whispergate-install] config not found: $OPENCLAW_CONFIG_PATH" >&2
|
local mode="$1"
|
||||||
exit 1
|
local prev_paths_json="$2"
|
||||||
fi
|
local next_paths_json="$3"
|
||||||
|
mkdir -p "$STATE_DIR"
|
||||||
cp -f "$OPENCLAW_CONFIG_PATH" "$BACKUP_PATH"
|
python3 - <<'PY'
|
||||||
echo "[whispergate-install] backup created: $BACKUP_PATH"
|
|
||||||
|
|
||||||
# 1) plugins.load.paths append plugin path (dedupe)
|
|
||||||
CURRENT_PATHS_JSON="[]"
|
|
||||||
if openclaw config get plugins.load.paths --json >/tmp/wg_paths.json 2>/dev/null; then
|
|
||||||
CURRENT_PATHS_JSON="$(cat /tmp/wg_paths.json)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
NEW_PATHS_JSON="$(python3 - <<'PY'
|
|
||||||
import json, os
|
import json, os
|
||||||
cur=json.loads(os.environ['CURRENT_PATHS_JSON'])
|
record={
|
||||||
pp=os.environ['PLUGIN_PATH']
|
'mode': os.environ['REC_MODE'],
|
||||||
if not isinstance(cur,list):
|
'timestamp': os.environ['TIMESTAMP'],
|
||||||
cur=[]
|
'openclawConfigPath': os.environ['OPENCLAW_CONFIG_PATH'],
|
||||||
if pp not in cur:
|
'backupPath': os.environ['BACKUP_PATH'],
|
||||||
cur.append(pp)
|
'paths': json.loads(os.environ['PREV_PATHS_JSON']),
|
||||||
print(json.dumps(cur, ensure_ascii=False))
|
'applied': json.loads(os.environ['NEXT_PATHS_JSON']),
|
||||||
|
}
|
||||||
|
with open(os.environ['RECORD_PATH'],'w',encoding='utf-8') as f:
|
||||||
|
json.dump(record,f,ensure_ascii=False,indent=2)
|
||||||
|
PY
|
||||||
|
cp -f "$RECORD_PATH" "$LATEST_RECORD_LINK"
|
||||||
|
}
|
||||||
|
|
||||||
|
rollback_install() {
|
||||||
|
local ec=$?
|
||||||
|
if [[ $INSTALLED_OK -eq 1 ]]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo "[whispergate] install failed (exit=$ec), rolling back..."
|
||||||
|
if [[ -f "$BACKUP_PATH" ]]; then
|
||||||
|
cp -f "$BACKUP_PATH" "$OPENCLAW_CONFIG_PATH"
|
||||||
|
echo "[whispergate] rollback complete"
|
||||||
|
else
|
||||||
|
echo "[whispergate] WARNING: backup missing; rollback skipped"
|
||||||
|
fi
|
||||||
|
exit "$ec"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_install() {
|
||||||
|
trap rollback_install ERR
|
||||||
|
|
||||||
|
cp -f "$OPENCLAW_CONFIG_PATH" "$BACKUP_PATH"
|
||||||
|
echo "[whispergate] backup: $BACKUP_PATH"
|
||||||
|
|
||||||
|
local prev_paths_json
|
||||||
|
prev_paths_json="$(python3 - <<'PY'
|
||||||
|
import json, os, subprocess
|
||||||
|
|
||||||
|
def get(path):
|
||||||
|
p=subprocess.run(['openclaw','config','get',path,'--json'],capture_output=True,text=True)
|
||||||
|
if p.returncode==0:
|
||||||
|
return {'exists':True,'value':json.loads(p.stdout)}
|
||||||
|
return {'exists':False}
|
||||||
|
|
||||||
|
payload={
|
||||||
|
os.environ['PATH_PLUGINS_LOAD']: get(os.environ['PATH_PLUGINS_LOAD']),
|
||||||
|
os.environ['PATH_PLUGIN_ENTRY']: get(os.environ['PATH_PLUGIN_ENTRY']),
|
||||||
|
os.environ['PROVIDER_PATH']: get(os.environ['PROVIDER_PATH']),
|
||||||
|
}
|
||||||
|
print(json.dumps(payload,ensure_ascii=False))
|
||||||
PY
|
PY
|
||||||
)"
|
)"
|
||||||
|
|
||||||
oc_set_json "plugins.load.paths" "$NEW_PATHS_JSON"
|
# 1) plugins.load.paths append and dedupe
|
||||||
echo "[whispergate-install] set plugins.load.paths"
|
local current_paths_json new_paths_json
|
||||||
|
if openclaw config get "$PATH_PLUGINS_LOAD" --json >/tmp/wg_paths.json 2>/dev/null; then
|
||||||
|
current_paths_json="$(cat /tmp/wg_paths.json)"
|
||||||
|
else
|
||||||
|
current_paths_json="[]"
|
||||||
|
fi
|
||||||
|
|
||||||
# 2) plugin entry
|
new_paths_json="$(CURRENT_PATHS_JSON="$current_paths_json" PLUGIN_PATH="$PLUGIN_PATH" python3 - <<'PY'
|
||||||
PLUGIN_ENTRY_JSON="$(python3 - <<'PY'
|
import json, os
|
||||||
|
cur=json.loads(os.environ['CURRENT_PATHS_JSON'])
|
||||||
|
if not isinstance(cur,list):
|
||||||
|
cur=[]
|
||||||
|
pp=os.environ['PLUGIN_PATH']
|
||||||
|
if pp not in cur:
|
||||||
|
cur.append(pp)
|
||||||
|
print(json.dumps(cur,ensure_ascii=False))
|
||||||
|
PY
|
||||||
|
)"
|
||||||
|
oc_set_json "$PATH_PLUGINS_LOAD" "$new_paths_json"
|
||||||
|
|
||||||
|
# 2) plugin entry
|
||||||
|
local plugin_entry_json
|
||||||
|
plugin_entry_json="$(BYPASS_USER_IDS_JSON="$BYPASS_USER_IDS_JSON" END_SYMBOLS_JSON="$END_SYMBOLS_JSON" NO_REPLY_PROVIDER_ID="$NO_REPLY_PROVIDER_ID" NO_REPLY_MODEL_ID="$NO_REPLY_MODEL_ID" python3 - <<'PY'
|
||||||
import json, os
|
import json, os
|
||||||
entry={
|
entry={
|
||||||
'enabled': True,
|
'enabled': True,
|
||||||
@@ -95,15 +164,14 @@ entry={
|
|||||||
'noReplyModel': os.environ['NO_REPLY_MODEL_ID'],
|
'noReplyModel': os.environ['NO_REPLY_MODEL_ID'],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
print(json.dumps(entry, ensure_ascii=False))
|
print(json.dumps(entry,ensure_ascii=False))
|
||||||
PY
|
PY
|
||||||
)"
|
)"
|
||||||
|
oc_set_json "$PATH_PLUGIN_ENTRY" "$plugin_entry_json"
|
||||||
|
|
||||||
oc_set_json "plugins.entries.whispergate" "$PLUGIN_ENTRY_JSON"
|
# 3) provider
|
||||||
echo "[whispergate-install] set plugins.entries.whispergate"
|
local provider_json
|
||||||
|
provider_json="$(NO_REPLY_BASE_URL="$NO_REPLY_BASE_URL" NO_REPLY_API_KEY="$NO_REPLY_API_KEY" NO_REPLY_MODEL_ID="$NO_REPLY_MODEL_ID" python3 - <<'PY'
|
||||||
# 3) no-reply model provider (under models.providers)
|
|
||||||
PROVIDER_JSON="$(python3 - <<'PY'
|
|
||||||
import json, os
|
import json, os
|
||||||
provider={
|
provider={
|
||||||
'baseUrl': os.environ['NO_REPLY_BASE_URL'],
|
'baseUrl': os.environ['NO_REPLY_BASE_URL'],
|
||||||
@@ -111,7 +179,7 @@ provider={
|
|||||||
'api': 'openai-completions',
|
'api': 'openai-completions',
|
||||||
'models': [{
|
'models': [{
|
||||||
'id': os.environ['NO_REPLY_MODEL_ID'],
|
'id': os.environ['NO_REPLY_MODEL_ID'],
|
||||||
'name': 'whispergate-no-reply-v1 (Custom Provider)',
|
'name': f"{os.environ['NO_REPLY_MODEL_ID']} (Custom Provider)",
|
||||||
'reasoning': False,
|
'reasoning': False,
|
||||||
'input': ['text'],
|
'input': ['text'],
|
||||||
'cost': {'input': 0, 'output': 0, 'cacheRead': 0, 'cacheWrite': 0},
|
'cost': {'input': 0, 'output': 0, 'cacheRead': 0, 'cacheWrite': 0},
|
||||||
@@ -119,22 +187,123 @@ provider={
|
|||||||
'maxTokens': 4096,
|
'maxTokens': 4096,
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
print(json.dumps(provider, ensure_ascii=False))
|
print(json.dumps(provider,ensure_ascii=False))
|
||||||
|
PY
|
||||||
|
)"
|
||||||
|
oc_set_json "$PROVIDER_PATH" "$provider_json"
|
||||||
|
|
||||||
|
# validate writes
|
||||||
|
openclaw config get "$PATH_PLUGINS_LOAD" --json >/dev/null
|
||||||
|
openclaw config get "$PATH_PLUGIN_ENTRY" --json >/dev/null
|
||||||
|
openclaw config get "$PROVIDER_PATH" --json >/dev/null
|
||||||
|
|
||||||
|
local next_paths_json
|
||||||
|
next_paths_json="$(python3 - <<'PY'
|
||||||
|
import json, os, subprocess
|
||||||
|
|
||||||
|
def get(path):
|
||||||
|
p=subprocess.run(['openclaw','config','get',path,'--json'],capture_output=True,text=True)
|
||||||
|
if p.returncode==0:
|
||||||
|
return {'exists':True,'value':json.loads(p.stdout)}
|
||||||
|
return {'exists':False}
|
||||||
|
|
||||||
|
payload={
|
||||||
|
os.environ['PATH_PLUGINS_LOAD']: get(os.environ['PATH_PLUGINS_LOAD']),
|
||||||
|
os.environ['PATH_PLUGIN_ENTRY']: get(os.environ['PATH_PLUGIN_ENTRY']),
|
||||||
|
os.environ['PROVIDER_PATH']: get(os.environ['PROVIDER_PATH']),
|
||||||
|
}
|
||||||
|
print(json.dumps(payload,ensure_ascii=False))
|
||||||
PY
|
PY
|
||||||
)"
|
)"
|
||||||
|
|
||||||
PROVIDER_PATH="models.providers[\"${NO_REPLY_PROVIDER_ID}\"]"
|
REC_MODE="install" PREV_PATHS_JSON="$prev_paths_json" NEXT_PATHS_JSON="$next_paths_json" TIMESTAMP="$TIMESTAMP" OPENCLAW_CONFIG_PATH="$OPENCLAW_CONFIG_PATH" BACKUP_PATH="$BACKUP_PATH" RECORD_PATH="$RECORD_PATH" write_record install "$prev_paths_json" "$next_paths_json"
|
||||||
oc_set_json "$PROVIDER_PATH" "$PROVIDER_JSON"
|
|
||||||
echo "[whispergate-install] set ${PROVIDER_PATH}"
|
|
||||||
|
|
||||||
# 4) quick validation reads
|
INSTALLED_OK=1
|
||||||
openclaw config get plugins.entries.whispergate --json >/dev/null
|
trap - ERR
|
||||||
openclaw config get "$PROVIDER_PATH" --json >/dev/null
|
echo "[whispergate] install ok"
|
||||||
|
echo "[whispergate] record: $RECORD_PATH"
|
||||||
|
}
|
||||||
|
|
||||||
INSTALLED_OK=1
|
run_uninstall() {
|
||||||
trap - ERR
|
local rec_file=""
|
||||||
|
if [[ -n "${RECORD_FILE:-}" ]]; then
|
||||||
|
rec_file="$RECORD_FILE"
|
||||||
|
elif [[ -f "$LATEST_RECORD_LINK" ]]; then
|
||||||
|
rec_file="$LATEST_RECORD_LINK"
|
||||||
|
else
|
||||||
|
echo "[whispergate] no record found. set RECORD_FILE=<path> or install first." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
echo "[whispergate-install] install completed successfully"
|
if [[ ! -f "$rec_file" ]]; then
|
||||||
echo "[whispergate-install] next steps:"
|
echo "[whispergate] record file not found: $rec_file" >&2
|
||||||
echo " 1) start no-reply api (port 8787)"
|
exit 1
|
||||||
echo " 2) restart gateway: openclaw gateway restart"
|
fi
|
||||||
|
|
||||||
|
cp -f "$OPENCLAW_CONFIG_PATH" "$BACKUP_PATH"
|
||||||
|
echo "[whispergate] backup before uninstall: $BACKUP_PATH"
|
||||||
|
|
||||||
|
python3 - <<'PY'
|
||||||
|
import json, os, subprocess, sys
|
||||||
|
rec=json.load(open(os.environ['REC_FILE'],encoding='utf-8'))
|
||||||
|
paths=rec.get('paths',{})
|
||||||
|
|
||||||
|
for path, info in paths.items():
|
||||||
|
exists=bool(info.get('exists'))
|
||||||
|
if exists:
|
||||||
|
val=json.dumps(info.get('value'),ensure_ascii=False)
|
||||||
|
p=subprocess.run(['openclaw','config','set',path,val,'--json'],capture_output=True,text=True)
|
||||||
|
if p.returncode!=0:
|
||||||
|
sys.stderr.write(p.stderr)
|
||||||
|
raise SystemExit(1)
|
||||||
|
else:
|
||||||
|
p=subprocess.run(['openclaw','config','unset',path],capture_output=True,text=True)
|
||||||
|
if p.returncode!=0:
|
||||||
|
# path may already be absent; tolerate common "not found" style failures
|
||||||
|
txt=(p.stderr or p.stdout or '').lower()
|
||||||
|
if 'not found' not in txt and 'missing' not in txt and 'does not exist' not in txt:
|
||||||
|
sys.stderr.write(p.stderr)
|
||||||
|
raise SystemExit(1)
|
||||||
|
print('ok')
|
||||||
|
PY
|
||||||
|
|
||||||
|
local next_paths_json
|
||||||
|
next_paths_json="$(python3 - <<'PY'
|
||||||
|
import json, os, subprocess
|
||||||
|
paths=json.load(open(os.environ['REC_FILE'],encoding='utf-8')).get('paths',{}).keys()
|
||||||
|
def get(path):
|
||||||
|
p=subprocess.run(['openclaw','config','get',path,'--json'],capture_output=True,text=True)
|
||||||
|
if p.returncode==0:
|
||||||
|
return {'exists':True,'value':json.loads(p.stdout)}
|
||||||
|
return {'exists':False}
|
||||||
|
payload={k:get(k) for k in paths}
|
||||||
|
print(json.dumps(payload,ensure_ascii=False))
|
||||||
|
PY
|
||||||
|
)"
|
||||||
|
|
||||||
|
local prev_paths_json
|
||||||
|
prev_paths_json="$(python3 - <<'PY'
|
||||||
|
import json, os
|
||||||
|
print(json.dumps(json.load(open(os.environ['REC_FILE'],encoding='utf-8')).get('applied',{}),ensure_ascii=False))
|
||||||
|
PY
|
||||||
|
)"
|
||||||
|
|
||||||
|
REC_MODE="uninstall" PREV_PATHS_JSON="$prev_paths_json" NEXT_PATHS_JSON="$next_paths_json" TIMESTAMP="$TIMESTAMP" OPENCLAW_CONFIG_PATH="$OPENCLAW_CONFIG_PATH" BACKUP_PATH="$BACKUP_PATH" RECORD_PATH="$RECORD_PATH" write_record uninstall "$prev_paths_json" "$next_paths_json"
|
||||||
|
|
||||||
|
echo "[whispergate] uninstall ok"
|
||||||
|
echo "[whispergate] record: $RECORD_PATH"
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
require_cmd openclaw
|
||||||
|
require_cmd python3
|
||||||
|
[[ -f "$OPENCLAW_CONFIG_PATH" ]] || { echo "[whispergate] config not found: $OPENCLAW_CONFIG_PATH"; exit 1; }
|
||||||
|
|
||||||
|
if [[ "$MODE" == "install" ]]; then
|
||||||
|
run_install
|
||||||
|
else
|
||||||
|
REC_FILE="${RECORD_FILE:-$LATEST_RECORD_LINK}" run_uninstall
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
main
|
||||||
|
|||||||
Reference in New Issue
Block a user