feat: /login支持codex订阅登录

This commit is contained in:
Bill
2026-05-08 20:35:34 +08:00
parent 73e54d4bbc
commit c7cb3d8f93
17 changed files with 1318 additions and 39 deletions

View File

@@ -0,0 +1,26 @@
import { describe, expect, test } from 'bun:test'
import { inferLegacyCompanionBones } from '../companion.js'
describe('inferLegacyCompanionBones', () => {
test('infers species and rarity from legacy seedless companion text', () => {
expect(
inferLegacyCompanionBones({
name: 'Biscuit',
personality: 'A common mushroom of few words.',
}),
).toEqual({
species: 'mushroom',
rarity: 'common',
})
})
test('does not override seeded companions', () => {
expect(
inferLegacyCompanionBones({
name: 'Spore',
personality: 'A common mushroom of few words.',
seed: 'rehatch-1',
}),
).toEqual({})
})
})

View File

@@ -2,6 +2,7 @@ import { getGlobalConfig } from '../utils/config.js'
import {
type Companion,
type CompanionBones,
type CompanionSoul,
EYES,
HATS,
RARITIES,
@@ -125,12 +126,36 @@ export function companionUserId(): string {
return config.oauthAccount?.accountUuid ?? config.userID ?? 'anon'
}
const WORD_BOUNDARY = '[^a-z0-9]+'
function hasWord(text: string, word: string): boolean {
return new RegExp(`(^|${WORD_BOUNDARY})${word}($|${WORD_BOUNDARY})`).test(
text,
)
}
export function inferLegacyCompanionBones(
stored: CompanionSoul,
): Partial<Pick<CompanionBones, 'species' | 'rarity'>> {
if (stored.seed) return {}
const text = `${stored.name} ${stored.personality}`.toLowerCase()
const inferred: Partial<Pick<CompanionBones, 'species' | 'rarity'>> = {}
const species = SPECIES.find(species => hasWord(text, species))
const rarity = RARITIES.find(rarity => hasWord(text, rarity))
if (species) inferred.species = species
if (rarity) inferred.rarity = rarity
return inferred
}
// Regenerate bones from seed or userId, merge with stored soul.
export function getCompanion(): Companion | undefined {
const stored = getGlobalConfig().companion
if (!stored) return undefined
const seed = stored.seed ?? companionUserId()
const { bones } = rollWithSeed(seed)
// bones last so stale bones fields in old-format configs get overridden
return { ...stored, ...bones }
const legacyBones = inferLegacyCompanionBones(stored)
// Seeded companions use regenerated bones. Legacy seedless companions may
// have species/rarity embedded in their generated soul text; keep that
// visible identity coherent when the userId-derived roll drifts.
return { ...stored, ...bones, ...legacyBones }
}