@sleekcms/sync 3.4.0 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -319,8 +319,7 @@ Models describe the shape of your content. They're JSON-like files with unquoted
319
319
  | Function | Description |
320
320
  |---|---|
321
321
  | `render(val)` | Render a block or entry through its template |
322
- | `marked(md)` | Convert markdown to HTML |
323
- | `remark(md, opts?)` | Convert markdown to HTML with syntax-highlighted code blocks, math (`$…$`, `$$…$$`) and Mermaid diagrams, adding only the CSS/JS a page needs. `opts.codeTheme` picks a highlight.js theme (default `github-dark`) |
322
+ | `remark(md, opts?)` | Convert markdown to HTML with syntax-highlighted code blocks, math (`$…$`, `$$…$$`) and Mermaid diagrams, adding only the CSS/JS a page needs. `opts.codeTheme` picks a highlight.js theme (default `github-dark`). |
324
323
 
325
324
  **Images**
326
325
 
@@ -375,7 +374,7 @@ Models describe the shape of your content. They're JSON-like files with unquoted
375
374
  <article>
376
375
  <h1><%= item.title %></h1>
377
376
  <%- img(item.cover, '1200x600') %>
378
- <%- marked(item.body) %>
377
+ <%- remark(item.body) %>
379
378
  </article>
380
379
  ```
381
380
 
package/dist/AGENTS.md CHANGED
@@ -203,7 +203,7 @@ Like JSON but with **no quotes**; each value is a field type name:
203
203
  |---|---|
204
204
  | `text`, `paragraph`, `code`, `color`, `link` | String |
205
205
  | `richtext` | String of **HTML** — output with `<%- %>` |
206
- | `markdown` | String of **raw markdown** — convert with `<%- marked(item.x) %>`, or `<%- remark(item.x) %>` when it holds code blocks, math or diagrams |
206
+ | `markdown` | String of **raw markdown** — convert with `<%- remark(item.x) %>` (see Helpers — rendering) |
207
207
  | `number` | Number |
208
208
  | `boolean` | `true` / `false` |
209
209
  | `emoji` | One emoji character string, e.g. `"😀"` (not free text) |
@@ -350,7 +350,7 @@ Frontmatter may be JSON (canonical — prefer it when generating), JSON5, or YAM
350
350
  | Tag | Purpose |
351
351
  |---|---|
352
352
  | `<%= expr %>` | Output, HTML-escaped (plain text) |
353
- | `<%- expr %>` | Output raw HTML (`render()`, `img()`, `marked()`, `remark()`, richtext) |
353
+ | `<%- expr %>` | Output raw HTML (`render()`, `img()`, `remark()`, richtext) |
354
354
  | `<% code %>` | Run JS (loops, conditionals) |
355
355
 
356
356
  ### Context variables (every view)
@@ -403,12 +403,11 @@ There is exactly **one** layout: `layouts/common.ejs`. It wraps every page autom
403
403
  | Function | Notes |
404
404
  |---|---|
405
405
  | `render(val, separator?)` | Render a block, entry, or array of them through its `blocks/<key>.ejs` / `entries/<key>.ejs` view. An `entries/<key>.ejs` view is only needed if you call `render()` on that entry — otherwise just use its data directly |
406
- | `marked(md)` | Markdown string → HTML string |
407
- | `remark(md, opts?)` | Markdown string → HTML string, plus highlighted code blocks, math and Mermaid diagrams (below) |
406
+ | `remark(md, opts?)` | Markdown string → HTML string, with build-time highlighted code blocks, math and Mermaid diagrams (below) |
408
407
 
409
- Use `remark()` instead of `marked()` for docs, tutorials and technical posts. On top of `marked()` it renders fenced code blocks (```` ```js ````) with syntax highlighting, math written as `$e^{i\pi} + 1 = 0$` inline, `$$ … $$` on its own lines or a ```` ```math ```` fence (KaTeX), and ```` ```mermaid ```` fences as diagrams drawn in the visitor's browser. It injects only the CSS/JS the rendered markdown needs (KaTeX CSS, a code theme, the Mermaid runtime), so never `link()`/`script()` those libraries yourself.
408
+ Render every `markdown` field with `remark()`, whatever it holds today — editors add code, math and diagrams later; plain prose comes out as ordinary HTML with nothing injected. On top of standard markdown (including GFM tables, task lists and footnotes) it renders fenced code blocks (```` ```js ````) with syntax highlighting at build time, math written as `$e^{i\pi} + 1 = 0$` inline, `$$ … $$` on its own lines or a ```` ```math ```` fence (KaTeX), and ```` ```mermaid ```` fences as diagrams drawn in the visitor's browser. It injects only the CSS/JS the rendered markdown needs (KaTeX CSS, the code theme, the Mermaid runtime). **Never rebuild any of it yourself:** no highlight.js, Prism, KaTeX or Mermaid via `link()`/`script()`, and no `public/js` that highlights `pre code`, converts `language-mermaid` blocks or renders math.
410
409
 
411
- - `opts.codeTheme` — a highlight.js theme name: default `github-dark`, or e.g. `github`, `atom-one-dark`, `base16/dracula`. `github-dark` suits the dark `pre` that `prose` draws; use a light theme only when the site gives `pre` a light background. One theme per page.
410
+ - `opts.codeTheme` — a highlight.js theme name: default `github-dark`, or e.g. `github`, `atom-one-dark`, `base16/dracula`. The theme sets the token colors and the code box's background together, so pick code colors with `codeTheme`, never CSS: don't restyle `.hljs` / `pre code` colors or backgrounds, and don't clear `pre`'s background (`prose-pre:bg-transparent`) — a dark theme's light text on a light page all but disappears. `github-dark` suits the dark `pre` that `prose` draws; use `github` only when the design wants light code blocks. One theme per page.
412
411
  - A single-`$` pair is math only when it hugs its content (`$x^2$`), so prices like "$5 to $10" stay text. Escape a dollar that must stay literal as `\$`.
413
412
  - Mermaid uses its light default theme. On a dark design, start each diagram with frontmatter: `---`, `config:`, ` theme: dark`, `---` on their own lines above the diagram.
414
413
 
@@ -862,7 +861,7 @@ if (!matchMedia('(prefers-reduced-motion: reduce)').matches) {
862
861
  3. Blocks never have content files. Block values live inline in the page/entry JSON that declares them.
863
862
  4. `file` field values are plain file names ending in a supported extension (`"brochure.pdf"`) — the CMS attaches or placeholder-creates the file server-side; never write objects or URLs.
864
863
  5. Never invent `video_id`/`embed_url` objects — render videos with `embed()` only. Videos are set via refs: `content/videos.json` handles for shared videos, and `cms:<handle>` / provider refs in `video` fields (§7); values that read back as embed URLs or objects are user uploads — leave them untouched.
865
- 6. `richtext` → `<%- item.x %>`. `markdown` → `<%- marked(item.x) %>`, or `<%- remark(item.x) %>` when it holds code, math or diagrams.
864
+ 6. `richtext` → `<%- item.x %>`. `markdown` → `<%- remark(item.x) %>`. Never load highlight.js, KaTeX or Mermaid yourself.
866
865
  7. CSS/JS via `link()`/`script()` only; `css/tailwind.css` is auto-injected — never `link()` it. When writing tailwind.css, `@import "tailwindcss";` must stay as its first statement — dropping it silently removes all Tailwind styling.
867
866
  8. Editable copy belongs in `content/`, not hard-coded in `.ejs`. Fixed micro-labels may stay in views.
868
867
  9. In page keys, `-` separates words and `_` means `/`. `_index` is the home page only; a section index page uses the plain section key.
package/dist/index.js CHANGED
@@ -7,4 +7,4 @@ Examples:
7
7
  `);s(`\u{1F500} ${_}`);for(let C of N)s(` ${C}`);x++}return await at(e,h),{renamed:x,held:i}}var Te="sync-errors.log",ve=new Map;async function ot(e){let t=y.resolve(e),n=ve.get(t);if(n)return n;let r=await bn(t);return ve.set(t,r),r}async function bn(e){let t=y.join(e,Te);if(!await p.pathExists(t))return{};let n=await p.readFile(t,"utf-8"),r={};for(let s of n.split(`
8
8
  `)){let i=s.indexOf(": ");i>0&&(r[s.slice(0,i)]=s.slice(i+2))}return r}async function at(e,t){let n=y.join(e,Te),r=Object.entries(t);if(r.length===0){await p.remove(n);return}await p.outputFile(n,r.map(([s,i])=>`${s}: ${i}`).join(`
9
9
  `)+`
10
- `)}async function vn(e,t,n,r,s){ve.delete(y.resolve(e)),await p.remove(y.join(e,Te)),r("\u{1F4E5} Fetching files...");let i=await le(t,n,"GET","/get_files"),d={},l=0;for(let w of i){let S=y.join(e,w.path);await p.outputFile(S,w.content);let u=(await p.stat(S)).mtimeMs;l++,s?.(l,i.length),d[w.path]={mtimeMs:u,hash:V(w.content)}}return r(`\u2714\uFE0F Synced ${i.length} file(s).`),{fileMap:d,pulled:l}}import Sn from"path";import xn from"chokidar";var Tn=5e3,kn=/(^|[/\\])(\.DS_Store|Thumbs\.db|desktop\.ini)$/,Q=class{viewsDir;onSync;debounceMs;watcher=null;debounceTimer=null;isShuttingDown=!1;dirty=!1;syncInFlight=!1;constructor(t){this.viewsDir=t.viewsDir,this.onSync=t.onSync,this.debounceMs=t.debounceMs??Tn}start(){if(this.watcher)return;let t=Sn.join(this.viewsDir,"src");this.watcher=xn.watch([t],{persistent:!0,ignoreInitial:!0,ignored:kn}).on("change",()=>this.scheduleSync()).on("add",()=>this.scheduleSync()).on("unlink",()=>this.scheduleSync()).on("addDir",()=>this.scheduleSync()).on("unlinkDir",()=>this.scheduleSync())}setShuttingDown(t){this.isShuttingDown=t}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(t){let n=t;console.error("\u274C Sync failed:",n.body||n.message)}finally{this.syncInFlight=!1}}}};import ke from"fs-extra";import $n from"os";import lt from"path";var ct={localhost:"http://app.sleekcms.test/api",development:"https://app.sleekcms.dev/api",production:"https://app.sleekcms.com/api"};function Z(e){return ct[e]||ct.production}function Be(e){return Z(e).replace(/\/api$/,"")}var Bn=lt.join($n.homedir(),"SleekCMS"),H=lt.join(Bn,"auth.json"),$e=class extends Error{status;body;constructor(t,n,r=""){super(t),this.name="ApiError",this.status=n,this.body=r}};async function ue(e=H){let t=null;try{t=await ke.readJson(e)}catch{return{sites:[],hadLegacyLogin:!1}}return{sites:Array.isArray(t?.sites)?t.sites.filter(r=>r&&typeof r.token=="string"&&typeof r.siteId=="number"):[],hadLegacyLogin:typeof t?.userToken=="string"}}async function ut(e,t=H){await ke.outputJson(t,{sites:e},{spaces:2});try{await ke.chmod(t,384)}catch{}}async function Pe(e,t=H){let{sites:n}=await ue(t),r=[...n.filter(s=>!(s.env===e.env&&s.siteId===e.siteId)),e];return await ut(r,t),r}async function Ee(e,t,n=H){let{sites:r}=await ue(n),s=r.filter(i=>!(i.env===e&&i.siteId===t));return await ut(s,n),s}async function Ce(e,t,n,r){let s=await fetch(Z(e)+r,{method:n,headers:{Authorization:`Bearer ${t}`}});if(!s.ok){let i=await s.text().catch(()=>"");throw new $e(`${n} ${r} \u2192 ${s.status}: ${i}`,s.status,i)}return s.json()}function Pn(e){if(e==null)return"(unknown error)";if(typeof e=="string")return e;if(typeof e!="object")return String(e);let t=e,n=typeof t.file_path=="string"?t.file_path:"",r=typeof t.path=="string"?t.path:typeof t.filename=="string"?t.filename:"",s=n||r,i=t.line!=null?`:${t.line}`:"",d=t.column!=null?`:${t.column}`:"",l=typeof t.message=="string"&&t.message?t.message:typeof t.name=="string"&&t.name?t.name:"",w=n&&r&&r!==n?` (on page ${r})`:"";if(s)return`${s}${i}${d}${w}: ${l||"(no message)"}`;if(l)return l;try{return JSON.stringify(e)}catch{return"(unknown error)"}}async function dt(e,t){let n;try{n=await Ce(e,t,"GET","/site_build")}catch{return null}if(!Array.isArray(n)||n.length===0)return null;let r=n[0],s=n.find(d=>typeof d.url=="string"&&d.url.length>0),i=Array.isArray(r.errors)?r.errors.map(Pn):[];return i.length>0?{status:"error",mode:"preview",errors:i,url:s?.url||void 0,at:r.updated_at?Date.parse(r.updated_at):void 0}:s?{status:"success",mode:"preview",url:s.url||void 0,at:s.updated_at?Date.parse(s.updated_at):void 0}:null}import{createHash as En,randomBytes as Cn}from"crypto";var Mn="urn:sleekcms:grant-type:cli_poll",Rn="SleekCMS Sync",gt=e=>e.toString("base64").replace(/=+$/,"").replace(/\+/g,"-").replace(/\//g,"_");function ht(){let e=gt(Cn(32)),t=gt(En("sha256").update(e).digest());return{verifier:e,challenge:t}}function ft(e){let t=e.replace(/[^A-Za-z0-9]/g,"").slice(0,8).toUpperCase();return`${t.slice(0,4)}-${t.slice(4)}`}async function pt(e,t=fetch){let n={origin:Be(e),authorizeEndpoint:`${Z(e)}/oauth/authorize`};try{let r=await t(`${Be(e)}/.well-known/oauth-authorization-server`);if(!r.ok)return n;let s=await r.json();if(typeof s.issuer=="string"&&typeof s.authorization_endpoint=="string")return{origin:s.issuer.replace(/\/+$/,""),authorizeEndpoint:s.authorization_endpoint}}catch{}return n}function mt(e,t){let n=new URLSearchParams({response_type:"code",client_name:Rn,redirect_uri:`${e.origin}/oauth/cli-done`,code_challenge:t,code_challenge_method:"S256",resource:`${e.origin}/mcp/`});return`${e.authorizeEndpoint}?${n.toString()}`}var z=class extends Error{constructor(t){super(t),this.name="SignInError"}},Ln=e=>new Promise(t=>setTimeout(t,e));async function wt(e,t,n={}){let{timeoutMs:r=10*6e4,signal:s,fetchImpl:i=fetch,sleep:d=Ln}=n,l=n.intervalMs??2e3,w=Date.now()+r,S=`${Z(e)}/oauth/token`;for(;;){if(s?.aborted)throw new z("Cancelled.");let u=null;try{u=await i(S,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({grant_type:Mn,code_verifier:t})})}catch{u=null}if(u){let h=await u.json().catch(()=>({}));if(u.ok&&h.access_token)return h.access_token;if(u.status===429)l=Math.min(l*2,3e4);else if(h.error!=="authorization_pending")throw new z(h.error==="invalid_grant"?"That approval can no longer be used. Add the site again.":`Sign-in failed: ${h.error||`HTTP ${u.status}`}`)}if(Date.now()+l>w)throw new z("Timed out waiting for approval in the browser.");await d(l)}}function Dn(e){let t=e.split(".")[1];if(!t)throw new z("The server returned a token this version can't read.");return JSON.parse(Buffer.from(t,"base64url").toString("utf8"))}async function yt(e,t){let n=Dn(t),r=await Ce(e,t,"GET","/mcp/get_site");return{env:e,siteId:n.sid,slug:n.slug,name:r.name,org:{name:r.org,subdomain:r.subdomain||n.sub},role:r.role||void 0,token:t,addedAt:Date.now()}}var bt=500;function Fn(){return new Date().toTimeString().slice(0,8)}function On(e){let t=e;if(t.body){try{let n=JSON.parse(t.body);if(n&&typeof n.message=="string")return n.message;if(n&&typeof n.error=="string")return n.error}catch{}return t.body}return t.message}function vt(e){return e instanceof J&&e.status===401}var In=e=>({id:e.siteId,name:e.name,slug:e.slug});async function St(e){e&&(await ee.remove(Me.join(e,"src")),await ee.remove(Me.join(e,".cache","state.json")))}var de=class extends jn{env="production";saved=[];synced=new Map;connecting=null;message;messageIsError=!1;agentMd;buildScript;configPath;isShuttingDown=!1;basePath;pollOptions={};openBrowser=ce;constructor(t,n,r=H){super(),this.agentMd=t,this.buildScript=n,this.configPath=r}changed(){this.emit("change")}flash(t,n=!1){this.message=t,this.messageIsError=n,this.changed()}appendLog(t,n){let r=this.synced.get(t);r&&(r.logs.push({time:Fn(),text:n}),r.logs.length>bt&&r.logs.splice(0,r.logs.length-bt),this.changed())}siteCount(){return this.synced.size}syncingCount(){let t=0;for(let n of this.synced.values())n.status==="syncing"&&t++;return t}watchingCount(){let t=0;for(let n of this.synced.values())(n.status==="watching"||n.status==="syncing")&&t++;return t}async init(t){this.env=t;let{sites:n,hadLegacyLogin:r}=await ue(this.configPath);this.saved=n.filter(s=>s.env===t),r&&this.flash("SleekCMS Sync now connects one site at a time \u2014 press a to add a site in your browser."),this.changed()}isSynced(t){return this.synced.has(t)}async connectSite(){if(this.connecting)return;let{verifier:t,challenge:n}=ht(),r=new AbortController;this.connecting={code:ft(n),url:"",abort:r},this.changed();let s=mt(await pt(this.env),n);if(r.signal.aborted){this.connecting=null,this.changed();return}this.connecting.url=s,this.message=void 0,this.changed(),this.openBrowser(s)||this.flash("Couldn't open a browser \u2014 open the address shown to continue.",!0);let i;try{let l=await wt(this.env,t,{...this.pollOptions,signal:r.signal});i=await yt(this.env,l)}catch(l){this.connecting=null,r.signal.aborted?this.changed():this.flash(On(l),!0);return}this.connecting=null;let d=await Pe(i,this.configPath);this.saved=d.filter(l=>l.env===this.env),this.flash(`Added "${i.name}".`),this.synced.has(i.siteId)||await this.startSite(i)}cancelConnect(){this.connecting?.abort.abort()}async startSite(t){let n=In(t);if(this.synced.has(n.id))return;let r={org:t.org,site:n,siteToken:t.token,role:t.role,viewsDir:"",status:"syncing",logs:[]};this.synced.set(n.id,r),this.appendLog(n.id,"\u2795 added \u2014 pulling files"),this.changed();try{let s=await Se({token:t.token,env:this.env,path:this.basePath,agentMd:this.agentMd,buildScript:this.buildScript,log:i=>this.appendLog(n.id,i),onProgress:(i,d)=>{r.progress={done:i,total:d},this.changed()}});this.noteRole(n.id,s.site.role),r.viewsDir=s.viewsDir,r.progress=void 0,r.status="watching",r.lastSyncedAt=Date.now(),r.lastMsg=`pulled ${s.pulled} file(s)`,r.watcher=new Q({viewsDir:s.viewsDir,onSync:()=>this.runSync(n.id)}),r.watcher.start(),this.watchBuildStatus(n.id),this.seedLastBuild(n.id),this.appendLog(n.id,"\u{1F440} watching for changes")}catch(s){if(vt(s))return void await this.revoke(n.id);let i=s;r.status="error",r.progress=void 0,r.lastMsg=i.body||i.message,this.appendLog(n.id,`\u274C ${i.body||i.message}`)}this.changed()}noteRole(t,n){let r=this.synced.get(t);if(!n||!r||r.role===n)return;r.role=n;let s=this.saved.find(i=>i.siteId===t);s&&(s.role=n,Pe({...s},this.configPath).catch(()=>{}))}async revoke(t){let n=this.synced.get(t),r=n?.site.name??this.saved.find(d=>d.siteId===t)?.name??"this site";n&&(n.watcher?.setShuttingDown(!0),await n.watcher?.stop(),await n.buildWatcher?.close(),this.synced.delete(t));let s=await Ee(this.env,t,this.configPath);this.saved=s.filter(d=>d.env===this.env);let i=n?.viewsDir?` Local files kept in ${n.viewsDir}.`:"";this.flash(`Access to "${r}" was revoked \u2014 press a to add it again.${i}`,!0)}async forgetSite(t){if(this.synced.has(t)){this.flash("Disconnect it first (d), then press x to forget it.",!0);return}let n=this.saved.find(s=>s.siteId===t)?.name??"the site",r=await Ee(this.env,t,this.configPath);this.saved=r.filter(s=>s.env===this.env),this.flash(`Forgot "${n}". To revoke its access everywhere, log out of all sessions in SleekCMS.`)}async removeSite(t){let n=this.synced.get(t);n&&(n.watcher?.setShuttingDown(!0),await n.watcher?.stop(),await n.buildWatcher?.close(),await St(n.viewsDir),this.synced.delete(t),this.changed())}buildSite(t,n=!1){let r=this.synced.get(t);if(!r||!r.viewsDir)return;if(r.role&&r.role!=="ADMIN"){this.flash(`Building and publishing are for site ADMINs \u2014 you're ${r.role} on "${r.site.name}".`,!0);return}let s=n?"live":"preview";this.writeBuildFile(r.viewsDir,{status:"building",mode:s,stage:"queued",url:r.lastBuild?.url,at:Date.now()}),An("node",n?["build.mjs","--publish"]:["build.mjs"],{cwd:r.viewsDir,stdio:"ignore"}).on("error",l=>{this.writeBuildFile(r.viewsDir,{status:"error",mode:s,errors:[l.message],url:r.lastBuild?.url,at:Date.now()}),this.appendLog(t,`\u274C build: ${l.message}`)}),this.appendLog(t,n?"\u{1F680} publishing\u2026":"\u{1F528} building\u2026")}buildFile(t){return Me.join(t,".cache","build.json")}async readBuildFile(t){try{let n=await ee.readJson(this.buildFile(t));return n&&n.status?n:null}catch{return null}}async writeBuildFile(t,n){let r=this.buildFile(t),s=`${r}.tmp`;try{await ee.outputJson(s,n),await ee.rename(s,r)}catch{}}watchBuildStatus(t){let n=this.synced.get(t);!n||!n.viewsDir||(n.buildWatcher=_n.watch(this.buildFile(n.viewsDir),{ignoreInitial:!1}).on("add",()=>{this.readBuildStatus(t)}).on("change",()=>{this.readBuildStatus(t)}))}async readBuildStatus(t){let n=this.synced.get(t);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(t){let n=this.synced.get(t);if(!n)return;let r=await dt(this.env,n.siteToken);if(!r)return;let s=this.synced.get(t);if(!s)return;let i=await this.readBuildFile(s.viewsDir);i?.at!=null&&(r.at==null||i.at>=r.at)||await this.writeBuildFile(s.viewsDir,r)}async refetch(t){await this.runSync(t,{flush:!0})}async runSync(t,n={}){let r=this.synced.get(t);if(!(!r||!r.viewsDir||r.status==="paused")){r.status="syncing",this.changed();try{let s=await Se({token:r.siteToken,env:this.env,viewsDir:r.viewsDir,agentMd:this.agentMd,buildScript:this.buildScript,flush:n.flush,log:i=>this.appendLog(t,i),onProgress:(i,d)=>{r.progress={done:i,total:d},this.changed()}});if(this.noteRole(t,s.site.role),r.progress=void 0,r.status="watching",r.lastSyncedAt=Date.now(),n.flush)r.lastMsg=`re-fetched ${s.pulled} file(s)`;else{let i=[s.renamed>0?`renamed ${s.renamed}`:"",s.pushed>0?`pushed ${s.pushed} file(s)`:""].filter(Boolean);r.lastMsg=i.length>0?i.join(", "):"up to date"}}catch(s){if(vt(s))return void await this.revoke(t);let i=s;r.status="error",r.progress=void 0,r.lastMsg=i.body||i.message,this.appendLog(t,`\u274C ${i.body||i.message}`)}this.changed()}}async togglePause(t){let n=this.synced.get(t);!n||!n.viewsDir||(n.status==="paused"?(n.watcher=new Q({viewsDir:n.viewsDir,onSync:()=>this.runSync(t)}),n.watcher.start(),n.status="watching",this.appendLog(t,"\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(t,"\u23F8 paused")),this.changed())}async cleanupAll(){this.cancelConnect();for(let t of this.synced.values())t.watcher?.setShuttingDown(!0),await t.watcher?.stop(),await t.buildWatcher?.close(),await St(t.viewsDir)}async exit(){this.isShuttingDown||(this.isShuttingDown=!0,await this.cleanupAll(),this.emit("exit"))}};import{useInput as nr}from"ink";import{Fragment as Un,useEffect as te,useMemo as Jn,useRef as Hn,useState as L}from"react";import{Box as f,Text as c,useInput as zn,useStdout as qn}from"ink";import Yn from"ink-text-input";import{createRequire as Nn}from"node:module";var Wn=Nn(import.meta.url),ge=Wn("../package.json").version;import Gn from"node:os";function Re(e){if(!e)return"\u2014";let t=Gn.homedir();return e.startsWith(t)?"~"+e.slice(t.length):e}function Le(e){if(!e)return"\u2014";let t=Math.floor((Date.now()-e)/1e3);if(t<5)return"just now";if(t<60)return`${t}s ago`;let n=Math.floor(t/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 fe(e){return!e||e.total<=0?null:Math.round(e.done/e.total*100)}var he=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"];function De(e){return he[(e%he.length+he.length)%he.length]}function pe(e){switch(e){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 xt(e){if(!e)return{dot:"\xB7",color:"gray",label:"\u2014"};switch(e.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:e.mode==="live"?"live":"preview"}}}import{Fragment as je,jsx as o,jsxs as g}from"react/jsx-runtime";var Xn=(e,t)=>t<=0?0:Math.max(0,Math.min(e,t-1)),Kn=16,Vn=5,Qn="greenBright",Tt="blueBright",kt="magentaBright";function $t({k:e,bg:t=Qn}){return o(c,{color:"black",backgroundColor:t,children:` ${e} `})}function F({k:e,label:t,width:n=16,bg:r}){return g(f,{width:n,children:[o($t,{k:e,bg:r}),o(c,{color:"gray",children:` ${t}`})]})}function B({k:e,label:t,bg:n}){return g(f,{marginBottom:1,children:[o(f,{width:9,children:o($t,{k:e,bg:n})}),o(c,{color:"gray",children:t})]})}function Zn({launchers:e}){return g(f,{flexDirection:"column",borderStyle:"round",borderColor:"green",paddingX:2,paddingY:1,width:66,children:[g(f,{justifyContent:"space-between",children:[o(c,{color:"green",bold:!0,children:"KEYBOARD SHORTCUTS"}),o(c,{color:"gray",children:"? or esc to close"})]}),g(f,{marginTop:1,children:[g(f,{flexDirection:"column",width:34,children:[o(c,{color:"cyan",bold:!0,children:"Navigate"}),o(B,{k:"\u2191\u2193",label:"move selection"}),o(B,{k:"tab",label:"focus logs \xB7 \u2191\u2193 scroll"}),o(B,{k:"esc",label:"clear filter / back"}),o(f,{marginTop:1,children:o(c,{color:"cyan",bold:!0,children:"Global"})}),o(B,{k:"/",label:"search sites"}),o(B,{k:"a",label:"add a site (browser)"}),o(B,{k:"?",label:"toggle this help",bg:Tt}),o(B,{k:"ctrl+c",label:"quit"})]}),g(f,{flexDirection:"column",flexGrow:1,children:[o(c,{color:"cyan",bold:!0,children:"Site actions"}),o(B,{k:"s",label:"start sync"}),o(B,{k:"d",label:"disconnect"}),o(B,{k:"x",label:"forget site"}),o(B,{k:"p",label:"pause / resume sync"}),o(B,{k:"b",label:"build preview"}),o(B,{k:"B",label:"publish live"}),o(B,{k:"o",label:"open preview url"}),e.map((t,n)=>o(B,{k:String(n+1),label:`open in ${t.label}`,bg:kt},t.key))]})]})]})}function er({percent:e,width:t=28}){let n=Math.round(Math.max(0,Math.min(100,e))/100*t);return g(c,{children:[o(c,{color:"green",children:"\u2588".repeat(n)}),o(c,{color:"gray",children:"\u2591".repeat(Math.max(0,t-n))})]})}function tr({frame:e,width:t=24,seg:n=6}){let r=t+n,s=e%r-n,i="";for(let d=0;d<t;d++)i+=d>=s&&d<s+n?"\u25AE":"\u25AF";return o(c,{color:"yellow",children:i})}function Bt({controller:e}){let{stdout:t}=qn(),n=Jn(()=>Ze(),[]),[,r]=L(0),[s,i]=L(()=>new Date),[d,l]=L(null),[w,S]=L(""),[u,h]=L(!1),[x,m]=L(0),[b,k]=L(!1),[_,N]=L(!1),[C,M]=L("list"),[q,D]=L(0),Fe=()=>r(a=>a+1);te(()=>(e.on("change",Fe),()=>{e.off("change",Fe)}),[e]),te(()=>{let a,v=()=>{i(new Date),a=setTimeout(v,6e4-Date.now()%6e4)};return a=setTimeout(v,6e4-Date.now()%6e4),()=>clearTimeout(a)},[]);let Y=e.saved.map(a=>({org:a.org,site:{id:a.siteId,name:a.name,slug:a.slug},saved:a,entry:e.synced.get(a.siteId)})).sort((a,v)=>(a.entry?0:1)-(v.entry?0:1)),W=e.connecting,O=w.trim().toLowerCase(),E=O?Y.filter(a=>a.site.name.toLowerCase().includes(O)||(a.org.name||a.org.subdomain||"").toLowerCase().includes(O)):Y,G=E.findIndex(a=>a.site.id===d);G<0&&(G=0);let $=E[G],T=$?.entry,ne=Math.max(3,(t?.rows??30)-26),I=T?.logs??[],me=Math.max(0,I.length-ne),Mt=Math.min(q,me),re=I.length-Mt,Oe=I.slice(Math.max(0,re-ne),re),Rt=$?.site.id;te(()=>D(0),[Rt]);let j=T?.lastBuild,we=j?.status==="building",ye=xt(j),se=we||!!W;if(!se){for(let a of e.synced.values())if(a.status==="syncing"){se=!0;break}}te(()=>{if(!se)return;let a=setInterval(()=>m(v=>v+1),120);return()=>clearInterval(a)},[se]);let Ie=E.findIndex(a=>!a.entry),U=Math.max(Vn,(t?.rows??30)-Kn),X=E.length,ie=X>U,Lt=Math.floor(U/2),K=ie?Math.max(0,Math.min(G-Lt,X-U)):0,be=Math.min(X,K+U),Dt=E.slice(K,be),Ne=Hn(!1);te(()=>{Ne.current||(Ne.current=!0,e.saved.length===0&&!e.connecting&&e.connectSite())},[e]);let oe=a=>{if(E.length===0)return;let v=Xn(G+a,E.length);l(E[v].site.id)},jt=()=>{if($){if($.entry){e.flash("Already syncing \u2014 press d to disconnect.");return}e.startSite($.saved)}},At=()=>{$?.entry&&e.removeSite($.site.id)},_t=()=>{$&&e.forgetSite($.site.id)},Ft=a=>{if(!T){e.flash("Press s to start syncing this site first.",!0);return}N(!0);let v=a.run(T.viewsDir);e.flash(v?`Opened ${a.label} at ${Re(T.viewsDir)}`:`Couldn't open ${a.label}.`,!v)},Ot=()=>{if(!T){e.flash("Press s to start syncing this site first.",!0);return}e.togglePause(T.site.id)},We=a=>{if(!T){e.flash("Press s to start syncing this site first.",!0);return}e.buildSite(T.site.id,a),e.flash(a?"Publishing \u2014 making this live\u2026":"Building preview\u2026")},It=()=>{let a=T?.lastBuild?.url;if(!a){e.flash("No build URL yet \u2014 press b to build.",!0);return}e.flash(ce(a)?`Opening ${a}`:"Couldn't open the browser.",!1)};zn((a,v)=>{if(b){(a==="?"||v.escape)&&k(!1);return}if(W&&v.escape)return void e.cancelConnect();if(u){v.escape?(h(!1),S(""),l(null)):v.return?h(!1):v.upArrow?oe(-1):v.downArrow&&oe(1);return}if(v.tab){C==="logs"?M("list"):T&&M("logs"),D(0);return}if(C==="logs"){if(v.escape){M("list"),D(0);return}if(v.upArrow)return void D(A=>Math.min(A+1,me));if(v.downArrow)return void D(A=>Math.max(0,A-1))}if(v.escape){S(""),l(null);return}if(a==="?")return void k(!0);if(a==="/")return M("list"),void h(!0);if(a==="a"||a==="n")return void e.connectSite();if(v.upArrow)return oe(-1);if(v.downArrow)return oe(1);if(a==="s")return jt();if(a==="d")return At();if(a==="x")return _t();if(a==="p")return Ot();if(a==="b")return We(!1);if(a==="B")return We(!0);if(a==="o")return It();if(a>="1"&&a<="9"){let A=n[parseInt(a,10)-1];if(A)return Ft(A)}});let Ge=g(f,{justifyContent:"space-between",paddingX:1,children:[g(f,{children:[o(c,{color:"green",bold:!0,children:"\u25C6 SleekCMS"}),o(c,{color:"gray",children:` v${ge} \u2502 `}),o(c,{children:e.env==="production"?"Sync":`Sync \xB7 ${e.env}`})]}),g(f,{children:[g(c,{color:"gray",children:[O?`${E.length}/${Y.length}`:Y.length," sites \xB7 "]}),g(c,{color:"green",children:[e.watchingCount()," watching"]}),o(c,{color:"gray",children:` \xB7 ${s.toTimeString().slice(0,5)}`})]})]});return b?g(f,{flexDirection:"column",children:[Ge,o(f,{marginTop:1,justifyContent:"center",children:o(Zn,{launchers:n})})]}):g(f,{flexDirection:"column",children:[Ge,g(f,{children:[g(f,{flexDirection:"column",borderStyle:"round",borderColor:"green",width:38,paddingX:1,children:[g(f,{justifyContent:"space-between",children:[o(c,{color:"green",bold:!0,children:"SITE WORKSPACES"}),Y.length>U&&!u&&!O?o(c,{color:"gray",children:"/ filter"}):null]}),u?g(f,{children:[o(c,{color:"cyan",children:"/ "}),o(Yn,{value:w,onChange:S,onSubmit:()=>h(!1),placeholder:"type to filter\u2026"})]}):O?o(c,{color:"gray",children:`/ ${w} \xB7 ${E.length} match${E.length===1?"":"es"}`}):null,o(f,{flexDirection:"column",marginTop:1,minHeight:ie?U+3:void 0,children:E.length===0?o(c,{color:"gray",children:O?`no matches for \u201C${w}\u201D`:"no sites yet \u2014 press a to add one"}):g(je,{children:[ie?o(c,{color:"gray",children:K>0?` \u2191 ${K} more`:" "}):null,Dt.map((a,v)=>{let A=K+v,Ue=a.entry?pe(a.entry.status):{dot:"\xB7",color:"gray",label:"idle"},Je=fe(a.entry?.progress),Nt=a.entry?a.entry.status==="syncing"&&Je!=null?`${Je}%`:a.entry.status==="paused"?"paused":"--":"",ae=A===G,Wt=A===Ie&&Ie>0;return g(Un,{children:[Wt?o(c,{color:"gray",children:"\u2500".repeat(34)}):null,g(f,{justifyContent:"space-between",children:[g(f,{children:[o(c,{color:ae?"green":"gray",children:ae?"\u276F ":" "}),g(c,{color:a.entry?.status==="watching"||a.entry?.status==="syncing"?"green":Ue.color,children:[a.entry?.status==="syncing"?De(x):a.entry?.status==="watching"?"\u25CF":Ue.dot," "]}),o(c,{color:ae?"white":a.entry?void 0:"gray",bold:ae,children:a.site.name})]}),o(c,{color:a.entry?.status==="syncing"?"green":"gray",children:Nt})]})]},a.site.id)}),ie?o(c,{color:"gray",children:be<X?` \u2193 ${X-be} more`:" "}):null]})}),o(f,{marginTop:1,children:o(c,{color:"gray",children:"+ add site \xB7 a"})})]}),g(f,{flexDirection:"column",flexGrow:1,marginLeft:1,children:[o(f,{flexDirection:"column",borderStyle:"round",borderColor:"gray",paddingX:1,children:$?g(je,{children:[g(c,{children:[$.org.name||$.org.subdomain?g(c,{color:"gray",children:[$.org.name||$.org.subdomain," / "]}):null,o(c,{color:"cyan",bold:!0,children:$.site.name})]}),g(f,{marginTop:1,children:[o(c,{color:"gray",children:"status "}),T?o(c,{color:pe(T.status).color,children:`: ${pe(T.status).label}`}):o(c,{color:"gray",children:": idle \u2014 not syncing (press s to start)"})]}),g(f,{children:[o(c,{color:"gray",children:"role "}),o(c,{children:T?.role??$.saved.role??"\u2014"}),(T?.role??$.saved.role??"ADMIN")!=="ADMIN"?o(c,{color:"gray",children:" \xB7 build & publish need ADMIN"}):null]}),g(f,{children:[o(c,{color:"gray",children:"path "}),o(c,{wrap:"truncate-end",children:T?.viewsDir?Re(T.viewsDir):"\u2014"})]}),g(f,{children:[o(c,{color:"gray",children:"synced "}),o(c,{children:Le(T?.lastSyncedAt)}),o(c,{color:"gray",children:" progress "}),o(c,{color:T?.status==="syncing"?"green":"gray",children:T?.status==="syncing"?`${fe(T.progress)??0}%`:"\u2014"})]}),o(f,{marginTop:1,children:o(er,{percent:T?T.status==="syncing"?fe(T.progress)??0:T.status==="watching"?100:0:0})}),T?g(je,{children:[g(f,{marginTop:1,children:[o(c,{color:"gray",children:"build "}),o(c,{color:ye.color,children:`${ye.dot} ${ye.label}`}),o(c,{color:"gray",children:" last "}),o(c,{children:we?"in progress":Le(j?.at)})]}),g(f,{children:[o(c,{color:"gray",children:"url "}),j?.url?o(c,{color:"cyan",wrap:"truncate-end",children:j.url}):o(c,{color:"gray",children:"\u2014"})]}),j?.status==="error"&&j.errors?.length?g(f,{children:[o(c,{color:"red",children:"\u258C "}),o(c,{color:"red",wrap:"truncate-end",children:`\u2717 ${j.errors[0]}`})]}):null,we?g(f,{children:[o(tr,{frame:x}),o(c,{color:"gray",children:` ${j?.stage??"building"}`})]}):null]}):null]}):o(c,{color:"gray",children:"No site selected."})}),W?g(f,{flexDirection:"column",borderStyle:"round",borderColor:"blue",paddingX:1,marginTop:1,flexGrow:1,children:[o(c,{color:"cyan",bold:!0,children:"ADD A SITE"}),o(c,{children:"Approve it in your browser: pick a site, or create one."}),g(f,{marginTop:1,children:[o(c,{color:"gray",children:"Check the page shows "}),o(c,{color:"cyan",bold:!0,children:W.code})]}),o(c,{color:"gray",children:"Only approve a page that shows this code."}),g(f,{marginTop:1,children:[g(c,{color:"yellow",children:[De(x)," "]}),o(c,{color:"gray",children:"waiting for approval \xB7 esc to cancel"})]})]}):g(f,{flexDirection:"column",borderStyle:"round",borderColor:C==="logs"?"green":"gray",paddingX:1,marginTop:1,height:ne+4,flexShrink:0,overflowY:"hidden",children:[g(f,{justifyContent:"space-between",children:[g(c,{children:[o(c,{color:"cyan",bold:!0,children:"LOGS \xB7 "}),o(c,{color:"white",children:$?.site.name??"\u2014"})]}),T&&I.length>0?C==="logs"?o(c,{color:"green",children:`\u2191\u2193 scroll \xB7 esc back${me>0?` \xB7 ${re}/${I.length}`:""}`}):I.length>ne?o(c,{color:"gray",children:"tab to scroll"}):null:null]}),o(f,{flexDirection:"column",marginTop:1,children:!T||I.length===0?o(c,{color:"gray",children:"no activity yet"}):Oe.map((a,v)=>g(c,{wrap:"truncate-end",children:[g(c,{color:"gray",children:[a.time," "]}),a.text]},re-Oe.length+v))})]})]})]}),W?.url?g(f,{flexDirection:"column",paddingX:1,marginTop:1,children:[o(c,{color:"gray",children:"No browser? Open this address on any device:"}),o(c,{color:"cyan",children:W.url})]}):null,e.message?o(f,{paddingX:1,children:g(c,{color:e.messageIsError?"red":"gray",wrap:"truncate-end",children:[e.messageIsError?"\u2717 ":"",e.message]})}):null,g(f,{flexDirection:"column",marginTop:1,children:[g(f,{paddingX:1,justifyContent:"space-between",children:[g(f,{flexWrap:"wrap",children:[o(F,{k:"\u2191\u2193",label:"select"}),o(F,{k:"/",label:"search"}),o(F,{k:"s",label:"start sync"}),o(F,{k:"d",label:"disconnect"}),o(F,{k:"a",label:"add site"}),o(F,{k:"ctrl+c",label:"quit",width:14})]}),o(F,{k:"?",label:"all keys",width:12,bg:Tt})]}),!_&&n.length>0?o(f,{paddingX:1,marginTop:1,flexWrap:"wrap",children:n.map((a,v)=>o(F,{k:String(v+1),label:a.label,bg:kt},a.key))}):null]})]})}import{jsx as rr}from"react/jsx-runtime";function Pt({controller:e}){return nr((t,n)=>{n.ctrl&&t==="c"&&e.exit()}),rr(Bt,{controller:e})}import{jsx as lr}from"react/jsx-runtime";var Ae=Ke();Ae.version&&(console.log(ge),process.exit(0));var Ct=_e.dirname(sr(import.meta.url)),or=Et(_e.join(Ct,"AGENTS.md"),"utf-8"),ar=Et(_e.join(Ct,"build.mjs"),"utf-8");async function cr(){let e=new de(or,ar);e.basePath=Ae.path,await e.init((Ae.env??"production").toLowerCase()),process.stdout.isTTY&&process.stdout.write("\x1B[2J\x1B[3J\x1B[H");let t=ir(lr(Pt,{controller:e}),{exitOnCtrlC:!1}),n=r=>{t.unmount(),console.log(r),process.exit(0)};e.once("exit",()=>n("\u2713 Stopped. Local site files were cleaned up.")),process.on("SIGINT",()=>{e.exit()}),process.on("SIGTERM",()=>{e.exit()}),await t.waitUntilExit()}cr().catch(e=>{let t=e;console.error("\u274C",t.body||t.message),process.exit(1)});
10
+ `)}async function vn(e,t,n,r,s){ve.delete(y.resolve(e)),await p.remove(y.join(e,Te)),r("\u{1F4E5} Fetching files...");let i=await le(t,n,"GET","/get_files"),d={},l=0;for(let w of i){let S=y.join(e,w.path);await p.outputFile(S,w.content);let u=(await p.stat(S)).mtimeMs;l++,s?.(l,i.length),d[w.path]={mtimeMs:u,hash:V(w.content)}}return r(`\u2714\uFE0F Synced ${i.length} file(s).`),{fileMap:d,pulled:l}}import Sn from"path";import xn from"chokidar";var Tn=5e3,kn=/(^|[/\\])(\.DS_Store|Thumbs\.db|desktop\.ini)$/,Q=class{viewsDir;onSync;debounceMs;watcher=null;debounceTimer=null;isShuttingDown=!1;dirty=!1;syncInFlight=!1;constructor(t){this.viewsDir=t.viewsDir,this.onSync=t.onSync,this.debounceMs=t.debounceMs??Tn}start(){if(this.watcher)return;let t=Sn.join(this.viewsDir,"src");this.watcher=xn.watch([t],{persistent:!0,ignoreInitial:!0,ignored:kn}).on("change",()=>this.scheduleSync()).on("add",()=>this.scheduleSync()).on("unlink",()=>this.scheduleSync()).on("addDir",()=>this.scheduleSync()).on("unlinkDir",()=>this.scheduleSync())}setShuttingDown(t){this.isShuttingDown=t}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(t){let n=t;console.error("\u274C Sync failed:",n.body||n.message)}finally{this.syncInFlight=!1}}}};import ke from"fs-extra";import $n from"os";import lt from"path";var ct={localhost:"http://app.sleekcms.test/api",development:"https://app.sleekcms.dev/api",production:"https://app.sleekcms.com/api"};function Z(e){return ct[e]||ct.production}function Be(e){return Z(e).replace(/\/api$/,"")}var Bn=lt.join($n.homedir(),"SleekCMS"),H=lt.join(Bn,"auth.json"),$e=class extends Error{status;body;constructor(t,n,r=""){super(t),this.name="ApiError",this.status=n,this.body=r}};async function ue(e=H){let t=null;try{t=await ke.readJson(e)}catch{return{sites:[],hadLegacyLogin:!1}}return{sites:Array.isArray(t?.sites)?t.sites.filter(r=>r&&typeof r.token=="string"&&typeof r.siteId=="number"):[],hadLegacyLogin:typeof t?.userToken=="string"}}async function ut(e,t=H){await ke.outputJson(t,{sites:e},{spaces:2});try{await ke.chmod(t,384)}catch{}}async function Pe(e,t=H){let{sites:n}=await ue(t),r=[...n.filter(s=>!(s.env===e.env&&s.siteId===e.siteId)),e];return await ut(r,t),r}async function Ee(e,t,n=H){let{sites:r}=await ue(n),s=r.filter(i=>!(i.env===e&&i.siteId===t));return await ut(s,n),s}async function Ce(e,t,n,r){let s=await fetch(Z(e)+r,{method:n,headers:{Authorization:`Bearer ${t}`}});if(!s.ok){let i=await s.text().catch(()=>"");throw new $e(`${n} ${r} \u2192 ${s.status}: ${i}`,s.status,i)}return s.json()}function Pn(e){if(e==null)return"(unknown error)";if(typeof e=="string")return e;if(typeof e!="object")return String(e);let t=e,n=typeof t.file_path=="string"?t.file_path:"",r=typeof t.path=="string"?t.path:typeof t.filename=="string"?t.filename:"",s=n||r,i=t.line!=null?`:${t.line}`:"",d=t.column!=null?`:${t.column}`:"",l=typeof t.message=="string"&&t.message?t.message:typeof t.name=="string"&&t.name?t.name:"",w=n&&r&&r!==n?` (on page ${r})`:"";if(s)return`${s}${i}${d}${w}: ${l||"(no message)"}`;if(l)return l;try{return JSON.stringify(e)}catch{return"(unknown error)"}}async function dt(e,t){let n;try{n=await Ce(e,t,"GET","/site_build")}catch{return null}if(!Array.isArray(n)||n.length===0)return null;let r=n[0],s=n.find(d=>typeof d.url=="string"&&d.url.length>0),i=Array.isArray(r.errors)?r.errors.map(Pn):[];return i.length>0?{status:"error",mode:"preview",errors:i,url:s?.url||void 0,at:r.updated_at?Date.parse(r.updated_at):void 0}:s?{status:"success",mode:"preview",url:s.url||void 0,at:s.updated_at?Date.parse(s.updated_at):void 0}:null}import{createHash as En,randomBytes as Cn}from"crypto";var Mn="urn:sleekcms:grant-type:cli_poll",Rn="SleekCMS Sync",gt=e=>e.toString("base64").replace(/=+$/,"").replace(/\+/g,"-").replace(/\//g,"_");function ht(){let e=gt(Cn(32)),t=gt(En("sha256").update(e).digest());return{verifier:e,challenge:t}}function ft(e){let t=e.replace(/[^A-Za-z0-9]/g,"").slice(0,8).toUpperCase();return`${t.slice(0,4)}-${t.slice(4)}`}async function pt(e,t=fetch){let n={origin:Be(e),authorizeEndpoint:`${Z(e)}/oauth/authorize`};try{let r=await t(`${Be(e)}/.well-known/oauth-authorization-server`);if(!r.ok)return n;let s=await r.json();if(typeof s.issuer=="string"&&typeof s.authorization_endpoint=="string")return{origin:s.issuer.replace(/\/+$/,""),authorizeEndpoint:s.authorization_endpoint}}catch{}return n}function mt(e,t){let n=new URLSearchParams({response_type:"code",client_name:Rn,redirect_uri:`${e.origin}/oauth/cli-done`,code_challenge:t,code_challenge_method:"S256",resource:`${e.origin}/mcp/`});return`${e.authorizeEndpoint}?${n.toString()}`}var z=class extends Error{constructor(t){super(t),this.name="SignInError"}},Ln=e=>new Promise(t=>setTimeout(t,e));async function wt(e,t,n={}){let{timeoutMs:r=10*6e4,signal:s,fetchImpl:i=fetch,sleep:d=Ln}=n,l=n.intervalMs??2e3,w=Date.now()+r,S=`${Z(e)}/oauth/token`;for(;;){if(s?.aborted)throw new z("Cancelled.");let u=null;try{u=await i(S,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({grant_type:Mn,code_verifier:t})})}catch{u=null}if(u){let h=await u.json().catch(()=>({}));if(u.ok&&h.access_token)return h.access_token;if(u.status===429)l=Math.min(l*2,3e4);else if(h.error!=="authorization_pending")throw new z(h.error==="invalid_grant"?"That approval can no longer be used. Add the site again.":`Sign-in failed: ${h.error||`HTTP ${u.status}`}`)}if(Date.now()+l>w)throw new z("Timed out waiting for approval in the browser.");await d(l)}}function Dn(e){let t=e.split(".")[1];if(!t)throw new z("The server returned a token this version can't read.");return JSON.parse(Buffer.from(t,"base64url").toString("utf8"))}async function yt(e,t){let n=Dn(t),r=await Ce(e,t,"GET","/mcp/get_site");return{env:e,siteId:n.sid,slug:n.slug,name:r.name,org:{name:r.org},role:r.role||void 0,token:t,addedAt:Date.now()}}var bt=500;function Fn(){return new Date().toTimeString().slice(0,8)}function On(e){let t=e;if(t.body){try{let n=JSON.parse(t.body);if(n&&typeof n.message=="string")return n.message;if(n&&typeof n.error=="string")return n.error}catch{}return t.body}return t.message}function vt(e){return e instanceof J&&e.status===401}var In=e=>({id:e.siteId,name:e.name,slug:e.slug});async function St(e){e&&(await ee.remove(Me.join(e,"src")),await ee.remove(Me.join(e,".cache","state.json")))}var de=class extends jn{env="production";saved=[];synced=new Map;connecting=null;message;messageIsError=!1;agentMd;buildScript;configPath;isShuttingDown=!1;basePath;pollOptions={};openBrowser=ce;constructor(t,n,r=H){super(),this.agentMd=t,this.buildScript=n,this.configPath=r}changed(){this.emit("change")}flash(t,n=!1){this.message=t,this.messageIsError=n,this.changed()}appendLog(t,n){let r=this.synced.get(t);r&&(r.logs.push({time:Fn(),text:n}),r.logs.length>bt&&r.logs.splice(0,r.logs.length-bt),this.changed())}siteCount(){return this.synced.size}syncingCount(){let t=0;for(let n of this.synced.values())n.status==="syncing"&&t++;return t}watchingCount(){let t=0;for(let n of this.synced.values())(n.status==="watching"||n.status==="syncing")&&t++;return t}async init(t){this.env=t;let{sites:n,hadLegacyLogin:r}=await ue(this.configPath);this.saved=n.filter(s=>s.env===t),r&&this.flash("SleekCMS Sync now connects one site at a time \u2014 press a to add a site in your browser."),this.changed()}isSynced(t){return this.synced.has(t)}async connectSite(){if(this.connecting)return;let{verifier:t,challenge:n}=ht(),r=new AbortController;this.connecting={code:ft(n),url:"",abort:r},this.changed();let s=mt(await pt(this.env),n);if(r.signal.aborted){this.connecting=null,this.changed();return}this.connecting.url=s,this.message=void 0,this.changed(),this.openBrowser(s)||this.flash("Couldn't open a browser \u2014 open the address shown to continue.",!0);let i;try{let l=await wt(this.env,t,{...this.pollOptions,signal:r.signal});i=await yt(this.env,l)}catch(l){this.connecting=null,r.signal.aborted?this.changed():this.flash(On(l),!0);return}this.connecting=null;let d=await Pe(i,this.configPath);this.saved=d.filter(l=>l.env===this.env),this.flash(`Added "${i.name}".`),this.synced.has(i.siteId)||await this.startSite(i)}cancelConnect(){this.connecting?.abort.abort()}async startSite(t){let n=In(t);if(this.synced.has(n.id))return;let r={org:t.org,site:n,siteToken:t.token,role:t.role,viewsDir:"",status:"syncing",logs:[]};this.synced.set(n.id,r),this.appendLog(n.id,"\u2795 added \u2014 pulling files"),this.changed();try{let s=await Se({token:t.token,env:this.env,path:this.basePath,agentMd:this.agentMd,buildScript:this.buildScript,log:i=>this.appendLog(n.id,i),onProgress:(i,d)=>{r.progress={done:i,total:d},this.changed()}});this.noteRole(n.id,s.site.role),r.viewsDir=s.viewsDir,r.progress=void 0,r.status="watching",r.lastSyncedAt=Date.now(),r.lastMsg=`pulled ${s.pulled} file(s)`,r.watcher=new Q({viewsDir:s.viewsDir,onSync:()=>this.runSync(n.id)}),r.watcher.start(),this.watchBuildStatus(n.id),this.seedLastBuild(n.id),this.appendLog(n.id,"\u{1F440} watching for changes")}catch(s){if(vt(s))return void await this.revoke(n.id);let i=s;r.status="error",r.progress=void 0,r.lastMsg=i.body||i.message,this.appendLog(n.id,`\u274C ${i.body||i.message}`)}this.changed()}noteRole(t,n){let r=this.synced.get(t);if(!n||!r||r.role===n)return;r.role=n;let s=this.saved.find(i=>i.siteId===t);s&&(s.role=n,Pe({...s},this.configPath).catch(()=>{}))}async revoke(t){let n=this.synced.get(t),r=n?.site.name??this.saved.find(d=>d.siteId===t)?.name??"this site";n&&(n.watcher?.setShuttingDown(!0),await n.watcher?.stop(),await n.buildWatcher?.close(),this.synced.delete(t));let s=await Ee(this.env,t,this.configPath);this.saved=s.filter(d=>d.env===this.env);let i=n?.viewsDir?` Local files kept in ${n.viewsDir}.`:"";this.flash(`Access to "${r}" was revoked \u2014 press a to add it again.${i}`,!0)}async forgetSite(t){if(this.synced.has(t)){this.flash("Disconnect it first (d), then press x to forget it.",!0);return}let n=this.saved.find(s=>s.siteId===t)?.name??"the site",r=await Ee(this.env,t,this.configPath);this.saved=r.filter(s=>s.env===this.env),this.flash(`Forgot "${n}". To revoke its access everywhere, log out of all sessions in SleekCMS.`)}async removeSite(t){let n=this.synced.get(t);n&&(n.watcher?.setShuttingDown(!0),await n.watcher?.stop(),await n.buildWatcher?.close(),await St(n.viewsDir),this.synced.delete(t),this.changed())}buildSite(t,n=!1){let r=this.synced.get(t);if(!r||!r.viewsDir)return;if(r.role&&r.role!=="ADMIN"){this.flash(`Building and publishing are for site ADMINs \u2014 you're ${r.role} on "${r.site.name}".`,!0);return}let s=n?"live":"preview";this.writeBuildFile(r.viewsDir,{status:"building",mode:s,stage:"queued",url:r.lastBuild?.url,at:Date.now()}),An("node",n?["build.mjs","--publish"]:["build.mjs"],{cwd:r.viewsDir,stdio:"ignore"}).on("error",l=>{this.writeBuildFile(r.viewsDir,{status:"error",mode:s,errors:[l.message],url:r.lastBuild?.url,at:Date.now()}),this.appendLog(t,`\u274C build: ${l.message}`)}),this.appendLog(t,n?"\u{1F680} publishing\u2026":"\u{1F528} building\u2026")}buildFile(t){return Me.join(t,".cache","build.json")}async readBuildFile(t){try{let n=await ee.readJson(this.buildFile(t));return n&&n.status?n:null}catch{return null}}async writeBuildFile(t,n){let r=this.buildFile(t),s=`${r}.tmp`;try{await ee.outputJson(s,n),await ee.rename(s,r)}catch{}}watchBuildStatus(t){let n=this.synced.get(t);!n||!n.viewsDir||(n.buildWatcher=_n.watch(this.buildFile(n.viewsDir),{ignoreInitial:!1}).on("add",()=>{this.readBuildStatus(t)}).on("change",()=>{this.readBuildStatus(t)}))}async readBuildStatus(t){let n=this.synced.get(t);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(t){let n=this.synced.get(t);if(!n)return;let r=await dt(this.env,n.siteToken);if(!r)return;let s=this.synced.get(t);if(!s)return;let i=await this.readBuildFile(s.viewsDir);i?.at!=null&&(r.at==null||i.at>=r.at)||await this.writeBuildFile(s.viewsDir,r)}async refetch(t){await this.runSync(t,{flush:!0})}async runSync(t,n={}){let r=this.synced.get(t);if(!(!r||!r.viewsDir||r.status==="paused")){r.status="syncing",this.changed();try{let s=await Se({token:r.siteToken,env:this.env,viewsDir:r.viewsDir,agentMd:this.agentMd,buildScript:this.buildScript,flush:n.flush,log:i=>this.appendLog(t,i),onProgress:(i,d)=>{r.progress={done:i,total:d},this.changed()}});if(this.noteRole(t,s.site.role),r.progress=void 0,r.status="watching",r.lastSyncedAt=Date.now(),n.flush)r.lastMsg=`re-fetched ${s.pulled} file(s)`;else{let i=[s.renamed>0?`renamed ${s.renamed}`:"",s.pushed>0?`pushed ${s.pushed} file(s)`:""].filter(Boolean);r.lastMsg=i.length>0?i.join(", "):"up to date"}}catch(s){if(vt(s))return void await this.revoke(t);let i=s;r.status="error",r.progress=void 0,r.lastMsg=i.body||i.message,this.appendLog(t,`\u274C ${i.body||i.message}`)}this.changed()}}async togglePause(t){let n=this.synced.get(t);!n||!n.viewsDir||(n.status==="paused"?(n.watcher=new Q({viewsDir:n.viewsDir,onSync:()=>this.runSync(t)}),n.watcher.start(),n.status="watching",this.appendLog(t,"\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(t,"\u23F8 paused")),this.changed())}async cleanupAll(){this.cancelConnect();for(let t of this.synced.values())t.watcher?.setShuttingDown(!0),await t.watcher?.stop(),await t.buildWatcher?.close(),await St(t.viewsDir)}async exit(){this.isShuttingDown||(this.isShuttingDown=!0,await this.cleanupAll(),this.emit("exit"))}};import{useInput as nr}from"ink";import{Fragment as Un,useEffect as te,useMemo as Jn,useRef as Hn,useState as L}from"react";import{Box as f,Text as c,useInput as zn,useStdout as qn}from"ink";import Yn from"ink-text-input";import{createRequire as Nn}from"node:module";var Wn=Nn(import.meta.url),ge=Wn("../package.json").version;import Gn from"node:os";function Re(e){if(!e)return"\u2014";let t=Gn.homedir();return e.startsWith(t)?"~"+e.slice(t.length):e}function Le(e){if(!e)return"\u2014";let t=Math.floor((Date.now()-e)/1e3);if(t<5)return"just now";if(t<60)return`${t}s ago`;let n=Math.floor(t/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 fe(e){return!e||e.total<=0?null:Math.round(e.done/e.total*100)}var he=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"];function De(e){return he[(e%he.length+he.length)%he.length]}function pe(e){switch(e){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 xt(e){if(!e)return{dot:"\xB7",color:"gray",label:"\u2014"};switch(e.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:e.mode==="live"?"live":"preview"}}}import{Fragment as je,jsx as o,jsxs as g}from"react/jsx-runtime";var Xn=(e,t)=>t<=0?0:Math.max(0,Math.min(e,t-1)),Kn=16,Vn=5,Qn="greenBright",Tt="blueBright",kt="magentaBright";function $t({k:e,bg:t=Qn}){return o(c,{color:"black",backgroundColor:t,children:` ${e} `})}function F({k:e,label:t,width:n=16,bg:r}){return g(f,{width:n,children:[o($t,{k:e,bg:r}),o(c,{color:"gray",children:` ${t}`})]})}function B({k:e,label:t,bg:n}){return g(f,{marginBottom:1,children:[o(f,{width:9,children:o($t,{k:e,bg:n})}),o(c,{color:"gray",children:t})]})}function Zn({launchers:e}){return g(f,{flexDirection:"column",borderStyle:"round",borderColor:"green",paddingX:2,paddingY:1,width:66,children:[g(f,{justifyContent:"space-between",children:[o(c,{color:"green",bold:!0,children:"KEYBOARD SHORTCUTS"}),o(c,{color:"gray",children:"? or esc to close"})]}),g(f,{marginTop:1,children:[g(f,{flexDirection:"column",width:34,children:[o(c,{color:"cyan",bold:!0,children:"Navigate"}),o(B,{k:"\u2191\u2193",label:"move selection"}),o(B,{k:"tab",label:"focus logs \xB7 \u2191\u2193 scroll"}),o(B,{k:"esc",label:"clear filter / back"}),o(f,{marginTop:1,children:o(c,{color:"cyan",bold:!0,children:"Global"})}),o(B,{k:"/",label:"search sites"}),o(B,{k:"a",label:"add a site (browser)"}),o(B,{k:"?",label:"toggle this help",bg:Tt}),o(B,{k:"ctrl+c",label:"quit"})]}),g(f,{flexDirection:"column",flexGrow:1,children:[o(c,{color:"cyan",bold:!0,children:"Site actions"}),o(B,{k:"s",label:"start sync"}),o(B,{k:"d",label:"disconnect"}),o(B,{k:"x",label:"forget site"}),o(B,{k:"p",label:"pause / resume sync"}),o(B,{k:"b",label:"build preview"}),o(B,{k:"B",label:"publish live"}),o(B,{k:"o",label:"open preview url"}),e.map((t,n)=>o(B,{k:String(n+1),label:`open in ${t.label}`,bg:kt},t.key))]})]})]})}function er({percent:e,width:t=28}){let n=Math.round(Math.max(0,Math.min(100,e))/100*t);return g(c,{children:[o(c,{color:"green",children:"\u2588".repeat(n)}),o(c,{color:"gray",children:"\u2591".repeat(Math.max(0,t-n))})]})}function tr({frame:e,width:t=24,seg:n=6}){let r=t+n,s=e%r-n,i="";for(let d=0;d<t;d++)i+=d>=s&&d<s+n?"\u25AE":"\u25AF";return o(c,{color:"yellow",children:i})}function Bt({controller:e}){let{stdout:t}=qn(),n=Jn(()=>Ze(),[]),[,r]=L(0),[s,i]=L(()=>new Date),[d,l]=L(null),[w,S]=L(""),[u,h]=L(!1),[x,m]=L(0),[b,k]=L(!1),[_,N]=L(!1),[C,M]=L("list"),[q,D]=L(0),Fe=()=>r(a=>a+1);te(()=>(e.on("change",Fe),()=>{e.off("change",Fe)}),[e]),te(()=>{let a,v=()=>{i(new Date),a=setTimeout(v,6e4-Date.now()%6e4)};return a=setTimeout(v,6e4-Date.now()%6e4),()=>clearTimeout(a)},[]);let Y=e.saved.map(a=>({org:a.org,site:{id:a.siteId,name:a.name,slug:a.slug},saved:a,entry:e.synced.get(a.siteId)})).sort((a,v)=>(a.entry?0:1)-(v.entry?0:1)),W=e.connecting,O=w.trim().toLowerCase(),E=O?Y.filter(a=>a.site.name.toLowerCase().includes(O)||(a.org.name||"").toLowerCase().includes(O)):Y,G=E.findIndex(a=>a.site.id===d);G<0&&(G=0);let $=E[G],T=$?.entry,ne=Math.max(3,(t?.rows??30)-26),I=T?.logs??[],me=Math.max(0,I.length-ne),Mt=Math.min(q,me),re=I.length-Mt,Oe=I.slice(Math.max(0,re-ne),re),Rt=$?.site.id;te(()=>D(0),[Rt]);let j=T?.lastBuild,we=j?.status==="building",ye=xt(j),se=we||!!W;if(!se){for(let a of e.synced.values())if(a.status==="syncing"){se=!0;break}}te(()=>{if(!se)return;let a=setInterval(()=>m(v=>v+1),120);return()=>clearInterval(a)},[se]);let Ie=E.findIndex(a=>!a.entry),U=Math.max(Vn,(t?.rows??30)-Kn),X=E.length,ie=X>U,Lt=Math.floor(U/2),K=ie?Math.max(0,Math.min(G-Lt,X-U)):0,be=Math.min(X,K+U),Dt=E.slice(K,be),Ne=Hn(!1);te(()=>{Ne.current||(Ne.current=!0,e.saved.length===0&&!e.connecting&&e.connectSite())},[e]);let oe=a=>{if(E.length===0)return;let v=Xn(G+a,E.length);l(E[v].site.id)},jt=()=>{if($){if($.entry){e.flash("Already syncing \u2014 press d to disconnect.");return}e.startSite($.saved)}},At=()=>{$?.entry&&e.removeSite($.site.id)},_t=()=>{$&&e.forgetSite($.site.id)},Ft=a=>{if(!T){e.flash("Press s to start syncing this site first.",!0);return}N(!0);let v=a.run(T.viewsDir);e.flash(v?`Opened ${a.label} at ${Re(T.viewsDir)}`:`Couldn't open ${a.label}.`,!v)},Ot=()=>{if(!T){e.flash("Press s to start syncing this site first.",!0);return}e.togglePause(T.site.id)},We=a=>{if(!T){e.flash("Press s to start syncing this site first.",!0);return}e.buildSite(T.site.id,a),e.flash(a?"Publishing \u2014 making this live\u2026":"Building preview\u2026")},It=()=>{let a=T?.lastBuild?.url;if(!a){e.flash("No build URL yet \u2014 press b to build.",!0);return}e.flash(ce(a)?`Opening ${a}`:"Couldn't open the browser.",!1)};zn((a,v)=>{if(b){(a==="?"||v.escape)&&k(!1);return}if(W&&v.escape)return void e.cancelConnect();if(u){v.escape?(h(!1),S(""),l(null)):v.return?h(!1):v.upArrow?oe(-1):v.downArrow&&oe(1);return}if(v.tab){C==="logs"?M("list"):T&&M("logs"),D(0);return}if(C==="logs"){if(v.escape){M("list"),D(0);return}if(v.upArrow)return void D(A=>Math.min(A+1,me));if(v.downArrow)return void D(A=>Math.max(0,A-1))}if(v.escape){S(""),l(null);return}if(a==="?")return void k(!0);if(a==="/")return M("list"),void h(!0);if(a==="a"||a==="n")return void e.connectSite();if(v.upArrow)return oe(-1);if(v.downArrow)return oe(1);if(a==="s")return jt();if(a==="d")return At();if(a==="x")return _t();if(a==="p")return Ot();if(a==="b")return We(!1);if(a==="B")return We(!0);if(a==="o")return It();if(a>="1"&&a<="9"){let A=n[parseInt(a,10)-1];if(A)return Ft(A)}});let Ge=g(f,{justifyContent:"space-between",paddingX:1,children:[g(f,{children:[o(c,{color:"green",bold:!0,children:"\u25C6 SleekCMS"}),o(c,{color:"gray",children:` v${ge} \u2502 `}),o(c,{children:e.env==="production"?"Sync":`Sync \xB7 ${e.env}`})]}),g(f,{children:[g(c,{color:"gray",children:[O?`${E.length}/${Y.length}`:Y.length," sites \xB7 "]}),g(c,{color:"green",children:[e.watchingCount()," watching"]}),o(c,{color:"gray",children:` \xB7 ${s.toTimeString().slice(0,5)}`})]})]});return b?g(f,{flexDirection:"column",children:[Ge,o(f,{marginTop:1,justifyContent:"center",children:o(Zn,{launchers:n})})]}):g(f,{flexDirection:"column",children:[Ge,g(f,{children:[g(f,{flexDirection:"column",borderStyle:"round",borderColor:"green",width:38,paddingX:1,children:[g(f,{justifyContent:"space-between",children:[o(c,{color:"green",bold:!0,children:"SITE WORKSPACES"}),Y.length>U&&!u&&!O?o(c,{color:"gray",children:"/ filter"}):null]}),u?g(f,{children:[o(c,{color:"cyan",children:"/ "}),o(Yn,{value:w,onChange:S,onSubmit:()=>h(!1),placeholder:"type to filter\u2026"})]}):O?o(c,{color:"gray",children:`/ ${w} \xB7 ${E.length} match${E.length===1?"":"es"}`}):null,o(f,{flexDirection:"column",marginTop:1,minHeight:ie?U+3:void 0,children:E.length===0?o(c,{color:"gray",children:O?`no matches for \u201C${w}\u201D`:"no sites yet \u2014 press a to add one"}):g(je,{children:[ie?o(c,{color:"gray",children:K>0?` \u2191 ${K} more`:" "}):null,Dt.map((a,v)=>{let A=K+v,Ue=a.entry?pe(a.entry.status):{dot:"\xB7",color:"gray",label:"idle"},Je=fe(a.entry?.progress),Nt=a.entry?a.entry.status==="syncing"&&Je!=null?`${Je}%`:a.entry.status==="paused"?"paused":"--":"",ae=A===G,Wt=A===Ie&&Ie>0;return g(Un,{children:[Wt?o(c,{color:"gray",children:"\u2500".repeat(34)}):null,g(f,{justifyContent:"space-between",children:[g(f,{children:[o(c,{color:ae?"green":"gray",children:ae?"\u276F ":" "}),g(c,{color:a.entry?.status==="watching"||a.entry?.status==="syncing"?"green":Ue.color,children:[a.entry?.status==="syncing"?De(x):a.entry?.status==="watching"?"\u25CF":Ue.dot," "]}),o(c,{color:ae?"white":a.entry?void 0:"gray",bold:ae,children:a.site.name})]}),o(c,{color:a.entry?.status==="syncing"?"green":"gray",children:Nt})]})]},a.site.id)}),ie?o(c,{color:"gray",children:be<X?` \u2193 ${X-be} more`:" "}):null]})}),o(f,{marginTop:1,children:o(c,{color:"gray",children:"+ add site \xB7 a"})})]}),g(f,{flexDirection:"column",flexGrow:1,marginLeft:1,children:[o(f,{flexDirection:"column",borderStyle:"round",borderColor:"gray",paddingX:1,children:$?g(je,{children:[g(c,{children:[$.org.name?g(c,{color:"gray",children:[$.org.name," / "]}):null,o(c,{color:"cyan",bold:!0,children:$.site.name})]}),g(f,{marginTop:1,children:[o(c,{color:"gray",children:"status "}),T?o(c,{color:pe(T.status).color,children:`: ${pe(T.status).label}`}):o(c,{color:"gray",children:": idle \u2014 not syncing (press s to start)"})]}),g(f,{children:[o(c,{color:"gray",children:"role "}),o(c,{children:T?.role??$.saved.role??"\u2014"}),(T?.role??$.saved.role??"ADMIN")!=="ADMIN"?o(c,{color:"gray",children:" \xB7 build & publish need ADMIN"}):null]}),g(f,{children:[o(c,{color:"gray",children:"path "}),o(c,{wrap:"truncate-end",children:T?.viewsDir?Re(T.viewsDir):"\u2014"})]}),g(f,{children:[o(c,{color:"gray",children:"synced "}),o(c,{children:Le(T?.lastSyncedAt)}),o(c,{color:"gray",children:" progress "}),o(c,{color:T?.status==="syncing"?"green":"gray",children:T?.status==="syncing"?`${fe(T.progress)??0}%`:"\u2014"})]}),o(f,{marginTop:1,children:o(er,{percent:T?T.status==="syncing"?fe(T.progress)??0:T.status==="watching"?100:0:0})}),T?g(je,{children:[g(f,{marginTop:1,children:[o(c,{color:"gray",children:"build "}),o(c,{color:ye.color,children:`${ye.dot} ${ye.label}`}),o(c,{color:"gray",children:" last "}),o(c,{children:we?"in progress":Le(j?.at)})]}),g(f,{children:[o(c,{color:"gray",children:"url "}),j?.url?o(c,{color:"cyan",wrap:"truncate-end",children:j.url}):o(c,{color:"gray",children:"\u2014"})]}),j?.status==="error"&&j.errors?.length?g(f,{children:[o(c,{color:"red",children:"\u258C "}),o(c,{color:"red",wrap:"truncate-end",children:`\u2717 ${j.errors[0]}`})]}):null,we?g(f,{children:[o(tr,{frame:x}),o(c,{color:"gray",children:` ${j?.stage??"building"}`})]}):null]}):null]}):o(c,{color:"gray",children:"No site selected."})}),W?g(f,{flexDirection:"column",borderStyle:"round",borderColor:"blue",paddingX:1,marginTop:1,flexGrow:1,children:[o(c,{color:"cyan",bold:!0,children:"ADD A SITE"}),o(c,{children:"Approve it in your browser: pick a site, or create one."}),g(f,{marginTop:1,children:[o(c,{color:"gray",children:"Check the page shows "}),o(c,{color:"cyan",bold:!0,children:W.code})]}),o(c,{color:"gray",children:"Only approve a page that shows this code."}),g(f,{marginTop:1,children:[g(c,{color:"yellow",children:[De(x)," "]}),o(c,{color:"gray",children:"waiting for approval \xB7 esc to cancel"})]})]}):g(f,{flexDirection:"column",borderStyle:"round",borderColor:C==="logs"?"green":"gray",paddingX:1,marginTop:1,height:ne+4,flexShrink:0,overflowY:"hidden",children:[g(f,{justifyContent:"space-between",children:[g(c,{children:[o(c,{color:"cyan",bold:!0,children:"LOGS \xB7 "}),o(c,{color:"white",children:$?.site.name??"\u2014"})]}),T&&I.length>0?C==="logs"?o(c,{color:"green",children:`\u2191\u2193 scroll \xB7 esc back${me>0?` \xB7 ${re}/${I.length}`:""}`}):I.length>ne?o(c,{color:"gray",children:"tab to scroll"}):null:null]}),o(f,{flexDirection:"column",marginTop:1,children:!T||I.length===0?o(c,{color:"gray",children:"no activity yet"}):Oe.map((a,v)=>g(c,{wrap:"truncate-end",children:[g(c,{color:"gray",children:[a.time," "]}),a.text]},re-Oe.length+v))})]})]})]}),W?.url?g(f,{flexDirection:"column",paddingX:1,marginTop:1,children:[o(c,{color:"gray",children:"No browser? Open this address on any device:"}),o(c,{color:"cyan",children:W.url})]}):null,e.message?o(f,{paddingX:1,children:g(c,{color:e.messageIsError?"red":"gray",wrap:"truncate-end",children:[e.messageIsError?"\u2717 ":"",e.message]})}):null,g(f,{flexDirection:"column",marginTop:1,children:[g(f,{paddingX:1,justifyContent:"space-between",children:[g(f,{flexWrap:"wrap",children:[o(F,{k:"\u2191\u2193",label:"select"}),o(F,{k:"/",label:"search"}),o(F,{k:"s",label:"start sync"}),o(F,{k:"d",label:"disconnect"}),o(F,{k:"a",label:"add site"}),o(F,{k:"ctrl+c",label:"quit",width:14})]}),o(F,{k:"?",label:"all keys",width:12,bg:Tt})]}),!_&&n.length>0?o(f,{paddingX:1,marginTop:1,flexWrap:"wrap",children:n.map((a,v)=>o(F,{k:String(v+1),label:a.label,bg:kt},a.key))}):null]})]})}import{jsx as rr}from"react/jsx-runtime";function Pt({controller:e}){return nr((t,n)=>{n.ctrl&&t==="c"&&e.exit()}),rr(Bt,{controller:e})}import{jsx as lr}from"react/jsx-runtime";var Ae=Ke();Ae.version&&(console.log(ge),process.exit(0));var Ct=_e.dirname(sr(import.meta.url)),or=Et(_e.join(Ct,"AGENTS.md"),"utf-8"),ar=Et(_e.join(Ct,"build.mjs"),"utf-8");async function cr(){let e=new de(or,ar);e.basePath=Ae.path,await e.init((Ae.env??"production").toLowerCase()),process.stdout.isTTY&&process.stdout.write("\x1B[2J\x1B[3J\x1B[H");let t=ir(lr(Pt,{controller:e}),{exitOnCtrlC:!1}),n=r=>{t.unmount(),console.log(r),process.exit(0)};e.once("exit",()=>n("\u2713 Stopped. Local site files were cleaned up.")),process.on("SIGINT",()=>{e.exit()}),process.on("SIGTERM",()=>{e.exit()}),await t.waitUntilExit()}cr().catch(e=>{let t=e;console.error("\u274C",t.body||t.message),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleekcms/sync",
3
- "version": "3.4.0",
3
+ "version": "3.5.0",
4
4
  "description": "Edit SleekCMS sites locally — models, content, templates, images — with live two-way sync and AI agent support.",
5
5
  "keywords": [
6
6
  "sleekcms",