@simonyea/holysheep-cli 1.7.64 → 1.7.66
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/webui/index.html +10 -0
- package/src/webui/server.js +36 -12
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simonyea/holysheep-cli",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.66",
|
|
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/webui/index.html
CHANGED
|
@@ -479,16 +479,26 @@ async function doUpgradeTool(id, name) {
|
|
|
479
479
|
openConsole(`${t('upgradeOne')}: ${name}`)
|
|
480
480
|
document.getElementById('console-section').classList.add('busy')
|
|
481
481
|
|
|
482
|
+
let upgraded = false
|
|
482
483
|
await streamSSE('/api/tool/upgrade', { toolId: id }, (ev) => {
|
|
483
484
|
if (ev.type === 'progress') appendLog(ev.message, 'info')
|
|
484
485
|
else if (ev.type === 'output') appendLogRaw(ev.text)
|
|
485
486
|
else if (ev.type === 'done') {
|
|
487
|
+
upgraded = ev.success
|
|
486
488
|
appendLog(ev.success ? `\n✓ ${t('configSuccess')}` : `\n✗ ${t('configFailed')}`, ev.success ? 'ok' : 'err')
|
|
487
489
|
}
|
|
488
490
|
})
|
|
489
491
|
|
|
490
492
|
document.getElementById('console-section').classList.remove('busy')
|
|
491
493
|
busy = false
|
|
494
|
+
|
|
495
|
+
if (upgraded && id === 'holysheep') {
|
|
496
|
+
appendLog(`\n${lang === 'zh' ? '⚠ HolySheep CLI 已升级,需要重启 WebUI 服务。正在重启...' : '⚠ HolySheep CLI upgraded. Restarting WebUI...'}`, 'warn')
|
|
497
|
+
// 调用后端重启,3 秒后刷新页面
|
|
498
|
+
try { await fetch('/api/restart', { method: 'POST' }) } catch {}
|
|
499
|
+
setTimeout(() => location.reload(), 4000)
|
|
500
|
+
return
|
|
501
|
+
}
|
|
492
502
|
loadTools()
|
|
493
503
|
}
|
|
494
504
|
|
package/src/webui/server.js
CHANGED
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
'use strict'
|
|
6
6
|
|
|
7
|
+
// 设置 DNS 回退:Cloudflare (1.1.1.1) + 阿里 (223.5.5.5) + 系统默认
|
|
8
|
+
// 解决大陆服务器 DNS 解析 api.holysheep.ai 失败 (EAI_AGAIN) 的问题
|
|
9
|
+
const dns = require('dns')
|
|
10
|
+
dns.setServers([...new Set([...dns.getServers(), '1.1.1.1', '223.5.5.5'])])
|
|
11
|
+
|
|
7
12
|
const http = require('http')
|
|
8
13
|
const fs = require('fs')
|
|
9
14
|
const path = require('path')
|
|
@@ -21,12 +26,26 @@ function maskKey(key) {
|
|
|
21
26
|
return key.slice(0, 6) + '...' + key.slice(-4)
|
|
22
27
|
}
|
|
23
28
|
|
|
24
|
-
async function
|
|
29
|
+
async function fetchWithRetry(url, opts = {}, retries = 3) {
|
|
25
30
|
const fetch = require('node-fetch')
|
|
26
|
-
|
|
31
|
+
for (let i = 0; i < retries; i++) {
|
|
32
|
+
try {
|
|
33
|
+
return await fetch(url, { timeout: 10000, ...opts })
|
|
34
|
+
} catch (e) {
|
|
35
|
+
// DNS 暂时失败 (EAI_AGAIN) 或网络抖动,重试
|
|
36
|
+
if (i < retries - 1 && (e.code === 'EAI_AGAIN' || e.code === 'ETIMEDOUT' || e.code === 'ECONNRESET' || e.type === 'system')) {
|
|
37
|
+
await new Promise(r => setTimeout(r, 1500 * (i + 1)))
|
|
38
|
+
continue
|
|
39
|
+
}
|
|
40
|
+
throw e
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function validateApiKey(apiKey) {
|
|
46
|
+
const res = await fetchWithRetry(`${BASE_URL_OPENAI}/models`, {
|
|
27
47
|
method: 'GET',
|
|
28
48
|
headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
|
|
29
|
-
timeout: 10000,
|
|
30
49
|
})
|
|
31
50
|
return res.status === 200
|
|
32
51
|
}
|
|
@@ -121,8 +140,7 @@ async function checkLatestVersion() {
|
|
|
121
140
|
const now = Date.now()
|
|
122
141
|
if (_latestVersion && now - _lastCheckTime < UPDATE_CHECK_INTERVAL) return _latestVersion
|
|
123
142
|
try {
|
|
124
|
-
const
|
|
125
|
-
const r = await fetch('https://registry.npmjs.org/@simonyea/holysheep-cli/latest', { timeout: 5000 })
|
|
143
|
+
const r = await fetchWithRetry('https://registry.npmjs.org/@simonyea/holysheep-cli/latest', {}, 2)
|
|
126
144
|
if (r.ok) {
|
|
127
145
|
const data = await r.json()
|
|
128
146
|
_latestVersion = data.version || null
|
|
@@ -188,14 +206,9 @@ async function handleBalance(_req, res) {
|
|
|
188
206
|
const apiKey = getApiKey()
|
|
189
207
|
if (!apiKey) return json(res, { error: '未登录' }, 401)
|
|
190
208
|
try {
|
|
191
|
-
const
|
|
192
|
-
const controller = new AbortController()
|
|
193
|
-
const timer = setTimeout(() => controller.abort(), 15000)
|
|
194
|
-
const r = await fetch(`${SHOP_URL}/api/stats/overview`, {
|
|
209
|
+
const r = await fetchWithRetry(`${SHOP_URL}/api/stats/overview`, {
|
|
195
210
|
headers: { Authorization: `Bearer ${apiKey}` },
|
|
196
|
-
signal: controller.signal,
|
|
197
211
|
})
|
|
198
|
-
clearTimeout(timer)
|
|
199
212
|
if (r.status === 401) return json(res, { error: 'API Key 无效或已过期' }, 401)
|
|
200
213
|
if (!r.ok) return json(res, { error: `HTTP ${r.status}` }, r.status)
|
|
201
214
|
const data = await r.json()
|
|
@@ -206,7 +219,7 @@ async function handleBalance(_req, res) {
|
|
|
206
219
|
totalCalls: Number(data.totalCalls || 0),
|
|
207
220
|
})
|
|
208
221
|
} catch (e) {
|
|
209
|
-
const msg = e.
|
|
222
|
+
const msg = e.code === 'EAI_AGAIN' ? 'DNS 解析失败,请检查网络' : e.message
|
|
210
223
|
json(res, { error: msg }, 500)
|
|
211
224
|
}
|
|
212
225
|
}
|
|
@@ -685,6 +698,17 @@ async function handleRequest(req, res) {
|
|
|
685
698
|
if (route === '/api/tool/upgrade' && req.method === 'POST') return await handleToolUpgrade(req, res)
|
|
686
699
|
if (route === '/api/env' && req.method === 'GET') return handleEnv(req, res)
|
|
687
700
|
if (route === '/api/env/clean' && req.method === 'POST') return handleEnvClean(req, res)
|
|
701
|
+
if (route === '/api/restart' && req.method === 'POST') {
|
|
702
|
+
json(res, { ok: true })
|
|
703
|
+
// 升级后用新版 hs web 重启自身
|
|
704
|
+
const port = req.headers.host?.split(':')[1] || '9876'
|
|
705
|
+
setTimeout(() => {
|
|
706
|
+
const child = spawn(process.execPath, [path.join(__dirname, '..', 'index.js'), 'web', '--port', port, '--no-open'], { detached: true, stdio: 'ignore' })
|
|
707
|
+
child.unref()
|
|
708
|
+
process.exit(0)
|
|
709
|
+
}, 500)
|
|
710
|
+
return
|
|
711
|
+
}
|
|
688
712
|
|
|
689
713
|
res.writeHead(404)
|
|
690
714
|
res.end('Not Found')
|