@simonyea/holysheep-cli 1.7.57 → 1.7.59
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/package.json +1 -1
- package/src/tools/openclaw.js +23 -2
- package/src/webui/server.js +33 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simonyea/holysheep-cli",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.59",
|
|
4
4
|
"description": "Claude Code/Cursor/Cline API relay for China — ¥1=$1, WeChat/Alipay payment, no credit card, no VPN. One command setup for all AI coding tools.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"openai-china",
|
package/src/tools/openclaw.js
CHANGED
|
@@ -199,6 +199,21 @@ function waitForBridge(port) {
|
|
|
199
199
|
return false
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
+
function stopBridge() {
|
|
203
|
+
// 杀掉所有已有的 bridge 进程,避免重新配置时端口冲突
|
|
204
|
+
try {
|
|
205
|
+
if (isWin) {
|
|
206
|
+
execSync('taskkill /F /FI "WINDOWTITLE eq openclaw-bridge*" 2>nul', { shell: true, stdio: 'ignore' })
|
|
207
|
+
// 按命令行匹配
|
|
208
|
+
const out = execSync('wmic process where "commandline like \'%openclaw-bridge%\'" get processid', { shell: true, stdio: 'pipe', encoding: 'utf8' })
|
|
209
|
+
const pids = out.match(/\d+/g)
|
|
210
|
+
if (pids) pids.forEach(pid => { try { execSync(`taskkill /F /PID ${pid}`, { stdio: 'ignore' }) } catch {} })
|
|
211
|
+
} else {
|
|
212
|
+
execSync("pkill -f 'openclaw-bridge' 2>/dev/null || true", { shell: true, stdio: 'ignore' })
|
|
213
|
+
}
|
|
214
|
+
} catch {}
|
|
215
|
+
}
|
|
216
|
+
|
|
202
217
|
function startBridge(port) {
|
|
203
218
|
if (waitForBridge(port)) return true
|
|
204
219
|
|
|
@@ -611,6 +626,14 @@ module.exports = {
|
|
|
611
626
|
this._lastRuntimeCommand = runtime.command
|
|
612
627
|
this._lastRuntimeVia = runtime.via
|
|
613
628
|
|
|
629
|
+
// 重新配置前先停掉旧的 Bridge 和 Gateway,释放端口
|
|
630
|
+
console.log(chalk.gray(' → 停止旧的 Bridge 和 Gateway...'))
|
|
631
|
+
stopBridge()
|
|
632
|
+
runOpenClaw(['gateway', 'stop'], { preferNpx: runtime.via === 'npx' })
|
|
633
|
+
// 等端口释放
|
|
634
|
+
const t0 = Date.now()
|
|
635
|
+
while (Date.now() - t0 < 1000) {}
|
|
636
|
+
|
|
614
637
|
if (runtime.via === 'npx' && removeBrokenLaunchAgent()) {
|
|
615
638
|
console.log(chalk.gray(' → 已清理旧的 OpenClaw 守护进程配置(失效的 npx 缓存路径)'))
|
|
616
639
|
}
|
|
@@ -654,8 +677,6 @@ module.exports = {
|
|
|
654
677
|
}
|
|
655
678
|
const bridgeBaseUrl = getBridgeBaseUrl(bridgePort)
|
|
656
679
|
|
|
657
|
-
runOpenClaw(['gateway', 'stop'], { preferNpx: runtime.via === 'npx' })
|
|
658
|
-
|
|
659
680
|
if (gatewayPort !== DEFAULT_GATEWAY_PORT) {
|
|
660
681
|
console.log(chalk.yellow(` ⚠️ 端口 ${DEFAULT_GATEWAY_PORT} 已占用,自动切换到 ${gatewayPort}`))
|
|
661
682
|
const listeners = listPortListeners(DEFAULT_GATEWAY_PORT)
|
package/src/webui/server.js
CHANGED
|
@@ -107,16 +107,43 @@ const UPGRADABLE_TOOLS = [
|
|
|
107
107
|
{ name: 'Gemini CLI', id: 'gemini-cli', command: 'gemini', versionCmd: 'gemini --version', npmPkg: '@google/gemini-cli', installCmd: 'npm install -g @google/gemini-cli@latest' },
|
|
108
108
|
]
|
|
109
109
|
|
|
110
|
+
// ── Update check (cached, refreshes every 30min) ────────────────────────────
|
|
111
|
+
|
|
112
|
+
let _latestVersion = null
|
|
113
|
+
let _lastCheckTime = 0
|
|
114
|
+
const UPDATE_CHECK_INTERVAL = 30 * 60 * 1000
|
|
115
|
+
|
|
116
|
+
async function checkLatestVersion() {
|
|
117
|
+
const now = Date.now()
|
|
118
|
+
if (_latestVersion && now - _lastCheckTime < UPDATE_CHECK_INTERVAL) return _latestVersion
|
|
119
|
+
try {
|
|
120
|
+
const fetch = require('node-fetch')
|
|
121
|
+
const r = await fetch('https://registry.npmjs.org/@simonyea/holysheep-cli/latest', { timeout: 5000 })
|
|
122
|
+
if (r.ok) {
|
|
123
|
+
const data = await r.json()
|
|
124
|
+
_latestVersion = data.version || null
|
|
125
|
+
_lastCheckTime = now
|
|
126
|
+
}
|
|
127
|
+
} catch {}
|
|
128
|
+
return _latestVersion
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// 启动时立即检查一次
|
|
132
|
+
checkLatestVersion()
|
|
133
|
+
|
|
110
134
|
// ── API Handlers ─────────────────────────────────────────────────────────────
|
|
111
135
|
|
|
112
136
|
async function handleStatus(_req, res) {
|
|
113
137
|
const apiKey = getApiKey()
|
|
114
138
|
const config = loadConfig()
|
|
139
|
+
const latest = await checkLatestVersion()
|
|
115
140
|
json(res, {
|
|
116
141
|
loggedIn: !!apiKey,
|
|
117
142
|
apiKey: apiKey ? maskKey(apiKey) : null,
|
|
118
143
|
savedAt: config.savedAt || null,
|
|
119
144
|
version: pkg.version,
|
|
145
|
+
latestVersion: latest || null,
|
|
146
|
+
updateAvailable: latest && latest !== pkg.version ? latest : null,
|
|
120
147
|
})
|
|
121
148
|
}
|
|
122
149
|
|
|
@@ -158,10 +185,13 @@ async function handleBalance(_req, res) {
|
|
|
158
185
|
if (!apiKey) return json(res, { error: '未登录' }, 401)
|
|
159
186
|
try {
|
|
160
187
|
const fetch = require('node-fetch')
|
|
188
|
+
const controller = new AbortController()
|
|
189
|
+
const timer = setTimeout(() => controller.abort(), 15000)
|
|
161
190
|
const r = await fetch(`${SHOP_URL}/api/stats/overview`, {
|
|
162
191
|
headers: { Authorization: `Bearer ${apiKey}` },
|
|
163
|
-
|
|
192
|
+
signal: controller.signal,
|
|
164
193
|
})
|
|
194
|
+
clearTimeout(timer)
|
|
165
195
|
if (r.status === 401) return json(res, { error: 'API Key 无效或已过期' }, 401)
|
|
166
196
|
if (!r.ok) return json(res, { error: `HTTP ${r.status}` }, r.status)
|
|
167
197
|
const data = await r.json()
|
|
@@ -172,7 +202,8 @@ async function handleBalance(_req, res) {
|
|
|
172
202
|
totalCalls: Number(data.totalCalls || 0),
|
|
173
203
|
})
|
|
174
204
|
} catch (e) {
|
|
175
|
-
|
|
205
|
+
const msg = e.name === 'AbortError' ? '请求超时,请稍后重试' : e.message
|
|
206
|
+
json(res, { error: msg }, 500)
|
|
176
207
|
}
|
|
177
208
|
}
|
|
178
209
|
|