@simonyea/holysheep-cli 2.1.31 → 2.1.33
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": "2.1.
|
|
3
|
+
"version": "2.1.33",
|
|
4
4
|
"description": "Claude Code/Cursor/Cline API relay for China \u2014 \u00a51=$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 && node tests/runtime-stale-upgrade.test.js && node tests/hermes.test.js && node tests/preflight.test.js",
|
package/src/commands/webui.js
CHANGED
|
@@ -144,7 +144,7 @@ function probeAionUiRuntimeLabel() {
|
|
|
144
144
|
// is about to return user-cache).
|
|
145
145
|
if (process.env.HOLYSHEEP_DEV === '1') {
|
|
146
146
|
const forkDir = path.resolve(__dirname, '..', '..', 'aionui-fork')
|
|
147
|
-
if (isValidRuntimeDir(forkDir)) return '
|
|
147
|
+
if (isValidRuntimeDir(forkDir)) return 'dev-checkout'
|
|
148
148
|
}
|
|
149
149
|
if (isValidRuntimeDir(VENDOR_DIR)) return 'vendor'
|
|
150
150
|
if (isValidRuntimeDir(USER_CACHE_DIR)) {
|
|
@@ -200,7 +200,7 @@ async function fetchCsrfToken(port) {
|
|
|
200
200
|
|
|
201
201
|
async function loginWithApiKey(port, apiKey) {
|
|
202
202
|
const csrf = await fetchCsrfToken(port)
|
|
203
|
-
if (!csrf) throw new Error('Failed to acquire CSRF token from
|
|
203
|
+
if (!csrf) throw new Error('Failed to acquire CSRF token from HolySheep runtime')
|
|
204
204
|
const body = JSON.stringify({ apiKey, csrfToken: decodeURIComponent(csrf.csrfToken) })
|
|
205
205
|
const r = await httpRequest(
|
|
206
206
|
{
|
|
@@ -237,7 +237,7 @@ function spawnAionUiServer({ bunPath, runtimeDir, port }) {
|
|
|
237
237
|
return new Promise((resolve, reject) => {
|
|
238
238
|
const entry = path.join(runtimeDir, 'dist-server', 'server.mjs')
|
|
239
239
|
if (!fs.existsSync(entry)) {
|
|
240
|
-
return reject(new Error(`
|
|
240
|
+
return reject(new Error(`HolySheep runtime entry not found: ${entry}`))
|
|
241
241
|
}
|
|
242
242
|
|
|
243
243
|
// AionUi reads PORT / HOST / JWT_SECRET from env. We set a stable JWT secret
|
|
@@ -269,7 +269,7 @@ function spawnAionUiServer({ bunPath, runtimeDir, port }) {
|
|
|
269
269
|
}
|
|
270
270
|
if (debug) {
|
|
271
271
|
const target = stream === 'err' ? process.stderr : process.stdout
|
|
272
|
-
target.write(chalk.gray(`[
|
|
272
|
+
target.write(chalk.gray(`[holysheep] ${s}`))
|
|
273
273
|
}
|
|
274
274
|
}
|
|
275
275
|
child.stdout.on('data', (d) => appendLog('out', d))
|
|
@@ -292,12 +292,12 @@ function spawnAionUiServer({ bunPath, runtimeDir, port }) {
|
|
|
292
292
|
const tail = logTail.trim()
|
|
293
293
|
let msg = reason
|
|
294
294
|
if (tail) {
|
|
295
|
-
msg += `\n\n --- last
|
|
295
|
+
msg += `\n\n --- last HolySheep runtime output (stderr+stdout tail) ---\n${tail
|
|
296
296
|
.split(/\r?\n/)
|
|
297
297
|
.map((line) => ` ${line}`)
|
|
298
298
|
.join('\n')}\n ------------------------------------------------`
|
|
299
299
|
} else {
|
|
300
|
-
msg += '\n (no output captured from
|
|
300
|
+
msg += '\n (no output captured from HolySheep runtime — check ' +
|
|
301
301
|
(process.platform === 'win32' ? 'Windows Defender / antivirus quarantining bun.exe' : 'bun or runtime corruption') +
|
|
302
302
|
')'
|
|
303
303
|
}
|
|
@@ -312,11 +312,11 @@ function spawnAionUiServer({ bunPath, runtimeDir, port }) {
|
|
|
312
312
|
const hint = process.platform === 'win32'
|
|
313
313
|
? ' (Windows first-launch can take up to 60s while Defender scans bun.exe + server.mjs)'
|
|
314
314
|
: ''
|
|
315
|
-
console.log(chalk.gray(` still starting
|
|
315
|
+
console.log(chalk.gray(` still starting HolySheep runtime — waited ${waited}s…${hint}`))
|
|
316
316
|
}, 10_000)
|
|
317
317
|
|
|
318
318
|
const timer = setTimeout(() => {
|
|
319
|
-
fail(`
|
|
319
|
+
fail(`HolySheep runtime failed to start within ${Math.round(AIONUI_STARTUP_TIMEOUT_MS / 1000)}s`)
|
|
320
320
|
}, AIONUI_STARTUP_TIMEOUT_MS)
|
|
321
321
|
|
|
322
322
|
// ── Readiness poll ─────────────────────────────────────────────────────
|
|
@@ -346,14 +346,14 @@ function spawnAionUiServer({ bunPath, runtimeDir, port }) {
|
|
|
346
346
|
// ── Early exit ─────────────────────────────────────────────────────────
|
|
347
347
|
child.on('exit', (code, signal) => {
|
|
348
348
|
const reason = signal
|
|
349
|
-
? `
|
|
350
|
-
: `
|
|
349
|
+
? `HolySheep runtime exited with signal ${signal} before becoming ready`
|
|
350
|
+
: `HolySheep runtime exited with code ${code} before becoming ready`
|
|
351
351
|
clearTimeout(timer)
|
|
352
352
|
fail(reason)
|
|
353
353
|
})
|
|
354
354
|
child.on('error', (err) => {
|
|
355
355
|
clearTimeout(timer)
|
|
356
|
-
fail(`
|
|
356
|
+
fail(`HolySheep runtime spawn error: ${err.message || err}`)
|
|
357
357
|
})
|
|
358
358
|
})
|
|
359
359
|
}
|
|
@@ -382,10 +382,10 @@ async function startAionUiMode(opts) {
|
|
|
382
382
|
// on HOLYSHEEP_AIONUI_SKIP_UPGRADE.
|
|
383
383
|
const probe = probeAionUiRuntimeLabel()
|
|
384
384
|
if (probe === 'none') {
|
|
385
|
-
console.log(chalk.cyan('▶
|
|
385
|
+
console.log(chalk.cyan('▶ HolySheep runtime not installed — downloading automatically (~21 MB, one-time)'))
|
|
386
386
|
console.log(chalk.gray(' (disable with HOLYSHEEP_WEBUI_NO_AUTOFETCH=1; override URL with HOLYSHEEP_AIONUI_RUNTIME_URL)'))
|
|
387
387
|
} else if (probe === 'installed-stale' && process.env.HOLYSHEEP_AIONUI_SKIP_UPGRADE !== '1') {
|
|
388
|
-
console.log(chalk.cyan('▶
|
|
388
|
+
console.log(chalk.cyan('▶ HolySheep runtime is out of date — upgrading in place (atomic)'))
|
|
389
389
|
console.log(chalk.gray(' (set HOLYSHEEP_AIONUI_SKIP_UPGRADE=1 to pin current cache; HOLYSHEEP_AIONUI_PIN_VERSION=<v> to lock a version)'))
|
|
390
390
|
}
|
|
391
391
|
}
|
|
@@ -394,18 +394,18 @@ async function startAionUiMode(opts) {
|
|
|
394
394
|
logger: (m) => console.log(chalk.gray(` ${m}`)),
|
|
395
395
|
})
|
|
396
396
|
if (runtime && runtime.source === 'download-upgrade') {
|
|
397
|
-
console.log(chalk.green(` ✓
|
|
397
|
+
console.log(chalk.green(` ✓ HolySheep runtime upgraded to ${runtime.version}`))
|
|
398
398
|
} else if (runtime && runtime.source === 'user-cache-stale') {
|
|
399
399
|
console.log(chalk.yellow(
|
|
400
|
-
` ⚠ Running stale
|
|
400
|
+
` ⚠ Running stale HolySheep runtime (${runtime.version}, expected newer). ` +
|
|
401
401
|
`Run: rm -rf ~/.holysheep/aionui-runtime && hs web to force rebuild.`
|
|
402
402
|
))
|
|
403
403
|
} else if (runtime && runtime.source === 'download') {
|
|
404
|
-
console.log(chalk.gray('
|
|
404
|
+
console.log(chalk.gray(' HolySheep runtime installed. Next: ensure bun is available to launch it.'))
|
|
405
405
|
}
|
|
406
406
|
if (!runtime) {
|
|
407
407
|
const home = process.env.HOME || process.env.USERPROFILE || '~'
|
|
408
|
-
console.log(chalk.red('✗
|
|
408
|
+
console.log(chalk.red('✗ HolySheep runtime not found and auto-download did not succeed'))
|
|
409
409
|
console.log()
|
|
410
410
|
console.log(chalk.gray(' Expected at one of:'))
|
|
411
411
|
console.log(chalk.gray(` • ${path.resolve(__dirname, '..', '..', 'aionui-fork', 'dist-server')} (dev checkout)`))
|
|
@@ -451,9 +451,9 @@ async function startAionUiMode(opts) {
|
|
|
451
451
|
}
|
|
452
452
|
}
|
|
453
453
|
if (!bunPath) {
|
|
454
|
-
console.log(chalk.red('✗ bun is required to run the
|
|
455
|
-
console.log(chalk.gray(' (
|
|
456
|
-
console.log(chalk.gray('
|
|
454
|
+
console.log(chalk.red('✗ bun is required to run the HolySheep runtime'))
|
|
455
|
+
console.log(chalk.gray(' (HolySheep runtime uses bun: URL-scheme imports that Node cannot load directly)'))
|
|
456
|
+
console.log(chalk.gray(' HolySheep runtime itself is ready — bun is the last missing piece.'))
|
|
457
457
|
console.log()
|
|
458
458
|
console.log(chalk.yellow(' Install bun manually:'))
|
|
459
459
|
console.log(chalk.cyan(` ${describeBunInstall()}`))
|
|
@@ -463,12 +463,12 @@ async function startAionUiMode(opts) {
|
|
|
463
463
|
console.log(chalk.red(' --aionui flag requires bun. Aborting.'))
|
|
464
464
|
process.exit(1)
|
|
465
465
|
}
|
|
466
|
-
console.log(chalk.yellow('
|
|
466
|
+
console.log(chalk.yellow(' HolySheep runtime ready but bun auto-install failed — falling back to legacy HolySheep workspace.'))
|
|
467
467
|
console.log()
|
|
468
468
|
return startLegacyMode(opts)
|
|
469
469
|
}
|
|
470
470
|
|
|
471
|
-
console.log(chalk.cyan(`▶ Starting
|
|
471
|
+
console.log(chalk.cyan(`▶ Starting HolySheep runtime (source: ${runtime.source})`))
|
|
472
472
|
|
|
473
473
|
// 3. Spawn AionUi on an internal loopback port and wrap it with
|
|
474
474
|
// `aionui-wrapper.js` which listens on the user-visible port. This is the
|
|
@@ -498,12 +498,12 @@ async function startAionUiMode(opts) {
|
|
|
498
498
|
aionuiProc = wrapper.aionui
|
|
499
499
|
} catch (e) {
|
|
500
500
|
const [firstLine, ...rest] = String(e.message).split(/\r?\n/)
|
|
501
|
-
console.log(chalk.red(`✗
|
|
501
|
+
console.log(chalk.red(`✗ HolySheep runtime failed to start: ${firstLine}`))
|
|
502
502
|
for (const line of rest) {
|
|
503
503
|
if (line.trim()) console.log(chalk.gray(line))
|
|
504
504
|
}
|
|
505
505
|
console.log()
|
|
506
|
-
console.log(chalk.gray(' Tip: run again with HS_WEB_DEBUG=1 to stream
|
|
506
|
+
console.log(chalk.gray(' Tip: run again with HS_WEB_DEBUG=1 to stream HolySheep runtime logs live.'))
|
|
507
507
|
console.log(chalk.gray(' If this is a first launch on Windows, try once more — Defender'))
|
|
508
508
|
console.log(chalk.gray(' will cache bun.exe + server.mjs after the first scan.'))
|
|
509
509
|
if (opts.aionui) process.exit(1)
|
|
@@ -544,7 +544,7 @@ async function startAionUiMode(opts) {
|
|
|
544
544
|
}
|
|
545
545
|
|
|
546
546
|
console.log(chalk.green(`✓ WebUI 已启动: ${chalk.cyan.bold(launchUrl)}`))
|
|
547
|
-
console.log(chalk.gray(' Mode:
|
|
547
|
+
console.log(chalk.gray(' Mode: HolySheep WebUI (HolySheep 登录)'))
|
|
548
548
|
console.log(chalk.gray(' Press Ctrl+C to stop'))
|
|
549
549
|
console.log()
|
|
550
550
|
|
|
@@ -610,7 +610,7 @@ async function webui(opts) {
|
|
|
610
610
|
// next call to resolveAionUiRuntime). `none` means first-ever launch.
|
|
611
611
|
const bunFound = resolveBunPath() ? 'found' : 'missing'
|
|
612
612
|
const rtLabel = probeAionUiRuntimeLabel()
|
|
613
|
-
console.log(chalk.gray(`[mode=
|
|
613
|
+
console.log(chalk.gray(`[mode=holysheep platform=${process.platform} bun=${bunFound} runtime=${rtLabel}]`))
|
|
614
614
|
console.log()
|
|
615
615
|
return await startAionUiMode(opts)
|
|
616
616
|
} catch (err) {
|
package/src/index.js
CHANGED
|
@@ -174,11 +174,11 @@ program
|
|
|
174
174
|
program
|
|
175
175
|
.command('web')
|
|
176
176
|
.alias('webui')
|
|
177
|
-
.description('启动 WebUI 本地管理面板 (
|
|
177
|
+
.description('启动 HolySheep WebUI 本地管理面板 (默认 HolySheep API Key 登录)')
|
|
178
178
|
.option('-p, --port <port>', '指定端口', '9876')
|
|
179
179
|
.option('--no-open', '不自动打开浏览器')
|
|
180
|
-
.option('--aionui', '
|
|
181
|
-
.option('--setup-runtime', '
|
|
180
|
+
.option('--aionui', '强制使用 HolySheep runtime (兼容 flag 名保留;需本地已安装 runtime)')
|
|
181
|
+
.option('--setup-runtime', '允许首次运行时自动下载 HolySheep runtime')
|
|
182
182
|
.action(async (opts) => {
|
|
183
183
|
printBanner()
|
|
184
184
|
await require('./commands/webui')(opts)
|
|
@@ -60,10 +60,10 @@ const VENDOR_DIR = path.join(__dirname, 'vendor', 'aionui')
|
|
|
60
60
|
// new CLI release, the next `hs web` invocation on user machines will detect
|
|
61
61
|
// the version drift and upgrade the cache in place.
|
|
62
62
|
const DEFAULT_RUNTIME_URL =
|
|
63
|
-
'https://mail.holysheep.ai/app/cli/aionui-runtime-v1.9.18-holysheep-
|
|
63
|
+
'https://mail.holysheep.ai/app/cli/aionui-runtime-v1.9.18-holysheep-hs23.tar.gz'
|
|
64
64
|
const DEFAULT_RUNTIME_SHA256 =
|
|
65
|
-
'
|
|
66
|
-
const DEFAULT_RUNTIME_VERSION = '1.9.18-holysheep-
|
|
65
|
+
'96bcf47a06c4cdda206ed6d258edd9d347a587ef300c053e7679df469ccf8856'
|
|
66
|
+
const DEFAULT_RUNTIME_VERSION = '1.9.18-holysheep-hs23'
|
|
67
67
|
|
|
68
68
|
function isValidRuntimeDir(dir) {
|
|
69
69
|
if (!dir) return false
|
|
@@ -206,7 +206,7 @@ async function resolveRuntime({ allowDownload = false, logger = () => {} } = {})
|
|
|
206
206
|
if (process.env.HOLYSHEEP_DEV === '1') {
|
|
207
207
|
const forkDir = path.resolve(__dirname, '..', '..', 'aionui-fork')
|
|
208
208
|
if (isValidRuntimeDir(forkDir)) {
|
|
209
|
-
return { dir: forkDir, version: readVersion(forkDir), source: '
|
|
209
|
+
return { dir: forkDir, version: readVersion(forkDir), source: 'dev-checkout' }
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
212
|
|
|
@@ -227,9 +227,9 @@ async function resolveRuntime({ allowDownload = false, logger = () => {} } = {})
|
|
|
227
227
|
source: 'download',
|
|
228
228
|
}
|
|
229
229
|
}
|
|
230
|
-
logger('
|
|
230
|
+
logger('HolySheep runtime downloaded but directory structure invalid')
|
|
231
231
|
} catch (e) {
|
|
232
|
-
logger(`
|
|
232
|
+
logger(`HolySheep runtime download failed: ${e.message}`)
|
|
233
233
|
}
|
|
234
234
|
}
|
|
235
235
|
|
|
@@ -319,7 +319,7 @@ function rmrf(p) {
|
|
|
319
319
|
function downloadAndExtract(url, destDir, expectedSha, logger) {
|
|
320
320
|
return new Promise((resolve, reject) => {
|
|
321
321
|
const tmpFile = path.join(os.tmpdir(), `aionui-runtime-${Date.now()}.tar.gz`)
|
|
322
|
-
logger(`Downloading
|
|
322
|
+
logger(`Downloading HolySheep runtime from ${url}`)
|
|
323
323
|
|
|
324
324
|
const client = url.startsWith('https:') ? https : http
|
|
325
325
|
const file = fs.createWriteStream(tmpFile)
|
|
@@ -394,7 +394,7 @@ function downloadAndExtract(url, destDir, expectedSha, logger) {
|
|
|
394
394
|
|
|
395
395
|
function describeInstallGuidance() {
|
|
396
396
|
return [
|
|
397
|
-
'
|
|
397
|
+
'HolySheep runtime not installed. Fastest way to get started:',
|
|
398
398
|
'',
|
|
399
399
|
' One command (downloads the prebuilt 21 MB runtime):',
|
|
400
400
|
' hs web --setup-runtime',
|
|
@@ -85,7 +85,7 @@ function waitForReady(port, timeoutMs = 15000) {
|
|
|
85
85
|
|
|
86
86
|
const retry = () => {
|
|
87
87
|
if (Date.now() - startedAt >= timeoutMs) {
|
|
88
|
-
reject(new Error('
|
|
88
|
+
reject(new Error('HolySheep runtime did not become ready in time'))
|
|
89
89
|
return
|
|
90
90
|
}
|
|
91
91
|
setTimeout(tick, 500)
|
|
@@ -99,13 +99,13 @@ async function startAionUiRuntime(port) {
|
|
|
99
99
|
const runtimeDir = resolveAionUiRuntimeDir()
|
|
100
100
|
if (!runtimeDir) {
|
|
101
101
|
throw new Error(
|
|
102
|
-
'
|
|
102
|
+
'HolySheep runtime not bundled in this build — set HOLYSHEEP_WEBUI_AIONUI=1 with a local vendor/ directory to enable'
|
|
103
103
|
)
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
const bunPath = resolveBunPath()
|
|
107
107
|
if (!bunPath) {
|
|
108
|
-
throw new Error('bun is required to start the
|
|
108
|
+
throw new Error('bun is required to start the HolySheep runtime (install: https://bun.sh)')
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
const child = spawn(bunPath, ['dist-server/server.mjs'], {
|
|
@@ -73,7 +73,7 @@ const AIONUI_COOKIE_TTL_MS = 10 * 60 * 1000
|
|
|
73
73
|
|
|
74
74
|
function log(msg) {
|
|
75
75
|
// eslint-disable-next-line no-console
|
|
76
|
-
console.log(`[
|
|
76
|
+
console.log(`[holysheep-web] ${msg}`)
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
function randomToken(bytes = 24) {
|
|
@@ -235,17 +235,17 @@ function aionuiInternalLoginWithApiKey({ internalPort, apiKey }) {
|
|
|
235
235
|
res.on('data', (c) => { buf += c.toString() })
|
|
236
236
|
res.on('end', () => {
|
|
237
237
|
if (res.statusCode !== 200) {
|
|
238
|
-
return reject(new Error(`
|
|
238
|
+
return reject(new Error(`HolySheep runtime /login returned ${res.statusCode}: ${buf.slice(0, 200)}`))
|
|
239
239
|
}
|
|
240
240
|
const setCookie = res.headers['set-cookie']
|
|
241
241
|
if (!setCookie || setCookie.length === 0) {
|
|
242
|
-
return reject(new Error('
|
|
242
|
+
return reject(new Error('HolySheep runtime /login succeeded but no Set-Cookie header returned'))
|
|
243
243
|
}
|
|
244
244
|
resolve(setCookie)
|
|
245
245
|
})
|
|
246
246
|
})
|
|
247
247
|
req.on('error', reject)
|
|
248
|
-
req.setTimeout(15_000, () => { req.destroy(new Error('
|
|
248
|
+
req.setTimeout(15_000, () => { req.destroy(new Error('HolySheep runtime /login timed out')) })
|
|
249
249
|
req.write(body)
|
|
250
250
|
req.end()
|
|
251
251
|
})
|
|
@@ -308,7 +308,7 @@ async function handleBootstrap(req, res, ctx) {
|
|
|
308
308
|
})
|
|
309
309
|
res.end()
|
|
310
310
|
} catch (e) {
|
|
311
|
-
sendJson(res, 502, { success: false, message: `
|
|
311
|
+
sendJson(res, 502, { success: false, message: `bridge login failed: ${e.message}` })
|
|
312
312
|
}
|
|
313
313
|
}
|
|
314
314
|
|
|
@@ -317,7 +317,7 @@ async function handleHolySheepStatus(req, res) {
|
|
|
317
317
|
sendJson(res, 200, {
|
|
318
318
|
loggedIn: !!apiKey,
|
|
319
319
|
apiKeyMasked: apiKey ? `${apiKey.slice(0, 6)}...${apiKey.slice(-4)}` : null,
|
|
320
|
-
mode: '
|
|
320
|
+
mode: 'holysheep-webui',
|
|
321
321
|
version: require('../../package.json').version,
|
|
322
322
|
})
|
|
323
323
|
}
|
|
@@ -380,8 +380,29 @@ function proxyHttp(req, res, internalPort) {
|
|
|
380
380
|
req.pipe(upstream)
|
|
381
381
|
}
|
|
382
382
|
|
|
383
|
-
// Client disconnect → kill upstream
|
|
384
|
-
|
|
383
|
+
// Client disconnect → kill upstream.
|
|
384
|
+
//
|
|
385
|
+
// [HolySheep fork v2.1.32 / hs23] Root-cause fix for "Network error" on
|
|
386
|
+
// /login: Node's IncomingMessage (`req`) emits 'close' as soon as its
|
|
387
|
+
// Readable side is fully consumed — NOT when the TCP socket disconnects.
|
|
388
|
+
// For POST requests, req.pipe(upstream) drains the body and the very next
|
|
389
|
+
// tick `req` fires 'close', which here destroyed the upstream *before*
|
|
390
|
+
// Express on 9877 had a chance to respond → ECONNRESET → wrapper 502.
|
|
391
|
+
//
|
|
392
|
+
// The correct signal for "client disconnected" is `res.on('close')` +
|
|
393
|
+
// `res.writableFinished === false`. That only fires if the downstream
|
|
394
|
+
// client aborts before we've finished writing the response.
|
|
395
|
+
res.on('close', () => {
|
|
396
|
+
if (res.writableFinished) return
|
|
397
|
+
if (!upstream.destroyed) upstream.destroy()
|
|
398
|
+
})
|
|
399
|
+
upstream.on('close', () => {
|
|
400
|
+
// If upstream closes before we finished writing to `res` (e.g. upstream
|
|
401
|
+
// crash), make sure we don't leave the client hanging.
|
|
402
|
+
if (!res.writableFinished && !res.headersSent) {
|
|
403
|
+
try { res.writeHead(502, { 'Content-Type': 'text/plain' }); res.end('upstream closed') } catch {}
|
|
404
|
+
}
|
|
405
|
+
})
|
|
385
406
|
}
|
|
386
407
|
|
|
387
408
|
// ── WebSocket proxy (upgrade event) ──────────────────────────────────────────
|
|
@@ -586,7 +607,7 @@ async function startWrapper({ port, runtimeDir, runtimeVersion, runtimeSource, b
|
|
|
586
607
|
// Detect if the vendored AionUi build natively speaks HolySheep auth.
|
|
587
608
|
// Vendored v1.9.17 does; upstream AionUi releases do not (use username/password).
|
|
588
609
|
const hsNative = detectHolySheepAionUi(runtimeDir)
|
|
589
|
-
log(
|
|
610
|
+
log(`/login mode: ${hsNative ? 'holysheep-native (apiKey)' : 'legacy (username/password bridge)'}`)
|
|
590
611
|
|
|
591
612
|
// If the build is legacy username/password, eager pre-flight the bridge cred
|
|
592
613
|
// perms so a misconfigured file fails at boot rather than during a request.
|
|
@@ -595,7 +616,7 @@ async function startWrapper({ port, runtimeDir, runtimeVersion, runtimeSource, b
|
|
|
595
616
|
}
|
|
596
617
|
|
|
597
618
|
const internalPort = await pickInternalPort()
|
|
598
|
-
log(`internal
|
|
619
|
+
log(`internal runtime port: ${internalPort}`)
|
|
599
620
|
|
|
600
621
|
// Spawn AionUi, bound 127.0.0.1 only (ALLOW_REMOTE never set).
|
|
601
622
|
// stdio is piped (not inherited) so that:
|
|
@@ -655,7 +676,7 @@ async function startWrapper({ port, runtimeDir, runtimeVersion, runtimeSource, b
|
|
|
655
676
|
aionui.stdout.on('data', (d) => appendLog('out', d))
|
|
656
677
|
aionui.stderr.on('data', (d) => appendLog('err', d))
|
|
657
678
|
aionui.on('exit', (code) => {
|
|
658
|
-
log(`
|
|
679
|
+
log(`runtime upstream exited (code=${code})`)
|
|
659
680
|
process.exit(code || 1)
|
|
660
681
|
})
|
|
661
682
|
|
|
@@ -671,7 +692,7 @@ async function startWrapper({ port, runtimeDir, runtimeVersion, runtimeSource, b
|
|
|
671
692
|
: e.message
|
|
672
693
|
throw new Error(msg)
|
|
673
694
|
}
|
|
674
|
-
log(`
|
|
695
|
+
log(`runtime ready (version=${runtimeVersion}, source=${runtimeSource})`)
|
|
675
696
|
|
|
676
697
|
const ctx = { internalPort, runtimeDir, runtimeVersion, runtimeSource, bunPath }
|
|
677
698
|
const server = http.createServer(buildRouter(ctx))
|