feat(hololake): add human-confirmed PNCC mounts
This commit is contained in:
parent
4d2415a35b
commit
e70c9201c2
10 changed files with 524 additions and 15 deletions
|
|
@ -43,6 +43,26 @@ interface ReceiptProjection {
|
|||
lastSequence: number
|
||||
}
|
||||
|
||||
interface RepositoryInspection {
|
||||
personaId: string
|
||||
humanResponsibilitySubject: string
|
||||
repositoryPath: string
|
||||
gitHead: string
|
||||
repositoryClean: boolean
|
||||
currentCheckpoint: { relativePath: string; sha256: string }
|
||||
modelInferenceStarted: boolean
|
||||
realityExecutionAllowed: boolean
|
||||
}
|
||||
|
||||
interface RepositoryCandidate {
|
||||
schema: string
|
||||
state: string
|
||||
candidateId: string
|
||||
confirmationToken: string
|
||||
expiresAtUnixMs: number
|
||||
inspection: RepositoryInspection
|
||||
}
|
||||
|
||||
const themes: Array<{ id: ThemeId; name: string }> = [
|
||||
{ id: 'night', name: '夜湖星光' },
|
||||
{ id: 'dawn', name: '晨湖曦光' },
|
||||
|
|
@ -88,6 +108,9 @@ function HoloLakeApp() {
|
|||
const [panel, setPanel] = useState<Panel>(null)
|
||||
const [ticket, setTicket] = useState<DiscoveryTicketReceipt | null>(null)
|
||||
const [receipts, setReceipts] = useState<ReceiptEvent[]>([])
|
||||
const [repositoryCandidate, setRepositoryCandidate] = useState<RepositoryCandidate | null>(null)
|
||||
const [repositoryBusy, setRepositoryBusy] = useState(false)
|
||||
const [repositoryMessage, setRepositoryMessage] = useState('')
|
||||
const [message, setMessage] = useState('')
|
||||
|
||||
const refreshStatus = useCallback(async () => {
|
||||
|
|
@ -117,6 +140,10 @@ function HoloLakeApp() {
|
|||
|
||||
const openReceipts = async () => {
|
||||
setPanel('receipts')
|
||||
await loadReceipts()
|
||||
}
|
||||
|
||||
const loadReceipts = async () => {
|
||||
try {
|
||||
const projection = await invoke<ReceiptProjection>('query_pncc_receipt_projection', { input: { afterSequence: 0, limit: 25 } })
|
||||
setReceipts(projection.events)
|
||||
|
|
@ -125,6 +152,42 @@ function HoloLakeApp() {
|
|||
}
|
||||
}
|
||||
|
||||
const chooseRepository = async () => {
|
||||
setRepositoryBusy(true)
|
||||
setRepositoryMessage('')
|
||||
try {
|
||||
const candidate = await invoke<RepositoryCandidate | null>('select_pncc_repository_candidate')
|
||||
setRepositoryCandidate(candidate)
|
||||
if (!candidate) setRepositoryMessage('已取消选择,没有写入任何绑定。')
|
||||
} catch (error) {
|
||||
setRepositoryCandidate(null)
|
||||
setRepositoryMessage(`这个文件夹不能作为代码通道:${String(error)}`)
|
||||
} finally {
|
||||
setRepositoryBusy(false)
|
||||
}
|
||||
}
|
||||
|
||||
const confirmRepository = async () => {
|
||||
if (!repositoryCandidate) return
|
||||
setRepositoryBusy(true)
|
||||
setRepositoryMessage('')
|
||||
try {
|
||||
await invoke('confirm_pncc_repository_mount', {
|
||||
input: {
|
||||
candidateId: repositoryCandidate.candidateId,
|
||||
confirmationToken: repositoryCandidate.confirmationToken,
|
||||
},
|
||||
})
|
||||
setRepositoryCandidate(null)
|
||||
setRepositoryMessage('代码仓库已按当前固定提交绑定为只读通道。')
|
||||
await Promise.all([refreshStatus(), loadReceipts()])
|
||||
} catch (error) {
|
||||
setRepositoryMessage(`绑定没有写入:${String(error)}`)
|
||||
} finally {
|
||||
setRepositoryBusy(false)
|
||||
}
|
||||
}
|
||||
|
||||
const issueInvitation = async () => {
|
||||
setMessage('')
|
||||
try {
|
||||
|
|
@ -238,7 +301,20 @@ function HoloLakeApp() {
|
|||
<p className="panel-kicker">可核验的发生</p>
|
||||
<h2 id="panel-title">代码通道回执</h2>
|
||||
<p className="panel-intro">这里是只读投影,不是权限来源。没有回执,也不代表服务器离线。</p>
|
||||
{receipts.length ? <ol className="receipt-list">{receipts.map((receipt) => <li key={receipt.sequence}><span>{receipt.sequence.toString().padStart(2, '0')}</span><div><b>{receipt.kind === 'REPOSITORY_BINDING_REVALIDATED' ? '仓库绑定已复核' : '远端对象读取已验证'}</b><small>{new Date(Number(receipt.observedAtUnixMs)).toLocaleString('zh-CN')} · {receipt.eventHash.slice(0, 12)}</small></div></li>)}</ol> : <div className="empty-state"><i /><b>湖面还没有新回执</b><span>完成代码仓库绑定或读取后,这里会出现可核验记录。</span></div>}
|
||||
{repositoryCandidate ? <div className="candidate-card">
|
||||
<div className="candidate-heading"><span className="status-light waiting" /><div><b>等待你确认</b><span>HoloLake 已只读检查这个 Git 仓库</span></div></div>
|
||||
<dl className="candidate-facts">
|
||||
<div><dt>人格</dt><dd>{repositoryCandidate.inspection.personaId}</dd></div>
|
||||
<div><dt>责任人</dt><dd>{repositoryCandidate.inspection.humanResponsibilitySubject}</dd></div>
|
||||
<div><dt>固定提交</dt><dd>{repositoryCandidate.inspection.gitHead.slice(0, 12)}</dd></div>
|
||||
<div><dt>工作区</dt><dd>{repositoryCandidate.inspection.repositoryClean ? '干净' : '有未提交变化 · 不读取'}</dd></div>
|
||||
<div><dt>检查点</dt><dd>{repositoryCandidate.inspection.currentCheckpoint.relativePath}</dd></div>
|
||||
</dl>
|
||||
<p>确认后只绑定这一份已提交证据;不会启动模型、不会取得人格租约、不会执行现实动作。</p>
|
||||
<div className="candidate-actions"><button className="primary-action" type="button" disabled={repositoryBusy} onClick={() => void confirmRepository()}>确认只读绑定<Icon name="arrow" /></button><button className="text-action" type="button" disabled={repositoryBusy} onClick={() => setRepositoryCandidate(null)}>重新选择</button></div>
|
||||
</div> : <button className="repository-picker" type="button" disabled={repositoryBusy} onClick={() => void chooseRepository()}><span><b>{status.codeRepositoryMountCount > 0 ? '再绑定一个代码仓库' : '绑定代码通道仓库'}</b><small>从 macOS 原生文件夹选择器进入</small></span><Icon name="arrow" /></button>}
|
||||
{repositoryMessage && <p className="panel-message" aria-live="polite">{repositoryMessage}</p>}
|
||||
{receipts.length ? <ol className="receipt-list">{receipts.map((receipt) => <li key={receipt.sequence}><span>{receipt.sequence.toString().padStart(2, '0')}</span><div><b>{receipt.kind === 'REPOSITORY_BINDING_REVALIDATED' ? '仓库绑定已复核' : '远端对象读取已验证'}</b><small>{new Date(Number(receipt.observedAtUnixMs)).toLocaleString('zh-CN')} · {receipt.eventHash.slice(0, 12)}</small></div></li>)}</ol> : <div className="empty-state compact"><i /><b>湖面还没有新回执</b><span>完成代码仓库绑定或读取后,这里会出现可核验记录。</span></div>}
|
||||
</>}
|
||||
{panel === 'settings' && <>
|
||||
<p className="panel-kicker">湖面设置</p>
|
||||
|
|
|
|||
Loading…
Reference in a new issue