@simonyea/holysheep-cli 1.7.5 → 1.7.7

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.5",
3
+ "version": "1.7.7",
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",
@@ -5,17 +5,19 @@ const inquirer = require('inquirer')
5
5
  const chalk = require('chalk')
6
6
  const ora = require('ora')
7
7
  const { execSync, spawnSync } = require('child_process')
8
+ const pkg = require('../../package.json')
8
9
  const { saveConfig, getApiKey, BASE_URL_ANTHROPIC, BASE_URL_OPENAI, SHOP_URL } = require('../utils/config')
9
- const { writeEnvToShell } = require('../utils/shell')
10
+ const { writeEnvToShell, ensureWindowsUserPathHasNpmBin } = require('../utils/shell')
11
+ const { commandExists } = require('../utils/which')
10
12
  const TOOLS = require('../tools')
11
13
 
12
14
  // 工具的自动安装命令(npm/pip)
13
15
  const AUTO_INSTALL = {
14
16
  'claude-code': {
15
17
  cmd: process.platform === 'win32'
16
- ? 'powershell -NoProfile -ExecutionPolicy Bypass -Command "irm https://claude.ai/install.ps1 | iex"'
18
+ ? 'powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "irm https://claude.ai/install.ps1 | iex"'
17
19
  : 'curl -fsSL https://claude.ai/install.sh | bash',
18
- mgr: process.platform === 'win32' ? 'powershell' : 'bash',
20
+ mgr: process.platform === 'win32' ? 'powershell.exe' : 'bash',
19
21
  },
20
22
  'codex': { cmd: 'npm install -g @openai/codex', mgr: 'npm' },
21
23
  'gemini-cli': { cmd: 'npm install -g @google/gemini-cli', mgr: 'npm' },
@@ -45,6 +47,19 @@ function getWindowsImmediateLaunchCmd(tool) {
45
47
  return null
46
48
  }
47
49
 
50
+ function getPreferredCliPrefix() {
51
+ return commandExists('hs')
52
+ ? 'hs'
53
+ : `npx @simonyea/holysheep-cli@${pkg.version}`
54
+ }
55
+
56
+ function getPreferredLaunchCmd(tool) {
57
+ if (!tool?.launchCmd) return null
58
+ if (!tool.launchCmd.startsWith('hs')) return tool.launchCmd
59
+ const prefix = getPreferredCliPrefix()
60
+ return tool.launchCmd.replace(/^hs\b/, prefix)
61
+ }
62
+
48
63
  function canAutoInstall(toolId) {
49
64
  return !!AUTO_INSTALL[toolId]
50
65
  }
@@ -55,7 +70,11 @@ async function tryAutoInstall(tool) {
55
70
 
56
71
  // 检查 npm/pip 是否可用
57
72
  try {
58
- execSync(`${info.mgr} --version`, { stdio: 'ignore' })
73
+ if (process.platform === 'win32' && info.mgr.toLowerCase() === 'powershell.exe') {
74
+ execSync('where powershell.exe', { stdio: 'ignore' })
75
+ } else {
76
+ execSync(`${info.mgr} --version`, { stdio: 'ignore' })
77
+ }
59
78
  } catch {
60
79
  console.log(chalk.red(` ✗ 未找到 ${info.mgr},无法自动安装 ${tool.name}`))
61
80
  return false
@@ -75,6 +94,7 @@ async function tryAutoInstall(tool) {
75
94
  console.log(chalk.yellow(` ⚠ 安装后未检测到命令,尝试直接配置...`))
76
95
  }
77
96
  if (process.platform === 'win32') {
97
+ ensureWindowsUserPathHasNpmBin()
78
98
  tool._winJustInstalled = true // 标记为 Windows 刚装的,摘要里特殊处理
79
99
  }
80
100
  return true // 安装成功就视为可配置
@@ -312,12 +332,13 @@ async function setup(options) {
312
332
  console.log(` ${chalk.gray(` ${i + 1}.`)} ${chalk.cyan.bold(s.cmd)} ${chalk.gray(s.note)}`)
313
333
  })
314
334
  } else if (r.tool.launchCmd) {
335
+ const preferredLaunchCmd = getPreferredLaunchCmd(r.tool)
315
336
  if (r.tool._winJustInstalled) {
316
- const immediateCmd = getWindowsImmediateLaunchCmd(r.tool)
317
- console.log(` ${chalk.gray('▶ 立即启动:')} ${chalk.cyan.bold(immediateCmd || r.tool.launchCmd)}`)
318
- console.log(` ${chalk.gray('▶ 新开终端后:')} ${chalk.cyan.bold(r.tool.launchCmd)}`)
337
+ const immediateCmd = getWindowsImmediateLaunchCmd({ ...r.tool, launchCmd: preferredLaunchCmd || r.tool.launchCmd })
338
+ console.log(` ${chalk.gray('▶ 立即启动:')} ${chalk.cyan.bold(immediateCmd || preferredLaunchCmd || r.tool.launchCmd)}`)
339
+ console.log(` ${chalk.gray('▶ 新开终端后:')} ${chalk.cyan.bold(preferredLaunchCmd || r.tool.launchCmd)}`)
319
340
  } else {
320
- console.log(` ${chalk.gray('▶ 启动命令:')} ${chalk.cyan.bold(r.tool.launchCmd)}`)
341
+ console.log(` ${chalk.gray('▶ 启动命令:')} ${chalk.cyan.bold(preferredLaunchCmd || r.tool.launchCmd)}`)
321
342
  }
322
343
  if (r.tool.launchNote) console.log(` ${chalk.gray(' ' + r.tool.launchNote)}`)
323
344
  } else if (r.tool.launchNote) {
@@ -338,9 +359,10 @@ async function setup(options) {
338
359
  console.log()
339
360
  }
340
361
 
341
- console.log(chalk.gray('如需切换其他工具,运行: hs setup'))
342
- console.log(chalk.gray('查看余额: hs balance'))
343
- console.log(chalk.gray('检查配置: hs doctor'))
362
+ const cliPrefix = getPreferredCliPrefix()
363
+ console.log(chalk.gray(`如需切换其他工具,运行: ${cliPrefix} setup`))
364
+ console.log(chalk.gray(`查看余额: ${cliPrefix} balance`))
365
+ console.log(chalk.gray(`检查配置: ${cliPrefix} doctor`))
344
366
  console.log()
345
367
 
346
368
  // 注册引导 banner
@@ -4,6 +4,7 @@
4
4
  const fs = require('fs')
5
5
  const path = require('path')
6
6
  const os = require('os')
7
+ const { execSync } = require('child_process')
7
8
 
8
9
  const MARKER_START = '# >>> holysheep-cli managed >>>'
9
10
  const MARKER_END = '# <<< holysheep-cli managed <<<'
@@ -70,10 +71,47 @@ function buildEnvBlock(envVars, isFish = false) {
70
71
  return '\n' + lines.join('\n') + '\n'
71
72
  }
72
73
 
74
+ function ensureWindowsUserPathHasNpmBin() {
75
+ if (process.platform !== 'win32') return []
76
+
77
+ const appData = process.env.APPDATA
78
+ if (!appData) return []
79
+
80
+ const npmBin = path.join(appData, 'npm')
81
+ let currentPath = ''
82
+ try {
83
+ currentPath = execSync(
84
+ 'powershell.exe -NoProfile -Command "[Environment]::GetEnvironmentVariable(\'Path\', \'User\')"',
85
+ { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }
86
+ ).trim()
87
+ } catch {
88
+ currentPath = process.env.PATH || ''
89
+ }
90
+
91
+ const parts = currentPath
92
+ .split(';')
93
+ .map((item) => item.trim())
94
+ .filter(Boolean)
95
+
96
+ const hasNpmBin = parts.some((item) => item.toLowerCase() === npmBin.toLowerCase())
97
+ if (hasNpmBin) return []
98
+
99
+ const nextPath = [...parts, npmBin].join(';')
100
+ try {
101
+ const escapedPath = nextPath.replace(/'/g, "''")
102
+ execSync(
103
+ `powershell.exe -NoProfile -Command "[Environment]::SetEnvironmentVariable('Path', '${escapedPath}', 'User')"`,
104
+ { stdio: 'ignore' }
105
+ )
106
+ return ['[用户 PATH] %APPDATA%\\npm']
107
+ } catch {
108
+ return []
109
+ }
110
+ }
111
+
73
112
  function writeEnvToShell(envVars) {
74
113
  // Windows: 用 setx 写入用户级环境变量(需重启终端生效)
75
114
  if (process.platform === 'win32') {
76
- const { execSync } = require('child_process')
77
115
  const written = []
78
116
  for (const [k, v] of Object.entries(envVars)) {
79
117
  try {
@@ -81,6 +119,7 @@ function writeEnvToShell(envVars) {
81
119
  written.push(`[系统环境变量] ${k}`)
82
120
  } catch {}
83
121
  }
122
+ written.push(...ensureWindowsUserPathHasNpmBin())
84
123
  if (written.length > 0) {
85
124
  const chalk = require('chalk')
86
125
  console.log(chalk.yellow('\n ⚠️ Windows 环境变量已写入,需要重启终端后生效'))
@@ -135,4 +174,4 @@ function escapeRegex(s) {
135
174
  return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
136
175
  }
137
176
 
138
- module.exports = { getShellRcFiles, writeEnvToShell, removeEnvFromShell }
177
+ module.exports = { getShellRcFiles, writeEnvToShell, removeEnvFromShell, ensureWindowsUserPathHasNpmBin }