chore(release): prepare v0.19.1

This commit is contained in:
晴天
2026-07-24 02:57:17 +08:00
parent 62d852c2b3
commit f315d7c084
39 changed files with 1993 additions and 790 deletions

View File

@@ -0,0 +1,108 @@
import assert from 'node:assert/strict'
import fs from 'node:fs'
import path from 'node:path'
import test from 'node:test'
import { fileURLToPath } from 'node:url'
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const read = file => fs.readFileSync(path.join(root, file), 'utf8')
const readmes = [
'README.md',
'README.en.md',
'README.zh-TW.md',
'README.ja.md',
'README.ko.md',
'README.vi.md',
'README.es.md',
'README.pt.md',
'README.ru.md',
'README.fr.md',
'README.de.md',
]
test('晴辰云免费测试与词元 API 赞助入口使用独立 Provider', () => {
const presets = read('src/lib/model-presets.js')
const models = read('src/pages/models.js')
const assistant = read('src/pages/assistant.js')
assert.match(presets, /providerKey:\s*'qtcool'/)
assert.match(presets, /providerKey:\s*'ciyapi'/)
assert.match(presets, /label:\s*'晴辰云'/)
assert.match(presets, /badge:\s*'免费测试'/)
assert.match(presets, /badge:\s*'赞助'/)
assert.match(models, /prefix:\s*'qtcool'/)
assert.match(models, /prefix:\s*'ciyapi'/)
assert.match(models, /models\.providers\[provider\.providerKey\]/)
assert.match(assistant, /qtcool:\s*\{[\s\S]*provider:\s*QTCOOL/)
assert.match(assistant, /ciyapi:\s*\{[\s\S]*provider:\s*CIYAPI/)
assert.match(assistant, /config\.models\.providers\[providerKey\]/)
assert.match(assistant, /\.\.\.existingProvider/)
assert.match(assistant, /const providerKey = definition\.provider\.providerKey/)
})
test('全部 README 同时保留晴辰云免费测试与词元 API 赞助推广', () => {
for (const file of readmes) {
const content = read(file)
assert.match(content, /https:\/\/gpt\.qt\.cool/)
assert.match(content, /https:\/\/ciyapi\.79tian\.com/)
assert.match(content, /rel="sponsored noopener noreferrer"/)
}
})
test('运行时与知识库同时说明两类服务的不同定位', () => {
for (const file of ['src/lib/model-presets.js', 'src/lib/openclaw-kb.js']) {
const content = read(file)
assert.match(content, /gpt\.qt\.cool/)
assert.match(content, /ciyapi\.79tian\.com/)
}
for (const file of ['src/pages/models.js', 'src/pages/assistant.js']) {
const content = read(file)
assert.match(content, /\bQTCOOL\b/)
assert.match(content, /\bCIYAPI\b/)
}
})
test('晴辰云快捷入口不内置、自动读取或静默复用旧密钥', () => {
const presets = read('src/lib/model-presets.js')
const models = read('src/pages/models.js')
const assistant = read('src/pages/assistant.js')
const modelsLocale = read('src/locales/modules/models.js')
const assistantLocale = read('src/locales/modules/assistant.js')
assert.doesNotMatch(presets, /readOpenclawConfig/)
assert.match(presets, /if \(!key\) return provider\.models/)
assert.doesNotMatch(models, /bannerKey\s*\|\|\s*existingProvider\?\.apiKey/)
assert.match(models, /if \(!bannerKey\)/)
assert.match(assistant, /qtcool:\s*\{[\s\S]*?allowConfigImport:\s*false/)
assert.doesNotMatch(assistant, /quickDrafts\[providerKey\]\.key\s*=\s*configured\.apiKey/)
assert.doesNotMatch(assistant, /assistant\.qtcoolSyncFrom/)
assert.match(assistant, /syncFromButton\.style\.display\s*=\s*definition\.allowConfigImport/)
assert.match(modelsLocale, /不内置晴辰云密钥/)
assert.match(assistantLocale, /不内置或自动读取晴辰云密钥/)
assert.doesNotMatch(assistantLocale, /qtcoolSyncFrom/)
for (const file of readmes) {
const content = read(file)
assert.match(content, /does not include|不内置|不內建|内蔵されず|내장되어 있지|không tích hợp|no incluye|não inclui|не содержит|n'intègre|enthält keinen/i)
}
for (const lang of ['zh-CN', 'zh-TW', 'en', 'ja', 'ko', 'vi', 'es', 'pt', 'ru', 'fr', 'de']) {
const locale = JSON.parse(read(`src/locales/${lang}.json`))
assert.equal('qtcoolSyncFrom' in locale.assistant, false)
}
})
test('文案区分免费测试与赞助福利', () => {
const modelsLocale = read('src/locales/modules/models.js')
const assistantLocale = read('src/locales/modules/assistant.js')
const readme = read('README.md')
for (const content of [modelsLocale, assistantLocale, readme]) {
assert.match(content, /免费测试/)
assert.match(content, /晴辰云/)
assert.match(content, /赞助/)
assert.match(content, /¥1/)
assert.match(content, /\$1/)
}
assert.match(readme, /第三方赞助推广/)
})

View File

@@ -3,7 +3,7 @@ import assert from 'node:assert/strict'
import * as presets from '../src/lib/model-presets.js'
const { API_TYPES, PROVIDER_PRESETS, MODEL_PRESETS } = presets
const { API_TYPES, PROVIDER_PRESETS, MODEL_PRESETS, QTCOOL, CIYAPI } = presets
test('OpenClaw 7.1 API 类型与上游契约一致', () => {
assert.deepEqual(API_TYPES.map(item => item.value), [
@@ -36,6 +36,51 @@ test('编辑未知 OpenClaw API 类型时保留原值供用户选择', () => {
// ===== Provider Presets =====
test('词元 API 赞助预设使用独立 provider 与大陆加速地址', () => {
const ciyapi = PROVIDER_PRESETS.find(p => p.key === 'ciyapi')
assert.ok(ciyapi)
assert.equal(ciyapi.label, '词元 API')
assert.equal(ciyapi.badge, '赞助')
assert.equal(ciyapi.sponsored, true)
assert.equal(ciyapi.baseUrl, 'https://ciyapi.79tian.com/v1')
assert.equal(ciyapi.api, 'openai-completions')
assert.equal(CIYAPI.providerKey, 'ciyapi')
assert.equal(CIYAPI.keyUrl, 'https://ciyapi.79tian.com/keys/')
assert.equal(CIYAPI.pricingUrl, 'https://ciyapi.79tian.com/pricing/')
assert.equal(CIYAPI.walletUrl, 'https://ciyapi.79tian.com/wallet/')
})
test('晴辰云免费测试预设与词元赞助预设并存且互不覆盖', () => {
const qtcool = PROVIDER_PRESETS.find(p => p.key === 'qtcool')
const ciyapi = PROVIDER_PRESETS.find(p => p.key === 'ciyapi')
assert.ok(qtcool)
assert.ok(ciyapi)
assert.equal(qtcool.label, '晴辰云')
assert.equal(qtcool.badge, '免费测试')
assert.equal(qtcool.baseUrl, 'https://gpt.qt.cool/v1')
assert.equal(QTCOOL.providerKey, 'qtcool')
assert.equal(QTCOOL.checkinUrl, 'https://gpt.qt.cool/checkin')
assert.equal(QTCOOL.defaultKey, '')
assert.equal(CIYAPI.defaultKey, '')
assert.notEqual(QTCOOL.providerKey, CIYAPI.providerKey)
})
test('快捷模型列表未提供密钥时不发请求也不读取旧配置', async () => {
const originalFetch = globalThis.fetch
let calls = 0
globalThis.fetch = async () => {
calls += 1
throw new Error('不应发起请求')
}
try {
assert.deepEqual(await presets.fetchQtcoolModels(''), [])
assert.deepEqual(await presets.fetchCiyapiModels(), [])
assert.equal(calls, 0)
} finally {
globalThis.fetch = originalFetch
}
})
test('PROVIDER_PRESETS contains MiniMax entry', () => {
const minimax = PROVIDER_PRESETS.find(p => p.key === 'minimax')
assert.ok(minimax, 'MiniMax provider preset should exist')