@kitelev/exocortex-cli 16.235.7 → 16.235.9
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.235.
|
|
2
|
+
// @kitelev/exocortex-cli v16.235.9
|
|
3
3
|
// CLI tool for Exocortex knowledge management system - SPARQL queries, task management, and more
|
|
4
4
|
// License: MIT
|
|
5
5
|
|
|
@@ -632,7 +632,7 @@ setting__Setting_value: ${i}
|
|
|
632
632
|
|
|
633
633
|
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.
|
|
634
634
|
`}a(cye,"settingAsset");function JB(){return new fe("scaffold").description("Scaffold homoiconic configuration assets").addCommand(lye())}a(JB,"scaffoldCommand");function lye(){return new fe("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";se.setFormat(e);try{let t=(0,cu.resolve)(r.vault);if(!(0,xw.existsSync)(t)||!(0,xw.statSync)(t).isDirectory())throw new Me(t);let s=(await new xi(t).indexedAssets()).find(u=>(0,xo.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,cu.join)(t,(0,cu.dirname)(s.path)),c=new Date().toISOString().slice(0,19),l=[];for(let u of aye){let f=(0,XB.randomUUID)(),d=(0,cu.join)(o,`${f}.md`);await(0,QB.writeFile)(d,cye(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(`
|
|
635
|
-
Defaults: uid-uniqueness=true, co-location/shacl/dag=false. Run 'exocortex validate vault --vault <path>'.`)}}catch(t){se.handle(t)}})}a(lye,"scaffoldValidationCommand");ct();var f7=require("fs"),d7=require("path");var Ow=J(Ta()),Pw=J(require("path")),Uc=J(We()),Dw=J(We());var uye=Uc.SDK_FLOOR_ASSETSPACE_UIDS,Xm="73bd00e4-ccc0-4f3f-b20d-c4388c4588fb",OO="3de846cd-1f0e-4f98-8613-b8587aa15174",fye=5,Fw=class{static{a(this,"CliProfileResolver")}constructor(e){this.vaultPaths=[Pw.default.resolve(e.vaultPath)],this.maxExtendsDepth=e.maxExtendsDepth??fye,this.warn=e.warn??(()=>{})}async resolveFilter(e){if(e==null||e==="")return{outcome:"no-profile"};let t;try{t=await this.scanAllVaults()}catch(f){let d=`[CliProfileResolver] vault scan threw \u2014 ${String(f)}`;return this.warn(d),{outcome:"error",reason:d}}if(t.profiles.get(e)===void 0)return this.warn(`[CliProfileResolver] profileUid=${e} not found in any scanned vault \u2014 falling back to no-filter (full vault).`),{outcome:"missing-profile",profileUid:e};let i=new Set;try{this.walkProfileChain(e,t.profiles,i,new Set,0)}catch(f){let d=`[CliProfileResolver] profile chain walk threw \u2014 ${String(f)}`;return this.warn(d),{outcome:"error",reason:d}}let s=(0,Uc.transitiveDependsOnClosure)(i,t.dependsOn),o=new Set(t.folderMap.values()),c=new Set,l=[];for(let f of s){if(o.has(f)){c.add(f);continue}l.push(f)}for(let f of uye)c.add(f);if(!Array.from(c).some(f=>o.has(f))){let f=`[CliProfileResolver] profileUid=${e} produced effective set with zero AssetSpace folder overlap (${c.size} AS UIDs incl. floor; ${t.folderMap.size} folders known). Likely cause: profile declares AssetSpace UIDs not present as descriptors, or vault has no scanned AssetSpaces. Falling back to no-filter (full vault indexed).`;return this.warn(f),{outcome:"degraded",reason:f}}return{outcome:"engaged",result:{effective:c,folderMap:t.folderMap,declaredOntologies:s,untranslated:l}}}walkProfileChain(e,t,n,i,s){if(s>this.maxExtendsDepth)throw new Error(`Profile chain exceeds max depth ${this.maxExtendsDepth} at ${e} \u2014 possible cycle`);if(i.has(e))return;i.add(e);let o=t.get(e);if(o!==void 0){for(let c of o.includes)n.add(c);typeof o.extends=="string"&&o.extends.length>0&&this.walkProfileChain(o.extends,t,n,i,s+1)}}async scanAllVaults(){let e=new Map,t=new Map,n=new Map;for(let i of this.vaultPaths){if(!await Ow.default.pathExists(i)){this.warn(`[CliProfileResolver] vault path does not exist: ${i} \u2014 skipping.`);continue}let s=await this.walkVault(i);for(let o of s){let c=ja(o.frontmatter.exo__Instance_class);if(c.some(l=>l===Xm)){let l=pye(o.filePath,o.vaultRoot);l.length>0&&!e.has(l)&&e.set(l,o.uid);let u=typeof o.frontmatter.exo__AssetSpace_source=="string"?o.frontmatter.exo__AssetSpace_source:typeof o.frontmatter.exo__AssetSpace_git=="string"?o.frontmatter.exo__AssetSpace_git:"";if(u.length>0){let d=(0,Uc.derivePath)(u);d!==null&&e.set(d,o.uid)}let f=ja(o.frontmatter.exo__AssetSpace_dependsOn);f.length>0&&n.set(o.uid,f)}if(c.some(l=>l===OO)){let l={uid:o.uid,label:typeof o.frontmatter.exo__Asset_label=="string"?o.frontmatter.exo__Asset_label:void 0,includes:ja(o.frontmatter.exo__Profile_includes),extends:ja(o.frontmatter.exo__Profile_imports)[0]??null};t.set(o.uid,l)}}}return{folderMap:e,profiles:t,dependsOn:n}}async walkVault(e){let t=[],n=[e];for(;n.length>0;){let i=n.pop(),s;try{s=await Ow.default.readdir(i,{withFileTypes:!0})}catch{continue}for(let o of s){let c=Pw.default.join(i,o.name);if(o.isDirectory()){if(o.name.startsWith(".")||o.name==="node_modules"||o.name===".obsidian"||o.name===".exocortex")continue;n.push(c);continue}if(!o.isFile()||!o.name.endsWith(".md"))continue;let l=await this.tryReadFrontmatter(c);if(l===null)continue;let u=l.exo__Asset_uid;typeof u!="string"||u.length===0||t.push({uid:u,filePath:c,vaultRoot:e,frontmatter:l})}}return t}async tryReadFrontmatter(e){let t;try{t=await Ow.default.readFile(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,Uc.parseYamlFrontmatterTolerant)(n[1],e);return i&&typeof i=="object"&&!Array.isArray(i)?i:null}catch{return null}}};function ja(r){if(r==null)return[];let e=Array.isArray(r)?r:[r],t=[];for(let n of e){if(typeof n!="string")continue;let i=dye(n);i!==null&&t.push(i)}return t}a(ja,"parseWikilinkArray");function dye(r){let e=r.trim().replace(/^\[\[/,"").replace(/\]\]$/,"").split("|")[0].trim();return e.length===0?null:e}a(dye,"extractUidFromWikilink");function pye(r,e){let t=Pw.default.relative(e,r).replace(/\\/g,"/");if(!t.startsWith("assetspaces/"))return"";let n=t.slice(12),i=n.indexOf("/");return i<0?"":`assetspaces/${n.slice(0,i)}`}a(pye,"parentFolderRelative");var Bc=require("node:fs"),lu=require("node:path"),Nr=J(We());var pt=require("node:fs"),e7=require("node:fs/promises"),t7=require("node:os"),an=require("node:path"),as=J(We());var hye=/^https:\/\/github\.com\/([a-zA-Z0-9_-]+)\/([a-zA-Z0-9_.-]+?)(?:\.git)?$/,ZB=50*1024*1024,Oi=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(hye);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,pt.existsSync)(n)&&(0,pt.readdirSync)(n).length>0)throw new Error(`pullAssetSpace: target ${n} exists \u0438 not empty \u2014 refusing \u043A overwrite`);let o=await(0,as.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}#${as.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>ZB)throw new Error(`pullAssetSpace: tarball too large (${f.byteLength} bytes > ${ZB})`);return new Uint8Array(f)}}}fsPort(){return{async exists(e){return(0,pt.existsSync)(e)},async read(e){return(0,pt.readFileSync)(e,"utf8")},async write(e,t){(0,pt.writeFileSync)(e,t,{encoding:"utf8"})},async removeDir(e){(0,pt.rmSync)(e,{recursive:!0,force:!0})},async materialize(e,t,n){let i=await(0,e7.mkdtemp)((0,an.join)((0,t7.tmpdir)(),`exo-bootstrap-${(0,an.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,an.resolve)(i,c);if(l!==i&&!l.startsWith(i+an.sep))throw new Error(`pullAssetSpace: zip-slip detected for entry "${c}"`);(0,pt.mkdirSync)((0,an.dirname)(l),{recursive:!0}),(0,pt.writeFileSync)(l,o.content),s++,n?.(s)}(0,pt.mkdirSync)((0,an.dirname)(e),{recursive:!0});try{(0,pt.renameSync)(i,e)}catch(o){if(o.code==="EXDEV"){let{cpSync:c}=await import("node:fs");c(i,e,{recursive:!0}),(0,pt.rmSync)(i,{recursive:!0,force:!0})}else throw o}}catch(o){throw(0,pt.rmSync)(i,{recursive:!0,force:!0}),o}return s}}}ensureGitmodulesEntry(e,t,n){let i=(0,an.join)(e,".gitmodules"),s="";try{s=(0,pt.readFileSync)(i,"utf8")}catch(l){if(l.code!=="ENOENT")throw l}let{content:o,added:c}=(0,as.appendGitmodulesEntry)(s,t,n);return c?((0,pt.appendFileSync)(i,o.slice(s.length),{encoding:"utf8"}),{added:!0}):{added:!1}}removeGitmodulesEntry(e,t){let n=(0,an.join)(e,".gitmodules"),i;try{i=(0,pt.readFileSync)(n,"utf8")}catch(o){if(o.code==="ENOENT")return{removed:!1};throw o}let s=(0,as.stripGitmodulesEntry)(i,t);return s===i?{removed:!1}:((0,pt.writeFileSync)(n,s,{encoding:"utf8"}),{removed:!0})}static stripGitmodulesEntry(e,t){return(0,as.stripGitmodulesEntry)(e,t)}unmountAssetSpace(e,t){let n=this.assertSafeVaultTarget(e,t,"unmountAssetSpace");this.removeGitmodulesEntry(e,t),(0,pt.existsSync)(n)&&(0,pt.rmSync)(n,{recursive:!0,force:!0})}parkAssetSpace(e,t){let n=this.assertSafeVaultTarget(e,t,"parkAssetSpace"),i=(0,as.parkedPathFor)(t),s=this.assertSafeVaultTarget(e,i,"parkAssetSpace",{requireAssetspacesRoot:!1});if((0,pt.existsSync)(n)){if((0,pt.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,pt.mkdirSync)((0,an.dirname)(s),{recursive:!0}),(0,pt.renameSync)(n,s)}}unparkAssetSpace(e,t){let n=this.assertSafeVaultTarget(e,t,"unparkAssetSpace"),i=(0,as.parkedPathFor)(t),s=this.assertSafeVaultTarget(e,i,"unparkAssetSpace",{requireAssetspacesRoot:!1});if(!(0,pt.existsSync)(s))throw new Error(`unparkAssetSpace: nothing parked at "${i}" for "${t}"`);if((0,pt.existsSync)(n))throw new Error(`unparkAssetSpace: refusing to unpark onto existing mount "${t}"`);(0,pt.mkdirSync)((0,an.dirname)(n),{recursive:!0}),(0,pt.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,an.resolve)(e),l=(0,an.resolve)(c,t);if(l===c||!l.startsWith(c+an.sep))throw new Error(`${n}: target "${l}" escapes vault root "${c}"`);return l}};function mye(r){let e=(0,Nr.derivePath)(r);return e===null?r:(0,Nr.deriveUrl)(e)??r}a(mye,"toHttpsGitHubUrl");var gye=new Set(["node_modules",".obsidian",".exocortex",".git"]),Hd=class{static{a(this,"CliApplyProfileService")}constructor(e){this.vaultPath=e.vaultPath,this.mount=e.mount??new Oi,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,Bc.readdirSync)(s,{withFileTypes:!0})}catch{continue}for(let c of o){let l=(0,lu.join)(s,c.name);if(c.isDirectory()){if(c.name.startsWith(".")||gye.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=ja(u.exo__Instance_class);if(d.includes(Xm)){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 m=(0,Nr.derivePath)(h);if(m===null)continue;let y=typeof u.exo__AssetSpace_namespace=="string"?u.exo__AssetSpace_namespace:"",_=y||_ye(m)||f.slice(0,8),b=u.exo__AssetSpace_dependsOnKind,T=typeof b=="string"?b:Array.isArray(b)&&typeof b[0]=="string"?b[0]:void 0;n.add(f),e.push({uid:f,git:h,folderName:m,label:_,namespace:y,dependsOnKind:T})}if(d.includes(OO)){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 S of c)l.set(S.uid,S);let u=new Set(l.keys()),f=new Set,d=new Set;for(let S of o.declaredOntologies)if(u.has(S)){f.add(S);let R=l.get(S)?.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 S of c){let R=(0,Bc.existsSync)((0,lu.join)(this.vaultPath,S.folderName))?"active":(0,Bc.existsSync)((0,lu.join)(this.vaultPath,(0,Nr.parkedPathFor)(S.folderName)))?"parked":"absent";h.set(S.uid,R)}let m=new Set([...h].filter(([,S])=>S==="active").map(([S])=>S));for(let S of c)Nr.CATALOG_KEEP_NAMESPACES.has(S.namespace)&&m.has(S.uid)&&p.add(S.uid);let y=[],_=[],b=[];for(let S of c){let R=(0,Nr.classifyMountTransition)({state:h.get(S.uid)??"absent",inEffectiveSet:p.has(S.uid),dependsOnKind:S.dependsOnKind});switch(R){case"none":break;case"destroy":y.push({asUid:S.uid,folderName:S.folderName,label:S.label,files:this.enumerateFilesUnder(S.folderName)});break;case"park":_.push({asUid:S.uid,folderName:S.folderName,label:S.label,files:this.enumerateFilesUnder(S.folderName)});break;case"unpark":case"materialize":b.push({asUid:S.uid,folderName:S.folderName,httpsUrl:mye(S.git),label:S.label,source:R==="unpark"?"unpark":"pull"});break}}let T=new Map;for(let S of y)T.set(S.asUid,S.files);let I=new Set(b.filter(S=>S.source==="unpark").map(S=>S.asUid)),E={targetProfileUid:t,targetProfileLabel:n,sourceProfileUid:i,sourceProfileLabel:s,filesToDestroy:T,assetSpacesBeingTornDown:y.map(S=>({asUid:S.asUid,asLabel:S.label,fileCount:S.files.length})),assetSpacesBeingParked:_.map(S=>({asUid:S.asUid,asLabel:S.label,fileCount:S.files.length})),assetSpacesBeingMaterialized:b.map(S=>({asUid:S.asUid,asLabel:S.label})),assetSpacesBeingUnparked:b.filter(S=>I.has(S.asUid)).map(S=>({asUid:S.asUid,asLabel:S.label}))};return{toDestroy:y,toPark:_,toMaterialize:b,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,lu.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,lu.join)(this.vaultPath,e)];for(;i.length>0;){let s=i.pop(),o;try{o=(0,Bc.readdirSync)(s,{withFileTypes:!0})}catch{continue}for(let c of o){let l=(0,lu.join)(s,c.name);if(c.isDirectory()){if(c.name.startsWith("."))continue;i.push(l);continue}c.isFile()&&t.push(yye(l,this.vaultPath))}}return t}readFrontmatter(e){let t;try{t=(0,Bc.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 yye(r,e){let t=e.endsWith("/")?e:e+"/";return(r.startsWith(t)?r.slice(t.length):r).split("\\").join("/")}a(yye,"relativeVaultPath");function _ye(r){let e=r.split("/").filter(t=>t.length>0);return e.length>0?e[e.length-1]:""}a(_ye,"lastSegment");ct();var qr=require("node:fs"),a7=require("node:crypto"),c7=require("node:child_process"),l7=require("node:util"),Wr=J(require("node:path"));hm();var it=J(We());ct();var Po=require("node:fs"),n7=require("node:crypto"),qi=J(require("node:path"));hm();var Vn=J(We());Bi();var r7=require("node:child_process"),Lw=J(We());var bye=6e4,Oo=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,r7.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,Lw.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(bye)})}catch(u){throw new Error(n(`GitHub request failed: ${Sye(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,Lw.enrichRateLimitError)(new Error(wye(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 Sye(r){if(r instanceof Error)return r.message;if(typeof r=="string")return r;try{return JSON.stringify(r)}catch{return String(r)}}a(Sye,"errMsg");function wye(r,e){return r.length>e?r.slice(0,e)+"...":r}a(wye,"truncate");Pt();var Eye=a(async r=>{let e=await n7.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"),vye={parse:a(r=>gd(r,{schema:Xl}),"parse"),stringify:a(r=>yd(r,{schema:Xl,lineWidth:-1}),"stringify")};function Tye(r,e){if(r.tokenFromGh===!0)return Oo.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(Tye,"resolveToken");function Aye(r){let e=a(n=>qi.join(r,n),"abs"),t=a(async(n,i)=>{let s;try{s=await Po.promises.readdir(n,{withFileTypes:!0})}catch{return}for(let o of s){let c=qi.join(n,o.name);if(o.isDirectory()){if(o.name===".git")continue;await t(c,i)}else o.isFile()&&i.push(qi.relative(r,c).split(qi.sep).join("/"))}},"walk");return{async list(){let n=[];return(0,Po.existsSync)(r)&&await t(r,n),n},read:a(n=>Po.promises.readFile(e(n),"utf-8"),"read"),async readBinary(n){return new Uint8Array(await Po.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(Aye,"nodeLocalFilesPort");function Iye(r,e){let t;try{t=(0,Vn.parkedPathFor)(e)}catch{return!1}return(0,Po.existsSync)(qi.join(r,t))}a(Iye,"isParked");function zd(r){let e=new $t(r),t=new Vn.SpaceSpecAccumulator,n=[];for(let i of e.getAllFiles()){let s=e.getFrontmatter(i);if(!s)continue;let o=(0,Vn.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,Po.existsSync)(qi.join(r,c.localPath))){Iye(r,c.localPath)&&n.push(c);continue}t.commit(o.candidate)}}return{specs:t.specs,parked:n,warnings:t.warnings}}a(zd,"collectVaultSpecs");function Rye(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,Vn.summarizeParityRound)(r))}a(Rye,"printHumanReport");function Cye(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(Cye,"printParkedSection");async function xye(r,e={}){let t=e.out??(_=>console.log(_)),n=qi.resolve(r.vault);if(!(0,Po.existsSync)(n))throw new Error(`Vault path does not exist: ${n}`);let i=Tye(r,e),s=new Oo({token:i,...r.apiBase!==void 0?{apiBase:r.apiBase}:{}}),o=e.transportFactory?.(i,r.apiBase)??s.transport(),{specs:c,parked:l,warnings:u}=zd(n);for(let _ of u)t(`warn: ${_}`);let f=r.configDir??".obsidian",d=qi.join(n,f,"plugins","exocortex",Vn.WATERMARK_STORE_FILENAME),p=new Vn.FileWatermarkStore({read:a(async()=>{try{return await Po.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 _ of l)h.push(await(0,Vn.checkParkedStaleness)(_,{transport:o,watermarks:{get:a(b=>p.get(b),"get")},redact:a(b=>s.redact(b),"redact"),...r.apiBase!==void 0?{baseURL:r.apiBase}:{}}));if(r.json!==!0&&Cye(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 m=new Vn.ParityValidator({transport:o,sha1:Eye,localFilesFor:a(_=>Aye(qi.join(n,_.localPath)),"localFilesFor"),watermarks:{get:a(_=>p.get(_),"get")},yaml:vye,redact:a(_=>s.redact(_),"redact"),...r.apiBase!==void 0?{baseURL:r.apiBase}:{}});t(`ExoSync parity check: ${c.length} repo(s), vault ${n}`);let y=await m.runRound(c,{trigger:"standalone"});return r.json===!0?t(JSON.stringify({...y,parked:h},null,2)):Rye(y,t),y.vacuous?2:y.ok?0:1}a(xye,"runExosyncParity");function i7(){return new fe("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 xye(r)}catch(e){se.handle(e,{command:"exosync-parity"}),process.exitCode=1}})}a(i7,"exosyncParityCommand");ct();var Wi=require("node:fs"),FO=require("node:crypto"),Pi=J(require("node:path")),kr=J(We());Pt();var Oye="exosync-watermarks.local.json";function s7(r,e,t){let n=NO(e,t),i=new Oo({token:n,...e.apiBase!==void 0?{apiBase:e.apiBase}:{}}),s=t.transportFactory?.(n,e.apiBase)??i.transport(),{specs:o,warnings:c}=zd(r),l=e.configDir??".obsidian",u=Pi.join(r,l,"plugins","exocortex",Oye),f=Pi.join(r,l,"plugins","exocortex",kr.CONFLICT_CACHE_STORE_FILENAME),d=new kr.LocalConflictCacheStore({io:$a(f)}),p=Pi.join(r,l,"plugins","exocortex",kr.OUTBOX_STORE_FILENAME),h=new kr.LocalOutboxStore({io:$a(p)});return{resolver:new kr.QuarantineResolver({transport:s,watermarkStore:new kr.FileWatermarkStore($a(u)),localFilesFor:a(y=>Nw(Pi.join(r,y.localPath)),"localFilesFor"),sha1:LO,...e.apiBase!==void 0?{baseURL:e.apiBase}:{},redact:a(y=>i.redact(y),"redact"),conflictCache:d,outbox:h}),specs:o,warnings:c}}a(s7,"buildResolver");async function Pye(r,e={}){let t=e.out??(l=>console.log(l)),n=Pi.resolve(r.vault);if(!(0,Wi.existsSync)(n))throw new Error(`Vault path does not exist: ${n}`);let{resolver:i,specs:s,warnings:o}=s7(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 <path> --vault <vault>"),0}a(Pye,"runQuarantineList");async function Fye(r,e,t={}){let n=t.out??(_=>console.log(_)),i=Pi.resolve(e.vault);if(!(0,Wi.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 Wi.promises.readFile(Pi.resolve(e.file),"utf-8")}}else o={take:s};let{resolver:c,specs:l,warnings:u}=s7(i,e,t);for(let _ of u)n(`warn: ${_}`);let d=(await c.listOpenConflicts(l)).filter(_=>_.path===r&&(e.repo===void 0||_.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 _ of d)n(` --repo ${_.repoKey}`);return 1}let p=d[0],h=l.find(_=>_.repoKey===p.repoKey),m=await c.resolve(h,p.path,o),y=m.awaitingPush===!0?" \u2014 awaiting push (run `exosync sync` to push it)":m.pushedSha!==void 0?` \u2014 pushed @${m.pushedSha.slice(0,7)}`:" \u2014 remote already matched";return n(`Resolved ${p.path} (${m.resolvedTo}) in ${p.repoKey}${y}.`),m.discardedLocalBackupPath!==void 0&&n(` \u21B3 your discarded local version is preserved at ${h.localPath}/${m.discardedLocalBackupPath}`),0}a(Fye,"runQuarantineResolve");function DO(r){return r.replace(/\r\n?/g,`
|
|
635
|
+
Defaults: uid-uniqueness=true, co-location/shacl/dag=false. Run 'exocortex validate vault --vault <path>'.`)}}catch(t){se.handle(t)}})}a(lye,"scaffoldValidationCommand");ct();var f7=require("fs"),d7=require("path");var Ow=J(Ta()),Pw=J(require("path")),Uc=J(We()),Dw=J(We());var uye=Uc.SDK_FLOOR_ASSETSPACE_UIDS,Xm="73bd00e4-ccc0-4f3f-b20d-c4388c4588fb",OO="3de846cd-1f0e-4f98-8613-b8587aa15174",fye=5,Fw=class{static{a(this,"CliProfileResolver")}constructor(e){this.vaultPaths=[Pw.default.resolve(e.vaultPath)],this.maxExtendsDepth=e.maxExtendsDepth??fye,this.warn=e.warn??(()=>{})}async resolveFilter(e){if(e==null||e==="")return{outcome:"no-profile"};let t;try{t=await this.scanAllVaults()}catch(f){let d=`[CliProfileResolver] vault scan threw \u2014 ${String(f)}`;return this.warn(d),{outcome:"error",reason:d}}if(t.profiles.get(e)===void 0)return this.warn(`[CliProfileResolver] profileUid=${e} not found in any scanned vault \u2014 falling back to no-filter (full vault).`),{outcome:"missing-profile",profileUid:e};let i=new Set;try{this.walkProfileChain(e,t.profiles,i,new Set,0)}catch(f){let d=`[CliProfileResolver] profile chain walk threw \u2014 ${String(f)}`;return this.warn(d),{outcome:"error",reason:d}}let s=(0,Uc.transitiveDependsOnClosure)(i,t.dependsOn),o=new Set(t.folderMap.values()),c=new Set,l=[];for(let f of s){if(o.has(f)){c.add(f);continue}l.push(f)}for(let f of uye)c.add(f);if(!Array.from(c).some(f=>o.has(f))){let f=`[CliProfileResolver] profileUid=${e} produced effective set with zero AssetSpace folder overlap (${c.size} AS UIDs incl. floor; ${t.folderMap.size} folders known). Likely cause: profile declares AssetSpace UIDs not present as descriptors, or vault has no scanned AssetSpaces. Falling back to no-filter (full vault indexed).`;return this.warn(f),{outcome:"degraded",reason:f}}return{outcome:"engaged",result:{effective:c,folderMap:t.folderMap,declaredOntologies:s,untranslated:l}}}walkProfileChain(e,t,n,i,s){if(s>this.maxExtendsDepth)throw new Error(`Profile chain exceeds max depth ${this.maxExtendsDepth} at ${e} \u2014 possible cycle`);if(i.has(e))return;i.add(e);let o=t.get(e);if(o!==void 0){for(let c of o.includes)n.add(c);typeof o.extends=="string"&&o.extends.length>0&&this.walkProfileChain(o.extends,t,n,i,s+1)}}async scanAllVaults(){let e=new Map,t=new Map,n=new Map;for(let i of this.vaultPaths){if(!await Ow.default.pathExists(i)){this.warn(`[CliProfileResolver] vault path does not exist: ${i} \u2014 skipping.`);continue}let s=await this.walkVault(i);for(let o of s){let c=ja(o.frontmatter.exo__Instance_class);if(c.some(l=>l===Xm)){let l=pye(o.filePath,o.vaultRoot);l.length>0&&!e.has(l)&&e.set(l,o.uid);let u=typeof o.frontmatter.exo__AssetSpace_source=="string"?o.frontmatter.exo__AssetSpace_source:typeof o.frontmatter.exo__AssetSpace_git=="string"?o.frontmatter.exo__AssetSpace_git:"";if(u.length>0){let d=(0,Uc.derivePath)(u);d!==null&&e.set(d,o.uid)}let f=ja(o.frontmatter.exo__AssetSpace_dependsOn);f.length>0&&n.set(o.uid,f)}if(c.some(l=>l===OO)){let l={uid:o.uid,label:typeof o.frontmatter.exo__Asset_label=="string"?o.frontmatter.exo__Asset_label:void 0,includes:ja(o.frontmatter.exo__Profile_includes),extends:ja(o.frontmatter.exo__Profile_imports)[0]??null};t.set(o.uid,l)}}}return{folderMap:e,profiles:t,dependsOn:n}}async walkVault(e){let t=[],n=[e];for(;n.length>0;){let i=n.pop(),s;try{s=await Ow.default.readdir(i,{withFileTypes:!0})}catch{continue}for(let o of s){let c=Pw.default.join(i,o.name);if(o.isDirectory()){if(o.name.startsWith(".")||o.name==="node_modules"||o.name===".obsidian"||o.name===".exocortex")continue;n.push(c);continue}if(!o.isFile()||!o.name.endsWith(".md"))continue;let l=await this.tryReadFrontmatter(c);if(l===null)continue;let u=l.exo__Asset_uid;typeof u!="string"||u.length===0||t.push({uid:u,filePath:c,vaultRoot:e,frontmatter:l})}}return t}async tryReadFrontmatter(e){let t;try{t=await Ow.default.readFile(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,Uc.parseYamlFrontmatterTolerant)(n[1],e);return i&&typeof i=="object"&&!Array.isArray(i)?i:null}catch{return null}}};function ja(r){if(r==null)return[];let e=Array.isArray(r)?r:[r],t=[];for(let n of e){if(typeof n!="string")continue;let i=dye(n);i!==null&&t.push(i)}return t}a(ja,"parseWikilinkArray");function dye(r){let e=r.trim().replace(/^\[\[/,"").replace(/\]\]$/,"").split("|")[0].trim();return e.length===0?null:e}a(dye,"extractUidFromWikilink");function pye(r,e){let t=Pw.default.relative(e,r).replace(/\\/g,"/");if(!t.startsWith("assetspaces/"))return"";let n=t.slice(12),i=n.indexOf("/");return i<0?"":`assetspaces/${n.slice(0,i)}`}a(pye,"parentFolderRelative");var Bc=require("node:fs"),lu=require("node:path"),Nr=J(We());var pt=require("node:fs"),e7=require("node:fs/promises"),t7=require("node:os"),an=require("node:path"),as=J(We());var hye=/^https:\/\/github\.com\/([a-zA-Z0-9_-]+)\/([a-zA-Z0-9_.-]+?)(?:\.git)?$/,ZB=50*1024*1024,Oi=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(hye);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,pt.existsSync)(n)&&(0,pt.readdirSync)(n).length>0)throw new Error(`pullAssetSpace: target ${n} exists \u0438 not empty \u2014 refusing \u043A overwrite`);let o=await(0,as.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}#${as.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>ZB)throw new Error(`pullAssetSpace: tarball too large (${f.byteLength} bytes > ${ZB})`);return new Uint8Array(f)}}}fsPort(){return{async exists(e){return(0,pt.existsSync)(e)},async read(e){return(0,pt.readFileSync)(e,"utf8")},async write(e,t){(0,pt.writeFileSync)(e,t,{encoding:"utf8"})},async removeDir(e){(0,pt.rmSync)(e,{recursive:!0,force:!0})},async materialize(e,t,n){let i=await(0,e7.mkdtemp)((0,an.join)((0,t7.tmpdir)(),`exo-bootstrap-${(0,an.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,an.resolve)(i,c);if(l!==i&&!l.startsWith(i+an.sep))throw new Error(`pullAssetSpace: zip-slip detected for entry "${c}"`);(0,pt.mkdirSync)((0,an.dirname)(l),{recursive:!0}),(0,pt.writeFileSync)(l,o.content),s++,n?.(s)}(0,pt.mkdirSync)((0,an.dirname)(e),{recursive:!0});try{(0,pt.renameSync)(i,e)}catch(o){if(o.code==="EXDEV"){let{cpSync:c}=await import("node:fs");c(i,e,{recursive:!0}),(0,pt.rmSync)(i,{recursive:!0,force:!0})}else throw o}}catch(o){throw(0,pt.rmSync)(i,{recursive:!0,force:!0}),o}return s}}}ensureGitmodulesEntry(e,t,n){let i=(0,an.join)(e,".gitmodules"),s="";try{s=(0,pt.readFileSync)(i,"utf8")}catch(l){if(l.code!=="ENOENT")throw l}let{content:o,added:c}=(0,as.appendGitmodulesEntry)(s,t,n);return c?((0,pt.appendFileSync)(i,o.slice(s.length),{encoding:"utf8"}),{added:!0}):{added:!1}}removeGitmodulesEntry(e,t){let n=(0,an.join)(e,".gitmodules"),i;try{i=(0,pt.readFileSync)(n,"utf8")}catch(o){if(o.code==="ENOENT")return{removed:!1};throw o}let s=(0,as.stripGitmodulesEntry)(i,t);return s===i?{removed:!1}:((0,pt.writeFileSync)(n,s,{encoding:"utf8"}),{removed:!0})}static stripGitmodulesEntry(e,t){return(0,as.stripGitmodulesEntry)(e,t)}unmountAssetSpace(e,t){let n=this.assertSafeVaultTarget(e,t,"unmountAssetSpace");this.removeGitmodulesEntry(e,t),(0,pt.existsSync)(n)&&(0,pt.rmSync)(n,{recursive:!0,force:!0})}parkAssetSpace(e,t){let n=this.assertSafeVaultTarget(e,t,"parkAssetSpace"),i=(0,as.parkedPathFor)(t),s=this.assertSafeVaultTarget(e,i,"parkAssetSpace",{requireAssetspacesRoot:!1});if((0,pt.existsSync)(n)){if((0,pt.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,pt.mkdirSync)((0,an.dirname)(s),{recursive:!0}),(0,pt.renameSync)(n,s)}}unparkAssetSpace(e,t){let n=this.assertSafeVaultTarget(e,t,"unparkAssetSpace"),i=(0,as.parkedPathFor)(t),s=this.assertSafeVaultTarget(e,i,"unparkAssetSpace",{requireAssetspacesRoot:!1});if(!(0,pt.existsSync)(s))throw new Error(`unparkAssetSpace: nothing parked at "${i}" for "${t}"`);if((0,pt.existsSync)(n))throw new Error(`unparkAssetSpace: refusing to unpark onto existing mount "${t}"`);(0,pt.mkdirSync)((0,an.dirname)(n),{recursive:!0}),(0,pt.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,an.resolve)(e),l=(0,an.resolve)(c,t);if(l===c||!l.startsWith(c+an.sep))throw new Error(`${n}: target "${l}" escapes vault root "${c}"`);return l}};function mye(r){let e=(0,Nr.derivePath)(r);return e===null?r:(0,Nr.deriveUrl)(e)??r}a(mye,"toHttpsGitHubUrl");var gye=new Set(["node_modules",".obsidian",".exocortex",".git"]),Hd=class{static{a(this,"CliApplyProfileService")}constructor(e){this.vaultPath=e.vaultPath,this.mount=e.mount??new Oi,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,Bc.readdirSync)(s,{withFileTypes:!0})}catch{continue}for(let c of o){let l=(0,lu.join)(s,c.name);if(c.isDirectory()){if(c.name.startsWith(".")||gye.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=ja(u.exo__Instance_class);if(d.includes(Xm)){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 m=(0,Nr.derivePath)(h);if(m===null)continue;let y=typeof u.exo__AssetSpace_namespace=="string"?u.exo__AssetSpace_namespace:"",_=y||_ye(m)||f.slice(0,8),b=u.exo__AssetSpace_dependsOnKind,T=typeof b=="string"?b:Array.isArray(b)&&typeof b[0]=="string"?b[0]:void 0;n.add(f),e.push({uid:f,git:h,folderName:m,label:_,namespace:y,dependsOnKind:T})}if(d.includes(OO)){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 S of c)l.set(S.uid,S);let u=new Set(l.keys()),f=new Set,d=new Set;for(let S of o.declaredOntologies)if(u.has(S)){f.add(S);let R=l.get(S)?.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 S of c){let R=(0,Bc.existsSync)((0,lu.join)(this.vaultPath,S.folderName))?"active":(0,Bc.existsSync)((0,lu.join)(this.vaultPath,(0,Nr.parkedPathFor)(S.folderName)))?"parked":"absent";h.set(S.uid,R)}let m=new Set([...h].filter(([,S])=>S==="active").map(([S])=>S));for(let S of c)Nr.CATALOG_KEEP_NAMESPACES.has(S.namespace)&&m.has(S.uid)&&p.add(S.uid);let y=[],_=[],b=[];for(let S of c){let R=(0,Nr.classifyMountTransition)({state:h.get(S.uid)??"absent",inEffectiveSet:p.has(S.uid),dependsOnKind:S.dependsOnKind});switch(R){case"none":break;case"destroy":y.push({asUid:S.uid,folderName:S.folderName,label:S.label,files:this.enumerateFilesUnder(S.folderName)});break;case"park":_.push({asUid:S.uid,folderName:S.folderName,label:S.label,files:this.enumerateFilesUnder(S.folderName)});break;case"unpark":case"materialize":b.push({asUid:S.uid,folderName:S.folderName,httpsUrl:mye(S.git),label:S.label,source:R==="unpark"?"unpark":"pull"});break}}let T=new Map;for(let S of y)T.set(S.asUid,S.files);let I=new Set(b.filter(S=>S.source==="unpark").map(S=>S.asUid)),E={targetProfileUid:t,targetProfileLabel:n,sourceProfileUid:i,sourceProfileLabel:s,filesToDestroy:T,assetSpacesBeingTornDown:y.map(S=>({asUid:S.asUid,asLabel:S.label,fileCount:S.files.length})),assetSpacesBeingParked:_.map(S=>({asUid:S.asUid,asLabel:S.label,fileCount:S.files.length})),assetSpacesBeingMaterialized:b.map(S=>({asUid:S.asUid,asLabel:S.label})),assetSpacesBeingUnparked:b.filter(S=>I.has(S.asUid)).map(S=>({asUid:S.asUid,asLabel:S.label}))};return{toDestroy:y,toPark:_,toMaterialize:b,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,lu.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,lu.join)(this.vaultPath,e)];for(;i.length>0;){let s=i.pop(),o;try{o=(0,Bc.readdirSync)(s,{withFileTypes:!0})}catch{continue}for(let c of o){let l=(0,lu.join)(s,c.name);if(c.isDirectory()){if(c.name.startsWith("."))continue;i.push(l);continue}c.isFile()&&t.push(yye(l,this.vaultPath))}}return t}readFrontmatter(e){let t;try{t=(0,Bc.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 yye(r,e){let t=e.endsWith("/")?e:e+"/";return(r.startsWith(t)?r.slice(t.length):r).split("\\").join("/")}a(yye,"relativeVaultPath");function _ye(r){let e=r.split("/").filter(t=>t.length>0);return e.length>0?e[e.length-1]:""}a(_ye,"lastSegment");ct();var qr=require("node:fs"),a7=require("node:crypto"),c7=require("node:child_process"),l7=require("node:util"),Wr=J(require("node:path"));hm();var it=J(We());ct();var Po=require("node:fs"),n7=require("node:crypto"),qi=J(require("node:path"));hm();var Vn=J(We());Bi();var r7=require("node:child_process"),Lw=J(We());var bye=6e4,Oo=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,r7.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,Lw.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(bye)})}catch(u){throw new Error(n(`GitHub request failed: ${Sye(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,Lw.enrichRateLimitError)(new Error(wye(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 Sye(r){if(r instanceof Error)return r.message;if(typeof r=="string")return r;try{return JSON.stringify(r)}catch{return String(r)}}a(Sye,"errMsg");function wye(r,e){return r.length>e?r.slice(0,e)+"...":r}a(wye,"truncate");Pt();var Eye=a(async r=>{let e=await n7.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"),vye={parse:a(r=>gd(r,{schema:Xl}),"parse"),stringify:a(r=>yd(r,{schema:Xl,lineWidth:-1}),"stringify")};function Tye(r,e){if(r.tokenFromGh===!0)return Oo.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(Tye,"resolveToken");function Aye(r){let e=a(n=>qi.join(r,n),"abs"),t=a(async(n,i)=>{let s;try{s=await Po.promises.readdir(n,{withFileTypes:!0})}catch{return}for(let o of s){let c=qi.join(n,o.name);if(o.isDirectory()){if(o.name===".git")continue;await t(c,i)}else o.isFile()&&i.push(qi.relative(r,c).split(qi.sep).join("/"))}},"walk");return{async list(){let n=[];return(0,Po.existsSync)(r)&&await t(r,n),n},read:a(n=>Po.promises.readFile(e(n),"utf-8"),"read"),async readBinary(n){return new Uint8Array(await Po.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(Aye,"nodeLocalFilesPort");function Iye(r,e){let t;try{t=(0,Vn.parkedPathFor)(e)}catch{return!1}return(0,Po.existsSync)(qi.join(r,t))}a(Iye,"isParked");function zd(r){let e=new $t(r),t=new Vn.SpaceSpecAccumulator,n=[];for(let i of e.getAllFiles()){let s=e.getFrontmatter(i);if(!s)continue;let o=(0,Vn.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,Po.existsSync)(qi.join(r,c.localPath))){Iye(r,c.localPath)&&n.push(c);continue}t.commit(o.candidate)}}return{specs:t.specs,parked:n,warnings:t.warnings}}a(zd,"collectVaultSpecs");function Rye(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,Vn.summarizeParityRound)(r))}a(Rye,"printHumanReport");function Cye(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(Cye,"printParkedSection");async function xye(r,e={}){let t=e.out??(_=>console.log(_)),n=qi.resolve(r.vault);if(!(0,Po.existsSync)(n))throw new Error(`Vault path does not exist: ${n}`);let i=Tye(r,e),s=new Oo({token:i,...r.apiBase!==void 0?{apiBase:r.apiBase}:{}}),o=e.transportFactory?.(i,r.apiBase)??s.transport(),{specs:c,parked:l,warnings:u}=zd(n);for(let _ of u)t(`warn: ${_}`);let f=r.configDir??".obsidian",d=qi.join(n,f,"plugins","exocortex",Vn.WATERMARK_STORE_FILENAME),p=new Vn.FileWatermarkStore({read:a(async()=>{try{return await Po.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 _ of l)h.push(await(0,Vn.checkParkedStaleness)(_,{transport:o,watermarks:{get:a(b=>p.get(b),"get")},redact:a(b=>s.redact(b),"redact"),...r.apiBase!==void 0?{baseURL:r.apiBase}:{}}));if(r.json!==!0&&Cye(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 m=new Vn.ParityValidator({transport:o,sha1:Eye,localFilesFor:a(_=>Aye(qi.join(n,_.localPath)),"localFilesFor"),watermarks:{get:a(_=>p.get(_),"get")},yaml:vye,redact:a(_=>s.redact(_),"redact"),...r.apiBase!==void 0?{baseURL:r.apiBase}:{}});t(`ExoSync parity check: ${c.length} repo(s), vault ${n}`);let y=await m.runRound(c,{trigger:"standalone"});return r.json===!0?t(JSON.stringify({...y,parked:h},null,2)):Rye(y,t),y.vacuous?2:y.ok?0:1}a(xye,"runExosyncParity");function i7(){return new fe("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 xye(r)}catch(e){se.handle(e,{command:"exosync-parity"}),process.exitCode=1}})}a(i7,"exosyncParityCommand");ct();var Wi=require("node:fs"),FO=require("node:crypto"),Pi=J(require("node:path")),kr=J(We());Pt();var Oye="exosync-watermarks.local.json";function s7(r,e,t){let n=NO(e,t),i=new Oo({token:n,...e.apiBase!==void 0?{apiBase:e.apiBase}:{}}),s=t.transportFactory?.(n,e.apiBase)??i.transport(),{specs:o,warnings:c}=zd(r),l=e.configDir??".obsidian",u=Pi.join(r,l,"plugins","exocortex",Oye),f=Pi.join(r,l,"plugins","exocortex",kr.CONFLICT_CACHE_STORE_FILENAME),d=new kr.LocalConflictCacheStore({io:$a(f)}),p=Pi.join(r,l,"plugins","exocortex",kr.OUTBOX_STORE_FILENAME),h=new kr.LocalOutboxStore({io:$a(p)});return{resolver:new kr.QuarantineResolver({transport:s,watermarkStore:new kr.FileWatermarkStore($a(u)),localFilesFor:a(y=>Nw(Pi.join(r,y.localPath)),"localFilesFor"),sha1:LO,...e.apiBase!==void 0?{baseURL:e.apiBase}:{},redact:a(y=>i.redact(y),"redact"),conflictCache:d,outbox:h}),specs:o,warnings:c}}a(s7,"buildResolver");async function Pye(r,e={}){let t=e.out??(l=>console.log(l)),n=Pi.resolve(r.vault);if(!(0,Wi.existsSync)(n))throw new Error(`Vault path does not exist: ${n}`);let{resolver:i,specs:s,warnings:o}=s7(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(Pye,"runQuarantineList");async function Fye(r,e,t={}){let n=t.out??(_=>console.log(_)),i=Pi.resolve(e.vault);if(!(0,Wi.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 Wi.promises.readFile(Pi.resolve(e.file),"utf-8")}}else o={take:s};let{resolver:c,specs:l,warnings:u}=s7(i,e,t);for(let _ of u)n(`warn: ${_}`);let d=(await c.listOpenConflicts(l)).filter(_=>_.path===r&&(e.repo===void 0||_.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 _ of d)n(` --repo ${_.repoKey}`);return 1}let p=d[0],h=l.find(_=>_.repoKey===p.repoKey),m=await c.resolve(h,p.path,o),y=m.awaitingPush===!0?" \u2014 awaiting push (run `exosync sync` to push it)":m.pushedSha!==void 0?` \u2014 pushed @${m.pushedSha.slice(0,7)}`:" \u2014 remote already matched";return n(`Resolved ${p.path} (${m.resolvedTo}) in ${p.repoKey}${y}.`),m.discardedLocalBackupPath!==void 0&&n(` \u21B3 your discarded local version is preserved at ${h.localPath}/${m.discardedLocalBackupPath}`),0}a(Fye,"runQuarantineResolve");function DO(r){return r.replace(/\r\n?/g,`
|
|
636
636
|
`).replace(/\n+$/,"")}a(DO,"normalizeForCompare");function Dye(r,e,t){let n=[...e].sort((o,c)=>o.path.localeCompare(c.path)),i=[],s=[];for(let o of n){let c=DO(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(Dye,"planDedupGroup");async function Lye(r,e={}){let t=e.out??(f=>console.log(f)),n=Pi.resolve(r.vault);if(!(0,Wi.existsSync)(n))throw new Error(`Vault path does not exist: ${n}`);let{specs:i,warnings:s}=zd(n);for(let f of s)t(`warn: ${f}`);let o=[];for(let f of i){let d=Pi.join(n,f.localPath);if(!(0,Wi.existsSync)(d))continue;let p=Nw(d);for(let h of await p.list())if(h.endsWith(".md"))try{o.push({path:Pi.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=>Pi.relative(n,f),"rel");if(r.auto===!0){let f=[];for(let[_,b]of c){let T=[],I=new Map;for(let E of b){let S=await Wi.promises.readFile(E,"utf-8");T.push({path:E,content:S}),I.set(E,S)}f.push({uid:_,decisions:Dye(_,T,FO.randomUUID),snap:I})}let d=0,p=0;t(`${c.length} duplicate uid(s) on disk \u2014 zero-loss plan:`);for(let{uid:_,decisions:b}of f){t(` ${_}:`);for(let T of b)T.action==="keep"?t(` KEEP ${l(T.path)} (keeps uid ${_})`):T.action==="reuuid"?(p++,t(` RE-UUID ${l(T.path)} (content differs \u2192 fresh uid; both survive, zero-loss)`)):(d++,t(` DELETE ${l(T.path)} (identical content \u2192 ${l(T.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,m=0,y=0;for(let{decisions:_,snap:b}of f){for(let T of _){if(T.action!=="delete")continue;let I;try{I=await Wi.promises.readFile(T.path,"utf-8")}catch{continue}if(DO(I)!==DO(b.get(T.path)??"")){t(` ! skipped delete ${l(T.path)} (changed since scan \u2014 re-run dedup-uids)`),y++;continue}await Wi.promises.rm(T.path,{force:!0}),t(` deleted ${l(T.path)} (identical to ${l(T.identicalTo)})`),h++}for(let T of _){if(T.action!=="reuuid")continue;let I;try{I=await Wi.promises.readFile(T.path,"utf-8")}catch{continue}if((0,kr.extractAssetUid)(I)!==T.fromUid){t(` ! skipped re-uuid ${l(T.path)} (uid changed since scan \u2014 re-run dedup-uids)`),y++;continue}let E=(0,kr.rewriteAssetUid)(I,T.toUid);E!==I&&(await Wi.promises.writeFile(T.path,E,"utf-8"),t(` re-uuid ${l(T.path)} \u2192 uid=${T.toUid} (content differs from kept copy)`),m++)}}return t(`Resolved: deleted ${h} identical copy(ies), re-uuid ${m} differing variant(s).${y>0?` ${y} skipped (re-run).`:""} Run \`exosync sync\` to propagate.`),y>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,FO.randomUUID))await Wi.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(Lye,"runDedupUids");function PO(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(PO,"withQuarantineOptions");function o7(r){let e=new fe("quarantine").description("Inspect and resolve ExoSync conflicts (quarantine resolver; RFC 4e4dc453, finding a0a3d1d6)");PO(e.command("list").description("List open conflicts that need a manual choice")).action(async t=>{try{process.exitCode=await Pye(t)}catch(n){se.handle(n,{command:"exosync quarantine list"}),process.exitCode=1}}),PO(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 Fye(t,n)}catch(i){se.handle(i,{command:"exosync quarantine resolve"}),process.exitCode=1}}),r.addCommand(e),PO(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 Lye(t)}catch(n){se.handle(n,{command:"exosync dedup-uids"}),process.exitCode=1}})}a(o7,"registerQuarantineCommands");Pt();var LO=a(async r=>{let e=await a7.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"),Nye={parse:a(r=>gd(r,{schema:Xl}),"parse"),stringify:a(r=>yd(r,{schema:Xl,lineWidth:-1}),"stringify")};function NO(r,e){if(r.tokenFromGh===!0)return Oo.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(NO,"resolveToken");function Nw(r){let e=a(i=>Wr.join(r,i),"abs"),t=a(async(i,s)=>{let o;try{o=await qr.promises.readdir(i,{withFileTypes:!0})}catch{return}for(let c of o){let l=Wr.join(i,c.name);if(c.isDirectory()){if(c.name===".git")continue;await t(l,s)}else c.isFile()&&s.push(Wr.relative(r,l).split(Wr.sep).join("/"))}},"walk"),n=a(async(i,s)=>{let o=Wr.dirname(i);await qr.promises.mkdir(o,{recursive:!0});let c=`${i}.local.tmp`;await s(c),await qr.promises.rename(c,i)},"writeAtomic");return{async list(){let i=[];return(0,qr.existsSync)(r)&&await t(r,i),i},read:a(i=>qr.promises.readFile(e(i),"utf-8"),"read"),async write(i,s){await n(e(i),o=>qr.promises.writeFile(o,s,"utf-8"))},async delete(i){await qr.promises.rm(e(i),{force:!0})},async readBinary(i){return new Uint8Array(await qr.promises.readFile(e(i)))},async writeBinary(i,s){let o=s.buffer.slice(s.byteOffset,s.byteOffset+s.byteLength);await n(e(i),c=>qr.promises.writeFile(c,Buffer.from(o)))},async stat(i){try{let s=await qr.promises.stat(e(i));return{mtimeMs:s.mtimeMs,size:s.size}}catch{return null}}}}a(Nw,"nodeLocalFilesPort");function $a(r){return{async read(){try{return await qr.promises.readFile(r,"utf-8")}catch{return null}},async writeAtomic(e){await qr.promises.mkdir(Wr.dirname(r),{recursive:!0});let t=`${r}.local.tmp`;await qr.promises.writeFile(t,e,"utf-8"),await qr.promises.rename(t,r)}}}a($a,"nodeWatermarkFileIO");function uu(r,e=".obsidian"){let t=Wr.join(r,e,"plugins","exocortex",it.MOUNT_BASE_STORE_FILENAME);return new it.FileMountBaseStore($a(t))}a(uu,"nodeMountBaseStore");var kye=(0,l7.promisify)(c7.execFile);function Mye(r){return async e=>{if(e.localPath.length===0||e.localPath.startsWith("-"))return null;try{let{stdout:t}=await kye("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(Mye,"nodeLocalBaseShaProvider");function jye(r){return{async check(e){let t=Wr.join(r,e.localPath);if(!(0,qr.existsSync)(t))return{fullyMaterialized:!1,reason:`mount folder ${e.localPath} does not exist`};let n=0;try{n=(await qr.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(jye,"nodeMaterializationCheck");function $ye(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,it.totalMs)(r.timings)>0&&e(` ${(0,it.formatRepoTimings)(r.repoKey,r.timings)}`)}a($ye,"printRepoResult");function Uye(r){return r==="conflict"||r==="full-conflict"||r==="retry-exhausted"||r==="busy"||r==="auth-required"||r==="error"}a(Uye,"isFailureStatus");async function Bye(r,e,t={}){let n=t.out??(R=>console.log(R)),i=Wr.resolve(e.vault);if(!(0,qr.existsSync)(i))throw new Error(`Vault path does not exist: ${i}`);let s=NO(e,t),o=new Oo({token:s,...e.apiBase!==void 0?{apiBase:e.apiBase}:{}}),c=t.transportFactory?.(s,e.apiBase)??o.transport(),{specs:l,warnings:u}=zd(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=Wr.join(i,f,"plugins","exocortex",it.WATERMARK_STORE_FILENAME),p=Wr.join(i,f,"plugins","exocortex",it.LOCAL_MANIFEST_STORE_FILENAME),h=Wr.join(i,f,"plugins","exocortex",it.CONFLICT_CACHE_STORE_FILENAME),m=new it.LocalConflictCacheStore({io:$a(h)}),y=Wr.join(i,f,"plugins","exocortex",it.OUTBOX_STORE_FILENAME),_=new it.LocalOutboxStore({io:$a(y)}),b=m,T=new it.SyncEngine({transport:c,watermarkStore:new it.FileWatermarkStore($a(d)),localManifestStore:new it.FileLocalManifestStore($a(p)),mountBaseStore:uu(i,f),localBaseShaProvider:Mye(i),materializationCheck:jye(i),localFilesFor:a(R=>Nw(Wr.join(i,R.localPath)),"localFilesFor"),sha1:LO,redact:a(R=>o.redact(R),"redact"),commitMessage:a((R,D,P=0)=>`exosync: ${D} change(s)${P>0?`, ${P} deletion(s)`:""} (CLI)`,"commitMessage"),mergeLayer:new it.GatedStructuredMerger(new it.StructuredMerger(Nye)),quarantine:b,outbox:_,...e.apiBase!==void 0?{baseURL:e.apiBase}:{}});n(`ExoSync ${r}: ${l.length} repo(s), vault ${i}`);let I=(0,it.orderChildrenFirst)(l),E=e.json===!0?void 0:R=>{n(`[ExoSync] ${R.repoKey}: ${(0,it.syncProgressPhaseText)(R)}`)},S=await T.syncAll(I,r,E);if(e.json===!0)n(JSON.stringify(S,null,2));else{for(let P of S)$ye(P,n);let R=S.reduce((P,M)=>({pulled:P.pulled+M.pulledCount,pushed:P.pushed+M.pushedCount,merged:P.merged+M.mergedCount,quarantined:P.quarantined+M.quarantinedCount}),{pulled:0,pushed:0,merged:0,quarantined:0});n(`Summary: ${S.length} repo(s) \u2014 pulled ${R.pulled}, pushed ${R.pushed}, merged ${R.merged}, quarantined ${R.quarantined}`);let D=(0,it.aggregateTimings)(S);(0,it.totalMs)(D)>0&&n((0,it.formatTimingsLine)(D))}return S.some(R=>Uye(R.status))?1:0}a(Bye,"runExosyncSync");function kO(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(kO,"withSyncOptions");function MO(r){return async e=>{try{process.exitCode=await Bye(r,e)}catch(t){se.handle(t,{command:`exosync ${r}`}),process.exitCode=1}}}a(MO,"makeDirectionAction");function u7(){let r=new fe("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 kO(r.command("sync").description("Full pull\u2192merge\u2192push cycle for every materialized repo")).action(MO("sync")),kO(r.command("pull").description("Apply remote changes only (nothing leaves the device; local changes re-derive)")).action(MO("pull")),kO(r.command("push").description("Send the local delta only (remote changes are pinned to re-derive on the next pull/sync)")).action(MO("push")),o7(r),r}a(u7,"exosyncCommand");var Vye=a(r=>{process.stderr.write(`${r}
|
|
637
637
|
`)},"DEFAULT_LOG"),kw=class{static{a(this,"HeadlessConfirmGate")}constructor(e){this.yes=e.yes,this.verbose=e.verbose??!1,this.log=e.log??Vye}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)}};Pt();qt();mr();async function Gye(r,e,t={}){let n=[],i=[],s=t.out??(R=>process.stdout.write(`${R}
|
|
638
638
|
`)),o=t.err??(R=>process.stderr.write(`${R}
|
|
@@ -666,7 +666,7 @@ Next steps:
|
|
|
666
666
|
`).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(e0e,"parseLastJsonLine");function P7(r,e){return e.filter(t=>{let n=r[t];return typeof n!="string"||n.trim().length===0})}a(P7,"missingStringFields");var Bw=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:O7,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:O7,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:Jye,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:x7}};case"tools/call":return{jsonrpc:"2.0",id:i,result:await this.callTool(e.params??{})};default:return{jsonrpc:"2.0",id:i,error:{code:Zye,message:`Method not found: ${e.method}`}}}}async callTool(e){let t=e.name,n=e.arguments??{},i;if(t==="create_asset"){let c=P7(n,["vault","class","label"]);if(c.length>0)return Xd(`create_asset: missing required argument(s): ${c.join(", ")}`,!0);let l=I7(n);if(l!==null)return Xd(`create_asset: ${l}`,!0);i=R7(n)}else if(t==="create_effort"){let c=P7(n,["vault","command","target"]);if(c.length>0)return Xd(`create_effort: missing required argument(s): ${c.join(", ")}`,!0);i=C7(n)}else return Xd(`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 Xd(`exocortex-cli exited ${s.exitCode}
|
|
667
667
|
${c}`,!0)}let o=e0e(s.stdout);return Xd(o===void 0?s.stdout.trim():JSON.stringify(o))}};var L7=require("child_process");var t0e=32*1024*1024;function N7(r){return e=>new Promise(t=>{(0,L7.execFile)(process.execPath,[r,...e],{maxBuffer:t0e},(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(N7,"createSelfCliRunner");async function r0e(r,e,t){let n=(0,k7.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:F7,message:"Parse error: invalid JSON"}})+`
|
|
668
668
|
`);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:D7,message:`Internal error: ${l instanceof Error?l.message:String(l)}`}}}c!==null&&t(JSON.stringify(c)+`
|
|
669
|
-
`)}}a(r0e,"runStdioServer");function M7(r){return new fe("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 Bw({runCli:N7(t),version:r??"unknown"});await r0e(n,process.stdin,i=>process.stdout.write(i))})}a(M7,"mcpCommand");function Vw(r){let e=new fe;return e.name("exocortex").description("CLI tool for Exocortex knowledge management system - SPARQL queries, task management, and more").version(r??"16.235.
|
|
669
|
+
`)}}a(r0e,"runStdioServer");function M7(r){return new fe("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 Bw({runCli:N7(t),version:r??"unknown"});await r0e(n,process.stdin,i=>process.stdout.write(i))})}a(M7,"mcpCommand");function Vw(r){let e=new fe;return e.name("exocortex").description("CLI tool for Exocortex knowledge management system - SPARQL queries, task management, and more").version(r??"16.235.9"),e.addCommand(SB()),e.addCommand(xB()),e.addCommand(h6()),e.addCommand(_6()),e.addCommand(U6()),e.addCommand(b6()),e.addCommand(A6()),e.addCommand(I6()),e.addCommand(z6()),e.addCommand(J6()),e.addCommand(uB()),e.addCommand(pB()),e.addCommand(hB()),e.addCommand(gB()),e.addCommand(yB()),e.addCommand(YB()),e.addCommand(JB()),e.addCommand(p7()),e.addCommand(m7()),e.addCommand(_7()),e.addCommand(b7()),e.addCommand(v7()),e.addCommand(i7()),e.addCommand(u7()),e.addCommand(T7()),e.addCommand(M7(r??"16.235.9")),e}a(Vw,"createProgram");Vw().parse();0&&(module.exports={createProgram});
|
|
670
670
|
/*! Bundled license information:
|
|
671
671
|
|
|
672
672
|
reflect-metadata/Reflect.js:
|
package/package.json
CHANGED