@sleekcms/sync 2.2.0 → 2.3.0
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 +4 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{readFileSync as
|
|
2
|
+
import{readFileSync as rn}from"node:fs";import{fileURLToPath as sn}from"node:url";import{createRequire as on}from"node:module";import ie from"node:path";import{render as an}from"ink";import{execSync as Dt,spawn as L}from"child_process";import{program as Pt,Option as ut}from"commander";function $t(){return Pt.name("cms-sync").description("SleekCMS CLI tool to sync and edit CMS templates locally. Log in once, then add the sites you want to sync \u2014 changes are watched and pushed automatically.").addOption(new ut("-v, --version","output the version number").hideHelp()).addOption(new ut("-e, --env <env>","Environment (localhost, development, production)").default("production").hideHelp()).addOption(new ut("-d, --dev <secret>","Dev secret for non-production envs (sent as the _dev cookie; enables OTP-in-response). Falls back to SLEEKCMS_DEV_SECRET.").hideHelp()).option("-p, --path <path>","Directory path for files (default: ~/SleekCMS)").addHelpText("after",`
|
|
3
3
|
Examples:
|
|
4
4
|
$ sleekcms # log in and pick sites to sync
|
|
5
5
|
$ sleekcms -e development # use a non-production backend
|
|
6
|
-
`).parse(process.argv),
|
|
7
|
-
`)){let s
|
|
6
|
+
`).parse(process.argv),Pt.opts()}function I(t){let e=process.platform==="win32"?`where ${t}`:`which ${t}`;try{return Dt(e,{stdio:"ignore"}),!0}catch{return!1}}function Rt(t){if(process.platform!=="darwin")return!1;try{return Dt(`osascript -e 'id of application "${t}"'`,{stdio:"ignore"}),!0}catch{return!1}}function fe(t,e){let n={detached:!0,stdio:"ignore",windowsHide:!0};process.platform==="win32"?L("cmd.exe",["/c",t,...e],n).unref():L(t,e,n).unref()}var Ot=[{name:"VS Code",cmd:"code",args:t=>["-n",t]},{name:"Cursor",cmd:"cursor",args:t=>["-n",t]}];function Mt(t,e){let r=(e?Ot.filter(s=>s.cmd===e):Ot).find(s=>I(s.cmd));return r?(fe(r.cmd,r.args(t)),r.name):null}function ye(t){if(process.platform==="darwin")return L("open",["-a","Terminal",t],{detached:!0,stdio:"ignore"}).unref(),!0;if(process.platform==="win32"){if(I("wt"))L("wt.exe",["-d",t],{detached:!0,stdio:"ignore"}).unref();else{let e=I("powershell")?"powershell.exe":"cmd.exe";L("cmd.exe",["/c","start","",e],{cwd:t,detached:!0,stdio:"ignore"}).unref()}return!0}return!1}function we(t){return Rt("iTerm")?(L("open",["-a","iTerm",t],{detached:!0,stdio:"ignore"}).unref(),!0):!1}function Se(t){if(process.platform!=="darwin"||!I("claude"))return!1;let e=`tell application "Terminal" to do script ("cd " & quoted form of "${t}" & " && claude")`;return L("osascript",["-e",e,"-e",'tell application "Terminal" to activate'],{detached:!0,stdio:"ignore"}).unref(),!0}function At(){let t=process.platform==="darwin",e=process.platform==="win32",n=[];return(t||e)&&n.push({key:"terminal",label:"terminal",run:ye}),Rt("iTerm")&&n.push({key:"iterm",label:"iterm",run:we}),I("code")&&n.push({key:"code",label:"vscode",run:r=>Mt(r,"code")!==null}),I("cursor")&&n.push({key:"cursor",label:"cursor",run:r=>Mt(r,"cursor")!==null}),t&&I("claude")&&n.push({key:"claude",label:"claude",run:Se}),n}import{EventEmitter as We}from"node:events";import Ht from"fs-extra";import Xt from"path";import w from"fs-extra";import be from"os";import b from"path";var Bt={localhost:"http://app.sleekcms.test/api/mcp",development:"https://app.sleekcms.dev/api/mcp",production:"https://app.sleekcms.com/api/mcp"},ve=["src/views/blocks","src/views/entries","src/views/pages","src/views/layouts","src/models/blocks","src/models/entries","src/models/pages","src/content/pages","src/content/entries","src/public/js","src/public/css"],lt=class extends Error{status;body;constructor(e,n,r){super(e),this.status=n,this.body=r}};async function dt(t,e,n,r,s){let i=await fetch(t+r,{method:n,headers:{Authorization:`Bearer ${e}`,...s!==void 0?{"Content-Type":"application/json"}:{}},body:s!==void 0?JSON.stringify(s):void 0});if(!i.ok){let a=await i.text();throw new lt(`${n} ${r} \u2192 ${i.status}: ${a}`,i.status,a)}return i.json()}async function Te(t,e){e&&(await w.outputFile(b.join(t,"AGENT.md"),e),await w.outputFile(b.join(t,"CLAUDE.md"),e),await w.outputFile(b.join(t,".vscode","copilot-instructions.md"),e)),await w.outputFile(b.join(t,".vscode","settings.json"),JSON.stringify({"files.associations":{"*.model":"javascript"},"[javascript]":{"editor.defaultFormatter":"esbenp.prettier-vscode"},"js/ts.validate.enabled":!1},null,2))}async function ke(t,e){let n=b.join(t,".cache","token");if(await w.pathExists(n)){if((await w.readFile(n,"utf-8")).trim()!==e)throw new Error(`Workspace at ${t} is tied to a different token. Remove ${n} or use a different workspace.`);return}await w.outputFile(n,e)}function Ee(t,e){let n=`${e.name.substr(0,20)} ${e.id}`.replace(/[\s_]+/g,"-").toLowerCase(),r=t||b.join(be.homedir(),"SleekCMS");return b.resolve(r,n)}async function ht(t){let e=(t.token||"").trim();if(!e)throw new Error("syncSite: token is required");let n=t.log??(v=>console.log(v)),r=t.onProgress,s=(t.env||e.split("-")[2]||"production").toLowerCase(),i=Bt[s]||Bt.production,a=await dt(i,e,"GET","/get_site"),d=t.viewsDir?b.resolve(t.viewsDir):Ee(t.path,a);await w.ensureDir(d),await Promise.all(ve.map(v=>w.ensureDir(b.join(d,v)))),await ke(d,e);let h=b.join(d,".cache","state.json");t.flush&&await w.remove(h);let S=!await w.pathExists(h),f=S?{}:(await w.readJson(h)).fileMap||{},p=0,y=0;return S?({fileMap:f,pulled:y}=await Be(d,i,e,n,r),await Te(d,t.agentMd)):p=await Oe(d,f,i,e,n,r),await w.outputJson(h,{fileMap:f},{spaces:2}),{viewsDir:d,site:a,isFirstRun:S,pushed:p,pulled:y}}var Ce=new Set([".DS_Store","Thumbs.db","desktop.ini"]);async function Pe(t){let e=b.join(t,"src");if(!await w.pathExists(e))return[];let n=[];async function r(s){let i=await w.readdir(s,{withFileTypes:!0});for(let a of i){if(Ce.has(a.name))continue;let d=b.join(s,a.name);a.isDirectory()?await r(d):a.isFile()&&n.push(b.relative(t,d).replace(/\\/g,"/"))}}return await r(e),n}async function Oe(t,e,n,r,s,i){let a=[];for(let p of await Pe(t)){let y=b.join(t,p),v=e[p],O=await w.stat(y);if(v&&v.mtimeMs===O.mtimeMs)continue;let D=await w.readFile(y,"utf-8");D.trim()&&a.push({rel:p,full:y,stat:O,content:D,prior:v})}if(a.length===0)return 0;let d;try{d=await dt(n,r,"POST","/save_files",a.map(p=>({path:p.rel,content:p.content})))}catch(p){let y=p;return s(`\u274C Error saving files: ${y.body||y.message}`),0}let h=await $e(t),S=0,f=Me(d);for(let p=0;p<a.length;p++){let y=a[p],v=De(y,p,d,f);if(i?.(p+1,a.length),!v){let O="No save response returned";h[y.rel]=O,s(`\u274C Error saving ${y.rel}: ${O}`);continue}if(v.error){h[y.rel]=v.error,s(`\u274C Error saving ${y.rel}: ${v.error}`);continue}delete h[y.rel],e[y.rel]={mtimeMs:y.stat.mtimeMs},s(`\u2705 ${y.prior?"Updated":"Created"} ${y.rel}`),S++}return await Ae(t,h),S}function Me(t){let e=new Map;for(let n of t)n.path&&e.set(n.path,n);return e}function De(t,e,n,r){let s=r.get(t.rel);if(s)return s;let i=n[e];if(!i?.path||i.path===t.rel)return i}var pt="sync-errors.log",gt=new Map;async function $e(t){let e=b.resolve(t),n=gt.get(e);if(n)return n;let r=await Re(e);return gt.set(e,r),r}async function Re(t){let e=b.join(t,pt);if(!await w.pathExists(e))return{};let n=await w.readFile(e,"utf-8"),r={};for(let s of n.split(`
|
|
7
|
+
`)){let i=s.indexOf(": ");i>0&&(r[s.slice(0,i)]=s.slice(i+2))}return r}async function Ae(t,e){let n=b.join(t,pt),r=Object.entries(e);if(r.length===0){await w.remove(n);return}await w.outputFile(n,r.map(([s,i])=>`${s}: ${i}`).join(`
|
|
8
8
|
`)+`
|
|
9
|
-
`)}async function qe(t,e,n,r,i){ht.delete(v.resolve(t)),await w.remove(v.join(t,ft)),r("\u{1F4E5} Fetching files...");let s=await pt(e,n,"GET","/get_files"),a={},d=0;for(let h of s){let S=v.join(t,h.path);await w.outputFile(S,h.content);let f=(await w.stat(S)).mtimeMs;d++,i?.(d,s.length),a[h.path]={mtimeMs:f}}return r(`\u2714\uFE0F Synced ${s.length} file(s).`),{fileMap:a,pulled:d}}import Je from"path";import Ue from"chokidar";var He=5e3,Wt=3600*1e3,Xe=60*1e3,ze=/(^|[/\\])(\.DS_Store|Thumbs\.db|desktop\.ini)$/,z=class{viewsDir;onSync;onActivity;debounceMs;watcher=null;debounceTimer=null;isShuttingDown=!1;dirty=!1;syncInFlight=!1;constructor(e){this.viewsDir=e.viewsDir,this.onSync=e.onSync,this.onActivity=e.onActivity??(()=>{}),this.debounceMs=e.debounceMs??He}start(){if(this.watcher)return;let e=Je.join(this.viewsDir,"src");this.watcher=Ue.watch([e],{persistent:!0,ignoreInitial:!0,ignored:ze}).on("change",()=>this.scheduleSync()).on("add",()=>this.scheduleSync()).on("unlink",()=>this.scheduleSync())}setShuttingDown(e){this.isShuttingDown=e}async stop(){this.debounceTimer&&(clearTimeout(this.debounceTimer),this.debounceTimer=null),this.watcher&&(await this.watcher.close(),this.watcher=null)}scheduleSync(){this.isShuttingDown||(this.dirty=!0,this.debounceTimer&&clearTimeout(this.debounceTimer),this.debounceTimer=setTimeout(()=>this.flush(),this.debounceMs),W(),this.onActivity())}async flush(){if(this.debounceTimer=null,!(!this.dirty||this.isShuttingDown)){if(this.syncInFlight){this.debounceTimer=setTimeout(()=>this.flush(),this.debounceMs);return}this.dirty=!1,this.syncInFlight=!0;try{await this.onSync()}catch(e){let n=e;console.error("\u274C Sync failed:",n.body||n.message)}finally{this.syncInFlight=!1}}}},G=null,St=0,yt=null,wt=Wt,xt=!1;function qt(t,e=Wt){yt=t,wt=e,xt=!1,St=Date.now(),!G&&(G=setInterval(()=>{xt||!yt||Date.now()-St<wt||(vt(),console.log(`
|
|
10
|
-
\u{1F4A4} No changes for ${Math.round(wt/6e4)} minutes. Terminating.`),yt())},Xe),G.unref?.())}function W(){St=Date.now()}function vt(){xt=!0,G&&(clearInterval(G),G=null)}import nt from"fs-extra";import Ke from"os";import Ut from"path";var Jt={localhost:"http://app.sleekcms.test/api",development:"https://app.sleekcms.dev/api",production:"https://app.sleekcms.com/api"};function Tt(t){return Jt[t]||Jt.production}var Ve=Ut.join(Ke.homedir(),"SleekCMS"),rt=Ut.join(Ve,"auth.json"),bt;function Ht(t){bt=t||void 0}function kt(t){let e=[];return t&&e.push(`_auth=${t}`),bt&&e.push(`_dev=${bt}`),e.length?e.join("; "):void 0}var P=class extends Error{status;body;constructor(e,n,r=""){super(e),this.name="UserAuthError",this.status=n,this.body=r}};async function Xt(){try{let t=await nt.readJson(rt);return t&&t.userToken?t:null}catch{return null}}async function zt(t){await nt.outputJson(rt,t,{spaces:2});try{await nt.chmod(rt,384)}catch{}}async function Kt(){await nt.remove(rt)}async function K(t){let e={},n=kt(t.userToken);n&&(e.Cookie=n),t.subdomain&&(e["x-subdomain"]=t.subdomain),t.slug&&(e.site=t.slug),t.body!==void 0&&(e["Content-Type"]="application/json");let r=await fetch(Tt(t.env)+t.path,{method:t.method,headers:e,body:t.body!==void 0?JSON.stringify(t.body):void 0});if(!r.ok){let i=await r.text().catch(()=>"");throw new P(`${t.method} ${t.path} \u2192 ${r.status}: ${i}`,r.status,i)}return r.json()}async function Vt(t,e){let n={"Content-Type":"application/json"},r=kt();r&&(n.Cookie=r);let i=await fetch(Tt(t)+"/auth/otp",{method:"PUT",headers:n,body:JSON.stringify({email:e})});if(!i.ok){let a=await i.text().catch(()=>"");throw new P(`Failed to send OTP \u2192 ${i.status}: ${a}`,i.status,a)}return{otp:(await i.json().catch(()=>({}))).do_not_use_in_code}}async function Et(t,e,n){let r={"Content-Type":"application/json"},i=kt();i&&(r.Cookie=i);let s=await fetch(Tt(t)+"/auth/login",{method:"POST",headers:r,body:JSON.stringify({email:e,otp:n})});if(!s.ok){let h=await s.text().catch(()=>"");throw new P(`Login failed \u2192 ${s.status}: ${h}`,s.status,h)}let a=s.headers.getSetCookie?.()??[],d=Ye(a);if(!d)throw new P("Login succeeded but no _auth cookie was returned.",500);return d}function Ye(t){for(let e of t){let n=e.match(/(?:^|;\s*)_auth=([^;]+)/);if(n&&n[1])return decodeURIComponent(n[1])}return null}async function Ct(t,e){return K({env:t,userToken:e,method:"GET",path:"/org"})}async function Yt(t,e,n){return K({env:t,userToken:e,method:"GET",path:"/site",subdomain:n})}async function Qt(t,e,n,r){let{token:i}=await K({env:t,userToken:e,method:"POST",path:"/site_user/api_token",subdomain:n,slug:r,body:{refresh:!1}});return i}async function Zt(t,e,n){let r=await K({env:t,userToken:e,method:"POST",path:"/org",body:n?{name:n}:{}});return{id:r.id,name:r.name,subdomain:r.subdomain}}async function te(t,e,n,r){let i=await K({env:t,userToken:e,method:"POST",path:"/site",subdomain:n,body:{name:r}});return{id:i.id,name:i.name,slug:i.slug}}var re=500;function Ze(){return new Date().toTimeString().slice(0,8)}function it(t){let e=t;if(e.body){try{let n=JSON.parse(e.body);if(n&&typeof n.message=="string")return n.message;if(n&&typeof n.error=="string")return n.error}catch{}return e.body}return e.message}async function ie(t){t&&(await ee.remove(ne.join(t,"src")),await ee.remove(ne.join(t,".cache","state.json")))}var st=class extends Qe{env="production";userToken="";email;catalog=[];synced=new Map;message;messageIsError=!1;agentMd;isShuttingDown=!1;constructor(e){super(),this.agentMd=e}changed(){this.emit("change")}flash(e,n=!1){this.message=e,this.messageIsError=n,this.changed()}appendLog(e,n){let r=this.synced.get(e);r&&(r.logs.push({time:Ze(),text:n}),r.logs.length>re&&r.logs.splice(0,r.logs.length-re),this.changed())}siteCount(){return this.synced.size}syncingCount(){let e=0;for(let n of this.synced.values())n.status==="syncing"&&e++;return e}watchingCount(){let e=0;for(let n of this.synced.values())(n.status==="watching"||n.status==="syncing")&&e++;return e}async init(e,n,r){this.env=e,this.userToken=n,r&&(this.email=r);let i=await Ct(e,n);await this.buildCatalog(i),qt(()=>{this.exit()})}async buildCatalog(e){let n=[];for(let r of e)try{n.push({org:r,sites:await Yt(this.env,this.userToken,r.subdomain)})}catch(i){if(i instanceof P&&i.status===401)throw i;n.push({org:r,sites:[]})}this.catalog=n,this.changed()}isSynced(e){return this.synced.has(e)}availableSites(){let e=[];for(let n of this.catalog)for(let r of n.sites)this.synced.has(r.id)||e.push({org:n.org,site:r});return e}async addSite(e,n){if(this.synced.has(n.id))return;let r;try{r=await Qt(this.env,this.userToken,e.subdomain,n.slug)}catch(s){this.flash(`Failed to authorize "${n.name}": ${it(s)}`,!0);return}let i={org:e,site:n,siteToken:r,viewsDir:"",status:"syncing",logs:[]};this.synced.set(n.id,i),this.appendLog(n.id,"\u2795 added \u2014 pulling files"),W(),this.changed();try{let s=await mt({token:r,env:this.env,agentMd:this.agentMd,log:a=>this.appendLog(n.id,a),onProgress:(a,d)=>{i.progress={done:a,total:d},this.changed()}});i.viewsDir=s.viewsDir,i.progress=void 0,i.status="watching",i.lastSyncedAt=Date.now(),i.lastMsg=`pulled ${s.pulled} file(s)`,i.watcher=new z({viewsDir:s.viewsDir,onSync:()=>this.runSync(n.id),onActivity:W}),i.watcher.start(),this.appendLog(n.id,"\u{1F440} watching for changes")}catch(s){let a=s;i.status="error",i.progress=void 0,i.lastMsg=a.body||a.message,this.appendLog(n.id,`\u274C ${a.body||a.message}`)}this.changed()}async removeSite(e){let n=this.synced.get(e);n&&(n.watcher?.setShuttingDown(!0),await n.watcher?.stop(),await ie(n.viewsDir),this.synced.delete(e),W(),this.changed())}async ensureOrg(e){if(e)return e;if(this.catalog[0])return this.catalog[0].org;let n;try{n=await Ct(this.env,this.userToken)}catch(r){return this.flash(`Couldn't check your organizations: ${it(r)}`,!0),null}if(n.length>0){try{await this.buildCatalog(n)}catch{}return n[0]}try{let r=this.email?`${this.email.split("@")[0]}'s Sites`:"My Sites";this.flash("No organization yet \u2014 creating one\u2026");let i=await Zt(this.env,this.userToken,r);return this.catalog=[...this.catalog,{org:i,sites:[]}],this.changed(),i}catch(r){return this.flash(`Could not create an organization: ${it(r)}`,!0),null}}async createNewSite(e,n){let r=await this.ensureOrg(n);if(!r)return;let i;try{i=await te(this.env,this.userToken,r.subdomain,e)}catch(a){this.flash(`Could not create "${e}": ${it(a)}`,!0);return}let s=this.catalog.find(a=>a.org.id===r.id);s&&!s.sites.some(a=>a.id===i.id)&&(s.sites=[...s.sites,i]),this.flash(`Created "${i.name}".`),await this.addSite(r,i)}async refetch(e){await this.runSync(e,{flush:!0})}async runSync(e,n={}){let r=this.synced.get(e);if(!(!r||!r.viewsDir||r.status==="paused")){r.status="syncing",this.changed();try{let i=await mt({token:r.siteToken,env:this.env,viewsDir:r.viewsDir,agentMd:this.agentMd,flush:n.flush,log:s=>this.appendLog(e,s),onProgress:(s,a)=>{r.progress={done:s,total:a},this.changed()}});r.progress=void 0,r.status="watching",r.lastSyncedAt=Date.now(),n.flush?r.lastMsg=`re-fetched ${i.pulled} file(s)`:r.lastMsg=i.pushed>0?`pushed ${i.pushed} file(s)`:"up to date"}catch(i){let s=i;r.status="error",r.progress=void 0,r.lastMsg=s.body||s.message,this.appendLog(e,`\u274C ${s.body||s.message}`)}this.changed()}}async togglePause(e){let n=this.synced.get(e);!n||!n.viewsDir||(n.status==="paused"?(n.watcher=new z({viewsDir:n.viewsDir,onSync:()=>this.runSync(e),onActivity:W}),n.watcher.start(),n.status="watching",this.appendLog(e,"\u25B6 resumed \u2014 watching for changes")):(n.watcher?.setShuttingDown(!0),await n.watcher?.stop(),n.watcher=void 0,n.progress=void 0,n.status="paused",this.appendLog(e,"\u23F8 paused")),this.changed())}async cleanupAll(){vt();for(let e of this.synced.values())e.watcher?.setShuttingDown(!0),await e.watcher?.stop(),await ie(e.viewsDir)}async exit(){this.isShuttingDown||(this.isShuttingDown=!0,await this.cleanupAll(),this.emit("exit"))}async logout(){this.isShuttingDown||(this.isShuttingDown=!0,await Kt(),await this.cleanupAll(),this.emit("logout"))}};import{useEffect as ln,useState as ge}from"react";import{Box as gn,Text as dn,useInput as hn}from"ink";import{useState as V}from"react";import{Box as Y,Text as q}from"ink";import se from"ink-text-input";import{jsx as _,jsxs as J}from"react/jsx-runtime";function oe(t){let e=t;return e?.body||e?.message||String(t)}function ae({env:t,initialError:e,onAuthed:n}){let[r,i]=V("email"),[s,a]=V(""),[d,h]=V(""),[S,f]=V(e),[p,y]=V(),b=async D=>{let T=D.trim();if(T){f(void 0),i("busy");try{let{otp:E}=await Vt(t,T);if(a(T),E){let U=await Et(t,T,E);await n(U,T);return}y(`OTP sent to ${T}`),i("otp")}catch(E){f(oe(E)),i("email")}}},O=async D=>{let T=D.trim();if(T){f(void 0),i("busy");try{let E=await Et(t,s,T);await n(E,s)}catch(E){f(oe(E)),h(""),i("otp")}}};return J(Y,{flexDirection:"column",children:[J(q,{bold:!0,children:["\u25C6 SleekCMS \u2014 sign in",t!=="production"?` (${t})`:""]}),S?J(q,{color:"red",children:["\u2717 ",S]}):null,r==="email"&&J(Y,{marginTop:1,children:[_(q,{children:"Email: "}),_(se,{value:s,onChange:a,onSubmit:b,placeholder:"you@example.com"})]}),r==="otp"&&J(Y,{flexDirection:"column",marginTop:1,children:[p?_(q,{color:"green",children:p}):null,J(Y,{children:[_(q,{children:"Enter OTP: "}),_(se,{value:d,onChange:h,onSubmit:O})]})]}),r==="busy"&&_(Y,{marginTop:1,children:_(q,{children:"Working\u2026"})})]})}import{Fragment as en,useEffect as Ot,useMemo as nn,useState as $}from"react";import{Box as m,Text as u,useInput as rn,useStdout as sn}from"ink";import ue from"ink-text-input";import tn from"node:os";function Mt(t){if(!t)return"\u2014";let e=tn.homedir();return t.startsWith(e)?"~"+t.slice(e.length):t}function ce(t){if(!t)return"\u2014";let e=Math.floor((Date.now()-t)/1e3);if(e<5)return"just now";if(e<60)return`${e}s ago`;let n=Math.floor(e/60);if(n<60)return`${n}m ago`;let r=Math.floor(n/60);return r<24?`${r}h ago`:`${Math.floor(r/24)}d ago`}function ot(t){return!t||t.total<=0?null:Math.round(t.done/t.total*100)}function at(t){switch(t){case"syncing":return{dot:"\u25CF",color:"green",label:"syncing"};case"watching":return{dot:"\u25CB",color:"gray",label:"watching"};case"paused":return{dot:"\u2016",color:"yellow",label:"paused"};case"error":return{dot:"\u2717",color:"red",label:"error"}}}import{Fragment as ct,jsx as o,jsxs as g}from"react/jsx-runtime";var F=(t,e)=>e<=0?0:Math.max(0,Math.min(t,e-1)),on=16,an=5;function cn({k:t}){return o(u,{color:"black",backgroundColor:"green",children:` ${t} `})}function A({k:t,label:e,width:n=16}){return g(m,{width:n,children:[o(cn,{k:t}),o(u,{color:"gray",children:` ${e}`})]})}function un({percent:t,width:e=28}){let n=Math.round(Math.max(0,Math.min(100,t))/100*e);return g(u,{children:[o(u,{color:"green",children:"\u2588".repeat(n)}),o(u,{color:"gray",children:"\u2591".repeat(Math.max(0,e-n))})]})}function le({controller:t}){let{stdout:e}=sn(),n=nn(()=>Nt(),[]),[,r]=$(0),[i,s]=$(()=>new Date),[a,d]=$(null),[h,S]=$(()=>t.synced.size>0||t.catalog.some(l=>l.sites.length>0)?"dashboard":t.catalog.length<=1?"newName":"newOrg"),[f,p]=$(0),[y,b]=$(""),[O,D]=$(!1),[T,E]=$(""),[U,Q]=$(!1),Dt=()=>r(c=>c+1);Ot(()=>(t.on("change",Dt),()=>{t.off("change",Dt)}),[t]),Ot(()=>{let c=setInterval(()=>s(new Date),1e3);return()=>clearInterval(c)},[]);let R=t.catalog.flatMap(c=>c.sites.map(l=>({org:c.org,site:l,entry:t.synced.get(l.id)}))).sort((c,l)=>(c.entry?0:1)-(l.entry?0:1)),I=T.trim().toLowerCase(),C=I?R.filter(c=>c.site.name.toLowerCase().includes(I)||(c.org.name||c.org.subdomain||"").toLowerCase().includes(I)):R,j=C.findIndex(c=>c.site.id===a);j<0&&(j=0);let k=C[j],x=k?.entry,$t=C.findIndex(c=>!c.entry),N=Math.max(an,(e?.rows??30)-on),H=C.length,Z=H>N,me=Math.floor(N/2),X=Z?Math.max(0,Math.min(j-me,H-N)):0,lt=Math.min(H,X+N),fe=C.slice(X,lt);Ot(()=>{h!=="dashboard"||O||R.length>0||(b(""),t.catalog.length<=1?S("newName"):(p(0),S("newOrg")))},[R.length,h,O,t.catalog.length]);let tt=c=>{if(C.length===0)return;let l=F(j+c,C.length);d(C[l].site.id)},ye=()=>{k&&(k.entry?t.removeSite(k.site.id):t.addSite(k.org,k.site))},we=()=>{b(""),t.catalog.length<=1?S("newName"):(p(0),S("newOrg"))},Se=c=>{if(!x){t.flash("Press \u23CE to start syncing this site first.",!0);return}let l=c.run(x.viewsDir);t.flash(l?`Opened ${c.label} at ${Mt(x.viewsDir)}`:`Couldn't open ${c.label}.`,!l)},xe=()=>{if(!x){t.flash("Press \u23CE to start syncing this site first.",!0);return}t.togglePause(x.site.id)};rn((c,l)=>{if(h==="newOrg"){l.escape?S("dashboard"):l.upArrow?p(M=>F(M-1,t.catalog.length)):l.downArrow?p(M=>F(M+1,t.catalog.length)):l.return&&S("newName");return}if(U){l.escape?(Q(!1),E(""),d(null)):l.return?Q(!1):l.upArrow?tt(-1):l.downArrow&&tt(1);return}if(l.escape){E(""),d(null);return}if(c==="/")return void Q(!0);if(c==="n")return void we();if(c==="q")return void t.logout();if(l.upArrow)return tt(-1);if(l.downArrow)return tt(1);if(l.return||c===" ")return ye();if(c==="p")return xe();if(c>="1"&&c<="9"){let M=n[parseInt(c,10)-1];if(M)return Se(M)}},{isActive:h!=="newName"});let ve=async c=>{let l=c.trim();if(!l){S("dashboard");return}let M=t.catalog[F(f,t.catalog.length)]?.org??t.catalog[0]?.org;D(!0),S("dashboard"),await t.createNewSite(l,M),D(!1)},be=Math.max(5,(e?.rows??30)-18);return g(m,{flexDirection:"column",children:[g(m,{justifyContent:"space-between",paddingX:1,children:[g(m,{children:[o(u,{color:"green",bold:!0,children:"\u25C6 SleekCMS"}),o(u,{color:"gray",children:" \u2502 "}),o(u,{children:t.email??"signed in"})]}),g(m,{children:[g(u,{color:"gray",children:[I?`${C.length}/${R.length}`:R.length," sites \xB7 "]}),g(u,{color:"green",children:[t.watchingCount()," watching"]}),o(u,{color:"gray",children:` \xB7 ${i.toTimeString().slice(0,8)}`})]})]}),g(m,{children:[g(m,{flexDirection:"column",borderStyle:"round",borderColor:"green",width:38,paddingX:1,children:[g(m,{justifyContent:"space-between",children:[o(u,{color:"green",bold:!0,children:"SITE WORKSPACES"}),R.length>N&&!U&&!I?o(u,{color:"gray",children:"/ filter"}):null]}),U?g(m,{children:[o(u,{color:"cyan",children:"/ "}),o(ue,{value:T,onChange:E,onSubmit:()=>Q(!1),placeholder:"type to filter\u2026"})]}):I?o(u,{color:"gray",children:`/ ${T} \xB7 ${C.length} match${C.length===1?"":"es"}`}):null,o(m,{flexDirection:"column",marginTop:1,minHeight:Z?N+3:void 0,children:C.length===0?o(u,{color:"gray",children:I?`no matches for \u201C${T}\u201D`:"no sites \u2014 press n to create one"}):g(ct,{children:[Z?o(u,{color:"gray",children:X>0?` \u2191 ${X} more`:" "}):null,fe.map((c,l)=>{let M=X+l,At=c.entry?at(c.entry.status):{dot:"\xB7",color:"gray",label:"idle"},Rt=ot(c.entry?.progress),Te=c.entry?c.entry.status==="syncing"&&Rt!=null?`${Rt}%`:c.entry.status==="paused"?"paused":"--":"",et=M===j,ke=M===$t&&$t>0;return g(en,{children:[ke?o(u,{color:"gray",children:"\u2500".repeat(34)}):null,g(m,{justifyContent:"space-between",children:[g(m,{children:[o(u,{color:et?"green":"gray",children:et?"\u276F ":" "}),g(u,{color:At.color,children:[At.dot," "]}),o(u,{color:et?"white":c.entry?void 0:"gray",bold:et,children:c.site.name})]}),o(u,{color:c.entry?.status==="syncing"?"green":"gray",children:Te})]})]},c.site.id)}),Z?o(u,{color:"gray",children:lt<H?` \u2193 ${H-lt} more`:" "}):null]})}),o(m,{marginTop:1,children:o(u,{color:"gray",children:"+ new site \xB7 n"})})]}),g(m,{flexDirection:"column",flexGrow:1,marginLeft:1,children:[o(m,{flexDirection:"column",borderStyle:"round",borderColor:"gray",paddingX:1,children:k?g(ct,{children:[g(u,{children:[k.org.name||k.org.subdomain?g(u,{color:"gray",children:[k.org.name||k.org.subdomain," / "]}):null,o(u,{color:"cyan",bold:!0,children:k.site.name})]}),g(m,{marginTop:1,children:[o(u,{color:"gray",children:"status "}),x?o(u,{color:at(x.status).color,children:`: ${at(x.status).label}`}):o(u,{color:"gray",children:": idle \u2014 not syncing (press \u23CE to start)"})]}),g(m,{children:[o(u,{color:"gray",children:"path "}),o(u,{children:x?.viewsDir?Mt(x.viewsDir):"\u2014"})]}),g(m,{children:[o(u,{color:"gray",children:"synced "}),o(u,{children:ce(x?.lastSyncedAt)}),o(u,{color:"gray",children:" progress "}),o(u,{color:x?.status==="syncing"?"green":"gray",children:x?.status==="syncing"?`${ot(x.progress)??0}%`:"\u2014"})]}),o(m,{marginTop:1,children:o(un,{percent:x?x.status==="syncing"?ot(x.progress)??0:x.status==="watching"?100:0:0})})]}):o(u,{color:"gray",children:"No site selected."})}),h==="dashboard"?g(m,{flexDirection:"column",borderStyle:"round",borderColor:"gray",paddingX:1,marginTop:1,flexGrow:1,children:[g(u,{children:[o(u,{color:"cyan",bold:!0,children:"LOGS \xB7 "}),o(u,{color:"white",children:k?.site.name??"\u2014"})]}),o(m,{flexDirection:"column",marginTop:1,children:!x||x.logs.length===0?o(u,{color:"gray",children:"no activity yet"}):x.logs.slice(-be).map((c,l)=>g(m,{children:[g(u,{color:"gray",children:[c.time," "]}),o(u,{children:c.text})]},l))})]}):g(m,{flexDirection:"column",borderStyle:"round",borderColor:"blue",paddingX:1,marginTop:1,flexGrow:1,children:[h==="newOrg"&&g(ct,{children:[o(u,{color:"cyan",bold:!0,children:"CREATE SITE \u2014 PICK ORG"}),o(u,{color:"gray",children:"\u2191/\u2193 select \xB7 \u23CE next \xB7 esc cancel"}),o(m,{flexDirection:"column",marginTop:1,children:t.catalog.map((c,l)=>g(u,{color:l===F(f,t.catalog.length)?"blue":void 0,bold:l===F(f,t.catalog.length),children:[l===F(f,t.catalog.length)?"\u276F ":" ",c.org.name||c.org.subdomain]},c.org.id))})]}),h==="newName"&&g(ct,{children:[o(u,{color:"cyan",bold:!0,children:"CREATE SITE"}),g(m,{marginTop:1,children:[o(u,{children:"New site name: "}),o(ue,{value:y,onChange:b,onSubmit:ve})]}),o(u,{color:"gray",children:"\u23CE create \xB7 empty + \u23CE to cancel"})]})]})]})]}),t.message?o(m,{paddingX:1,children:g(u,{color:t.messageIsError?"red":"gray",children:[t.messageIsError?"\u2717 ":"",t.message]})}):null,g(m,{flexDirection:"column",marginTop:1,children:[g(m,{paddingX:1,flexWrap:"wrap",children:[o(A,{k:"\u2191\u2193",label:"select"}),o(A,{k:"/",label:"search"}),o(A,{k:"\u23CE",label:"start/stop"}),o(A,{k:"p",label:"pause"}),o(A,{k:"n",label:"new site"}),o(A,{k:"q",label:"logout"}),o(A,{k:"ctrl+c",label:"quit",width:14})]}),n.length>0?o(m,{paddingX:1,marginTop:1,flexWrap:"wrap",children:n.map((c,l)=>o(A,{k:String(l+1),label:c.label},c.key))}):null]})]})}import{jsx as ut}from"react/jsx-runtime";function de(t){let e=t;return e?.body||e?.message||String(t)}function he({controller:t,env:e,devSecret:n,savedToken:r,savedEmail:i}){let[s,a]=ge(r?"loading":"auth"),[d,h]=ge();hn((f,p)=>{p.ctrl&&f==="c"&&t.exit()}),ln(()=>{r&&t.init(e,r,i).then(()=>a("manage")).catch(f=>{f instanceof P&&f.status===401?h("Saved session expired \u2014 please sign in again."):h(de(f)),a("auth")})},[]);let S=async(f,p)=>{h(void 0),await zt({env:e,userToken:f,devSecret:n,email:p}),a("loading");try{await t.init(e,f,p),a("manage")}catch(y){h(de(y)),a("auth")}};return s==="auth"?ut(ae,{env:e,initialError:d,onAuthed:S}):s==="loading"?ut(gn,{children:ut(dn,{children:"Loading your sites\u2026 "})}):ut(le,{controller:t})}import{jsx as bn}from"react/jsx-runtime";var wn=fn(import.meta.url),Pt=Ft();if(Pt.version){let t=wn("../package.json");console.log(t.version),process.exit(0)}var Sn=pe.dirname(mn(import.meta.url)),xn=pn(pe.join(Sn,"AGENT.md"),"utf-8");async function vn(){let t=new st(xn),e=await Xt(),n=(Pt.env??e?.env??"production").toLowerCase(),r=Pt.dev??process.env.SLEEKCMS_DEV_SECRET??e?.devSecret;Ht(r),process.stdout.isTTY&&process.stdout.write("\x1B[2J\x1B[3J\x1B[H");let i=yn(bn(he,{controller:t,env:n,devSecret:r,savedToken:e?.userToken,savedEmail:e?.email}),{exitOnCtrlC:!1}),s=a=>{i.unmount(),console.log(a),process.exit(0)};t.once("exit",()=>s("\u2713 Stopped. Local site files were cleaned up.")),t.once("logout",()=>s("\u{1F44B} Logged out. Run `sleekcms` to sign in again.")),process.on("SIGINT",()=>{t.exit()}),process.on("SIGTERM",()=>{t.exit()}),await i.waitUntilExit()}vn().catch(t=>{let e=t;console.error("\u274C",e.body||e.message),process.exit(1)});
|
|
9
|
+
`)}async function Be(t,e,n,r,s){gt.delete(b.resolve(t)),await w.remove(b.join(t,pt)),r("\u{1F4E5} Fetching files...");let i=await dt(e,n,"GET","/get_files"),a={},d=0;for(let h of i){let S=b.join(t,h.path);await w.outputFile(S,h.content);let f=(await w.stat(S)).mtimeMs;d++,s?.(d,i.length),a[h.path]={mtimeMs:f}}return r(`\u2714\uFE0F Synced ${i.length} file(s).`),{fileMap:a,pulled:d}}import Le from"path";import Ie from"chokidar";var Fe=5e3,je=/(^|[/\\])(\.DS_Store|Thumbs\.db|desktop\.ini)$/,H=class{viewsDir;onSync;debounceMs;watcher=null;debounceTimer=null;isShuttingDown=!1;dirty=!1;syncInFlight=!1;constructor(e){this.viewsDir=e.viewsDir,this.onSync=e.onSync,this.debounceMs=e.debounceMs??Fe}start(){if(this.watcher)return;let e=Le.join(this.viewsDir,"src");this.watcher=Ie.watch([e],{persistent:!0,ignoreInitial:!0,ignored:je}).on("change",()=>this.scheduleSync()).on("add",()=>this.scheduleSync()).on("unlink",()=>this.scheduleSync())}setShuttingDown(e){this.isShuttingDown=e}async stop(){this.debounceTimer&&(clearTimeout(this.debounceTimer),this.debounceTimer=null),this.watcher&&(await this.watcher.close(),this.watcher=null)}scheduleSync(){this.isShuttingDown||(this.dirty=!0,this.debounceTimer&&clearTimeout(this.debounceTimer),this.debounceTimer=setTimeout(()=>this.flush(),this.debounceMs))}async flush(){if(this.debounceTimer=null,!(!this.dirty||this.isShuttingDown)){if(this.syncInFlight){this.debounceTimer=setTimeout(()=>this.flush(),this.debounceMs);return}this.dirty=!1,this.syncInFlight=!0;try{await this.onSync()}catch(e){let n=e;console.error("\u274C Sync failed:",n.body||n.message)}finally{this.syncInFlight=!1}}}};import tt from"fs-extra";import _e from"os";import It from"path";var Lt={localhost:"http://app.sleekcms.test/api",development:"https://app.sleekcms.dev/api",production:"https://app.sleekcms.com/api"};function ft(t){return Lt[t]||Lt.production}var Ne=It.join(_e.homedir(),"SleekCMS"),et=It.join(Ne,"auth.json"),mt;function Ft(t){mt=t||void 0}function yt(t){let e=[];return t&&e.push(`_auth=${t}`),mt&&e.push(`_dev=${mt}`),e.length?e.join("; "):void 0}var M=class extends Error{status;body;constructor(e,n,r=""){super(e),this.name="UserAuthError",this.status=n,this.body=r}};async function jt(){try{let t=await tt.readJson(et);return t&&t.userToken?t:null}catch{return null}}async function _t(t){await tt.outputJson(et,t,{spaces:2});try{await tt.chmod(et,384)}catch{}}async function Nt(){await tt.remove(et)}async function X(t){let e={},n=yt(t.userToken);n&&(e.Cookie=n),t.subdomain&&(e["x-subdomain"]=t.subdomain),t.slug&&(e.site=t.slug),t.body!==void 0&&(e["Content-Type"]="application/json");let r=await fetch(ft(t.env)+t.path,{method:t.method,headers:e,body:t.body!==void 0?JSON.stringify(t.body):void 0});if(!r.ok){let s=await r.text().catch(()=>"");throw new M(`${t.method} ${t.path} \u2192 ${r.status}: ${s}`,r.status,s)}return r.json()}async function Gt(t,e){let n={"Content-Type":"application/json"},r=yt();r&&(n.Cookie=r);let s=await fetch(ft(t)+"/auth/otp",{method:"PUT",headers:n,body:JSON.stringify({email:e})});if(!s.ok){let a=await s.text().catch(()=>"");throw new M(`Failed to send OTP \u2192 ${s.status}: ${a}`,s.status,a)}return{otp:(await s.json().catch(()=>({}))).do_not_use_in_code}}async function wt(t,e,n){let r={"Content-Type":"application/json"},s=yt();s&&(r.Cookie=s);let i=await fetch(ft(t)+"/auth/login",{method:"POST",headers:r,body:JSON.stringify({email:e,otp:n})});if(!i.ok){let h=await i.text().catch(()=>"");throw new M(`Login failed \u2192 ${i.status}: ${h}`,i.status,h)}let a=i.headers.getSetCookie?.()??[],d=Ge(a);if(!d)throw new M("Login succeeded but no _auth cookie was returned.",500);return d}function Ge(t){for(let e of t){let n=e.match(/(?:^|;\s*)_auth=([^;]+)/);if(n&&n[1])return decodeURIComponent(n[1])}return null}async function St(t,e){return X({env:t,userToken:e,method:"GET",path:"/org"})}async function Wt(t,e,n){return X({env:t,userToken:e,method:"GET",path:"/site",subdomain:n})}async function qt(t,e,n,r){let{token:s}=await X({env:t,userToken:e,method:"POST",path:"/site_user/api_token",subdomain:n,slug:r,body:{refresh:!1}});return s}async function Jt(t,e,n){let r=await X({env:t,userToken:e,method:"POST",path:"/org",body:n?{name:n}:{}});return{id:r.id,name:r.name,subdomain:r.subdomain}}async function Ut(t,e,n,r){let s=await X({env:t,userToken:e,method:"POST",path:"/site",subdomain:n,body:{name:r}});return{id:s.id,name:s.name,slug:s.slug}}var zt=500;function qe(){return new Date().toTimeString().slice(0,8)}function nt(t){let e=t;if(e.body){try{let n=JSON.parse(e.body);if(n&&typeof n.message=="string")return n.message;if(n&&typeof n.error=="string")return n.error}catch{}return e.body}return e.message}async function Kt(t){t&&(await Ht.remove(Xt.join(t,"src")),await Ht.remove(Xt.join(t,".cache","state.json")))}var rt=class extends We{env="production";userToken="";email;catalog=[];synced=new Map;message;messageIsError=!1;agentMd;isShuttingDown=!1;constructor(e){super(),this.agentMd=e}changed(){this.emit("change")}flash(e,n=!1){this.message=e,this.messageIsError=n,this.changed()}appendLog(e,n){let r=this.synced.get(e);r&&(r.logs.push({time:qe(),text:n}),r.logs.length>zt&&r.logs.splice(0,r.logs.length-zt),this.changed())}siteCount(){return this.synced.size}syncingCount(){let e=0;for(let n of this.synced.values())n.status==="syncing"&&e++;return e}watchingCount(){let e=0;for(let n of this.synced.values())(n.status==="watching"||n.status==="syncing")&&e++;return e}async init(e,n,r){this.env=e,this.userToken=n,r&&(this.email=r);let s=await St(e,n);await this.buildCatalog(s)}async buildCatalog(e){let n=[];for(let r of e)try{n.push({org:r,sites:await Wt(this.env,this.userToken,r.subdomain)})}catch(s){if(s instanceof M&&s.status===401)throw s;n.push({org:r,sites:[]})}this.catalog=n,this.changed()}isSynced(e){return this.synced.has(e)}availableSites(){let e=[];for(let n of this.catalog)for(let r of n.sites)this.synced.has(r.id)||e.push({org:n.org,site:r});return e}async addSite(e,n){if(this.synced.has(n.id))return;let r;try{r=await qt(this.env,this.userToken,e.subdomain,n.slug)}catch(i){this.flash(`Failed to authorize "${n.name}": ${nt(i)}`,!0);return}let s={org:e,site:n,siteToken:r,viewsDir:"",status:"syncing",logs:[]};this.synced.set(n.id,s),this.appendLog(n.id,"\u2795 added \u2014 pulling files"),this.changed();try{let i=await ht({token:r,env:this.env,agentMd:this.agentMd,log:a=>this.appendLog(n.id,a),onProgress:(a,d)=>{s.progress={done:a,total:d},this.changed()}});s.viewsDir=i.viewsDir,s.progress=void 0,s.status="watching",s.lastSyncedAt=Date.now(),s.lastMsg=`pulled ${i.pulled} file(s)`,s.watcher=new H({viewsDir:i.viewsDir,onSync:()=>this.runSync(n.id)}),s.watcher.start(),this.appendLog(n.id,"\u{1F440} watching for changes")}catch(i){let a=i;s.status="error",s.progress=void 0,s.lastMsg=a.body||a.message,this.appendLog(n.id,`\u274C ${a.body||a.message}`)}this.changed()}async removeSite(e){let n=this.synced.get(e);n&&(n.watcher?.setShuttingDown(!0),await n.watcher?.stop(),await Kt(n.viewsDir),this.synced.delete(e),this.changed())}async ensureOrg(e){if(e)return e;if(this.catalog[0])return this.catalog[0].org;let n;try{n=await St(this.env,this.userToken)}catch(r){return this.flash(`Couldn't check your organizations: ${nt(r)}`,!0),null}if(n.length>0){try{await this.buildCatalog(n)}catch{}return n[0]}try{let r=this.email?`${this.email.split("@")[0]}'s Sites`:"My Sites";this.flash("No organization yet \u2014 creating one\u2026");let s=await Jt(this.env,this.userToken,r);return this.catalog=[...this.catalog,{org:s,sites:[]}],this.changed(),s}catch(r){return this.flash(`Could not create an organization: ${nt(r)}`,!0),null}}async createNewSite(e,n){let r=await this.ensureOrg(n);if(!r)return;let s;try{s=await Ut(this.env,this.userToken,r.subdomain,e)}catch(a){this.flash(`Could not create "${e}": ${nt(a)}`,!0);return}let i=this.catalog.find(a=>a.org.id===r.id);i&&!i.sites.some(a=>a.id===s.id)&&(i.sites=[...i.sites,s]),this.flash(`Created "${s.name}".`),await this.addSite(r,s)}async refetch(e){await this.runSync(e,{flush:!0})}async runSync(e,n={}){let r=this.synced.get(e);if(!(!r||!r.viewsDir||r.status==="paused")){r.status="syncing",this.changed();try{let s=await ht({token:r.siteToken,env:this.env,viewsDir:r.viewsDir,agentMd:this.agentMd,flush:n.flush,log:i=>this.appendLog(e,i),onProgress:(i,a)=>{r.progress={done:i,total:a},this.changed()}});r.progress=void 0,r.status="watching",r.lastSyncedAt=Date.now(),n.flush?r.lastMsg=`re-fetched ${s.pulled} file(s)`:r.lastMsg=s.pushed>0?`pushed ${s.pushed} file(s)`:"up to date"}catch(s){let i=s;r.status="error",r.progress=void 0,r.lastMsg=i.body||i.message,this.appendLog(e,`\u274C ${i.body||i.message}`)}this.changed()}}async togglePause(e){let n=this.synced.get(e);!n||!n.viewsDir||(n.status==="paused"?(n.watcher=new H({viewsDir:n.viewsDir,onSync:()=>this.runSync(e)}),n.watcher.start(),n.status="watching",this.appendLog(e,"\u25B6 resumed \u2014 watching for changes")):(n.watcher?.setShuttingDown(!0),await n.watcher?.stop(),n.watcher=void 0,n.progress=void 0,n.status="paused",this.appendLog(e,"\u23F8 paused")),this.changed())}async cleanupAll(){for(let e of this.synced.values())e.watcher?.setShuttingDown(!0),await e.watcher?.stop(),await Kt(e.viewsDir)}async exit(){this.isShuttingDown||(this.isShuttingDown=!0,await this.cleanupAll(),this.emit("exit"))}async logout(){this.isShuttingDown||(this.isShuttingDown=!0,await Nt(),await this.cleanupAll(),this.emit("logout"))}};import{useEffect as Ze,useState as ne}from"react";import{Box as tn,Text as en,useInput as nn}from"ink";import{useState as z}from"react";import{Box as K,Text as G}from"ink";import Vt from"ink-text-input";import{jsx as F,jsxs as W}from"react/jsx-runtime";function Yt(t){let e=t;return e?.body||e?.message||String(t)}function Qt({env:t,initialError:e,onAuthed:n}){let[r,s]=z("email"),[i,a]=z(""),[d,h]=z(""),[S,f]=z(e),[p,y]=z(),v=async D=>{let T=D.trim();if(T){f(void 0),s("busy");try{let{otp:E}=await Gt(t,T);if(a(T),E){let q=await wt(t,T,E);await n(q,T);return}y(`OTP sent to ${T}`),s("otp")}catch(E){f(Yt(E)),s("email")}}},O=async D=>{let T=D.trim();if(T){f(void 0),s("busy");try{let E=await wt(t,i,T);await n(E,i)}catch(E){f(Yt(E)),h(""),s("otp")}}};return W(K,{flexDirection:"column",children:[W(G,{bold:!0,children:["\u25C6 SleekCMS \u2014 sign in",t!=="production"?` (${t})`:""]}),S?W(G,{color:"red",children:["\u2717 ",S]}):null,r==="email"&&W(K,{marginTop:1,children:[F(G,{children:"Email: "}),F(Vt,{value:i,onChange:a,onSubmit:v,placeholder:"you@example.com"})]}),r==="otp"&&W(K,{flexDirection:"column",marginTop:1,children:[p?F(G,{color:"green",children:p}):null,W(K,{children:[F(G,{children:"Enter OTP: "}),F(Vt,{value:d,onChange:h,onSubmit:O})]})]}),r==="busy"&&F(K,{marginTop:1,children:F(G,{children:"Working\u2026"})})]})}import{Fragment as Ue,useEffect as bt,useMemo as He,useState as $}from"react";import{Box as m,Text as u,useInput as Xe,useStdout as ze}from"ink";import te from"ink-text-input";import Je from"node:os";function xt(t){if(!t)return"\u2014";let e=Je.homedir();return t.startsWith(e)?"~"+t.slice(e.length):t}function Zt(t){if(!t)return"\u2014";let e=Math.floor((Date.now()-t)/1e3);if(e<5)return"just now";if(e<60)return`${e}s ago`;let n=Math.floor(e/60);if(n<60)return`${n}m ago`;let r=Math.floor(n/60);return r<24?`${r}h ago`:`${Math.floor(r/24)}d ago`}function st(t){return!t||t.total<=0?null:Math.round(t.done/t.total*100)}function it(t){switch(t){case"syncing":return{dot:"\u25CF",color:"green",label:"syncing"};case"watching":return{dot:"\u25CB",color:"gray",label:"watching"};case"paused":return{dot:"\u2016",color:"yellow",label:"paused"};case"error":return{dot:"\u2717",color:"red",label:"error"}}}import{Fragment as ot,jsx as o,jsxs as g}from"react/jsx-runtime";var j=(t,e)=>e<=0?0:Math.max(0,Math.min(t,e-1)),Ke=16,Ve=5;function Ye({k:t}){return o(u,{color:"black",backgroundColor:"green",children:` ${t} `})}function R({k:t,label:e,width:n=16}){return g(m,{width:n,children:[o(Ye,{k:t}),o(u,{color:"gray",children:` ${e}`})]})}function Qe({percent:t,width:e=28}){let n=Math.round(Math.max(0,Math.min(100,t))/100*e);return g(u,{children:[o(u,{color:"green",children:"\u2588".repeat(n)}),o(u,{color:"gray",children:"\u2591".repeat(Math.max(0,e-n))})]})}function ee({controller:t}){let{stdout:e}=ze(),n=He(()=>At(),[]),[,r]=$(0),[s,i]=$(()=>new Date),[a,d]=$(null),[h,S]=$(()=>t.synced.size>0||t.catalog.some(l=>l.sites.length>0)?"dashboard":t.catalog.length<=1?"newName":"newOrg"),[f,p]=$(0),[y,v]=$(""),[O,D]=$(!1),[T,E]=$(""),[q,V]=$(!1),Tt=()=>r(c=>c+1);bt(()=>(t.on("change",Tt),()=>{t.off("change",Tt)}),[t]),bt(()=>{let c=setInterval(()=>i(new Date),1e3);return()=>clearInterval(c)},[]);let A=t.catalog.flatMap(c=>c.sites.map(l=>({org:c.org,site:l,entry:t.synced.get(l.id)}))).sort((c,l)=>(c.entry?0:1)-(l.entry?0:1)),B=T.trim().toLowerCase(),C=B?A.filter(c=>c.site.name.toLowerCase().includes(B)||(c.org.name||c.org.subdomain||"").toLowerCase().includes(B)):A,_=C.findIndex(c=>c.site.id===a);_<0&&(_=0);let k=C[_],x=k?.entry,kt=C.findIndex(c=>!c.entry),N=Math.max(Ve,(e?.rows??30)-Ke),J=C.length,Y=J>N,oe=Math.floor(N/2),U=Y?Math.max(0,Math.min(_-oe,J-N)):0,ct=Math.min(J,U+N),ae=C.slice(U,ct);bt(()=>{h!=="dashboard"||O||A.length>0||(v(""),t.catalog.length<=1?S("newName"):(p(0),S("newOrg")))},[A.length,h,O,t.catalog.length]);let Q=c=>{if(C.length===0)return;let l=j(_+c,C.length);d(C[l].site.id)},ce=()=>{k&&(k.entry?t.removeSite(k.site.id):t.addSite(k.org,k.site))},ue=()=>{v(""),t.catalog.length<=1?S("newName"):(p(0),S("newOrg"))},le=c=>{if(!x){t.flash("Press \u23CE to start syncing this site first.",!0);return}let l=c.run(x.viewsDir);t.flash(l?`Opened ${c.label} at ${xt(x.viewsDir)}`:`Couldn't open ${c.label}.`,!l)},ge=()=>{if(!x){t.flash("Press \u23CE to start syncing this site first.",!0);return}t.togglePause(x.site.id)};Xe((c,l)=>{if(h==="newOrg"){l.escape?S("dashboard"):l.upArrow?p(P=>j(P-1,t.catalog.length)):l.downArrow?p(P=>j(P+1,t.catalog.length)):l.return&&S("newName");return}if(q){l.escape?(V(!1),E(""),d(null)):l.return?V(!1):l.upArrow?Q(-1):l.downArrow&&Q(1);return}if(l.escape){E(""),d(null);return}if(c==="/")return void V(!0);if(c==="n")return void ue();if(c==="q")return void t.logout();if(l.upArrow)return Q(-1);if(l.downArrow)return Q(1);if(l.return||c===" ")return ce();if(c==="p")return ge();if(c>="1"&&c<="9"){let P=n[parseInt(c,10)-1];if(P)return le(P)}},{isActive:h!=="newName"});let de=async c=>{let l=c.trim();if(!l){S("dashboard");return}let P=t.catalog[j(f,t.catalog.length)]?.org??t.catalog[0]?.org;D(!0),S("dashboard"),await t.createNewSite(l,P),D(!1)},he=Math.max(5,(e?.rows??30)-18);return g(m,{flexDirection:"column",children:[g(m,{justifyContent:"space-between",paddingX:1,children:[g(m,{children:[o(u,{color:"green",bold:!0,children:"\u25C6 SleekCMS"}),o(u,{color:"gray",children:" \u2502 "}),o(u,{children:t.email??"signed in"})]}),g(m,{children:[g(u,{color:"gray",children:[B?`${C.length}/${A.length}`:A.length," sites \xB7 "]}),g(u,{color:"green",children:[t.watchingCount()," watching"]}),o(u,{color:"gray",children:` \xB7 ${s.toTimeString().slice(0,8)}`})]})]}),g(m,{children:[g(m,{flexDirection:"column",borderStyle:"round",borderColor:"green",width:38,paddingX:1,children:[g(m,{justifyContent:"space-between",children:[o(u,{color:"green",bold:!0,children:"SITE WORKSPACES"}),A.length>N&&!q&&!B?o(u,{color:"gray",children:"/ filter"}):null]}),q?g(m,{children:[o(u,{color:"cyan",children:"/ "}),o(te,{value:T,onChange:E,onSubmit:()=>V(!1),placeholder:"type to filter\u2026"})]}):B?o(u,{color:"gray",children:`/ ${T} \xB7 ${C.length} match${C.length===1?"":"es"}`}):null,o(m,{flexDirection:"column",marginTop:1,minHeight:Y?N+3:void 0,children:C.length===0?o(u,{color:"gray",children:B?`no matches for \u201C${T}\u201D`:"no sites \u2014 press n to create one"}):g(ot,{children:[Y?o(u,{color:"gray",children:U>0?` \u2191 ${U} more`:" "}):null,ae.map((c,l)=>{let P=U+l,Et=c.entry?it(c.entry.status):{dot:"\xB7",color:"gray",label:"idle"},Ct=st(c.entry?.progress),pe=c.entry?c.entry.status==="syncing"&&Ct!=null?`${Ct}%`:c.entry.status==="paused"?"paused":"--":"",Z=P===_,me=P===kt&&kt>0;return g(Ue,{children:[me?o(u,{color:"gray",children:"\u2500".repeat(34)}):null,g(m,{justifyContent:"space-between",children:[g(m,{children:[o(u,{color:Z?"green":"gray",children:Z?"\u276F ":" "}),g(u,{color:Et.color,children:[Et.dot," "]}),o(u,{color:Z?"white":c.entry?void 0:"gray",bold:Z,children:c.site.name})]}),o(u,{color:c.entry?.status==="syncing"?"green":"gray",children:pe})]})]},c.site.id)}),Y?o(u,{color:"gray",children:ct<J?` \u2193 ${J-ct} more`:" "}):null]})}),o(m,{marginTop:1,children:o(u,{color:"gray",children:"+ new site \xB7 n"})})]}),g(m,{flexDirection:"column",flexGrow:1,marginLeft:1,children:[o(m,{flexDirection:"column",borderStyle:"round",borderColor:"gray",paddingX:1,children:k?g(ot,{children:[g(u,{children:[k.org.name||k.org.subdomain?g(u,{color:"gray",children:[k.org.name||k.org.subdomain," / "]}):null,o(u,{color:"cyan",bold:!0,children:k.site.name})]}),g(m,{marginTop:1,children:[o(u,{color:"gray",children:"status "}),x?o(u,{color:it(x.status).color,children:`: ${it(x.status).label}`}):o(u,{color:"gray",children:": idle \u2014 not syncing (press \u23CE to start)"})]}),g(m,{children:[o(u,{color:"gray",children:"path "}),o(u,{children:x?.viewsDir?xt(x.viewsDir):"\u2014"})]}),g(m,{children:[o(u,{color:"gray",children:"synced "}),o(u,{children:Zt(x?.lastSyncedAt)}),o(u,{color:"gray",children:" progress "}),o(u,{color:x?.status==="syncing"?"green":"gray",children:x?.status==="syncing"?`${st(x.progress)??0}%`:"\u2014"})]}),o(m,{marginTop:1,children:o(Qe,{percent:x?x.status==="syncing"?st(x.progress)??0:x.status==="watching"?100:0:0})})]}):o(u,{color:"gray",children:"No site selected."})}),h==="dashboard"?g(m,{flexDirection:"column",borderStyle:"round",borderColor:"gray",paddingX:1,marginTop:1,flexGrow:1,children:[g(u,{children:[o(u,{color:"cyan",bold:!0,children:"LOGS \xB7 "}),o(u,{color:"white",children:k?.site.name??"\u2014"})]}),o(m,{flexDirection:"column",marginTop:1,children:!x||x.logs.length===0?o(u,{color:"gray",children:"no activity yet"}):x.logs.slice(-he).map((c,l)=>g(m,{children:[g(u,{color:"gray",children:[c.time," "]}),o(u,{children:c.text})]},l))})]}):g(m,{flexDirection:"column",borderStyle:"round",borderColor:"blue",paddingX:1,marginTop:1,flexGrow:1,children:[h==="newOrg"&&g(ot,{children:[o(u,{color:"cyan",bold:!0,children:"CREATE SITE \u2014 PICK ORG"}),o(u,{color:"gray",children:"\u2191/\u2193 select \xB7 \u23CE next \xB7 esc cancel"}),o(m,{flexDirection:"column",marginTop:1,children:t.catalog.map((c,l)=>g(u,{color:l===j(f,t.catalog.length)?"blue":void 0,bold:l===j(f,t.catalog.length),children:[l===j(f,t.catalog.length)?"\u276F ":" ",c.org.name||c.org.subdomain]},c.org.id))})]}),h==="newName"&&g(ot,{children:[o(u,{color:"cyan",bold:!0,children:"CREATE SITE"}),g(m,{marginTop:1,children:[o(u,{children:"New site name: "}),o(te,{value:y,onChange:v,onSubmit:de})]}),o(u,{color:"gray",children:"\u23CE create \xB7 empty + \u23CE to cancel"})]})]})]})]}),t.message?o(m,{paddingX:1,children:g(u,{color:t.messageIsError?"red":"gray",children:[t.messageIsError?"\u2717 ":"",t.message]})}):null,g(m,{flexDirection:"column",marginTop:1,children:[g(m,{paddingX:1,flexWrap:"wrap",children:[o(R,{k:"\u2191\u2193",label:"select"}),o(R,{k:"/",label:"search"}),o(R,{k:"\u23CE",label:"start/stop"}),o(R,{k:"p",label:"pause"}),o(R,{k:"n",label:"new site"}),o(R,{k:"q",label:"logout"}),o(R,{k:"ctrl+c",label:"quit",width:14})]}),n.length>0?o(m,{paddingX:1,marginTop:1,flexWrap:"wrap",children:n.map((c,l)=>o(R,{k:String(l+1),label:c.label},c.key))}):null]})]})}import{jsx as at}from"react/jsx-runtime";function re(t){let e=t;return e?.body||e?.message||String(t)}function se({controller:t,env:e,devSecret:n,savedToken:r,savedEmail:s}){let[i,a]=ne(r?"loading":"auth"),[d,h]=ne();nn((f,p)=>{p.ctrl&&f==="c"&&t.exit()}),Ze(()=>{r&&t.init(e,r,s).then(()=>a("manage")).catch(f=>{f instanceof M&&f.status===401?h("Saved session expired \u2014 please sign in again."):h(re(f)),a("auth")})},[]);let S=async(f,p)=>{h(void 0),await _t({env:e,userToken:f,devSecret:n,email:p}),a("loading");try{await t.init(e,f,p),a("manage")}catch(y){h(re(y)),a("auth")}};return i==="auth"?at(Qt,{env:e,initialError:d,onAuthed:S}):i==="loading"?at(tn,{children:at(en,{children:"Loading your sites\u2026 "})}):at(ee,{controller:t})}import{jsx as dn}from"react/jsx-runtime";var cn=on(import.meta.url),vt=$t();if(vt.version){let t=cn("../package.json");console.log(t.version),process.exit(0)}var un=ie.dirname(sn(import.meta.url)),ln=rn(ie.join(un,"AGENT.md"),"utf-8");async function gn(){let t=new rt(ln),e=await jt(),n=(vt.env??e?.env??"production").toLowerCase(),r=vt.dev??process.env.SLEEKCMS_DEV_SECRET??e?.devSecret;Ft(r),process.stdout.isTTY&&process.stdout.write("\x1B[2J\x1B[3J\x1B[H");let s=an(dn(se,{controller:t,env:n,devSecret:r,savedToken:e?.userToken,savedEmail:e?.email}),{exitOnCtrlC:!1}),i=a=>{s.unmount(),console.log(a),process.exit(0)};t.once("exit",()=>i("\u2713 Stopped. Local site files were cleaned up.")),t.once("logout",()=>i("\u{1F44B} Logged out. Run `sleekcms` to sign in again.")),process.on("SIGINT",()=>{t.exit()}),process.on("SIGTERM",()=>{t.exit()}),await s.waitUntilExit()}gn().catch(t=>{let e=t;console.error("\u274C",e.body||e.message),process.exit(1)});
|