mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-08-06 13:43:50 +08:00
chore(release): prepare v0.19.0
This commit is contained in:
@@ -3,6 +3,7 @@ import assert from 'node:assert/strict'
|
||||
import { readFileSync } from 'node:fs'
|
||||
|
||||
const css = readFileSync(new URL('../src/engines/hermes/style/hermes.css', import.meta.url), 'utf8')
|
||||
const dashboard = readFileSync(new URL('../src/engines/hermes/pages/dashboard.js', import.meta.url), 'utf8')
|
||||
|
||||
function cssBlock(selector) {
|
||||
const escaped = selector.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
||||
@@ -17,11 +18,9 @@ test('Hermes dashboard 图标按钮必须满足移动端触控尺寸', () => {
|
||||
assert.match(block, /min-height:\s*44px/, '图标按钮需要显式保留 44px 最小高度')
|
||||
})
|
||||
|
||||
test('Hermes dashboard 原生面板入口必须满足移动端触控尺寸', () => {
|
||||
const block = cssBlock('[data-engine="hermes"] button.hm-native-dashboard-link')
|
||||
assert.match(block, /min-width:\s*44px/, '原生 Dashboard 入口宽度必须至少 44px')
|
||||
assert.match(block, /min-height:\s*44px/, '原生 Dashboard 入口高度必须至少 44px')
|
||||
assert.match(block, /inline-flex/, '原生 Dashboard 入口需要扩展可点区域并居中内容')
|
||||
test('Hermes dashboard 不展示未默认运行的 9119 外部入口', () => {
|
||||
assert.doesNotMatch(dashboard, /<div class="hm-native-dashboard-hint">/)
|
||||
assert.doesNotMatch(dashboard, /<button class="hm-native-dashboard-link/)
|
||||
})
|
||||
|
||||
test('Hermes dashboard pill 选择器必须满足移动端触控尺寸', () => {
|
||||
|
||||
@@ -119,3 +119,93 @@ test('Tauri Hermes installer pins a uv release with the archive collision fix',
|
||||
assert.match(source, /is_uv_wheel_cache_error/)
|
||||
assert.match(source, /"--no-cache"/)
|
||||
})
|
||||
|
||||
test('Hermes runtime environment pins the same home used by ClawPanel', async () => {
|
||||
const { buildHermesRuntimeEnv } = await import('../scripts/dev-api.js')
|
||||
|
||||
assert.equal(typeof buildHermesRuntimeEnv, 'function')
|
||||
const env = buildHermesRuntimeEnv({ PATH: 'old' }, 'C:\\Users\\tester\\.hermes', 'managed-path')
|
||||
assert.equal(env.PATH, 'managed-path')
|
||||
assert.equal(env.HERMES_HOME, 'C:\\Users\\tester\\.hermes')
|
||||
})
|
||||
|
||||
test('Hermes API Server runtime env creates a strong key without dropping provider settings', async () => {
|
||||
const { ensureHermesApiServerRuntimeEnvAt } = await import('../scripts/dev-api.js')
|
||||
const home = fs.mkdtempSync(path.join(process.env.TEMP || process.cwd(), 'clawpanel-hermes-env-'))
|
||||
fs.writeFileSync(path.join(home, '.env'), 'CUSTOM_API_KEY=provider-secret\nOPENAI_BASE_URL=https://example.test/v1\n')
|
||||
|
||||
const result = ensureHermesApiServerRuntimeEnvAt(home, () => 'a'.repeat(64))
|
||||
const saved = fs.readFileSync(path.join(home, '.env'), 'utf8')
|
||||
|
||||
assert.equal(result.changed, true)
|
||||
assert.equal(result.apiServerKey, 'a'.repeat(64))
|
||||
assert.match(saved, /^API_SERVER_ENABLED=true$/m)
|
||||
assert.match(saved, new RegExp(`^API_SERVER_KEY=${'a'.repeat(64)}$`, 'm'))
|
||||
assert.match(saved, /^CUSTOM_API_KEY=provider-secret$/m)
|
||||
assert.match(saved, /^OPENAI_BASE_URL=https:\/\/example\.test\/v1$/m)
|
||||
|
||||
const second = ensureHermesApiServerRuntimeEnvAt(home, () => 'b'.repeat(64))
|
||||
assert.equal(second.changed, false)
|
||||
assert.equal(fs.readFileSync(path.join(home, '.env'), 'utf8'), saved)
|
||||
})
|
||||
|
||||
test('Hermes API Server runtime env preserves a usable existing key and replaces the legacy weak key', async () => {
|
||||
const { ensureHermesApiServerRuntimeEnvAt } = await import('../scripts/dev-api.js')
|
||||
const strongHome = fs.mkdtempSync(path.join(process.env.TEMP || process.cwd(), 'clawpanel-hermes-strong-'))
|
||||
const existing = 'existing-strong-key-1234567890'
|
||||
fs.writeFileSync(path.join(strongHome, '.env'), `API_SERVER_KEY=${existing}\n`)
|
||||
const preserved = ensureHermesApiServerRuntimeEnvAt(strongHome, () => 'b'.repeat(64))
|
||||
assert.equal(preserved.apiServerKey, existing)
|
||||
|
||||
const weakHome = fs.mkdtempSync(path.join(process.env.TEMP || process.cwd(), 'clawpanel-hermes-weak-'))
|
||||
fs.writeFileSync(path.join(weakHome, '.env'), 'API_SERVER_KEY=clawpanel-local\n')
|
||||
const replaced = ensureHermesApiServerRuntimeEnvAt(weakHome, () => 'c'.repeat(64))
|
||||
assert.equal(replaced.apiServerKey, 'c'.repeat(64))
|
||||
assert.doesNotMatch(fs.readFileSync(path.join(weakHome, '.env'), 'utf8'), /clawpanel-local/)
|
||||
})
|
||||
|
||||
test('Tauri Hermes runtime pins HERMES_HOME and repairs API Server auth before start', () => {
|
||||
const source = fs.readFileSync(path.join(root, 'src-tauri/src/commands/hermes.rs'), 'utf8')
|
||||
|
||||
assert.match(source, /cmd\.env\("HERMES_HOME", hermes_home\(\)\);/)
|
||||
assert.match(source, /ensure_hermes_api_server_runtime_env\(\)\?/)
|
||||
assert.doesNotMatch(source, /\("API_SERVER_KEY"\.into\(\), "clawpanel-local"\.into\(\)\)/)
|
||||
})
|
||||
|
||||
test('Hermes 0.18.2 migrates a legacy bare custom endpoint to a named provider route', async () => {
|
||||
const { repairHermesCustomProviderRoutingAt } = await import('../scripts/dev-api.js')
|
||||
const YAML = await import('yaml')
|
||||
const home = fs.mkdtempSync(path.join(process.env.TEMP || process.cwd(), 'clawpanel-hermes-route-'))
|
||||
fs.writeFileSync(path.join(home, 'config.yaml'), `model:\n default: gpt-5.5\n provider: custom\n context_length: 131072\nhooks:\n enabled: true\n`)
|
||||
fs.writeFileSync(path.join(home, '.env'), `OPENAI_BASE_URL=https://example.test/v1\nCUSTOM_API_KEY=provider-secret\n`)
|
||||
|
||||
const result = repairHermesCustomProviderRoutingAt(home)
|
||||
const saved = YAML.parse(fs.readFileSync(path.join(home, 'config.yaml'), 'utf8'))
|
||||
|
||||
assert.equal(result.changed, true)
|
||||
assert.equal(saved.model.provider, 'custom:clawpanel')
|
||||
assert.equal(saved.model.base_url, 'https://example.test/v1')
|
||||
assert.equal(saved.model.context_length, 131072)
|
||||
assert.equal(saved.providers.clawpanel.base_url, 'https://example.test/v1')
|
||||
assert.equal(saved.providers.clawpanel.key_env, 'CUSTOM_API_KEY')
|
||||
assert.equal(saved.providers.clawpanel.default_model, 'gpt-5.5')
|
||||
assert.equal(saved.providers.clawpanel.api_mode, 'chat_completions')
|
||||
assert.deepEqual(saved.hooks, { enabled: true })
|
||||
|
||||
const second = repairHermesCustomProviderRoutingAt(home)
|
||||
assert.equal(second.changed, false)
|
||||
})
|
||||
|
||||
test('Hermes 0.18.2 keeps a normal OpenRouter route even when legacy custom env remains', async () => {
|
||||
const { repairHermesCustomProviderRoutingAt } = await import('../scripts/dev-api.js')
|
||||
const home = fs.mkdtempSync(path.join(process.env.TEMP || process.cwd(), 'clawpanel-hermes-openrouter-'))
|
||||
const config = `model:\n default: openai/gpt-5.5\n provider: openrouter\n`
|
||||
fs.writeFileSync(path.join(home, 'config.yaml'), config)
|
||||
fs.writeFileSync(path.join(home, '.env'), `OPENAI_BASE_URL=https://legacy.example.test/v1\nOPENROUTER_API_KEY=openrouter-secret\n`)
|
||||
|
||||
const result = repairHermesCustomProviderRoutingAt(home)
|
||||
|
||||
assert.equal(result.changed, false)
|
||||
assert.equal(fs.readFileSync(path.join(home, 'config.yaml'), 'utf8'), config)
|
||||
assert.equal(fs.existsSync(path.join(home, 'config.yaml.bak')), false)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user