@simonyea/holysheep-cli 1.7.59 → 1.7.60

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simonyea/holysheep-cli",
3
- "version": "1.7.59",
3
+ "version": "1.7.60",
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",
@@ -129,6 +129,9 @@ a:hover { text-decoration: underline; }
129
129
  .footer-links { display: flex; gap: 16px; justify-content: center; flex-wrap: wrap; font-size: 0.85rem; margin-bottom: 8px; }
130
130
  .footer-sub { font-size: 0.78rem; color: var(--text2); }
131
131
 
132
+ /* Update banner */
133
+ .update-banner { background: var(--primary-dim); border: 1px solid var(--primary); border-radius: 8px; padding: 10px 16px; margin-bottom: 16px; display: flex; align-items: center; justify-content: space-between; font-size: 0.85rem; }
134
+
132
135
  /* Misc */
133
136
  .loading { color: var(--text2); font-size: 0.85rem; }
134
137
  .hidden { display: none !important; }
@@ -184,7 +187,8 @@ const I18N = {
184
187
  installSuccess: '安装完成', installFailed: '安装失败',
185
188
  needLogin: '请先登录', cleanDone: '已清理',
186
189
  hotReload: '已生效,无需重启', needRestart: '重启终端后生效',
187
- launch: '启动命令',
190
+ launch: '启动命令', upgradeOne: '升级',
191
+ updateAvailable: '有新版本可用', updateNow: '立即升级',
188
192
  },
189
193
  en: {
190
194
  loggedIn: 'Logged in', notLoggedIn: 'Not logged in', login: 'Login', logout: 'Logout',
@@ -206,7 +210,8 @@ const I18N = {
206
210
  installSuccess: 'Installed', installFailed: 'Install failed',
207
211
  needLogin: 'Please login first', cleanDone: 'Cleaned',
208
212
  hotReload: 'Active, no restart needed', needRestart: 'Restart terminal to apply',
209
- launch: 'Launch',
213
+ launch: 'Launch', upgradeOne: 'Upgrade',
214
+ updateAvailable: 'Update available', updateNow: 'Update now',
210
215
  },
211
216
  }
212
217
 
@@ -251,6 +256,11 @@ async function loadAccount() {
251
256
 
252
257
  document.getElementById('version').textContent = 'v' + (s.version || '')
253
258
 
259
+ // 版本更新提示
260
+ if (s.updateAvailable) {
261
+ document.getElementById('version').innerHTML = `v${s.version} <span style="color:var(--primary);cursor:pointer" onclick="doUpgradeTool('holysheep','HolySheep CLI')" title="${t('updateNow')}">→ v${s.updateAvailable} ${t('updateNow')}</span>`
262
+ }
263
+
254
264
  if (s.loggedIn) {
255
265
  const hasBalance = !b.error && typeof b.balance === 'number'
256
266
  el.innerHTML = `<div class="card account-card account-logged-in">
@@ -333,12 +343,15 @@ function renderToolCard(tool) {
333
343
  } else if (!tool.configured) {
334
344
  dotClass = 'dot-warn'
335
345
  statusBadges = `<span class="badge badge-ok">${t('installed')}</span> <span class="badge badge-warn">${t('notConfigured')}</span>`
336
- actions = `<button class="btn btn-primary btn-sm" onclick="doConfigureTool('${tool.id}','${esc(tool.name)}')" ${busy ? 'disabled' : ''}>${t('configure')}</button>`
346
+ const upgradeBtn = tool.canUpgrade ? `<button class="btn btn-outline btn-sm" onclick="doUpgradeTool('${tool.id}','${esc(tool.name)}')" ${busy ? 'disabled' : ''}>${t('upgradeOne')}</button>` : ''
347
+ actions = `<button class="btn btn-primary btn-sm" onclick="doConfigureTool('${tool.id}','${esc(tool.name)}')" ${busy ? 'disabled' : ''}>${t('configure')}</button>${upgradeBtn}`
337
348
  hintLine = tool.version ? `<span class="mono" style="font-size:0.78rem;color:var(--text2)">${esc(tool.version)}</span>` : ''
338
349
  } else {
339
350
  dotClass = 'dot-ok'
340
351
  statusBadges = `<span class="badge badge-ok">${t('installed')}</span> <span class="badge badge-ok">${t('configured')}</span>`
352
+ const upgradeBtn = tool.canUpgrade ? `<button class="btn btn-outline btn-sm" onclick="doUpgradeTool('${tool.id}','${esc(tool.name)}')" ${busy ? 'disabled' : ''}>${t('upgradeOne')}</button>` : ''
341
353
  actions = `<button class="btn btn-outline btn-sm" onclick="doConfigureTool('${tool.id}','${esc(tool.name)}')" ${busy ? 'disabled' : ''}>${t('reconfigure')}</button>
354
+ ${upgradeBtn}
342
355
  <button class="btn btn-danger btn-sm" onclick="doResetTool('${tool.id}','${esc(tool.name)}')" ${busy ? 'disabled' : ''}>${t('reset')}</button>`
343
356
  hintLine = tool.version ? `<span class="mono" style="font-size:0.78rem;color:var(--text2)">${esc(tool.version)}</span>` : ''
344
357
  }
@@ -445,6 +458,25 @@ async function doUpgrade() {
445
458
  loadTools()
446
459
  }
447
460
 
461
+ async function doUpgradeTool(id, name) {
462
+ if (busy) return
463
+ busy = true
464
+ openConsole(`${t('upgradeOne')}: ${name}`)
465
+ document.getElementById('console-section').classList.add('busy')
466
+
467
+ await streamSSE('/api/tool/upgrade', { toolId: id }, (ev) => {
468
+ if (ev.type === 'progress') appendLog(ev.message, 'info')
469
+ else if (ev.type === 'output') appendLogRaw(ev.text)
470
+ else if (ev.type === 'done') {
471
+ appendLog(ev.success ? `\n✓ ${t('configSuccess')}` : `\n✗ ${t('configFailed')}`, ev.success ? 'ok' : 'err')
472
+ }
473
+ })
474
+
475
+ document.getElementById('console-section').classList.remove('busy')
476
+ busy = false
477
+ loadTools()
478
+ }
479
+
448
480
  // ── Console ──────────────────────────────────────────────────────────────────
449
481
  function openConsole(title) {
450
482
  const area = document.getElementById('console-section')
@@ -96,6 +96,7 @@ const AUTO_INSTALL = {
96
96
  // ── UPGRADABLE_TOOLS (from upgrade.js) ───────────────────────────────────────
97
97
 
98
98
  const UPGRADABLE_TOOLS = [
99
+ { name: 'HolySheep CLI', id: 'holysheep', command: 'hs', versionCmd: 'hs --version', npmPkg: '@simonyea/holysheep-cli', installCmd: 'npm install -g @simonyea/holysheep-cli@latest' },
99
100
  {
100
101
  name: 'Claude Code', id: 'claude-code', command: 'claude',
101
102
  versionCmd: 'claude --version', npmPkg: null,
@@ -104,6 +105,9 @@ const UPGRADABLE_TOOLS = [
104
105
  : 'curl -fsSL https://claude.ai/install.sh | bash',
105
106
  },
106
107
  { name: 'Codex CLI', id: 'codex', command: 'codex', versionCmd: 'codex --version', npmPkg: '@openai/codex', installCmd: 'npm install -g @openai/codex@latest' },
108
+ { name: 'Droid CLI', id: 'droid', command: 'droid', versionCmd: 'droid --version', npmPkg: null, installCmd: 'brew install --cask droid' },
109
+ { name: 'OpenCode', id: 'opencode', command: 'opencode', versionCmd: 'opencode --version', npmPkg: 'opencode-ai', installCmd: 'npm install -g opencode-ai@latest' },
110
+ { name: 'OpenClaw', id: 'openclaw', command: 'openclaw', versionCmd: 'openclaw --version', npmPkg: 'openclaw', installCmd: 'npm install -g openclaw@latest --ignore-scripts' },
107
111
  { name: 'Gemini CLI', id: 'gemini-cli', command: 'gemini', versionCmd: 'gemini --version', npmPkg: '@google/gemini-cli', installCmd: 'npm install -g @google/gemini-cli@latest' },
108
112
  ]
109
113
 
@@ -270,6 +274,7 @@ async function handleTools(_req, res) {
270
274
  hint: t.hint || null,
271
275
  launchCmd: t.launchCmd || null,
272
276
  canAutoInstall: !!AUTO_INSTALL[t.id],
277
+ canUpgrade: !!UPGRADABLE_TOOLS.find(u => u.id === t.id),
273
278
  }
274
279
  })
275
280
  json(res, tools)
@@ -493,11 +498,10 @@ async function handleToolConfigure(req, res) {
493
498
 
494
499
  const allModelIds = [
495
500
  'gpt-5.4', 'gpt-5.3-codex-spark',
496
- 'claude-sonnet-4-6', 'claude-sonnet-4-6[1m]',
497
- 'claude-opus-4-6', 'claude-opus-4-6[1m]',
501
+ 'claude-sonnet-4-6', 'claude-opus-4-6',
498
502
  'MiniMax-M2.7-highspeed', 'claude-haiku-4-5',
499
503
  ]
500
- const primaryModel = toolId === 'openclaw' ? 'claude-sonnet-4-6[1m]' : 'claude-sonnet-4-6'
504
+ const primaryModel = 'claude-sonnet-4-6'
501
505
 
502
506
  sseEmit(res, { type: 'progress', message: `正在配置 ${tool.name}...` })
503
507
 
@@ -537,6 +541,51 @@ async function handleToolConfigure(req, res) {
537
541
  })
538
542
  }
539
543
 
544
+ // ── Single-tool upgrade (SSE) ────────────────────────────────────────────────
545
+
546
+ async function handleToolUpgrade(req, res) {
547
+ const body = await parseBody(req)
548
+ const { toolId } = body
549
+ const entry = UPGRADABLE_TOOLS.find(t => t.id === toolId)
550
+ if (!entry) return json(res, { error: '不支持升级此工具' }, 400)
551
+
552
+ sseStart(res)
553
+
554
+ let localVer = null
555
+ try {
556
+ const out = execSync(entry.versionCmd, { stdio: 'pipe', timeout: 10000 }).toString().trim()
557
+ const m = out.match(/(\d+\.\d+\.\d+[\w.-]*)/)
558
+ localVer = m ? m[1] : out.split('\n')[0].slice(0, 30)
559
+ } catch {}
560
+
561
+ sseEmit(res, { type: 'progress', message: `${entry.name} 当前版本: ${localVer || '未知'}` })
562
+ sseEmit(res, { type: 'progress', message: `正在升级 ${entry.name}...` })
563
+
564
+ const ok = await new Promise(resolve => {
565
+ const child = spawn(entry.installCmd, [], { shell: true })
566
+ child.stdout?.on('data', chunk => sseEmit(res, { type: 'output', text: chunk.toString() }))
567
+ child.stderr?.on('data', chunk => sseEmit(res, { type: 'output', text: chunk.toString() }))
568
+ child.on('close', code => resolve(code === 0))
569
+ child.on('error', () => resolve(false))
570
+ })
571
+
572
+ let newVer = null
573
+ try {
574
+ const out = execSync(entry.versionCmd, { stdio: 'pipe', timeout: 10000 }).toString().trim()
575
+ const m = out.match(/(\d+\.\d+\.\d+[\w.-]*)/)
576
+ newVer = m ? m[1] : null
577
+ } catch {}
578
+
579
+ if (ok) {
580
+ sseEmit(res, { type: 'progress', message: `✓ ${entry.name} 升级成功: ${localVer || '?'} → ${newVer || 'latest'}` })
581
+ } else {
582
+ sseEmit(res, { type: 'progress', message: `✗ ${entry.name} 升级失败` })
583
+ }
584
+
585
+ sseEmit(res, { type: 'done', success: ok, localVer, newVer })
586
+ res.end()
587
+ }
588
+
540
589
  // ── Single-tool reset ────────────────────────────────────────────────────────
541
590
 
542
591
  async function handleToolReset(req, res) {
@@ -632,6 +681,7 @@ async function handleRequest(req, res) {
632
681
  if (route === '/api/tool/install' && req.method === 'POST') return await handleToolInstall(req, res)
633
682
  if (route === '/api/tool/configure' && req.method === 'POST') return await handleToolConfigure(req, res)
634
683
  if (route === '/api/tool/reset' && req.method === 'POST') return await handleToolReset(req, res)
684
+ if (route === '/api/tool/upgrade' && req.method === 'POST') return await handleToolUpgrade(req, res)
635
685
  if (route === '/api/env' && req.method === 'GET') return handleEnv(req, res)
636
686
  if (route === '/api/env/clean' && req.method === 'POST') return handleEnvClean(req, res)
637
687