hofi-cli 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/hofi.js +2 -2
  2. package/package.json +1 -1
package/dist/hofi.js CHANGED
@@ -31,7 +31,7 @@ System Environment Report`),console.log(i.join(`
31
31
  `);return["[defaults.mime.defaultApplications]",t(i.defaultApplications),"","[defaults.mime.addedAssociations]",t(i.addedAssociations)].join(`
32
32
  `)}function $6(r,i){let{description:t,uuid:$,type:n,options:v,wantedBy:u}=i;return["[Unit]",`Description=${t}`,"","[Mount]",`What=/dev/disk/by-uuid/${$}`,`Where=${r}`,`Type=${n}`,`Options=${v.join(",")}`,"","[Install]",`WantedBy=${u}`].join(`
33
33
  `)}import{hostname as z_}from"os";import{dirname as j_}from"path";function J_(r,i,t){let $={generatedAt:new Date().toLocaleString(),hostname:z_(),configFile:t,environment:{...i},config:{...r}};return JSON.stringify($,null,2)+`
34
- `}async function Di(r,i,t){let $=`${J_(r,i,t)}`,n=`${j_(t)}/hofi-lock.json`;await Wr($,n)}async function u6(r){if(!await L(r))return{};let t=await Bun.file(r).text();return Bun.JSONC.parse(t).config}async function o6(){li(),j.info("Creating Project With Template");let[r,i]=await Promise.all([t6(),v6()]),t=Bun.TOML.parse(r),$=Bun.TOML.parse(i);delete t?.includes;let n={...t,...$},v=await Br();await Promise.all([Wr(r,"hofi/config.toml"),Wr(i,"hofii/mimeapps.toml"),Di(n,v,"hofii/hofi-lock.json")])}import{resolve as Uv,dirname as I6}from"path";async function Dv(r,i=new Set){let t=Uv(r);if(i.has(t))throw Error(`Circular dependency detected: ${t}`);i.add(t);let $=await Bun.file(t).text(),n=Bun.TOML.parse($),{includes:v=[],symlinks:u={}}=n;delete n.includes;let o=I6(t);n.symlinks=k_(o,u);let I={};for(let g of v){let c=Uv(I6(t),g),U=await Dv(c,i);I=lv(I,U)}return lv(I,n)}function lv(r,i){let t={...r};for(let $ in i)if(g6(t[$])&&g6(i[$]))t[$]=lv(t[$],i[$]);else t[$]=i[$];return t}function g6(r){return typeof r==="object"&&r!==null&&!Array.isArray(r)}function k_(r,i){if(!i)return{};let t={};for(let $ in i){let n=i[$];if(!n)continue;let{target:v,link:u}=n,o=Uv(r,v),I=m(u);t[$]={target:o,link:I}}return t}var{$:ev}=globalThis.Bun;async function c6(r){if(!r)return;let i=(await Promise.all(Object.entries(r).map(async([t,{target:$,link:n}])=>{let[v,u]=await Promise.all([m($),m(n)]);if(v===u)throw Error(`Target and symlink cannot be the same: ${$} -> ${n}`);if(!await L(v))return` - ${$}`}))).filter(Boolean);if(i.length>0)throw Error(`Target does not exist: ${i.join(`
34
+ `}async function Di(r,i,t){let $=`${J_(r,i,t)}`,n=`${j_(t)}/hofi-lock.json`;await Wr($,n)}async function u6(r){if(!await L(r))return{};let t=await Bun.file(r).text();return Bun.JSONC.parse(t).config}async function o6(){li(),j.info("Creating Project With Template");let[r,i]=await Promise.all([t6(),v6()]),t=Bun.TOML.parse(r),$=Bun.TOML.parse(i);delete t?.includes;let n={...t,...$},v=await Br();await Promise.all([Wr(r,"hofi/config.toml"),Wr(i,"hofi/mimeapps.toml"),Di(n,v,"hofi/hofi-lock.json")])}import{resolve as Uv,dirname as I6}from"path";async function Dv(r,i=new Set){let t=Uv(r);if(i.has(t))throw Error(`Circular dependency detected: ${t}`);i.add(t);let $=await Bun.file(t).text(),n=Bun.TOML.parse($),{includes:v=[],symlinks:u={}}=n;delete n.includes;let o=I6(t);n.symlinks=k_(o,u);let I={};for(let g of v){let c=Uv(I6(t),g),U=await Dv(c,i);I=lv(I,U)}return lv(I,n)}function lv(r,i){let t={...r};for(let $ in i)if(g6(t[$])&&g6(i[$]))t[$]=lv(t[$],i[$]);else t[$]=i[$];return t}function g6(r){return typeof r==="object"&&r!==null&&!Array.isArray(r)}function k_(r,i){if(!i)return{};let t={};for(let $ in i){let n=i[$];if(!n)continue;let{target:v,link:u}=n,o=Uv(r,v),I=m(u);t[$]={target:o,link:I}}return t}var{$:ev}=globalThis.Bun;async function c6(r){if(!r)return;let i=(await Promise.all(Object.entries(r).map(async([t,{target:$,link:n}])=>{let[v,u]=await Promise.all([m($),m(n)]);if(v===u)throw Error(`Target and symlink cannot be the same: ${$} -> ${n}`);if(!await L(v))return` - ${$}`}))).filter(Boolean);if(i.length>0)throw Error(`Target does not exist: ${i.join(`
35
35
  `)}`);j.info("Validation symlink passed")}async function Jn(r,i){if(!r)return;let t=[];if(i==="flatpak"){let $=await ev`flatpak remote-ls flathub --columns=application`.quiet().text(),n=new Set($.split(`
36
36
  `).filter(Boolean));for(let v of r)if(!n.has(v.trim()))t.push(` -> ${v}`)}else{let $=await ev`${i} -Slq`.quiet().text(),n=new Set($.split(`
37
37
  `));for(let v of r)if(!n.has(v.trim()))t.push(` -> ${v}`)}if(t.length>0)throw Error(`Packages not found:
@@ -106,4 +106,4 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
106
106
  \uD83D\uDCBD Mounts`),console.table(i)}if(r.symlinks){let i=GD(r.symlinks);if(i.length>0)console.log(`
107
107
  \uD83D\uDD17 Symlinks`),console.table(i)}if(r.defaults){let i=XD(r.defaults);if(i.length>0)console.log(`
108
108
  \u2699\uFE0F Defaults`),console.table(i)}}import{isDeepStrictEqual as zb}from"util";function VD(r,i){let t=[...i].filter((n)=>!r.has(n)),$=[...r].filter((n)=>!i.has(n));return{added:t,removed:$}}function LD(r={},i={}){let t={},$={},n=new Set([...Object.keys(r),...Object.keys(i)]);for(let v of n){let u=r[v],o=i[v];if(!(v in r)){t[v]=o;continue}if(!(v in i)){$[v]=u;continue}if(!zb(u,o))$[v]=u,t[v]=o}return{added:t,removed:$}}function pt(r,i){if(r===void 0&&i===void 0)return;let t=VD(new Set(r??[]),new Set(i??[]));if(t.added.length===0&&t.removed.length===0)return;return t}function Ob(r,i){if(!r&&!i)return;let t=LD(r,i);if(Object.keys(t.added).length===0&&Object.keys(t.removed).length===0)return;return t}async function jb(r,i){let t={};if(i.packages||r.packages){let v={},u=pt(i.packages?.pacman,r.packages?.pacman);if(u)v.pacman=u;let o=pt(i.packages?.aur,r.packages?.aur);if(o)v.aur=o;let I=pt(i.packages?.flatpak?.user,r.packages?.flatpak?.user),g=pt(i.packages?.flatpak?.system,r.packages?.flatpak?.system);if(I||g){let c={},U={};if(I?.added.length)c.user=I.added;if(I?.removed.length)U.user=I.removed;if(g?.added.length)c.system=g.added;if(g?.removed.length)U.system=g.removed;if(Object.keys(c).length>0||Object.keys(U).length>0)v.flatpak={added:c,removed:U}}if(Object.keys(v).length>0)t.packages=v}let $=Ob(i.mounts,r.mounts);if($)t.mounts=$;let n=Ob(i.symlinks,r.symlinks);if(n)t.symlinks=n;if(r.defaults&&!zb(i.defaults,r.defaults))t.defaults=r.defaults;return t}var{$:Jb}=globalThis.Bun;import*as lr from"fs/promises";function qD(r){return typeof r==="object"&&r!==null&&"code"in r}function at(r){if(qD(r))return r.code??"UNKNOWN_ERROR";return"UNKNOWN_ERROR"}async function kb(r){if(!r)return;for(let i of Object.values(r)){let{link:t}=i;if(!await Eb(t))continue;await lr.unlink(t),j.info(`Removed symlink: ${t}`)}}async function Wb(r){if(!r)return;let i={backup:[],create:[]},t=[],$=[];await Promise.all(Object.values(r).map(async(u)=>{await QD(u,i)}));let{backup:n,create:v}=i;try{for(let u of n){let o=await bi(u);if(o)j.info(`Backup path: ${o}`);$.push(o)}for(let{target:u,link:o}of v)await YD(u,o),t.push(o)}catch(u){await KD(t,$),C(`Error caused with code ${at(u)}, restore applied config`)}j.success("Symlinks created successfully")}async function KD(r,i){for(let t of[...r].reverse())await lr.rm(t,{force:!0,recursive:!0}),j.info(`Removed: ${t}`);for(let t of[...i].reverse()){if(!t)continue;let $=`${t}.bak`;await lr.rename($,t),j.info(`Restore: ${$} -> ${t}`)}j.success("Symlinks restored successfully")}async function QD(r,i){let{target:t,link:$}=r,n=await L(t),v=await L($);if(!n)throw Error(`Target path does not exist: ${t}`);if(v)i.backup.push($);i.create.push(r)}async function YD(r,i){let t=!1,[$,n]=await Promise.all([Eb(i),HD(i)]);if(n||$)await lr.unlink(i),j.warn(`Recreate symlink: ${r} -> ${i}`),t=!0;if(await lr.symlink(r,i),!t)j.info(`Creating symlink: ${r} -> ${i}`)}async function HD(r){let i=m(r);try{return await Jb`[ -L ${i} ] && [ ! -e ${i} ]`,!0}catch{return!1}}async function Eb(r){let i=m(r);try{return await Jb`[ -L ${i} ] && [ -e ${i} ]`,!0}catch{return!1}}async function Gb(r){if(!r)return;let{added:i,removed:t}=r;await Wb(i),await kb(t)}async function Xb(r){if(!r)return;let{pacman:i,aur:t,flatpak:$}=r;try{await AD(i),await PD(t),await BD($)}catch(n){let v=at(n);C(`Failed to install packages, error code: ${v}`)}}async function AD(r){if(!r)return;let{added:i,removed:t}=r;if(i.length===0&&t.length===0)return;await p4(i),await a4(t)}async function PD(r){if(!r)return;let{added:i,removed:t}=r;if(i.length===0&&t.length===0)return;await h4(i),await y4(t)}async function BD(r){if(!r)return;let{added:i,removed:t}=r;await m4(i.user??[]),await f4(t.user??[]),await C4(i.system??[]),await d4(t.system??[])}async function Vb(r,i){if(!r)return;let{apps:t,mime:$}=r,n=`${i}/.config/mimeapps.list`;if(await L(n)){let g=await bi(n);if(g)j.info(`Backup created: ${g}`)}let u=t?await i6(t,"object"):{},o={defaultApplications:{...$?.defaultApplications??{},...u},addedAssociations:{...$?.addedAssociations??{}}},I=n6(o);await Wr(I,`${i}/.config/mimeapps.list`)}var{$:wn}=globalThis.Bun;var st="/etc/systemd/system";async function Lb(r){for(let[$,{type:n,uuid:v}]of Object.entries(r))await FD($,n,v);let i=await Promise.all(Object.entries(r).map(async([$,n])=>{let v=$6($,n),u=`${Kb($).replaceAll("/","-")}.mount`,o=`${st}/${u}`,I=await L($),g=await L(o);if(I&&g)try{j.warn(`Remount ${$}`),await wn`sudo systemctl disable --now ${u}`,console.log(),await ci(o,!0)}catch(c){console.log(),console.error(c),C([`Failed to remount ${$}.`,"The mount may still be in use.",`Run: sudo fuser -vm ${$}`,"or",`Run: sudo lsof +D ${$}`].join(`
109
- `))}return[u,v]})),t=await Promise.all(i.map(async([$,n])=>{return await wn`printf ${n} | sudo tee ${st}/${$}`.quiet(),$}));try{await wn`sudo systemctl daemon-reload`,await wn`sudo systemctl enable --now ${t.join(" ")}`,console.log()}catch($){for(let n of t){let v=`${st}/${n}`;if(await L(v))await ci(v,!0)}C("Error enabling systemd units for mount points")}}async function qb(r){for(let i of Object.keys(r)){let t=`${Kb(i).replaceAll("/","-")}.mount`,$=`${st}/${t}`;if(await wn`sudo systemctl disable --now ${t}`,await L($))await ci($,!0)}}async function FD(r,i,t){let $=!1,n=await L(r);if(!n)await P4(r,!0);try{await y(["sudo","mount","-t",i,`/dev/disk/by-uuid/${t}`,r]),console.log(),$=!0}catch(v){console.error(v),C(`Failed to mount ${r} (${i}, ${t})`)}finally{if(j.info("Cleanup Tested Drive"),$)await wn`sudo umount ${r}`;if(!n)await B4(r,!0)}}function Kb(r){return r.replace(/^\/+|\/+$/g,"")}async function Qb(r){if(!r)return;let{added:i,removed:t}=r;if(i)await Lb(i);if(t)await qb(t)}async function Yb(r,i){let t=jn(),$=r?m(r):process.cwd(),n=await _i($);if(!await L(n))C("Config file not found");if(!r)j.warn(`No directory specified, using current working directory (${$})`);j.info(`Switching to new hofi configuration (${n})`),j.info("New Switch OCnfig");let u=await Br(),{config:o,diff:I}=await TD($),{packages:g,mounts:c,symlinks:U,defaults:w}=I;if(Nb(I),Object.keys(I).length===0){j.info("No changes to apply");return}if((prompt("Apply The Change ?")||"n")?.toLowerCase()!=="y")return;j.info("Applying change..."),await Xb(g),await Promise.all([Gb(U),Qb(c),Vb(w,t)]),await Di(o,u,n)}async function TD(r){let i=await dt(r);if(!i)C("Error parse config. Aborting process");let t=await u6(`${r}/hofi-lock.json`),$=await jb(i,t);return{config:i,diff:$}}var MD="0.1",Sn=k4("hofi").version(MD);Sn.command("scan","Detect system environment").action(x4);Sn.command("init","Initialize a new hofi project with a template").action(o6);Sn.command("validate [configPath]","Validate the hofi project configuration").action(dt);Sn.command("switch [dir]","Switch to a different hofi project").option("--dry-run","Run the switch command without making any changes").action(async(r,i)=>{await Yb(r,i)});Sn.help();try{Sn.parse()}catch(r){j.error(String(r)),process.exit(1)}
109
+ `))}return[u,v]})),t=await Promise.all(i.map(async([$,n])=>{return await wn`printf ${n} | sudo tee ${st}/${$}`.quiet(),$}));try{await wn`sudo systemctl daemon-reload`,await wn`sudo systemctl enable --now ${t.join(" ")}`,console.log()}catch($){for(let n of t){let v=`${st}/${n}`;if(await L(v))await ci(v,!0)}C("Error enabling systemd units for mount points")}}async function qb(r){for(let i of Object.keys(r)){let t=`${Kb(i).replaceAll("/","-")}.mount`,$=`${st}/${t}`;if(await wn`sudo systemctl disable --now ${t}`,await L($))await ci($,!0)}}async function FD(r,i,t){let $=!1,n=await L(r);if(!n)await P4(r,!0);try{await y(["sudo","mount","-t",i,`/dev/disk/by-uuid/${t}`,r]),console.log(),$=!0}catch(v){console.error(v),C(`Failed to mount ${r} (${i}, ${t})`)}finally{if(j.info("Cleanup Tested Drive"),$)await wn`sudo umount ${r}`;if(!n)await B4(r,!0)}}function Kb(r){return r.replace(/^\/+|\/+$/g,"")}async function Qb(r){if(!r)return;let{added:i,removed:t}=r;if(i)await Lb(i);if(t)await qb(t)}async function Yb(r,i){let t=jn(),$=r?m(r):process.cwd(),n=await _i($);if(!await L(n))C("Config file not found");if(!r)j.warn(`No directory specified, using current working directory (${$})`);j.info(`Switching to new hofi configuration (${n})`),j.info("New Switch OCnfig");let u=await Br(),{config:o,diff:I}=await TD($),{packages:g,mounts:c,symlinks:U,defaults:w}=I;if(Nb(I),Object.keys(I).length===0){j.info("No changes to apply");return}if((prompt("Apply The Change ?")||"n")?.toLowerCase()!=="y")return;j.info("Applying change..."),await Xb(g),await Promise.all([Gb(U),Qb(c),Vb(w,t)]),await Di(o,u,n)}async function TD(r){let i=await dt(r);if(!i)C("Error parse config. Aborting process");let t=await u6(`${r}/hofi-lock.json`),$=await jb(i,t);return{config:i,diff:$}}var MD="0.1.0",Sn=k4("hofi").version(MD);Sn.command("scan","Detect system environment").action(x4);Sn.command("init","Initialize a new hofi project with a template").action(o6);Sn.command("validate [configPath]","Validate the hofi project configuration").action(dt);Sn.command("switch [dir]","Switch to a different hofi project").option("--dry-run","Run the switch command without making any changes").action(async(r,i)=>{await Yb(r,i)});Sn.help();try{Sn.parse()}catch(r){j.error(String(r)),process.exit(1)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hofi-cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "bin": {