@sleekcms/sync 2.8.0 → 2.9.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/README.md +19 -3
- package/dist/AGENT.md +16 -1
- package/dist/index.js +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -201,9 +201,11 @@ No manual deploy. No waiting. SleekCMS rebuilds on every sync and the preview re
|
|
|
201
201
|
│ ├── entries/<key>[].json # Collection entry content (array)
|
|
202
202
|
│ └── images.json # Reusable site-level images (handle → shortcut)
|
|
203
203
|
│
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
204
|
+
├── public/
|
|
205
|
+
│ ├── css/<name>.css # Stylesheets
|
|
206
|
+
│ └── js/<name>.js # Scripts
|
|
207
|
+
│
|
|
208
|
+
└── setup.json # Site setup: default page title, Tailwind on/off, sitemap on/off
|
|
207
209
|
```
|
|
208
210
|
|
|
209
211
|
### File naming
|
|
@@ -410,6 +412,20 @@ Any form with `data-sleekcms="<name>"` captures and stores submissions in the CM
|
|
|
410
412
|
|
|
411
413
|
Content lives in `src/content/` as plain JSON. Edit any content file and save — it syncs like any other file.
|
|
412
414
|
|
|
415
|
+
## Site setup (`src/setup.json`)
|
|
416
|
+
|
|
417
|
+
Site-wide decisions made once — the default page title, whether Tailwind is compiled, and whether a sitemap is published — live in `src/setup.json`, mirrored from **Settings → Website** in the CMS. The file is JSON with a comment above each key explaining it:
|
|
418
|
+
|
|
419
|
+
```json5
|
|
420
|
+
{
|
|
421
|
+
title: "Acme Dental", // default <title> for pages without their own; null = the site name
|
|
422
|
+
tw_enabled: true, // compile src/public/css/tailwind.css and inject it into every page
|
|
423
|
+
sitemap_enabled: false, // publish sitemap.xml + robots.txt on every build
|
|
424
|
+
}
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
Edit the values and save — it syncs like any other file. Keys you leave out keep their current value. Which pages are excluded from the sitemap is managed in the CMS UI, not in this file.
|
|
428
|
+
|
|
413
429
|
## Images
|
|
414
430
|
|
|
415
431
|
Images are part of content, not a separate asset pipeline. Drop a shortcut string into any `image` field and the sync engine resolves it to a full `{ url, alt }` object on save:
|
package/dist/AGENT.md
CHANGED
|
@@ -86,6 +86,8 @@ content/entries/<key>.json Content for a single entry (object)
|
|
|
86
86
|
content/entries/<key>+.json Collection entry — ONE file holding a JSON array of all items
|
|
87
87
|
content/images.json Site-level reusable images: handle → shortcut (§7)
|
|
88
88
|
content/videos.json Site-level videos: handle → pexels:/pixabay: ref (user uploads show as embed_url) (§7)
|
|
89
|
+
|
|
90
|
+
setup.json Site setup (JSON, comments allowed): default page title, Tailwind on/off, sitemap on/off — edit values in place, never delete
|
|
89
91
|
```
|
|
90
92
|
|
|
91
93
|
**Collection storage differs by kind — do not confuse them:**
|
|
@@ -97,6 +99,16 @@ content/videos.json Site-level videos: handle → pexels:/pixabay: r
|
|
|
97
99
|
- **Public URL** (what `link()`/`script()` and HTML `href`/`src` take): `/css/styles.css`, `/js/app.js`.
|
|
98
100
|
- **Page route** (what visitors see): `/about`, `/blog/<slug>`. Computed from the page key.
|
|
99
101
|
|
|
102
|
+
**Site setup (`setup.json`)** — the set-once site decisions, mirrored from Settings > Website in the CMS. Each key is explained by the comment above it in the file:
|
|
103
|
+
|
|
104
|
+
| Key | Value | Effect |
|
|
105
|
+
|---|---|---|
|
|
106
|
+
| `title` | string or `null` | Default `<title>` for pages that don't set their own (via `title()` or the SEO block). `null` = the site name. |
|
|
107
|
+
| `tw_enabled` | boolean | `true` compiles `css/tailwind.css` and injects it into every page — required for any site styled with Tailwind utilities. |
|
|
108
|
+
| `sitemap_enabled` | boolean | `true` publishes `sitemap.xml` + `robots.txt` on every build. Turn it on for public sites and SEO work. |
|
|
109
|
+
|
|
110
|
+
Edit values in place and keep the keys. Keys you omit keep their stored values; unknown keys are ignored. Which pages are excluded from the sitemap is a CMS UI setting, not a key in this file. Never delete the file.
|
|
111
|
+
|
|
100
112
|
## 5. Common Tasks
|
|
101
113
|
|
|
102
114
|
### Create a single page (e.g. `/about`)
|
|
@@ -426,9 +438,11 @@ Make every site strongly SEO- and sharing-friendly. The standard pattern is a re
|
|
|
426
438
|
|
|
427
439
|
Add `seo: block(seo)` to page models and render it **once from the layout head**: `<% if (item.seo) { %><%- render(item.seo) %><% } %>` in `layouts/common.ejs` (as in the layout example above) — not per page view.
|
|
428
440
|
|
|
441
|
+
Two site-level SEO switches live in `setup.json` (§4), not in views: `sitemap_enabled: true` publishes `sitemap.xml` + `robots.txt` on every build, and `title` is the site's default `<title>` for pages whose SEO block leaves the title empty (`null` = the site name). Sitemap page exclusions are chosen in the CMS UI — there is no file setting for them.
|
|
442
|
+
|
|
429
443
|
## 10. Forms
|
|
430
444
|
|
|
431
|
-
Any `<form>` with `data-sleekcms="<name>"` works automatically — submissions are captured and shown in the CMS dashboard. No backend, no action URL, no JS.
|
|
445
|
+
Any `<form>` with `data-sleekcms="<hardcoded name>"` works automatically — submissions are captured and shown in the CMS dashboard. No backend, no action URL, no JS.
|
|
432
446
|
|
|
433
447
|
```html
|
|
434
448
|
<form data-sleekcms="contact">
|
|
@@ -545,3 +559,4 @@ Snipcart powers stores. Use it when the business **sells products** and the user
|
|
|
545
559
|
12. Image shortcuts (`"iconify:..."`, `"pexels:..."`, etc.) resolve **only** in `image`/`icon`-typed fields. CMS-editable icons are declared `icon: icon`, never `icon: text` — render with `svg(item.icon)`; fixed UI icons need no field: `svg('iconify:...')` directly in the view.
|
|
546
560
|
13. Use entries (`entry(key+)`) for shared content (authors, testimonials, nav, footer) — not blocks. Block models cannot contain `block(...)` or `stack` fields; use blocks only when a group of fields also benefits from a reusable view.
|
|
547
561
|
14. Always create an RSS feed for blogs (`rss.xml` key) and make every site strongly SEO- and sharing-friendly (reusable `seo` block rendered once from the layout head).
|
|
562
|
+
15. Site-wide setup — default page title, Tailwind on/off, sitemap on/off — lives in `setup.json` (§4). Change values there rather than hard-coding a site title into the layout, and never delete the file. Sitemap page exclusions are UI-only.
|
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 zn}from"node:url";import{createRequire as Yn}from"node:module";import It from"node:path";import{render as Vn}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
|
|
7
|
-
`)){let o=
|
|
6
|
+
`).parse(process.argv),Yt.opts()}function D(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 n={detached:!0,stdio:"ignore",windowsHide:!0};process.platform==="win32"?P("cmd.exe",["/c",t,...e],n).unref():P(t,e,n).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 r=(e?Vt.filter(s=>s.cmd===e):Vt).find(s=>D(s.cmd));return r?(Qe(r.cmd,r.args(t)),r.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(D("wt"))P("wt.exe",["-d",t],{detached:!0,stdio:"ignore"}).unref();else{let e=D("powershell")?"powershell.exe":"cmd.exe";P("cmd.exe",["/c","start","",e],{cwd:t,detached:!0,stdio:"ignore"}).unref()}return!0}return!1}function tn(t){return ee("iTerm")?(P("open",["-a","iTerm",t],{detached:!0,stdio:"ignore"}).unref(),!0):!1}function en(t){return`'${t.replace(/'/g,"'\\''")}'`}var nn=[{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 ne(t,e){if(!D(e))return!1;if(process.platform==="darwin"){let r=`tell application "Terminal" to do script ("cd " & quoted form of "${t}" & " && ${e}")`;return P("osascript",["-e",r,"-e",'tell application "Terminal" to activate'],{detached:!0,stdio:"ignore"}).unref(),!0}if(process.platform==="win32")return D("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 n=`cd ${en(t)} && ${e}; exec bash`;for(let r of nn)if(D(r.cmd))try{return P(r.cmd,r.args(n),{cwd:t,detached:!0,stdio:"ignore"}).unref(),!0}catch{}return!1}function rn(t){return ne(t,"claude")}function sn(t){return ne(t,"pi")}function re(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 se(){let t=process.platform==="darwin",e=process.platform==="win32",n=[];return(t||e)&&n.push({key:"terminal",label:"terminal",run:Ze}),ee("iTerm")&&n.push({key:"iterm",label:"iterm",run:tn}),D("code")&&n.push({key:"code",label:"vscode",run:r=>Qt(r,"code")!==null}),D("cursor")&&n.push({key:"cursor",label:"cursor",run:r=>Qt(r,"cursor")!==null}),D("claude")&&n.push({key:"claude",label:"claude",run:rn}),D("pi")&&n.push({key:"pi",label:"pi",run:sn}),n}import{EventEmitter as Mn}from"node:events";import{spawn as On}from"node:child_process";import Q from"fs-extra";import At from"path";import Dn from"chokidar";import m from"fs-extra";import an from"os";import S from"path";var ie={localhost:"http://app.sleekcms.test/api/mcp",development:"https://app.sleekcms.dev/api/mcp",production:"https://app.sleekcms.com/api/mcp"},cn=["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,n,r){super(e),this.status=n,this.body=r}};async function Ct(t,e,n,r,s){let o=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(!o.ok){let c=await o.text();throw new Bt(`${n} ${r} \u2192 ${o.status}: ${c}`,o.status,c)}return o.json()}async function ln(t,e){e&&(await m.outputFile(S.join(t,"AGENT.md"),e),await m.outputFile(S.join(t,"CLAUDE.md"),e),await m.outputFile(S.join(t,".vscode","copilot-instructions.md"),e)),await m.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 un(t,e){if(!e)return;let n=S.join(t,"build.mjs"),r=e.match(/build\.mjs v\d+/)?.[0];if(await m.pathExists(n)){let s=(await m.readFile(n,"utf-8")).match(/build\.mjs v\d+/)?.[0];if(s&&s===r)return}await m.outputFile(n,e)}async function dn(t,e,n){let r=S.join(t,".cache","token");if(await m.pathExists(r)){if((await m.readFile(r,"utf-8")).trim()===e)return;n?.("\u267B\uFE0F Workspace was tied to a different site \u2014 recreating it."),await m.emptyDir(t)}await m.outputFile(r,e)}function gn(t,e){let n=`${e.name.substr(0,20)} ${e.id}`.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,""),r=t||S.join(an.homedir(),"SleekCMS");return S.resolve(r,n)}async function Pt(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(),o=ie[s]||ie.production,c=await Ct(o,e,"GET","/get_site"),h=t.viewsDir?S.resolve(t.viewsDir):gn(t.path,c);await m.ensureDir(h),await dn(h,e,n),await Promise.all(cn.map(v=>m.ensureDir(S.join(h,v)))),await un(h,t.buildScript);let p=S.join(h,".cache","state.json");t.flush&&await m.remove(p);let x=!await m.pathExists(p),w=x?{}:(await m.readJson(p)).fileMap||{},f=0,b=0;return x?({fileMap:w,pulled:b}=await Sn(h,o,e,n,r),await ln(h,t.agentMd)):f=await fn(h,w,o,e,n,r),await m.outputJson(p,{fileMap:w},{spaces:2}),{viewsDir:h,site:c,isFirstRun:x,pushed:f,pulled:b}}var hn=new Set([".DS_Store","Thumbs.db","desktop.ini"]);async function pn(t){let e=S.join(t,"src");if(!await m.pathExists(e))return[];let n=[];async function r(s){let o=await m.readdir(s,{withFileTypes:!0});for(let c of o){if(hn.has(c.name))continue;let h=S.join(s,c.name);c.isDirectory()?await r(h):c.isFile()&&n.push(S.relative(t,h).replace(/\\/g,"/"))}}return await r(e),n}async function fn(t,e,n,r,s,o){let c=[];for(let f of await pn(t)){let b=S.join(t,f),v=e[f],O=await m.stat(b);if(v&&v.mtimeMs===O.mtimeMs)continue;let R=await m.readFile(b,"utf-8");R.trim()&&c.push({rel:f,full:b,stat:O,content:R,prior:v})}if(c.length===0)return 0;let h;try{h=await Ct(n,r,"POST","/save_files",c.map(f=>({path:f.rel,content:f.content})))}catch(f){let b=f;return s(`\u274C Error saving files: ${b.body||b.message}`),0}let p=await wn(t),x=0,w=mn(h);for(let f=0;f<c.length;f++){let b=c[f],v=yn(b,f,h,w);if(o?.(f+1,c.length),!v){let O="No save response returned";p[b.rel]=O,s(`\u274C Error saving ${b.rel}: ${O}`);continue}if(v.error){p[b.rel]=v.error,s(`\u274C Error saving ${b.rel}: ${v.error}`);continue}delete p[b.rel],e[b.rel]={mtimeMs:b.stat.mtimeMs},s(`\u2705 ${b.prior?"Updated":"Created"} ${b.rel}`),x++}return await xn(t,p),x}function mn(t){let e=new Map;for(let n of t)n.path&&e.set(n.path,n);return e}function yn(t,e,n,r){let s=r.get(t.rel);if(s)return s;let o=n[e];if(!o?.path||o.path===t.rel)return o}var $t="sync-errors.log",Et=new Map;async function wn(t){let e=S.resolve(t),n=Et.get(e);if(n)return n;let r=await bn(e);return Et.set(e,r),r}async function bn(t){let e=S.join(t,$t);if(!await m.pathExists(e))return{};let n=await m.readFile(e,"utf-8"),r={};for(let s of n.split(`
|
|
7
|
+
`)){let o=s.indexOf(": ");o>0&&(r[s.slice(0,o)]=s.slice(o+2))}return r}async function xn(t,e){let n=S.join(t,$t),r=Object.entries(e);if(r.length===0){await m.remove(n);return}await m.outputFile(n,r.map(([s,o])=>`${s}: ${o}`).join(`
|
|
8
8
|
`)+`
|
|
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)});
|
|
9
|
+
`)}async function Sn(t,e,n,r,s){Et.delete(S.resolve(t)),await m.remove(S.join(t,$t)),r("\u{1F4E5} Fetching files...");let o=await Ct(e,n,"GET","/get_files"),c={},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++,s?.(h,o.length),c[p.path]={mtimeMs:w}}return r(`\u2714\uFE0F Synced ${o.length} file(s).`),{fileMap:c,pulled:h}}import vn from"path";import Tn from"chokidar";var kn=5e3,Bn=/(^|[/\\])(\.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??kn}start(){if(this.watcher)return;let e=vn.join(this.viewsDir,"src");this.watcher=Tn.watch([e],{persistent:!0,ignoreInitial:!0,ignored:Bn}).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 gt from"fs-extra";import En 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 Cn=ae.join(En.homedir(),"SleekCMS"),ht=ae.join(Cn,"auth.json"),Mt;function ce(t){Mt=t||void 0}function Dt(t){let e=[];return t&&e.push(`_auth=${t}`),Mt&&e.push(`_dev=${Mt}`),e.length?e.join("; "):void 0}var L=class extends Error{status;body;constructor(e,n,r=""){super(e),this.name="UserAuthError",this.status=n,this.body=r}};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={},n=Dt(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(Ot(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 L(`${t.method} ${t.path} \u2192 ${r.status}: ${s}`,r.status,s)}return r.json()}async function ge(t,e){let n={"Content-Type":"application/json"},r=Dt();r&&(n.Cookie=r);let s=await fetch(Ot(t)+"/auth/otp",{method:"PUT",headers:n,body:JSON.stringify({email:e})});if(!s.ok){let c=await s.text().catch(()=>"");throw new L(`Failed to send OTP \u2192 ${s.status}: ${c}`,s.status,c)}return{otp:(await s.json().catch(()=>({}))).do_not_use_in_code}}async function Lt(t,e,n){let r={"Content-Type":"application/json"},s=Dt();s&&(r.Cookie=s);let o=await fetch(Ot(t)+"/auth/login",{method:"POST",headers:r,body:JSON.stringify({email:e,otp:n})});if(!o.ok){let p=await o.text().catch(()=>"");throw new L(`Login failed \u2192 ${o.status}: ${p}`,o.status,p)}let c=o.headers.getSetCookie?.()??[],h=Pn(c);if(!h)throw new L("Login succeeded but no _auth cookie was returned.",500);return h}function Pn(t){for(let e of t){let n=e.match(/(?:^|;\s*)_auth=([^;]+)/);if(n&&n[1])return decodeURIComponent(n[1])}return null}async function Rt(t,e){return q({env:t,userToken:e,method:"GET",path:"/org"})}async function he(t,e,n){return q({env:t,userToken:e,method:"GET",path:"/site",subdomain:n})}function $n(t){if(t==null)return"(unknown error)";if(typeof t=="string")return t;if(typeof t!="object")return String(t);let e=t,n=typeof e.file_path=="string"?e.file_path:"",r=typeof e.path=="string"?e.path:typeof e.filename=="string"?e.filename:"",s=n||r,o=e.line!=null?`:${e.line}`:"",c=e.column!=null?`:${e.column}`:"",h=typeof e.message=="string"&&e.message?e.message:typeof e.name=="string"&&e.name?e.name:"",p=n&&r&&r!==n?` (on page ${r})`:"";if(s)return`${s}${o}${c}${p}: ${h||"(no message)"}`;if(h)return h;try{return JSON.stringify(t)}catch{return"(unknown error)"}}async function pe(t,e,n,r){let s;try{s=await q({env:t,userToken:e,method:"GET",path:"/site_build",subdomain:n,slug:r})}catch{return null}if(!Array.isArray(s)||s.length===0)return null;let o=s[0],c=s.find(p=>typeof p.url=="string"&&p.url.length>0),h=Array.isArray(o.errors)?o.errors.map($n):[];return h.length>0?{status:"error",mode:"preview",errors:h,url:c?.url||void 0,at:o.updated_at?Date.parse(o.updated_at):void 0}:c?{status:"success",mode:"preview",url:c.url||void 0,at:c.updated_at?Date.parse(c.updated_at):void 0}:null}async function fe(t,e,n,r){let{token:s}=await q({env:t,userToken:e,method:"POST",path:"/site_user/api_token",subdomain:n,slug:r,body:{refresh:!1}});return s}async function me(t,e,n){let r=await q({env:t,userToken:e,method:"POST",path:"/org",body:n?{name:n}:{}});return{id:r.id,name:r.name,subdomain:r.subdomain}}async function ye(t,e,n,r){let s=await q({env:t,userToken:e,method:"POST",path:"/site",subdomain:n,body:{name:r}});return{id:s.id,name:s.name,slug:s.slug}}var we=500;function Ln(){return new Date().toTimeString().slice(0,8)}function pt(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 be(t){t&&(await Q.remove(At.join(t,"src")),await Q.remove(At.join(t,".cache","state.json")))}var ft=class extends Mn{env="production";userToken="";email;catalog=[];synced=new Map;message;messageIsError=!1;agentMd;buildScript;isShuttingDown=!1;constructor(e,n){super(),this.agentMd=e,this.buildScript=n}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:Ln(),text:n}),r.logs.length>we&&r.logs.splice(0,r.logs.length-we),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 Rt(e,n);await this.buildCatalog(s)}async buildCatalog(e){let n=[];for(let r of e)try{n.push({org:r,sites:await he(this.env,this.userToken,r.subdomain)})}catch(s){if(s instanceof L&&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 fe(this.env,this.userToken,e.subdomain,n.slug)}catch(o){this.flash(`Failed to authorize "${n.name}": ${pt(o)}`,!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 o=await Pt({token:r,env:this.env,agentMd:this.agentMd,buildScript:this.buildScript,log:c=>this.appendLog(n.id,c),onProgress:(c,h)=>{s.progress={done:c,total:h},this.changed()}});s.viewsDir=o.viewsDir,s.progress=void 0,s.status="watching",s.lastSyncedAt=Date.now(),s.lastMsg=`pulled ${o.pulled} file(s)`,s.watcher=new V({viewsDir:o.viewsDir,onSync:()=>this.runSync(n.id)}),s.watcher.start(),this.watchBuildStatus(n.id),this.seedLastBuild(n.id),this.appendLog(n.id,"\u{1F440} watching for changes")}catch(o){let c=o;s.status="error",s.progress=void 0,s.lastMsg=c.body||c.message,this.appendLog(n.id,`\u274C ${c.body||c.message}`)}this.changed()}async removeSite(e){let n=this.synced.get(e);n&&(n.watcher?.setShuttingDown(!0),await n.watcher?.stop(),await n.buildWatcher?.close(),await be(n.viewsDir),this.synced.delete(e),this.changed())}buildSite(e,n=!1){let r=this.synced.get(e);if(!r||!r.viewsDir)return;let s=n?"live":"preview";this.writeBuildFile(r.viewsDir,{status:"building",mode:s,stage:"queued",url:r.lastBuild?.url,at:Date.now()}),On("node",n?["build.mjs","--deploy"]:["build.mjs"],{cwd:r.viewsDir,stdio:"ignore"}).on("error",h=>{this.writeBuildFile(r.viewsDir,{status:"error",mode:s,errors:[h.message],url:r.lastBuild?.url,at:Date.now()}),this.appendLog(e,`\u274C build: ${h.message}`)}),this.appendLog(e,n?"\u{1F680} deploying\u2026":"\u{1F528} building\u2026")}buildFile(e){return At.join(e,".cache","build.json")}async readBuildFile(e){try{let n=await Q.readJson(this.buildFile(e));return n&&n.status?n:null}catch{return null}}async writeBuildFile(e,n){let r=this.buildFile(e),s=`${r}.tmp`;try{await Q.outputJson(s,n),await Q.rename(s,r)}catch{}}watchBuildStatus(e){let n=this.synced.get(e);!n||!n.viewsDir||(n.buildWatcher=Dn.watch(this.buildFile(n.viewsDir),{ignoreInitial:!1}).on("add",()=>{this.readBuildStatus(e)}).on("change",()=>{this.readBuildStatus(e)}))}async readBuildStatus(e){let n=this.synced.get(e);if(!n)return;let r=await this.readBuildFile(n.viewsDir);if(!r)return;let s=n.lastBuild?.url;n.lastBuild={status:r.status,mode:r.mode??"preview",url:r.url??(r.status==="success"?void 0:s),errors:r.errors,stage:r.stage,at:r.at},this.changed()}async seedLastBuild(e){let n=this.synced.get(e);if(!n)return;let r=await pe(this.env,this.userToken,n.org.subdomain,n.site.slug);if(!r)return;let s=this.synced.get(e);if(!s)return;let o=await this.readBuildFile(s.viewsDir);o?.at!=null&&(r.at==null||o.at>=r.at)||await this.writeBuildFile(s.viewsDir,r)}async ensureOrg(e){if(e)return e;if(this.catalog[0])return this.catalog[0].org;let n;try{n=await Rt(this.env,this.userToken)}catch(r){return this.flash(`Couldn't check your organizations: ${pt(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 me(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: ${pt(r)}`,!0),null}}async createNewSite(e,n){let r=await this.ensureOrg(n);if(!r)return;let s;try{s=await ye(this.env,this.userToken,r.subdomain,e)}catch(c){this.flash(`Could not create "${e}": ${pt(c)}`,!0);return}let o=this.catalog.find(c=>c.org.id===r.id);o&&!o.sites.some(c=>c.id===s.id)&&(o.sites=[...o.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 Pt({token:r.siteToken,env:this.env,viewsDir:r.viewsDir,agentMd:this.agentMd,buildScript:this.buildScript,flush:n.flush,log:o=>this.appendLog(e,o),onProgress:(o,c)=>{r.progress={done:o,total:c},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 o=s;r.status="error",r.progress=void 0,r.lastMsg=o.body||o.message,this.appendLog(e,`\u274C ${o.body||o.message}`)}this.changed()}}async togglePause(e){let n=this.synced.get(e);!n||!n.viewsDir||(n.status==="paused"?(n.watcher=new V({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 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 Hn,useState as Me}from"react";import{Box as Jn,Text as Xn,useInput as Kn}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:n}){let[r,s]=Z("email"),[o,c]=Z(""),[h,p]=Z(""),[x,w]=Z(e),[f,b]=Z(),v=async R=>{let E=R.trim();if(E){w(void 0),s("busy");try{let{otp:$}=await ge(t,E);if(c(E),$){let X=await Lt(t,E,$);await n(X,E);return}b(`OTP sent to ${E}`),s("otp")}catch($){w(Se($)),s("email")}}},O=async R=>{let E=R.trim();if(E){w(void 0),s("busy");try{let $=await Lt(t,o,E);await n($,o)}catch($){w(Se($)),p(""),s("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,r==="email"&&J(tt,{marginTop:1,children:[N(H,{children:"Email: "}),N(xe,{value:o,onChange:c,onSubmit:v,placeholder:"you@example.com"})]}),r==="otp"&&J(tt,{flexDirection:"column",marginTop:1,children:[f?N(H,{color:"green",children:f}):null,J(tt,{children:[N(H,{children:"Enter OTP: "}),N(xe,{value:h,onChange:p,onSubmit:O})]})]}),r==="busy"&&N(tt,{marginTop:1,children:N(H,{children:"Working\u2026"})})]})}import{Fragment as An,useEffect as et,useMemo as Fn,useState as C}from"react";import{Box as g,Text as l,useInput as jn,useStdout as _n}from"ink";import Be from"ink-text-input";import Rn from"node:os";function Ft(t){if(!t)return"\u2014";let e=Rn.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 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 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 nt,jsx as i,jsxs as u}from"react/jsx-runtime";var U=(t,e)=>e<=0?0:Math.max(0,Math.min(t,e-1)),In=16,Nn=5,Un="greenBright",Ee="blueBright",Ce="magentaBright";function Pe({k:t,bg:e=Un}){return i(l,{color:"black",backgroundColor:e,children:` ${t} `})}function j({k:t,label:e,width:n=16,bg:r}){return u(g,{width:n,children:[i(Pe,{k:t,bg:r}),i(l,{color:"gray",children:` ${e}`})]})}function B({k:t,label:e,bg:n}){return u(g,{marginBottom:1,children:[i(g,{width:9,children:i(Pe,{k:t,bg:n})}),i(l,{color:"gray",children:e})]})}function Wn({launchers:t}){return u(g,{flexDirection:"column",borderStyle:"round",borderColor:"green",paddingX:2,paddingY:1,width:66,children:[u(g,{justifyContent:"space-between",children:[i(l,{color:"green",bold:!0,children:"KEYBOARD SHORTCUTS"}),i(l,{color:"gray",children:"? or esc to close"})]}),u(g,{marginTop:1,children:[u(g,{flexDirection:"column",width:34,children:[i(l,{color:"cyan",bold:!0,children:"Navigate"}),i(B,{k:"\u2191\u2193",label:"move selection"}),i(B,{k:"tab",label:"focus logs \xB7 \u2191\u2193 scroll"}),i(B,{k:"esc",label:"clear filter / back"}),i(g,{marginTop:1,children:i(l,{color:"cyan",bold:!0,children:"Global"})}),i(B,{k:"/",label:"search sites"}),i(B,{k:"n",label:"new site"}),i(B,{k:"?",label:"toggle this help",bg:Ee}),i(B,{k:"q",label:"logout"}),i(B,{k:"ctrl+c",label:"quit"})]}),u(g,{flexDirection:"column",flexGrow:1,children:[i(l,{color:"cyan",bold:!0,children:"Site actions"}),i(B,{k:"s",label:"start sync"}),i(B,{k:"d",label:"disconnect"}),i(B,{k:"p",label:"pause / resume sync"}),i(B,{k:"b",label:"build preview"}),i(B,{k:"B",label:"deploy live"}),i(B,{k:"o",label:"open preview url"}),t.map((e,n)=>i(B,{k:String(n+1),label:`open in ${e.label}`,bg:Ce},e.key))]})]})]})}function Gn({percent:t,width:e=28}){let n=Math.round(Math.max(0,Math.min(100,t))/100*e);return u(l,{children:[i(l,{color:"green",children:"\u2588".repeat(n)}),i(l,{color:"gray",children:"\u2591".repeat(Math.max(0,e-n))})]})}function qn({frame:t,width:e=24,seg:n=6}){let r=e+n,s=t%r-n,o="";for(let c=0;c<e;c++)o+=c>=s&&c<s+n?"\u25AE":"\u25AF";return i(l,{color:"yellow",children:o})}function $e({controller:t}){let{stdout:e}=_n(),n=Fn(()=>se(),[]),[,r]=C(0),[s,o]=C(()=>new Date),[c,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,f]=C(0),[b,v]=C(""),[O,R]=C(!1),[E,$]=C(""),[X,rt]=C(!1),[Nt,Ae]=C(0),[Ut,Wt]=C(!1),[Fe,je]=C(!1),[st,it]=C("list"),[_e,K]=C(0),Gt=()=>r(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(),M=_?F.filter(a=>a.site.name.toLowerCase().includes(_)||(a.org.name||a.org.subdomain||"").toLowerCase().includes(_)):F,W=M.findIndex(a=>a.site.id===c);W<0&&(W=0);let T=M[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=M.findIndex(a=>!a.entry),G=Math.max(Nn,(e?.rows??30)-In),z=M.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=M.slice(Y,Tt);et(()=>{p!=="dashboard"||O||F.length>0||(v(""),t.catalog.length<=1?x("newName"):(f(0),x("newOrg")))},[F.length,p,O,t.catalog.length]);let ut=a=>{if(M.length===0)return;let d=U(W+a,M.length);h(M[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"):(f(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(re(a)?`Opening ${a}`:"Couldn't open the browser.",!1)};jn((a,d)=>{if(Ut){(a==="?"||d.escape)&&Wt(!1);return}if(p==="newOrg"){d.escape?x("dashboard"):d.upArrow?f(k=>U(k-1,t.catalog.length)):d.downArrow?f(k=>U(k+1,t.catalog.length)):d.return&&x("newName");return}if(X){d.escape?(rt(!1),$(""),h(null)):d.return?rt(!1):d.upArrow?ut(-1):d.downArrow&&ut(1);return}if(d.tab){st==="logs"?it("list"):y&&it("logs"),K(0);return}if(st==="logs"){if(d.escape){it("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){$(""),h(null);return}if(a==="?")return void Wt(!0);if(a==="/")return it("list"),void rt(!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=n[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:[i(l,{color:"green",bold:!0,children:"\u25C6 SleekCMS"}),i(l,{color:"gray",children:" \u2502 "}),i(l,{children:t.email??"signed in"})]}),u(g,{children:[u(l,{color:"gray",children:[_?`${M.length}/${F.length}`:F.length," sites \xB7 "]}),u(l,{color:"green",children:[t.watchingCount()," watching"]}),i(l,{color:"gray",children:` \xB7 ${s.toTimeString().slice(0,5)}`})]})]});return Ut?u(g,{flexDirection:"column",children:[Xt,i(g,{marginTop:1,justifyContent:"center",children:i(Wn,{launchers:n})})]}):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:[i(l,{color:"green",bold:!0,children:"SITE WORKSPACES"}),F.length>G&&!X&&!_?i(l,{color:"gray",children:"/ filter"}):null]}),X?u(g,{children:[i(l,{color:"cyan",children:"/ "}),i(Be,{value:E,onChange:$,onSubmit:()=>rt(!1),placeholder:"type to filter\u2026"})]}):_?i(l,{color:"gray",children:`/ ${E} \xB7 ${M.length} match${M.length===1?"":"es"}`}):null,i(g,{flexDirection:"column",marginTop:1,minHeight:lt?G+3:void 0,children:M.length===0?i(l,{color:"gray",children:_?`no matches for \u201C${E}\u201D`:"no sites \u2014 press n to create one"}):u(nt,{children:[lt?i(l,{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(An,{children:[Ve?i(l,{color:"gray",children:"\u2500".repeat(34)}):null,u(g,{justifyContent:"space-between",children:[u(g,{children:[i(l,{color:dt?"green":"gray",children:dt?"\u276F ":" "}),u(l,{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," "]}),i(l,{color:dt?"white":a.entry?void 0:"gray",bold:dt,children:a.site.name})]}),i(l,{color:a.entry?.status==="syncing"?"green":"gray",children:Ye})]})]},a.site.id)}),lt?i(l,{color:"gray",children:Tt<z?` \u2193 ${z-Tt} more`:" "}):null]})}),i(g,{marginTop:1,children:i(l,{color:"gray",children:"+ new site \xB7 n"})})]}),u(g,{flexDirection:"column",flexGrow:1,marginLeft:1,children:[i(g,{flexDirection:"column",borderStyle:"round",borderColor:"gray",paddingX:1,children:T?u(nt,{children:[u(l,{children:[T.org.name||T.org.subdomain?u(l,{color:"gray",children:[T.org.name||T.org.subdomain," / "]}):null,i(l,{color:"cyan",bold:!0,children:T.site.name})]}),u(g,{marginTop:1,children:[i(l,{color:"gray",children:"status "}),y?i(l,{color:wt(y.status).color,children:`: ${wt(y.status).label}`}):i(l,{color:"gray",children:": idle \u2014 not syncing (press s to start)"})]}),u(g,{children:[i(l,{color:"gray",children:"path "}),i(l,{wrap:"truncate-end",children:y?.viewsDir?Ft(y.viewsDir):"\u2014"})]}),u(g,{children:[i(l,{color:"gray",children:"synced "}),i(l,{children:jt(y?.lastSyncedAt)}),i(l,{color:"gray",children:" progress "}),i(l,{color:y?.status==="syncing"?"green":"gray",children:y?.status==="syncing"?`${yt(y.progress)??0}%`:"\u2014"})]}),i(g,{marginTop:1,children:i(Gn,{percent:y?y.status==="syncing"?yt(y.progress)??0:y.status==="watching"?100:0:0})}),y?u(nt,{children:[u(g,{marginTop:1,children:[i(l,{color:"gray",children:"build "}),i(l,{color:vt.color,children:`${vt.dot} ${vt.label}`}),i(l,{color:"gray",children:" last "}),i(l,{children:St?"in progress":jt(A?.at)})]}),u(g,{children:[i(l,{color:"gray",children:"url "}),A?.url?i(l,{color:"cyan",wrap:"truncate-end",children:A.url}):i(l,{color:"gray",children:"\u2014"})]}),A?.status==="error"&&A.errors?.length?u(g,{children:[i(l,{color:"red",children:"\u258C "}),i(l,{color:"red",wrap:"truncate-end",children:`\u2717 ${A.errors[0]}`})]}):null,St?u(g,{children:[i(qn,{frame:Nt}),i(l,{color:"gray",children:` ${A?.stage??"building"}`})]}):null]}):null]}):i(l,{color:"gray",children:"No site selected."})}),p==="dashboard"?u(g,{flexDirection:"column",borderStyle:"round",borderColor:st==="logs"?"green":"gray",paddingX:1,marginTop:1,height:ot+4,flexShrink:0,overflowY:"hidden",children:[u(g,{justifyContent:"space-between",children:[u(l,{children:[i(l,{color:"cyan",bold:!0,children:"LOGS \xB7 "}),i(l,{color:"white",children:T?.site.name??"\u2014"})]}),y&&I.length>0?st==="logs"?i(l,{color:"green",children:`\u2191\u2193 scroll \xB7 esc back${xt>0?` \xB7 ${at}/${I.length}`:""}`}):I.length>ot?i(l,{color:"gray",children:"tab to scroll"}):null:null]}),i(g,{flexDirection:"column",marginTop:1,children:!y||I.length===0?i(l,{color:"gray",children:"no activity yet"}):qt.map((a,d)=>u(l,{wrap:"truncate-end",children:[u(l,{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(nt,{children:[i(l,{color:"cyan",bold:!0,children:"CREATE SITE \u2014 PICK ORG"}),i(l,{color:"gray",children:"\u2191/\u2193 select \xB7 \u23CE next \xB7 esc cancel"}),i(g,{flexDirection:"column",marginTop:1,children:t.catalog.map((a,d)=>u(l,{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(nt,{children:[i(l,{color:"cyan",bold:!0,children:"CREATE SITE"}),u(g,{marginTop:1,children:[i(l,{children:"New site name: "}),i(Be,{value:b,onChange:v,onSubmit:ze})]}),i(l,{color:"gray",children:"\u23CE create \xB7 empty + \u23CE to cancel"})]})]})]})]}),t.message?i(g,{paddingX:1,children:u(l,{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:[i(j,{k:"\u2191\u2193",label:"select"}),i(j,{k:"/",label:"search"}),i(j,{k:"s",label:"start sync"}),i(j,{k:"d",label:"disconnect"}),F.length===0?i(j,{k:"n",label:"new site"}):null,i(j,{k:"ctrl+c",label:"quit",width:14})]}),i(j,{k:"?",label:"all keys",width:12,bg:Ee})]}),!Fe&&n.length>0?i(g,{paddingX:1,marginTop:1,flexWrap:"wrap",children:n.map((a,d)=>i(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 De({controller:t,env:e,devSecret:n,savedToken:r,savedEmail:s}){let[o,c]=Me(r?"loading":"auth"),[h,p]=Me();Kn((w,f)=>{f.ctrl&&w==="c"&&t.exit()}),Hn(()=>{r&&t.init(e,r,s).then(()=>c("manage")).catch(w=>{w instanceof L&&w.status===401?p("Saved session expired \u2014 please sign in again."):p(Oe(w)),c("auth")})},[]);let x=async(w,f)=>{p(void 0),await ue({env:e,userToken:w,devSecret:n,email:f}),c("loading");try{await t.init(e,w,f),c("manage")}catch(b){p(Oe(b)),c("auth")}};return o==="auth"?bt(ve,{env:e,initialError:h,onAuthed:x}):o==="loading"?bt(Jn,{children:bt(Xn,{children:"Loading your sites\u2026 "})}):bt($e,{controller:t})}import{jsx as nr}from"react/jsx-runtime";var Qn=Yn(import.meta.url),_t=te();if(_t.version){let t=Qn("../package.json");console.log(t.version),process.exit(0)}var Re=It.dirname(zn(import.meta.url)),Zn=Le(It.join(Re,"AGENT.md"),"utf-8"),tr=Le(It.join(Re,"build.mjs"),"utf-8");async function er(){let t=new ft(Zn,tr),e=await le(),n=(_t.env??e?.env??"production").toLowerCase(),r=_t.dev??process.env.SLEEKCMS_DEV_SECRET??e?.devSecret;ce(r),process.stdout.isTTY&&process.stdout.write("\x1B[2J\x1B[3J\x1B[H");let s=Vn(nr(De,{controller:t,env:n,devSecret:r,savedToken:e?.userToken,savedEmail:e?.email}),{exitOnCtrlC:!1}),o=c=>{s.unmount(),console.log(c),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 s.waitUntilExit()}er().catch(t=>{let e=t;console.error("\u274C",e.body||e.message),process.exit(1)});
|