feat: expand companion platform, device controls, and memoir workflows
ci / verify (push) Canceled after 0s

Add customer and operator login, BLE provisioning, device fleet controls, call memory, memoir generation, and refreshed family and maintenance portals. Include API contracts, migrations, firmware sources, tests, assets, and deployment documentation.
This commit is contained in:
Codex Local
2026-10-08 10:06:44 +08:00
parent 98fb2965d0
commit bf1233d5df
861 changed files with 94614 additions and 3209 deletions
+70
View File
@@ -0,0 +1,70 @@
import test from "node:test";
import assert from "node:assert/strict";
import { usageDateRange, usageMinutes, usageConversationContext, usageConversationURL } from "../src/components/usage-model.ts";
test("usage range begins at Beijing midnight independent of browser timezone", () => {
const previous = process.env.TZ;
try {
for (const zone of ["Etc/UTC", "America/Los_Angeles", "Asia/Tokyo"]) {
process.env.TZ = zone;
assert.deepEqual(usageDateRange(30, new Date("2026-10-05T16:30:00Z")), {
from: "2026-09-06T16:00:00.000Z", to: "2026-10-05T16:30:00.000Z", timezone: "Asia/Shanghai",
});
}
} finally {
if (previous === undefined) delete process.env.TZ; else process.env.TZ = previous;
}
});
test("usage range includes the current local day and handles year rollover", () => {
assert.equal(usageDateRange(1, new Date("2026-01-01T00:05:00+08:00")).from, "2025-12-31T16:00:00.000Z");
assert.equal(usageDateRange(7, new Date("2026-01-01T00:05:00+08:00")).from, "2025-12-25T16:00:00.000Z");
for (const days of [0, -1, 367, NaN, 1.5]) assert.throws(() => usageDateRange(days), RangeError);
});
test('30/90/365 days are inclusive Beijing calendar days across midnight, with an exact request-time end', () => {
const cases = [
['2026-10-05T15:59:59.999Z', {30:'2026-09-05T16:00:00.000Z',90:'2026-07-07T16:00:00.000Z',365:'2025-10-05T16:00:00.000Z'}],
['2026-10-05T16:00:00.000Z', {30:'2026-09-06T16:00:00.000Z',90:'2026-07-08T16:00:00.000Z',365:'2025-10-06T16:00:00.000Z'}],
['2026-10-05T16:00:00.001Z', {30:'2026-09-06T16:00:00.000Z',90:'2026-07-08T16:00:00.000Z',365:'2025-10-06T16:00:00.000Z'}],
];
for(const [at,starts] of cases) for(const days of [30,90,365]) {
assert.deepEqual(usageDateRange(days,new Date(at)), {from:starts[days],to:at,timezone:'Asia/Shanghai'});
}
assert.equal(usageDateRange(365,new Date('2028-03-01T00:00:00+08:00')).from,'2027-03-02T16:00:00.000Z',
'the year option means 365 inclusive days, rather than the same date twelve months earlier');
});
test("short completed chats do not display zero minutes", () => {
assert.equal(usageMinutes(0), "0");
assert.equal(usageMinutes(1), "<1");
assert.equal(usageMinutes(29000), "<1");
assert.equal(usageMinutes(60000), "1");
assert.equal(usageMinutes(98000), "1.6");
});
const roster = [{ id: 'a-first', household_id: 'a' }, { id: 'a-second', household_id: 'a' }, { id: 'b-first', household_id: 'b' }];
const fleet = [{ id: 'a-device', household_id: 'a', subject_id: 'a-second' }, { id: 'b-device', household_id: 'b', subject_id: 'b-first' }, { id: 'unassigned', household_id: 'a' }, { id: 'stale-owner', household_id: 'a', subject_id: 'b-first' }];
test('a selected device selects its verified elder even when another elder is already current', () => {
for (const current of [undefined, 'a-first', 'a-second']) assert.deepEqual(usageConversationContext('a', fleet, roster, current, 'a-device'), { householdID: 'a', deviceID: 'a-device', subjectID: 'a-second' });
});
test('all devices use the current valid elder or the existing family default', () => {
assert.equal(usageConversationContext('a', fleet, roster, 'a-second').subjectID, 'a-second');
assert.deepEqual(usageConversationContext('a', fleet, roster, 'b-first'), { householdID: 'a', deviceID: undefined, subjectID: 'a-first' });
});
test('all devices with a single verified owner select that elder instead of an unrelated default', () => {
const singleOwnerFleet = [{ id: 'one', household_id: 'a', subject_id: 'a-second' }, { id: 'two', household_id: 'a', subject_id: 'a-second' }, fleet[1]];
assert.equal(usageConversationContext('a', singleOwnerFleet, roster, 'a-first').subjectID, 'a-second');
assert.equal(usageConversationContext('a', [...singleOwnerFleet, fleet[2]], roster, 'a-first').subjectID, 'a-first', 'incomplete ownership uses the existing default');
});
test('foreign or removed devices and invalid owners never cross the household boundary', () => {
for (const device of ['b-device', 'removed']) assert.deepEqual(usageConversationContext('a', fleet, roster, 'b-first', device), { householdID: 'a', deviceID: undefined, subjectID: 'a-first' });
for (const device of ['unassigned', 'stale-owner']) assert.equal(usageConversationContext('a', fleet, roster, 'b-first', device).subjectID, 'a-first');
assert.equal(usageConversationContext('empty', fleet, roster, 'a-first', 'a-device').subjectID, undefined);
});
test('the all-records URL carries verified context without promising date or device filtering', () => {
const context = usageConversationContext('a', fleet, roster, 'a-first', 'a-device');
const url = new URL(usageConversationURL(context), 'https://synthetic.invalid');
assert.deepEqual(Object.fromEntries(url.searchParams), { section: 'conversations', household_id: 'a', usage_device_id: 'a-device', subject_id: 'a-second' });
assert(!url.searchParams.has('device_id') && !url.searchParams.has('from') && !url.searchParams.has('to'));
});