import assert from "node:assert/strict"; import { getBmeHostAdapter, isBmeLightweightHostMode, normalizeBmeChatStateTarget, resolveBmeHostProfile, resolveCurrentBmeChatStateTarget, resolveChatStateTargetChatId, serializeBmeChatStateTarget, } from "../host/runtime-host-adapter.js"; const originalNavigator = globalThis.navigator; const originalLuker = globalThis.Luker; try { Object.defineProperty(globalThis, "navigator", { configurable: true, value: { userAgent: "Mozilla/5.0 (Linux; Android 14; wv) AppleWebKit/537.36 Mobile Safari/537.36", }, }); const context = { groupId: "group-1", chatId: "group-1", getChatState() {}, updateChatState() {}, getChatStateBatch() {}, }; globalThis.Luker = { getContext() { return context; }, }; assert.equal(resolveBmeHostProfile(context), "luker"); assert.equal(isBmeLightweightHostMode(context), true); const target = resolveCurrentBmeChatStateTarget(context); assert.deepEqual(target, { is_group: true, id: "group-1", }); assert.equal(resolveChatStateTargetChatId(target), "group-1"); assert.equal(serializeBmeChatStateTarget(target), "group:group-1"); const noChatSelectedContext = { chatId: "", characterId: "", groupId: null, getChatState() {}, updateChatState() {}, getChatStateBatch() {}, }; globalThis.Luker = { getContext() { return noChatSelectedContext; }, }; assert.equal( resolveBmeHostProfile(noChatSelectedContext), "luker", "未进入聊天时,宿主档案仍应反映 Luker 环境,而不是退回 generic-st", ); assert.equal(resolveCurrentBmeChatStateTarget(noChatSelectedContext), null); const characterContext = { chatId: "chat-char-1", characterId: "char-1", characters: { "char-1": { avatar: "alice.png", }, }, getChatState() {}, updateChatState() {}, getChatStateBatch() {}, }; globalThis.Luker = { getContext() { return characterContext; }, }; const adapter = getBmeHostAdapter(characterContext); const explicitTarget = normalizeBmeChatStateTarget({ is_group: false, avatar_url: "alice.png", file_name: "chat-char-branch", }); let recordedTarget = null; characterContext.updateChatState = async function(namespace, updater, options = {}) { recordedTarget = options?.target ?? null; return { ok: true, updated: true, state: await updater({}) }; }; await adapter.updateChatState("st_bme_graph_manifest", () => ({ ok: true }), { target: explicitTarget, }); assert.deepEqual(recordedTarget, explicitTarget); } finally { if (originalNavigator === undefined) { delete globalThis.navigator; } else { Object.defineProperty(globalThis, "navigator", { configurable: true, value: originalNavigator, }); } if (originalLuker === undefined) { delete globalThis.Luker; } else { globalThis.Luker = originalLuker; } } console.log("luker-host-adapter tests passed");