@kitelev/exocortex-cli 16.257.2 → 16.257.3
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.
- package/dist/index.js +3 -3
- 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.257.
|
|
2
|
+
// @kitelev/exocortex-cli v16.257.3
|
|
3
3
|
// CLI tool for Exocortex knowledge management system - SPARQL queries, task management, and more
|
|
4
4
|
// License: MIT
|
|
5
5
|
|
|
@@ -667,7 +667,7 @@ setting__Setting_value: ${i}
|
|
|
667
667
|
|
|
668
668
|
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.
|
|
669
669
|
`}a($Se,"settingAsset");function $V(){return new pe("scaffold").description("Scaffold homoiconic configuration assets").addCommand(USe())}a($V,"scaffoldCommand");function USe(){return new pe("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,xu.resolve)(r.vault);if(!(0,jE.existsSync)(t)||!(0,jE.statSync)(t).isDirectory())throw new Le(t);let s=(await new Cn(t).indexedAssets()).find(u=>(0,Go.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,xu.join)(t,(0,xu.dirname)(s.path)),c=new Date().toISOString().slice(0,19),l=[];for(let u of jSe){let f=(0,jV.randomUUID)(),d=(0,xu.join)(o,`${f}.md`);await(0,MV.writeFile)(d,$Se(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(`
|
|
670
|
-
Defaults: uid-uniqueness=true, co-location/shacl/dag=false. Run 'exocortex validate vault --vault <path>'.`)}}catch(t){ue.handle(t)}})}a(USe,"scaffoldValidationCommand");ct();var eG=require("fs"),tG=require("path");var ol=require("node:fs"),Pu=require("node:path"),kr=Q(Fe());var mt=require("node:fs"),BV=require("node:fs/promises"),VV=require("node:os"),ln=require("node:path"),ys=Q(Fe());var BSe=/^https:\/\/github\.com\/([a-zA-Z0-9_-]+)\/([a-zA-Z0-9_.-]+?)(?:\.git)?$/,UV=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(BSe);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>UV)throw new Error(`pullAssetSpace: tarball too large (${f.byteLength} bytes > ${UV})`);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,BV.mkdtemp)((0,ln.join)((0,VV.tmpdir)(),`exo-bootstrap-${(0,ln.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,ln.resolve)(i,c);if(l!==i&&!l.startsWith(i+ln.sep))throw new Error(`pullAssetSpace: zip-slip detected for entry "${c}"`);(0,mt.mkdirSync)((0,ln.dirname)(l),{recursive:!0}),(0,mt.writeFileSync)(l,o.content),s++,n?.(s)}(0,mt.mkdirSync)((0,ln.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,ln.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,ln.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,ln.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,ln.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,ln.resolve)(e),l=(0,ln.resolve)(c,t);if(l===c||!l.startsWith(c+ln.sep))throw new Error(`${n}: target "${l}" escapes vault root "${c}"`);return l}};function VSe(r){let e=(0,kr.derivePath)(r);return e===null?r:(0,kr.deriveUrl)(e)??r}a(VSe,"toHttpsGitHubUrl");var GSe=new Set(["node_modules",".obsidian",".exocortex",".git"]),Sp=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,ol.readdirSync)(s,{withFileTypes:!0})}catch{continue}for(let c of o){let l=(0,Pu.join)(s,c.name);if(c.isDirectory()){if(c.name.startsWith(".")||GSe.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(Cu)){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,kr.derivePath)(h);if(g===null)continue;let m=typeof u.exo__AssetSpace_namespace=="string"?u.exo__AssetSpace_namespace:"",b=m||WSe(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(iF)){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,kr.assertTsFloorReconciled)(e,t,kr.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,ol.existsSync)((0,Pu.join)(this.vaultPath,w.folderName))?"active":(0,ol.existsSync)((0,Pu.join)(this.vaultPath,(0,kr.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)kr.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,kr.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:VSe(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 A=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=>A.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,Pu.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,Pu.join)(this.vaultPath,e)];for(;i.length>0;){let s=i.pop(),o;try{o=(0,ol.readdirSync)(s,{withFileTypes:!0})}catch{continue}for(let c of o){let l=(0,Pu.join)(s,c.name);if(c.isDirectory()){if(c.name.startsWith("."))continue;i.push(l);continue}c.isFile()&&t.push(qSe(l,this.vaultPath))}}return t}readFrontmatter(e){let t;try{t=(0,ol.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,kr.parseYamlFrontmatterTolerant)(n[1],e);return i&&typeof i=="object"&&!Array.isArray(i)?i:null}catch{return null}}};function qSe(r,e){let t=e.endsWith("/")?e:e+"/";return(r.startsWith(t)?r.slice(t.length):r).split("\\").join("/")}a(qSe,"relativeVaultPath");function WSe(r){let e=r.split("/").filter(t=>t.length>0);return e.length>0?e[e.length-1]:""}a(WSe,"lastSegment");ct();var zr=require("node:fs"),XV=require("node:crypto"),QV=require("node:child_process"),JV=require("node:util"),Hr=Q(require("node:path"));zm();var at=Q(Fe());ct();var Wo=require("node:fs"),qV=require("node:crypto"),es=Q(require("node:path"));zm();var Yn=Q(Fe());Xs();var GV=require("node:child_process"),$E=Q(Fe());var zSe=6e4,qo=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,GV.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,$E.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(zSe)})}catch(u){throw new Error(n(`GitHub request failed: ${HSe(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,$E.enrichRateLimitError)(new Error(KSe(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 HSe(r){if(r instanceof Error)return r.message;if(typeof r=="string")return r;try{return JSON.stringify(r)}catch{return String(r)}}a(HSe,"errMsg");function KSe(r,e){return r.length>e?r.slice(0,e)+"...":r}a(KSe,"truncate");St();var YSe=a(async r=>{let e=await qV.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"),XSe={parse:a(r=>Gd(r,{schema:gu}),"parse"),stringify:a(r=>qd(r,{schema:gu,lineWidth:-1}),"stringify")};function QSe(r,e){if(r.tokenFromGh===!0)return qo.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(QSe,"resolveToken");function JSe(r){let e=a(n=>es.join(r,n),"abs"),t=a(async(n,i)=>{let s;try{s=await Wo.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,Wo.existsSync)(r)&&await t(r,n),n},read:a(n=>Wo.promises.readFile(e(n),"utf-8"),"read"),async readBinary(n){return new Uint8Array(await Wo.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(JSe,"nodeLocalFilesPort");function ZSe(r,e){let t;try{t=(0,Yn.parkedPathFor)(e)}catch{return!1}return(0,Wo.existsSync)(es.join(r,t))}a(ZSe,"isParked");function wp(r){let e=new Ht(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,Wo.existsSync)(es.join(r,c.localPath))){ZSe(r,c.localPath)&&n.push(c);continue}t.commit(o.candidate)}}return{specs:t.specs,parked:n,warnings:t.warnings}}a(wp,"collectVaultSpecs");function ewe(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(ewe,"printHumanReport");function twe(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(twe,"printParkedSection");async function rwe(r,e={}){let t=e.out??(b=>console.log(b)),n=es.resolve(r.vault);if(!(0,Wo.existsSync)(n))throw new Error(`Vault path does not exist: ${n}`);let i=QSe(r,e),s=new qo({token:i,...r.apiBase!==void 0?{apiBase:r.apiBase}:{}}),o=e.transportFactory?.(i,r.apiBase)??s.transport(),{specs:c,parked:l,warnings:u}=wp(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 Wo.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&&twe(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:YSe,localFilesFor:a(b=>JSe(es.join(n,b.localPath)),"localFilesFor"),watermarks:{get:a(b=>p.get(b),"get")},yaml:XSe,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)):ewe(m,t),m.vacuous?2:m.ok?0:1}a(rwe,"runExosyncParity");function WV(){return new pe("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 rwe(r)}catch(e){ue.handle(e,{command:"exosync-parity"}),process.exitCode=1}})}a(WV,"exosyncParityCommand");ct();var ts=require("node:fs"),yF=require("node:crypto"),Ui=Q(require("node:path")),Mr=Q(Fe());St();var nwe="exosync-watermarks.local.json";function HV(r,e,t){let n=SF(e,t),i=new qo({token:n,...e.apiBase!==void 0?{apiBase:e.apiBase}:{}}),s=t.transportFactory?.(n,e.apiBase)??i.transport(),{specs:o,warnings:c}=wp(r),l=e.configDir??".obsidian",u=Ui.join(r,l,"plugins","exocortex",nwe),f=Ui.join(r,l,"plugins","exocortex",Mr.CONFLICT_CACHE_STORE_FILENAME),d=new Mr.LocalConflictCacheStore({io:ic(f)}),p=Ui.join(r,l,"plugins","exocortex",Mr.OUTBOX_STORE_FILENAME),h=new Mr.LocalOutboxStore({io:ic(p)});return{resolver:new Mr.QuarantineResolver({transport:s,watermarkStore:new Mr.FileWatermarkStore(ic(u)),localFilesFor:a(m=>UE(Ui.join(r,m.localPath)),"localFilesFor"),sha1:_F,...e.apiBase!==void 0?{baseURL:e.apiBase}:{},redact:a(m=>i.redact(m),"redact"),conflictCache:d,outbox:h}),specs:o,warnings:c}}a(HV,"buildResolver");async function iwe(r,e={}){let t=e.out??(u=>console.log(u)),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}=HV(n,r,e);for(let u of o)t(`warn: ${u}`);if(s.length===0)return t("No materialized AssetSpaces with a GitHub source found in this vault."),0;let{conflicts:c,pinned:l}=await i.classifyPins(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"),zV(l,n,t),0;t(`${c.length} open conflict(s):`);for(let u of c){let f=`${u.hasLocal?"local":"(no local)"} vs ${u.hasRemote?"remote":"(no remote)"}`;t(` ${u.repoKey} ${u.path} [${f}]${u.uid?` uid=${u.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>)"),zV(l,n,t),0}a(iwe,"runQuarantineList");var mF={"remote-pending":"remote change not applied here yet \u2014 this copy is behind","local-withheld":"local change, delivered by the next full sync",converged:"converged, clears on the next sync",unclassified:"unclassified (remote tree unavailable, or a file-mode space)"};function zV(r,e,t){if(r.length!==0){t(""),t(`${r.length} pinned path(s) are not conflicts:`);for(let n of Object.keys(mF)){let i=r.filter(s=>s.kind===n).length;i>0&&t(` ${String(i).padStart(5)} ${mF[n]}`)}for(let n of r.filter(i=>i.kind==="local-withheld"))t(` ${n.repoKey} ${n.path} [${mF["local-withheld"]}]`);t(`Clear with: exosync sync --vault ${e} --token-from-gh`)}}a(zV,"printPinnedNotConflicting");async function swe(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}=HV(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(A=>A.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 A of S)n(d===void 0?` ${A.repoKey} ${A.path}`:` ${A.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(swe,"runQuarantineResolve");function bF(r){return r.replace(/\r\n?/g,`
|
|
670
|
+
Defaults: uid-uniqueness=true, co-location/shacl/dag=false. Run 'exocortex validate vault --vault <path>'.`)}}catch(t){ue.handle(t)}})}a(USe,"scaffoldValidationCommand");ct();var eG=require("fs"),tG=require("path");var ol=require("node:fs"),Pu=require("node:path"),kr=Q(Fe());var mt=require("node:fs"),BV=require("node:fs/promises"),VV=require("node:os"),ln=require("node:path"),ys=Q(Fe());var BSe=/^https:\/\/github\.com\/([a-zA-Z0-9_-]+)\/([a-zA-Z0-9_.-]+?)(?:\.git)?$/,UV=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(BSe);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>UV)throw new Error(`pullAssetSpace: tarball too large (${f.byteLength} bytes > ${UV})`);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,BV.mkdtemp)((0,ln.join)((0,VV.tmpdir)(),`exo-bootstrap-${(0,ln.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,ln.resolve)(i,c);if(l!==i&&!l.startsWith(i+ln.sep))throw new Error(`pullAssetSpace: zip-slip detected for entry "${c}"`);(0,mt.mkdirSync)((0,ln.dirname)(l),{recursive:!0}),(0,mt.writeFileSync)(l,o.content),s++,n?.(s)}(0,mt.mkdirSync)((0,ln.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,ln.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,ln.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,ln.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,ln.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,ln.resolve)(e),l=(0,ln.resolve)(c,t);if(l===c||!l.startsWith(c+ln.sep))throw new Error(`${n}: target "${l}" escapes vault root "${c}"`);return l}};function VSe(r){let e=(0,kr.derivePath)(r);return e===null?r:(0,kr.deriveUrl)(e)??r}a(VSe,"toHttpsGitHubUrl");var GSe=new Set(["node_modules",".obsidian",".exocortex",".git"]),Sp=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,ol.readdirSync)(s,{withFileTypes:!0})}catch{continue}for(let c of o){let l=(0,Pu.join)(s,c.name);if(c.isDirectory()){if(c.name.startsWith(".")||GSe.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(Cu)){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,kr.derivePath)(h);if(g===null)continue;let m=typeof u.exo__AssetSpace_namespace=="string"?u.exo__AssetSpace_namespace:"",b=m||WSe(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(iF)){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,kr.assertTsFloorReconciled)(e,t,kr.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,ol.existsSync)((0,Pu.join)(this.vaultPath,w.folderName))?"active":(0,ol.existsSync)((0,Pu.join)(this.vaultPath,(0,kr.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)kr.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,kr.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:VSe(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 A=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=>A.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,Pu.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,Pu.join)(this.vaultPath,e)];for(;i.length>0;){let s=i.pop(),o;try{o=(0,ol.readdirSync)(s,{withFileTypes:!0})}catch{continue}for(let c of o){let l=(0,Pu.join)(s,c.name);if(c.isDirectory()){if(c.name.startsWith("."))continue;i.push(l);continue}c.isFile()&&t.push(qSe(l,this.vaultPath))}}return t}readFrontmatter(e){let t;try{t=(0,ol.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,kr.parseYamlFrontmatterTolerant)(n[1],e);return i&&typeof i=="object"&&!Array.isArray(i)?i:null}catch{return null}}};function qSe(r,e){let t=e.endsWith("/")?e:e+"/";return(r.startsWith(t)?r.slice(t.length):r).split("\\").join("/")}a(qSe,"relativeVaultPath");function WSe(r){let e=r.split("/").filter(t=>t.length>0);return e.length>0?e[e.length-1]:""}a(WSe,"lastSegment");ct();var zr=require("node:fs"),XV=require("node:crypto"),QV=require("node:child_process"),JV=require("node:util"),Hr=Q(require("node:path"));zm();var at=Q(Fe());ct();var Wo=require("node:fs"),qV=require("node:crypto"),es=Q(require("node:path"));zm();var Yn=Q(Fe());Xs();var GV=require("node:child_process"),$E=Q(Fe());var zSe=6e4,qo=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,GV.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,$E.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(zSe)})}catch(u){throw new Error(n(`GitHub request failed: ${HSe(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,$E.enrichRateLimitError)(new Error(KSe(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 HSe(r){if(r instanceof Error)return r.message;if(typeof r=="string")return r;try{return JSON.stringify(r)}catch{return String(r)}}a(HSe,"errMsg");function KSe(r,e){return r.length>e?r.slice(0,e)+"...":r}a(KSe,"truncate");St();var YSe=a(async r=>{let e=await qV.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"),XSe={parse:a(r=>Gd(r,{schema:gu}),"parse"),stringify:a(r=>qd(r,{schema:gu,lineWidth:-1}),"stringify")};function QSe(r,e){if(r.tokenFromGh===!0)return qo.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(QSe,"resolveToken");function JSe(r){let e=a(n=>es.join(r,n),"abs"),t=a(async(n,i)=>{let s;try{s=await Wo.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,Wo.existsSync)(r)&&await t(r,n),n},read:a(n=>Wo.promises.readFile(e(n),"utf-8"),"read"),async readBinary(n){return new Uint8Array(await Wo.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(JSe,"nodeLocalFilesPort");function ZSe(r,e){let t;try{t=(0,Yn.parkedPathFor)(e)}catch{return!1}return(0,Wo.existsSync)(es.join(r,t))}a(ZSe,"isParked");function wp(r){let e=new Ht(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,Wo.existsSync)(es.join(r,c.localPath))){ZSe(r,c.localPath)&&n.push(c);continue}t.commit(o.candidate)}}return{specs:t.specs,parked:n,warnings:t.warnings}}a(wp,"collectVaultSpecs");function ewe(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(ewe,"printHumanReport");function twe(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(twe,"printParkedSection");async function rwe(r,e={}){let t=e.out??(b=>console.log(b)),n=es.resolve(r.vault);if(!(0,Wo.existsSync)(n))throw new Error(`Vault path does not exist: ${n}`);let i=QSe(r,e),s=new qo({token:i,...r.apiBase!==void 0?{apiBase:r.apiBase}:{}}),o=e.transportFactory?.(i,r.apiBase)??s.transport(),{specs:c,parked:l,warnings:u}=wp(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 Wo.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&&twe(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:YSe,localFilesFor:a(b=>JSe(es.join(n,b.localPath)),"localFilesFor"),watermarks:{get:a(b=>p.get(b),"get")},yaml:XSe,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)):ewe(m,t),m.vacuous?2:m.ok?0:1}a(rwe,"runExosyncParity");function WV(){return new pe("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 rwe(r)}catch(e){ue.handle(e,{command:"exosync-parity"}),process.exitCode=1}})}a(WV,"exosyncParityCommand");ct();var ts=require("node:fs"),yF=require("node:crypto"),Ui=Q(require("node:path")),Mr=Q(Fe());St();var nwe="exosync-watermarks.local.json";function HV(r,e,t){let n=SF(e,t),i=new qo({token:n,...e.apiBase!==void 0?{apiBase:e.apiBase}:{}}),s=t.transportFactory?.(n,e.apiBase)??i.transport(),{specs:o,warnings:c}=wp(r),l=e.configDir??".obsidian",u=Ui.join(r,l,"plugins","exocortex",nwe),f=Ui.join(r,l,"plugins","exocortex",Mr.CONFLICT_CACHE_STORE_FILENAME),d=new Mr.LocalConflictCacheStore({io:ic(f)}),p=Ui.join(r,l,"plugins","exocortex",Mr.OUTBOX_STORE_FILENAME),h=new Mr.LocalOutboxStore({io:ic(p)});return{resolver:new Mr.QuarantineResolver({transport:s,watermarkStore:new Mr.FileWatermarkStore(ic(u)),localFilesFor:a(m=>UE(Ui.join(r,m.localPath)),"localFilesFor"),sha1:_F,...e.apiBase!==void 0?{baseURL:e.apiBase}:{},redact:a(m=>i.redact(m),"redact"),conflictCache:d,outbox:h}),specs:o,warnings:c}}a(HV,"buildResolver");async function iwe(r,e={}){let t=e.out??(u=>console.log(u)),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}=HV(n,r,e);for(let u of o)t(`warn: ${u}`);if(s.length===0)return t("No materialized AssetSpaces with a GitHub source found in this vault."),0;let{conflicts:c,pinned:l}=await i.classifyPins(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"),zV(l,n,t),0;t(`${c.length} open conflict(s):`);for(let u of c){let f=`${u.hasLocal?"local":"(no local)"} vs ${u.hasRemote?"remote":"(no remote)"}`;t(` ${u.repoKey} ${u.path} [${f}]${u.uid?` uid=${u.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>)"),zV(l,n,t),0}a(iwe,"runQuarantineList");var mF={"remote-pending":"remote change not applied here yet \u2014 this copy is behind","local-withheld":"local change, not pushed yet \u2014 the next push or sync delivers it",converged:"converged, clears on the next sync",unclassified:"unclassified (remote tree unavailable, or a file-mode space)"};function zV(r,e,t){if(r.length!==0){t(""),t(`${r.length} pinned path(s) are not conflicts:`);for(let n of Object.keys(mF)){let i=r.filter(s=>s.kind===n).length;i>0&&t(` ${String(i).padStart(5)} ${mF[n]}`)}for(let n of r.filter(i=>i.kind==="local-withheld"))t(` ${n.repoKey} ${n.path} [${mF["local-withheld"]}]`);t(`Clear with: exosync sync --vault ${e} --token-from-gh`)}}a(zV,"printPinnedNotConflicting");async function swe(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}=HV(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(A=>A.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 A of S)n(d===void 0?` ${A.repoKey} ${A.path}`:` ${A.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(swe,"runQuarantineResolve");function bF(r){return r.replace(/\r\n?/g,`
|
|
671
671
|
`).replace(/\n+$/,"")}a(bF,"normalizeForCompare");function owe(r,e,t){let n=[...e].sort((o,c)=>o.path.localeCompare(c.path)),i=[],s=[];for(let o of n){let c=bF(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(owe,"planDedupGroup");async function awe(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}=wp(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=UE(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,Mr.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=[],A=new Map;for(let E of _){let w=await ts.promises.readFile(E,"utf-8");S.push({path:E,content:w}),A.set(E,w)}f.push({uid:b,decisions:owe(b,S,yF.randomUUID),snap:A})}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 A;try{A=await ts.promises.readFile(S.path,"utf-8")}catch{continue}if(bF(A)!==bF(_.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 A;try{A=await ts.promises.readFile(S.path,"utf-8")}catch{continue}if((0,Mr.extractAssetUid)(A)!==S.fromUid){t(` ! skipped re-uuid ${l(S.path)} (uid changed since scan \u2014 re-run dedup-uids)`),m++;continue}let E=(0,Mr.rewriteAssetUid)(A,S.toUid);E!==A&&(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,Mr.planDuplicateUidFix)(o,yF.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(awe,"runDedupUids");function gF(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(gF,"withQuarantineOptions");function KV(r){let e=new pe("quarantine").description("Inspect and resolve ExoSync conflicts (quarantine resolver; RFC 4e4dc453, finding a0a3d1d6)");gF(e.command("list").description("List open conflicts that need a manual choice")).action(async t=>{try{process.exitCode=await iwe(t)}catch(n){ue.handle(n,{command:"exosync quarantine list"}),process.exitCode=1}}),gF(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 swe(t,n)}catch(i){ue.handle(i,{command:"exosync quarantine resolve"}),process.exitCode=1}}),r.addCommand(e),gF(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 awe(t)}catch(n){ue.handle(n,{command:"exosync dedup-uids"}),process.exitCode=1}})}a(KV,"registerQuarantineCommands");St();var cwe=["GIT_ALTERNATE_OBJECT_DIRECTORIES","GIT_CONFIG","GIT_OBJECT_DIRECTORY","GIT_DIR","GIT_WORK_TREE","GIT_IMPLICIT_WORK_TREE","GIT_GRAFT_FILE","GIT_INDEX_FILE","GIT_NO_REPLACE_OBJECTS","GIT_REPLACE_REF_BASE","GIT_PREFIX","GIT_INTERNAL_SUPER_PREFIX","GIT_SHALLOW_FILE","GIT_COMMON_DIR"];function YV(r){let e={...r};for(let t of cwe)delete e[t];return e}a(YV,"repoIsolatedGitEnv");var _F=a(async r=>{let e=await XV.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"),lwe={parse:a(r=>Gd(r,{schema:gu}),"parse"),stringify:a(r=>qd(r,{schema:gu,lineWidth:-1}),"stringify")};function SF(r,e){if(r.tokenFromGh===!0)return qo.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(SF,"resolveToken");function UE(r){let e=a(i=>Hr.join(r,i),"abs"),t=a(async(i,s)=>{let o;try{o=await zr.promises.readdir(i,{withFileTypes:!0})}catch{return}for(let c of o){let l=Hr.join(i,c.name);if(c.isDirectory()){if(c.name===".git")continue;await t(l,s)}else c.isFile()&&s.push(Hr.relative(r,l).split(Hr.sep).join("/"))}},"walk"),n=a(async(i,s)=>{let o=Hr.dirname(i);await zr.promises.mkdir(o,{recursive:!0});let c=`${i}.local.tmp`;await s(c),await zr.promises.rename(c,i)},"writeAtomic");return{async list(){let i=[];return(0,zr.existsSync)(r)&&await t(r,i),i},read:a(i=>zr.promises.readFile(e(i),"utf-8"),"read"),async write(i,s){await n(e(i),o=>zr.promises.writeFile(o,s,"utf-8"))},async delete(i){await zr.promises.rm(e(i),{force:!0})},async readBinary(i){return new Uint8Array(await zr.promises.readFile(e(i)))},async writeBinary(i,s){let o=s.buffer.slice(s.byteOffset,s.byteOffset+s.byteLength);await n(e(i),c=>zr.promises.writeFile(c,Buffer.from(o)))},async stat(i){try{let s=await zr.promises.stat(e(i));return{mtimeMs:s.mtimeMs,size:s.size}}catch{return null}}}}a(UE,"nodeLocalFilesPort");function ic(r){return{async read(){try{return await zr.promises.readFile(r,"utf-8")}catch{return null}},async writeAtomic(e){await zr.promises.mkdir(Hr.dirname(r),{recursive:!0});let t=`${r}.local.tmp`;await zr.promises.writeFile(t,e,"utf-8"),await zr.promises.rename(t,r)}}}a(ic,"nodeWatermarkFileIO");function Ou(r,e=".obsidian"){let t=Hr.join(r,e,"plugins","exocortex",at.MOUNT_BASE_STORE_FILENAME);return new at.FileMountBaseStore(ic(t))}a(Ou,"nodeMountBaseStore");var uwe=(0,JV.promisify)(QV.execFile);function fwe(r){return async e=>{if(e.localPath.length===0||e.localPath.startsWith("-"))return null;try{let{stdout:t}=await uwe("git",["-C",r,"submodule","status",e.localPath],{timeout:3e4,maxBuffer:4194304,env:YV(process.env)}),n=t.match(/^[ +\-U]?([0-9a-f]{40})\b/m);return n?n[1]:null}catch{return null}}}a(fwe,"nodeLocalBaseShaProvider");function dwe(r){return{async check(e){let t=Hr.join(r,e.localPath);if(!(0,zr.existsSync)(t))return{fullyMaterialized:!1,reason:`mount folder ${e.localPath} does not exist`};let n=0;try{n=(await zr.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(dwe,"nodeMaterializationCheck");function pwe(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(pwe,"printRepoResult");function hwe(r){return r==="conflict"||r==="full-conflict"||r==="retry-exhausted"||r==="busy"||r==="auth-required"||r==="error"}a(hwe,"isFailureStatus");async function mwe(r,e,t={}){let n=t.out??(C=>console.log(C)),i=Hr.resolve(e.vault);if(!(0,zr.existsSync)(i))throw new Error(`Vault path does not exist: ${i}`);let s=SF(e,t),o=new qo({token:s,...e.apiBase!==void 0?{apiBase:e.apiBase}:{}}),c=t.transportFactory?.(s,e.apiBase)??o.transport(),{specs:l,warnings:u}=wp(i);for(let C of u)n(`warn: ${C}`);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=Hr.join(i,f,"plugins","exocortex",at.WATERMARK_STORE_FILENAME),p=Hr.join(i,f,"plugins","exocortex",at.LOCAL_MANIFEST_STORE_FILENAME),h=Hr.join(i,f,"plugins","exocortex",at.CONFLICT_CACHE_STORE_FILENAME),g=new at.LocalConflictCacheStore({io:ic(h)}),m=Hr.join(i,f,"plugins","exocortex",at.OUTBOX_STORE_FILENAME),b=new at.LocalOutboxStore({io:ic(m)}),_=g,S=new at.FileWatermarkStore(ic(d)),A=new at.SyncEngine({transport:c,watermarkStore:S,localManifestStore:new at.FileLocalManifestStore(ic(p)),mountBaseStore:Ou(i,f),localBaseShaProvider:fwe(i),materializationCheck:dwe(i),localFilesFor:a(C=>UE(Hr.join(i,C.localPath)),"localFilesFor"),sha1:_F,redact:a(C=>o.redact(C),"redact"),commitMessage:a((C,O,N=0)=>`exosync: ${O} change(s)${N>0?`, ${N} deletion(s)`:""} (CLI)`,"commitMessage"),mergeLayer:new at.GatedStructuredMerger(new at.StructuredMerger(lwe)),quarantine:_,outbox:b,...e.apiBase!==void 0?{baseURL:e.apiBase}:{}});n(`ExoSync ${r}: ${l.length} repo(s), vault ${i}`);let E=(0,at.orderChildrenFirst)(l),w=e.json===!0?void 0:C=>{n(`[ExoSync] ${C.repoKey}: ${(0,at.syncProgressPhaseText)(C)}`)},R=await A.syncAll(E,r,w);if(e.json===!0)n(JSON.stringify(R,null,2));else{for(let ee of R)pwe(ee,n);let C=R.reduce((ee,K)=>({pulled:ee.pulled+K.pulledCount,pushed:ee.pushed+K.pushedCount,merged:ee.merged+K.mergedCount,quarantined:ee.quarantined+K.quarantinedCount}),{pulled:0,pushed:0,merged:0,quarantined:0}),O=await S.getAll(),N=0;for(let ee of l)N+=O[ee.repoKey]?.pinnedPaths?.length??0;let V=N>0?`, pinned ${N} (incl. conflicts; see \`exosync quarantine list\`)`:"";n(`Summary: ${R.length} repo(s) \u2014 pulled ${C.pulled}, pushed ${C.pushed}, merged ${C.merged}, quarantined ${C.quarantined}${V}`);let $=(0,at.aggregateTimings)(R);(0,at.totalMs)($)>0&&n((0,at.formatTimingsLine)($))}return R.some(C=>hwe(C.status))?1:0}a(mwe,"runExosyncSync");function wF(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(wF,"withSyncOptions");function EF(r){return async e=>{try{process.exitCode=await mwe(r,e)}catch(t){ue.handle(t,{command:`exosync ${r}`}),process.exitCode=1}}}a(EF,"makeDirectionAction");function ZV(){let r=new pe("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 wF(r.command("sync").description("Full pull\u2192merge\u2192push cycle for every materialized repo")).action(EF("sync")),wF(r.command("pull").description("Apply remote changes only (nothing leaves the device; local changes re-derive)")).action(EF("pull")),wF(r.command("push").description("Send the local delta only (remote changes are pinned to re-derive on the next pull/sync)")).action(EF("push")),KV(r),r}a(ZV,"exosyncCommand");var gwe=a(r=>{process.stderr.write(`${r}
|
|
672
672
|
`)},"DEFAULT_LOG"),BE=class{static{a(this,"HeadlessConfirmGate")}constructor(e){this.yes=e.yes,this.verbose=e.verbose??!1,this.log=e.log??gwe}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)}};St();Mt();tr();async function ywe(r,e,t={}){let n=[],i=[],s=t.out??(R=>process.stdout.write(`${R}
|
|
673
673
|
`)),o=t.err??(R=>process.stderr.write(`${R}
|
|
@@ -701,7 +701,7 @@ Next steps:
|
|
|
701
701
|
`).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(Rwe,"parseLastJsonLine");function _G(r,e){return e.filter(t=>{let n=r[t];return typeof n!="string"||n.trim().length===0})}a(_G,"missingStringFields");var qE=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:bG,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:bG,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:Awe,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:yG}};case"tools/call":return{jsonrpc:"2.0",id:i,result:await this.callTool(e.params??{})};default:return{jsonrpc:"2.0",id:i,error:{code:Iwe,message:`Method not found: ${e.method}`}}}}async callTool(e){let t=e.name,n=e.arguments??{},i;if(t==="create_asset"){let c=_G(n,["vault","class","label"]);if(c.length>0)return Tp(`create_asset: missing required argument(s): ${c.join(", ")}`,!0);let l=hG(n);if(l!==null)return Tp(`create_asset: ${l}`,!0);i=mG(n)}else if(t==="create_effort"){let c=_G(n,["vault","command","target"]);if(c.length>0)return Tp(`create_effort: missing required argument(s): ${c.join(", ")}`,!0);i=gG(n)}else return Tp(`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 Tp(`exocortex-cli exited ${s.exitCode}
|
|
702
702
|
${c}`,!0)}let o=Rwe(s.stdout);return Tp(o===void 0?s.stdout.trim():JSON.stringify(o))}};var EG=require("child_process");var Cwe=32*1024*1024;function vG(r){return e=>new Promise(t=>{(0,EG.execFile)(process.execPath,[r,...e],{maxBuffer:Cwe},(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(vG,"createSelfCliRunner");async function xwe(r,e,t){let n=(0,TG.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:SG,message:"Parse error: invalid JSON"}})+`
|
|
703
703
|
`);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:wG,message:`Internal error: ${l instanceof Error?l.message:String(l)}`}}}c!==null&&t(JSON.stringify(c)+`
|
|
704
|
-
`)}}a(xwe,"runStdioServer");function AG(r){return new pe("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 qE({runCli:vG(t),version:r??"unknown"});await xwe(n,process.stdin,i=>process.stdout.write(i))})}a(AG,"mcpCommand");function WE(r){let e=new pe;return e.name("exocortex").description("CLI tool for Exocortex knowledge management system - SPARQL queries, task management, and more").version(r??"16.257.
|
|
704
|
+
`)}}a(xwe,"runStdioServer");function AG(r){return new pe("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 qE({runCli:vG(t),version:r??"unknown"});await xwe(n,process.stdin,i=>process.stdout.write(i))})}a(AG,"mcpCommand");function WE(r){let e=new pe;return e.name("exocortex").description("CLI tool for Exocortex knowledge management system - SPARQL queries, task management, and more").version(r??"16.257.3"),e.addCommand(nV()),e.addCommand(pV()),e.addCommand(j8()),e.addCommand(V8()),e.addCommand(l7()),e.addCommand(G8()),e.addCommand(K8()),e.addCommand(Y8()),e.addCommand(y7()),e.addCommand(E7()),e.addCommand(B7()),e.addCommand(z7()),e.addCommand(X7()),e.addCommand(Q7()),e.addCommand(Z7()),e.addCommand(eV()),e.addCommand(kV()),e.addCommand($V()),e.addCommand(rG()),e.addCommand(iG()),e.addCommand(aG()),e.addCommand(cG()),e.addCommand(fG()),e.addCommand(WV()),e.addCommand(ZV()),e.addCommand(dG()),e.addCommand(AG(r??"16.257.3")),e}a(WE,"createProgram");WE().parse();0&&(module.exports={createProgram});
|
|
705
705
|
/*! Bundled license information:
|
|
706
706
|
|
|
707
707
|
reflect-metadata/Reflect.js:
|
package/package.json
CHANGED