import test from "node:test"; import assert from "node:assert/strict"; import os from "node:os"; import path from "node:path"; import { mkdtemp, rm } from "node:fs/promises"; let runtimeRoot = ""; let readState: (typeof import("../src/lib/boss-data"))["readState"]; let upsertDeviceHeartbeat: (typeof import("../src/lib/boss-data"))["upsertDeviceHeartbeat"]; let subscribeBossEvents: (typeof import("../src/lib/boss-events"))["subscribeBossEvents"]; async function setup() { if (runtimeRoot) return; runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-device-heartbeat-noise-")); process.env.BOSS_RUNTIME_ROOT = runtimeRoot; process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json"); const [data, events] = await Promise.all([ import("../src/lib/boss-data.ts"), import("../src/lib/boss-events.ts"), ]); readState = data.readState; upsertDeviceHeartbeat = data.upsertDeviceHeartbeat; subscribeBossEvents = events.subscribeBossEvents; } test.after(async () => { if (runtimeRoot) { await rm(runtimeRoot, { recursive: true, force: true }); } }); function buildHeartbeatPayload(deviceId: string, projectCandidates: Array<{ folderName: string; threadId: string; threadDisplayName: string; codexFolderRef: string; codexThreadRef: string; lastActiveAt: string; }>) { return { deviceId, token: `${deviceId}-token`, name: "Mac Studio", avatar: "M", account: "17600003315", status: "online" as const, quota5h: 68, quota7d: 81, projects: [], endpoint: "mac://kris.local", projectCandidates, }; } const bossThreadCandidate = { folderName: "boss", threadId: "thread-boss-main", threadDisplayName: "Boss开发主线程", codexFolderRef: "/Users/kris/code/boss", codexThreadRef: "thread-boss-main", lastActiveAt: "2026-04-10T10:00:00.000Z", }; test("unchanged device heartbeats do not publish conversation refresh events", async () => { await setup(); const heartbeat = buildHeartbeatPayload("noise-device-a", [bossThreadCandidate]); await upsertDeviceHeartbeat(heartbeat); await upsertDeviceHeartbeat(heartbeat); const primedState = await readState(); const primedDraft = primedState.deviceImportDrafts.find((draft) => draft.deviceId === "noise-device-a"); assert.equal(primedDraft?.status, "applied"); const events: Array<{ event: string; payload: { note?: string; deviceId?: string } }> = []; const unsubscribe = subscribeBossEvents((event, payload) => { events.push({ event, payload }); }); await upsertDeviceHeartbeat(heartbeat); unsubscribe(); assert.deepEqual(events.map((event) => event.event), ["devices.updated"]); }); test("device heartbeats publish one conversation refresh when import candidates change", async () => { await setup(); const nextThreadCandidate = { folderName: "boss", threadId: "thread-boss-review", threadDisplayName: "Boss回归线程", codexFolderRef: "/Users/kris/code/boss", codexThreadRef: "thread-boss-review", lastActiveAt: "2026-04-10T10:01:00.000Z", }; await upsertDeviceHeartbeat(buildHeartbeatPayload("noise-device-b", [bossThreadCandidate])); await upsertDeviceHeartbeat(buildHeartbeatPayload("noise-device-b", [bossThreadCandidate])); const events: Array<{ event: string; payload: { note?: string; deviceId?: string } }> = []; const unsubscribe = subscribeBossEvents((event, payload) => { events.push({ event, payload }); }); await upsertDeviceHeartbeat(buildHeartbeatPayload("noise-device-b", [bossThreadCandidate, nextThreadCandidate])); unsubscribe(); assert.deepEqual( events.map((event) => [event.event, event.payload.deviceId, event.payload.note]), [ ["devices.updated", "noise-device-b", undefined], ["conversation.updated", "noise-device-b", "device_import.updated"], ], ); });