@simonyea/holysheep-cli 1.7.67 → 1.7.69

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.67",
3
+ "version": "1.7.69",
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",
@@ -763,6 +763,9 @@ module.exports = {
763
763
  },
764
764
 
765
765
  reset() {
766
+ // 先停进程再删配置,避免残留进程指向已删除的配置导致 timeout
767
+ stopBridge()
768
+ try { runOpenClaw(['gateway', 'stop'], { preferNpx: detectRuntime().via === 'npx' }) } catch {}
766
769
  try { fs.unlinkSync(CONFIG_FILE) } catch {}
767
770
  try { fs.unlinkSync(BRIDGE_CONFIG_FILE) } catch {}
768
771
  },
@@ -187,7 +187,7 @@ const I18N = {
187
187
  installSuccess: '安装完成', installFailed: '安装失败',
188
188
  needLogin: '请先登录', cleanDone: '已清理',
189
189
  hotReload: '已生效,无需重启', needRestart: '重启终端后生效',
190
- launch: '启动命令', upgradeOne: '升级',
190
+ launch: '启动命令', upgradeOne: '升级', open: '打开',
191
191
  updateAvailable: '有新版本可用', updateNow: '立即升级',
192
192
  },
193
193
  en: {
@@ -210,7 +210,7 @@ const I18N = {
210
210
  installSuccess: 'Installed', installFailed: 'Install failed',
211
211
  needLogin: 'Please login first', cleanDone: 'Cleaned',
212
212
  hotReload: 'Active, no restart needed', needRestart: 'Restart terminal to apply',
213
- launch: 'Launch', upgradeOne: 'Upgrade',
213
+ launch: 'Launch', upgradeOne: 'Upgrade', open: 'Open',
214
214
  updateAvailable: 'Update available', updateNow: 'Update now',
215
215
  },
216
216
  }
@@ -365,7 +365,8 @@ function renderToolCard(tool) {
365
365
  dotClass = 'dot-ok'
366
366
  statusBadges = `<span class="badge badge-ok">${t('installed')}</span> <span class="badge badge-ok">${t('configured')}</span>`
367
367
  const upgradeBtn = tool.canUpgrade ? `<button class="btn btn-outline btn-sm" onclick="doUpgradeTool('${tool.id}','${esc(tool.name)}')" ${busy ? 'disabled' : ''}>${t('upgradeOne')}</button>` : ''
368
- actions = `<button class="btn btn-outline btn-sm" onclick="doConfigureTool('${tool.id}','${esc(tool.name)}')" ${busy ? 'disabled' : ''}>${t('reconfigure')}</button>
368
+ actions = `<button class="btn btn-primary btn-sm" onclick="doLaunchTool('${tool.id}')">${t('open')}</button>
369
+ <button class="btn btn-outline btn-sm" onclick="doConfigureTool('${tool.id}','${esc(tool.name)}')" ${busy ? 'disabled' : ''}>${t('reconfigure')}</button>
369
370
  ${upgradeBtn}
370
371
  <button class="btn btn-danger btn-sm" onclick="doResetTool('${tool.id}','${esc(tool.name)}')" ${busy ? 'disabled' : ''}>${t('reset')}</button>`
371
372
  hintLine = tool.version ? `<span class="mono" style="font-size:0.78rem;color:var(--text2)">${esc(tool.version)}</span>` : ''
@@ -423,6 +424,12 @@ async function doConfigureTool(id, name) {
423
424
  loadTools()
424
425
  }
425
426
 
427
+ async function doLaunchTool(id) {
428
+ try {
429
+ await api('tool/launch', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ toolId: id }) })
430
+ } catch {}
431
+ }
432
+
426
433
  async function doResetTool(id, name) {
427
434
  if (!confirm(t('confirmReset'))) return
428
435
  try {
@@ -620,6 +620,43 @@ async function handleToolReset(req, res) {
620
620
  }
621
621
  }
622
622
 
623
+ // ── Launch tool in new terminal ───────────────────────────────────────────────
624
+
625
+ async function handleToolLaunch(req, res) {
626
+ const body = await parseBody(req)
627
+ const { toolId } = body
628
+ const tool = TOOLS.find(t => t.id === toolId)
629
+ if (!tool) return json(res, { error: '未知工具' }, 400)
630
+
631
+ // OpenClaw: 打开浏览器 dashboard 而非终端
632
+ if (toolId === 'openclaw') {
633
+ const port = tool.getGatewayPort?.() || 18789
634
+ const url = `http://127.0.0.1:${port}/`
635
+ if (process.platform === 'darwin') spawn('open', [url], { detached: true, stdio: 'ignore' }).unref()
636
+ else if (process.platform === 'win32') spawn('cmd.exe', ['/c', 'start', '', url], { detached: true, stdio: 'ignore', shell: true }).unref()
637
+ else spawn('xdg-open', [url], { detached: true, stdio: 'ignore' }).unref()
638
+ return json(res, { ok: true, type: 'browser', url })
639
+ }
640
+
641
+ const cmd = tool.launchCmd
642
+ if (!cmd) return json(res, { error: '无启动命令' }, 400)
643
+
644
+ if (process.platform === 'darwin') {
645
+ spawn('osascript', ['-e', `tell app "Terminal" to do script "${cmd.replace(/"/g, '\\"')}"`], { detached: true, stdio: 'ignore' }).unref()
646
+ } else if (process.platform === 'win32') {
647
+ spawn('cmd.exe', ['/c', 'start', 'cmd.exe', '/k', cmd], { detached: true, stdio: 'ignore', shell: true }).unref()
648
+ } else {
649
+ const terms = ['x-terminal-emulator', 'gnome-terminal', 'xterm']
650
+ for (const term of terms) {
651
+ if (commandExists(term)) {
652
+ spawn(term, ['-e', cmd], { detached: true, stdio: 'ignore' }).unref()
653
+ break
654
+ }
655
+ }
656
+ }
657
+ json(res, { ok: true, type: 'terminal', cmd })
658
+ }
659
+
623
660
  // ── Environment variables ────────────────────────────────────────────────────
624
661
 
625
662
  const HS_ENV_KEYS = ['ANTHROPIC_API_KEY', 'ANTHROPIC_AUTH_TOKEN', 'ANTHROPIC_BASE_URL', 'OPENAI_API_KEY', 'OPENAI_BASE_URL']
@@ -701,6 +738,7 @@ async function handleRequest(req, res) {
701
738
  if (route === '/api/tool/configure' && req.method === 'POST') return await handleToolConfigure(req, res)
702
739
  if (route === '/api/tool/reset' && req.method === 'POST') return await handleToolReset(req, res)
703
740
  if (route === '/api/tool/upgrade' && req.method === 'POST') return await handleToolUpgrade(req, res)
741
+ if (route === '/api/tool/launch' && req.method === 'POST') return await handleToolLaunch(req, res)
704
742
  if (route === '/api/env' && req.method === 'GET') return handleEnv(req, res)
705
743
  if (route === '/api/env/clean' && req.method === 'POST') return handleEnvClean(req, res)
706
744
  if (route === '/api/restart' && req.method === 'POST') {