@sleekcms/sync 2.6.1 → 2.7.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.
- package/dist/AGENT.md +14 -12
- package/dist/index.js +4 -4
- package/package.json +1 -1
package/dist/AGENT.md
CHANGED
|
@@ -349,10 +349,6 @@ There is exactly **one** layout: `layouts/common.ejs`. It wraps every page autom
|
|
|
349
349
|
|
|
350
350
|
`embed(video, attr?)` returns a provider-aware `<iframe>`. `attr` is `"WxH"` or `{ w, h, size, autoplay, muted, loop, preload, t, class, style }`. Browsers block autoplay unless `muted: true` is also set.
|
|
351
351
|
|
|
352
|
-
### Helpers — bookings
|
|
353
|
-
|
|
354
|
-
`bookings(name, options?)` renders the appointment-booking / scheduling widget mount div — use it when visitors should book or schedule a time (appointment, call, demo, consultation, class, reservation); see §11 for when it's appropriate. `options` is `{ accent, class, style }`.
|
|
355
|
-
|
|
356
352
|
### Helpers — head injection
|
|
357
353
|
|
|
358
354
|
Callable from **any** view (page, block, entry, layout); duplicates are removed automatically.
|
|
@@ -402,21 +398,27 @@ Any `<form>` with `data-sleekcms="<name>"` works automatically — submissions a
|
|
|
402
398
|
</form>
|
|
403
399
|
```
|
|
404
400
|
|
|
405
|
-
The `data-sleekcms` value groups submissions by form; each input's `name` is stored as-is.
|
|
401
|
+
The `data-sleekcms` value groups submissions by form; each input's `name` is stored as-is. Add an `<input type="datetime-local">` and the form becomes a **booking form** — see §11.
|
|
406
402
|
|
|
407
403
|
## 11. Bookings
|
|
408
404
|
|
|
409
|
-
Use this whenever visitors should **book or schedule** something — an appointment, a call, a demo, a consultation, a class, or a reservation ("pick a time / reserve a slot"). It fits appointment-based businesses: salon, spa, barber, dental/medical clinic, therapist, tutor, photographer, consultant, repair shop, restaurant reservations. **Only add a booking
|
|
405
|
+
Use this whenever visitors should **book or schedule** something — an appointment, a call, a demo, a consultation, a class, or a reservation ("pick a time / reserve a slot"). It fits appointment-based businesses: salon, spa, barber, dental/medical clinic, therapist, tutor, photographer, consultant, repair shop, restaurant reservations. **Only add a booking form when the business genuinely takes appointments and the user asks for it (or it is clearly core to the business) — never add one speculatively to an arbitrary site.**
|
|
410
406
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
```ejs
|
|
414
|
-
<%- bookings('consultation') %>
|
|
407
|
+
**A booking is just a form.** Give a normal `data-sleekcms` form an `<input type="datetime-local">` — at publish time that input is replaced by an availability slot picker, and submissions become appointments with email confirmation, calendar invites, and no-login cancel/reschedule. Every other field is yours: bookings store arbitrary fields, exactly like form submissions.
|
|
415
408
|
|
|
416
|
-
|
|
409
|
+
```html
|
|
410
|
+
<form data-sleekcms="consultation">
|
|
411
|
+
<input name="name" type="text" required placeholder="Your name">
|
|
412
|
+
<input name="email" type="email" required placeholder="Email">
|
|
413
|
+
<input name="slot" type="datetime-local" required>
|
|
414
|
+
<textarea name="notes" placeholder="Anything we should know?"></textarea>
|
|
415
|
+
<button type="submit">Book appointment</button>
|
|
416
|
+
</form>
|
|
417
417
|
```
|
|
418
418
|
|
|
419
|
-
|
|
419
|
+
Rules: **include an email field** (`type="email"` or `name="email"`) — confirmation and manage links go there; the datetime input's own `name` doesn't matter (it is replaced by the picker, and the chosen time is stored as the booking's slot); style the form like any other — the picker inherits the page's `--accent` CSS variable (or set `data-accent` on the form).
|
|
420
|
+
|
|
421
|
+
The form named `consultation` is **auto-created on sync** like any form, and because it has a datetime input it gets default availability: 30-minute slots, one booking at a time, Mon–Fri 9 AM–5 PM in America/New_York, bookings confirmed instantly (email-confirmation off — the owner can require it in settings). The defaults are guesses — tell the user to review the timezone and open hours in the form's **Booking settings** (Customers › Inbox); appointments appear under **Customers › Bookings**. Reusing the same name across pages shares one booking form.
|
|
420
422
|
|
|
421
423
|
## 12. RSS Feeds
|
|
422
424
|
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{readFileSync as Le}from"node:fs";import{fileURLToPath as
|
|
2
|
+
import{readFileSync as Le}from"node:fs";import{fileURLToPath as Xr}from"node:url";import{createRequire as Kr}from"node:module";import It from"node:path";import{render as zr}from"ink";import{execSync as Zt,spawn as P}from"child_process";import{program as Yt,Option as kt}from"commander";function te(){return Yt.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 kt("-v, --version","output the version number").hideHelp()).addOption(new kt("-e, --env <env>","Environment (localhost, development, production)").default("production").hideHelp()).addOption(new kt("-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),Yt.opts()}function $(t){let e=process.platform==="win32"?`where ${t}`:`which ${t}`;try{return Zt(e,{stdio:"ignore"}),!0}catch{return!1}}function ee(t){if(process.platform!=="darwin")return!1;try{return Zt(`osascript -e 'id of application "${t}"'`,{stdio:"ignore"}),!0}catch{return!1}}function
|
|
7
|
-
`)){let o=i.indexOf(": ");o>0&&(n[i.slice(0,o)]=i.slice(o+2))}return n}async function
|
|
6
|
+
`).parse(process.argv),Yt.opts()}function $(t){let e=process.platform==="win32"?`where ${t}`:`which ${t}`;try{return Zt(e,{stdio:"ignore"}),!0}catch{return!1}}function ee(t){if(process.platform!=="darwin")return!1;try{return Zt(`osascript -e 'id of application "${t}"'`,{stdio:"ignore"}),!0}catch{return!1}}function Qe(t,e){let r={detached:!0,stdio:"ignore",windowsHide:!0};process.platform==="win32"?P("cmd.exe",["/c",t,...e],r).unref():P(t,e,r).unref()}var Vt=[{name:"VS Code",cmd:"code",args:t=>["-n",t]},{name:"Cursor",cmd:"cursor",args:t=>["-n",t]}];function Qt(t,e){let n=(e?Vt.filter(i=>i.cmd===e):Vt).find(i=>$(i.cmd));return n?(Qe(n.cmd,n.args(t)),n.name):null}function Ze(t){if(process.platform==="darwin")return P("open",["-a","Terminal",t],{detached:!0,stdio:"ignore"}).unref(),!0;if(process.platform==="win32"){if($("wt"))P("wt.exe",["-d",t],{detached:!0,stdio:"ignore"}).unref();else{let e=$("powershell")?"powershell.exe":"cmd.exe";P("cmd.exe",["/c","start","",e],{cwd:t,detached:!0,stdio:"ignore"}).unref()}return!0}return!1}function tr(t){return ee("iTerm")?(P("open",["-a","iTerm",t],{detached:!0,stdio:"ignore"}).unref(),!0):!1}function er(t){return`'${t.replace(/'/g,"'\\''")}'`}var rr=[{cmd:"x-terminal-emulator",args:t=>["-e","bash","-c",t]},{cmd:"gnome-terminal",args:t=>["--","bash","-c",t]},{cmd:"konsole",args:t=>["-e","bash","-c",t]},{cmd:"xfce4-terminal",args:t=>["-x","bash","-c",t]},{cmd:"kitty",args:t=>["bash","-c",t]},{cmd:"alacritty",args:t=>["-e","bash","-c",t]},{cmd:"xterm",args:t=>["-e","bash","-c",t]}];function re(t,e){if(!$(e))return!1;if(process.platform==="darwin"){let n=`tell application "Terminal" to do script ("cd " & quoted form of "${t}" & " && ${e}")`;return P("osascript",["-e",n,"-e",'tell application "Terminal" to activate'],{detached:!0,stdio:"ignore"}).unref(),!0}if(process.platform==="win32")return $("wt")?P("wt.exe",["-d",t,"cmd","/k",e],{detached:!0,stdio:"ignore"}).unref():P("cmd.exe",["/c","start","","cmd","/k",e],{cwd:t,detached:!0,stdio:"ignore"}).unref(),!0;let r=`cd ${er(t)} && ${e}; exec bash`;for(let n of rr)if($(n.cmd))try{return P(n.cmd,n.args(r),{cwd:t,detached:!0,stdio:"ignore"}).unref(),!0}catch{}return!1}function nr(t){return re(t,"claude")}function ir(t){return re(t,"pi")}function ne(t){if(!t)return!1;try{return process.platform==="darwin"?P("open",[t],{detached:!0,stdio:"ignore"}).unref():process.platform==="win32"?P("cmd.exe",["/c","start","",t],{detached:!0,stdio:"ignore",windowsHide:!0}).unref():P("xdg-open",[t],{detached:!0,stdio:"ignore"}).unref(),!0}catch{return!1}}function ie(){let t=process.platform==="darwin",e=process.platform==="win32",r=[];return(t||e)&&r.push({key:"terminal",label:"terminal",run:Ze}),ee("iTerm")&&r.push({key:"iterm",label:"iterm",run:tr}),$("code")&&r.push({key:"code",label:"vscode",run:n=>Qt(n,"code")!==null}),$("cursor")&&r.push({key:"cursor",label:"cursor",run:n=>Qt(n,"cursor")!==null}),$("claude")&&r.push({key:"claude",label:"claude",run:nr}),$("pi")&&r.push({key:"pi",label:"pi",run:ir}),r}import{EventEmitter as Pr}from"node:events";import{spawn as Mr}from"node:child_process";import Q from"fs-extra";import At from"path";import Dr from"chokidar";import f from"fs-extra";import or from"os";import S from"path";var se={localhost:"http://app.sleekcms.test/api/mcp",development:"https://app.sleekcms.dev/api/mcp",production:"https://app.sleekcms.com/api/mcp"},ar=["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"],Bt=class extends Error{status;body;constructor(e,r,n){super(e),this.status=r,this.body=n}};async function Ct(t,e,r,n,i){let o=await fetch(t+n,{method:r,headers:{Authorization:`Bearer ${e}`,...i!==void 0?{"Content-Type":"application/json"}:{}},body:i!==void 0?JSON.stringify(i):void 0});if(!o.ok){let l=await o.text();throw new Bt(`${r} ${n} \u2192 ${o.status}: ${l}`,o.status,l)}return o.json()}async function cr(t,e){e&&(await f.outputFile(S.join(t,"AGENT.md"),e),await f.outputFile(S.join(t,"CLAUDE.md"),e),await f.outputFile(S.join(t,".vscode","copilot-instructions.md"),e)),await f.outputFile(S.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 lr(t,e){if(!e)return;let r=S.join(t,"build.mjs"),n=e.match(/build\.mjs v\d+/)?.[0];if(await f.pathExists(r)){let i=(await f.readFile(r,"utf-8")).match(/build\.mjs v\d+/)?.[0];if(i&&i===n)return}await f.outputFile(r,e)}async function ur(t,e,r){let n=S.join(t,".cache","token");if(await f.pathExists(n)){if((await f.readFile(n,"utf-8")).trim()===e)return;r?.("\u267B\uFE0F Workspace was tied to a different site \u2014 recreating it."),await f.emptyDir(t)}await f.outputFile(n,e)}function dr(t,e){let r=`${e.name.substr(0,20)} ${e.id}`.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,""),n=t||S.join(or.homedir(),"SleekCMS");return S.resolve(n,r)}async function Pt(t){let e=(t.token||"").trim();if(!e)throw new Error("syncSite: token is required");let r=t.log??(v=>console.log(v)),n=t.onProgress,i=(t.env||e.split("-")[2]||"production").toLowerCase(),o=se[i]||se.production,l=await Ct(o,e,"GET","/get_site"),h=t.viewsDir?S.resolve(t.viewsDir):dr(t.path,l);await f.ensureDir(h),await ur(h,e,r),await Promise.all(ar.map(v=>f.ensureDir(S.join(h,v)))),await lr(h,t.buildScript);let p=S.join(h,".cache","state.json");t.flush&&await f.remove(p);let x=!await f.pathExists(p),w=x?{}:(await f.readJson(p)).fileMap||{},m=0,b=0;return x?({fileMap:w,pulled:b}=await xr(h,o,e,r,n),await cr(h,t.agentMd)):m=await pr(h,w,o,e,r,n),await f.outputJson(p,{fileMap:w},{spaces:2}),{viewsDir:h,site:l,isFirstRun:x,pushed:m,pulled:b}}var gr=new Set([".DS_Store","Thumbs.db","desktop.ini"]);async function hr(t){let e=S.join(t,"src");if(!await f.pathExists(e))return[];let r=[];async function n(i){let o=await f.readdir(i,{withFileTypes:!0});for(let l of o){if(gr.has(l.name))continue;let h=S.join(i,l.name);l.isDirectory()?await n(h):l.isFile()&&r.push(S.relative(t,h).replace(/\\/g,"/"))}}return await n(e),r}async function pr(t,e,r,n,i,o){let l=[];for(let m of await hr(t)){let b=S.join(t,m),v=e[m],O=await f.stat(b);if(v&&v.mtimeMs===O.mtimeMs)continue;let R=await f.readFile(b,"utf-8");R.trim()&&l.push({rel:m,full:b,stat:O,content:R,prior:v})}if(l.length===0)return 0;let h;try{h=await Ct(r,n,"POST","/save_files",l.map(m=>({path:m.rel,content:m.content})))}catch(m){let b=m;return i(`\u274C Error saving files: ${b.body||b.message}`),0}let p=await yr(t),x=0,w=mr(h);for(let m=0;m<l.length;m++){let b=l[m],v=fr(b,m,h,w);if(o?.(m+1,l.length),!v){let O="No save response returned";p[b.rel]=O,i(`\u274C Error saving ${b.rel}: ${O}`);continue}if(v.error){p[b.rel]=v.error,i(`\u274C Error saving ${b.rel}: ${v.error}`);continue}delete p[b.rel],e[b.rel]={mtimeMs:b.stat.mtimeMs},i(`\u2705 ${b.prior?"Updated":"Created"} ${b.rel}`),x++}return await br(t,p),x}function mr(t){let e=new Map;for(let r of t)r.path&&e.set(r.path,r);return e}function fr(t,e,r,n){let i=n.get(t.rel);if(i)return i;let o=r[e];if(!o?.path||o.path===t.rel)return o}var Mt="sync-errors.log",Et=new Map;async function yr(t){let e=S.resolve(t),r=Et.get(e);if(r)return r;let n=await wr(e);return Et.set(e,n),n}async function wr(t){let e=S.join(t,Mt);if(!await f.pathExists(e))return{};let r=await f.readFile(e,"utf-8"),n={};for(let i of r.split(`
|
|
7
|
+
`)){let o=i.indexOf(": ");o>0&&(n[i.slice(0,o)]=i.slice(o+2))}return n}async function br(t,e){let r=S.join(t,Mt),n=Object.entries(e);if(n.length===0){await f.remove(r);return}await f.outputFile(r,n.map(([i,o])=>`${i}: ${o}`).join(`
|
|
8
8
|
`)+`
|
|
9
|
-
`)}async function br(t,e,r,n,i){Et.delete(S.resolve(t)),await m.remove(S.join(t,Mt)),n("\u{1F4E5} Fetching files...");let o=await Ct(e,r,"GET","/get_files"),l={},h=0;for(let p of o){let x=S.join(t,p.path);await m.outputFile(x,p.content);let w=(await m.stat(x)).mtimeMs;h++,i?.(h,o.length),l[p.path]={mtimeMs:w}}return n(`\u2714\uFE0F Synced ${o.length} file(s).`),{fileMap:l,pulled:h}}import xr from"path";import Sr from"chokidar";var vr=5e3,Tr=/(^|[/\\])(\.DS_Store|Thumbs\.db|desktop\.ini)$/,V=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??vr}start(){if(this.watcher)return;let e=xr.join(this.viewsDir,"src");this.watcher=Sr.watch([e],{persistent:!0,ignoreInitial:!0,ignored:Tr}).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 r=e;console.error("\u274C Sync failed:",r.body||r.message)}finally{this.syncInFlight=!1}}}};import gt from"fs-extra";import kr from"os";import ae from"path";var oe={localhost:"http://app.sleekcms.test/api",development:"https://app.sleekcms.dev/api",production:"https://app.sleekcms.com/api"};function Dt(t){return oe[t]||oe.production}var Br=ae.join(kr.homedir(),"SleekCMS"),ht=ae.join(Br,"auth.json"),Ot;function ce(t){Ot=t||void 0}function $t(t){let e=[];return t&&e.push(`_auth=${t}`),Ot&&e.push(`_dev=${Ot}`),e.length?e.join("; "):void 0}var L=class extends Error{status;body;constructor(e,r,n=""){super(e),this.name="UserAuthError",this.status=r,this.body=n}};async function le(){try{let t=await gt.readJson(ht);return t&&t.userToken?t:null}catch{return null}}async function ue(t){await gt.outputJson(ht,t,{spaces:2});try{await gt.chmod(ht,384)}catch{}}async function de(){await gt.remove(ht)}async function q(t){let e={},r=$t(t.userToken);r&&(e.Cookie=r),t.subdomain&&(e["x-subdomain"]=t.subdomain),t.slug&&(e.site=t.slug),t.body!==void 0&&(e["Content-Type"]="application/json");let n=await fetch(Dt(t.env)+t.path,{method:t.method,headers:e,body:t.body!==void 0?JSON.stringify(t.body):void 0});if(!n.ok){let i=await n.text().catch(()=>"");throw new L(`${t.method} ${t.path} \u2192 ${n.status}: ${i}`,n.status,i)}return n.json()}async function ge(t,e){let r={"Content-Type":"application/json"},n=$t();n&&(r.Cookie=n);let i=await fetch(Dt(t)+"/auth/otp",{method:"PUT",headers:r,body:JSON.stringify({email:e})});if(!i.ok){let l=await i.text().catch(()=>"");throw new L(`Failed to send OTP \u2192 ${i.status}: ${l}`,i.status,l)}return{otp:(await i.json().catch(()=>({}))).do_not_use_in_code}}async function Lt(t,e,r){let n={"Content-Type":"application/json"},i=$t();i&&(n.Cookie=i);let o=await fetch(Dt(t)+"/auth/login",{method:"POST",headers:n,body:JSON.stringify({email:e,otp:r})});if(!o.ok){let p=await o.text().catch(()=>"");throw new L(`Login failed \u2192 ${o.status}: ${p}`,o.status,p)}let l=o.headers.getSetCookie?.()??[],h=Er(l);if(!h)throw new L("Login succeeded but no _auth cookie was returned.",500);return h}function Er(t){for(let e of t){let r=e.match(/(?:^|;\s*)_auth=([^;]+)/);if(r&&r[1])return decodeURIComponent(r[1])}return null}async function Rt(t,e){return q({env:t,userToken:e,method:"GET",path:"/org"})}async function he(t,e,r){return q({env:t,userToken:e,method:"GET",path:"/site",subdomain:r})}async function pe(t,e,r,n){let i;try{i=await q({env:t,userToken:e,method:"GET",path:"/site_build",subdomain:r,slug:n})}catch{return null}if(!Array.isArray(i)||i.length===0)return null;let o=i[0],l=i.find(p=>typeof p.url=="string"&&p.url.length>0),h=Array.isArray(o.errors)?o.errors.map(p=>String(p)):[];return h.length>0?{status:"error",mode:"preview",errors:h,url:l?.url||void 0,at:o.updated_at?Date.parse(o.updated_at):void 0}:l?{status:"success",mode:"preview",url:l.url||void 0,at:l.updated_at?Date.parse(l.updated_at):void 0}:null}async function fe(t,e,r,n){let{token:i}=await q({env:t,userToken:e,method:"POST",path:"/site_user/api_token",subdomain:r,slug:n,body:{refresh:!1}});return i}async function me(t,e,r){let n=await q({env:t,userToken:e,method:"POST",path:"/org",body:r?{name:r}:{}});return{id:n.id,name:n.name,subdomain:n.subdomain}}async function ye(t,e,r,n){let i=await q({env:t,userToken:e,method:"POST",path:"/site",subdomain:r,body:{name:n}});return{id:i.id,name:i.name,slug:i.slug}}var we=500;function Or(){return new Date().toTimeString().slice(0,8)}function pt(t){let e=t;if(e.body){try{let r=JSON.parse(e.body);if(r&&typeof r.message=="string")return r.message;if(r&&typeof r.error=="string")return r.error}catch{}return e.body}return e.message}async function be(t){t&&(await Q.remove(Ft.join(t,"src")),await Q.remove(Ft.join(t,".cache","state.json")))}var ft=class extends Cr{env="production";userToken="";email;catalog=[];synced=new Map;message;messageIsError=!1;agentMd;buildScript;isShuttingDown=!1;constructor(e,r){super(),this.agentMd=e,this.buildScript=r}changed(){this.emit("change")}flash(e,r=!1){this.message=e,this.messageIsError=r,this.changed()}appendLog(e,r){let n=this.synced.get(e);n&&(n.logs.push({time:Or(),text:r}),n.logs.length>we&&n.logs.splice(0,n.logs.length-we),this.changed())}siteCount(){return this.synced.size}syncingCount(){let e=0;for(let r of this.synced.values())r.status==="syncing"&&e++;return e}watchingCount(){let e=0;for(let r of this.synced.values())(r.status==="watching"||r.status==="syncing")&&e++;return e}async init(e,r,n){this.env=e,this.userToken=r,n&&(this.email=n);let i=await Rt(e,r);await this.buildCatalog(i)}async buildCatalog(e){let r=[];for(let n of e)try{r.push({org:n,sites:await he(this.env,this.userToken,n.subdomain)})}catch(i){if(i instanceof L&&i.status===401)throw i;r.push({org:n,sites:[]})}this.catalog=r,this.changed()}isSynced(e){return this.synced.has(e)}availableSites(){let e=[];for(let r of this.catalog)for(let n of r.sites)this.synced.has(n.id)||e.push({org:r.org,site:n});return e}async addSite(e,r){if(this.synced.has(r.id))return;let n;try{n=await fe(this.env,this.userToken,e.subdomain,r.slug)}catch(o){this.flash(`Failed to authorize "${r.name}": ${pt(o)}`,!0);return}let i={org:e,site:r,siteToken:n,viewsDir:"",status:"syncing",logs:[]};this.synced.set(r.id,i),this.appendLog(r.id,"\u2795 added \u2014 pulling files"),this.changed();try{let o=await Pt({token:n,env:this.env,agentMd:this.agentMd,buildScript:this.buildScript,log:l=>this.appendLog(r.id,l),onProgress:(l,h)=>{i.progress={done:l,total:h},this.changed()}});i.viewsDir=o.viewsDir,i.progress=void 0,i.status="watching",i.lastSyncedAt=Date.now(),i.lastMsg=`pulled ${o.pulled} file(s)`,i.watcher=new V({viewsDir:o.viewsDir,onSync:()=>this.runSync(r.id)}),i.watcher.start(),this.watchBuildStatus(r.id),this.seedLastBuild(r.id),this.appendLog(r.id,"\u{1F440} watching for changes")}catch(o){let l=o;i.status="error",i.progress=void 0,i.lastMsg=l.body||l.message,this.appendLog(r.id,`\u274C ${l.body||l.message}`)}this.changed()}async removeSite(e){let r=this.synced.get(e);r&&(r.watcher?.setShuttingDown(!0),await r.watcher?.stop(),await r.buildWatcher?.close(),await be(r.viewsDir),this.synced.delete(e),this.changed())}buildSite(e,r=!1){let n=this.synced.get(e);if(!n||!n.viewsDir)return;let i=r?"live":"preview";this.writeBuildFile(n.viewsDir,{status:"building",mode:i,stage:"queued",url:n.lastBuild?.url,at:Date.now()}),Pr("node",r?["build.mjs","--deploy"]:["build.mjs"],{cwd:n.viewsDir,stdio:"ignore"}).on("error",h=>{this.writeBuildFile(n.viewsDir,{status:"error",mode:i,errors:[h.message],url:n.lastBuild?.url,at:Date.now()}),this.appendLog(e,`\u274C build: ${h.message}`)}),this.appendLog(e,r?"\u{1F680} deploying\u2026":"\u{1F528} building\u2026")}buildFile(e){return Ft.join(e,".cache","build.json")}async readBuildFile(e){try{let r=await Q.readJson(this.buildFile(e));return r&&r.status?r:null}catch{return null}}async writeBuildFile(e,r){let n=this.buildFile(e),i=`${n}.tmp`;try{await Q.outputJson(i,r),await Q.rename(i,n)}catch{}}watchBuildStatus(e){let r=this.synced.get(e);!r||!r.viewsDir||(r.buildWatcher=Mr.watch(this.buildFile(r.viewsDir),{ignoreInitial:!1}).on("add",()=>{this.readBuildStatus(e)}).on("change",()=>{this.readBuildStatus(e)}))}async readBuildStatus(e){let r=this.synced.get(e);if(!r)return;let n=await this.readBuildFile(r.viewsDir);if(!n)return;let i=r.lastBuild?.url;r.lastBuild={status:n.status,mode:n.mode??"preview",url:n.url??(n.status==="success"?void 0:i),errors:n.errors,stage:n.stage,at:n.at},this.changed()}async seedLastBuild(e){let r=this.synced.get(e);if(!r)return;let n=await pe(this.env,this.userToken,r.org.subdomain,r.site.slug);if(!n)return;let i=this.synced.get(e);if(!i)return;let o=await this.readBuildFile(i.viewsDir);o?.at!=null&&(n.at==null||o.at>=n.at)||await this.writeBuildFile(i.viewsDir,n)}async ensureOrg(e){if(e)return e;if(this.catalog[0])return this.catalog[0].org;let r;try{r=await Rt(this.env,this.userToken)}catch(n){return this.flash(`Couldn't check your organizations: ${pt(n)}`,!0),null}if(r.length>0){try{await this.buildCatalog(r)}catch{}return r[0]}try{let n=this.email?`${this.email.split("@")[0]}'s Sites`:"My Sites";this.flash("No organization yet \u2014 creating one\u2026");let i=await me(this.env,this.userToken,n);return this.catalog=[...this.catalog,{org:i,sites:[]}],this.changed(),i}catch(n){return this.flash(`Could not create an organization: ${pt(n)}`,!0),null}}async createNewSite(e,r){let n=await this.ensureOrg(r);if(!n)return;let i;try{i=await ye(this.env,this.userToken,n.subdomain,e)}catch(l){this.flash(`Could not create "${e}": ${pt(l)}`,!0);return}let o=this.catalog.find(l=>l.org.id===n.id);o&&!o.sites.some(l=>l.id===i.id)&&(o.sites=[...o.sites,i]),this.flash(`Created "${i.name}".`),await this.addSite(n,i)}async refetch(e){await this.runSync(e,{flush:!0})}async runSync(e,r={}){let n=this.synced.get(e);if(!(!n||!n.viewsDir||n.status==="paused")){n.status="syncing",this.changed();try{let i=await Pt({token:n.siteToken,env:this.env,viewsDir:n.viewsDir,agentMd:this.agentMd,buildScript:this.buildScript,flush:r.flush,log:o=>this.appendLog(e,o),onProgress:(o,l)=>{n.progress={done:o,total:l},this.changed()}});n.progress=void 0,n.status="watching",n.lastSyncedAt=Date.now(),r.flush?n.lastMsg=`re-fetched ${i.pulled} file(s)`:n.lastMsg=i.pushed>0?`pushed ${i.pushed} file(s)`:"up to date"}catch(i){let o=i;n.status="error",n.progress=void 0,n.lastMsg=o.body||o.message,this.appendLog(e,`\u274C ${o.body||o.message}`)}this.changed()}}async togglePause(e){let r=this.synced.get(e);!r||!r.viewsDir||(r.status==="paused"?(r.watcher=new V({viewsDir:r.viewsDir,onSync:()=>this.runSync(e)}),r.watcher.start(),r.status="watching",this.appendLog(e,"\u25B6 resumed \u2014 watching for changes")):(r.watcher?.setShuttingDown(!0),await r.watcher?.stop(),r.watcher=void 0,r.progress=void 0,r.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 e.buildWatcher?.close(),await be(e.viewsDir)}async exit(){this.isShuttingDown||(this.isShuttingDown=!0,await this.cleanupAll(),this.emit("exit"))}async logout(){this.isShuttingDown||(this.isShuttingDown=!0,await de(),await this.cleanupAll(),this.emit("logout"))}};import{useEffect as Wr,useState as Oe}from"react";import{Box as Gr,Text as qr,useInput as Hr}from"ink";import{useState as Z}from"react";import{Box as tt,Text as H}from"ink";import xe from"ink-text-input";import{jsx as _,jsxs as J}from"react/jsx-runtime";function Se(t){let e=t;return e?.body||e?.message||String(t)}function ve({env:t,initialError:e,onAuthed:r}){let[n,i]=Z("email"),[o,l]=Z(""),[h,p]=Z(""),[x,w]=Z(e),[f,b]=Z(),v=async R=>{let k=R.trim();if(k){w(void 0),i("busy");try{let{otp:M}=await ge(t,k);if(l(k),M){let X=await Lt(t,k,M);await r(X,k);return}b(`OTP sent to ${k}`),i("otp")}catch(M){w(Se(M)),i("email")}}},D=async R=>{let k=R.trim();if(k){w(void 0),i("busy");try{let M=await Lt(t,o,k);await r(M,o)}catch(M){w(Se(M)),p(""),i("otp")}}};return J(tt,{flexDirection:"column",children:[J(H,{bold:!0,children:["\u25C6 SleekCMS \u2014 sign in",t!=="production"?` (${t})`:""]}),x?J(H,{color:"red",children:["\u2717 ",x]}):null,n==="email"&&J(tt,{marginTop:1,children:[_(H,{children:"Email: "}),_(xe,{value:o,onChange:l,onSubmit:v,placeholder:"you@example.com"})]}),n==="otp"&&J(tt,{flexDirection:"column",marginTop:1,children:[f?_(H,{color:"green",children:f}):null,J(tt,{children:[_(H,{children:"Enter OTP: "}),_(xe,{value:h,onChange:p,onSubmit:D})]})]}),n==="busy"&&_(tt,{marginTop:1,children:_(H,{children:"Working\u2026"})})]})}import{Fragment as $r,useEffect as et,useMemo as Lr,useState as E}from"react";import{Box as g,Text as c,useInput as Rr,useStdout as Fr}from"ink";import Be from"ink-text-input";import Dr from"node:os";function At(t){if(!t)return"\u2014";let e=Dr.homedir();return t.startsWith(e)?"~"+t.slice(e.length):t}function jt(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 r=Math.floor(e/60);if(r<60)return`${r}m ago`;let n=Math.floor(r/60);return n<24?`${n}h ago`:`${Math.floor(n/24)}d ago`}function yt(t){return!t||t.total<=0?null:Math.round(t.done/t.total*100)}var mt=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"];function Te(t){return mt[(t%mt.length+mt.length)%mt.length]}function wt(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"}}}function ke(t){if(!t)return{dot:"\xB7",color:"gray",label:"\u2014"};switch(t.status){case"building":return{dot:"\u2234",color:"yellow",label:"building"};case"error":return{dot:"\u2717",color:"red",label:"failed"};case"success":return{dot:"\u2713",color:"green",label:t.mode==="live"?"live":"preview"}}}import{Fragment as rt,jsx as s,jsxs as u}from"react/jsx-runtime";var N=(t,e)=>e<=0?0:Math.max(0,Math.min(t,e-1)),Ar=16,jr=5,Ir="greenBright",Ee="blueBright",Ce="magentaBright";function Pe({k:t,bg:e=Ir}){return s(c,{color:"black",backgroundColor:e,children:` ${t} `})}function U({k:t,label:e,width:r=16,bg:n}){return u(g,{width:r,children:[s(Pe,{k:t,bg:n}),s(c,{color:"gray",children:` ${e}`})]})}function C({k:t,label:e,bg:r}){return u(g,{marginBottom:1,children:[s(g,{width:9,children:s(Pe,{k:t,bg:r})}),s(c,{color:"gray",children:e})]})}function _r({launchers:t}){return u(g,{flexDirection:"column",borderStyle:"round",borderColor:"green",paddingX:2,paddingY:1,width:66,children:[u(g,{justifyContent:"space-between",children:[s(c,{color:"green",bold:!0,children:"KEYBOARD SHORTCUTS"}),s(c,{color:"gray",children:"? or esc to close"})]}),u(g,{marginTop:1,children:[u(g,{flexDirection:"column",width:34,children:[s(c,{color:"cyan",bold:!0,children:"Navigate"}),s(C,{k:"\u2191\u2193",label:"move selection"}),s(C,{k:"\u23CE",label:"start / stop sync"}),s(C,{k:"tab",label:"focus logs \xB7 \u2191\u2193 scroll"}),s(C,{k:"esc",label:"clear filter / back"}),s(g,{marginTop:1,children:s(c,{color:"cyan",bold:!0,children:"Global"})}),s(C,{k:"/",label:"search sites"}),s(C,{k:"n",label:"new site"}),s(C,{k:"?",label:"toggle this help",bg:Ee}),s(C,{k:"q",label:"logout"}),s(C,{k:"ctrl+c",label:"quit"})]}),u(g,{flexDirection:"column",flexGrow:1,children:[s(c,{color:"cyan",bold:!0,children:"Site actions"}),s(C,{k:"p",label:"pause / resume sync"}),s(C,{k:"b",label:"build preview"}),s(C,{k:"B",label:"deploy live"}),s(C,{k:"o",label:"open preview url"}),t.map((e,r)=>s(C,{k:String(r+1),label:`open in ${e.label}`,bg:Ce},e.key))]})]})]})}function Nr({percent:t,width:e=28}){let r=Math.round(Math.max(0,Math.min(100,t))/100*e);return u(c,{children:[s(c,{color:"green",children:"\u2588".repeat(r)}),s(c,{color:"gray",children:"\u2591".repeat(Math.max(0,e-r))})]})}function Ur({frame:t,width:e=24,seg:r=6}){let n=e+r,i=t%n-r,o="";for(let l=0;l<e;l++)o+=l>=i&&l<i+r?"\u25AE":"\u25AF";return s(c,{color:"yellow",children:o})}function Me({controller:t}){let{stdout:e}=Fr(),r=Lr(()=>ie(),[]),[,n]=E(0),[i,o]=E(()=>new Date),[l,h]=E(null),[p,x]=E(()=>t.synced.size>0||t.catalog.some(d=>d.sites.length>0)?"dashboard":t.catalog.length<=1?"newName":"newOrg"),[w,f]=E(0),[b,v]=E(""),[D,R]=E(!1),[k,M]=E(""),[X,nt]=E(!1),[Nt,Fe]=E(0),[Ut,Wt]=E(!1),[Ae,je]=E(!1),[it,st]=E("list"),[Ie,K]=E(0),Gt=()=>n(a=>a+1);et(()=>(t.on("change",Gt),()=>{t.off("change",Gt)}),[t]),et(()=>{let a=setInterval(()=>o(new Date),1e3);return()=>clearInterval(a)},[]);let A=t.catalog.flatMap(a=>a.sites.map(d=>({org:a.org,site:d,entry:t.synced.get(d.id)}))).sort((a,d)=>(a.entry?0:1)-(d.entry?0:1)),j=k.trim().toLowerCase(),O=j?A.filter(a=>a.site.name.toLowerCase().includes(j)||(a.org.name||a.org.subdomain||"").toLowerCase().includes(j)):A,W=O.findIndex(a=>a.site.id===l);W<0&&(W=0);let B=O[W],y=B?.entry,ot=Math.max(3,(e?.rows??30)-26),I=y?.logs??[],xt=Math.max(0,I.length-ot),_e=Math.min(Ie,xt),at=I.length-_e,qt=I.slice(Math.max(0,at-ot),at),Ne=B?.site.id;et(()=>K(0),[Ne]);let F=y?.lastBuild,St=F?.status==="building",vt=ke(F),ct=St;if(!ct){for(let a of t.synced.values())if(a.status==="watching"||a.status==="syncing"){ct=!0;break}}et(()=>{if(!ct)return;let a=setInterval(()=>Fe(d=>d+1),120);return()=>clearInterval(a)},[ct]);let Ht=O.findIndex(a=>!a.entry),G=Math.max(jr,(e?.rows??30)-Ar),z=O.length,lt=z>G,Ue=Math.floor(G/2),Y=lt?Math.max(0,Math.min(W-Ue,z-G)):0,Tt=Math.min(z,Y+G),We=O.slice(Y,Tt);et(()=>{p!=="dashboard"||D||A.length>0||(v(""),t.catalog.length<=1?x("newName"):(f(0),x("newOrg")))},[A.length,p,D,t.catalog.length]);let ut=a=>{if(O.length===0)return;let d=N(W+a,O.length);h(O[d].site.id)},Ge=()=>{B&&(B.entry?t.removeSite(B.site.id):t.addSite(B.org,B.site))},qe=()=>{v(""),t.catalog.length<=1?x("newName"):(f(0),x("newOrg"))},He=a=>{if(!y){t.flash("Press \u23CE to start syncing this site first.",!0);return}je(!0);let d=a.run(y.viewsDir);t.flash(d?`Opened ${a.label} at ${At(y.viewsDir)}`:`Couldn't open ${a.label}.`,!d)},Je=()=>{if(!y){t.flash("Press \u23CE to start syncing this site first.",!0);return}t.togglePause(y.site.id)},Jt=a=>{if(!y){t.flash("Press \u23CE to start syncing this site first.",!0);return}t.buildSite(y.site.id,a),t.flash(a?"Deploying \u2014 building live\u2026":"Building preview\u2026")},Xe=()=>{let a=y?.lastBuild?.url;if(!a){t.flash("No build URL yet \u2014 press b to build.",!0);return}t.flash(ne(a)?`Opening ${a}`:"Couldn't open the browser.",!1)};Rr((a,d)=>{if(Ut){(a==="?"||d.escape)&&Wt(!1);return}if(p==="newOrg"){d.escape?x("dashboard"):d.upArrow?f(T=>N(T-1,t.catalog.length)):d.downArrow?f(T=>N(T+1,t.catalog.length)):d.return&&x("newName");return}if(X){d.escape?(nt(!1),M(""),h(null)):d.return?nt(!1):d.upArrow?ut(-1):d.downArrow&&ut(1);return}if(d.tab){it==="logs"?st("list"):y&&st("logs"),K(0);return}if(it==="logs"){if(d.escape){st("list"),K(0);return}if(d.upArrow)return void K(T=>Math.min(T+1,xt));if(d.downArrow)return void K(T=>Math.max(0,T-1))}if(d.escape){M(""),h(null);return}if(a==="?")return void Wt(!0);if(a==="/")return st("list"),void nt(!0);if(a==="n")return void qe();if(a==="q")return void t.logout();if(d.upArrow)return ut(-1);if(d.downArrow)return ut(1);if(d.return||a===" ")return Ge();if(a==="p")return Je();if(a==="b")return Jt(!1);if(a==="B")return Jt(!0);if(a==="o")return Xe();if(a>="1"&&a<="9"){let T=r[parseInt(a,10)-1];if(T)return He(T)}},{isActive:p!=="newName"});let Ke=async a=>{let d=a.trim();if(!d){x("dashboard");return}let T=t.catalog[N(w,t.catalog.length)]?.org??t.catalog[0]?.org;R(!0),x("dashboard"),await t.createNewSite(d,T),R(!1)},Xt=u(g,{justifyContent:"space-between",paddingX:1,children:[u(g,{children:[s(c,{color:"green",bold:!0,children:"\u25C6 SleekCMS"}),s(c,{color:"gray",children:" \u2502 "}),s(c,{children:t.email??"signed in"})]}),u(g,{children:[u(c,{color:"gray",children:[j?`${O.length}/${A.length}`:A.length," sites \xB7 "]}),u(c,{color:"green",children:[t.watchingCount()," watching"]}),s(c,{color:"gray",children:` \xB7 ${i.toTimeString().slice(0,8)}`})]})]});return Ut?u(g,{flexDirection:"column",children:[Xt,s(g,{marginTop:1,justifyContent:"center",children:s(_r,{launchers:r})})]}):u(g,{flexDirection:"column",children:[Xt,u(g,{children:[u(g,{flexDirection:"column",borderStyle:"round",borderColor:"green",width:38,paddingX:1,children:[u(g,{justifyContent:"space-between",children:[s(c,{color:"green",bold:!0,children:"SITE WORKSPACES"}),A.length>G&&!X&&!j?s(c,{color:"gray",children:"/ filter"}):null]}),X?u(g,{children:[s(c,{color:"cyan",children:"/ "}),s(Be,{value:k,onChange:M,onSubmit:()=>nt(!1),placeholder:"type to filter\u2026"})]}):j?s(c,{color:"gray",children:`/ ${k} \xB7 ${O.length} match${O.length===1?"":"es"}`}):null,s(g,{flexDirection:"column",marginTop:1,minHeight:lt?G+3:void 0,children:O.length===0?s(c,{color:"gray",children:j?`no matches for \u201C${k}\u201D`:"no sites \u2014 press n to create one"}):u(rt,{children:[lt?s(c,{color:"gray",children:Y>0?` \u2191 ${Y} more`:" "}):null,We.map((a,d)=>{let T=Y+d,Kt=a.entry?wt(a.entry.status):{dot:"\xB7",color:"gray",label:"idle"},zt=yt(a.entry?.progress),ze=a.entry?a.entry.status==="syncing"&&zt!=null?`${zt}%`:a.entry.status==="paused"?"paused":"--":"",dt=T===W,Ye=T===Ht&&Ht>0;return u($r,{children:[Ye?s(c,{color:"gray",children:"\u2500".repeat(34)}):null,u(g,{justifyContent:"space-between",children:[u(g,{children:[s(c,{color:dt?"green":"gray",children:dt?"\u276F ":" "}),u(c,{color:a.entry?.status==="watching"?"green":Kt.color,children:[a.entry?.status==="watching"?Te(Nt):Kt.dot," "]}),s(c,{color:dt?"white":a.entry?void 0:"gray",bold:dt,children:a.site.name})]}),s(c,{color:a.entry?.status==="syncing"?"green":"gray",children:ze})]})]},a.site.id)}),lt?s(c,{color:"gray",children:Tt<z?` \u2193 ${z-Tt} more`:" "}):null]})}),s(g,{marginTop:1,children:s(c,{color:"gray",children:"+ new site \xB7 n"})})]}),u(g,{flexDirection:"column",flexGrow:1,marginLeft:1,children:[s(g,{flexDirection:"column",borderStyle:"round",borderColor:"gray",paddingX:1,children:B?u(rt,{children:[u(c,{children:[B.org.name||B.org.subdomain?u(c,{color:"gray",children:[B.org.name||B.org.subdomain," / "]}):null,s(c,{color:"cyan",bold:!0,children:B.site.name})]}),u(g,{marginTop:1,children:[s(c,{color:"gray",children:"status "}),y?s(c,{color:wt(y.status).color,children:`: ${wt(y.status).label}`}):s(c,{color:"gray",children:": idle \u2014 not syncing (press \u23CE to start)"})]}),u(g,{children:[s(c,{color:"gray",children:"path "}),s(c,{wrap:"truncate-end",children:y?.viewsDir?At(y.viewsDir):"\u2014"})]}),u(g,{children:[s(c,{color:"gray",children:"synced "}),s(c,{children:jt(y?.lastSyncedAt)}),s(c,{color:"gray",children:" progress "}),s(c,{color:y?.status==="syncing"?"green":"gray",children:y?.status==="syncing"?`${yt(y.progress)??0}%`:"\u2014"})]}),s(g,{marginTop:1,children:s(Nr,{percent:y?y.status==="syncing"?yt(y.progress)??0:y.status==="watching"?100:0:0})}),y?u(rt,{children:[u(g,{marginTop:1,children:[s(c,{color:"gray",children:"build "}),s(c,{color:vt.color,children:`${vt.dot} ${vt.label}`}),s(c,{color:"gray",children:" last "}),s(c,{children:St?"in progress":jt(F?.at)})]}),u(g,{children:[s(c,{color:"gray",children:"url "}),F?.url?s(c,{color:"cyan",wrap:"truncate-end",children:F.url}):s(c,{color:"gray",children:"\u2014"})]}),F?.status==="error"&&F.errors?.length?u(g,{children:[s(c,{color:"red",children:"\u258C "}),s(c,{color:"red",wrap:"truncate-end",children:`\u2717 ${F.errors[0]}`})]}):null,St?u(g,{children:[s(Ur,{frame:Nt}),s(c,{color:"gray",children:` ${F?.stage??"building"}`})]}):null]}):null]}):s(c,{color:"gray",children:"No site selected."})}),p==="dashboard"?u(g,{flexDirection:"column",borderStyle:"round",borderColor:it==="logs"?"green":"gray",paddingX:1,marginTop:1,height:ot+4,flexShrink:0,overflowY:"hidden",children:[u(g,{justifyContent:"space-between",children:[u(c,{children:[s(c,{color:"cyan",bold:!0,children:"LOGS \xB7 "}),s(c,{color:"white",children:B?.site.name??"\u2014"})]}),y&&I.length>0?it==="logs"?s(c,{color:"green",children:`\u2191\u2193 scroll \xB7 esc back${xt>0?` \xB7 ${at}/${I.length}`:""}`}):I.length>ot?s(c,{color:"gray",children:"tab to scroll"}):null:null]}),s(g,{flexDirection:"column",marginTop:1,children:!y||I.length===0?s(c,{color:"gray",children:"no activity yet"}):qt.map((a,d)=>u(c,{wrap:"truncate-end",children:[u(c,{color:"gray",children:[a.time," "]}),a.text]},at-qt.length+d))})]}):u(g,{flexDirection:"column",borderStyle:"round",borderColor:"blue",paddingX:1,marginTop:1,flexGrow:1,children:[p==="newOrg"&&u(rt,{children:[s(c,{color:"cyan",bold:!0,children:"CREATE SITE \u2014 PICK ORG"}),s(c,{color:"gray",children:"\u2191/\u2193 select \xB7 \u23CE next \xB7 esc cancel"}),s(g,{flexDirection:"column",marginTop:1,children:t.catalog.map((a,d)=>u(c,{color:d===N(w,t.catalog.length)?"blue":void 0,bold:d===N(w,t.catalog.length),children:[d===N(w,t.catalog.length)?"\u276F ":" ",a.org.name||a.org.subdomain]},a.org.id))})]}),p==="newName"&&u(rt,{children:[s(c,{color:"cyan",bold:!0,children:"CREATE SITE"}),u(g,{marginTop:1,children:[s(c,{children:"New site name: "}),s(Be,{value:b,onChange:v,onSubmit:Ke})]}),s(c,{color:"gray",children:"\u23CE create \xB7 empty + \u23CE to cancel"})]})]})]})]}),t.message?s(g,{paddingX:1,children:u(c,{color:t.messageIsError?"red":"gray",wrap:"truncate-end",children:[t.messageIsError?"\u2717 ":"",t.message]})}):null,u(g,{flexDirection:"column",marginTop:1,children:[u(g,{paddingX:1,justifyContent:"space-between",children:[u(g,{flexWrap:"wrap",children:[s(U,{k:"\u2191\u2193",label:"select"}),s(U,{k:"/",label:"search"}),s(U,{k:"\u23CE",label:"start/stop"}),A.length===0?s(U,{k:"n",label:"new site"}):null,s(U,{k:"ctrl+c",label:"quit",width:14})]}),s(U,{k:"?",label:"all keys",width:12,bg:Ee})]}),!Ae&&r.length>0?s(g,{paddingX:1,marginTop:1,flexWrap:"wrap",children:r.map((a,d)=>s(U,{k:String(d+1),label:a.label,bg:Ce},a.key))}):null]})]})}import{jsx as bt}from"react/jsx-runtime";function De(t){let e=t;return e?.body||e?.message||String(t)}function $e({controller:t,env:e,devSecret:r,savedToken:n,savedEmail:i}){let[o,l]=Oe(n?"loading":"auth"),[h,p]=Oe();Hr((w,f)=>{f.ctrl&&w==="c"&&t.exit()}),Wr(()=>{n&&t.init(e,n,i).then(()=>l("manage")).catch(w=>{w instanceof L&&w.status===401?p("Saved session expired \u2014 please sign in again."):p(De(w)),l("auth")})},[]);let x=async(w,f)=>{p(void 0),await ue({env:e,userToken:w,devSecret:r,email:f}),l("loading");try{await t.init(e,w,f),l("manage")}catch(b){p(De(b)),l("auth")}};return o==="auth"?bt(ve,{env:e,initialError:h,onAuthed:x}):o==="loading"?bt(Gr,{children:bt(qr,{children:"Loading your sites\u2026 "})}):bt(Me,{controller:t})}import{jsx as Zr}from"react/jsx-runtime";var zr=Xr(import.meta.url),It=te();if(It.version){let t=zr("../package.json");console.log(t.version),process.exit(0)}var Re=_t.dirname(Jr(import.meta.url)),Yr=Le(_t.join(Re,"AGENT.md"),"utf-8"),Vr=Le(_t.join(Re,"build.mjs"),"utf-8");async function Qr(){let t=new ft(Yr,Vr),e=await le(),r=(It.env??e?.env??"production").toLowerCase(),n=It.dev??process.env.SLEEKCMS_DEV_SECRET??e?.devSecret;ce(n),process.stdout.isTTY&&process.stdout.write("\x1B[2J\x1B[3J\x1B[H");let i=Kr(Zr($e,{controller:t,env:r,devSecret:n,savedToken:e?.userToken,savedEmail:e?.email}),{exitOnCtrlC:!1}),o=l=>{i.unmount(),console.log(l),process.exit(0)};t.once("exit",()=>o("\u2713 Stopped. Local site files were cleaned up.")),t.once("logout",()=>o("\u{1F44B} Logged out. Run `sleekcms` to sign in again.")),process.on("SIGINT",()=>{t.exit()}),process.on("SIGTERM",()=>{t.exit()}),await i.waitUntilExit()}Qr().catch(t=>{let e=t;console.error("\u274C",e.body||e.message),process.exit(1)});
|
|
9
|
+
`)}async function xr(t,e,r,n,i){Et.delete(S.resolve(t)),await f.remove(S.join(t,Mt)),n("\u{1F4E5} Fetching files...");let o=await Ct(e,r,"GET","/get_files"),l={},h=0;for(let p of o){let x=S.join(t,p.path);await f.outputFile(x,p.content);let w=(await f.stat(x)).mtimeMs;h++,i?.(h,o.length),l[p.path]={mtimeMs:w}}return n(`\u2714\uFE0F Synced ${o.length} file(s).`),{fileMap:l,pulled:h}}import Sr from"path";import vr from"chokidar";var Tr=5e3,kr=/(^|[/\\])(\.DS_Store|Thumbs\.db|desktop\.ini)$/,V=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??Tr}start(){if(this.watcher)return;let e=Sr.join(this.viewsDir,"src");this.watcher=vr.watch([e],{persistent:!0,ignoreInitial:!0,ignored:kr}).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 r=e;console.error("\u274C Sync failed:",r.body||r.message)}finally{this.syncInFlight=!1}}}};import gt from"fs-extra";import Br from"os";import ae from"path";var oe={localhost:"http://app.sleekcms.test/api",development:"https://app.sleekcms.dev/api",production:"https://app.sleekcms.com/api"};function Ot(t){return oe[t]||oe.production}var Er=ae.join(Br.homedir(),"SleekCMS"),ht=ae.join(Er,"auth.json"),Dt;function ce(t){Dt=t||void 0}function $t(t){let e=[];return t&&e.push(`_auth=${t}`),Dt&&e.push(`_dev=${Dt}`),e.length?e.join("; "):void 0}var L=class extends Error{status;body;constructor(e,r,n=""){super(e),this.name="UserAuthError",this.status=r,this.body=n}};async function le(){try{let t=await gt.readJson(ht);return t&&t.userToken?t:null}catch{return null}}async function ue(t){await gt.outputJson(ht,t,{spaces:2});try{await gt.chmod(ht,384)}catch{}}async function de(){await gt.remove(ht)}async function q(t){let e={},r=$t(t.userToken);r&&(e.Cookie=r),t.subdomain&&(e["x-subdomain"]=t.subdomain),t.slug&&(e.site=t.slug),t.body!==void 0&&(e["Content-Type"]="application/json");let n=await fetch(Ot(t.env)+t.path,{method:t.method,headers:e,body:t.body!==void 0?JSON.stringify(t.body):void 0});if(!n.ok){let i=await n.text().catch(()=>"");throw new L(`${t.method} ${t.path} \u2192 ${n.status}: ${i}`,n.status,i)}return n.json()}async function ge(t,e){let r={"Content-Type":"application/json"},n=$t();n&&(r.Cookie=n);let i=await fetch(Ot(t)+"/auth/otp",{method:"PUT",headers:r,body:JSON.stringify({email:e})});if(!i.ok){let l=await i.text().catch(()=>"");throw new L(`Failed to send OTP \u2192 ${i.status}: ${l}`,i.status,l)}return{otp:(await i.json().catch(()=>({}))).do_not_use_in_code}}async function Lt(t,e,r){let n={"Content-Type":"application/json"},i=$t();i&&(n.Cookie=i);let o=await fetch(Ot(t)+"/auth/login",{method:"POST",headers:n,body:JSON.stringify({email:e,otp:r})});if(!o.ok){let p=await o.text().catch(()=>"");throw new L(`Login failed \u2192 ${o.status}: ${p}`,o.status,p)}let l=o.headers.getSetCookie?.()??[],h=Cr(l);if(!h)throw new L("Login succeeded but no _auth cookie was returned.",500);return h}function Cr(t){for(let e of t){let r=e.match(/(?:^|;\s*)_auth=([^;]+)/);if(r&&r[1])return decodeURIComponent(r[1])}return null}async function Rt(t,e){return q({env:t,userToken:e,method:"GET",path:"/org"})}async function he(t,e,r){return q({env:t,userToken:e,method:"GET",path:"/site",subdomain:r})}async function pe(t,e,r,n){let i;try{i=await q({env:t,userToken:e,method:"GET",path:"/site_build",subdomain:r,slug:n})}catch{return null}if(!Array.isArray(i)||i.length===0)return null;let o=i[0],l=i.find(p=>typeof p.url=="string"&&p.url.length>0),h=Array.isArray(o.errors)?o.errors.map(p=>String(p)):[];return h.length>0?{status:"error",mode:"preview",errors:h,url:l?.url||void 0,at:o.updated_at?Date.parse(o.updated_at):void 0}:l?{status:"success",mode:"preview",url:l.url||void 0,at:l.updated_at?Date.parse(l.updated_at):void 0}:null}async function me(t,e,r,n){let{token:i}=await q({env:t,userToken:e,method:"POST",path:"/site_user/api_token",subdomain:r,slug:n,body:{refresh:!1}});return i}async function fe(t,e,r){let n=await q({env:t,userToken:e,method:"POST",path:"/org",body:r?{name:r}:{}});return{id:n.id,name:n.name,subdomain:n.subdomain}}async function ye(t,e,r,n){let i=await q({env:t,userToken:e,method:"POST",path:"/site",subdomain:r,body:{name:n}});return{id:i.id,name:i.name,slug:i.slug}}var we=500;function Or(){return new Date().toTimeString().slice(0,8)}function pt(t){let e=t;if(e.body){try{let r=JSON.parse(e.body);if(r&&typeof r.message=="string")return r.message;if(r&&typeof r.error=="string")return r.error}catch{}return e.body}return e.message}async function be(t){t&&(await Q.remove(At.join(t,"src")),await Q.remove(At.join(t,".cache","state.json")))}var mt=class extends Pr{env="production";userToken="";email;catalog=[];synced=new Map;message;messageIsError=!1;agentMd;buildScript;isShuttingDown=!1;constructor(e,r){super(),this.agentMd=e,this.buildScript=r}changed(){this.emit("change")}flash(e,r=!1){this.message=e,this.messageIsError=r,this.changed()}appendLog(e,r){let n=this.synced.get(e);n&&(n.logs.push({time:Or(),text:r}),n.logs.length>we&&n.logs.splice(0,n.logs.length-we),this.changed())}siteCount(){return this.synced.size}syncingCount(){let e=0;for(let r of this.synced.values())r.status==="syncing"&&e++;return e}watchingCount(){let e=0;for(let r of this.synced.values())(r.status==="watching"||r.status==="syncing")&&e++;return e}async init(e,r,n){this.env=e,this.userToken=r,n&&(this.email=n);let i=await Rt(e,r);await this.buildCatalog(i)}async buildCatalog(e){let r=[];for(let n of e)try{r.push({org:n,sites:await he(this.env,this.userToken,n.subdomain)})}catch(i){if(i instanceof L&&i.status===401)throw i;r.push({org:n,sites:[]})}this.catalog=r,this.changed()}isSynced(e){return this.synced.has(e)}availableSites(){let e=[];for(let r of this.catalog)for(let n of r.sites)this.synced.has(n.id)||e.push({org:r.org,site:n});return e}async addSite(e,r){if(this.synced.has(r.id))return;let n;try{n=await me(this.env,this.userToken,e.subdomain,r.slug)}catch(o){this.flash(`Failed to authorize "${r.name}": ${pt(o)}`,!0);return}let i={org:e,site:r,siteToken:n,viewsDir:"",status:"syncing",logs:[]};this.synced.set(r.id,i),this.appendLog(r.id,"\u2795 added \u2014 pulling files"),this.changed();try{let o=await Pt({token:n,env:this.env,agentMd:this.agentMd,buildScript:this.buildScript,log:l=>this.appendLog(r.id,l),onProgress:(l,h)=>{i.progress={done:l,total:h},this.changed()}});i.viewsDir=o.viewsDir,i.progress=void 0,i.status="watching",i.lastSyncedAt=Date.now(),i.lastMsg=`pulled ${o.pulled} file(s)`,i.watcher=new V({viewsDir:o.viewsDir,onSync:()=>this.runSync(r.id)}),i.watcher.start(),this.watchBuildStatus(r.id),this.seedLastBuild(r.id),this.appendLog(r.id,"\u{1F440} watching for changes")}catch(o){let l=o;i.status="error",i.progress=void 0,i.lastMsg=l.body||l.message,this.appendLog(r.id,`\u274C ${l.body||l.message}`)}this.changed()}async removeSite(e){let r=this.synced.get(e);r&&(r.watcher?.setShuttingDown(!0),await r.watcher?.stop(),await r.buildWatcher?.close(),await be(r.viewsDir),this.synced.delete(e),this.changed())}buildSite(e,r=!1){let n=this.synced.get(e);if(!n||!n.viewsDir)return;let i=r?"live":"preview";this.writeBuildFile(n.viewsDir,{status:"building",mode:i,stage:"queued",url:n.lastBuild?.url,at:Date.now()}),Mr("node",r?["build.mjs","--deploy"]:["build.mjs"],{cwd:n.viewsDir,stdio:"ignore"}).on("error",h=>{this.writeBuildFile(n.viewsDir,{status:"error",mode:i,errors:[h.message],url:n.lastBuild?.url,at:Date.now()}),this.appendLog(e,`\u274C build: ${h.message}`)}),this.appendLog(e,r?"\u{1F680} deploying\u2026":"\u{1F528} building\u2026")}buildFile(e){return At.join(e,".cache","build.json")}async readBuildFile(e){try{let r=await Q.readJson(this.buildFile(e));return r&&r.status?r:null}catch{return null}}async writeBuildFile(e,r){let n=this.buildFile(e),i=`${n}.tmp`;try{await Q.outputJson(i,r),await Q.rename(i,n)}catch{}}watchBuildStatus(e){let r=this.synced.get(e);!r||!r.viewsDir||(r.buildWatcher=Dr.watch(this.buildFile(r.viewsDir),{ignoreInitial:!1}).on("add",()=>{this.readBuildStatus(e)}).on("change",()=>{this.readBuildStatus(e)}))}async readBuildStatus(e){let r=this.synced.get(e);if(!r)return;let n=await this.readBuildFile(r.viewsDir);if(!n)return;let i=r.lastBuild?.url;r.lastBuild={status:n.status,mode:n.mode??"preview",url:n.url??(n.status==="success"?void 0:i),errors:n.errors,stage:n.stage,at:n.at},this.changed()}async seedLastBuild(e){let r=this.synced.get(e);if(!r)return;let n=await pe(this.env,this.userToken,r.org.subdomain,r.site.slug);if(!n)return;let i=this.synced.get(e);if(!i)return;let o=await this.readBuildFile(i.viewsDir);o?.at!=null&&(n.at==null||o.at>=n.at)||await this.writeBuildFile(i.viewsDir,n)}async ensureOrg(e){if(e)return e;if(this.catalog[0])return this.catalog[0].org;let r;try{r=await Rt(this.env,this.userToken)}catch(n){return this.flash(`Couldn't check your organizations: ${pt(n)}`,!0),null}if(r.length>0){try{await this.buildCatalog(r)}catch{}return r[0]}try{let n=this.email?`${this.email.split("@")[0]}'s Sites`:"My Sites";this.flash("No organization yet \u2014 creating one\u2026");let i=await fe(this.env,this.userToken,n);return this.catalog=[...this.catalog,{org:i,sites:[]}],this.changed(),i}catch(n){return this.flash(`Could not create an organization: ${pt(n)}`,!0),null}}async createNewSite(e,r){let n=await this.ensureOrg(r);if(!n)return;let i;try{i=await ye(this.env,this.userToken,n.subdomain,e)}catch(l){this.flash(`Could not create "${e}": ${pt(l)}`,!0);return}let o=this.catalog.find(l=>l.org.id===n.id);o&&!o.sites.some(l=>l.id===i.id)&&(o.sites=[...o.sites,i]),this.flash(`Created "${i.name}".`),await this.addSite(n,i)}async refetch(e){await this.runSync(e,{flush:!0})}async runSync(e,r={}){let n=this.synced.get(e);if(!(!n||!n.viewsDir||n.status==="paused")){n.status="syncing",this.changed();try{let i=await Pt({token:n.siteToken,env:this.env,viewsDir:n.viewsDir,agentMd:this.agentMd,buildScript:this.buildScript,flush:r.flush,log:o=>this.appendLog(e,o),onProgress:(o,l)=>{n.progress={done:o,total:l},this.changed()}});n.progress=void 0,n.status="watching",n.lastSyncedAt=Date.now(),r.flush?n.lastMsg=`re-fetched ${i.pulled} file(s)`:n.lastMsg=i.pushed>0?`pushed ${i.pushed} file(s)`:"up to date"}catch(i){let o=i;n.status="error",n.progress=void 0,n.lastMsg=o.body||o.message,this.appendLog(e,`\u274C ${o.body||o.message}`)}this.changed()}}async togglePause(e){let r=this.synced.get(e);!r||!r.viewsDir||(r.status==="paused"?(r.watcher=new V({viewsDir:r.viewsDir,onSync:()=>this.runSync(e)}),r.watcher.start(),r.status="watching",this.appendLog(e,"\u25B6 resumed \u2014 watching for changes")):(r.watcher?.setShuttingDown(!0),await r.watcher?.stop(),r.watcher=void 0,r.progress=void 0,r.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 e.buildWatcher?.close(),await be(e.viewsDir)}async exit(){this.isShuttingDown||(this.isShuttingDown=!0,await this.cleanupAll(),this.emit("exit"))}async logout(){this.isShuttingDown||(this.isShuttingDown=!0,await de(),await this.cleanupAll(),this.emit("logout"))}};import{useEffect as Gr,useState as De}from"react";import{Box as qr,Text as Hr,useInput as Jr}from"ink";import{useState as Z}from"react";import{Box as tt,Text as H}from"ink";import xe from"ink-text-input";import{jsx as N,jsxs as J}from"react/jsx-runtime";function Se(t){let e=t;return e?.body||e?.message||String(t)}function ve({env:t,initialError:e,onAuthed:r}){let[n,i]=Z("email"),[o,l]=Z(""),[h,p]=Z(""),[x,w]=Z(e),[m,b]=Z(),v=async R=>{let E=R.trim();if(E){w(void 0),i("busy");try{let{otp:M}=await ge(t,E);if(l(E),M){let X=await Lt(t,E,M);await r(X,E);return}b(`OTP sent to ${E}`),i("otp")}catch(M){w(Se(M)),i("email")}}},O=async R=>{let E=R.trim();if(E){w(void 0),i("busy");try{let M=await Lt(t,o,E);await r(M,o)}catch(M){w(Se(M)),p(""),i("otp")}}};return J(tt,{flexDirection:"column",children:[J(H,{bold:!0,children:["\u25C6 SleekCMS \u2014 sign in",t!=="production"?` (${t})`:""]}),x?J(H,{color:"red",children:["\u2717 ",x]}):null,n==="email"&&J(tt,{marginTop:1,children:[N(H,{children:"Email: "}),N(xe,{value:o,onChange:l,onSubmit:v,placeholder:"you@example.com"})]}),n==="otp"&&J(tt,{flexDirection:"column",marginTop:1,children:[m?N(H,{color:"green",children:m}):null,J(tt,{children:[N(H,{children:"Enter OTP: "}),N(xe,{value:h,onChange:p,onSubmit:O})]})]}),n==="busy"&&N(tt,{marginTop:1,children:N(H,{children:"Working\u2026"})})]})}import{Fragment as Lr,useEffect as et,useMemo as Rr,useState as C}from"react";import{Box as g,Text as c,useInput as Ar,useStdout as Fr}from"ink";import Be from"ink-text-input";import $r from"node:os";function Ft(t){if(!t)return"\u2014";let e=$r.homedir();return t.startsWith(e)?"~"+t.slice(e.length):t}function jt(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 r=Math.floor(e/60);if(r<60)return`${r}m ago`;let n=Math.floor(r/60);return n<24?`${n}h ago`:`${Math.floor(n/24)}d ago`}function yt(t){return!t||t.total<=0?null:Math.round(t.done/t.total*100)}var ft=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"];function Te(t){return ft[(t%ft.length+ft.length)%ft.length]}function wt(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"}}}function ke(t){if(!t)return{dot:"\xB7",color:"gray",label:"\u2014"};switch(t.status){case"building":return{dot:"\u2234",color:"yellow",label:"building"};case"error":return{dot:"\u2717",color:"red",label:"failed"};case"success":return{dot:"\u2713",color:"green",label:t.mode==="live"?"live":"preview"}}}import{Fragment as rt,jsx as s,jsxs as u}from"react/jsx-runtime";var U=(t,e)=>e<=0?0:Math.max(0,Math.min(t,e-1)),jr=16,_r=5,Ir="greenBright",Ee="blueBright",Ce="magentaBright";function Pe({k:t,bg:e=Ir}){return s(c,{color:"black",backgroundColor:e,children:` ${t} `})}function j({k:t,label:e,width:r=16,bg:n}){return u(g,{width:r,children:[s(Pe,{k:t,bg:n}),s(c,{color:"gray",children:` ${e}`})]})}function B({k:t,label:e,bg:r}){return u(g,{marginBottom:1,children:[s(g,{width:9,children:s(Pe,{k:t,bg:r})}),s(c,{color:"gray",children:e})]})}function Nr({launchers:t}){return u(g,{flexDirection:"column",borderStyle:"round",borderColor:"green",paddingX:2,paddingY:1,width:66,children:[u(g,{justifyContent:"space-between",children:[s(c,{color:"green",bold:!0,children:"KEYBOARD SHORTCUTS"}),s(c,{color:"gray",children:"? or esc to close"})]}),u(g,{marginTop:1,children:[u(g,{flexDirection:"column",width:34,children:[s(c,{color:"cyan",bold:!0,children:"Navigate"}),s(B,{k:"\u2191\u2193",label:"move selection"}),s(B,{k:"tab",label:"focus logs \xB7 \u2191\u2193 scroll"}),s(B,{k:"esc",label:"clear filter / back"}),s(g,{marginTop:1,children:s(c,{color:"cyan",bold:!0,children:"Global"})}),s(B,{k:"/",label:"search sites"}),s(B,{k:"n",label:"new site"}),s(B,{k:"?",label:"toggle this help",bg:Ee}),s(B,{k:"q",label:"logout"}),s(B,{k:"ctrl+c",label:"quit"})]}),u(g,{flexDirection:"column",flexGrow:1,children:[s(c,{color:"cyan",bold:!0,children:"Site actions"}),s(B,{k:"s",label:"start sync"}),s(B,{k:"d",label:"disconnect"}),s(B,{k:"p",label:"pause / resume sync"}),s(B,{k:"b",label:"build preview"}),s(B,{k:"B",label:"deploy live"}),s(B,{k:"o",label:"open preview url"}),t.map((e,r)=>s(B,{k:String(r+1),label:`open in ${e.label}`,bg:Ce},e.key))]})]})]})}function Ur({percent:t,width:e=28}){let r=Math.round(Math.max(0,Math.min(100,t))/100*e);return u(c,{children:[s(c,{color:"green",children:"\u2588".repeat(r)}),s(c,{color:"gray",children:"\u2591".repeat(Math.max(0,e-r))})]})}function Wr({frame:t,width:e=24,seg:r=6}){let n=e+r,i=t%n-r,o="";for(let l=0;l<e;l++)o+=l>=i&&l<i+r?"\u25AE":"\u25AF";return s(c,{color:"yellow",children:o})}function Me({controller:t}){let{stdout:e}=Fr(),r=Rr(()=>ie(),[]),[,n]=C(0),[i,o]=C(()=>new Date),[l,h]=C(null),[p,x]=C(()=>t.synced.size>0||t.catalog.some(d=>d.sites.length>0)?"dashboard":t.catalog.length<=1?"newName":"newOrg"),[w,m]=C(0),[b,v]=C(""),[O,R]=C(!1),[E,M]=C(""),[X,nt]=C(!1),[Nt,Ae]=C(0),[Ut,Wt]=C(!1),[Fe,je]=C(!1),[it,st]=C("list"),[_e,K]=C(0),Gt=()=>n(a=>a+1);et(()=>(t.on("change",Gt),()=>{t.off("change",Gt)}),[t]),et(()=>{let a,d=()=>{o(new Date),a=setTimeout(d,6e4-Date.now()%6e4)};return a=setTimeout(d,6e4-Date.now()%6e4),()=>clearTimeout(a)},[]);let F=t.catalog.flatMap(a=>a.sites.map(d=>({org:a.org,site:d,entry:t.synced.get(d.id)}))).sort((a,d)=>(a.entry?0:1)-(d.entry?0:1)),_=E.trim().toLowerCase(),D=_?F.filter(a=>a.site.name.toLowerCase().includes(_)||(a.org.name||a.org.subdomain||"").toLowerCase().includes(_)):F,W=D.findIndex(a=>a.site.id===l);W<0&&(W=0);let T=D[W],y=T?.entry,ot=Math.max(3,(e?.rows??30)-26),I=y?.logs??[],xt=Math.max(0,I.length-ot),Ie=Math.min(_e,xt),at=I.length-Ie,qt=I.slice(Math.max(0,at-ot),at),Ne=T?.site.id;et(()=>K(0),[Ne]);let A=y?.lastBuild,St=A?.status==="building",vt=ke(A),ct=St;if(!ct){for(let a of t.synced.values())if(a.status==="syncing"){ct=!0;break}}et(()=>{if(!ct)return;let a=setInterval(()=>Ae(d=>d+1),120);return()=>clearInterval(a)},[ct]);let Ht=D.findIndex(a=>!a.entry),G=Math.max(_r,(e?.rows??30)-jr),z=D.length,lt=z>G,Ue=Math.floor(G/2),Y=lt?Math.max(0,Math.min(W-Ue,z-G)):0,Tt=Math.min(z,Y+G),We=D.slice(Y,Tt);et(()=>{p!=="dashboard"||O||F.length>0||(v(""),t.catalog.length<=1?x("newName"):(m(0),x("newOrg")))},[F.length,p,O,t.catalog.length]);let ut=a=>{if(D.length===0)return;let d=U(W+a,D.length);h(D[d].site.id)},Ge=()=>{if(T){if(T.entry){t.flash("Already syncing \u2014 press d to disconnect.");return}t.addSite(T.org,T.site)}},qe=()=>{T?.entry&&t.removeSite(T.site.id)},He=()=>{v(""),t.catalog.length<=1?x("newName"):(m(0),x("newOrg"))},Je=a=>{if(!y){t.flash("Press s to start syncing this site first.",!0);return}je(!0);let d=a.run(y.viewsDir);t.flash(d?`Opened ${a.label} at ${Ft(y.viewsDir)}`:`Couldn't open ${a.label}.`,!d)},Xe=()=>{if(!y){t.flash("Press s to start syncing this site first.",!0);return}t.togglePause(y.site.id)},Jt=a=>{if(!y){t.flash("Press s to start syncing this site first.",!0);return}t.buildSite(y.site.id,a),t.flash(a?"Deploying \u2014 building live\u2026":"Building preview\u2026")},Ke=()=>{let a=y?.lastBuild?.url;if(!a){t.flash("No build URL yet \u2014 press b to build.",!0);return}t.flash(ne(a)?`Opening ${a}`:"Couldn't open the browser.",!1)};Ar((a,d)=>{if(Ut){(a==="?"||d.escape)&&Wt(!1);return}if(p==="newOrg"){d.escape?x("dashboard"):d.upArrow?m(k=>U(k-1,t.catalog.length)):d.downArrow?m(k=>U(k+1,t.catalog.length)):d.return&&x("newName");return}if(X){d.escape?(nt(!1),M(""),h(null)):d.return?nt(!1):d.upArrow?ut(-1):d.downArrow&&ut(1);return}if(d.tab){it==="logs"?st("list"):y&&st("logs"),K(0);return}if(it==="logs"){if(d.escape){st("list"),K(0);return}if(d.upArrow)return void K(k=>Math.min(k+1,xt));if(d.downArrow)return void K(k=>Math.max(0,k-1))}if(d.escape){M(""),h(null);return}if(a==="?")return void Wt(!0);if(a==="/")return st("list"),void nt(!0);if(a==="n")return void He();if(a==="q")return void t.logout();if(d.upArrow)return ut(-1);if(d.downArrow)return ut(1);if(a==="s")return Ge();if(a==="d")return qe();if(a==="p")return Xe();if(a==="b")return Jt(!1);if(a==="B")return Jt(!0);if(a==="o")return Ke();if(a>="1"&&a<="9"){let k=r[parseInt(a,10)-1];if(k)return Je(k)}},{isActive:p!=="newName"});let ze=async a=>{let d=a.trim();if(!d){x("dashboard");return}let k=t.catalog[U(w,t.catalog.length)]?.org??t.catalog[0]?.org;R(!0),x("dashboard"),await t.createNewSite(d,k),R(!1)},Xt=u(g,{justifyContent:"space-between",paddingX:1,children:[u(g,{children:[s(c,{color:"green",bold:!0,children:"\u25C6 SleekCMS"}),s(c,{color:"gray",children:" \u2502 "}),s(c,{children:t.email??"signed in"})]}),u(g,{children:[u(c,{color:"gray",children:[_?`${D.length}/${F.length}`:F.length," sites \xB7 "]}),u(c,{color:"green",children:[t.watchingCount()," watching"]}),s(c,{color:"gray",children:` \xB7 ${i.toTimeString().slice(0,5)}`})]})]});return Ut?u(g,{flexDirection:"column",children:[Xt,s(g,{marginTop:1,justifyContent:"center",children:s(Nr,{launchers:r})})]}):u(g,{flexDirection:"column",children:[Xt,u(g,{children:[u(g,{flexDirection:"column",borderStyle:"round",borderColor:"green",width:38,paddingX:1,children:[u(g,{justifyContent:"space-between",children:[s(c,{color:"green",bold:!0,children:"SITE WORKSPACES"}),F.length>G&&!X&&!_?s(c,{color:"gray",children:"/ filter"}):null]}),X?u(g,{children:[s(c,{color:"cyan",children:"/ "}),s(Be,{value:E,onChange:M,onSubmit:()=>nt(!1),placeholder:"type to filter\u2026"})]}):_?s(c,{color:"gray",children:`/ ${E} \xB7 ${D.length} match${D.length===1?"":"es"}`}):null,s(g,{flexDirection:"column",marginTop:1,minHeight:lt?G+3:void 0,children:D.length===0?s(c,{color:"gray",children:_?`no matches for \u201C${E}\u201D`:"no sites \u2014 press n to create one"}):u(rt,{children:[lt?s(c,{color:"gray",children:Y>0?` \u2191 ${Y} more`:" "}):null,We.map((a,d)=>{let k=Y+d,Kt=a.entry?wt(a.entry.status):{dot:"\xB7",color:"gray",label:"idle"},zt=yt(a.entry?.progress),Ye=a.entry?a.entry.status==="syncing"&&zt!=null?`${zt}%`:a.entry.status==="paused"?"paused":"--":"",dt=k===W,Ve=k===Ht&&Ht>0;return u(Lr,{children:[Ve?s(c,{color:"gray",children:"\u2500".repeat(34)}):null,u(g,{justifyContent:"space-between",children:[u(g,{children:[s(c,{color:dt?"green":"gray",children:dt?"\u276F ":" "}),u(c,{color:a.entry?.status==="watching"||a.entry?.status==="syncing"?"green":Kt.color,children:[a.entry?.status==="syncing"?Te(Nt):a.entry?.status==="watching"?"\u25CF":Kt.dot," "]}),s(c,{color:dt?"white":a.entry?void 0:"gray",bold:dt,children:a.site.name})]}),s(c,{color:a.entry?.status==="syncing"?"green":"gray",children:Ye})]})]},a.site.id)}),lt?s(c,{color:"gray",children:Tt<z?` \u2193 ${z-Tt} more`:" "}):null]})}),s(g,{marginTop:1,children:s(c,{color:"gray",children:"+ new site \xB7 n"})})]}),u(g,{flexDirection:"column",flexGrow:1,marginLeft:1,children:[s(g,{flexDirection:"column",borderStyle:"round",borderColor:"gray",paddingX:1,children:T?u(rt,{children:[u(c,{children:[T.org.name||T.org.subdomain?u(c,{color:"gray",children:[T.org.name||T.org.subdomain," / "]}):null,s(c,{color:"cyan",bold:!0,children:T.site.name})]}),u(g,{marginTop:1,children:[s(c,{color:"gray",children:"status "}),y?s(c,{color:wt(y.status).color,children:`: ${wt(y.status).label}`}):s(c,{color:"gray",children:": idle \u2014 not syncing (press s to start)"})]}),u(g,{children:[s(c,{color:"gray",children:"path "}),s(c,{wrap:"truncate-end",children:y?.viewsDir?Ft(y.viewsDir):"\u2014"})]}),u(g,{children:[s(c,{color:"gray",children:"synced "}),s(c,{children:jt(y?.lastSyncedAt)}),s(c,{color:"gray",children:" progress "}),s(c,{color:y?.status==="syncing"?"green":"gray",children:y?.status==="syncing"?`${yt(y.progress)??0}%`:"\u2014"})]}),s(g,{marginTop:1,children:s(Ur,{percent:y?y.status==="syncing"?yt(y.progress)??0:y.status==="watching"?100:0:0})}),y?u(rt,{children:[u(g,{marginTop:1,children:[s(c,{color:"gray",children:"build "}),s(c,{color:vt.color,children:`${vt.dot} ${vt.label}`}),s(c,{color:"gray",children:" last "}),s(c,{children:St?"in progress":jt(A?.at)})]}),u(g,{children:[s(c,{color:"gray",children:"url "}),A?.url?s(c,{color:"cyan",wrap:"truncate-end",children:A.url}):s(c,{color:"gray",children:"\u2014"})]}),A?.status==="error"&&A.errors?.length?u(g,{children:[s(c,{color:"red",children:"\u258C "}),s(c,{color:"red",wrap:"truncate-end",children:`\u2717 ${A.errors[0]}`})]}):null,St?u(g,{children:[s(Wr,{frame:Nt}),s(c,{color:"gray",children:` ${A?.stage??"building"}`})]}):null]}):null]}):s(c,{color:"gray",children:"No site selected."})}),p==="dashboard"?u(g,{flexDirection:"column",borderStyle:"round",borderColor:it==="logs"?"green":"gray",paddingX:1,marginTop:1,height:ot+4,flexShrink:0,overflowY:"hidden",children:[u(g,{justifyContent:"space-between",children:[u(c,{children:[s(c,{color:"cyan",bold:!0,children:"LOGS \xB7 "}),s(c,{color:"white",children:T?.site.name??"\u2014"})]}),y&&I.length>0?it==="logs"?s(c,{color:"green",children:`\u2191\u2193 scroll \xB7 esc back${xt>0?` \xB7 ${at}/${I.length}`:""}`}):I.length>ot?s(c,{color:"gray",children:"tab to scroll"}):null:null]}),s(g,{flexDirection:"column",marginTop:1,children:!y||I.length===0?s(c,{color:"gray",children:"no activity yet"}):qt.map((a,d)=>u(c,{wrap:"truncate-end",children:[u(c,{color:"gray",children:[a.time," "]}),a.text]},at-qt.length+d))})]}):u(g,{flexDirection:"column",borderStyle:"round",borderColor:"blue",paddingX:1,marginTop:1,flexGrow:1,children:[p==="newOrg"&&u(rt,{children:[s(c,{color:"cyan",bold:!0,children:"CREATE SITE \u2014 PICK ORG"}),s(c,{color:"gray",children:"\u2191/\u2193 select \xB7 \u23CE next \xB7 esc cancel"}),s(g,{flexDirection:"column",marginTop:1,children:t.catalog.map((a,d)=>u(c,{color:d===U(w,t.catalog.length)?"blue":void 0,bold:d===U(w,t.catalog.length),children:[d===U(w,t.catalog.length)?"\u276F ":" ",a.org.name||a.org.subdomain]},a.org.id))})]}),p==="newName"&&u(rt,{children:[s(c,{color:"cyan",bold:!0,children:"CREATE SITE"}),u(g,{marginTop:1,children:[s(c,{children:"New site name: "}),s(Be,{value:b,onChange:v,onSubmit:ze})]}),s(c,{color:"gray",children:"\u23CE create \xB7 empty + \u23CE to cancel"})]})]})]})]}),t.message?s(g,{paddingX:1,children:u(c,{color:t.messageIsError?"red":"gray",wrap:"truncate-end",children:[t.messageIsError?"\u2717 ":"",t.message]})}):null,u(g,{flexDirection:"column",marginTop:1,children:[u(g,{paddingX:1,justifyContent:"space-between",children:[u(g,{flexWrap:"wrap",children:[s(j,{k:"\u2191\u2193",label:"select"}),s(j,{k:"/",label:"search"}),s(j,{k:"s",label:"start sync"}),s(j,{k:"d",label:"disconnect"}),F.length===0?s(j,{k:"n",label:"new site"}):null,s(j,{k:"ctrl+c",label:"quit",width:14})]}),s(j,{k:"?",label:"all keys",width:12,bg:Ee})]}),!Fe&&r.length>0?s(g,{paddingX:1,marginTop:1,flexWrap:"wrap",children:r.map((a,d)=>s(j,{k:String(d+1),label:a.label,bg:Ce},a.key))}):null]})]})}import{jsx as bt}from"react/jsx-runtime";function Oe(t){let e=t;return e?.body||e?.message||String(t)}function $e({controller:t,env:e,devSecret:r,savedToken:n,savedEmail:i}){let[o,l]=De(n?"loading":"auth"),[h,p]=De();Jr((w,m)=>{m.ctrl&&w==="c"&&t.exit()}),Gr(()=>{n&&t.init(e,n,i).then(()=>l("manage")).catch(w=>{w instanceof L&&w.status===401?p("Saved session expired \u2014 please sign in again."):p(Oe(w)),l("auth")})},[]);let x=async(w,m)=>{p(void 0),await ue({env:e,userToken:w,devSecret:r,email:m}),l("loading");try{await t.init(e,w,m),l("manage")}catch(b){p(Oe(b)),l("auth")}};return o==="auth"?bt(ve,{env:e,initialError:h,onAuthed:x}):o==="loading"?bt(qr,{children:bt(Hr,{children:"Loading your sites\u2026 "})}):bt(Me,{controller:t})}import{jsx as tn}from"react/jsx-runtime";var Yr=Kr(import.meta.url),_t=te();if(_t.version){let t=Yr("../package.json");console.log(t.version),process.exit(0)}var Re=It.dirname(Xr(import.meta.url)),Vr=Le(It.join(Re,"AGENT.md"),"utf-8"),Qr=Le(It.join(Re,"build.mjs"),"utf-8");async function Zr(){let t=new mt(Vr,Qr),e=await le(),r=(_t.env??e?.env??"production").toLowerCase(),n=_t.dev??process.env.SLEEKCMS_DEV_SECRET??e?.devSecret;ce(n),process.stdout.isTTY&&process.stdout.write("\x1B[2J\x1B[3J\x1B[H");let i=zr(tn($e,{controller:t,env:r,devSecret:n,savedToken:e?.userToken,savedEmail:e?.email}),{exitOnCtrlC:!1}),o=l=>{i.unmount(),console.log(l),process.exit(0)};t.once("exit",()=>o("\u2713 Stopped. Local site files were cleaned up.")),t.once("logout",()=>o("\u{1F44B} Logged out. Run `sleekcms` to sign in again.")),process.on("SIGINT",()=>{t.exit()}),process.on("SIGTERM",()=>{t.exit()}),await i.waitUntilExit()}Zr().catch(t=>{let e=t;console.error("\u274C",e.body||e.message),process.exit(1)});
|