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
+51
View File
@@ -0,0 +1,51 @@
// Real CallHistory component with synthetic responses; no production requests.
import assert from 'node:assert/strict';
import {createRequire} from 'node:module';
import {resolve} from 'node:path';
import {pathToFileURL} from 'node:url';
const require=createRequire(process.env.PLAYWRIGHT_REQUIRE_FROM??import.meta.url);
const {chromium}=require('playwright');
const {createServer}=await import(pathToFileURL(resolve('web/node_modules/vite/dist/node/index.js')));
const session='11111111-1111-4111-8111-111111111111';
const server=await createServer({configFile:false,root:resolve('web'),oxc:{jsx:{runtime:'automatic'}},logLevel:'error',server:{host:'127.0.0.1',port:5337,strictPort:true},plugins:[{
name:'direct-story-harness',
configureServer(s){s.middlewares.use('/__story-history',(_q,r)=>{r.setHeader('Content-Type','text/html');r.end('<!doctype html><meta charset="UTF-8"><div id="root"></div><script type="module" src="/@id/__story-history-module"></script>');});},
resolveId(id){if(id==='__story-history-module')return '\0__story-history-module';},
load(id){if(id==='\0__story-history-module')return `import React from 'react';import {createRoot} from 'react-dom/client';import {CallHistory} from '/src/pages/CallHistory.tsx';createRoot(document.getElementById('root')).render(React.createElement(CallHistory,{elderID:'synthetic-elder',accessToken:'synthetic-token',initialSessionID:'${session}'}));`;}
}]});
await server.listen();
let browser;
try {
browser=await chromium.launch({headless:true,executablePath:process.env.CHROME_PATH??'C:/Program Files/Google/Chrome/Application/chrome.exe'});
const page=await browser.newPage();
const errors=[],requested=[];
let denied=false;
page.on('pageerror',e=>errors.push(e.message));
await page.route('**/api/v1/memories/**',async route=>{
const req=route.request(),op=new URL(req.url()).pathname.split('/memories/')[1],body=req.postDataJSON();
let data={},status=200,code='OK';
if(op==='calls/query') data={items:[],next_cursor:'older-calls'};
else if(op==='calls/recordings') data={items:[],state:'NOT_CONFIGURED',can_play:false,can_download:false};
else if(op==='calls/turns') {
requested.push(body); assert.equal(body.business_session_id,session); assert.equal(body.elder_id,'synthetic-elder');
if(denied) {status=403;code='TRANSCRIPT_ACCESS_DENIED';}
else data={items:[{id:body.cursor?'u2':'u1',speaker:'USER',final_text:body.cursor?'后来学校搬到了山脚。':'小时候我在河边小学读书。',interrupted:false}],next_cursor:body.cursor?'':'second-page'};
} else throw new Error(`Unexpected endpoint ${op}`);
await route.fulfill({status,contentType:'application/json',body:JSON.stringify({ok:status===200,code,message:code,data,request_id:'synthetic',server_time_ms:Date.now()})});
});
await page.goto('http://127.0.0.1:5337/__story-history');
await page.getByText('小时候我在河边小学读书。',{exact:true}).waitFor();
assert.equal(await page.getByRole('heading',{name:'故事的原始通话'}).count(),1);
await page.getByRole('button',{name:'加载更多对话',exact:true}).click();
await page.getByText('后来学校搬到了山脚。',{exact:true}).waitFor();
assert.equal(requested.at(-1).cursor,'second-page');
await page.getByRole('button',{name:'刷新记录',exact:true}).click();
await page.getByText('小时候我在河边小学读书。',{exact:true}).waitFor();
assert.equal(requested.at(-1).cursor,undefined);
denied=true;
await page.getByRole('button',{name:'刷新记录',exact:true}).click();
await page.getByRole('alert').filter({hasText:'当前账号没有查看聊天文字的权限,请联系家庭所有者授权。'}).waitFor();
assert.equal(await page.getByText('小时候我在河边小学读书。',{exact:true}).count(),0);
assert.deepEqual(errors,[]);
console.log('PASS: story source outside first page; source pagination; refresh retains call; denied access clears text');
} finally {await browser?.close();await server.close();}