38 lines
4 KiB
JavaScript
38 lines
4 KiB
JavaScript
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import crypto from 'node:crypto';
|
|
|
|
const req=(value,code)=>{if(!value)throw Error(code);};
|
|
const stable=value=>JSON.stringify(value,Object.keys(value||{}).sort());
|
|
const sha=value=>crypto.createHash('sha256').update(value).digest('hex');
|
|
const atomic=(target,value)=>{fs.mkdirSync(path.dirname(target),{recursive:true,mode:0o700});const temp=`${target}.${crypto.randomUUID()}.tmp`;fs.writeFileSync(temp,JSON.stringify(value,null,2)+'\n',{mode:0o600});fs.renameSync(temp,target);};
|
|
const fileReceipt=target=>{const stat=fs.lstatSync(target);req(stat.isFile()&&!stat.isSymbolicLink(),'ORGAN_SOURCE_REGULAR_FILE_REQUIRED');return{path:target,bytes:stat.size,sha256:sha(fs.readFileSync(target))};};
|
|
|
|
export class PersonaOrganReflex {
|
|
constructor({root,personaId,shelf,sourcePaths,now=()=>new Date()}){
|
|
this.personaId=personaId;this.shelf=shelf;this.sourcePaths=sourcePaths;this.now=now;this.root=path.join(root,'personas',personaId,'organs');
|
|
fs.mkdirSync(this.root,{recursive:true,mode:0o700});
|
|
}
|
|
currentPath(){return path.join(this.root,'CURRENT.signed.json');}
|
|
current(){const artifact=JSON.parse(fs.readFileSync(this.currentPath(),'utf8')),value=this.shelf.verify(artifact);req(value.persona_id===this.personaId,'ORGAN_RECEIPT_PERSONA_MISMATCH');return{artifact,value};}
|
|
sourceEye(){return this.sourcePaths.map(fileReceipt);}
|
|
selfTest(){
|
|
const sequence=fs.existsSync(this.currentPath())?this.current().value.sequence+1:1,started=this.now().toISOString(),sources=this.sourceEye();
|
|
const intent={schema:'guanghu.persona-organ-intent/v1',persona_id:this.personaId,sequence,motion:'CLOSE_AND_OPEN',source_eye_sha256:sha(Buffer.from(JSON.stringify(sources))),issued_at:started};
|
|
const motorPath=path.join(this.root,'motor-sense.json'),pre={exists:fs.existsSync(motorPath),sha256:fs.existsSync(motorPath)?sha(fs.readFileSync(motorPath)):null};
|
|
const motion={schema:'guanghu.persona-native-hand-motion/v1',owner:this.personaId,sequence,intent_sha256:sha(Buffer.from(JSON.stringify(intent))),motion:'CLOSE_AND_OPEN'};
|
|
atomic(motorPath,motion);const sensed=JSON.parse(fs.readFileSync(motorPath,'utf8')),actionSense=JSON.stringify(sensed)===JSON.stringify(motion),post=fileReceipt(motorPath);
|
|
const eyeVerified=post.sha256===sha(fs.readFileSync(motorPath))&&sensed.owner===this.personaId&&sensed.intent_sha256===motion.intent_sha256;
|
|
const value={schema:'guanghu.persona-native-organ-reflex/v1',persona_id:this.personaId,sequence,
|
|
state:actionSense&&eyeVerified?'NATIVE_ORGAN_REFLEX_HEALTHY_AWAITING_PARENT_BRAIN_WAKE_CYCLE':'NATIVE_ORGAN_PAIN_ALARM',
|
|
stages:[{stage:'BRAIN_SELF_TEST_INTENT',seen:true},{stage:'SOURCE_EYE_MAP',seen:sources.length>0},{stage:'NATIVE_HAND_MOTION',seen:true},{stage:'ACTION_SENSE',seen:actionSense},{stage:'POST_EYE_VERIFY',seen:eyeVerified},{stage:'RETURN_TO_PARENT_BRAIN',seen:true}],
|
|
source_eye:sources,motor_pre:pre,motor_post:post,action_sense_received:actionSense,post_eye_verified:eyeVerified,parent_brain_final_verdict_required:true,
|
|
shared_organ_code_is_shared_persona_state:false,external_cognition_setter:false,external_toolbox_grasped:false,persona_wake_complete:false,completed_at:this.now().toISOString()};
|
|
const artifact=this.shelf.sign(value);atomic(path.join(this.root,'attempts',String(sequence).padStart(6,'0')+'.signed.json'),artifact);atomic(this.currentPath(),artifact);return value;
|
|
}
|
|
status(){
|
|
try{const current=this.current(),nowSources=this.sourceEye(),drift=nowSources.filter((item,index)=>item.sha256!==current.value.source_eye[index]?.sha256).map(item=>item.path);
|
|
return{...current.value,state:drift.length?'NATIVE_ORGAN_PAIN_ALARM':'NATIVE_ORGAN_REFLEX_HEALTHY_AWAITING_PARENT_BRAIN_WAKE_CYCLE',source_drift:drift,server_signed:true,signed_current_sha256:current.artifact.sha256};
|
|
}catch(error){return{schema:'guanghu.persona-native-organ-reflex/v1',persona_id:this.personaId,state:'NATIVE_ORGAN_PAIN_ALARM',error:String(error.message||error),server_signed:false,persona_wake_complete:false};}
|
|
}
|
|
}
|