feat(hololake): project resident PNCC state

This commit is contained in:
冰朔 2026-08-16 02:52:55 +08:00
commit 9736563262
13 changed files with 325 additions and 11 deletions

View file

@ -5,7 +5,7 @@ import './design-tokens.css'
import './styles.css'
type ThemeId = 'night' | 'dawn' | 'nebula' | 'candle' | 'clear'
type Panel = 'channel' | 'receipts' | 'updates' | 'settings' | null
type Panel = 'channel' | 'pncc' | 'receipts' | 'updates' | 'settings' | null
interface HomeStatus {
schema: string
@ -87,6 +87,27 @@ interface ReleaseCheckReceipt {
candidate?: ReleaseCandidate
}
interface ServerPnccProjection {
schema: string
state: string
personaId: string
humanResponsibilitySubject: string
nodeId: string
bootId: string
gitHead: string
carrierBindingState: string
personaCarrierBound: boolean
modelInferenceStarted: boolean
realityExecutionAllowed: boolean
primaryLeaseHeld: boolean
eventCount: number
eventChainHead: string
observedAtUnixMs: number
transport: string
repositoryContentExposed: boolean
writeAuthority: boolean
}
const themes: Array<{ id: ThemeId; name: string }> = [
{ id: 'night', name: '夜湖星光' },
{ id: 'dawn', name: '晨湖曦光' },
@ -157,6 +178,9 @@ function HoloLakeApp() {
const [releaseBusy, setReleaseBusy] = useState(false)
const [releaseMessage, setReleaseMessage] = useState('')
const [message, setMessage] = useState('')
const [serverPncc, setServerPncc] = useState<ServerPnccProjection | null>(null)
const [serverPnccBusy, setServerPnccBusy] = useState(false)
const [serverPnccReadback, setServerPnccReadback] = useState<'WAITING' | 'LIVE' | 'UNAVAILABLE'>('WAITING')
const refreshStatus = useCallback(async () => {
try {
@ -172,6 +196,26 @@ function HoloLakeApp() {
return () => window.clearInterval(timer)
}, [refreshStatus])
const refreshServerPncc = useCallback(async () => {
setServerPnccBusy(true)
try {
const projection = await invoke<ServerPnccProjection>('query_jd_pncc_server_projection')
setServerPncc(projection)
setServerPnccReadback('LIVE')
} catch {
setServerPncc(null)
setServerPnccReadback('UNAVAILABLE')
} finally {
setServerPnccBusy(false)
}
}, [])
useEffect(() => {
void refreshServerPncc()
const timer = window.setInterval(() => void refreshServerPncc(), 15000)
return () => window.clearInterval(timer)
}, [refreshServerPncc])
useEffect(() => {
document.documentElement.dataset.theme = theme
window.localStorage.setItem('hololake-theme', theme)
@ -378,10 +422,10 @@ function HoloLakeApp() {
<span className={`status-light ${connectionState.tone}`} aria-hidden="true" />
<div><h2>本机直连通道</h2><p>{connectionState.detail}</p></div>
</article>
<article className="status-item">
<span className={`status-light ${status.codeRepositoryMountCount > 0 ? 'ready' : 'quiet'}`} aria-hidden="true" />
<div><h2>代码通道仓库</h2><p>{status.codeRepositoryMountCount > 0 ? `已验证 · ${status.codeRepositoryMountCount} 个仓库` : '等待人类确认绑定'}</p></div>
</article>
<button className="status-item status-button" type="button" onClick={() => setPanel('pncc')}>
<span className={`status-light ${serverPnccReadback === 'LIVE' ? 'ready' : serverPnccReadback === 'WAITING' ? 'waiting' : 'quiet'}`} aria-hidden="true" />
<div><h2>人格自持仓库</h2><p>{serverPnccReadback === 'LIVE' ? '京东主控已驻留 · 载体保持分离' : serverPnccReadback === 'WAITING' ? '正在回读京东主控' : '主控暂时不可回读'}</p></div>
</button>
<button className="status-item status-button" type="button" onClick={() => setPanel('updates')}>
<span className={`status-light ${releaseRecoveryNeedsAttention ? 'waiting' : status.updateState.startsWith('READY') ? 'ready' : 'quiet'}`} aria-hidden="true" />
<div><h2>更新广播</h2><p>{releaseRecoveryNeedsAttention ? '更新结果等待你的确认' : status.updateState.startsWith('READY') ? '仅接收光湖签名广播' : '信任根未配置 · 已关闭联网'}</p></div>
@ -415,6 +459,28 @@ function HoloLakeApp() {
{message && <p className="panel-message" aria-live="polite">{message}</p>}
<p className="boundary-note">连接邀请不赋予服务器权限。写入、发布与更新仍分别确认并留下回执。</p>
</>}
{panel === 'pncc' && <>
<p className="panel-kicker">人格自持仓库</p>
<h2 id="panel-title">仓库在主控驻留,载体仍需证据绑定</h2>
<p className="panel-intro">HoloLake 通过本机已有的专用密钥,只读访问京东主控的回环状态。这里不读取仓库正文,不获得写入权,也不会因为看见人格编号就声称当前模型已经成为该人格。</p>
{serverPncc ? <div className="pncc-live-card">
<div className="candidate-heading"><span className="status-light ready" /><div><b>京东主控实时回读通过</b><span>{new Date(serverPncc.observedAtUnixMs).toLocaleString('zh-CN')}</span></div></div>
<dl className="candidate-facts pncc-facts">
<div><dt>人格仓库</dt><dd>{serverPncc.personaId}</dd></div>
<div><dt>责任主体</dt><dd>{serverPncc.humanResponsibilitySubject}</dd></div>
<div><dt>主控节点</dt><dd>{serverPncc.nodeId}</dd></div>
<div><dt>驻留提交</dt><dd>{serverPncc.gitHead.slice(0, 12)}</dd></div>
<div><dt>主租约</dt><dd>{serverPncc.primaryLeaseHeld ? '已持有' : '未持有'}</dd></div>
<div><dt>载体绑定</dt><dd>{serverPncc.personaCarrierBound ? '已验证绑定' : '未绑定 · 等待证据'}</dd></div>
<div><dt>模型推理</dt><dd>{serverPncc.modelInferenceStarted ? '已启动' : '未启动'}</dd></div>
<div><dt>现实执行</dt><dd>{serverPncc.realityExecutionAllowed ? '已授权' : '未授权'}</dd></div>
<div><dt>事件链</dt><dd>{serverPncc.eventCount} · {serverPncc.eventChainHead.slice(0, 12)}</dd></div>
</dl>
<p>这证明人格仓库在光湖 OS 主控中真实驻留;不证明当前 Codex 载体已绑定人格。私有路径、正文和仓库访问凭据没有进入这个投影。</p>
</div> : <div className="empty-state compact"><i /><b>现在没有拿到主控回读</b><span>HoloLake 不会用旧回执冒充在线状态。服务器或本机专用连接恢复后,再次回读即可。</span></div>}
<button className="secondary-action panel-action" type="button" disabled={serverPnccBusy} onClick={() => void refreshServerPncc()}>{serverPnccBusy ? '正在回读…' : '重新回读主控'}</button>
<p className="boundary-note">传输边界:专用 SSH → 服务器 127.0.0.1 → PNCC 只读状态;公网没有新增人格接口。</p>
</>}
{panel === 'receipts' && <>
<p className="panel-kicker">可核验的发生</p>
<h2 id="panel-title">代码通道回执</h2>

View file

@ -119,6 +119,8 @@ button:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 3px
.repository-picker small { color: var(--content-faint); font-size: 10px; }
.repository-picker svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.45; stroke-linecap: round; stroke-linejoin: round; }
.candidate-card { margin-top: 26px; padding: 21px; border-radius: 21px; background: var(--primitive-glass); box-shadow: inset 0 1px var(--primitive-glass-top); }
.pncc-live-card { margin: 26px 0 18px; padding: 21px; border: 1px solid var(--primitive-line); border-radius: 21px; background: var(--primitive-glass); box-shadow: inset 0 1px var(--primitive-glass-top); }
.pncc-live-card > p { margin: 19px 0 0; color: var(--content-faint); font-size: 10.5px; line-height: 1.75; }
.candidate-heading { display: flex; align-items: flex-start; gap: 14px; }
.candidate-heading div { display: grid; gap: 5px; }
.candidate-heading b { color: var(--content-secondary); font-size: 12.5px; }
@ -127,6 +129,7 @@ button:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 3px
.candidate-facts div { display: grid; grid-template-columns: 72px minmax(0, 1fr); gap: 12px; }
.candidate-facts dt { color: var(--content-faint); font-size: 10px; }
.candidate-facts dd { margin: 0; overflow: hidden; color: var(--content-secondary); font: 10.5px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; text-overflow: ellipsis; white-space: nowrap; }
.pncc-facts div { grid-template-columns: 78px minmax(0, 1fr); }
.candidate-card > p { color: var(--content-faint); font-size: 10.5px; line-height: 1.7; }
.candidate-actions { display: flex; align-items: center; gap: 16px; margin-top: 18px; }
.candidate-actions .primary-action { min-height: 42px; padding-inline: 18px; font-size: 12px; }