@simonyea/holysheep-cli 2.1.1 → 2.1.2
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/commands/webui.js +65 -6
- package/src/webui/aionui-runtime.js +17 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simonyea/holysheep-cli",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.2",
|
|
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
|
"scripts": {
|
|
6
6
|
"test": "node tests/droid.test.js && node tests/workspace-store.test.js",
|
package/src/commands/webui.js
CHANGED
|
@@ -23,6 +23,18 @@ function isLegacy(opts) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
// ── Bun resolution ───────────────────────────────────────────────────────────
|
|
26
|
+
function homeBunCandidate() {
|
|
27
|
+
// bun's official installer always drops the binary at:
|
|
28
|
+
// Windows: %USERPROFILE%\.bun\bin\bun.exe
|
|
29
|
+
// Unix: $HOME/.bun/bin/bun
|
|
30
|
+
// This fallback covers the very common "user just installed bun but hasn't
|
|
31
|
+
// reopened the terminal, so PATH doesn't see it yet" case.
|
|
32
|
+
const home = process.env.HOME || process.env.USERPROFILE || require('os').homedir()
|
|
33
|
+
if (!home) return null
|
|
34
|
+
const name = process.platform === 'win32' ? 'bun.exe' : 'bun'
|
|
35
|
+
return path.join(home, '.bun', 'bin', name)
|
|
36
|
+
}
|
|
37
|
+
|
|
26
38
|
function resolveBunPath() {
|
|
27
39
|
// 1. Dev: use bundled bun if present (darwin-arm64 only in 2.0.x)
|
|
28
40
|
const bundledBun = path.join(__dirname, '..', 'webui', 'vendor', 'bun-darwin-arm64')
|
|
@@ -48,9 +60,14 @@ function resolveBunPath() {
|
|
|
48
60
|
if (fs.existsSync(p)) return p
|
|
49
61
|
}
|
|
50
62
|
} catch {}
|
|
51
|
-
// 4.
|
|
63
|
+
// 4. ~/.bun/bin fallback — bun installer's canonical location. Catches the
|
|
64
|
+
// "installed bun, didn't reopen terminal" scenario that previously forced
|
|
65
|
+
// users to fallback to legacy workspace on first `hs web`.
|
|
66
|
+
const homeBun = homeBunCandidate()
|
|
67
|
+
if (homeBun && fs.existsSync(homeBun)) return homeBun
|
|
68
|
+
// 5. No bun found. AionUi's dist-server/server.mjs uses `bun:` URL scheme
|
|
52
69
|
// imports (verified 2026-04-21: node 25 throws ERR_UNSUPPORTED_ESM_URL_SCHEME
|
|
53
|
-
// immediately), so bun is a hard runtime dep. Caller
|
|
70
|
+
// immediately), so bun is a hard runtime dep. Caller can auto-install.
|
|
54
71
|
return null
|
|
55
72
|
}
|
|
56
73
|
|
|
@@ -61,6 +78,40 @@ function describeBunInstall() {
|
|
|
61
78
|
return 'curl -fsSL https://bun.sh/install | bash'
|
|
62
79
|
}
|
|
63
80
|
|
|
81
|
+
// Auto-install bun using the official bun.sh installer. Returns the newly
|
|
82
|
+
// resolved bun path on success, or null on any failure (caller falls back).
|
|
83
|
+
// Controlled by HOLYSHEEP_WEBUI_NO_AUTOFETCH_BUN=1 (opt-out).
|
|
84
|
+
function autoInstallBun(logger) {
|
|
85
|
+
const isWindows = process.platform === 'win32'
|
|
86
|
+
// Both installers are official bun.sh first-party scripts. We log the URL
|
|
87
|
+
// explicitly so the user knows what's running.
|
|
88
|
+
const cmd = isWindows
|
|
89
|
+
? 'powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "irm https://bun.sh/install.ps1 | iex"'
|
|
90
|
+
: 'bash -c "curl -fsSL https://bun.sh/install | bash"'
|
|
91
|
+
logger(`[auto-install-bun] platform=${process.platform} command=${cmd}`)
|
|
92
|
+
logger(' Fetching official bun installer from https://bun.sh — this takes ~30s')
|
|
93
|
+
try {
|
|
94
|
+
execSync(cmd, {
|
|
95
|
+
stdio: 'inherit',
|
|
96
|
+
timeout: 180_000,
|
|
97
|
+
// Keep env minimal — don't leak weird node_options into the installer
|
|
98
|
+
env: { ...process.env, NODE_OPTIONS: '' },
|
|
99
|
+
})
|
|
100
|
+
} catch (e) {
|
|
101
|
+
logger(`[auto-install-bun] failed: ${e.message || e}`)
|
|
102
|
+
return null
|
|
103
|
+
}
|
|
104
|
+
// Re-resolve — the installer drops bun at ~/.bun/bin/bun[.exe], which our
|
|
105
|
+
// resolveBunPath() fallback (step 4) picks up without needing a shell reload.
|
|
106
|
+
const p = resolveBunPath()
|
|
107
|
+
if (!p) {
|
|
108
|
+
logger('[auto-install-bun] installer finished but bun still not found at ~/.bun/bin — aborting')
|
|
109
|
+
return null
|
|
110
|
+
}
|
|
111
|
+
logger(`[auto-install-bun] success → ${p}`)
|
|
112
|
+
return p
|
|
113
|
+
}
|
|
114
|
+
|
|
64
115
|
// ── Runtime resolution ───────────────────────────────────────────────────────
|
|
65
116
|
// Priority:
|
|
66
117
|
// 1. Local dev checkout at ../aionui-fork/dist-server (developer building locally)
|
|
@@ -246,6 +297,7 @@ async function startAionUiMode(opts) {
|
|
|
246
297
|
const downloaded = await downloadAionUiRuntime((m) => console.log(chalk.gray(` ${m}`)))
|
|
247
298
|
if (downloaded) {
|
|
248
299
|
runtime = { dir: downloaded.dir, source: downloaded.source }
|
|
300
|
+
console.log(chalk.gray(' AionUi runtime installed. Next: ensure bun is available to launch it.'))
|
|
249
301
|
}
|
|
250
302
|
}
|
|
251
303
|
if (!runtime) {
|
|
@@ -272,13 +324,20 @@ async function startAionUiMode(opts) {
|
|
|
272
324
|
return startLegacyMode(opts)
|
|
273
325
|
}
|
|
274
326
|
|
|
275
|
-
// 2. Resolve bun
|
|
276
|
-
|
|
327
|
+
// 2. Resolve bun — and auto-install if missing (opt-out via env)
|
|
328
|
+
let bunPath = resolveBunPath()
|
|
329
|
+
const autoBunDisabled = process.env.HOLYSHEEP_WEBUI_NO_AUTOFETCH_BUN === '1'
|
|
330
|
+
if (!bunPath && !autoBunDisabled) {
|
|
331
|
+
console.log(chalk.cyan('▶ bun runtime not installed — installing automatically (one-time)'))
|
|
332
|
+
console.log(chalk.gray(' (disable with HOLYSHEEP_WEBUI_NO_AUTOFETCH_BUN=1; takes ~30s; source: bun.sh official installer)'))
|
|
333
|
+
bunPath = autoInstallBun((m) => console.log(chalk.gray(` ${m}`)))
|
|
334
|
+
}
|
|
277
335
|
if (!bunPath) {
|
|
278
336
|
console.log(chalk.red('✗ bun is required to run the AionUi server'))
|
|
279
337
|
console.log(chalk.gray(' (AionUi uses bun: URL-scheme imports that Node cannot load directly)'))
|
|
338
|
+
console.log(chalk.gray(' AionUi runtime itself is ready — bun is the last missing piece.'))
|
|
280
339
|
console.log()
|
|
281
|
-
console.log(chalk.yellow(' Install bun:'))
|
|
340
|
+
console.log(chalk.yellow(' Install bun manually:'))
|
|
282
341
|
console.log(chalk.cyan(` ${describeBunInstall()}`))
|
|
283
342
|
console.log(chalk.gray(' After install, close and reopen your terminal, then retry `hs web`.'))
|
|
284
343
|
console.log()
|
|
@@ -286,7 +345,7 @@ async function startAionUiMode(opts) {
|
|
|
286
345
|
console.log(chalk.red(' --aionui flag requires bun. Aborting.'))
|
|
287
346
|
process.exit(1)
|
|
288
347
|
}
|
|
289
|
-
console.log(chalk.yellow('
|
|
348
|
+
console.log(chalk.yellow(' AionUi runtime ready but bun auto-install failed — falling back to legacy HolySheep workspace.'))
|
|
290
349
|
console.log()
|
|
291
350
|
return startLegacyMode(opts)
|
|
292
351
|
}
|
|
@@ -12,16 +12,28 @@ function resolveBunPath() {
|
|
|
12
12
|
return bundledBun
|
|
13
13
|
}
|
|
14
14
|
if (process.env.BUN && fs.existsSync(process.env.BUN)) return process.env.BUN
|
|
15
|
+
const isWindows = process.platform === 'win32'
|
|
15
16
|
try {
|
|
16
|
-
const
|
|
17
|
+
const cmd = isWindows ? 'where.exe bun' : 'which bun'
|
|
18
|
+
const raw = execSync(cmd, {
|
|
17
19
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
18
20
|
encoding: 'utf8',
|
|
19
21
|
timeout: 2000,
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
})
|
|
23
|
+
const candidates = String(raw).split(/\r?\n/).map((s) => s.trim()).filter(Boolean)
|
|
24
|
+
for (const p of candidates) {
|
|
25
|
+
if (fs.existsSync(p)) return p
|
|
26
|
+
}
|
|
27
|
+
} catch {}
|
|
28
|
+
// ~/.bun/bin fallback — canonical location of bun's official installer.
|
|
29
|
+
// Catches "installed bun but didn't reopen terminal" without needing PATH refresh.
|
|
30
|
+
const home = process.env.HOME || process.env.USERPROFILE || os.homedir()
|
|
31
|
+
if (home) {
|
|
32
|
+
const name = isWindows ? 'bun.exe' : 'bun'
|
|
33
|
+
const homeBun = path.join(home, '.bun', 'bin', name)
|
|
34
|
+
if (fs.existsSync(homeBun)) return homeBun
|
|
24
35
|
}
|
|
36
|
+
return null
|
|
25
37
|
}
|
|
26
38
|
|
|
27
39
|
function getRuntimeCandidates() {
|