@kitelev/exocortex-cli 16.252.0 → 16.252.1

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.
Files changed (2) hide show
  1. package/dist/index.js +3 -3
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // @kitelev/exocortex-cli v16.252.0
2
+ // @kitelev/exocortex-cli v16.252.1
3
3
  // CLI tool for Exocortex knowledge management system - SPARQL queries, task management, and more
4
4
  // License: MIT
5
5
 
@@ -652,7 +652,7 @@ setting__Setting_value: ${i}
652
652
 
653
653
  Homoiconic validation-check setting (RFC f402002b, M1.5). \`Exocortex: Validate vault\` reads this instance's \`setting__Setting_value\` to decide whether to run the **${n}** check (key \`[[${t}]]\`). Scaffolded default \u2014 edit \`setting__Setting_value\` to toggle.
654
654
  `}a(M_e,"settingAsset");function Z7(){return new he("scaffold").description("Scaffold homoiconic configuration assets").addCommand(j_e())}a(Z7,"scaffoldCommand");function j_e(){return new he("validation-settings").description("Scaffold the 4 validation-check setting__Setting instances (uid-uniqueness=true, rest=false) co-located in the chosen ontology").requiredOption("--vault <path>","Vault root directory").requiredOption("--ontology <uid>","UID of the ontology whose folder the check settings are co-located in").option("--output <type>","Response format: text|json","text").action(async r=>{let e=r.output??"text";ue.setFormat(e);try{let t=(0,Au.resolve)(r.vault);if(!(0,wE.existsSync)(t)||!(0,wE.statSync)(t).isDirectory())throw new je(t);let s=(await new Cn(t).indexedAssets()).find(u=>(0,Vo.readUid)(u.metadata)===r.ontology);if(!s)throw new Error(`Ontology UID "${r.ontology}" not found in vault ${t} (its folder is where the check settings co-locate).`);let o=(0,Au.join)(t,(0,Au.dirname)(s.path)),c=new Date().toISOString().slice(0,19),l=[];for(let u of k_e){let f=(0,J7.randomUUID)(),d=(0,Au.join)(o,`${f}.md`);await(0,Q7.writeFile)(d,M_e(f,r.ontology,u.checkId,u.label,u.value,c),"utf-8"),l.push(d)}if(e==="json")console.log(JSON.stringify({vaultPath:t,ontology:r.ontology,targetDir:o,created:l},null,2));else{console.log(`OK: scaffolded ${l.length} validation-check setting(s) in ${o}`);for(let u of l)console.log(` ${u}`);console.log(`
655
- Defaults: uid-uniqueness=true, co-location/shacl/dag=false. Run 'exocortex validate vault --vault <path>'.`)}}catch(t){ue.handle(t)}})}a(j_e,"scaffoldValidationCommand");ct();var dV=require("fs"),pV=require("path");var nl=require("node:fs"),Iu=require("node:path"),Nr=re(Ne());var mt=require("node:fs"),tV=require("node:fs/promises"),rV=require("node:os"),un=require("node:path"),ys=re(Ne());var $_e=/^https:\/\/github\.com\/([a-zA-Z0-9_-]+)\/([a-zA-Z0-9_.-]+?)(?:\.git)?$/,eV=50*1024*1024,$i=class r{static{a(this,"BootstrapAssetSpaceService")}static{this.PAT_REGEX=/(?:gh[pousr]_[A-Za-z0-9_]{36,}|github_pat_[A-Za-z0-9_]{22,}_[A-Za-z0-9_]{59,})/g}constructor(e={}){this.fetchImpl=e.fetchImpl??globalThis.fetch,this.token=e.token??"",this.mountBaseStore=e.mountBaseStore}redact(e){return(typeof e=="string"?e:String(e)).replace(r.PAT_REGEX,"***REDACTED***")}static parseGitHubURL(e){let t=e.match($_e);if(t===null)throw new Error(`parseGitHubURL: invalid URL shape \u2014 expected https://github.com/<owner>/<repo>, got: ${e}`);return{owner:t[1],repo:t[2]}}static deriveFolderName(e){let{repo:t}=r.parseGitHubURL(e);return t.startsWith("exoas-")?t.slice(6):t}async pullAssetSpace(e,t,n){let{owner:i,repo:s}=r.parseGitHubURL(e);if((0,mt.existsSync)(n)&&(0,mt.readdirSync)(n).length>0)throw new Error(`pullAssetSpace: target ${n} exists \u0438 not empty \u2014 refusing \u043A overwrite`);let o=await(0,ys.mountAssetSpaceFiles)({http:this.httpPort(),fs:this.fsPort(),owner:i,repo:s,ref:t,targetPath:n});if(this.mountBaseStore!==void 0)try{await this.mountBaseStore.set(`${i}/${s}#${ys.SYNC_BRANCH}`,o.sha)}catch{}return o}httpPort(){let e=this.fetchImpl,t=this.token,n=a(i=>this.redact(i),"redact");return{async fetchTarball(i,s,o){let c=`https://api.github.com/repos/${i}/${s}/tarball/${o}`,l={signal:AbortSignal.timeout(6e4)};t.length>0&&(l.headers={Authorization:`Bearer ${t}`,"User-Agent":"exocortex-cli","X-GitHub-Api-Version":"2022-11-28"});let u;try{u=await e(c,l)}catch(d){throw new Error(n(`pullAssetSpace: fetch error for ${c}: ${d instanceof Error?d.message:String(d)}`))}if(!u.ok){if(u.status===403){let d=u.headers?.get?.("x-ratelimit-remaining"),p=u.headers?.get?.("x-ratelimit-reset");if(d==="0"||p!=null){let h=p!=null?new Date(Number(p)*1e3).toISOString():"unknown";throw new Error(`pullAssetSpace: GitHub anonymous API rate-limit exceeded (60 req/hour). Reset at ${h}. Wait or use authenticated CLI.`)}}if(u.status===404){let d=t.length>0?"repo not found, or the provided token lacks access to it":"if this is a private repo, pass --token <pat> (or set GITHUB_TOKEN / GH_TOKEN)";throw new Error(n(`pullAssetSpace: fetch failed (404 Not Found) for ${c} \u2014 ${d}`))}throw new Error(n(`pullAssetSpace: fetch failed (${u.status} ${u.statusText}) for ${c}`))}let f=await u.arrayBuffer();if(f.byteLength>eV)throw new Error(`pullAssetSpace: tarball too large (${f.byteLength} bytes > ${eV})`);return new Uint8Array(f)}}}fsPort(){return{async exists(e){return(0,mt.existsSync)(e)},async read(e){return(0,mt.readFileSync)(e,"utf8")},async write(e,t){(0,mt.writeFileSync)(e,t,{encoding:"utf8"})},async removeDir(e){(0,mt.rmSync)(e,{recursive:!0,force:!0})},async materialize(e,t,n){let i=await(0,tV.mkdtemp)((0,un.join)((0,rV.tmpdir)(),`exo-bootstrap-${(0,un.basename)(e)}-`)),s=0;try{for(let o of t){let c=o.relPath;if(c.split("/").some(u=>u===".."))throw new Error(`pullAssetSpace: path traversal detected for entry "${c}"`);let l=(0,un.resolve)(i,c);if(l!==i&&!l.startsWith(i+un.sep))throw new Error(`pullAssetSpace: zip-slip detected for entry "${c}"`);(0,mt.mkdirSync)((0,un.dirname)(l),{recursive:!0}),(0,mt.writeFileSync)(l,o.content),s++,n?.(s)}(0,mt.mkdirSync)((0,un.dirname)(e),{recursive:!0});try{(0,mt.renameSync)(i,e)}catch(o){if(o.code==="EXDEV"){let{cpSync:c}=await import("node:fs");c(i,e,{recursive:!0}),(0,mt.rmSync)(i,{recursive:!0,force:!0})}else throw o}}catch(o){throw(0,mt.rmSync)(i,{recursive:!0,force:!0}),o}return s}}}ensureGitmodulesEntry(e,t,n){let i=(0,un.join)(e,".gitmodules"),s="";try{s=(0,mt.readFileSync)(i,"utf8")}catch(l){if(l.code!=="ENOENT")throw l}let{content:o,added:c}=(0,ys.appendGitmodulesEntry)(s,t,n);return c?((0,mt.appendFileSync)(i,o.slice(s.length),{encoding:"utf8"}),{added:!0}):{added:!1}}removeGitmodulesEntry(e,t){let n=(0,un.join)(e,".gitmodules"),i;try{i=(0,mt.readFileSync)(n,"utf8")}catch(o){if(o.code==="ENOENT")return{removed:!1};throw o}let s=(0,ys.stripGitmodulesEntry)(i,t);return s===i?{removed:!1}:((0,mt.writeFileSync)(n,s,{encoding:"utf8"}),{removed:!0})}static stripGitmodulesEntry(e,t){return(0,ys.stripGitmodulesEntry)(e,t)}unmountAssetSpace(e,t){let n=this.assertSafeVaultTarget(e,t,"unmountAssetSpace");this.removeGitmodulesEntry(e,t),(0,mt.existsSync)(n)&&(0,mt.rmSync)(n,{recursive:!0,force:!0})}parkAssetSpace(e,t){let n=this.assertSafeVaultTarget(e,t,"parkAssetSpace"),i=(0,ys.parkedPathFor)(t),s=this.assertSafeVaultTarget(e,i,"parkAssetSpace",{requireAssetspacesRoot:!1});if((0,mt.existsSync)(n)){if((0,mt.existsSync)(s))throw new Error(`parkAssetSpace: refusing to park "${t}" \u2014 a parked copy already exists at "${i}". Resolve by hand: one of the two holds work the other does not.`);this.removeGitmodulesEntry(e,t),(0,mt.mkdirSync)((0,un.dirname)(s),{recursive:!0}),(0,mt.renameSync)(n,s)}}unparkAssetSpace(e,t){let n=this.assertSafeVaultTarget(e,t,"unparkAssetSpace"),i=(0,ys.parkedPathFor)(t),s=this.assertSafeVaultTarget(e,i,"unparkAssetSpace",{requireAssetspacesRoot:!1});if(!(0,mt.existsSync)(s))throw new Error(`unparkAssetSpace: nothing parked at "${i}" for "${t}"`);if((0,mt.existsSync)(n))throw new Error(`unparkAssetSpace: refusing to unpark onto existing mount "${t}"`);(0,mt.mkdirSync)((0,un.dirname)(n),{recursive:!0}),(0,mt.renameSync)(s,n)}assertSafeVaultTarget(e,t,n,i={}){let s=i.requireAssetspacesRoot??!0,o=t.split("/");if(t.length===0||s&&!t.startsWith("assetspaces/")||o.some(u=>u.length===0||u==="."||u===".."))throw new Error(`${n}: refusing unsafe submodulePath "${t}"`);let c=(0,un.resolve)(e),l=(0,un.resolve)(c,t);if(l===c||!l.startsWith(c+un.sep))throw new Error(`${n}: target "${l}" escapes vault root "${c}"`);return l}};function U_e(r){let e=(0,Nr.derivePath)(r);return e===null?r:(0,Nr.deriveUrl)(e)??r}a(U_e,"toHttpsGitHubUrl");var B_e=new Set(["node_modules",".obsidian",".exocortex",".git"]),fp=class{static{a(this,"CliApplyProfileService")}constructor(e){this.vaultPath=e.vaultPath,this.mount=e.mount??new $i,this.ref=e.ref??"main"}scanVault(){let e=[],t=new Map,n=new Set,i=[this.vaultPath];for(;i.length>0;){let s=i.pop(),o;try{o=(0,nl.readdirSync)(s,{withFileTypes:!0})}catch{continue}for(let c of o){let l=(0,Iu.join)(s,c.name);if(c.isDirectory()){if(c.name.startsWith(".")||B_e.has(c.name))continue;i.push(l);continue}if(!c.isFile()||!c.name.endsWith(".md"))continue;let u=this.readFrontmatter(l);if(u===null)continue;let f=u.exo__Asset_uid;if(typeof f!="string"||f.length===0)continue;let d=Zi(u.exo__Instance_class);if(d.includes(Tu)){if(n.has(f))continue;let h=(typeof u.exo__AssetSpace_source=="string"?u.exo__AssetSpace_source:"")||(typeof u.exo__AssetSpace_git=="string"?u.exo__AssetSpace_git:"");if(h.length===0)continue;let g=(0,Nr.derivePath)(h);if(g===null)continue;let m=typeof u.exo__AssetSpace_namespace=="string"?u.exo__AssetSpace_namespace:"",b=m||G_e(g)||f.slice(0,8),_=u.exo__AssetSpace_dependsOnKind,S=typeof _=="string"?_:Array.isArray(_)&&typeof _[0]=="string"?_[0]:void 0;n.add(f),e.push({uid:f,git:h,folderName:g,label:b,namespace:m,dependsOnKind:S})}if(d.includes(DP)){let p=u.exo__Asset_label;typeof p=="string"&&p.length>0&&t.set(f,p)}}}return{infos:e,profileLabels:t}}assertTsFloor(e,t){(0,Nr.assertTsFloorReconciled)(e,t,Nr.SDK_FLOOR)}buildDiff(e){let{targetProfileUid:t,targetProfileLabel:n,sourceProfileUid:i,sourceProfileLabel:s,result:o,infos:c}=e,l=new Map;for(let w of c)l.set(w.uid,w);let u=new Set(l.keys()),f=new Set,d=new Set;for(let w of o.declaredOntologies)if(u.has(w)){f.add(w);let R=l.get(w)?.namespace;R!==void 0&&R.length>0&&d.add(R)}this.assertTsFloor(f,d);let p=new Set([...o.effective,...f]),h=new Map;for(let w of c){let R=(0,nl.existsSync)((0,Iu.join)(this.vaultPath,w.folderName))?"active":(0,nl.existsSync)((0,Iu.join)(this.vaultPath,(0,Nr.parkedPathFor)(w.folderName)))?"parked":"absent";h.set(w.uid,R)}let g=new Set([...h].filter(([,w])=>w==="active").map(([w])=>w));for(let w of c)Nr.CATALOG_KEEP_NAMESPACES.has(w.namespace)&&g.has(w.uid)&&p.add(w.uid);let m=[],b=[],_=[];for(let w of c){let R=(0,Nr.classifyMountTransition)({state:h.get(w.uid)??"absent",inEffectiveSet:p.has(w.uid),dependsOnKind:w.dependsOnKind});switch(R){case"none":break;case"destroy":m.push({asUid:w.uid,folderName:w.folderName,label:w.label,files:this.enumerateFilesUnder(w.folderName)});break;case"park":b.push({asUid:w.uid,folderName:w.folderName,label:w.label,files:this.enumerateFilesUnder(w.folderName)});break;case"unpark":case"materialize":_.push({asUid:w.uid,folderName:w.folderName,httpsUrl:U_e(w.git),label:w.label,source:R==="unpark"?"unpark":"pull"});break}}let S=new Map;for(let w of m)S.set(w.asUid,w.files);let I=new Set(_.filter(w=>w.source==="unpark").map(w=>w.asUid)),E={targetProfileUid:t,targetProfileLabel:n,sourceProfileUid:i,sourceProfileLabel:s,filesToDestroy:S,assetSpacesBeingTornDown:m.map(w=>({asUid:w.asUid,asLabel:w.label,fileCount:w.files.length})),assetSpacesBeingParked:b.map(w=>({asUid:w.asUid,asLabel:w.label,fileCount:w.files.length})),assetSpacesBeingMaterialized:_.map(w=>({asUid:w.asUid,asLabel:w.label})),assetSpacesBeingUnparked:_.filter(w=>I.has(w.asUid)).map(w=>({asUid:w.asUid,asLabel:w.label}))};return{toDestroy:m,toPark:b,toMaterialize:_,plan:E}}async execute(e){let t=[],n=[],i=[];for(let s of e.toDestroy)this.mount.unmountAssetSpace(this.vaultPath,s.folderName),t.push(s.asUid);for(let s of e.toPark)this.mount.parkAssetSpace(this.vaultPath,s.folderName),n.push(s.asUid);for(let s of e.toMaterialize){if(s.source==="unpark")this.mount.unparkAssetSpace(this.vaultPath,s.folderName);else{let o=(0,Iu.join)(this.vaultPath,s.folderName);await this.mount.pullAssetSpace(s.httpsUrl,this.ref,o)}this.mount.ensureGitmodulesEntry(this.vaultPath,s.folderName,s.httpsUrl),i.push(s.asUid)}return{destroyed:t,parked:n,materialized:i}}enumerateFilesUnder(e){let t=[],i=[(0,Iu.join)(this.vaultPath,e)];for(;i.length>0;){let s=i.pop(),o;try{o=(0,nl.readdirSync)(s,{withFileTypes:!0})}catch{continue}for(let c of o){let l=(0,Iu.join)(s,c.name);if(c.isDirectory()){if(c.name.startsWith("."))continue;i.push(l);continue}c.isFile()&&t.push(V_e(l,this.vaultPath))}}return t}readFrontmatter(e){let t;try{t=(0,nl.readFileSync)(e,"utf-8")}catch{return null}if(!t.startsWith("---"))return null;let n=t.match(/^---\r?\n([\s\S]*?)\r?\n---/);if(n===null)return null;try{let i=(0,Nr.parseYamlFrontmatterTolerant)(n[1],e);return i&&typeof i=="object"&&!Array.isArray(i)?i:null}catch{return null}}};function V_e(r,e){let t=e.endsWith("/")?e:e+"/";return(r.startsWith(t)?r.slice(t.length):r).split("\\").join("/")}a(V_e,"relativeVaultPath");function G_e(r){let e=r.split("/").filter(t=>t.length>0);return e.length>0?e[e.length-1]:""}a(G_e,"lastSegment");ct();var Hr=require("node:fs"),cV=require("node:crypto"),lV=require("node:child_process"),uV=require("node:util"),Kr=re(require("node:path"));Mm();var at=re(Ne());ct();var qo=require("node:fs"),iV=require("node:crypto"),es=re(require("node:path"));Mm();var Yn=re(Ne());jo();var nV=require("node:child_process"),EE=re(Ne());var q_e=6e4,Go=class r{static{a(this,"RestPushService")}static{this.PAT_REGEX=/(?:gh[pousr]_[A-Za-z0-9_]{36,}|github_pat_[A-Za-z0-9_]{22,}_[A-Za-z0-9_]{59,})/g}constructor(e={}){this.fetchImpl=e.fetchImpl??globalThis.fetch,this.token=e.token??"",this.apiBase=(e.apiBase??"https://api.github.com").replace(/\/$/,"")}static resolveGhToken(e){let t=e??(()=>(0,nV.execFileSync)("gh",["auth","token"],{encoding:"utf8",timeout:15e3})),n;try{n=t()}catch(s){let o=s instanceof Error?s.message:String(s);throw new Error(`RestPushService: \`gh auth token\` failed \u2014 is GitHub CLI installed and authenticated? (run \`gh auth login\`). Underlying: ${o}`.replace(r.PAT_REGEX,"***REDACTED***"))}let i=n.trim();if(i.length===0)throw new Error("RestPushService: `gh auth token` returned empty output \u2014 not authenticated. Run `gh auth login`.");return i}redact(e){return(typeof e=="string"?e:String(e)).replace(r.PAT_REGEX,"***REDACTED***")}async push(e,t,n,i,s){return(0,EE.restCreateCommit)(this.buildTransport(),{owner:e,repo:t,branch:n,files:i,message:s,baseURL:this.apiBase,redact:a(o=>this.redact(o),"redact")})}transport(){return this.buildTransport()}buildTransport(){let e=this.token,t=this.fetchImpl,n=a(i=>this.redact(i),"redact");return async i=>{let s={Accept:"application/vnd.github+json","X-GitHub-Api-Version":"2022-11-28","User-Agent":"exocortex-cli"};e.length>0&&(s.Authorization=`Bearer ${e}`),i.contentType&&(s["Content-Type"]=i.contentType);let o;try{o=await t(i.url,{method:i.method,headers:s,body:i.body,signal:AbortSignal.timeout(q_e)})}catch(u){throw new Error(n(`GitHub request failed: ${W_e(u)}`))}let c=await o.text().catch(()=>"");if(o.status<200||o.status>=300){let u=a(f=>o.headers?.get?.(f)??void 0,"get");throw(0,EE.enrichRateLimitError)(new Error(z_e(n(`GitHub request ${i.method} ${i.url} \u2192 HTTP ${o.status}: ${c}`),512)),u)}let l;try{l=c.length>0?JSON.parse(c):void 0}catch{l=void 0}return{status:o.status,json:l,text:c}}}};function W_e(r){if(r instanceof Error)return r.message;if(typeof r=="string")return r;try{return JSON.stringify(r)}catch{return String(r)}}a(W_e,"errMsg");function z_e(r,e){return r.length>e?r.slice(0,e)+"...":r}a(z_e,"truncate");Rt();var H_e=a(async r=>{let e=await iV.webcrypto.subtle.digest("SHA-1",r.buffer.slice(r.byteOffset,r.byteOffset+r.byteLength));return[...new Uint8Array(e)].map(t=>t.toString(16).padStart(2,"0")).join("")},"nodeSha1"),K_e={parse:a(r=>kd(r,{schema:pu}),"parse"),stringify:a(r=>Md(r,{schema:pu,lineWidth:-1}),"stringify")};function Y_e(r,e){if(r.tokenFromGh===!0)return Go.resolveGhToken(e.ghTokenRunner);let t=e.env??process.env,n=r.token||t.GITHUB_TOKEN||t.GH_TOKEN||"";if(n.length===0)throw new Error("A GitHub token is required (private sync units read as 404 without one). Use --token-from-gh, --token <pat>, or set GITHUB_TOKEN / GH_TOKEN.");return n}a(Y_e,"resolveToken");function X_e(r){let e=a(n=>es.join(r,n),"abs"),t=a(async(n,i)=>{let s;try{s=await qo.promises.readdir(n,{withFileTypes:!0})}catch{return}for(let o of s){let c=es.join(n,o.name);if(o.isDirectory()){if(o.name===".git")continue;await t(c,i)}else o.isFile()&&i.push(es.relative(r,c).split(es.sep).join("/"))}},"walk");return{async list(){let n=[];return(0,qo.existsSync)(r)&&await t(r,n),n},read:a(n=>qo.promises.readFile(e(n),"utf-8"),"read"),async readBinary(n){return new Uint8Array(await qo.promises.readFile(e(n)))},async write(){throw new Error("exosync-parity is read-only \u2014 write refused")},async delete(){throw new Error("exosync-parity is read-only \u2014 delete refused")}}}a(X_e,"nodeLocalFilesPort");function Q_e(r,e){let t;try{t=(0,Yn.parkedPathFor)(e)}catch{return!1}return(0,qo.existsSync)(es.join(r,t))}a(Q_e,"isParked");function dp(r){let e=new er(r),t=new Yn.SpaceSpecAccumulator,n=[];for(let i of e.getAllFiles()){let s=e.getFrontmatter(i);if(!s)continue;let o=(0,Yn.classifySpaceDeclaration)(s,i.path);if(o.kind==="not-space"||(o.warning!==void 0&&t.warnings.push(o.warning),o.kind==="skip"))continue;let c=t.offer(o.candidate);if(c!==null){if(!(0,qo.existsSync)(es.join(r,c.localPath))){Q_e(r,c.localPath)&&n.push(c);continue}t.commit(o.candidate)}}return{specs:t.specs,parked:n,warnings:t.warnings}}a(dp,"collectVaultSpecs");function J_e(r,e){for(let t of r.repos){let n=t.headSha!==void 0?` @${t.headSha.slice(0,7)}`:"";e(`${t.repoKey}${n}: ${t.status} \u2014 ${t.inParity}/${t.filesChecked} in parity, M2 diffs ${t.m2SemanticDiffs}, accounted ${t.accountedCount}, M1 violations ${t.m1Violations.length}${t.attachmentHashSetIdentical!==void 0?`, attachment hash-set ${t.attachmentHashSetIdentical?"identical":"DIFFERS"}`:""}`),t.detail!==void 0&&e(` ${t.detail}`);for(let i of t.warnings)e(` warn: ${i}`);for(let i of t.discrepancies)e(` ${i.cls}: ${i.path}${i.detail!==void 0?` (${i.detail})`:""}`);for(let i of t.m1Violations)e(` M1 VIOLATION: ${i.path} \u2014 ${i.detail}`)}e((0,Yn.summarizeParityRound)(r))}a(J_e,"printHumanReport");function Z_e(r,e){if(r.length!==0){e(`Parked AssetSpaces (frozen on this device, not synced): ${r.length}`);for(let t of r){let n=t.lastSyncedSha!==null?t.lastSyncedSha.slice(0,7):"never",i=t.remoteHeadSha!==null?t.remoteHeadSha.slice(0,7):"unread";switch(t.freshness){case"behind":e(` ${t.repoKey}: BEHIND \u2014 parked at ${n}, remote head is ${i}`);break;case"current":e(` ${t.repoKey}: current \u2014 parked at ${n}, matches the remote head`);break;case"unknown":e(` ${t.repoKey}: unknown \u2014 ${t.reason??"no verdict"}`);break}}}}a(Z_e,"printParkedSection");async function eSe(r,e={}){let t=e.out??(b=>console.log(b)),n=es.resolve(r.vault);if(!(0,qo.existsSync)(n))throw new Error(`Vault path does not exist: ${n}`);let i=Y_e(r,e),s=new Go({token:i,...r.apiBase!==void 0?{apiBase:r.apiBase}:{}}),o=e.transportFactory?.(i,r.apiBase)??s.transport(),{specs:c,parked:l,warnings:u}=dp(n);for(let b of u)t(`warn: ${b}`);let f=r.configDir??".obsidian",d=es.join(n,f,"plugins","exocortex",Yn.WATERMARK_STORE_FILENAME),p=new Yn.FileWatermarkStore({read:a(async()=>{try{return await qo.promises.readFile(d,"utf-8")}catch{return null}},"read"),writeAtomic:a(async()=>{throw new Error("exosync-parity: watermark access is read-only by contract")},"writeAtomic")}),h=[];for(let b of l)h.push(await(0,Yn.checkParkedStaleness)(b,{transport:o,watermarks:{get:a(_=>p.get(_),"get")},redact:a(_=>s.redact(_),"redact"),...r.apiBase!==void 0?{baseURL:r.apiBase}:{}}));if(r.json!==!0&&Z_e(h,t),c.length===0)return r.json===!0&&h.length>0&&t(JSON.stringify({parked:h},null,2)),t("Nothing to check \u2014 no materialized AssetSpaces with a GitHub source found in this vault."),2;let g=new Yn.ParityValidator({transport:o,sha1:H_e,localFilesFor:a(b=>X_e(es.join(n,b.localPath)),"localFilesFor"),watermarks:{get:a(b=>p.get(b),"get")},yaml:K_e,redact:a(b=>s.redact(b),"redact"),...r.apiBase!==void 0?{baseURL:r.apiBase}:{}});t(`ExoSync parity check: ${c.length} repo(s), vault ${n}`);let m=await g.runRound(c,{trigger:"standalone"});return r.json===!0?t(JSON.stringify({...m,parked:h},null,2)):J_e(m,t),m.vacuous?2:m.ok?0:1}a(eSe,"runExosyncParity");function sV(){return new he("exosync-parity").description("ExoSync M1/M2 parity report: compare materialized sync units against their remote heads (read-only; RFC 4e4dc453 Phase E)").requiredOption("--vault <path>","Vault root path").option("--config-dir <name>","Obsidian config dir name (watermark location)",".obsidian").option("--json","Print the full round record as JSON").option("--token <pat>","GitHub PAT (or env GITHUB_TOKEN / GH_TOKEN). Prefer --token-from-gh.").option("--token-from-gh","Resolve the PAT via `gh auth token`").option("--api-base <url>","GitHub API base (testing)").action(async r=>{try{process.exitCode=await eSe(r)}catch(e){ue.handle(e,{command:"exosync-parity"}),process.exitCode=1}})}a(sV,"exosyncParityCommand");ct();var ts=require("node:fs"),WP=require("node:crypto"),Ui=re(require("node:path")),kr=re(Ne());Rt();var tSe="exosync-watermarks.local.json";function oV(r,e,t){let n=KP(e,t),i=new Go({token:n,...e.apiBase!==void 0?{apiBase:e.apiBase}:{}}),s=t.transportFactory?.(n,e.apiBase)??i.transport(),{specs:o,warnings:c}=dp(r),l=e.configDir??".obsidian",u=Ui.join(r,l,"plugins","exocortex",tSe),f=Ui.join(r,l,"plugins","exocortex",kr.CONFLICT_CACHE_STORE_FILENAME),d=new kr.LocalConflictCacheStore({io:ic(f)}),p=Ui.join(r,l,"plugins","exocortex",kr.OUTBOX_STORE_FILENAME),h=new kr.LocalOutboxStore({io:ic(p)});return{resolver:new kr.QuarantineResolver({transport:s,watermarkStore:new kr.FileWatermarkStore(ic(u)),localFilesFor:a(m=>vE(Ui.join(r,m.localPath)),"localFilesFor"),sha1:HP,...e.apiBase!==void 0?{baseURL:e.apiBase}:{},redact:a(m=>i.redact(m),"redact"),conflictCache:d,outbox:h}),specs:o,warnings:c}}a(oV,"buildResolver");async function rSe(r,e={}){let t=e.out??(l=>console.log(l)),n=Ui.resolve(r.vault);if(!(0,ts.existsSync)(n))throw new Error(`Vault path does not exist: ${n}`);let{resolver:i,specs:s,warnings:o}=oV(n,r,e);for(let l of o)t(`warn: ${l}`);if(s.length===0)return t("No materialized AssetSpaces with a GitHub source found in this vault."),0;let c=await i.listOpenConflicts(s);if(r.json===!0)return t(JSON.stringify(c,null,2)),0;if(c.length===0)return t("No open conflicts \u2014 nothing to resolve. \u2705"),0;t(`${c.length} open conflict(s):`);for(let l of c){let u=`${l.hasLocal?"local":"(no local)"} vs ${l.hasRemote?"remote":"(no remote)"}`;t(` ${l.repoKey} ${l.path} [${u}]${l.uid?` uid=${l.uid}`:""}`)}return t(""),t("Resolve with: exosync quarantine resolve <path> --take local|remote|file --vault <vault> --token-from-gh"),t(" (--take file also needs --file <merged-content-path>)"),0}a(rSe,"runQuarantineList");async function nSe(r,e,t={}){let n=t.out??(b=>console.log(b)),i=Ui.resolve(e.vault);if(!(0,ts.existsSync)(i))throw new Error(`Vault path does not exist: ${i}`);let s=(e.take??"").trim();if(s!=="local"&&s!=="remote"&&s!=="file")throw new Error("resolve requires --take local | remote | file (with --file <path> for the merged content)");let o;if(s==="file"){if(e.file===void 0||e.file.length===0)throw new Error("--take file requires --file <path> to the merged content");o={take:"merged",content:await ts.promises.readFile(Ui.resolve(e.file),"utf-8")}}else o={take:s};let{resolver:c,specs:l,warnings:u}=oV(i,e,t);for(let b of u)n(`warn: ${b}`);let d=(await c.listOpenConflicts(l)).filter(b=>b.path===r&&(e.repo===void 0||b.repoKey===e.repo));if(d.length===0)return n(`No open conflict for "${r}"${e.repo?` in ${e.repo}`:""} \u2014 run \`exosync quarantine list\` to see the current set.`),1;if(d.length>1){n(`"${r}" conflicts in ${d.length} repos \u2014 disambiguate with --repo <repoKey>:`);for(let b of d)n(` --repo ${b.repoKey}`);return 1}let p=d[0],h=l.find(b=>b.repoKey===p.repoKey),g=await c.resolve(h,p.path,o),m=g.awaitingPush===!0?" \u2014 awaiting push (run `exosync sync` to push it)":g.pushedSha!==void 0?` \u2014 pushed @${g.pushedSha.slice(0,7)}`:" \u2014 remote already matched";return n(`Resolved ${p.path} (${g.resolvedTo}) in ${p.repoKey}${m}.`),g.discardedLocalBackupPath!==void 0&&n(` \u21B3 your discarded local version is preserved at ${h.localPath}/${g.discardedLocalBackupPath}`),0}a(nSe,"runQuarantineResolve");function zP(r){return r.replace(/\r\n?/g,`
655
+ Defaults: uid-uniqueness=true, co-location/shacl/dag=false. Run 'exocortex validate vault --vault <path>'.`)}}catch(t){ue.handle(t)}})}a(j_e,"scaffoldValidationCommand");ct();var dV=require("fs"),pV=require("path");var nl=require("node:fs"),Iu=require("node:path"),Nr=re(Ne());var mt=require("node:fs"),tV=require("node:fs/promises"),rV=require("node:os"),un=require("node:path"),ys=re(Ne());var $_e=/^https:\/\/github\.com\/([a-zA-Z0-9_-]+)\/([a-zA-Z0-9_.-]+?)(?:\.git)?$/,eV=50*1024*1024,$i=class r{static{a(this,"BootstrapAssetSpaceService")}static{this.PAT_REGEX=/(?:gh[pousr]_[A-Za-z0-9_]{36,}|github_pat_[A-Za-z0-9_]{22,}_[A-Za-z0-9_]{59,})/g}constructor(e={}){this.fetchImpl=e.fetchImpl??globalThis.fetch,this.token=e.token??"",this.mountBaseStore=e.mountBaseStore}redact(e){return(typeof e=="string"?e:String(e)).replace(r.PAT_REGEX,"***REDACTED***")}static parseGitHubURL(e){let t=e.match($_e);if(t===null)throw new Error(`parseGitHubURL: invalid URL shape \u2014 expected https://github.com/<owner>/<repo>, got: ${e}`);return{owner:t[1],repo:t[2]}}static deriveFolderName(e){let{repo:t}=r.parseGitHubURL(e);return t.startsWith("exoas-")?t.slice(6):t}async pullAssetSpace(e,t,n){let{owner:i,repo:s}=r.parseGitHubURL(e);if((0,mt.existsSync)(n)&&(0,mt.readdirSync)(n).length>0)throw new Error(`pullAssetSpace: target ${n} exists \u0438 not empty \u2014 refusing \u043A overwrite`);let o=await(0,ys.mountAssetSpaceFiles)({http:this.httpPort(),fs:this.fsPort(),owner:i,repo:s,ref:t,targetPath:n});if(this.mountBaseStore!==void 0)try{await this.mountBaseStore.set(`${i}/${s}#${ys.SYNC_BRANCH}`,o.sha)}catch{}return o}httpPort(){let e=this.fetchImpl,t=this.token,n=a(i=>this.redact(i),"redact");return{async fetchTarball(i,s,o){let c=`https://api.github.com/repos/${i}/${s}/tarball/${o}`,l={signal:AbortSignal.timeout(6e4)};t.length>0&&(l.headers={Authorization:`Bearer ${t}`,"User-Agent":"exocortex-cli","X-GitHub-Api-Version":"2022-11-28"});let u;try{u=await e(c,l)}catch(d){throw new Error(n(`pullAssetSpace: fetch error for ${c}: ${d instanceof Error?d.message:String(d)}`))}if(!u.ok){if(u.status===403){let d=u.headers?.get?.("x-ratelimit-remaining"),p=u.headers?.get?.("x-ratelimit-reset");if(d==="0"||p!=null){let h=p!=null?new Date(Number(p)*1e3).toISOString():"unknown";throw new Error(`pullAssetSpace: GitHub anonymous API rate-limit exceeded (60 req/hour). Reset at ${h}. Wait or use authenticated CLI.`)}}if(u.status===404){let d=t.length>0?"repo not found, or the provided token lacks access to it":"if this is a private repo, pass --token <pat> (or set GITHUB_TOKEN / GH_TOKEN)";throw new Error(n(`pullAssetSpace: fetch failed (404 Not Found) for ${c} \u2014 ${d}`))}throw new Error(n(`pullAssetSpace: fetch failed (${u.status} ${u.statusText}) for ${c}`))}let f=await u.arrayBuffer();if(f.byteLength>eV)throw new Error(`pullAssetSpace: tarball too large (${f.byteLength} bytes > ${eV})`);return new Uint8Array(f)}}}fsPort(){return{async exists(e){return(0,mt.existsSync)(e)},async read(e){return(0,mt.readFileSync)(e,"utf8")},async write(e,t){(0,mt.writeFileSync)(e,t,{encoding:"utf8"})},async removeDir(e){(0,mt.rmSync)(e,{recursive:!0,force:!0})},async materialize(e,t,n){let i=await(0,tV.mkdtemp)((0,un.join)((0,rV.tmpdir)(),`exo-bootstrap-${(0,un.basename)(e)}-`)),s=0;try{for(let o of t){let c=o.relPath;if(c.split("/").some(u=>u===".."))throw new Error(`pullAssetSpace: path traversal detected for entry "${c}"`);let l=(0,un.resolve)(i,c);if(l!==i&&!l.startsWith(i+un.sep))throw new Error(`pullAssetSpace: zip-slip detected for entry "${c}"`);(0,mt.mkdirSync)((0,un.dirname)(l),{recursive:!0}),(0,mt.writeFileSync)(l,o.content),s++,n?.(s)}(0,mt.mkdirSync)((0,un.dirname)(e),{recursive:!0});try{(0,mt.renameSync)(i,e)}catch(o){if(o.code==="EXDEV"){let{cpSync:c}=await import("node:fs");c(i,e,{recursive:!0}),(0,mt.rmSync)(i,{recursive:!0,force:!0})}else throw o}}catch(o){throw(0,mt.rmSync)(i,{recursive:!0,force:!0}),o}return s}}}ensureGitmodulesEntry(e,t,n){let i=(0,un.join)(e,".gitmodules"),s="";try{s=(0,mt.readFileSync)(i,"utf8")}catch(l){if(l.code!=="ENOENT")throw l}let{content:o,added:c}=(0,ys.appendGitmodulesEntry)(s,t,n);return c?((0,mt.appendFileSync)(i,o.slice(s.length),{encoding:"utf8"}),{added:!0}):{added:!1}}removeGitmodulesEntry(e,t){let n=(0,un.join)(e,".gitmodules"),i;try{i=(0,mt.readFileSync)(n,"utf8")}catch(o){if(o.code==="ENOENT")return{removed:!1};throw o}let s=(0,ys.stripGitmodulesEntry)(i,t);return s===i?{removed:!1}:((0,mt.writeFileSync)(n,s,{encoding:"utf8"}),{removed:!0})}static stripGitmodulesEntry(e,t){return(0,ys.stripGitmodulesEntry)(e,t)}unmountAssetSpace(e,t){let n=this.assertSafeVaultTarget(e,t,"unmountAssetSpace");this.removeGitmodulesEntry(e,t),(0,mt.existsSync)(n)&&(0,mt.rmSync)(n,{recursive:!0,force:!0})}parkAssetSpace(e,t){let n=this.assertSafeVaultTarget(e,t,"parkAssetSpace"),i=(0,ys.parkedPathFor)(t),s=this.assertSafeVaultTarget(e,i,"parkAssetSpace",{requireAssetspacesRoot:!1});if((0,mt.existsSync)(n)){if((0,mt.existsSync)(s))throw new Error(`parkAssetSpace: refusing to park "${t}" \u2014 a parked copy already exists at "${i}". Resolve by hand: one of the two holds work the other does not.`);this.removeGitmodulesEntry(e,t),(0,mt.mkdirSync)((0,un.dirname)(s),{recursive:!0}),(0,mt.renameSync)(n,s)}}unparkAssetSpace(e,t){let n=this.assertSafeVaultTarget(e,t,"unparkAssetSpace"),i=(0,ys.parkedPathFor)(t),s=this.assertSafeVaultTarget(e,i,"unparkAssetSpace",{requireAssetspacesRoot:!1});if(!(0,mt.existsSync)(s))throw new Error(`unparkAssetSpace: nothing parked at "${i}" for "${t}"`);if((0,mt.existsSync)(n))throw new Error(`unparkAssetSpace: refusing to unpark onto existing mount "${t}"`);(0,mt.mkdirSync)((0,un.dirname)(n),{recursive:!0}),(0,mt.renameSync)(s,n)}assertSafeVaultTarget(e,t,n,i={}){let s=i.requireAssetspacesRoot??!0,o=t.split("/");if(t.length===0||s&&!t.startsWith("assetspaces/")||o.some(u=>u.length===0||u==="."||u===".."))throw new Error(`${n}: refusing unsafe submodulePath "${t}"`);let c=(0,un.resolve)(e),l=(0,un.resolve)(c,t);if(l===c||!l.startsWith(c+un.sep))throw new Error(`${n}: target "${l}" escapes vault root "${c}"`);return l}};function U_e(r){let e=(0,Nr.derivePath)(r);return e===null?r:(0,Nr.deriveUrl)(e)??r}a(U_e,"toHttpsGitHubUrl");var B_e=new Set(["node_modules",".obsidian",".exocortex",".git"]),fp=class{static{a(this,"CliApplyProfileService")}constructor(e){this.vaultPath=e.vaultPath,this.mount=e.mount??new $i,this.ref=e.ref??"main"}scanVault(){let e=[],t=new Map,n=new Set,i=[this.vaultPath];for(;i.length>0;){let s=i.pop(),o;try{o=(0,nl.readdirSync)(s,{withFileTypes:!0})}catch{continue}for(let c of o){let l=(0,Iu.join)(s,c.name);if(c.isDirectory()){if(c.name.startsWith(".")||B_e.has(c.name))continue;i.push(l);continue}if(!c.isFile()||!c.name.endsWith(".md"))continue;let u=this.readFrontmatter(l);if(u===null)continue;let f=u.exo__Asset_uid;if(typeof f!="string"||f.length===0)continue;let d=Zi(u.exo__Instance_class);if(d.includes(Tu)){if(n.has(f))continue;let h=(typeof u.exo__AssetSpace_source=="string"?u.exo__AssetSpace_source:"")||(typeof u.exo__AssetSpace_git=="string"?u.exo__AssetSpace_git:"");if(h.length===0)continue;let g=(0,Nr.derivePath)(h);if(g===null)continue;let m=typeof u.exo__AssetSpace_namespace=="string"?u.exo__AssetSpace_namespace:"",b=m||G_e(g)||f.slice(0,8),_=u.exo__AssetSpace_dependsOnKind,S=typeof _=="string"?_:Array.isArray(_)&&typeof _[0]=="string"?_[0]:void 0;n.add(f),e.push({uid:f,git:h,folderName:g,label:b,namespace:m,dependsOnKind:S})}if(d.includes(DP)){let p=u.exo__Asset_label;typeof p=="string"&&p.length>0&&t.set(f,p)}}}return{infos:e,profileLabels:t}}assertTsFloor(e,t){(0,Nr.assertTsFloorReconciled)(e,t,Nr.SDK_FLOOR)}buildDiff(e){let{targetProfileUid:t,targetProfileLabel:n,sourceProfileUid:i,sourceProfileLabel:s,result:o,infos:c}=e,l=new Map;for(let w of c)l.set(w.uid,w);let u=new Set(l.keys()),f=new Set,d=new Set;for(let w of o.declaredOntologies)if(u.has(w)){f.add(w);let R=l.get(w)?.namespace;R!==void 0&&R.length>0&&d.add(R)}this.assertTsFloor(f,d);let p=new Set([...o.effective,...f]),h=new Map;for(let w of c){let R=(0,nl.existsSync)((0,Iu.join)(this.vaultPath,w.folderName))?"active":(0,nl.existsSync)((0,Iu.join)(this.vaultPath,(0,Nr.parkedPathFor)(w.folderName)))?"parked":"absent";h.set(w.uid,R)}let g=new Set([...h].filter(([,w])=>w==="active").map(([w])=>w));for(let w of c)Nr.CATALOG_KEEP_NAMESPACES.has(w.namespace)&&g.has(w.uid)&&p.add(w.uid);let m=[],b=[],_=[];for(let w of c){let R=(0,Nr.classifyMountTransition)({state:h.get(w.uid)??"absent",inEffectiveSet:p.has(w.uid),dependsOnKind:w.dependsOnKind});switch(R){case"none":break;case"destroy":m.push({asUid:w.uid,folderName:w.folderName,label:w.label,files:this.enumerateFilesUnder(w.folderName)});break;case"park":b.push({asUid:w.uid,folderName:w.folderName,label:w.label,files:this.enumerateFilesUnder(w.folderName)});break;case"unpark":case"materialize":_.push({asUid:w.uid,folderName:w.folderName,httpsUrl:U_e(w.git),label:w.label,source:R==="unpark"?"unpark":"pull"});break}}let S=new Map;for(let w of m)S.set(w.asUid,w.files);let I=new Set(_.filter(w=>w.source==="unpark").map(w=>w.asUid)),E={targetProfileUid:t,targetProfileLabel:n,sourceProfileUid:i,sourceProfileLabel:s,filesToDestroy:S,assetSpacesBeingTornDown:m.map(w=>({asUid:w.asUid,asLabel:w.label,fileCount:w.files.length})),assetSpacesBeingParked:b.map(w=>({asUid:w.asUid,asLabel:w.label,fileCount:w.files.length})),assetSpacesBeingMaterialized:_.map(w=>({asUid:w.asUid,asLabel:w.label})),assetSpacesBeingUnparked:_.filter(w=>I.has(w.asUid)).map(w=>({asUid:w.asUid,asLabel:w.label}))};return{toDestroy:m,toPark:b,toMaterialize:_,plan:E}}async execute(e){let t=[],n=[],i=[];for(let s of e.toDestroy)this.mount.unmountAssetSpace(this.vaultPath,s.folderName),t.push(s.asUid);for(let s of e.toPark)this.mount.parkAssetSpace(this.vaultPath,s.folderName),n.push(s.asUid);for(let s of e.toMaterialize){if(s.source==="unpark")this.mount.unparkAssetSpace(this.vaultPath,s.folderName);else{let o=(0,Iu.join)(this.vaultPath,s.folderName);await this.mount.pullAssetSpace(s.httpsUrl,this.ref,o)}this.mount.ensureGitmodulesEntry(this.vaultPath,s.folderName,s.httpsUrl),i.push(s.asUid)}return{destroyed:t,parked:n,materialized:i}}enumerateFilesUnder(e){let t=[],i=[(0,Iu.join)(this.vaultPath,e)];for(;i.length>0;){let s=i.pop(),o;try{o=(0,nl.readdirSync)(s,{withFileTypes:!0})}catch{continue}for(let c of o){let l=(0,Iu.join)(s,c.name);if(c.isDirectory()){if(c.name.startsWith("."))continue;i.push(l);continue}c.isFile()&&t.push(V_e(l,this.vaultPath))}}return t}readFrontmatter(e){let t;try{t=(0,nl.readFileSync)(e,"utf-8")}catch{return null}if(!t.startsWith("---"))return null;let n=t.match(/^---\r?\n([\s\S]*?)\r?\n---/);if(n===null)return null;try{let i=(0,Nr.parseYamlFrontmatterTolerant)(n[1],e);return i&&typeof i=="object"&&!Array.isArray(i)?i:null}catch{return null}}};function V_e(r,e){let t=e.endsWith("/")?e:e+"/";return(r.startsWith(t)?r.slice(t.length):r).split("\\").join("/")}a(V_e,"relativeVaultPath");function G_e(r){let e=r.split("/").filter(t=>t.length>0);return e.length>0?e[e.length-1]:""}a(G_e,"lastSegment");ct();var Hr=require("node:fs"),cV=require("node:crypto"),lV=require("node:child_process"),uV=require("node:util"),Kr=re(require("node:path"));Mm();var at=re(Ne());ct();var qo=require("node:fs"),iV=require("node:crypto"),es=re(require("node:path"));Mm();var Yn=re(Ne());jo();var nV=require("node:child_process"),EE=re(Ne());var q_e=6e4,Go=class r{static{a(this,"RestPushService")}static{this.PAT_REGEX=/(?:gh[pousr]_[A-Za-z0-9_]{36,}|github_pat_[A-Za-z0-9_]{22,}_[A-Za-z0-9_]{59,})/g}constructor(e={}){this.fetchImpl=e.fetchImpl??globalThis.fetch,this.token=e.token??"",this.apiBase=(e.apiBase??"https://api.github.com").replace(/\/$/,"")}static resolveGhToken(e){let t=e??(()=>(0,nV.execFileSync)("gh",["auth","token"],{encoding:"utf8",timeout:15e3})),n;try{n=t()}catch(s){let o=s instanceof Error?s.message:String(s);throw new Error(`RestPushService: \`gh auth token\` failed \u2014 is GitHub CLI installed and authenticated? (run \`gh auth login\`). Underlying: ${o}`.replace(r.PAT_REGEX,"***REDACTED***"))}let i=n.trim();if(i.length===0)throw new Error("RestPushService: `gh auth token` returned empty output \u2014 not authenticated. Run `gh auth login`.");return i}redact(e){return(typeof e=="string"?e:String(e)).replace(r.PAT_REGEX,"***REDACTED***")}async push(e,t,n,i,s){return(0,EE.restCreateCommit)(this.buildTransport(),{owner:e,repo:t,branch:n,files:i,message:s,baseURL:this.apiBase,redact:a(o=>this.redact(o),"redact")})}transport(){return this.buildTransport()}buildTransport(){let e=this.token,t=this.fetchImpl,n=a(i=>this.redact(i),"redact");return async i=>{let s={Accept:"application/vnd.github+json","X-GitHub-Api-Version":"2022-11-28","User-Agent":"exocortex-cli"};e.length>0&&(s.Authorization=`Bearer ${e}`),i.contentType&&(s["Content-Type"]=i.contentType);let o;try{o=await t(i.url,{method:i.method,headers:s,body:i.body,signal:AbortSignal.timeout(q_e)})}catch(u){throw new Error(n(`GitHub request failed: ${W_e(u)}`))}let c=await o.text().catch(()=>"");if(o.status<200||o.status>=300){let u=a(f=>o.headers?.get?.(f)??void 0,"get");throw(0,EE.enrichRateLimitError)(new Error(z_e(n(`GitHub request ${i.method} ${i.url} \u2192 HTTP ${o.status}: ${c}`),512)),u)}let l;try{l=c.length>0?JSON.parse(c):void 0}catch{l=void 0}return{status:o.status,json:l,text:c}}}};function W_e(r){if(r instanceof Error)return r.message;if(typeof r=="string")return r;try{return JSON.stringify(r)}catch{return String(r)}}a(W_e,"errMsg");function z_e(r,e){return r.length>e?r.slice(0,e)+"...":r}a(z_e,"truncate");Rt();var H_e=a(async r=>{let e=await iV.webcrypto.subtle.digest("SHA-1",r.buffer.slice(r.byteOffset,r.byteOffset+r.byteLength));return[...new Uint8Array(e)].map(t=>t.toString(16).padStart(2,"0")).join("")},"nodeSha1"),K_e={parse:a(r=>kd(r,{schema:pu}),"parse"),stringify:a(r=>Md(r,{schema:pu,lineWidth:-1}),"stringify")};function Y_e(r,e){if(r.tokenFromGh===!0)return Go.resolveGhToken(e.ghTokenRunner);let t=e.env??process.env,n=r.token||t.GITHUB_TOKEN||t.GH_TOKEN||"";if(n.length===0)throw new Error("A GitHub token is required (private sync units read as 404 without one). Use --token-from-gh, --token <pat>, or set GITHUB_TOKEN / GH_TOKEN.");return n}a(Y_e,"resolveToken");function X_e(r){let e=a(n=>es.join(r,n),"abs"),t=a(async(n,i)=>{let s;try{s=await qo.promises.readdir(n,{withFileTypes:!0})}catch{return}for(let o of s){let c=es.join(n,o.name);if(o.isDirectory()){if(o.name===".git")continue;await t(c,i)}else o.isFile()&&i.push(es.relative(r,c).split(es.sep).join("/"))}},"walk");return{async list(){let n=[];return(0,qo.existsSync)(r)&&await t(r,n),n},read:a(n=>qo.promises.readFile(e(n),"utf-8"),"read"),async readBinary(n){return new Uint8Array(await qo.promises.readFile(e(n)))},async write(){throw new Error("exosync-parity is read-only \u2014 write refused")},async delete(){throw new Error("exosync-parity is read-only \u2014 delete refused")}}}a(X_e,"nodeLocalFilesPort");function Q_e(r,e){let t;try{t=(0,Yn.parkedPathFor)(e)}catch{return!1}return(0,qo.existsSync)(es.join(r,t))}a(Q_e,"isParked");function dp(r){let e=new er(r),t=new Yn.SpaceSpecAccumulator,n=[];for(let i of e.getAllFiles()){let s=e.getFrontmatter(i);if(!s)continue;let o=(0,Yn.classifySpaceDeclaration)(s,i.path);if(o.kind==="not-space"||(o.warning!==void 0&&t.warnings.push(o.warning),o.kind==="skip"))continue;let c=t.offer(o.candidate);if(c!==null){if(!(0,qo.existsSync)(es.join(r,c.localPath))){Q_e(r,c.localPath)&&n.push(c);continue}t.commit(o.candidate)}}return{specs:t.specs,parked:n,warnings:t.warnings}}a(dp,"collectVaultSpecs");function J_e(r,e){for(let t of r.repos){let n=t.headSha!==void 0?` @${t.headSha.slice(0,7)}`:"";e(`${t.repoKey}${n}: ${t.status} \u2014 ${t.inParity}/${t.filesChecked} in parity, M2 diffs ${t.m2SemanticDiffs}, accounted ${t.accountedCount}, M1 violations ${t.m1Violations.length}${t.attachmentHashSetIdentical!==void 0?`, attachment hash-set ${t.attachmentHashSetIdentical?"identical":"DIFFERS"}`:""}`),t.detail!==void 0&&e(` ${t.detail}`);for(let i of t.warnings)e(` warn: ${i}`);for(let i of t.discrepancies)e(` ${i.cls}: ${i.path}${i.detail!==void 0?` (${i.detail})`:""}`);for(let i of t.m1Violations)e(` M1 VIOLATION: ${i.path} \u2014 ${i.detail}`)}e((0,Yn.summarizeParityRound)(r))}a(J_e,"printHumanReport");function Z_e(r,e){if(r.length!==0){e(`Parked AssetSpaces (frozen on this device, not synced): ${r.length}`);for(let t of r){let n=t.lastSyncedSha!==null?t.lastSyncedSha.slice(0,7):"never",i=t.remoteHeadSha!==null?t.remoteHeadSha.slice(0,7):"unread";switch(t.freshness){case"behind":e(` ${t.repoKey}: BEHIND \u2014 parked at ${n}, remote head is ${i}`);break;case"current":e(` ${t.repoKey}: current \u2014 parked at ${n}, matches the remote head`);break;case"unknown":e(` ${t.repoKey}: unknown \u2014 ${t.reason??"no verdict"}`);break}}}}a(Z_e,"printParkedSection");async function eSe(r,e={}){let t=e.out??(b=>console.log(b)),n=es.resolve(r.vault);if(!(0,qo.existsSync)(n))throw new Error(`Vault path does not exist: ${n}`);let i=Y_e(r,e),s=new Go({token:i,...r.apiBase!==void 0?{apiBase:r.apiBase}:{}}),o=e.transportFactory?.(i,r.apiBase)??s.transport(),{specs:c,parked:l,warnings:u}=dp(n);for(let b of u)t(`warn: ${b}`);let f=r.configDir??".obsidian",d=es.join(n,f,"plugins","exocortex",Yn.WATERMARK_STORE_FILENAME),p=new Yn.FileWatermarkStore({read:a(async()=>{try{return await qo.promises.readFile(d,"utf-8")}catch{return null}},"read"),writeAtomic:a(async()=>{throw new Error("exosync-parity: watermark access is read-only by contract")},"writeAtomic")}),h=[];for(let b of l)h.push(await(0,Yn.checkParkedStaleness)(b,{transport:o,watermarks:{get:a(_=>p.get(_),"get")},redact:a(_=>s.redact(_),"redact"),...r.apiBase!==void 0?{baseURL:r.apiBase}:{}}));if(r.json!==!0&&Z_e(h,t),c.length===0)return r.json===!0&&h.length>0&&t(JSON.stringify({parked:h},null,2)),t("Nothing to check \u2014 no materialized AssetSpaces with a GitHub source found in this vault."),2;let g=new Yn.ParityValidator({transport:o,sha1:H_e,localFilesFor:a(b=>X_e(es.join(n,b.localPath)),"localFilesFor"),watermarks:{get:a(b=>p.get(b),"get")},yaml:K_e,redact:a(b=>s.redact(b),"redact"),...r.apiBase!==void 0?{baseURL:r.apiBase}:{}});t(`ExoSync parity check: ${c.length} repo(s), vault ${n}`);let m=await g.runRound(c,{trigger:"standalone"});return r.json===!0?t(JSON.stringify({...m,parked:h},null,2)):J_e(m,t),m.vacuous?2:m.ok?0:1}a(eSe,"runExosyncParity");function sV(){return new he("exosync-parity").description("ExoSync M1/M2 parity report: compare materialized sync units against their remote heads (read-only; RFC 4e4dc453 Phase E)").requiredOption("--vault <path>","Vault root path").option("--config-dir <name>","Obsidian config dir name (watermark location)",".obsidian").option("--json","Print the full round record as JSON").option("--token <pat>","GitHub PAT (or env GITHUB_TOKEN / GH_TOKEN). Prefer --token-from-gh.").option("--token-from-gh","Resolve the PAT via `gh auth token`").option("--api-base <url>","GitHub API base (testing)").action(async r=>{try{process.exitCode=await eSe(r)}catch(e){ue.handle(e,{command:"exosync-parity"}),process.exitCode=1}})}a(sV,"exosyncParityCommand");ct();var ts=require("node:fs"),WP=require("node:crypto"),Ui=re(require("node:path")),kr=re(Ne());Rt();var tSe="exosync-watermarks.local.json";function oV(r,e,t){let n=KP(e,t),i=new Go({token:n,...e.apiBase!==void 0?{apiBase:e.apiBase}:{}}),s=t.transportFactory?.(n,e.apiBase)??i.transport(),{specs:o,warnings:c}=dp(r),l=e.configDir??".obsidian",u=Ui.join(r,l,"plugins","exocortex",tSe),f=Ui.join(r,l,"plugins","exocortex",kr.CONFLICT_CACHE_STORE_FILENAME),d=new kr.LocalConflictCacheStore({io:ic(f)}),p=Ui.join(r,l,"plugins","exocortex",kr.OUTBOX_STORE_FILENAME),h=new kr.LocalOutboxStore({io:ic(p)});return{resolver:new kr.QuarantineResolver({transport:s,watermarkStore:new kr.FileWatermarkStore(ic(u)),localFilesFor:a(m=>vE(Ui.join(r,m.localPath)),"localFilesFor"),sha1:HP,...e.apiBase!==void 0?{baseURL:e.apiBase}:{},redact:a(m=>i.redact(m),"redact"),conflictCache:d,outbox:h}),specs:o,warnings:c}}a(oV,"buildResolver");async function rSe(r,e={}){let t=e.out??(l=>console.log(l)),n=Ui.resolve(r.vault);if(!(0,ts.existsSync)(n))throw new Error(`Vault path does not exist: ${n}`);let{resolver:i,specs:s,warnings:o}=oV(n,r,e);for(let l of o)t(`warn: ${l}`);if(s.length===0)return t("No materialized AssetSpaces with a GitHub source found in this vault."),0;let c=await i.listOpenConflicts(s);if(r.json===!0)return t(JSON.stringify(c,null,2)),0;if(c.length===0)return t("No open conflicts \u2014 nothing to resolve. \u2705"),0;t(`${c.length} open conflict(s):`);for(let l of c){let u=`${l.hasLocal?"local":"(no local)"} vs ${l.hasRemote?"remote":"(no remote)"}`;t(` ${l.repoKey} ${l.path} [${u}]${l.uid?` uid=${l.uid}`:""}`)}return t(""),t("Resolve with: exosync quarantine resolve <path> --take local|remote|file --vault <vault> --token-from-gh"),t(" (--take file also needs --file <merged-content-path>)"),0}a(rSe,"runQuarantineList");async function nSe(r,e,t={}){let n=t.out??(S=>console.log(S)),i=Ui.resolve(e.vault);if(!(0,ts.existsSync)(i))throw new Error(`Vault path does not exist: ${i}`);let s=(e.take??"").trim();if(s!=="local"&&s!=="remote"&&s!=="file")throw new Error("resolve requires --take local | remote | file (with --file <path> for the merged content)");let o;if(s==="file"){if(e.file===void 0||e.file.length===0)throw new Error("--take file requires --file <path> to the merged content");o={take:"merged",content:await ts.promises.readFile(Ui.resolve(e.file),"utf-8")}}else o={take:s};let{resolver:c,specs:l,warnings:u}=oV(i,e,t);for(let S of u)n(`warn: ${S}`);if(l.length===0)return n("No materialized AssetSpaces with a GitHub source found in this vault."),1;let f=await c.listOpenConflicts(l),d=e.repo,p=f.filter(S=>S.path===r),h=d===void 0?p:p.filter(S=>S.repoKey===d);if(h.length===0){if(f.length===0)n("No open conflicts in any mounted assetspace \u2014 nothing to resolve.");else if(d!==void 0&&p.length>0){n(`"${r}" is an open conflict, but not in "${d}" \u2014 a repoKey carries the sync branch (owner/repo#branch). It conflicts in:`);for(let S of p)n(` --repo ${S.repoKey}`)}else{let S=d===void 0?f:f.filter(I=>I.repoKey===d);n(`"${r}" is not among the ${S.length} open conflict(s)`+(d===void 0?"":` in "${d}"`)+" \u2014 the path is repo-relative, exactly as `exosync quarantine list` prints it:");for(let I of S)n(d===void 0?` ${I.repoKey} ${I.path}`:` ${I.path}`)}return 1}if(h.length>1){n(`"${r}" conflicts in ${h.length} repos \u2014 disambiguate with --repo <repoKey>:`);for(let S of h)n(` --repo ${S.repoKey}`);return 1}let g=h[0],m=l.find(S=>S.repoKey===g.repoKey),b=await c.resolve(m,g.path,o),_=b.awaitingPush===!0?" \u2014 awaiting push (run `exosync sync` to push it)":b.pushedSha!==void 0?` \u2014 pushed @${b.pushedSha.slice(0,7)}`:" \u2014 remote already matched";return n(`Resolved ${g.path} (${b.resolvedTo}) in ${g.repoKey}${_}.`),b.discardedLocalBackupPath!==void 0&&(n(` \u21B3 your discarded local version is preserved at ${m.localPath}/${b.discardedLocalBackupPath}`),n(" it stays on this device only \u2014 '.conflict.' paths are excluded from BOTH sync predicates, so the backup never reaches the remote. Delete it yourself once you have checked you do not need the discarded version.")),0}a(nSe,"runQuarantineResolve");function zP(r){return r.replace(/\r\n?/g,`
656
656
  `).replace(/\n+$/,"")}a(zP,"normalizeForCompare");function iSe(r,e,t){let n=[...e].sort((o,c)=>o.path.localeCompare(c.path)),i=[],s=[];for(let o of n){let c=zP(o.content),l=i.find(u=>u.norm===c);if(l!==void 0){s.push({action:"delete",path:o.path,identicalTo:l.path});continue}i.push({path:o.path,norm:c}),i.length===1?s.push({action:"keep",path:o.path,uid:r}):s.push({action:"reuuid",path:o.path,fromUid:r,toUid:t()})}return s}a(iSe,"planDedupGroup");async function sSe(r,e={}){let t=e.out??(f=>console.log(f)),n=Ui.resolve(r.vault);if(!(0,ts.existsSync)(n))throw new Error(`Vault path does not exist: ${n}`);let{specs:i,warnings:s}=dp(n);for(let f of s)t(`warn: ${f}`);let o=[];for(let f of i){let d=Ui.join(n,f.localPath);if(!(0,ts.existsSync)(d))continue;let p=vE(d);for(let h of await p.list())if(h.endsWith(".md"))try{o.push({path:Ui.join(d,h),content:await p.read(h)})}catch{continue}}let c=(0,kr.findDuplicateUidGroups)(o).map(f=>[f.uid,[...f.paths]]);if(c.length===0)return t("No duplicate uids on disk. \u2705"),0;let l=a(f=>Ui.relative(n,f),"rel");if(r.auto===!0){let f=[];for(let[b,_]of c){let S=[],I=new Map;for(let E of _){let w=await ts.promises.readFile(E,"utf-8");S.push({path:E,content:w}),I.set(E,w)}f.push({uid:b,decisions:iSe(b,S,WP.randomUUID),snap:I})}let d=0,p=0;t(`${c.length} duplicate uid(s) on disk \u2014 zero-loss plan:`);for(let{uid:b,decisions:_}of f){t(` ${b}:`);for(let S of _)S.action==="keep"?t(` KEEP ${l(S.path)} (keeps uid ${b})`):S.action==="reuuid"?(p++,t(` RE-UUID ${l(S.path)} (content differs \u2192 fresh uid; both survive, zero-loss)`)):(d++,t(` DELETE ${l(S.path)} (identical content \u2192 ${l(S.identicalTo)}; zero-loss)`))}if(r.apply!==!0)return t(""),t(`Plan: delete ${d} identical copy(ies), re-uuid ${p} differing variant(s).`),t("DRY-RUN \u2014 nothing changed. Re-run with `--auto --apply` to execute (a deleted copy's content always survives in the kept file; if previously synced it is also recoverable from the git remote)."),1;let h=0,g=0,m=0;for(let{decisions:b,snap:_}of f){for(let S of b){if(S.action!=="delete")continue;let I;try{I=await ts.promises.readFile(S.path,"utf-8")}catch{continue}if(zP(I)!==zP(_.get(S.path)??"")){t(` ! skipped delete ${l(S.path)} (changed since scan \u2014 re-run dedup-uids)`),m++;continue}await ts.promises.rm(S.path,{force:!0}),t(` deleted ${l(S.path)} (identical to ${l(S.identicalTo)})`),h++}for(let S of b){if(S.action!=="reuuid")continue;let I;try{I=await ts.promises.readFile(S.path,"utf-8")}catch{continue}if((0,kr.extractAssetUid)(I)!==S.fromUid){t(` ! skipped re-uuid ${l(S.path)} (uid changed since scan \u2014 re-run dedup-uids)`),m++;continue}let E=(0,kr.rewriteAssetUid)(I,S.toUid);E!==I&&(await ts.promises.writeFile(S.path,E,"utf-8"),t(` re-uuid ${l(S.path)} \u2192 uid=${S.toUid} (content differs from kept copy)`),g++)}}return t(`Resolved: deleted ${h} identical copy(ies), re-uuid ${g} differing variant(s).${m>0?` ${m} skipped (re-run).`:""} Run \`exosync sync\` to propagate.`),m>0?1:0}r.apply===!0&&t("Note: --apply only applies with --auto; running in report mode."),t(`${c.length} duplicate uid(s) on disk:`);for(let[f,d]of c){t(` ${f} \u2014 ${d.length} files:`);for(let p of d)t(` ${l(p)}`)}if(r.fix!==!0)return t(""),t("Re-run with --fix to assign a fresh uuid to every duplicate but the first (frontmatter rewrite only \u2014 never renames the file)."),1;let u=0;for(let f of(0,kr.planDuplicateUidFix)(o,WP.randomUUID))await ts.promises.writeFile(f.path,f.content,"utf-8"),t(` fixed ${l(f.path)} \u2192 uid=${f.toUid}`),u++;return t(`Reassigned ${u} uid(s). Run \`exosync sync\` to propagate.`),0}a(sSe,"runDedupUids");function qP(r){return r.requiredOption("--vault <path>","Vault root path").option("--config-dir <name>","Obsidian config dir name (watermark location)",".obsidian").option("--token <pat>","GitHub PAT (or env GITHUB_TOKEN / GH_TOKEN). Prefer --token-from-gh.").option("--token-from-gh","Resolve the PAT via `gh auth token`").option("--json","Machine-readable output").option("--api-base <url>","GitHub API base (testing)")}a(qP,"withQuarantineOptions");function aV(r){let e=new he("quarantine").description("Inspect and resolve ExoSync conflicts (quarantine resolver; RFC 4e4dc453, finding a0a3d1d6)");qP(e.command("list").description("List open conflicts that need a manual choice")).action(async t=>{try{process.exitCode=await rSe(t)}catch(n){ue.handle(n,{command:"exosync quarantine list"}),process.exitCode=1}}),qP(e.command("resolve <path>").description("Resolve one conflict: --take local | remote | file <path> (merged)").option("--take <choice>","local | remote | file").option("--file <path>","Merged content file (with --take file)").option("--repo <repoKey>","Disambiguate a cross-repo path collision")).action(async(t,n)=>{try{process.exitCode=await nSe(t,n)}catch(i){ue.handle(i,{command:"exosync quarantine resolve"}),process.exitCode=1}}),r.addCommand(e),qP(r.command("dedup-uids").description("Report duplicate exo__Asset_uid on disk (#3477); --fix re-uuids all but the first; --auto zero-loss auto-resolve (identical\u2192delete, differing\u2192re-uuid; dry-run unless --apply)").option("--fix","Assign a fresh uuid to every duplicate but the first").option("--auto","Zero-loss auto-resolve: delete identical copies, re-uuid distinct variants (DRY-RUN unless --apply). Supersedes --fix.").option("--apply","Execute the --auto plan (without it --auto is a dry-run; a deleted copy's content always survives in the kept file, and if previously synced is recoverable from the git remote)")).action(async t=>{try{process.exitCode=await sSe(t)}catch(n){ue.handle(n,{command:"exosync dedup-uids"}),process.exitCode=1}})}a(aV,"registerQuarantineCommands");Rt();var HP=a(async r=>{let e=await cV.webcrypto.subtle.digest("SHA-1",r.buffer.slice(r.byteOffset,r.byteOffset+r.byteLength));return[...new Uint8Array(e)].map(t=>t.toString(16).padStart(2,"0")).join("")},"nodeSha1"),oSe={parse:a(r=>kd(r,{schema:pu}),"parse"),stringify:a(r=>Md(r,{schema:pu,lineWidth:-1}),"stringify")};function KP(r,e){if(r.tokenFromGh===!0)return Go.resolveGhToken(e.ghTokenRunner);let t=e.env??process.env,n=r.token||t.GITHUB_TOKEN||t.GH_TOKEN||"";if(n.length===0)throw new Error("A GitHub token is required (private sync units read as 404 and push needs write scope). Use --token-from-gh, --token <pat>, or set GITHUB_TOKEN / GH_TOKEN.");return n}a(KP,"resolveToken");function vE(r){let e=a(i=>Kr.join(r,i),"abs"),t=a(async(i,s)=>{let o;try{o=await Hr.promises.readdir(i,{withFileTypes:!0})}catch{return}for(let c of o){let l=Kr.join(i,c.name);if(c.isDirectory()){if(c.name===".git")continue;await t(l,s)}else c.isFile()&&s.push(Kr.relative(r,l).split(Kr.sep).join("/"))}},"walk"),n=a(async(i,s)=>{let o=Kr.dirname(i);await Hr.promises.mkdir(o,{recursive:!0});let c=`${i}.local.tmp`;await s(c),await Hr.promises.rename(c,i)},"writeAtomic");return{async list(){let i=[];return(0,Hr.existsSync)(r)&&await t(r,i),i},read:a(i=>Hr.promises.readFile(e(i),"utf-8"),"read"),async write(i,s){await n(e(i),o=>Hr.promises.writeFile(o,s,"utf-8"))},async delete(i){await Hr.promises.rm(e(i),{force:!0})},async readBinary(i){return new Uint8Array(await Hr.promises.readFile(e(i)))},async writeBinary(i,s){let o=s.buffer.slice(s.byteOffset,s.byteOffset+s.byteLength);await n(e(i),c=>Hr.promises.writeFile(c,Buffer.from(o)))},async stat(i){try{let s=await Hr.promises.stat(e(i));return{mtimeMs:s.mtimeMs,size:s.size}}catch{return null}}}}a(vE,"nodeLocalFilesPort");function ic(r){return{async read(){try{return await Hr.promises.readFile(r,"utf-8")}catch{return null}},async writeAtomic(e){await Hr.promises.mkdir(Kr.dirname(r),{recursive:!0});let t=`${r}.local.tmp`;await Hr.promises.writeFile(t,e,"utf-8"),await Hr.promises.rename(t,r)}}}a(ic,"nodeWatermarkFileIO");function Ru(r,e=".obsidian"){let t=Kr.join(r,e,"plugins","exocortex",at.MOUNT_BASE_STORE_FILENAME);return new at.FileMountBaseStore(ic(t))}a(Ru,"nodeMountBaseStore");var aSe=(0,uV.promisify)(lV.execFile);function cSe(r){return async e=>{if(e.localPath.length===0||e.localPath.startsWith("-"))return null;try{let{stdout:t}=await aSe("git",["-C",r,"submodule","status",e.localPath],{timeout:3e4,maxBuffer:4194304}),n=t.match(/^[ +\-U]?([0-9a-f]{40})\b/m);return n?n[1]:null}catch{return null}}}a(cSe,"nodeLocalBaseShaProvider");function lSe(r){return{async check(e){let t=Kr.join(r,e.localPath);if(!(0,Hr.existsSync)(t))return{fullyMaterialized:!1,reason:`mount folder ${e.localPath} does not exist`};let n=0;try{n=(await Hr.promises.readdir(t)).length}catch{return{fullyMaterialized:!1,reason:`mount folder ${e.localPath} is not readable`}}return n===0&&e.spaceKind!=="file"?{fullyMaterialized:!1,reason:`mount folder ${e.localPath} is empty (mid-mount?)`}:{fullyMaterialized:!0}}}}a(lSe,"nodeMaterializationCheck");function uSe(r,e){let t=r.pushedSha!==void 0?` \u2192@${r.pushedSha.slice(0,7)}`:"",n=(r.outboxPushedCount??0)>0?`, pushed ${r.outboxPushedCount} resolved`:"",i=(r.outboxReconflictedCount??0)>0?`, ${r.outboxReconflictedCount} re-conflicted (remote moved)`:"";e(`${r.repoKey}${t}: ${r.status} \u2014 pulled ${r.pulledCount}, pushed ${r.pushedCount}, merged ${r.mergedCount}, quarantined ${r.quarantinedCount}${(r.pushedDeletes?.length??0)>0?`, deleted ${r.pushedDeletes.length}`:""}${n}${i}`),r.detail!==void 0&&e(` ${r.detail}`);for(let s of r.warnings)e(` warn: ${s}`);(r.quarantinedPaths?.length??0)>0&&e(` quarantined (\u2192 exosync quarantine resolve): ${r.quarantinedPaths.join(", ")}`),(r.deferredPaths?.length??0)>0&&e(` deferred (\u2192 full sync): ${r.deferredPaths.join(", ")}`),(r.deferredDeletes?.length??0)>0&&e(` deferred deletes: ${r.deferredDeletes.join(", ")}`),r.timings!==void 0&&(0,at.totalMs)(r.timings)>0&&e(` ${(0,at.formatRepoTimings)(r.repoKey,r.timings)}`)}a(uSe,"printRepoResult");function fSe(r){return r==="conflict"||r==="full-conflict"||r==="retry-exhausted"||r==="busy"||r==="auth-required"||r==="error"}a(fSe,"isFailureStatus");async function dSe(r,e,t={}){let n=t.out??(R=>console.log(R)),i=Kr.resolve(e.vault);if(!(0,Hr.existsSync)(i))throw new Error(`Vault path does not exist: ${i}`);let s=KP(e,t),o=new Go({token:s,...e.apiBase!==void 0?{apiBase:e.apiBase}:{}}),c=t.transportFactory?.(s,e.apiBase)??o.transport(),{specs:l,warnings:u}=dp(i);for(let R of u)n(`warn: ${R}`);if(l.length===0)return n("Nothing to sync \u2014 no materialized AssetSpaces with a GitHub source found in this vault."),2;let f=e.configDir??".obsidian",d=Kr.join(i,f,"plugins","exocortex",at.WATERMARK_STORE_FILENAME),p=Kr.join(i,f,"plugins","exocortex",at.LOCAL_MANIFEST_STORE_FILENAME),h=Kr.join(i,f,"plugins","exocortex",at.CONFLICT_CACHE_STORE_FILENAME),g=new at.LocalConflictCacheStore({io:ic(h)}),m=Kr.join(i,f,"plugins","exocortex",at.OUTBOX_STORE_FILENAME),b=new at.LocalOutboxStore({io:ic(m)}),_=g,S=new at.SyncEngine({transport:c,watermarkStore:new at.FileWatermarkStore(ic(d)),localManifestStore:new at.FileLocalManifestStore(ic(p)),mountBaseStore:Ru(i,f),localBaseShaProvider:cSe(i),materializationCheck:lSe(i),localFilesFor:a(R=>vE(Kr.join(i,R.localPath)),"localFilesFor"),sha1:HP,redact:a(R=>o.redact(R),"redact"),commitMessage:a((R,x,P=0)=>`exosync: ${x} change(s)${P>0?`, ${P} deletion(s)`:""} (CLI)`,"commitMessage"),mergeLayer:new at.GatedStructuredMerger(new at.StructuredMerger(oSe)),quarantine:_,outbox:b,...e.apiBase!==void 0?{baseURL:e.apiBase}:{}});n(`ExoSync ${r}: ${l.length} repo(s), vault ${i}`);let I=(0,at.orderChildrenFirst)(l),E=e.json===!0?void 0:R=>{n(`[ExoSync] ${R.repoKey}: ${(0,at.syncProgressPhaseText)(R)}`)},w=await S.syncAll(I,r,E);if(e.json===!0)n(JSON.stringify(w,null,2));else{for(let P of w)uSe(P,n);let R=w.reduce((P,k)=>({pulled:P.pulled+k.pulledCount,pushed:P.pushed+k.pushedCount,merged:P.merged+k.mergedCount,quarantined:P.quarantined+k.quarantinedCount}),{pulled:0,pushed:0,merged:0,quarantined:0});n(`Summary: ${w.length} repo(s) \u2014 pulled ${R.pulled}, pushed ${R.pushed}, merged ${R.merged}, quarantined ${R.quarantined}`);let x=(0,at.aggregateTimings)(w);(0,at.totalMs)(x)>0&&n((0,at.formatTimingsLine)(x))}return w.some(R=>fSe(R.status))?1:0}a(dSe,"runExosyncSync");function YP(r){return r.requiredOption("--vault <path>","Vault root path").option("--config-dir <name>","Obsidian config dir name (watermark location)",".obsidian").option("--token <pat>","GitHub PAT (or env GITHUB_TOKEN / GH_TOKEN). Prefer --token-from-gh.").option("--token-from-gh","Resolve the PAT via `gh auth token`").option("--json","Print the full per-repo result array as JSON").option("--api-base <url>","GitHub API base (testing)")}a(YP,"withSyncOptions");function XP(r){return async e=>{try{process.exitCode=await dSe(r,e)}catch(t){ue.handle(t,{command:`exosync ${r}`}),process.exitCode=1}}}a(XP,"makeDirectionAction");function fV(){let r=new he("exosync").description("ExoSync over the GitHub REST API: sync/pull/push the materialized AssetSpace set (CLI parity with the plugin; RFC 4e4dc453 Phase B). Read-only divergence check: `exosync-parity`.");return YP(r.command("sync").description("Full pull\u2192merge\u2192push cycle for every materialized repo")).action(XP("sync")),YP(r.command("pull").description("Apply remote changes only (nothing leaves the device; local changes re-derive)")).action(XP("pull")),YP(r.command("push").description("Send the local delta only (remote changes are pinned to re-derive on the next pull/sync)")).action(XP("push")),aV(r),r}a(fV,"exosyncCommand");var pSe=a(r=>{process.stderr.write(`${r}
657
657
  `)},"DEFAULT_LOG"),TE=class{static{a(this,"HeadlessConfirmGate")}constructor(e){this.yes=e.yes,this.verbose=e.verbose??!1,this.log=e.log??pSe}async confirmApply(e){if(this.verbose){let t=Array.from(e.filesToDestroy.values()).reduce((n,i)=>n+i.length,0);if(this.log(`[apply-profile] Target: ${e.targetProfileLabel} (${e.targetProfileUid})`),this.log(`[apply-profile] Source: ${e.sourceProfileLabel}`),this.log(`[apply-profile] ${t} files to remove, ${e.assetSpacesBeingTornDown.length} AS to tear down, ${e.assetSpacesBeingMaterialized.length} AS to materialize`),e.assetSpacesBeingParked.length>0){let n=e.assetSpacesBeingParked.reduce((i,s)=>i+s.fileCount,0);this.log(`[apply-profile] ${n} files to park (moved to .exocortex/parked, NOT removed), ${e.assetSpacesBeingParked.length} AS to park`)}e.assetSpacesBeingUnparked.length>0&&this.log(`[apply-profile] ${e.assetSpacesBeingUnparked.length} AS to unpark (restored from .exocortex/parked, no download)`)}return this.yes?!0:(this.log("[apply-profile] Refused: --yes flag required for headless mode (Decision #2 safety)."),!1)}};Rt();Ut();cr();async function hSe(r,e,t={}){let n=[],i=[],s=t.out??(R=>process.stdout.write(`${R}
658
658
  `)),o=t.err??(R=>process.stderr.write(`${R}
@@ -686,7 +686,7 @@ Next steps:
686
686
  `).map(n=>n.trim()).filter(n=>n.length>0),t=e[e.length-1];if(t!==void 0)try{return JSON.parse(t)}catch{return}}a(TSe,"parseLastJsonLine");function PV(r,e){return e.filter(t=>{let n=r[t];return typeof n!="string"||n.trim().length===0})}a(PV,"missingStringFields");var RE=class{constructor(e){this.deps=e}static{a(this,"McpServer")}async handle(e){if(typeof e!="object"||e===null||Array.isArray(e))return{jsonrpc:"2.0",id:null,error:{code:OV,message:"Invalid request: expected a JSON-RPC object"}};let t=e.id,n=t===void 0;if(typeof e.method!="string")return n?null:{jsonrpc:"2.0",id:t??null,error:{code:OV,message:"Invalid request: missing method"}};if(n)return null;let i=t??null;switch(e.method){case"initialize":return{jsonrpc:"2.0",id:i,result:{protocolVersion:ESe,capabilities:{tools:{}},serverInfo:{name:"exocortex",version:this.deps.version}}};case"ping":return{jsonrpc:"2.0",id:i,result:{}};case"tools/list":return{jsonrpc:"2.0",id:i,result:{tools:xV}};case"tools/call":return{jsonrpc:"2.0",id:i,result:await this.callTool(e.params??{})};default:return{jsonrpc:"2.0",id:i,error:{code:vSe,message:`Method not found: ${e.method}`}}}}async callTool(e){let t=e.name,n=e.arguments??{},i;if(t==="create_asset"){let c=PV(n,["vault","class","label"]);if(c.length>0)return mp(`create_asset: missing required argument(s): ${c.join(", ")}`,!0);let l=IV(n);if(l!==null)return mp(`create_asset: ${l}`,!0);i=RV(n)}else if(t==="create_effort"){let c=PV(n,["vault","command","target"]);if(c.length>0)return mp(`create_effort: missing required argument(s): ${c.join(", ")}`,!0);i=CV(n)}else return mp(`Unknown tool: ${String(t)}`,!0);let s=await this.deps.runCli(i);if(s.exitCode!==0){let c=s.stderr.trim()||s.stdout.trim()||"(no diagnostic output)";return mp(`exocortex-cli exited ${s.exitCode}
687
687
  ${c}`,!0)}let o=TSe(s.stdout);return mp(o===void 0?s.stdout.trim():JSON.stringify(o))}};var LV=require("child_process");var ASe=32*1024*1024;function NV(r){return e=>new Promise(t=>{(0,LV.execFile)(process.execPath,[r,...e],{maxBuffer:ASe},(n,i,s)=>{let o=0;if(n){let c=n.code;o=typeof c=="number"?c:1}t({stdout:i,stderr:s,exitCode:o})})})}a(NV,"createSelfCliRunner");async function ISe(r,e,t){let n=(0,kV.createInterface)({input:e,crlfDelay:1/0});for await(let i of n){let s=i.trim();if(s.length===0)continue;let o;try{o=JSON.parse(s)}catch{t(JSON.stringify({jsonrpc:"2.0",id:null,error:{code:FV,message:"Parse error: invalid JSON"}})+`
688
688
  `);continue}let c;try{c=await r.handle(o)}catch(l){c={jsonrpc:"2.0",id:typeof o?.id=="string"||typeof o?.id=="number"?o.id:null,error:{code:DV,message:`Internal error: ${l instanceof Error?l.message:String(l)}`}}}c!==null&&t(JSON.stringify(c)+`
689
- `)}}a(ISe,"runStdioServer");function MV(r){return new he("mcp").description("Run an MCP (Model Context Protocol) stdio server exposing create_asset / create_effort tools backed by this CLI").option("--entry <path>","Path to the CLI entry script the tools invoke (defaults to this process's script)").action(async e=>{let t=e.entry??process.argv[1],n=new RE({runCli:NV(t),version:r??"unknown"});await ISe(n,process.stdin,i=>process.stdout.write(i))})}a(MV,"mcpCommand");function CE(r){let e=new he;return e.name("exocortex").description("CLI tool for Exocortex knowledge management system - SPARQL queries, task management, and more").version(r??"16.252.0"),e.addCommand(b7()),e.addCommand(R7()),e.addCommand(i8()),e.addCommand(c8()),e.addCommand(O8()),e.addCommand(l8()),e.addCommand(m8()),e.addCommand(g8()),e.addCommand(j8()),e.addCommand(G8()),e.addCommand(a7()),e.addCommand(f7()),e.addCommand(d7()),e.addCommand(h7()),e.addCommand(m7()),e.addCommand(X7()),e.addCommand(Z7()),e.addCommand(hV()),e.addCommand(gV()),e.addCommand(_V()),e.addCommand(SV()),e.addCommand(vV()),e.addCommand(sV()),e.addCommand(fV()),e.addCommand(TV()),e.addCommand(MV(r??"16.252.0")),e}a(CE,"createProgram");CE().parse();0&&(module.exports={createProgram});
689
+ `)}}a(ISe,"runStdioServer");function MV(r){return new he("mcp").description("Run an MCP (Model Context Protocol) stdio server exposing create_asset / create_effort tools backed by this CLI").option("--entry <path>","Path to the CLI entry script the tools invoke (defaults to this process's script)").action(async e=>{let t=e.entry??process.argv[1],n=new RE({runCli:NV(t),version:r??"unknown"});await ISe(n,process.stdin,i=>process.stdout.write(i))})}a(MV,"mcpCommand");function CE(r){let e=new he;return e.name("exocortex").description("CLI tool for Exocortex knowledge management system - SPARQL queries, task management, and more").version(r??"16.252.1"),e.addCommand(b7()),e.addCommand(R7()),e.addCommand(i8()),e.addCommand(c8()),e.addCommand(O8()),e.addCommand(l8()),e.addCommand(m8()),e.addCommand(g8()),e.addCommand(j8()),e.addCommand(G8()),e.addCommand(a7()),e.addCommand(f7()),e.addCommand(d7()),e.addCommand(h7()),e.addCommand(m7()),e.addCommand(X7()),e.addCommand(Z7()),e.addCommand(hV()),e.addCommand(gV()),e.addCommand(_V()),e.addCommand(SV()),e.addCommand(vV()),e.addCommand(sV()),e.addCommand(fV()),e.addCommand(TV()),e.addCommand(MV(r??"16.252.1")),e}a(CE,"createProgram");CE().parse();0&&(module.exports={createProgram});
690
690
  /*! Bundled license information:
691
691
 
692
692
  reflect-metadata/Reflect.js:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kitelev/exocortex-cli",
3
- "version": "16.252.0",
3
+ "version": "16.252.1",
4
4
  "description": "CLI tool for Exocortex knowledge management system - SPARQL queries, task management, and more",
5
5
  "main": "dist/index.js",
6
6
  "bin": {