shennian 0.5.38 → 0.5.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1 +1 @@
|
|
|
1
|
-
import t from"node:fs";import
|
|
1
|
+
import t from"node:fs";import D from"node:os";import o from"node:path";import{createHash as E,randomUUID as O}from"node:crypto";import{getShennianDir as a}from"../config/index.js";import{assertPathsSeparate as N}from"../steward/workspace.js";const j=e=>{if(typeof e!="string"||!/^[\w-]{1,100}$/.test(e))throw new Error("recording_invalid_id");return e},S=e=>E("sha256").update(e).digest("hex"),y=e=>{if(!t.existsSync(e))return null;if(t.lstatSync(e).isSymbolicLink())return r("path_forbidden");const n=t.openSync(e,t.constants.O_RDONLY|(t.constants.O_NOFOLLOW??0));try{return t.fstatSync(n).isFile()?t.readFileSync(n):r("path_forbidden")}finally{t.closeSync(n)}},r=e=>{throw new Error(`recording_${e}`)},_=()=>o.join(a(),"personal-recordings.json");let m=null;const z=()=>{if(process.env.SHENNIAN_RECORDINGS_DIR)return process.env.SHENNIAN_RECORDINGS_DIR;if(m)return m;try{const e=JSON.parse(t.readFileSync(_(),"utf8"));return typeof e.directory!="string"||!o.isAbsolute(e.directory)?r("directory_invalid"):(m=e.directory,m)}catch(e){if(e.code==="ENOENT")return m=o.join(D.homedir(),"shennian_recordings"),m;throw e}};function H(){return k()}function M(e){if(process.env.SHENNIAN_RECORDINGS_DIR)return r("directory_env_override");if(!o.isAbsolute(e)||/[\u0000-\u001f]/.test(e))return r("directory_invalid");const n=o.resolve(e);N(n,a());const c=process.env.SHENNIAN_RECORDINGS_DIR;try{process.env.SHENNIAN_RECORDINGS_DIR=n,k()}finally{c===void 0?delete process.env.SHENNIAN_RECORDINGS_DIR:process.env.SHENNIAN_RECORDINGS_DIR=c}t.mkdirSync(a(),{recursive:!0,mode:448});const s=`${_()}.${O()}.tmp`;try{t.writeFileSync(s,JSON.stringify({directory:n}),{mode:384,flag:"wx"}),t.renameSync(s,_())}finally{try{t.unlinkSync(s)}catch{}}return n}function k(){const e=z();if(!o.isAbsolute(e))return r("directory_invalid");const n=o.resolve(e);N(n,a());let c=n;for(;;){try{if(t.lstatSync(c).isSymbolicLink())return r("path_forbidden")}catch(l){if(l.code!=="ENOENT")throw l}const f=o.dirname(c);if(f===c)break;c=f}t.mkdirSync(n,{recursive:!0,mode:448});const s=t.realpathSync(n),u=t.statSync(s);if(!u.isDirectory())return r("directory_invalid");if(process.platform!=="win32"){if(typeof process.getuid=="function"&&u.uid!==process.getuid())return r("directory_owner");u.mode&63&&t.chmodSync(s,448)}return N(s,t.existsSync(a())?t.realpathSync(a()):a()),s}function P(e){let n=k();for(const c of e){n=o.join(n,j(c));let s=null;try{s=t.lstatSync(n)}catch(f){if(f.code!=="ENOENT")throw f}if(s?.isSymbolicLink())return r("path_forbidden");s||t.mkdirSync(n,{mode:448});const u=t.lstatSync(n);if(!u.isDirectory())return r("path_forbidden");if(process.platform!=="win32"){if(typeof process.getuid=="function"&&u.uid!==process.getuid())return r("directory_owner");u.mode&63&&t.chmodSync(n,448)}}return n}function I(e,n,c){const s=o.join(e,`${O()}.tmp`);try{const u=t.openSync(s,"wx",384);try{t.writeFileSync(u,c),t.fsyncSync(u)}finally{t.closeSync(u)}if(t.renameSync(s,o.join(e,n)),process.platform!=="win32"){const f=t.openSync(e,"r");try{t.fsyncSync(f)}finally{t.closeSync(f)}}}finally{try{t.unlinkSync(s)}catch{}}}function G(e){try{if(!e||!["chunk","commit"].includes(e.action)||typeof e.initialMachineId!="string"||!/^[\w-]{1,100}$/.test(e.initialMachineId)||!Number.isSafeInteger(e.initialPrimaryRevision)||e.initialPrimaryRevision<0||!(e.confirmedPrimaryRevision===null||Number.isSafeInteger(e.confirmedPrimaryRevision)&&e.confirmedPrimaryRevision>=0))return r("invalid_request");const n=P([e.userId,e.sessionKey,e.batchKey]),c=o.join(n,"manifest.json");if(e.action==="chunk"){if(!Number.isInteger(e.index)||e.index<0||e.index>=400||typeof e.base64!="string"||e.base64.length>18e4||!/^[A-Za-z0-9+/]*={0,2}$/.test(e.base64)||!Number.isInteger(e.size)||e.size<1||e.size>128e3||!/^[a-f0-9]{64}$/.test(e.sha256??""))return r("invalid_chunk");const i=Buffer.from(e.base64,"base64");if(i.length!==e.size||S(i)!==e.sha256)return r("chunk_mismatch");const d=`${e.index}.chunk`,p=o.join(n,d),v=y(p);if(v){if(!v.equals(i))return r("chunk_conflict")}else{const b=y(c);if(b){const h=JSON.parse(b.toString("utf8")),g=y(o.join(n,"audio.pcm"));return h.initialMachineId!==e.initialMachineId||h.initialPrimaryRevision!==e.initialPrimaryRevision||h.chunks[e.index]?.size!==e.size||h.chunks[e.index]?.sha256!==e.sha256||!g||g.length!==h.audioSize||S(g)!==h.audioSha256?r("chunk_conflict"):{persisted:!0,complete:!0}}I(n,d,i)}return{persisted:!0,complete:t.existsSync(c)}}if(!Array.isArray(e.chunks)||e.chunks.length<1||e.chunks.length>400||!Array.isArray(e.transcript)||e.transcript.length>1e3||JSON.stringify(e.transcript).length>2e5||typeof e.startedAt!="string"||typeof e.endedAt!="string"||e.chunks.some(i=>!Number.isInteger(i.size)||i.size<1||i.size>128e3||!/^[a-f0-9]{64}$/.test(i.sha256)))return r("invalid_request");const s=e.transcript;if(s.some(i=>!i||typeof i!="object"||typeof i.text!="string"))return r("invalid_request");const u=[];for(let i=0;i<e.chunks.length;i++){const d=y(o.join(n,`${i}.chunk`));if(d)u.push(d);else if(!t.existsSync(c))return r("missing_chunk")}const f=u.length===e.chunks.length?Buffer.concat(u):null,l={sessionKey:e.sessionKey,batchKey:e.batchKey,initialMachineId:e.initialMachineId,initialPrimaryRevision:e.initialPrimaryRevision,startedAt:e.startedAt,endedAt:e.endedAt,audioFormat:{encoding:"pcm16le",sampleRate:16e3,channels:1},chunks:e.chunks,audioSize:f?.length??e.chunks.reduce((i,d)=>i+d.size,0),audioSha256:f?S(f):null,transcript:s},A=JSON.stringify(l),R=y(c);if(R){const i=JSON.parse(R.toString("utf8")),d=y(o.join(n,"audio.pcm"));return i.sessionKey!==l.sessionKey||i.batchKey!==l.batchKey||i.initialMachineId!==l.initialMachineId||i.initialPrimaryRevision!==l.initialPrimaryRevision||i.startedAt!==l.startedAt||i.endedAt!==l.endedAt||JSON.stringify(i.chunks)!==JSON.stringify(l.chunks)||JSON.stringify(i.transcript)!==JSON.stringify(l.transcript)||!d||d.length!==i.audioSize||S(d)!==i.audioSha256?r("batch_conflict"):{persisted:!0,complete:!0}}for(let i=0;i<e.chunks.length;i++){const d=o.join(n,`${i}.chunk`),p=y(d);if(!p)return r("missing_chunk");if(p.length!==e.chunks[i].size||S(p)!==e.chunks[i].sha256)return r("chunk_mismatch")}if(!f)return r("missing_chunk");I(n,"audio.pcm",f),I(n,"manifest.json",Buffer.from(A));for(let i=0;i<e.chunks.length;i++)t.unlinkSync(o.join(n,`${i}.chunk`));return{persisted:!0,complete:!0}}catch(n){if(["ENOSPC","EDQUOT"].includes(n.code??""))return r("storage_full");throw n}}export{M as configureRecordingDirectory,G as receivePersonalRecording,H as recordingDirectory};
|