@sleekcms/sync 3.5.0 → 4.1.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
@@ -414,7 +414,7 @@ Content lives in `src/content/` as plain JSON. Edit any content file and save
414
414
 
415
415
  ## Site setup (`src/setup.json`)
416
416
 
417
- Site-wide decisions made once — the default page title, whether Tailwind is compiled, whether a sitemap is published, the cookie consent switch, and analytics tag ids — live in `src/setup.json`, mirrored from **Settings → Website** in the CMS. The file is JSON with a comment above each key explaining it:
417
+ Site-wide decisions made once — the default page title, whether Tailwind is compiled, whether a sitemap is published, the cookie consent switch, the chat bot switch, and analytics tag ids — 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
418
 
419
419
  ```json5
420
420
  {
@@ -422,6 +422,7 @@ Site-wide decisions made once — the default page title, whether Tailwind is co
422
422
  tw_enabled: true, // compile src/public/css/tailwind.css and inject it into every page
423
423
  sitemap_enabled: false, // publish sitemap.xml + robots.txt on every build
424
424
  cookie_consent: false, // consent banner; holds analytics + data-sleekcms-consent scripts until accepted
425
+ chat_enabled: false, // chat bot on every page, answering from the site's published pages
425
426
  analytics: { // tag ids injected into every page; include only the ones you use
426
427
  ga4: "G-XXXXXXXXXX",
427
428
  },
@@ -501,6 +502,10 @@ Inside `markdown` fields, the same shortcuts work in standard image syntax. Appe
501
502
  | Changes not syncing | Check terminal output; changes are debounced by 5 seconds |
502
503
  | Workspace disappeared | Expected — the local site files are wiped on `Ctrl+C` or `d`. Your edits are already on the server; re-run the CLI to pull a fresh copy. |
503
504
 
505
+ ## Crash reports
506
+
507
+ If the CLI crashes, it sends SleekCMS the error and its stack trace — no file contents, and your home folder is replaced with `~`. Set `DO_NOT_TRACK=1` to turn this off.
508
+
504
509
  ---
505
510
 
506
511
  ## License
package/dist/AGENTS.md CHANGED
@@ -118,6 +118,7 @@ setup.json Site setup (JSON, comments allowed): default pag
118
118
  | `tw_enabled` | boolean | `true` compiles `css/tailwind.css` and injects it into every page — required for any site styled with Tailwind utilities. |
119
119
  | `sitemap_enabled` | boolean | `true` publishes `sitemap.xml` + `robots.txt` on every build. Turn it on for public sites and SEO work. |
120
120
  | `cookie_consent` | boolean | `true` injects a consent banner into every page and holds the analytics tags, plus any script or iframe tagged `data-sleekcms-consent`, until the visitor accepts its category. A toggle only — banner text, position and styling are authored in HTML (§14). |
121
+ | `chat_enabled` | boolean | `true` adds the chat bot to every published page; it answers visitor questions from the site's published pages (re-read on every publish). A toggle only — the welcome message, knowledge files and instructions are managed in the CMS. `false` removes it from the next publish and stops open chats right away. |
121
122
  | `analytics` | object | Marketing/analytics tag ids injected into every published page. |
122
123
 
123
124
  `analytics` is a flat map — include only the keys the site uses:
@@ -203,7 +204,7 @@ Like JSON but with **no quotes**; each value is a field type name:
203
204
  |---|---|
204
205
  | `text`, `paragraph`, `code`, `color`, `link` | String |
205
206
  | `richtext` | String of **HTML** — output with `<%- %>` |
206
- | `markdown` | String of **raw markdown** — convert with `<%- remark(item.x) %>` (see Helpers — rendering) |
207
+ | `markdown` | String of **raw markdown** — convert with `<%- remark(item.x) %>`, which renders GFM (tables, task lists, footnotes), syntax-highlighted code fences, math (`$…$`, `$$…$$`) and ```` ```mermaid ```` diagrams as SVG (see Helpers — rendering) |
207
208
  | `number` | Number |
208
209
  | `boolean` | `true` / `false` |
209
210
  | `emoji` | One emoji character string, e.g. `"😀"` (not free text) |
@@ -405,11 +406,11 @@ There is exactly **one** layout: `layouts/common.ejs`. It wraps every page autom
405
406
  | `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
407
  | `remark(md, opts?)` | Markdown string → HTML string, with build-time highlighted code blocks, math and Mermaid diagrams (below) |
407
408
 
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.
409
+ 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 as SVG at build time. It injects only the CSS/JS the rendered markdown needs (KaTeX CSS, the code theme, the diagram styles). **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.
409
410
 
410
411
  - `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.
411
412
  - 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 `\$`.
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.
413
+ - Diagrams use Mermaid syntax (flowchart, sequence, class, state, ER, Gantt, pie and the other built-in types) and match the page's light or dark design on their own: don't style or theme them (no `theme` config, no CSS). Labels are plain text — no HTML.
413
414
 
414
415
  ```ejs
415
416
  <div class="prose prose-lg max-w-none"><%- remark(item.body) %></div>
package/dist/index.js CHANGED
@@ -1,10 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import{readFileSync as Et}from"node:fs";import{fileURLToPath as sr}from"node:url";import _e from"node:path";import{render as ir}from"ink";import{execSync as Xe,spawn as P}from"child_process";import{program as He,Option as ze}from"commander";function Ke(){return He.name("cms-sync").description("SleekCMS CLI tool to sync and edit CMS templates locally. Add each site you want to sync by approving it in your browser \u2014 changes are watched and pushed automatically.").addOption(new ze("-v, --version","output the version number").hideHelp()).addOption(new ze("-e, --env <env>","Environment (localhost, development, production)").default("production").hideHelp()).option("-p, --path <path>","Directory path for files (default: ~/SleekCMS)").addHelpText("after",`
2
+ import{readFileSync as Jt}from"node:fs";import{fileURLToPath as Ar}from"node:url";import Ge from"node:path";import{render as _r}from"ink";import{execSync as nt,spawn as B}from"child_process";import{program as Ze,Option as Qe}from"commander";function rt(){return Ze.name("cms-sync").description("SleekCMS CLI tool to sync and edit CMS templates locally. Add each site you want to sync by approving it in your browser \u2014 changes are watched and pushed automatically.").addOption(new Qe("-v, --version","output the version number").hideHelp()).addOption(new Qe("-e, --env <env>","Environment (localhost, development, production)").default("production").hideHelp()).option("-p, --path <path>","Directory path for files (default: ~/SleekCMS)").addHelpText("after",`
3
3
  Examples:
4
4
  $ sleekcms # add sites in your browser, pick which to sync
5
5
  $ sleekcms -e development # use a non-production backend
6
- `).parse(process.argv),He.opts()}function R(e){let t=process.platform==="win32"?`where ${e}`:`which ${e}`;try{return Xe(t,{stdio:"ignore"}),!0}catch{return!1}}function Ve(e){if(process.platform!=="darwin")return!1;try{return Xe(`osascript -e 'id of application "${e}"'`,{stdio:"ignore"}),!0}catch{return!1}}function Gt(e,t){let n={detached:!0,stdio:"ignore",windowsHide:!0};process.platform==="win32"?P("cmd.exe",["/c",e,...t],n).unref():P(e,t,n).unref()}var qe=[{name:"VS Code",cmd:"code",args:e=>["-n",e]},{name:"Cursor",cmd:"cursor",args:e=>["-n",e]}];function Ye(e,t){let r=(t?qe.filter(s=>s.cmd===t):qe).find(s=>R(s.cmd));return r?(Gt(r.cmd,r.args(e)),r.name):null}function Ut(e){if(process.platform==="darwin")return P("open",["-a","Terminal",e],{detached:!0,stdio:"ignore"}).unref(),!0;if(process.platform==="win32"){if(R("wt"))P("wt.exe",["-d",e],{detached:!0,stdio:"ignore"}).unref();else{let t=R("powershell")?"powershell.exe":"cmd.exe";P("cmd.exe",["/c","start","",t],{cwd:e,detached:!0,stdio:"ignore"}).unref()}return!0}return!1}function Jt(e){return Ve("iTerm")?(P("open",["-a","iTerm",e],{detached:!0,stdio:"ignore"}).unref(),!0):!1}function Ht(e){return`'${e.replace(/'/g,"'\\''")}'`}var zt=[{cmd:"x-terminal-emulator",args:e=>["-e","bash","-c",e]},{cmd:"gnome-terminal",args:e=>["--","bash","-c",e]},{cmd:"konsole",args:e=>["-e","bash","-c",e]},{cmd:"xfce4-terminal",args:e=>["-x","bash","-c",e]},{cmd:"kitty",args:e=>["bash","-c",e]},{cmd:"alacritty",args:e=>["-e","bash","-c",e]},{cmd:"xterm",args:e=>["-e","bash","-c",e]}];function Qe(e,t){if(!R(t))return!1;if(process.platform==="darwin"){let r=`tell application "Terminal" to do script ("cd " & quoted form of "${e}" & " && ${t}")`;return P("osascript",["-e",r,"-e",'tell application "Terminal" to activate'],{detached:!0,stdio:"ignore"}).unref(),!0}if(process.platform==="win32")return R("wt")?P("wt.exe",["-d",e,"cmd","/k",t],{detached:!0,stdio:"ignore"}).unref():P("cmd.exe",["/c","start","","cmd","/k",t],{cwd:e,detached:!0,stdio:"ignore"}).unref(),!0;let n=`cd ${Ht(e)} && ${t}; exec bash`;for(let r of zt)if(R(r.cmd))try{return P(r.cmd,r.args(n),{cwd:e,detached:!0,stdio:"ignore"}).unref(),!0}catch{}return!1}function qt(e){return Qe(e,"claude")}function Yt(e){return Qe(e,"pi")}function ce(e){if(!e)return!1;try{let t=process.platform==="darwin"?P("open",[e],{detached:!0,stdio:"ignore"}):process.platform==="win32"?P("cmd.exe",["/c","start","",e],{detached:!0,stdio:"ignore",windowsHide:!0}):P("xdg-open",[e],{detached:!0,stdio:"ignore"});return t.on("error",()=>{}),t.unref(),!0}catch{return!1}}function Ze(){let e=process.platform==="darwin",t=process.platform==="win32",n=[];return(e||t)&&n.push({key:"terminal",label:"terminal",run:Ut}),Ve("iTerm")&&n.push({key:"iterm",label:"iterm",run:Jt}),R("code")&&n.push({key:"code",label:"vscode",run:r=>Ye(r,"code")!==null}),R("cursor")&&n.push({key:"cursor",label:"cursor",run:r=>Ye(r,"cursor")!==null}),R("claude")&&n.push({key:"claude",label:"claude",run:qt}),R("pi")&&n.push({key:"pi",label:"pi",run:Yt}),n}import{EventEmitter as jn}from"node:events";import{spawn as An}from"node:child_process";import ee from"fs-extra";import Me from"path";import _n from"chokidar";import{createHash as Kt}from"crypto";import p from"fs-extra";import Vt from"os";import y from"path";var et={localhost:"http://app.sleekcms.test/api/mcp",development:"https://app.sleekcms.dev/api/mcp",production:"https://app.sleekcms.com/api/mcp"},Qt=["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"],J=class extends Error{status;body;constructor(t,n,r){super(t),this.status=n,this.body=r}};async function le(e,t,n,r,s){let i=await fetch(e+r,{method:n,headers:{Authorization:`Bearer ${t}`,...s!==void 0?{"Content-Type":"application/json"}:{}},body:s!==void 0?JSON.stringify(s):void 0});if(!i.ok){let d=await i.text();throw new J(`${n} ${r} \u2192 ${i.status}: ${d}`,i.status,d)}return i.json()}var Zt=["AGENT.md","CLAUDE.md",y.join(".vscode","copilot-instructions.md")],en="mrmlnc.vscode-json5";async function tn(e,t){t&&(await p.outputFile(y.join(e,"AGENTS.md"),t),await Promise.all(Zt.map(n=>p.remove(y.join(e,n))))),await p.outputFile(y.join(e,".vscode","settings.json"),JSON.stringify({"files.associations":{"*.model":"javascript","**/src/**/*.json":"json5"},"[javascript]":{"editor.defaultFormatter":"esbenp.prettier-vscode"},"js/ts.validate.enabled":!1},null,2)),await p.outputFile(y.join(e,".vscode","extensions.json"),JSON.stringify({recommendations:[en]},null,2))}async function nn(e,t){if(!t)return;let n=y.join(e,"build.mjs"),r=t.match(/build\.mjs v\d+/)?.[0];if(await p.pathExists(n)){let s=(await p.readFile(n,"utf-8")).match(/build\.mjs v\d+/)?.[0];if(s&&s===r)return}await p.outputFile(n,t)}async function rn(e,t,n,r){let s=y.join(e,".cache","site.json"),i=await p.readJson(s).catch(()=>null);i&&(i.env!==n.env||i.slug!==n.slug)&&(r?.("\u267B\uFE0F Workspace was tied to a different site \u2014 recreating it."),await p.emptyDir(e)),await p.outputJson(s,n),await p.outputFile(y.join(e,".cache","token"),t)}function sn(e,t){let n=`${t.name.substr(0,20)} ${t.id}`.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,""),r=e||y.join(Vt.homedir(),"SleekCMS");return y.resolve(r,n)}async function Se(e){let t=(e.token||"").trim();if(!t)throw new Error("syncSite: token is required");let n=e.log??(b=>console.log(b)),r=e.onProgress,s=(e.env||"production").toLowerCase(),i=et[s]||et.production,d=await le(i,t,"GET","/get_site"),l=e.viewsDir?y.resolve(e.viewsDir):sn(e.path,d);await p.ensureDir(l),await rn(l,t,{env:s,slug:String(d.id)},n),await Promise.all(Qt.map(b=>p.ensureDir(y.join(l,b)))),await nn(l,e.buildScript);let w=y.join(l,".cache","state.json");e.flush&&await p.remove(w);let S=!await p.pathExists(w),u=S?{}:(await p.readJson(w)).fileMap||{},h=0,x=0,m=0;if(S)({fileMap:u,pulled:x}=await vn(l,i,t,n,r)),await tn(l,e.agentMd);else{let b=await yn(l,u,i,t,n);m=b.renamed,h=await cn(l,u,i,t,n,r,b.held)}return await p.outputJson(w,{fileMap:u},{spaces:2}),{viewsDir:l,site:d,isFirstRun:S,pushed:h,pulled:x,renamed:m}}var V=e=>Kt("sha1").update(e).digest("hex"),on=new Set([".DS_Store","Thumbs.db","desktop.ini"]);async function xe(e){let t=y.join(e,"src");if(!await p.pathExists(t))return[];let n=[];async function r(s){let i=await p.readdir(s,{withFileTypes:!0});for(let d of i){if(on.has(d.name))continue;let l=y.join(s,d.name);d.isDirectory()?await r(l):d.isFile()&&n.push(y.relative(e,l).replace(/\\/g,"/"))}}return await r(t),n}function tt(e){let t=e.split(y.sep).join("/");return t==="src/setup.json"?0:t==="src/content/images.json"?1:t==="src/content/videos.json"?2:t.startsWith("src/models/")?3:t.startsWith("src/content/")?4:5}var nt=10;function an(e){let t=[];for(let n=0;n<e.length;n+=nt)t.push(e.slice(n,n+nt));return t}async function cn(e,t,n,r,s,i,d=new Set){let l=[];for(let h of await xe(e)){if(d.has(h))continue;let x=y.join(e,h),m=t[h],b=await p.stat(x);if(m&&m.mtimeMs===b.mtimeMs){m.hash||(m.hash=V(await p.readFile(x,"utf-8")));continue}let k=await p.readFile(x,"utf-8");k.trim()&&l.push({rel:h,full:x,stat:b,content:k,prior:m})}if(l.length===0)return 0;l.sort((h,x)=>tt(h.rel)-tt(x.rel)),s(`\u2B06 pushing ${l.length} file(s)`),i?.(0,l.length);let w=await ot(e),S=0,u=0;for(let h of an(l)){let x;try{x=await le(n,r,"POST","/save_files",h.map(b=>({path:b.rel,content:b.content})))}catch(b){s(`\u274C Error saving files: ${wn(b)}`),s(`\u23F8 ${l.length-u} file(s) not pushed \u2014 saving any file retries them.`);break}let m=ln(x);for(let b=0;b<h.length;b++){let k=h[b],_=un(k,b,x,m);if(!_){let N="No save response returned";w[k.rel]=N,s(`\u274C Error saving ${k.rel}: ${N}`);continue}if(_.error){w[k.rel]=_.error,s(`\u274C Error saving ${k.rel}: ${_.error}`);continue}delete w[k.rel],t[k.rel]={mtimeMs:k.stat.mtimeMs,hash:V(k.content)},s(`\u2705 ${k.prior?"Updated":"Created"} ${k.rel}`),S++}u+=h.length,i?.(u,l.length)}return u===0?0:(await at(e,w),S)}function ln(e){let t=new Map;for(let n of e)n.path&&t.set(n.path,n);return t}function un(e,t,n,r){let s=r.get(e.rel);if(s)return s;let i=n[t];if(!i?.path||i.path===e.rel)return i}var rt=/^src\/(?:(?:models|views)\/(?:pages|entries|blocks)|content\/(?:pages|entries))\//,dn={PAGE:"pages",ENTRY:"entries",BLOCK:"blocks"},gn=e=>e.split("/").slice(0,3).join("/");function hn(e,t,n){let r=new Map,s=(i,d)=>{let l=`${gn(i)}|${d}`;return r.has(l)||r.set(l,{from:[],to:[],hash:d}),r.get(l)};for(let i of e)s(i,n[i].hash).from.push(i);for(let[i,d]of t)s(i,d).to.push(i);return[...r.values()].filter(i=>i.from.length===1&&i.to.length===1).map(i=>({from:i.from[0],to:i.to[0],hash:i.hash}))}function fn(e,t){let n=new Map,r=(s,i,d,l,w)=>{let S=n.get(s);S?(S.pairs.push(w),i<S.rank&&Object.assign(S,{from:d,to:l,rank:i})):n.set(s,{from:d,to:l,rank:i,pairs:[w]})};for(let s of e){let[,i,d,...l]=s.from.split("/"),w=s.to.split("/").slice(3);if(i==="content"&&d==="pages"&&l.length>1){let[u,...h]=l,[x,...m]=w;if(m.length===0)continue;x===u?r(`record:${s.from}`,0,s.from,s.to,s):h.join("/")===m.join("/")&&![...t].some(b=>b.startsWith(`src/content/pages/${u}/`))&&r(`pages:${u}`,3,`src/content/pages/${u}`,`src/content/pages/${x}`,s);continue}let S=l.join("/").replace(/\.[^./]+$/,"");r(`${d}:${S}`,i==="models"?0:i==="views"?1:2,s.from,s.to,s)}return[...n.values()].map(({from:s,to:i,pairs:d})=>({from:s,to:i,pairs:d}))}function st(e,t){if(e.slug!==void 0&&e.new_slug!==void 0){let d=`src/content/pages/${e.key}/`,l=t.startsWith(d)?t.slice(d.length).match(/^(.+)\.(json|md)$/):null;return l&&l[1].toLowerCase()===e.slug.toLowerCase()?`${d}${e.new_slug}.${l[2]}`:null}let n=dn[e.type],{key:r,new_key:s}=e;if(!n||!s)return null;if(t===`src/models/${n}/${r}.model`)return`src/models/${n}/${s}.model`;if(t===`src/views/${n}/${r}.ejs`)return`src/views/${n}/${s}.ejs`;if(n!=="blocks"&&(t===`src/content/${n}/${r}.json`||t===`src/content/${n}/${r}.md`))return`src/content/${n}/${s}${y.extname(t)}`;let i=`src/content/pages/${r}/`;return n==="pages"&&r.endsWith("+")&&t.startsWith(i)?`src/content/pages/${s}/${t.slice(i.length)}`:null}async function pn(e,t){let n=e;for(;n.startsWith(t+y.sep);){let r=await p.readdir(n).catch(()=>null);if(!r||r.length>0)return;await p.remove(n),n=y.dirname(n)}}async function mn(e,t,n,r){let s=u=>y.join(e,u),i=y.join(e,"src"),d=u=>y.resolve(e,u).startsWith(i+y.sep),l=async(u,h)=>{t[u]={mtimeMs:(await p.stat(s(u))).mtimeMs,hash:h}},w=u=>pn(y.dirname(s(u)),y.join(e,...u.split("/").slice(0,3))),S=[];for(let u of r){let h=st(n.rename,u.from)??u.to;d(h)&&(h!==u.to&&(await p.move(s(u.to),s(h),{overwrite:!0}),await w(u.to),S.push(u.to)),delete t[u.from],S.push(u.from),await l(h,u.hash))}for(let u of new Set([...await xe(e),...Object.keys(t)])){let h=st(n.rename,u);if(!h||!d(h))continue;let x=await p.pathExists(s(u));if(x&&await p.pathExists(s(h)))continue;let m=t[u];if(delete t[u],S.push(u),!x)continue;let b=!!m?.hash&&m.mtimeMs===(await p.stat(s(u))).mtimeMs;await p.move(s(u),s(h)),b&&await l(h,m.hash),await w(u)}for(let u of n.files||[]){let h=t[u.path];if(!h||!d(u.path))continue;let x=await p.stat(s(u.path)).catch(()=>null);!x||x.mtimeMs!==h.mtimeMs||await p.readFile(s(u.path),"utf-8")!==u.content&&(await p.outputFile(s(u.path),u.content),await l(u.path,V(u.content)))}return S}function it(e){try{let t=JSON.parse(e.body).message;if(t)return t}catch{}return(e.body||e.message||String(e)).slice(0,300)}function wn(e){return e instanceof J?e.body.match(/<title>([^<]*)<\/title>/i)?.[1].trim()||it(e).replace(/\s+/g," "):e instanceof Error?e.message:String(e)}async function yn(e,t,n,r,s){let i=new Set,d=await xe(e),l=new Set(d),w=Object.keys(t).filter(m=>!l.has(m)&&t[m].hash&&rt.test(m));if(w.length===0)return{renamed:0,held:i};let S=new Map;for(let m of d){if(t[m]||!rt.test(m))continue;let b=await p.readFile(y.join(e,m),"utf-8");b.trim()&&S.set(m,V(b))}let u=fn(hn(w,S,t),l);if(u.length===0)return{renamed:0,held:i};let h=await ot(e),x=0;for(let m of u){let b;try{b=await le(n,r,"POST","/rename",{from:m.from,to:m.to})}catch(C){let M=C,q=it(M);if(M.status>=400&&M.status<500)s(`\u26A0\uFE0F Couldn't rename ${m.from} \u2192 ${m.to}: ${q} Saving it as a new file.`);else{for(let D of m.pairs)i.add(D.to),h[D.to]=`rename from ${D.from} failed: ${q}`;s(`\u274C Error renaming ${m.from} \u2192 ${m.to}: ${q}`)}continue}let k=await mn(e,t,b,m.pairs);for(let C of[...k,...m.pairs.map(M=>M.to)])delete h[C];let[_,...N]=b.message.split(`
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
- `)){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(`
6
+ `).parse(process.argv),Ze.opts()}function R(e){let t=process.platform==="win32"?`where ${e}`:`which ${e}`;try{return nt(t,{stdio:"ignore"}),!0}catch{return!1}}function st(e){if(process.platform!=="darwin")return!1;try{return nt(`osascript -e 'id of application "${e}"'`,{stdio:"ignore"}),!0}catch{return!1}}function an(e,t){let n={detached:!0,stdio:"ignore",windowsHide:!0};process.platform==="win32"?B("cmd.exe",["/c",e,...t],n).unref():B(e,t,n).unref()}var et=[{name:"VS Code",cmd:"code",args:e=>["-n",e]},{name:"Cursor",cmd:"cursor",args:e=>["-n",e]}];function tt(e,t){let r=(t?et.filter(s=>s.cmd===t):et).find(s=>R(s.cmd));return r?(an(r.cmd,r.args(e)),r.name):null}function cn(e){if(process.platform==="darwin")return B("open",["-a","Terminal",e],{detached:!0,stdio:"ignore"}).unref(),!0;if(process.platform==="win32"){if(R("wt"))B("wt.exe",["-d",e],{detached:!0,stdio:"ignore"}).unref();else{let t=R("powershell")?"powershell.exe":"cmd.exe";B("cmd.exe",["/c","start","",t],{cwd:e,detached:!0,stdio:"ignore"}).unref()}return!0}return!1}function ln(e){return st("iTerm")?(B("open",["-a","iTerm",e],{detached:!0,stdio:"ignore"}).unref(),!0):!1}function un(e){return`'${e.replace(/'/g,"'\\''")}'`}var dn=[{cmd:"x-terminal-emulator",args:e=>["-e","bash","-c",e]},{cmd:"gnome-terminal",args:e=>["--","bash","-c",e]},{cmd:"konsole",args:e=>["-e","bash","-c",e]},{cmd:"xfce4-terminal",args:e=>["-x","bash","-c",e]},{cmd:"kitty",args:e=>["bash","-c",e]},{cmd:"alacritty",args:e=>["-e","bash","-c",e]},{cmd:"xterm",args:e=>["-e","bash","-c",e]}];function it(e,t){if(!R(t))return!1;if(process.platform==="darwin"){let r=`tell application "Terminal" to do script ("cd " & quoted form of "${e}" & " && ${t}")`;return B("osascript",["-e",r,"-e",'tell application "Terminal" to activate'],{detached:!0,stdio:"ignore"}).unref(),!0}if(process.platform==="win32")return R("wt")?B("wt.exe",["-d",e,"cmd","/k",t],{detached:!0,stdio:"ignore"}).unref():B("cmd.exe",["/c","start","","cmd","/k",t],{cwd:e,detached:!0,stdio:"ignore"}).unref(),!0;let n=`cd ${un(e)} && ${t}; exec bash`;for(let r of dn)if(R(r.cmd))try{return B(r.cmd,r.args(n),{cwd:e,detached:!0,stdio:"ignore"}).unref(),!0}catch{}return!1}function gn(e){return it(e,"claude")}function pn(e){return it(e,"pi")}function ue(e){if(!e)return!1;try{let t=process.platform==="darwin"?B("open",[e],{detached:!0,stdio:"ignore"}):process.platform==="win32"?B("cmd.exe",["/c","start","",e],{detached:!0,stdio:"ignore",windowsHide:!0}):B("xdg-open",[e],{detached:!0,stdio:"ignore"});return t.on("error",()=>{}),t.unref(),!0}catch{return!1}}function ot(){let e=process.platform==="darwin",t=process.platform==="win32",n=[];return(e||t)&&n.push({key:"terminal",label:"terminal",run:cn}),st("iTerm")&&n.push({key:"iterm",label:"iterm",run:ln}),R("code")&&n.push({key:"code",label:"vscode",run:r=>tt(r,"code")!==null}),R("cursor")&&n.push({key:"cursor",label:"cursor",run:r=>tt(r,"cursor")!==null}),R("claude")&&n.push({key:"claude",label:"claude",run:gn}),R("pi")&&n.push({key:"pi",label:"pi",run:pn}),n}import{EventEmitter as Zn}from"node:events";import{spawn as Qn}from"node:child_process";import te from"fs-extra";import Ae from"path";import er from"chokidar";import{createHash as hn}from"crypto";import h from"fs-extra";import mn from"os";import y from"path";var at={localhost:"http://app.sleekcms.test/api/mcp",development:"https://app.sleekcms.dev/api/mcp",production:"https://app.sleekcms.com/api/mcp"},wn=["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"],H=class extends Error{status;body;constructor(t,n,r){super(t),this.status=n,this.body=r}};async function de(e,t,n,r,s){let i=await fetch(e+r,{method:n,headers:{Authorization:`Bearer ${t}`,...s!==void 0?{"Content-Type":"application/json"}:{}},body:s!==void 0?JSON.stringify(s):void 0});if(!i.ok){let d=await i.text();throw new H(`${n} ${r} \u2192 ${i.status}: ${d}`,i.status,d)}return i.json()}var yn=["AGENT.md","CLAUDE.md",y.join(".vscode","copilot-instructions.md")],bn="mrmlnc.vscode-json5";async function vn(e,t){t&&(await h.outputFile(y.join(e,"AGENTS.md"),t),await Promise.all(yn.map(n=>h.remove(y.join(e,n))))),await h.outputFile(y.join(e,".vscode","settings.json"),JSON.stringify({"files.associations":{"*.model":"javascript","**/src/**/*.json":"json5"},"[javascript]":{"editor.defaultFormatter":"esbenp.prettier-vscode"},"js/ts.validate.enabled":!1},null,2)),await h.outputFile(y.join(e,".vscode","extensions.json"),JSON.stringify({recommendations:[bn]},null,2))}async function Sn(e,t){if(!t)return;let n=y.join(e,"build.mjs"),r=t.match(/build\.mjs v\d+/)?.[0];if(await h.pathExists(n)){let s=(await h.readFile(n,"utf-8")).match(/build\.mjs v\d+/)?.[0];if(s&&s===r)return}await h.outputFile(n,t)}async function xn(e,t,n,r){let s=y.join(e,".cache","site.json"),i=await h.readJson(s).catch(()=>null);i&&(i.env!==n.env||i.slug!==n.slug)&&(r?.("\u267B\uFE0F Workspace was tied to a different site \u2014 recreating it."),await h.emptyDir(e)),await h.outputJson(s,n),await h.outputFile(y.join(e,".cache","token"),t)}function Tn(e,t){let n=`${t.name.substr(0,20)} ${t.id}`.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,""),r=e||y.join(mn.homedir(),"SleekCMS");return y.resolve(r,n)}async function Ee(e){let t=(e.token||"").trim();if(!t)throw new Error("syncSite: token is required");let n=e.log??(b=>console.log(b)),r=e.onProgress,s=(e.env||"production").toLowerCase(),i=at[s]||at.production,d=await de(i,t,"GET","/get_site"),l=e.viewsDir?y.resolve(e.viewsDir):Tn(e.path,d);await h.ensureDir(l),await xn(l,t,{env:s,slug:String(d.id)},n),await Promise.all(wn.map(b=>h.ensureDir(y.join(l,b)))),await Sn(l,e.buildScript);let w=y.join(l,".cache","state.json");e.flush&&await h.remove(w);let S=!await h.pathExists(w),u=S?{}:(await h.readJson(w)).fileMap||{},p=0,x=0,m=0;if(S)({fileMap:u,pulled:x}=await In(l,i,t,n,r)),await vn(l,e.agentMd);else{let b=await _n(l,u,i,t,n);m=b.renamed,p=await En(l,u,i,t,n,r,b.held)}return await h.outputJson(w,{fileMap:u},{spaces:2}),{viewsDir:l,site:d,isFirstRun:S,pushed:p,pulled:x,renamed:m}}var Z=e=>hn("sha1").update(e).digest("hex"),kn=new Set([".DS_Store","Thumbs.db","desktop.ini"]);async function Be(e){let t=y.join(e,"src");if(!await h.pathExists(t))return[];let n=[];async function r(s){let i=await h.readdir(s,{withFileTypes:!0});for(let d of i){if(kn.has(d.name))continue;let l=y.join(s,d.name);d.isDirectory()?await r(l):d.isFile()&&n.push(y.relative(e,l).replace(/\\/g,"/"))}}return await r(t),n}function ct(e){let t=e.split(y.sep).join("/");return t==="src/setup.json"?0:t==="src/content/images.json"?1:t==="src/content/videos.json"?2:t.startsWith("src/models/")?3:t.startsWith("src/content/")?4:5}var lt=10;function $n(e){let t=[];for(let n=0;n<e.length;n+=lt)t.push(e.slice(n,n+lt));return t}async function En(e,t,n,r,s,i,d=new Set){let l=[];for(let p of await Be(e)){if(d.has(p))continue;let x=y.join(e,p),m=t[p],b=await h.stat(x);if(m&&m.mtimeMs===b.mtimeMs){m.hash||(m.hash=Z(await h.readFile(x,"utf-8")));continue}let k=await h.readFile(x,"utf-8");k.trim()&&l.push({rel:p,full:x,stat:b,content:k,prior:m})}if(l.length===0)return 0;l.sort((p,x)=>ct(p.rel)-ct(x.rel)),s(`\u2B06 pushing ${l.length} file(s)`),i?.(0,l.length);let w=await pt(e),S=0,u=0;for(let p of $n(l)){let x;try{x=await de(n,r,"POST","/save_files",p.map(b=>({path:b.rel,content:b.content})))}catch(b){s(`\u274C Error saving files: ${An(b)}`),s(`\u23F8 ${l.length-u} file(s) not pushed \u2014 saving any file retries them.`);break}let m=Bn(x);for(let b=0;b<p.length;b++){let k=p[b],_=Pn(k,b,x,m);if(!_){let N="No save response returned";w[k.rel]=N,s(`\u274C Error saving ${k.rel}: ${N}`);continue}if(_.error){w[k.rel]=_.error,s(`\u274C Error saving ${k.rel}: ${_.error}`);continue}delete w[k.rel],t[k.rel]={mtimeMs:k.stat.mtimeMs,hash:Z(k.content)},s(`\u2705 ${k.prior?"Updated":"Created"} ${k.rel}`),S++}u+=p.length,i?.(u,l.length)}return u===0?0:(await ft(e,w),S)}function Bn(e){let t=new Map;for(let n of e)n.path&&t.set(n.path,n);return t}function Pn(e,t,n,r){let s=r.get(e.rel);if(s)return s;let i=n[t];if(!i?.path||i.path===e.rel)return i}var ut=/^src\/(?:(?:models|views)\/(?:pages|entries|blocks)|content\/(?:pages|entries))\//,Cn={PAGE:"pages",ENTRY:"entries",BLOCK:"blocks"},Mn=e=>e.split("/").slice(0,3).join("/");function Rn(e,t,n){let r=new Map,s=(i,d)=>{let l=`${Mn(i)}|${d}`;return r.has(l)||r.set(l,{from:[],to:[],hash:d}),r.get(l)};for(let i of e)s(i,n[i].hash).from.push(i);for(let[i,d]of t)s(i,d).to.push(i);return[...r.values()].filter(i=>i.from.length===1&&i.to.length===1).map(i=>({from:i.from[0],to:i.to[0],hash:i.hash}))}function Ln(e,t){let n=new Map,r=(s,i,d,l,w)=>{let S=n.get(s);S?(S.pairs.push(w),i<S.rank&&Object.assign(S,{from:d,to:l,rank:i})):n.set(s,{from:d,to:l,rank:i,pairs:[w]})};for(let s of e){let[,i,d,...l]=s.from.split("/"),w=s.to.split("/").slice(3);if(i==="content"&&d==="pages"&&l.length>1){let[u,...p]=l,[x,...m]=w;if(m.length===0)continue;x===u?r(`record:${s.from}`,0,s.from,s.to,s):p.join("/")===m.join("/")&&![...t].some(b=>b.startsWith(`src/content/pages/${u}/`))&&r(`pages:${u}`,3,`src/content/pages/${u}`,`src/content/pages/${x}`,s);continue}let S=l.join("/").replace(/\.[^./]+$/,"");r(`${d}:${S}`,i==="models"?0:i==="views"?1:2,s.from,s.to,s)}return[...n.values()].map(({from:s,to:i,pairs:d})=>({from:s,to:i,pairs:d}))}function dt(e,t){if(e.slug!==void 0&&e.new_slug!==void 0){let d=`src/content/pages/${e.key}/`,l=t.startsWith(d)?t.slice(d.length).match(/^(.+)\.(json|md)$/):null;return l&&l[1].toLowerCase()===e.slug.toLowerCase()?`${d}${e.new_slug}.${l[2]}`:null}let n=Cn[e.type],{key:r,new_key:s}=e;if(!n||!s)return null;if(t===`src/models/${n}/${r}.model`)return`src/models/${n}/${s}.model`;if(t===`src/views/${n}/${r}.ejs`)return`src/views/${n}/${s}.ejs`;if(n!=="blocks"&&(t===`src/content/${n}/${r}.json`||t===`src/content/${n}/${r}.md`))return`src/content/${n}/${s}${y.extname(t)}`;let i=`src/content/pages/${r}/`;return n==="pages"&&r.endsWith("+")&&t.startsWith(i)?`src/content/pages/${s}/${t.slice(i.length)}`:null}async function jn(e,t){let n=e;for(;n.startsWith(t+y.sep);){let r=await h.readdir(n).catch(()=>null);if(!r||r.length>0)return;await h.remove(n),n=y.dirname(n)}}async function Dn(e,t,n,r){let s=u=>y.join(e,u),i=y.join(e,"src"),d=u=>y.resolve(e,u).startsWith(i+y.sep),l=async(u,p)=>{t[u]={mtimeMs:(await h.stat(s(u))).mtimeMs,hash:p}},w=u=>jn(y.dirname(s(u)),y.join(e,...u.split("/").slice(0,3))),S=[];for(let u of r){let p=dt(n.rename,u.from)??u.to;d(p)&&(p!==u.to&&(await h.move(s(u.to),s(p),{overwrite:!0}),await w(u.to),S.push(u.to)),delete t[u.from],S.push(u.from),await l(p,u.hash))}for(let u of new Set([...await Be(e),...Object.keys(t)])){let p=dt(n.rename,u);if(!p||!d(p))continue;let x=await h.pathExists(s(u));if(x&&await h.pathExists(s(p)))continue;let m=t[u];if(delete t[u],S.push(u),!x)continue;let b=!!m?.hash&&m.mtimeMs===(await h.stat(s(u))).mtimeMs;await h.move(s(u),s(p)),b&&await l(p,m.hash),await w(u)}for(let u of n.files||[]){let p=t[u.path];if(!p||!d(u.path))continue;let x=await h.stat(s(u.path)).catch(()=>null);!x||x.mtimeMs!==p.mtimeMs||await h.readFile(s(u.path),"utf-8")!==u.content&&(await h.outputFile(s(u.path),u.content),await l(u.path,Z(u.content)))}return S}function gt(e){try{let t=JSON.parse(e.body).message;if(t)return t}catch{}return(e.body||e.message||String(e)).slice(0,300)}function An(e){return e instanceof H?e.body.match(/<title>([^<]*)<\/title>/i)?.[1].trim()||gt(e).replace(/\s+/g," "):e instanceof Error?e.message:String(e)}async function _n(e,t,n,r,s){let i=new Set,d=await Be(e),l=new Set(d),w=Object.keys(t).filter(m=>!l.has(m)&&t[m].hash&&ut.test(m));if(w.length===0)return{renamed:0,held:i};let S=new Map;for(let m of d){if(t[m]||!ut.test(m))continue;let b=await h.readFile(y.join(e,m),"utf-8");b.trim()&&S.set(m,Z(b))}let u=Ln(Rn(w,S,t),l);if(u.length===0)return{renamed:0,held:i};let p=await pt(e),x=0;for(let m of u){let b;try{b=await de(n,r,"POST","/rename",{from:m.from,to:m.to})}catch(C){let M=C,Y=gt(M);if(M.status>=400&&M.status<500)s(`\u26A0\uFE0F Couldn't rename ${m.from} \u2192 ${m.to}: ${Y} Saving it as a new file.`);else{for(let j of m.pairs)i.add(j.to),p[j.to]=`rename from ${j.from} failed: ${Y}`;s(`\u274C Error renaming ${m.from} \u2192 ${m.to}: ${Y}`)}continue}let k=await Dn(e,t,b,m.pairs);for(let C of[...k,...m.pairs.map(M=>M.to)])delete p[C];let[_,...N]=b.message.split(`
7
+ `);s(`\u{1F500} ${_}`);for(let C of N)s(` ${C}`);x++}return await ft(e,p),{renamed:x,held:i}}var Pe="sync-errors.log",$e=new Map;async function pt(e){let t=y.resolve(e),n=$e.get(t);if(n)return n;let r=await On(t);return $e.set(t,r),r}async function On(e){let t=y.join(e,Pe);if(!await h.pathExists(t))return{};let n=await h.readFile(t,"utf-8"),r={};for(let s of n.split(`
8
+ `)){let i=s.indexOf(": ");i>0&&(r[s.slice(0,i)]=s.slice(i+2))}return r}async function ft(e,t){let n=y.join(e,Pe),r=Object.entries(t);if(r.length===0){await h.remove(n);return}await h.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},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)});
10
+ `)}async function In(e,t,n,r,s){$e.delete(y.resolve(e)),await h.remove(y.join(e,Pe)),r("\u{1F4E5} Fetching files...");let i=await de(t,n,"GET","/get_files"),d={},l=0;for(let w of i){let S=y.join(e,w.path);await h.outputFile(S,w.content);let u=(await h.stat(S)).mtimeMs;l++,s?.(l,i.length),d[w.path]={mtimeMs:u,hash:Z(w.content)}}return r(`\u2714\uFE0F Synced ${i.length} file(s).`),{fileMap:d,pulled:l}}import Fn from"path";import Nn from"chokidar";var Wn=5e3,Gn=/(^|[/\\])(\.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??Wn}start(){if(this.watcher)return;let t=Fn.join(this.viewsDir,"src");this.watcher=Nn.watch([t],{persistent:!0,ignoreInitial:!0,ignored:Gn}).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 Ce from"fs-extra";import Un from"os";import mt from"path";var ht={localhost:"http://app.sleekcms.test/api",development:"https://app.sleekcms.dev/api",production:"https://app.sleekcms.com/api"};function ee(e){return ht[e]||ht.production}function Re(e){return ee(e).replace(/\/api$/,"")}var Hn=mt.join(Un.homedir(),"SleekCMS"),J=mt.join(Hn,"auth.json"),Me=class extends Error{status;body;constructor(t,n,r=""){super(t),this.name="ApiError",this.status=n,this.body=r}};async function ge(e=J){let t=null;try{t=await Ce.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 wt(e,t=J){await Ce.outputJson(t,{sites:e},{spaces:2});try{await Ce.chmod(t,384)}catch{}}async function Le(e,t=J){let{sites:n}=await ge(t),r=[...n.filter(s=>!(s.env===e.env&&s.siteId===e.siteId)),e];return await wt(r,t),r}async function je(e,t,n=J){let{sites:r}=await ge(n),s=r.filter(i=>!(i.env===e&&i.siteId===t));return await wt(s,n),s}async function De(e,t,n,r){let s=await fetch(ee(e)+r,{method:n,headers:{Authorization:`Bearer ${t}`}});if(!s.ok){let i=await s.text().catch(()=>"");throw new Me(`${n} ${r} \u2192 ${s.status}: ${i}`,s.status,i)}return s.json()}function Jn(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 yt(e,t){let n;try{n=await De(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(Jn):[];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 zn,randomBytes as qn}from"crypto";var Yn="urn:sleekcms:grant-type:cli_poll",Kn="SleekCMS Sync",bt=e=>e.toString("base64").replace(/=+$/,"").replace(/\+/g,"-").replace(/\//g,"_");function vt(){let e=bt(qn(32)),t=bt(zn("sha256").update(e).digest());return{verifier:e,challenge:t}}function St(e){let t=e.replace(/[^A-Za-z0-9]/g,"").slice(0,8).toUpperCase();return`${t.slice(0,4)}-${t.slice(4)}`}async function xt(e,t=fetch){let n={origin:Re(e),authorizeEndpoint:`${ee(e)}/oauth/authorize`};try{let r=await t(`${Re(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 Tt(e,t){let n=new URLSearchParams({response_type:"code",client_name:Kn,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"}},Xn=e=>new Promise(t=>setTimeout(t,e));async function kt(e,t,n={}){let{timeoutMs:r=10*6e4,signal:s,fetchImpl:i=fetch,sleep:d=Xn}=n,l=n.intervalMs??2e3,w=Date.now()+r,S=`${ee(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:Yn,code_verifier:t})})}catch{u=null}if(u){let p=await u.json().catch(()=>({}));if(u.ok&&p.access_token)return p.access_token;if(u.status===429)l=Math.min(l*2,3e4);else if(p.error!=="authorization_pending")throw new z(p.error==="invalid_grant"?"That approval can no longer be used. Add the site again.":`Sign-in failed: ${p.error||`HTTP ${u.status}`}`)}if(Date.now()+l>w)throw new z("Timed out waiting for approval in the browser.");await d(l)}}function Vn(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 $t(e,t){let n=Vn(t),r=await De(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 Et=500;function tr(){return new Date().toTimeString().slice(0,8)}function nr(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 Bt(e){return e instanceof H&&e.status===401}var rr=e=>({id:e.siteId,name:e.name,slug:e.slug});async function Pt(e){e&&(await te.remove(Ae.join(e,"src")),await te.remove(Ae.join(e,".cache","state.json")))}var pe=class extends Zn{env="production";saved=[];synced=new Map;connecting=null;message;messageIsError=!1;agentMd;buildScript;configPath;isShuttingDown=!1;basePath;pollOptions={};openBrowser=ue;constructor(t,n,r=J){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:tr(),text:n}),r.logs.length>Et&&r.logs.splice(0,r.logs.length-Et),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 ge(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}=vt(),r=new AbortController;this.connecting={code:St(n),url:"",abort:r},this.changed();let s=Tt(await xt(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 kt(this.env,t,{...this.pollOptions,signal:r.signal});i=await $t(this.env,l)}catch(l){this.connecting=null,r.signal.aborted?this.changed():this.flash(nr(l),!0);return}this.connecting=null;let d=await Le(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=rr(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 Ee({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(Bt(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,Le({...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 je(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 je(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 Pt(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()}),Qn("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 Ae.join(t,".cache","build.json")}async readBuildFile(t){try{let n=await te.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 te.outputJson(s,n),await te.rename(s,r)}catch{}}watchBuildStatus(t){let n=this.synced.get(t);!n||!n.viewsDir||(n.buildWatcher=er.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 yt(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 Ee({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(Bt(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 Pt(t.viewsDir)}async exit(){this.isShuttingDown||(this.isShuttingDown=!0,await this.cleanupAll(),this.emit("exit"))}};import{useInput as vr}from"ink";import{Fragment as ar,useEffect as ne,useMemo as cr,useRef as lr,useState as L}from"react";import{Box as f,Text as c,useInput as ur,useStdout as dr}from"ink";import gr from"ink-text-input";import{createRequire as sr}from"node:module";var ir=sr(import.meta.url),fe=ir("../package.json").version;import or from"node:os";function _e(e){if(!e)return"\u2014";let t=or.homedir();return e.startsWith(t)?"~"+e.slice(t.length):e}function Oe(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 me(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 Ie(e){return he[(e%he.length+he.length)%he.length]}function we(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 Ct(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 Fe,jsx as o,jsxs as g}from"react/jsx-runtime";var pr=(e,t)=>t<=0?0:Math.max(0,Math.min(e,t-1)),fr=16,hr=5,mr="greenBright",Mt="blueBright",Rt="magentaBright";function Lt({k:e,bg:t=mr}){return o(c,{color:"black",backgroundColor:t,children:` ${e} `})}function O({k:e,label:t,width:n=16,bg:r}){return g(f,{width:n,children:[o(Lt,{k:e,bg:r}),o(c,{color:"gray",children:` ${t}`})]})}function E({k:e,label:t,bg:n}){return g(f,{marginBottom:1,children:[o(f,{width:9,children:o(Lt,{k:e,bg:n})}),o(c,{color:"gray",children:t})]})}function wr({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(E,{k:"\u2191\u2193",label:"move selection"}),o(E,{k:"tab",label:"focus logs \xB7 \u2191\u2193 scroll"}),o(E,{k:"esc",label:"clear filter / back"}),o(f,{marginTop:1,children:o(c,{color:"cyan",bold:!0,children:"Global"})}),o(E,{k:"/",label:"search sites"}),o(E,{k:"a",label:"add a site (browser)"}),o(E,{k:"?",label:"toggle this help",bg:Mt}),o(E,{k:"ctrl+c",label:"quit"})]}),g(f,{flexDirection:"column",flexGrow:1,children:[o(c,{color:"cyan",bold:!0,children:"Site actions"}),o(E,{k:"s",label:"start sync"}),o(E,{k:"d",label:"disconnect"}),o(E,{k:"x",label:"forget site"}),o(E,{k:"p",label:"pause / resume sync"}),o(E,{k:"b",label:"build preview"}),o(E,{k:"B",label:"publish live"}),o(E,{k:"o",label:"open preview url"}),e.map((t,n)=>o(E,{k:String(n+1),label:`open in ${t.label}`,bg:Rt},t.key))]})]})]})}function yr({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 br({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 jt({controller:e}){let{stdout:t}=dr(),n=cr(()=>ot(),[]),[,r]=L(0),[s,i]=L(()=>new Date),[d,l]=L(null),[w,S]=L(""),[u,p]=L(!1),[x,m]=L(0),[b,k]=L(!1),[_,N]=L(!1),[C,M]=L("list"),[Y,j]=L(0),He=()=>r(a=>a+1);ne(()=>(e.on("change",He),()=>{e.off("change",He)}),[e]),ne(()=>{let a,v=()=>{i(new Date),a=setTimeout(v,6e4-Date.now()%6e4)};return a=setTimeout(v,6e4-Date.now()%6e4),()=>clearTimeout(a)},[]);let K=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,I=w.trim().toLowerCase(),P=I?K.filter(a=>a.site.name.toLowerCase().includes(I)||(a.org.name||"").toLowerCase().includes(I)):K,G=P.findIndex(a=>a.site.id===d);G<0&&(G=0);let $=P[G],T=$?.entry,se=Math.max(3,(t?.rows??30)-26),F=T?.logs??[],Se=Math.max(0,F.length-se),Yt=Math.min(Y,Se),ie=F.length-Yt,Je=F.slice(Math.max(0,ie-se),ie),Kt=$?.site.id;ne(()=>j(0),[Kt]);let D=T?.lastBuild,xe=D?.status==="building",Te=Ct(D),oe=xe||!!W;if(!oe){for(let a of e.synced.values())if(a.status==="syncing"){oe=!0;break}}ne(()=>{if(!oe)return;let a=setInterval(()=>m(v=>v+1),120);return()=>clearInterval(a)},[oe]);let ze=P.findIndex(a=>!a.entry),U=Math.max(hr,(t?.rows??30)-fr),X=P.length,ae=X>U,Xt=Math.floor(U/2),V=ae?Math.max(0,Math.min(G-Xt,X-U)):0,ke=Math.min(X,V+U),Vt=P.slice(V,ke),qe=lr(!1);ne(()=>{qe.current||(qe.current=!0,e.saved.length===0&&!e.connecting&&e.connectSite())},[e]);let ce=a=>{if(P.length===0)return;let v=pr(G+a,P.length);l(P[v].site.id)},Zt=()=>{if($){if($.entry){e.flash("Already syncing \u2014 press d to disconnect.");return}e.startSite($.saved)}},Qt=()=>{$?.entry&&e.removeSite($.site.id)},en=()=>{$&&e.forgetSite($.site.id)},tn=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 ${_e(T.viewsDir)}`:`Couldn't open ${a.label}.`,!v)},nn=()=>{if(!T){e.flash("Press s to start syncing this site first.",!0);return}e.togglePause(T.site.id)},Ye=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")},rn=()=>{let a=T?.lastBuild?.url;if(!a){e.flash("No build URL yet \u2014 press b to build.",!0);return}e.flash(ue(a)?`Opening ${a}`:"Couldn't open the browser.",!1)};ur((a,v)=>{if(b){(a==="?"||v.escape)&&k(!1);return}if(W&&v.escape)return void e.cancelConnect();if(u){v.escape?(p(!1),S(""),l(null)):v.return?p(!1):v.upArrow?ce(-1):v.downArrow&&ce(1);return}if(v.tab){C==="logs"?M("list"):T&&M("logs"),j(0);return}if(C==="logs"){if(v.escape){M("list"),j(0);return}if(v.upArrow)return void j(A=>Math.min(A+1,Se));if(v.downArrow)return void j(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 p(!0);if(a==="a"||a==="n")return void e.connectSite();if(v.upArrow)return ce(-1);if(v.downArrow)return ce(1);if(a==="s")return Zt();if(a==="d")return Qt();if(a==="x")return en();if(a==="p")return nn();if(a==="b")return Ye(!1);if(a==="B")return Ye(!0);if(a==="o")return rn();if(a>="1"&&a<="9"){let A=n[parseInt(a,10)-1];if(A)return tn(A)}});let Ke=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${fe} \u2502 `}),o(c,{children:e.env==="production"?"Sync":`Sync \xB7 ${e.env}`})]}),g(f,{children:[g(c,{color:"gray",children:[I?`${P.length}/${K.length}`:K.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:[Ke,o(f,{marginTop:1,justifyContent:"center",children:o(wr,{launchers:n})})]}):g(f,{flexDirection:"column",children:[Ke,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"}),K.length>U&&!u&&!I?o(c,{color:"gray",children:"/ filter"}):null]}),u?g(f,{children:[o(c,{color:"cyan",children:"/ "}),o(gr,{value:w,onChange:S,onSubmit:()=>p(!1),placeholder:"type to filter\u2026"})]}):I?o(c,{color:"gray",children:`/ ${w} \xB7 ${P.length} match${P.length===1?"":"es"}`}):null,o(f,{flexDirection:"column",marginTop:1,minHeight:ae?U+3:void 0,children:P.length===0?o(c,{color:"gray",children:I?`no matches for \u201C${w}\u201D`:"no sites yet \u2014 press a to add one"}):g(Fe,{children:[ae?o(c,{color:"gray",children:V>0?` \u2191 ${V} more`:" "}):null,Vt.map((a,v)=>{let A=V+v,Xe=a.entry?we(a.entry.status):{dot:"\xB7",color:"gray",label:"idle"},Ve=me(a.entry?.progress),sn=a.entry?a.entry.status==="syncing"&&Ve!=null?`${Ve}%`:a.entry.status==="paused"?"paused":"--":"",le=A===G,on=A===ze&&ze>0;return g(ar,{children:[on?o(c,{color:"gray",children:"\u2500".repeat(34)}):null,g(f,{justifyContent:"space-between",children:[g(f,{children:[o(c,{color:le?"green":"gray",children:le?"\u276F ":" "}),g(c,{color:a.entry?.status==="watching"||a.entry?.status==="syncing"?"green":Xe.color,children:[a.entry?.status==="syncing"?Ie(x):a.entry?.status==="watching"?"\u25CF":Xe.dot," "]}),o(c,{color:le?"white":a.entry?void 0:"gray",bold:le,children:a.site.name})]}),o(c,{color:a.entry?.status==="syncing"?"green":"gray",children:sn})]})]},a.site.id)}),ae?o(c,{color:"gray",children:ke<X?` \u2193 ${X-ke} 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(Fe,{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:we(T.status).color,children:`: ${we(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?_e(T.viewsDir):"\u2014"})]}),g(f,{children:[o(c,{color:"gray",children:"synced "}),o(c,{children:Oe(T?.lastSyncedAt)}),o(c,{color:"gray",children:" progress "}),o(c,{color:T?.status==="syncing"?"green":"gray",children:T?.status==="syncing"?`${me(T.progress)??0}%`:"\u2014"})]}),o(f,{marginTop:1,children:o(yr,{percent:T?T.status==="syncing"?me(T.progress)??0:T.status==="watching"?100:0:0})}),T?g(Fe,{children:[g(f,{marginTop:1,children:[o(c,{color:"gray",children:"build "}),o(c,{color:Te.color,children:`${Te.dot} ${Te.label}`}),o(c,{color:"gray",children:" last "}),o(c,{children:xe?"in progress":Oe(D?.at)})]}),g(f,{children:[o(c,{color:"gray",children:"url "}),D?.url?o(c,{color:"cyan",wrap:"truncate-end",children:D.url}):o(c,{color:"gray",children:"\u2014"})]}),D?.status==="error"&&D.errors?.length?g(f,{children:[o(c,{color:"red",children:"\u258C "}),o(c,{color:"red",wrap:"truncate-end",children:`\u2717 ${D.errors[0]}`})]}):null,xe?g(f,{children:[o(br,{frame:x}),o(c,{color:"gray",children:` ${D?.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:[Ie(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:se+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&&F.length>0?C==="logs"?o(c,{color:"green",children:`\u2191\u2193 scroll \xB7 esc back${Se>0?` \xB7 ${ie}/${F.length}`:""}`}):F.length>se?o(c,{color:"gray",children:"tab to scroll"}):null:null]}),o(f,{flexDirection:"column",marginTop:1,children:!T||F.length===0?o(c,{color:"gray",children:"no activity yet"}):Je.map((a,v)=>g(c,{wrap:"truncate-end",children:[g(c,{color:"gray",children:[a.time," "]}),a.text]},ie-Je.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(O,{k:"\u2191\u2193",label:"select"}),o(O,{k:"/",label:"search"}),o(O,{k:"s",label:"start sync"}),o(O,{k:"d",label:"disconnect"}),o(O,{k:"a",label:"add site"}),o(O,{k:"ctrl+c",label:"quit",width:14})]}),o(O,{k:"?",label:"all keys",width:12,bg:Mt})]}),!_&&n.length>0?o(f,{paddingX:1,marginTop:1,flexWrap:"wrap",children:n.map((a,v)=>o(O,{k:String(v+1),label:a.label,bg:Rt},a.key))}):null]})]})}import{jsx as Sr}from"react/jsx-runtime";function Dt({controller:e}){return vr((t,n)=>{n.ctrl&&t==="c"&&e.exit()}),Sr(jt,{controller:e})}import*as Nt from"os";import*as We from"util";var xr={production:"https://9Q849fBKdCJcK6j8M5SHpWjt@s2790579.us-west-2a.betterstackdata.com/2790579",development:"https://oj8Je2g5rZhXAv6NZ3CxYHFb@s2790583.us-west-2a.betterstackdata.com/2790583"},re,At,Tr=()=>At??=import("@sentry/node").then(e=>(e.init({dsn:re.dsn,environment:re.environment,serverName:re.service,initialScope:{tags:{service:re.service}},defaultIntegrations:!1,tracesSampleRate:0,maxBreadcrumbs:0,normalizeDepth:3}),e)).catch(e=>{console.warn("[observability] provider failed to load:",e?.message)}),_t=({service:e,environment:t})=>{let n=xr[t];return n&&(re={dsn:n,environment:t,service:e}),!!n},Ot=(e,t)=>{Tr().then(n=>n?.captureException(e,t&&{extra:t}))},It=async e=>{await(await At)?.flush(e)};var $r=10*6e4,Er=3,Br=30,Pr=1e3;var ve=!1,Ne=!1;var Wt=console.error.bind(console),q=new Map,Ft=new WeakSet,ye={start:0,sent:0},Cr=()=>process.env.ENV||process.env.AWS_LAMBDA_FUNCTION_NAME?.match(/-(production|development)$/)?.[1],Gt=({service:e,environment:t=Cr(),quiet:n=!1})=>{ve=!!t&&_t({service:e,environment:t}),n||console.log(`[observability] ${e}: ${ve?`reporting errors (${t})`:"off"}`)},Ut=(e,t)=>{if(!(!ve||Ne)){Ne=!0;try{let n=jr(e);if(Ft.has(n))return;Ft.add(n);let r=Mr(Rr(n));if(r===!1)return;Ot(Dr(n),r?{...t,suppressed:r}:t)}catch(n){Wt("[observability] capture failed:",n?.message)}finally{Ne=!1}}},Ht=async(e=2e3)=>{if(ve)try{await It(e)}catch(t){Wt("[observability] flush failed:",t?.message)}};var Mr=e=>{let t=Date.now();t-ye.start>=6e4&&(ye={start:t,sent:0});let n=q.get(e),r=0;return(!n||t-n.start>=$r)&&(r=n?.dropped??0,n={start:t,sent:0,dropped:0},q.delete(e),q.set(e,n),q.size>Pr&&q.delete(q.keys().next().value)),n.sent>=Er||ye.sent>=Br?(n.dropped++,!1):(n.sent++,ye.sent++,r)},Rr=e=>{let t=e.stack?.split(`
11
+ `).find(n=>/^\s*at /.test(n)&&!/node_modules|node:|[\\/]observability[\\/]/.test(n));return`${e.name}|${Lr(e.message)}|${t?.trim()??""}`},Lr=e=>e.replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi,"<id>").replace(/\b[0-9a-f]{16,}\b/gi,"<id>").replace(/\d+/g,"<n>").slice(0,300),jr=e=>{if(e instanceof Error)return e;let t=new Error(typeof e=="string"?e:We.inspect(e,{depth:3,breakLength:1/0}));return t.name="NonErrorThrown",t},be=(()=>{try{return Nt.homedir()}catch{return""}})(),Dr=e=>{if(be.length<2||!`${e.message}
12
+ ${e.stack}`.includes(be))return e;let t=new Error(e.message.split(be).join("~"));return t.name=e.name,t.stack=e.stack?.split(be).join("~"),t};import{jsx as Nr}from"react/jsx-runtime";var Ue=rt();Ue.version&&(console.log(fe),process.exit(0));var zt=(Ue.env??"production").toLowerCase();Gt({service:"cms-sync",environment:process.env.DO_NOT_TRACK==="1"?null:zt,quiet:!0});var qt=Ge.dirname(Ar(import.meta.url)),Or=Jt(Ge.join(qt,"AGENTS.md"),"utf-8"),Ir=Jt(Ge.join(qt,"build.mjs"),"utf-8");async function Fr(){let e=new pe(Or,Ir);e.basePath=Ue.path,await e.init(zt),process.stdout.isTTY&&process.stdout.write("\x1B[2J\x1B[3J\x1B[H");let t=_r(Nr(Dt,{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()}Fr().catch(async e=>{let t=e;console.error("\u274C",t.body||t.message),t.body||(Ut(e),await Ht()),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleekcms/sync",
3
- "version": "3.5.0",
3
+ "version": "4.1.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",
@@ -39,6 +39,7 @@
39
39
  "author": "Yusuf Bhabhrawala",
40
40
  "license": "ISC",
41
41
  "dependencies": {
42
+ "@sentry/node": "^11.6.0",
42
43
  "chokidar": "^4.0.3",
43
44
  "commander": "^13.1.0",
44
45
  "fs-extra": "^11.3.0",