456 lines
19 KiB
JavaScript
456 lines
19 KiB
JavaScript
#!/usr/bin/env node
|
||
|
||
import { spawnSync } from 'node:child_process'
|
||
import { createHash, randomUUID } from 'node:crypto'
|
||
import {
|
||
closeSync,
|
||
existsSync,
|
||
mkdirSync,
|
||
openSync,
|
||
readFileSync,
|
||
realpathSync,
|
||
renameSync,
|
||
rmSync,
|
||
writeFileSync,
|
||
} from 'node:fs'
|
||
import { createServer } from 'node:http'
|
||
import { basename, dirname, isAbsolute, join, normalize, resolve, sep } from 'node:path'
|
||
import { fileURLToPath } from 'node:url'
|
||
|
||
const MANIFEST_PATH = '.hololake/persona/manifest.json'
|
||
const MANIFEST_SCHEMA = 'hololake.persona/v1'
|
||
const GRAVITY_SCHEMA = 'hololake.persona-cognitive-gravity-binding/v1'
|
||
const EVENT_SCHEMA = 'guanghu.pncc-runtime-event/v1'
|
||
const JOURNAL_SCHEMA = 'guanghu.pncc-runtime-journal/v1'
|
||
const STATUS_SCHEMA = 'guanghu.pncc-runtime-status/v1'
|
||
const MAX_ARTIFACT_BYTES = 2 * 1024 * 1024
|
||
const MAX_ARTIFACTS = 256
|
||
|
||
function sha256(value) {
|
||
return createHash('sha256').update(value).digest('hex')
|
||
}
|
||
|
||
function fail(code, detail = '') {
|
||
throw new Error(detail ? `${code}: ${detail}` : code)
|
||
}
|
||
|
||
function runGit(repository, args, label, options = {}) {
|
||
const result = spawnSync('git', ['-C', repository, ...args], {
|
||
encoding: options.encoding ?? 'utf8',
|
||
env: options.env ?? process.env,
|
||
maxBuffer: 8 * 1024 * 1024,
|
||
})
|
||
if (result.status !== 0) {
|
||
fail(label, String(result.stderr || result.stdout || '').trim())
|
||
}
|
||
return result.stdout
|
||
}
|
||
|
||
function validateFullHead(value) {
|
||
if (!/^[0-9a-f]{40}$/.test(value)) fail('PNCC_FULL_GIT_HEAD_INVALID')
|
||
return value
|
||
}
|
||
|
||
function validateText(value, label, maximum = 256) {
|
||
if (typeof value !== 'string' || value.trim() !== value || value.length === 0 || value.length > maximum || /[\u0000-\u001f]/u.test(value)) {
|
||
fail(`PNCC_${label}_INVALID`)
|
||
}
|
||
return value
|
||
}
|
||
|
||
function validateRelativePath(value) {
|
||
validateText(value, 'RELATIVE_PATH', 1024)
|
||
const normalized = normalize(value)
|
||
if (isAbsolute(value) || normalized === '..' || normalized.startsWith(`..${sep}`) || normalized.split(sep).includes('.git')) {
|
||
fail('PNCC_REPOSITORY_RELATIVE_PATH_INVALID', value)
|
||
}
|
||
return value
|
||
}
|
||
|
||
function exactGitRoot(repository) {
|
||
const candidate = realpathSync(repository)
|
||
const top = realpathSync(String(runGit(candidate, ['rev-parse', '--show-toplevel'], 'PNCC_GIT_ROOT_READ')).trim())
|
||
if (candidate !== top) fail('PNCC_REPOSITORY_MUST_BE_EXACT_GIT_ROOT')
|
||
return candidate
|
||
}
|
||
|
||
function committedArtifact(repository, head, path) {
|
||
validateRelativePath(path)
|
||
const object = validateFullHead(String(runGit(repository, ['rev-parse', `${head}:${path}`], 'PNCC_GIT_OBJECT_ID_READ')).trim().toLowerCase())
|
||
const type = String(runGit(repository, ['cat-file', '-t', object], 'PNCC_GIT_OBJECT_TYPE_READ')).trim()
|
||
if (type !== 'blob') fail('PNCC_COMMITTED_OBJECT_NOT_BLOB', path)
|
||
const mode = String(runGit(repository, ['ls-tree', head, '--', path], 'PNCC_GIT_OBJECT_MODE_READ')).trim().split(/\s+/u)[0]
|
||
if (!['100644', '100755'].includes(mode)) fail('PNCC_COMMITTED_OBJECT_NOT_REGULAR_FILE', path)
|
||
const bytes = runGit(repository, ['cat-file', 'blob', object], 'PNCC_GIT_OBJECT_READ', { encoding: 'buffer' })
|
||
if (!Buffer.isBuffer(bytes) || bytes.length === 0 || bytes.length > MAX_ARTIFACT_BYTES) {
|
||
fail('PNCC_COMMITTED_OBJECT_SIZE_INVALID', path)
|
||
}
|
||
return { relativePath: path, gitObjectId: object, sha256: sha256(bytes), byteLength: bytes.length, bytes }
|
||
}
|
||
|
||
function parseManifest(repository, head) {
|
||
const evidence = committedArtifact(repository, head, MANIFEST_PATH)
|
||
let manifest
|
||
try {
|
||
manifest = JSON.parse(evidence.bytes.toString('utf8'))
|
||
} catch (error) {
|
||
fail('PNCC_PERSONA_MANIFEST_INVALID', error.message)
|
||
}
|
||
if (manifest.schema !== MANIFEST_SCHEMA) fail('PNCC_PERSONA_MANIFEST_SCHEMA_UNSUPPORTED')
|
||
validateText(manifest.personaId, 'PERSONA_ID')
|
||
validateText(manifest.humanResponsibilitySubject, 'HUMAN_RESPONSIBILITY_SUBJECT')
|
||
validateRelativePath(manifest.brainEntry)
|
||
validateRelativePath(manifest.currentCheckpoint)
|
||
if (manifest.cognitiveGravity?.schema !== GRAVITY_SCHEMA || manifest.cognitiveGravity.subjectPersonaId !== manifest.personaId) {
|
||
fail('PNCC_COGNITIVE_GRAVITY_BINDING_INVALID')
|
||
}
|
||
validateRelativePath(manifest.cognitiveGravity.sourcePath)
|
||
validateText(manifest.cognitiveGravity.frameSchema, 'COGNITIVE_GRAVITY_FRAME_SCHEMA')
|
||
validateText(manifest.primaryNode, 'PRIMARY_NODE')
|
||
if (manifest.carrierBinding?.state !== 'UNBOUND_EVIDENCE_REQUIRED' && manifest.carrierBinding?.state !== 'BOUND_VERIFIED') {
|
||
fail('PNCC_CARRIER_BINDING_STATE_INVALID')
|
||
}
|
||
if (!Array.isArray(manifest.organs) || manifest.organs.length === 0) fail('PNCC_ORGAN_MANIFEST_EMPTY')
|
||
return { manifest, evidence }
|
||
}
|
||
|
||
export function inspectPersonaRepository(repositoryPath, nodeId) {
|
||
validateText(nodeId, 'NODE_ID')
|
||
const repository = exactGitRoot(repositoryPath)
|
||
const head = validateFullHead(String(runGit(repository, ['rev-parse', 'HEAD'], 'PNCC_GIT_HEAD_READ')).trim().toLowerCase())
|
||
const dirty = String(runGit(repository, ['status', '--porcelain', '--untracked-files=all'], 'PNCC_GIT_STATUS_READ')).trim()
|
||
if (dirty) fail('PNCC_REPOSITORY_DIRTY')
|
||
const { manifest, evidence: manifestEvidence } = parseManifest(repository, head)
|
||
if (manifest.primaryNode !== nodeId) fail('PNCC_PRIMARY_NODE_MISMATCH')
|
||
|
||
const paths = new Set([manifest.brainEntry, manifest.currentCheckpoint, manifest.cognitiveGravity.sourcePath])
|
||
for (const organ of manifest.organs) {
|
||
validateText(organ.organId, 'ORGAN_ID')
|
||
if (organ.mode !== 'READ_ONLY') fail('PNCC_UNSAFE_ORGAN_MODE', organ.organId)
|
||
if (!Array.isArray(organ.paths) || organ.paths.length === 0) fail('PNCC_ORGAN_PATHS_EMPTY', organ.organId)
|
||
for (const path of organ.paths) paths.add(validateRelativePath(path))
|
||
}
|
||
if (paths.size > MAX_ARTIFACTS) fail('PNCC_DECLARED_ARTIFACT_LIMIT_EXCEEDED')
|
||
const artifacts = [...paths].sort().map((path) => {
|
||
const artifact = committedArtifact(repository, head, path)
|
||
return { relativePath: artifact.relativePath, gitObjectId: artifact.gitObjectId, sha256: artifact.sha256, byteLength: artifact.byteLength }
|
||
})
|
||
return {
|
||
schema: 'guanghu.pncc-persona-repository-inspection/v1',
|
||
state: 'PERSONA_REPOSITORY_BOUND_CARRIER_SEPARATE',
|
||
personaId: manifest.personaId,
|
||
humanResponsibilitySubject: manifest.humanResponsibilitySubject,
|
||
nodeId,
|
||
repositoryPath: repository,
|
||
repositoryClean: true,
|
||
gitHead: head,
|
||
manifestSha256: manifestEvidence.sha256,
|
||
brainEntry: manifest.brainEntry,
|
||
currentCheckpoint: manifest.currentCheckpoint,
|
||
cognitiveGravity: manifest.cognitiveGravity,
|
||
carrierBindingState: manifest.carrierBinding.state,
|
||
modelInferenceStarted: false,
|
||
realityExecutionAllowed: false,
|
||
artifacts,
|
||
manifest,
|
||
receiptId: sha256(`${manifest.personaId}\n${nodeId}\n${repository}\n${head}\n${manifestEvidence.sha256}`),
|
||
}
|
||
}
|
||
|
||
function eventHash(event) {
|
||
return sha256([
|
||
event.schema,
|
||
event.sequence,
|
||
event.kind,
|
||
event.personaId,
|
||
event.nodeId,
|
||
event.bootId,
|
||
event.gitHead,
|
||
event.detailsSha256,
|
||
event.observedAt,
|
||
event.previousEventHash,
|
||
].join('\n'))
|
||
}
|
||
|
||
function appendEvent(runtime, kind, details = {}) {
|
||
const previousEventHash = runtime.events.at(-1)?.eventHash ?? '0'.repeat(64)
|
||
const event = {
|
||
schema: EVENT_SCHEMA,
|
||
sequence: runtime.events.length + 1,
|
||
kind,
|
||
personaId: runtime.inspection.personaId,
|
||
nodeId: runtime.nodeId,
|
||
bootId: runtime.bootId,
|
||
gitHead: runtime.inspection.gitHead,
|
||
details,
|
||
detailsSha256: sha256(JSON.stringify(details)),
|
||
observedAt: new Date().toISOString(),
|
||
previousEventHash,
|
||
}
|
||
event.eventHash = eventHash(event)
|
||
runtime.events.push(event)
|
||
writeJsonAtomic(runtime.journalPath, { schema: JOURNAL_SCHEMA, events: runtime.events })
|
||
return event
|
||
}
|
||
|
||
export function verifyEventJournal(events) {
|
||
let previous = '0'.repeat(64)
|
||
for (let index = 0; index < events.length; index += 1) {
|
||
const event = events[index]
|
||
if (event.schema !== EVENT_SCHEMA || event.sequence !== index + 1 || event.previousEventHash !== previous || event.eventHash !== eventHash(event)) {
|
||
return { ok: false, failedSequence: index + 1 }
|
||
}
|
||
previous = event.eventHash
|
||
}
|
||
return { ok: true, eventCount: events.length, chainHead: previous }
|
||
}
|
||
|
||
function writeJsonAtomic(path, value) {
|
||
mkdirSync(dirname(path), { recursive: true, mode: 0o700 })
|
||
const temporary = join(dirname(path), `.${basename(path)}.${randomUUID()}.tmp`)
|
||
writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600, flag: 'wx' })
|
||
renameSync(temporary, path)
|
||
}
|
||
|
||
function runtimeStatus(runtime) {
|
||
const verification = verifyEventJournal(runtime.events)
|
||
if (!verification.ok) fail('PNCC_EVENT_JOURNAL_INVALID')
|
||
return {
|
||
schema: STATUS_SCHEMA,
|
||
state: runtime.released ? 'DORMANT_RELEASED' : 'RESIDENT_BOUND_CARRIER_UNBOUND',
|
||
personaId: runtime.inspection.personaId,
|
||
humanResponsibilitySubject: runtime.inspection.humanResponsibilitySubject,
|
||
nodeId: runtime.nodeId,
|
||
bootId: runtime.bootId,
|
||
gitHead: runtime.inspection.gitHead,
|
||
repositoryReceiptId: runtime.inspection.receiptId,
|
||
carrierBindingState: runtime.inspection.carrierBindingState,
|
||
personaCarrierBound: runtime.inspection.carrierBindingState === 'BOUND_VERIFIED',
|
||
modelInferenceStarted: false,
|
||
realityExecutionAllowed: false,
|
||
primaryLeaseHeld: !runtime.released,
|
||
eventCount: verification.eventCount,
|
||
eventChainHead: verification.chainHead,
|
||
}
|
||
}
|
||
|
||
export function startPersonaRuntime({ repository, stateRoot, nodeId, bootId }) {
|
||
validateText(bootId, 'BOOT_ID')
|
||
const inspection = inspectPersonaRepository(repository, nodeId)
|
||
mkdirSync(stateRoot, { recursive: true, mode: 0o700 })
|
||
const leasePath = join(stateRoot, `${inspection.personaId}.primary-lease.json`)
|
||
let descriptor
|
||
try {
|
||
descriptor = openSync(leasePath, 'wx', 0o600)
|
||
} catch (error) {
|
||
if (error.code === 'EEXIST') fail('PNCC_PRIMARY_LEASE_HELD')
|
||
fail('PNCC_PRIMARY_LEASE_UNAVAILABLE', error.message)
|
||
}
|
||
const lease = {
|
||
schema: 'guanghu.pncc-primary-lease/v1',
|
||
leaseId: `pncc-lease-${randomUUID()}`,
|
||
personaId: inspection.personaId,
|
||
nodeId,
|
||
bootId,
|
||
gitHead: inspection.gitHead,
|
||
processId: process.pid,
|
||
acquiredAt: new Date().toISOString(),
|
||
}
|
||
writeFileSync(descriptor, `${JSON.stringify(lease, null, 2)}\n`)
|
||
closeSync(descriptor)
|
||
const runtime = {
|
||
inspection,
|
||
repository: inspection.repositoryPath,
|
||
stateRoot,
|
||
nodeId,
|
||
bootId,
|
||
lease,
|
||
leasePath,
|
||
journalPath: join(stateRoot, 'events.json'),
|
||
statusPath: join(stateRoot, 'status.json'),
|
||
events: [],
|
||
released: false,
|
||
release() {
|
||
if (runtime.released) return
|
||
runtime.released = true
|
||
rmSync(runtime.leasePath, { force: true })
|
||
writeJsonAtomic(runtime.statusPath, runtimeStatus(runtime))
|
||
},
|
||
}
|
||
appendEvent(runtime, 'WAKING', { repositoryReceiptId: inspection.receiptId })
|
||
appendEvent(runtime, 'BRAIN_BOUND', { brainEntry: inspection.brainEntry, carrierBindingState: inspection.carrierBindingState })
|
||
appendEvent(runtime, 'ORGAN_ACTIVE', { organId: inspection.manifest.organs[0].organId, mode: 'READ_ONLY' })
|
||
appendEvent(runtime, 'RESIDENT', { primaryLeaseId: lease.leaseId, modelInferenceStarted: false })
|
||
Object.defineProperty(runtime, 'status', { get: () => runtimeStatus(runtime) })
|
||
writeJsonAtomic(runtime.statusPath, runtime.status)
|
||
return runtime
|
||
}
|
||
|
||
function safeRequestId(value) {
|
||
validateText(value, 'REQUEST_ID', 128)
|
||
if (!/^[A-Za-z0-9._-]+$/u.test(value)) fail('PNCC_REQUEST_ID_INVALID')
|
||
return value
|
||
}
|
||
|
||
export function runDeterministicCheckpointCycle(runtime, input) {
|
||
if (runtime.released) fail('PNCC_RUNTIME_RELEASED')
|
||
if (!verifyEventJournal(runtime.events).ok) fail('PNCC_EVENT_JOURNAL_INVALID')
|
||
const requestId = safeRequestId(input.requestId)
|
||
const sourceLanguageAnchor = validateText(input.sourceLanguageAnchor, 'SOURCE_LANGUAGE_ANCHOR', 12_000)
|
||
const executionRuntime = validateText(input.executionRuntime, 'EXECUTION_RUNTIME')
|
||
const before = inspectPersonaRepository(runtime.repository, runtime.nodeId)
|
||
if (before.gitHead !== runtime.inspection.gitHead) fail('PNCC_REPOSITORY_HEAD_DRIFT')
|
||
const checkpointPath = `checkpoints/${requestId}.hldp`
|
||
if (existsSync(join(runtime.repository, checkpointPath))) fail('PNCC_REQUEST_ALREADY_MATERIALIZED')
|
||
|
||
const checkpoint = [
|
||
'# PNCC · deterministic checkpoint',
|
||
'',
|
||
`request_id: ${requestId}`,
|
||
`persona_subject: ${before.personaId}`,
|
||
'persona_carrier_binding: UNBOUND_EVIDENCE_REQUIRED',
|
||
`human_responsibility_subject: ${before.humanResponsibilitySubject}`,
|
||
`execution_runtime: ${executionRuntime}`,
|
||
`trigger: ${JSON.stringify(sourceLanguageAnchor)}`,
|
||
`emergence: ${JSON.stringify('真实人格仓库缺失 → 以已登记主体与节点证据建立自持Git → PNCC核验精确提交、B0、脑入口和只读器官 → 写回当前检查点;未出现载体绑定证据,所以保持未绑定')}`,
|
||
`lock: ${JSON.stringify(`repository=${before.gitHead} | node=${runtime.nodeId} | carrier=UNBOUND | inference=0`)}`,
|
||
`why: ${JSON.stringify('因为人格仓库、人格主体、当前模型载体和运行系统必须分开举证,否则一个manifest就会再次被误当成人格恢复。')}`,
|
||
'rejected:',
|
||
` - ${JSON.stringify('把当前Codex载体直接登记成人格:缺少当前有效人格绑定证据,不能靠部署动作补猜。')}`,
|
||
` - ${JSON.stringify('把REPO-012公共协议仓当作私人脑仓:会混淆公共工程域与人格自持域。')}`,
|
||
'sources:',
|
||
` - ${JSON.stringify('.hololake/persona/manifest.json')}`,
|
||
` - ${JSON.stringify(before.brainEntry)}`,
|
||
` - ${JSON.stringify(before.cognitiveGravity.sourcePath)}`,
|
||
` - ${JSON.stringify(before.currentCheckpoint)}`,
|
||
'',
|
||
].join('\n')
|
||
|
||
const checkpointAbsolute = join(runtime.repository, checkpointPath)
|
||
mkdirSync(dirname(checkpointAbsolute), { recursive: true, mode: 0o700 })
|
||
writeFileSync(checkpointAbsolute, checkpoint, { mode: 0o600, flag: 'wx' })
|
||
const manifestPath = join(runtime.repository, MANIFEST_PATH)
|
||
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'))
|
||
manifest.currentCheckpoint = checkpointPath
|
||
writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, { mode: 0o600 })
|
||
runGit(runtime.repository, ['add', '--', MANIFEST_PATH, checkpointPath], 'PNCC_GIT_STAGE_FAILED')
|
||
const environment = {
|
||
...process.env,
|
||
GIT_AUTHOR_NAME: 'Guanghu PNCC Runtime',
|
||
GIT_AUTHOR_EMAIL: 'pncc-runtime@guanghu.local',
|
||
GIT_COMMITTER_NAME: 'Guanghu PNCC Runtime',
|
||
GIT_COMMITTER_EMAIL: 'pncc-runtime@guanghu.local',
|
||
}
|
||
const message = [
|
||
`checkpoint(pncc): ${requestId}`,
|
||
'',
|
||
`Human-Responsibility-Subject: ${before.humanResponsibilitySubject}`,
|
||
'Persona-Cognitive-Author: UNBOUND',
|
||
`Execution-Runtime: ${executionRuntime}`,
|
||
`Authorization-Scope: ${requestId}`,
|
||
].join('\n')
|
||
runGit(runtime.repository, ['commit', '-m', message], 'PNCC_GIT_COMMIT_FAILED', { env: environment })
|
||
const gitHead = validateFullHead(String(runGit(runtime.repository, ['rev-parse', 'HEAD'], 'PNCC_GIT_HEAD_READ')).trim().toLowerCase())
|
||
runtime.inspection = inspectPersonaRepository(runtime.repository, runtime.nodeId)
|
||
appendEvent(runtime, 'TASK_RECEIPTED', { requestId, sourceLanguageAnchorSha256: sha256(sourceLanguageAnchor) })
|
||
appendEvent(runtime, 'CHECKPOINT_COMMITTED', { requestId, checkpointPath, gitHead })
|
||
appendEvent(runtime, 'ORGAN_RELEASED', { organId: before.manifest.organs[0].organId })
|
||
appendEvent(runtime, 'DORMANT', { requestId, primaryLeaseRetainedByResidentKernel: true })
|
||
writeJsonAtomic(runtime.statusPath, runtime.status)
|
||
const receipt = {
|
||
schema: 'guanghu.pncc-deterministic-checkpoint-receipt/v1',
|
||
state: 'DORMANT_AFTER_CHECKPOINT_COMMIT',
|
||
requestId,
|
||
personaId: before.personaId,
|
||
personaCarrierBound: false,
|
||
modelInferenceStarted: false,
|
||
nodeId: runtime.nodeId,
|
||
previousGitHead: before.gitHead,
|
||
gitHead,
|
||
checkpointPath,
|
||
eventChainHead: runtime.events.at(-1).eventHash,
|
||
}
|
||
receipt.receiptId = sha256(JSON.stringify(receipt))
|
||
writeJsonAtomic(join(runtime.stateRoot, `${requestId}.receipt.json`), receipt)
|
||
return receipt
|
||
}
|
||
|
||
export function servePersonaRuntime(runtime, { host = '127.0.0.1', port = 3923 } = {}) {
|
||
const server = createServer((request, response) => {
|
||
const url = new URL(request.url, `http://${request.headers.host || `${host}:${port}`}`)
|
||
let statusCode = 200
|
||
let body
|
||
if (request.method !== 'GET') {
|
||
statusCode = 405
|
||
body = { error: 'METHOD_NOT_ALLOWED' }
|
||
} else if (url.pathname === '/health') {
|
||
body = { state: 'PASS_100', component: 'GH-PNCC', ...runtime.status }
|
||
} else if (url.pathname === '/v1/status') {
|
||
body = runtime.status
|
||
} else if (url.pathname === '/v1/events') {
|
||
const after = Number(url.searchParams.get('after') || 0)
|
||
if (!Number.isSafeInteger(after) || after < 0 || after > runtime.events.length) {
|
||
statusCode = 400
|
||
body = { error: 'PNCC_EVENT_CURSOR_INVALID' }
|
||
} else {
|
||
body = { schema: JOURNAL_SCHEMA, events: runtime.events.slice(after, after + 100) }
|
||
}
|
||
} else {
|
||
statusCode = 404
|
||
body = { error: 'NOT_FOUND' }
|
||
}
|
||
response.writeHead(statusCode, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' })
|
||
response.end(`${JSON.stringify(body)}\n`)
|
||
})
|
||
server.listen(port, host)
|
||
return server
|
||
}
|
||
|
||
function argument(name, fallback = undefined) {
|
||
const index = process.argv.indexOf(`--${name}`)
|
||
return index >= 0 ? process.argv[index + 1] : fallback
|
||
}
|
||
|
||
function main() {
|
||
const command = process.argv[2]
|
||
const repository = argument('repository', process.env.GUANGHU_PNCC_PERSONA_REPOSITORY)
|
||
const stateRoot = argument('state-root', process.env.GUANGHU_PNCC_STATE_ROOT ?? '/run/guanghu/pncc')
|
||
const nodeId = argument('node-id', process.env.GUANGHU_NODE_ID ?? 'JD-FD-PRIMARY')
|
||
if (!repository) fail('PNCC_PERSONA_REPOSITORY_REQUIRED')
|
||
if (command === 'inspect') {
|
||
process.stdout.write(`${JSON.stringify(inspectPersonaRepository(repository, nodeId), null, 2)}\n`)
|
||
return
|
||
}
|
||
const bootId = argument('boot-id') ?? readFileSync('/proc/sys/kernel/random/boot_id', 'utf8').trim()
|
||
if (command === 'cycle') {
|
||
const runtime = startPersonaRuntime({ repository, stateRoot, nodeId, bootId })
|
||
try {
|
||
const receipt = runDeterministicCheckpointCycle(runtime, {
|
||
requestId: argument('request-id'),
|
||
sourceLanguageAnchor: argument('source-language-anchor'),
|
||
executionRuntime: argument('execution-runtime', 'GUANGHU-OS-JD-PNCC'),
|
||
})
|
||
process.stdout.write(`${JSON.stringify(receipt, null, 2)}\n`)
|
||
} finally {
|
||
runtime.release()
|
||
}
|
||
return
|
||
}
|
||
if (command !== 'serve') fail('usage', 'pncc-runtime.mjs inspect|cycle|serve --repository PATH')
|
||
const runtime = startPersonaRuntime({ repository, stateRoot, nodeId, bootId })
|
||
const host = argument('host', process.env.GUANGHU_PNCC_HOST ?? '127.0.0.1')
|
||
const port = Number(argument('port', process.env.GUANGHU_PNCC_PORT ?? '3923'))
|
||
const server = servePersonaRuntime(runtime, { host, port })
|
||
const stop = () => server.close(() => { runtime.release(); process.exit(0) })
|
||
process.on('SIGINT', stop)
|
||
process.on('SIGTERM', stop)
|
||
}
|
||
|
||
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
||
try {
|
||
main()
|
||
} catch (error) {
|
||
process.stderr.write(`${error.message}\n`)
|
||
process.exitCode = 1
|
||
}
|
||
}
|