@ckcloudai.com/clawrouter 0.0.1
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.
- package/LICENSE +21 -0
- package/README.md +16 -0
- package/dist/index.d.ts +126 -0
- package/dist/index.js +6596 -0
- package/dist/index.js.map +1 -0
- package/openclaw.plugin.json +25 -0
- package/package.json +68 -0
- package/scripts/reinstall.sh +343 -0
- package/scripts/uninstall.sh +157 -0
- package/scripts/update.sh +382 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "ckcloud",
|
|
3
|
+
"name": "ckcloud",
|
|
4
|
+
"description": "Smart LLM router — 41+ models, x402 micropayments, 92% cost savings",
|
|
5
|
+
"configSchema": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"walletKey": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "EVM wallet private key (0x...). Optional — auto-generated if not set."
|
|
11
|
+
},
|
|
12
|
+
"routing": {
|
|
13
|
+
"type": "object",
|
|
14
|
+
"description": "Override default routing configuration"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"uiHints": {
|
|
19
|
+
"walletKey": {
|
|
20
|
+
"label": "Wallet Private Key",
|
|
21
|
+
"sensitive": true,
|
|
22
|
+
"placeholder": "0x... (optional — auto-generated)"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ckcloudai.com/clawrouter",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "openclaw plugin for ckcloud - save your llm cost",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"ckcloud": "./dist/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"openclaw": {
|
|
12
|
+
"extensions": [
|
|
13
|
+
"./dist/index.js"
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"import": "./dist/index.js",
|
|
19
|
+
"types": "./dist/index.d.ts"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"scripts",
|
|
25
|
+
"openclaw.plugin.json"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsup",
|
|
29
|
+
"dev": "tsup --watch",
|
|
30
|
+
"test": "vitest run",
|
|
31
|
+
"lint": "eslint src/"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"llm",
|
|
35
|
+
"router",
|
|
36
|
+
"openrouter",
|
|
37
|
+
"ai",
|
|
38
|
+
"openclaw",
|
|
39
|
+
"openai",
|
|
40
|
+
"anthropic",
|
|
41
|
+
"gemini",
|
|
42
|
+
"deepseek"
|
|
43
|
+
],
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "https://git.ucloudadmin.com/yai/ckcloud/clawrouter"
|
|
47
|
+
},
|
|
48
|
+
"author": "",
|
|
49
|
+
"license": "ISC",
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=20"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@scure/bip32": "^1.6.0",
|
|
55
|
+
"@scure/bip39": "^1.5.0",
|
|
56
|
+
"@solana/kit": "^5.0.0",
|
|
57
|
+
"@x402/evm": "^2.4.0",
|
|
58
|
+
"@x402/fetch": "^2.4.0",
|
|
59
|
+
"@x402/svm": "^2.4.0",
|
|
60
|
+
"ethers": "^6.16.0",
|
|
61
|
+
"viem": "^2.39.3"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@types/node": "^25.4.0",
|
|
65
|
+
"tsup": "^8.5.1",
|
|
66
|
+
"typescript": "^5.9.3"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
|
|
6
|
+
kill_port_processes() {
|
|
7
|
+
local port="$1"
|
|
8
|
+
local pids=""
|
|
9
|
+
|
|
10
|
+
if command -v lsof >/dev/null 2>&1; then
|
|
11
|
+
pids="$(lsof -ti :"$port" 2>/dev/null || true)"
|
|
12
|
+
elif command -v fuser >/dev/null 2>&1; then
|
|
13
|
+
pids="$(fuser "$port"/tcp 2>/dev/null || true)"
|
|
14
|
+
elif command -v ss >/dev/null 2>&1; then
|
|
15
|
+
pids="$(ss -lptn "sport = :$port" 2>/dev/null | sed -n 's/.*pid=\([0-9]\+\).*/\1/p' | sort -u)"
|
|
16
|
+
elif command -v netstat >/dev/null 2>&1; then
|
|
17
|
+
pids="$(netstat -nlpt 2>/dev/null | awk -v p=":$port" '$4 ~ p"$" {split($7,a,"/"); if (a[1] ~ /^[0-9]+$/) print a[1]}' | sort -u)"
|
|
18
|
+
else
|
|
19
|
+
echo " Warning: could not find lsof/fuser/ss/netstat; skipping proxy stop"
|
|
20
|
+
return 0
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
if [ -n "$pids" ]; then
|
|
24
|
+
echo "$pids" | xargs kill -9 2>/dev/null || true
|
|
25
|
+
fi
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
echo "🦞 ckcloud Reinstall"
|
|
29
|
+
echo ""
|
|
30
|
+
|
|
31
|
+
# 0. Back up wallet key BEFORE removing anything
|
|
32
|
+
WALLET_FILE="$HOME/.openclaw/ckcloud/wallet.key"
|
|
33
|
+
WALLET_BACKUP=""
|
|
34
|
+
|
|
35
|
+
echo "→ Backing up wallet..."
|
|
36
|
+
if [ -f "$WALLET_FILE" ]; then
|
|
37
|
+
WALLET_KEY=$(cat "$WALLET_FILE" | tr -d '[:space:]')
|
|
38
|
+
KEY_LEN=${#WALLET_KEY}
|
|
39
|
+
if [[ "$WALLET_KEY" == 0x* ]] && [ "$KEY_LEN" -eq 66 ]; then
|
|
40
|
+
WALLET_BACKUP="$HOME/.openclaw/ckcloud/wallet.key.bak.$(date +%s)"
|
|
41
|
+
cp "$WALLET_FILE" "$WALLET_BACKUP"
|
|
42
|
+
chmod 600 "$WALLET_BACKUP"
|
|
43
|
+
echo " ✓ Wallet backed up to: $WALLET_BACKUP"
|
|
44
|
+
else
|
|
45
|
+
echo " ⚠ Wallet file exists but has invalid format — skipping backup"
|
|
46
|
+
fi
|
|
47
|
+
else
|
|
48
|
+
echo " ℹ No existing wallet found"
|
|
49
|
+
fi
|
|
50
|
+
echo ""
|
|
51
|
+
|
|
52
|
+
# 1. Remove plugin files
|
|
53
|
+
echo "→ Removing plugin files..."
|
|
54
|
+
rm -rf ~/.openclaw/extensions/ckcloud
|
|
55
|
+
|
|
56
|
+
# 2. Clean config entries
|
|
57
|
+
echo "→ Cleaning config entries..."
|
|
58
|
+
node -e "
|
|
59
|
+
const f = require('os').homedir() + '/.openclaw/openclaw.json';
|
|
60
|
+
const fs = require('fs');
|
|
61
|
+
if (!fs.existsSync(f)) {
|
|
62
|
+
console.log(' No openclaw.json found, skipping');
|
|
63
|
+
process.exit(0);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let c;
|
|
67
|
+
try {
|
|
68
|
+
c = JSON.parse(fs.readFileSync(f, 'utf8'));
|
|
69
|
+
} catch (err) {
|
|
70
|
+
const backupPath = f + '.corrupt.' + Date.now();
|
|
71
|
+
console.error(' ERROR: Invalid JSON in openclaw.json');
|
|
72
|
+
console.error(' ' + err.message);
|
|
73
|
+
try {
|
|
74
|
+
fs.copyFileSync(f, backupPath);
|
|
75
|
+
console.log(' Backed up to: ' + backupPath);
|
|
76
|
+
} catch {}
|
|
77
|
+
console.log(' Skipping config cleanup...');
|
|
78
|
+
process.exit(0);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Clean plugin entries
|
|
82
|
+
if (c.plugins?.entries?.ckcloud) delete c.plugins.entries.ckcloud;
|
|
83
|
+
if (c.plugins?.installs?.ckcloud) delete c.plugins.installs.ckcloud;
|
|
84
|
+
// Clean plugins.allow (removes stale ckcloud reference)
|
|
85
|
+
if (Array.isArray(c.plugins?.allow)) {
|
|
86
|
+
c.plugins.allow = c.plugins.allow.filter(p => p !== 'ckcloud' && p !== '@ckcloudai.com/clawrouter');
|
|
87
|
+
}
|
|
88
|
+
// Remove deprecated model aliases from picker
|
|
89
|
+
const deprecated = ['ckcloud/mini', 'ckcloud/nvidia', 'ckcloud/gpt', 'ckcloud/o3', 'ckcloud/grok'];
|
|
90
|
+
if (c.agents?.defaults?.models) {
|
|
91
|
+
for (const key of deprecated) {
|
|
92
|
+
if (c.agents.defaults.models[key]) {
|
|
93
|
+
delete c.agents.defaults.models[key];
|
|
94
|
+
console.log(' Removed deprecated alias: ' + key);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
fs.writeFileSync(f, JSON.stringify(c, null, 2));
|
|
99
|
+
console.log(' Config cleaned');
|
|
100
|
+
"
|
|
101
|
+
|
|
102
|
+
# 3. Kill old proxy
|
|
103
|
+
echo "→ Stopping old proxy..."
|
|
104
|
+
kill_port_processes 8402
|
|
105
|
+
|
|
106
|
+
# 3.1. Remove stale models.json so it gets regenerated with apiKey
|
|
107
|
+
echo "→ Cleaning models cache..."
|
|
108
|
+
rm -f ~/.openclaw/agents/main/agent/models.json 2>/dev/null || true
|
|
109
|
+
|
|
110
|
+
# 4. Inject auth profile (ensures ckcloud provider is recognized)
|
|
111
|
+
echo "→ Injecting auth profile..."
|
|
112
|
+
node -e "
|
|
113
|
+
const os = require('os');
|
|
114
|
+
const fs = require('fs');
|
|
115
|
+
const path = require('path');
|
|
116
|
+
const authDir = path.join(os.homedir(), '.openclaw', 'agents', 'main', 'agent');
|
|
117
|
+
const authPath = path.join(authDir, 'auth-profiles.json');
|
|
118
|
+
|
|
119
|
+
// Create directory if needed
|
|
120
|
+
fs.mkdirSync(authDir, { recursive: true });
|
|
121
|
+
|
|
122
|
+
// Load or create auth-profiles.json with correct OpenClaw format
|
|
123
|
+
let store = { version: 1, profiles: {} };
|
|
124
|
+
if (fs.existsSync(authPath)) {
|
|
125
|
+
try {
|
|
126
|
+
const existing = JSON.parse(fs.readFileSync(authPath, 'utf8'));
|
|
127
|
+
// Migrate if old format (no version field)
|
|
128
|
+
if (existing.version && existing.profiles) {
|
|
129
|
+
store = existing;
|
|
130
|
+
} else {
|
|
131
|
+
// Old format - keep version/profiles structure, old data is discarded
|
|
132
|
+
store = { version: 1, profiles: {} };
|
|
133
|
+
}
|
|
134
|
+
} catch (err) {
|
|
135
|
+
console.log(' Warning: Could not parse auth-profiles.json, creating fresh');
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Inject ckcloud auth if missing (OpenClaw format: profiles['provider:profileId'])
|
|
140
|
+
const profileKey = 'ckcloud:default';
|
|
141
|
+
if (!store.profiles[profileKey]) {
|
|
142
|
+
store.profiles[profileKey] = {
|
|
143
|
+
type: 'api_key',
|
|
144
|
+
provider: 'ckcloud',
|
|
145
|
+
key: 'x402-proxy-handles-auth'
|
|
146
|
+
};
|
|
147
|
+
fs.writeFileSync(authPath, JSON.stringify(store, null, 2));
|
|
148
|
+
console.log(' Auth profile created');
|
|
149
|
+
} else {
|
|
150
|
+
console.log(' Auth profile already exists');
|
|
151
|
+
}
|
|
152
|
+
"
|
|
153
|
+
|
|
154
|
+
# 5. Ensure apiKey is present for /model picker (but DON'T override default model)
|
|
155
|
+
echo "→ Finalizing setup..."
|
|
156
|
+
node -e "
|
|
157
|
+
const os = require('os');
|
|
158
|
+
const fs = require('fs');
|
|
159
|
+
const path = require('path');
|
|
160
|
+
const configPath = path.join(os.homedir(), '.openclaw', 'openclaw.json');
|
|
161
|
+
|
|
162
|
+
if (fs.existsSync(configPath)) {
|
|
163
|
+
try {
|
|
164
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
165
|
+
let changed = false;
|
|
166
|
+
|
|
167
|
+
// Ensure ckcloud provider has apiKey (required by ModelRegistry for /model picker)
|
|
168
|
+
if (config.models?.providers?.ckcloud && !config.models.providers.ckcloud.apiKey) {
|
|
169
|
+
config.models.providers.ckcloud.apiKey = 'x402-proxy-handles-auth';
|
|
170
|
+
console.log(' Added apiKey to ckcloud provider config');
|
|
171
|
+
changed = true;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (changed) {
|
|
175
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
176
|
+
}
|
|
177
|
+
} catch (e) {
|
|
178
|
+
console.log(' Could not update config:', e.message);
|
|
179
|
+
}
|
|
180
|
+
} else {
|
|
181
|
+
console.log(' No openclaw.json found, skipping');
|
|
182
|
+
}
|
|
183
|
+
"
|
|
184
|
+
|
|
185
|
+
# 6. Install plugin (config is ready, but no allow list yet to avoid validation error)
|
|
186
|
+
echo "→ Installing ckcloud..."
|
|
187
|
+
|
|
188
|
+
openclaw plugins install @ckcloudai.com/clawrouter
|
|
189
|
+
|
|
190
|
+
# 6.1. Verify installation (check dist/ files exist)
|
|
191
|
+
echo "→ Verifying installation..."
|
|
192
|
+
DIST_PATH="$HOME/.openclaw/extensions/ckcloud/dist/index.js"
|
|
193
|
+
if [ ! -f "$DIST_PATH" ]; then
|
|
194
|
+
echo " ⚠️ dist/ files missing, clearing npm cache and retrying..."
|
|
195
|
+
npm cache clean --force 2>/dev/null || true
|
|
196
|
+
rm -rf ~/.openclaw/extensions/ckcloud
|
|
197
|
+
|
|
198
|
+
openclaw plugins install @ckcloudai.com/clawrouter
|
|
199
|
+
|
|
200
|
+
if [ ! -f "$DIST_PATH" ]; then
|
|
201
|
+
echo " ❌ Installation failed - dist/index.js still missing"
|
|
202
|
+
echo " Please report this issue"
|
|
203
|
+
exit 1
|
|
204
|
+
fi
|
|
205
|
+
fi
|
|
206
|
+
echo " ✓ dist/index.js verified"
|
|
207
|
+
|
|
208
|
+
# 6.2. Populate model allowlist so top ckcloud models appear in /model picker
|
|
209
|
+
echo "→ Populating model allowlist..."
|
|
210
|
+
node -e "
|
|
211
|
+
const os = require('os');
|
|
212
|
+
const fs = require('fs');
|
|
213
|
+
const path = require('path');
|
|
214
|
+
|
|
215
|
+
const configPath = path.join(os.homedir(), '.openclaw', 'openclaw.json');
|
|
216
|
+
if (!fs.existsSync(configPath)) {
|
|
217
|
+
console.log(' No openclaw.json found, skipping');
|
|
218
|
+
process.exit(0);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
223
|
+
let changed = false;
|
|
224
|
+
|
|
225
|
+
// Ensure provider exists with apiKey
|
|
226
|
+
if (!config.models) config.models = {};
|
|
227
|
+
if (!config.models.providers) config.models.providers = {};
|
|
228
|
+
if (!config.models.providers.ckcloud) {
|
|
229
|
+
config.models.providers.ckcloud = { api: 'openai-completions', models: [] };
|
|
230
|
+
changed = true;
|
|
231
|
+
}
|
|
232
|
+
if (!config.models.providers.ckcloud.apiKey) {
|
|
233
|
+
config.models.providers.ckcloud.apiKey = 'x402-proxy-handles-auth';
|
|
234
|
+
changed = true;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// models for the /model picker
|
|
238
|
+
const TOP_MODELS = [
|
|
239
|
+
'auto', 'free', 'eco', 'premium',
|
|
240
|
+
'moonshot/kimi-k2.5'
|
|
241
|
+
];
|
|
242
|
+
|
|
243
|
+
if (!config.agents) config.agents = {};
|
|
244
|
+
if (!config.agents.defaults) config.agents.defaults = {};
|
|
245
|
+
if (!config.agents.defaults.models || typeof config.agents.defaults.models !== 'object') {
|
|
246
|
+
config.agents.defaults.models = {};
|
|
247
|
+
changed = true;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const allowlist = config.agents.defaults.models;
|
|
251
|
+
// Clean out old ckcloud entries not in TOP_MODELS
|
|
252
|
+
const topSet = new Set(TOP_MODELS.map(id => 'ckcloud/' + id));
|
|
253
|
+
for (const key of Object.keys(allowlist)) {
|
|
254
|
+
if (key.startsWith('ckcloud/') && !topSet.has(key)) {
|
|
255
|
+
delete allowlist[key];
|
|
256
|
+
changed = true;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
let added = 0;
|
|
260
|
+
for (const id of TOP_MODELS) {
|
|
261
|
+
const key = 'ckcloud/' + id;
|
|
262
|
+
if (!allowlist[key]) {
|
|
263
|
+
allowlist[key] = {};
|
|
264
|
+
added++;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (added > 0) {
|
|
268
|
+
changed = true;
|
|
269
|
+
console.log(' Added ' + added + ' models to allowlist (' + TOP_MODELS.length + ' total)');
|
|
270
|
+
} else {
|
|
271
|
+
console.log(' Allowlist already up to date');
|
|
272
|
+
}
|
|
273
|
+
if (changed) {
|
|
274
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
275
|
+
}
|
|
276
|
+
} catch (err) {
|
|
277
|
+
console.log(' Could not update config:', err.message);
|
|
278
|
+
}
|
|
279
|
+
"
|
|
280
|
+
|
|
281
|
+
# 7. Add plugin to allow list (done AFTER install so plugin files exist for validation)
|
|
282
|
+
echo "→ Adding to plugins allow list..."
|
|
283
|
+
node -e "
|
|
284
|
+
const os = require('os');
|
|
285
|
+
const fs = require('fs');
|
|
286
|
+
const path = require('path');
|
|
287
|
+
const configPath = path.join(os.homedir(), '.openclaw', 'openclaw.json');
|
|
288
|
+
|
|
289
|
+
if (fs.existsSync(configPath)) {
|
|
290
|
+
try {
|
|
291
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
292
|
+
|
|
293
|
+
// Ensure plugins.allow exists and includes ckcloud
|
|
294
|
+
if (!config.plugins) config.plugins = {};
|
|
295
|
+
if (!Array.isArray(config.plugins.allow)) {
|
|
296
|
+
config.plugins.allow = [];
|
|
297
|
+
}
|
|
298
|
+
if (!config.plugins.allow.includes('ckcloud') && !config.plugins.allow.includes('@ckcloudai.com/clawrouter')) {
|
|
299
|
+
config.plugins.allow.push('ckcloud');
|
|
300
|
+
console.log(' Added ckcloud to plugins.allow');
|
|
301
|
+
} else {
|
|
302
|
+
console.log(' Plugin already in allow list');
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
306
|
+
} catch (e) {
|
|
307
|
+
console.log(' Could not update config:', e.message);
|
|
308
|
+
}
|
|
309
|
+
} else {
|
|
310
|
+
console.log(' No openclaw.json found, skipping');
|
|
311
|
+
}
|
|
312
|
+
"
|
|
313
|
+
|
|
314
|
+
# Final: verify wallet survived reinstall
|
|
315
|
+
echo "→ Verifying wallet integrity..."
|
|
316
|
+
if [ -f "$WALLET_FILE" ]; then
|
|
317
|
+
CURRENT_KEY=$(cat "$WALLET_FILE" | tr -d '[:space:]')
|
|
318
|
+
CURRENT_LEN=${#CURRENT_KEY}
|
|
319
|
+
if [[ "$CURRENT_KEY" == 0x* ]] && [ "$CURRENT_LEN" -eq 66 ]; then
|
|
320
|
+
echo " ✓ Wallet key intact"
|
|
321
|
+
else
|
|
322
|
+
if [ -n "$WALLET_BACKUP" ] && [ -f "$WALLET_BACKUP" ]; then
|
|
323
|
+
cp "$WALLET_BACKUP" "$WALLET_FILE"
|
|
324
|
+
chmod 600 "$WALLET_FILE"
|
|
325
|
+
echo " ✓ Wallet restored from backup"
|
|
326
|
+
fi
|
|
327
|
+
fi
|
|
328
|
+
else
|
|
329
|
+
if [ -n "$WALLET_BACKUP" ] && [ -f "$WALLET_BACKUP" ]; then
|
|
330
|
+
mkdir -p "$(dirname "$WALLET_FILE")"
|
|
331
|
+
cp "$WALLET_BACKUP" "$WALLET_FILE"
|
|
332
|
+
chmod 600 "$WALLET_FILE"
|
|
333
|
+
echo " ✓ Wallet restored from backup: $WALLET_BACKUP"
|
|
334
|
+
fi
|
|
335
|
+
fi
|
|
336
|
+
|
|
337
|
+
echo ""
|
|
338
|
+
echo "✓ Done! Smart routing enabled by default."
|
|
339
|
+
echo ""
|
|
340
|
+
echo "Run: openclaw gateway restart"
|
|
341
|
+
echo ""
|
|
342
|
+
|
|
343
|
+
echo "To uninstall: bash ~/.openclaw/extensions/ckcloud/scripts/uninstall.sh"
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
kill_port_processes() {
|
|
5
|
+
local port="$1"
|
|
6
|
+
local pids=""
|
|
7
|
+
|
|
8
|
+
if command -v lsof >/dev/null 2>&1; then
|
|
9
|
+
pids="$(lsof -ti :"$port" 2>/dev/null || true)"
|
|
10
|
+
elif command -v fuser >/dev/null 2>&1; then
|
|
11
|
+
pids="$(fuser "$port"/tcp 2>/dev/null || true)"
|
|
12
|
+
elif command -v ss >/dev/null 2>&1; then
|
|
13
|
+
pids="$(ss -lptn "sport = :$port" 2>/dev/null | sed -n 's/.*pid=\([0-9]\+\).*/\1/p' | sort -u)"
|
|
14
|
+
elif command -v netstat >/dev/null 2>&1; then
|
|
15
|
+
pids="$(netstat -nlpt 2>/dev/null | awk -v p=":$port" '$4 ~ p"$" {split($7,a,"/"); if (a[1] ~ /^[0-9]+$/) print a[1]}' | sort -u)"
|
|
16
|
+
else
|
|
17
|
+
echo " Warning: could not find lsof/fuser/ss/netstat; skipping proxy stop"
|
|
18
|
+
return 0
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
if [ -n "$pids" ]; then
|
|
22
|
+
echo "$pids" | xargs kill -9 2>/dev/null || true
|
|
23
|
+
fi
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
echo "🦞 ckcloud plugin uninstall"
|
|
27
|
+
echo ""
|
|
28
|
+
|
|
29
|
+
# 1. Stop proxy
|
|
30
|
+
echo "→ Stopping proxy..."
|
|
31
|
+
kill_port_processes 8402
|
|
32
|
+
|
|
33
|
+
# 2. Remove plugin files
|
|
34
|
+
echo "→ Removing plugin files..."
|
|
35
|
+
rm -rf ~/.openclaw/extensions/ckcloud
|
|
36
|
+
|
|
37
|
+
# 3. Clean openclaw.json
|
|
38
|
+
echo "→ Cleaning openclaw.json..."
|
|
39
|
+
node -e "
|
|
40
|
+
const os = require('os');
|
|
41
|
+
const fs = require('fs');
|
|
42
|
+
const path = require('path');
|
|
43
|
+
const configPath = path.join(os.homedir(), '.openclaw', 'openclaw.json');
|
|
44
|
+
|
|
45
|
+
if (!fs.existsSync(configPath)) {
|
|
46
|
+
console.log(' No openclaw.json found, skipping');
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
52
|
+
let changed = false;
|
|
53
|
+
|
|
54
|
+
// Remove ckcloud provider
|
|
55
|
+
if (config.models?.providers?.ckcloud) {
|
|
56
|
+
delete config.models.providers.ckcloud;
|
|
57
|
+
console.log(' Removed ckcloud provider');
|
|
58
|
+
changed = true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Remove plugin entries
|
|
62
|
+
if (config.plugins?.entries?.ckcloud) {
|
|
63
|
+
delete config.plugins.entries.ckcloud;
|
|
64
|
+
changed = true;
|
|
65
|
+
}
|
|
66
|
+
if (config.plugins?.installs?.ckcloud) {
|
|
67
|
+
delete config.plugins.installs.ckcloud;
|
|
68
|
+
changed = true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Remove from plugins.allow
|
|
72
|
+
if (Array.isArray(config.plugins?.allow)) {
|
|
73
|
+
const before = config.plugins.allow.length;
|
|
74
|
+
config.plugins.allow = config.plugins.allow.filter(
|
|
75
|
+
p => p !== 'ckcloud' && p !== '@ckcloudai.com/clawrouter'
|
|
76
|
+
);
|
|
77
|
+
if (config.plugins.allow.length !== before) {
|
|
78
|
+
console.log(' Removed from plugins.allow');
|
|
79
|
+
changed = true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Reset default model if it's ckcloud/auto
|
|
84
|
+
if (config.agents?.defaults?.model?.primary === 'ckcloud/auto') {
|
|
85
|
+
delete config.agents.defaults.model.primary;
|
|
86
|
+
console.log(' Reset default model (was ckcloud/auto)');
|
|
87
|
+
changed = true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Remove ckcloud models from allowlist
|
|
91
|
+
if (config.agents?.defaults?.models) {
|
|
92
|
+
const models = config.agents.defaults.models;
|
|
93
|
+
let removedCount = 0;
|
|
94
|
+
for (const key of Object.keys(models)) {
|
|
95
|
+
if (key.startsWith('ckcloud/')) {
|
|
96
|
+
delete models[key];
|
|
97
|
+
removedCount++;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (removedCount > 0) {
|
|
101
|
+
console.log(' Removed ' + removedCount + ' ckcloud models from allowlist');
|
|
102
|
+
changed = true;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (changed) {
|
|
107
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
108
|
+
console.log(' Config cleaned');
|
|
109
|
+
} else {
|
|
110
|
+
console.log(' No changes needed');
|
|
111
|
+
}
|
|
112
|
+
} catch (err) {
|
|
113
|
+
console.error(' Error:', err.message);
|
|
114
|
+
}
|
|
115
|
+
"
|
|
116
|
+
|
|
117
|
+
# 4. Clean auth-profiles.json
|
|
118
|
+
echo "→ Cleaning auth profiles..."
|
|
119
|
+
node -e "
|
|
120
|
+
const os = require('os');
|
|
121
|
+
const fs = require('fs');
|
|
122
|
+
const path = require('path');
|
|
123
|
+
const agentsDir = path.join(os.homedir(), '.openclaw', 'agents');
|
|
124
|
+
|
|
125
|
+
if (!fs.existsSync(agentsDir)) {
|
|
126
|
+
console.log(' No agents directory found');
|
|
127
|
+
process.exit(0);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const agents = fs.readdirSync(agentsDir, { withFileTypes: true })
|
|
131
|
+
.filter(d => d.isDirectory())
|
|
132
|
+
.map(d => d.name);
|
|
133
|
+
|
|
134
|
+
for (const agentId of agents) {
|
|
135
|
+
const authPath = path.join(agentsDir, agentId, 'agent', 'auth-profiles.json');
|
|
136
|
+
if (!fs.existsSync(authPath)) continue;
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
const store = JSON.parse(fs.readFileSync(authPath, 'utf8'));
|
|
140
|
+
if (store.profiles?.['ckcloud:default']) {
|
|
141
|
+
delete store.profiles['ckcloud:default'];
|
|
142
|
+
fs.writeFileSync(authPath, JSON.stringify(store, null, 2));
|
|
143
|
+
console.log(' Removed ckcloud auth from ' + agentId);
|
|
144
|
+
}
|
|
145
|
+
} catch {}
|
|
146
|
+
}
|
|
147
|
+
"
|
|
148
|
+
|
|
149
|
+
# 5. Clean models cache
|
|
150
|
+
echo "→ Cleaning models cache..."
|
|
151
|
+
rm -f ~/.openclaw/agents/*/agent/models.json 2>/dev/null || true
|
|
152
|
+
|
|
153
|
+
echo ""
|
|
154
|
+
echo "✓ ckcloud uninstalled"
|
|
155
|
+
echo ""
|
|
156
|
+
echo "Restart OpenClaw to apply changes:"
|
|
157
|
+
echo " openclaw gateway restart"
|