@sleekcms/sync 2.11.0 → 2.14.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 +8 -1
- package/dist/AGENT.md +24 -4
- package/dist/index.js +5 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -135,6 +135,8 @@ The CLI runs a local watcher after the initial fetch. Every time you save a file
|
|
|
135
135
|
|
|
136
136
|
On first run, the CLI pulls the full site state from the server. After that, a local `.cache/` folder tracks server-known state so only real diffs are pushed — no redundant API calls.
|
|
137
137
|
|
|
138
|
+
Renames sync too. Rename or move a page's, entry's, or block's model, view, or content file (in your editor, with `mv`, or `git mv`) and SleekCMS renames it: its other files are moved to match and a page's URL follows (`models/pages/about.model` → `about-us.model` moves `/about` to `/about-us`). Rename a collection by its model file (`models/pages/blog+.model` → `news+.model`) and its records folder follows; renaming a record's content file changes its URL. Links typed into templates or copy keep the old URL, and the sync log lists them.
|
|
139
|
+
|
|
138
140
|
```
|
|
139
141
|
Your editor → file save → watcher → SleekCMS API → rebuild → live site
|
|
140
142
|
```
|
|
@@ -324,6 +326,7 @@ Models describe the shape of your content. They're JSON-like files with unquoted
|
|
|
324
326
|
|---|---|
|
|
325
327
|
| `render(val)` | Render a block or entry through its template |
|
|
326
328
|
| `marked(md)` | Convert markdown to HTML |
|
|
329
|
+
| `remark(md, opts?)` | Convert markdown to HTML with syntax-highlighted code blocks, math (`$…$`, `$$…$$`) and Mermaid diagrams, adding only the CSS/JS a page needs. `opts.codeTheme` picks a highlight.js theme (default `github-dark`) |
|
|
327
330
|
|
|
328
331
|
**Images**
|
|
329
332
|
|
|
@@ -404,7 +407,11 @@ Models describe the shape of your content. They're JSON-like files with unquoted
|
|
|
404
407
|
</form>
|
|
405
408
|
```
|
|
406
409
|
|
|
407
|
-
Any form with `data-sleekcms="<name>"` captures and stores submissions in the CMS dashboard automatically.
|
|
410
|
+
Any form with `data-sleekcms="<name>"` captures and stores submissions in the CMS dashboard automatically. File uploads work out of the box too — add an `<input type="file">` and visitors can attach documents (PDF, Word, Excel, CSV, text) and images:
|
|
411
|
+
|
|
412
|
+
```html
|
|
413
|
+
<input name="resume" type="file" accept=".pdf">
|
|
414
|
+
```
|
|
408
415
|
|
|
409
416
|
---
|
|
410
417
|
|
package/dist/AGENT.md
CHANGED
|
@@ -72,6 +72,8 @@ Never guess at why something isn't rendering — read `sync-errors.log` first, t
|
|
|
72
72
|
|
|
73
73
|
For collection pages, the `<slug>` is the content file's name: `content/pages/blog+/my-post.json` → `/blog/my-post`. Renaming the file renames the URL.
|
|
74
74
|
|
|
75
|
+
**Renaming.** Rename a page, entry, or block by `mv`-ing only its model file — a collection too (`blog+.model`, not its folder). The sync renames it on the server and moves its view, content, and records to match: `models/pages/about.model` → `about-us.model` moves `/about` to `/about-us`. A collection record is the exception: its content file name is its slug, so to change one record's URL, rename that file within its folder — `content/pages/blog+/old-post.json` → `new-post.json` moves just that record from `/blog/old-post` to `/blog/new-post`. Keep slugs lowercase and dash-separated, and keep the file in its collection's folder. The kind and `+` can't change. Update hard-coded links and `getEntry()` calls that use the old name.
|
|
76
|
+
|
|
75
77
|
## 4. Folder Structure (under `src/`)
|
|
76
78
|
|
|
77
79
|
```
|
|
@@ -201,7 +203,7 @@ Like JSON but with **no quotes**; each value is a field type name:
|
|
|
201
203
|
|---|---|
|
|
202
204
|
| `text`, `paragraph`, `code`, `color`, `link` | String |
|
|
203
205
|
| `richtext` | String of **HTML** — output with `<%- %>` |
|
|
204
|
-
| `markdown` | String of **raw markdown** — convert with `<%- marked(item.x) %>` |
|
|
206
|
+
| `markdown` | String of **raw markdown** — convert with `<%- marked(item.x) %>`, or `<%- remark(item.x) %>` when it holds code blocks, math or diagrams |
|
|
205
207
|
| `number` | Number |
|
|
206
208
|
| `boolean` | `true` / `false` |
|
|
207
209
|
| `emoji` | One emoji character string, e.g. `"😀"` (not free text) |
|
|
@@ -348,7 +350,7 @@ Frontmatter may be JSON (canonical — prefer it when generating), JSON5, or YAM
|
|
|
348
350
|
| Tag | Purpose |
|
|
349
351
|
|---|---|
|
|
350
352
|
| `<%= expr %>` | Output, HTML-escaped (plain text) |
|
|
351
|
-
| `<%- expr %>` | Output raw HTML (`render()`, `img()`, `marked()`, richtext) |
|
|
353
|
+
| `<%- expr %>` | Output raw HTML (`render()`, `img()`, `marked()`, `remark()`, richtext) |
|
|
352
354
|
| `<% code %>` | Run JS (loops, conditionals) |
|
|
353
355
|
|
|
354
356
|
### Context variables (every view)
|
|
@@ -402,6 +404,18 @@ There is exactly **one** layout: `layouts/common.ejs`. It wraps every page autom
|
|
|
402
404
|
|---|---|
|
|
403
405
|
| `render(val, separator?)` | Render a block, entry, or array of them through its `blocks/<key>.ejs` / `entries/<key>.ejs` view. An `entries/<key>.ejs` view is only needed if you call `render()` on that entry — otherwise just use its data directly |
|
|
404
406
|
| `marked(md)` | Markdown string → HTML string |
|
|
407
|
+
| `remark(md, opts?)` | Markdown string → HTML string, plus highlighted code blocks, math and Mermaid diagrams (below) |
|
|
408
|
+
|
|
409
|
+
Use `remark()` instead of `marked()` for docs, tutorials and technical posts. On top of `marked()` it renders fenced code blocks (```` ```js ````) with syntax highlighting, math written as `$e^{i\pi} + 1 = 0$` inline, `$$ … $$` on its own lines or a ```` ```math ```` fence (KaTeX), and ```` ```mermaid ```` fences as diagrams drawn in the visitor's browser. It injects only the CSS/JS the rendered markdown needs (KaTeX CSS, a code theme, the Mermaid runtime), so never `link()`/`script()` those libraries yourself.
|
|
410
|
+
|
|
411
|
+
- `opts.codeTheme` — a highlight.js theme name: default `github-dark`, or e.g. `github`, `atom-one-dark`, `base16/dracula`. `github-dark` suits the dark `pre` that `prose` draws; use a light theme only when the site gives `pre` a light background. One theme per page.
|
|
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 `\$`.
|
|
413
|
+
- Mermaid uses its light default theme. On a dark design, start each diagram with frontmatter: `---`, `config:`, ` theme: dark`, `---` on their own lines above the diagram.
|
|
414
|
+
|
|
415
|
+
```ejs
|
|
416
|
+
<div class="prose prose-lg max-w-none"><%- remark(item.body) %></div>
|
|
417
|
+
<%- remark(item.body, { codeTheme: 'github' }) %> <!-- light code theme -->
|
|
418
|
+
```
|
|
405
419
|
|
|
406
420
|
### Helpers — images & maps
|
|
407
421
|
|
|
@@ -436,7 +450,7 @@ Callable from **any** view (page, block, entry, layout); duplicates are removed
|
|
|
436
450
|
| `style(css, order?)` | Add `<style>` |
|
|
437
451
|
| `script(value, order?)` | Add `<script>` — `.js` URL → external, otherwise inline |
|
|
438
452
|
|
|
439
|
-
`script()`/`link()` also accept a predefined library name — `gsap` (includes ScrollTrigger, pre-registered), `aos` (auto-`AOS.init()`, JS+CSS), `
|
|
453
|
+
`script()`/`link()` also accept a predefined library name — `gsap` (includes ScrollTrigger, pre-registered), `aos` (auto-`AOS.init()`, JS+CSS), `swiper`, `animejs`, `chartjs`, `threejs` (import map — pair with a `type: 'module'` script), `lottie`, `typedjs`, `splide`, `htmx`, `vanilla-tilt` — either call injects all of the library's CDN assets, deduped. Prefer these names over hand-picked CDN URLs, and only add a library the site actually uses.
|
|
440
454
|
|
|
441
455
|
`script()` also takes an attributes object — `script({ src: '/js/app.js', defer: '' })`, `script({ content: '...', type: 'module' })` — so a tag that needs attributes still never needs a raw `<script>`. That is how a third-party script is put behind cookie consent: `script({ src: 'https://…', 'data-sleekcms-consent': 'marketing' })` (§14).
|
|
442
456
|
|
|
@@ -489,6 +503,12 @@ Any `<form>` with `data-sleekcms="<hardcoded name>"` works automatically — sub
|
|
|
489
503
|
|
|
490
504
|
The `data-sleekcms` value groups submissions by form; each input's `name` is stored as-is. Add an `<input type="datetime-local">` and the form becomes a **booking form** — see §11.
|
|
491
505
|
|
|
506
|
+
**File uploads** are supported out of the box — add an `<input type="file">` and visitors can attach documents (PDF, Word, Excel, CSV, text) and images (add `multiple` for several):
|
|
507
|
+
|
|
508
|
+
```html
|
|
509
|
+
<input name="resume" type="file" accept=".pdf,.doc,.docx">
|
|
510
|
+
```
|
|
511
|
+
|
|
492
512
|
## 11. Bookings
|
|
493
513
|
|
|
494
514
|
Use this whenever visitors should **book or schedule** something — an appointment, a call, a demo, a consultation, a class, or a reservation ("pick a time / reserve a slot"). It fits appointment-based businesses: salon, spa, barber, dental/medical clinic, therapist, tutor, photographer, consultant, repair shop, restaurant reservations. **Only add a booking form when the business genuinely takes appointments and the user asks for it (or it is clearly core to the business) — never add one speculatively to an arbitrary site.**
|
|
@@ -653,7 +673,7 @@ No config element → all defaults. One per site: a second one is ignored with a
|
|
|
653
673
|
3. Blocks never have content files. Block values live inline in the page/entry JSON that declares them.
|
|
654
674
|
4. `file` field values are plain file names ending in a supported extension (`"brochure.pdf"`) — the CMS attaches or placeholder-creates the file server-side; never write objects or URLs.
|
|
655
675
|
5. Never invent `video_id`/`embed_url` objects — render videos with `embed()` only. Videos are set via refs: `content/videos.json` handles for shared videos, and `cms:<handle>` / provider refs in `video` fields (§7); values that read back as embed URLs or objects are user uploads — leave them untouched.
|
|
656
|
-
6. `richtext` → `<%- item.x %>`. `markdown` → `<%- marked(item.x)
|
|
676
|
+
6. `richtext` → `<%- item.x %>`. `markdown` → `<%- marked(item.x) %>`, or `<%- remark(item.x) %>` when it holds code, math or diagrams.
|
|
657
677
|
7. CSS/JS via `link()`/`script()` only; `css/tailwind.css` is auto-injected — never `link()` it. When writing tailwind.css, `@import "tailwindcss";` must stay as its first statement — dropping it silently removes all Tailwind styling.
|
|
658
678
|
8. Editable copy belongs in `content/`, not hard-coded in `.ejs`. Fixed micro-labels may stay in views.
|
|
659
679
|
9. In page keys, `-` separates words and `_` means `/`. `_index` is the home page only; a section index page uses the plain section key.
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{readFileSync as
|
|
2
|
+
import{readFileSync as Ne}from"node:fs";import{fileURLToPath as ar}from"node:url";import{createRequire as cr}from"node:module";import Ut from"node:path";import{render as lr}from"ink";import{execSync as ne,spawn as M}from"child_process";import{program as Zt,Option as Ct}from"commander";function re(){return Zt.name("cms-sync").description("SleekCMS CLI tool to sync and edit CMS templates locally. Log in once, then add the sites you want to sync \u2014 changes are watched and pushed automatically.").addOption(new Ct("-v, --version","output the version number").hideHelp()).addOption(new Ct("-e, --env <env>","Environment (localhost, development, production)").default("production").hideHelp()).addOption(new Ct("-d, --dev <secret>","Dev secret for non-production envs (sent as the _dev cookie; enables OTP-in-response). Falls back to SLEEKCMS_DEV_SECRET.").hideHelp()).option("-p, --path <path>","Directory path for files (default: ~/SleekCMS)").addHelpText("after",`
|
|
3
3
|
Examples:
|
|
4
4
|
$ sleekcms # log in and pick sites to sync
|
|
5
5
|
$ sleekcms -e development # use a non-production backend
|
|
6
|
-
`).parse(process.argv),Yt.opts()}function D(t){let e=process.platform==="win32"?`where ${t}`:`which ${t}`;try{return Zt(e,{stdio:"ignore"}),!0}catch{return!1}}function ee(t){if(process.platform!=="darwin")return!1;try{return Zt(`osascript -e 'id of application "${t}"'`,{stdio:"ignore"}),!0}catch{return!1}}function Qe(t,e){let n={detached:!0,stdio:"ignore",windowsHide:!0};process.platform==="win32"?P("cmd.exe",["/c",t,...e],n).unref():P(t,e,n).unref()}var Vt=[{name:"VS Code",cmd:"code",args:t=>["-n",t]},{name:"Cursor",cmd:"cursor",args:t=>["-n",t]}];function Qt(t,e){let r=(e?Vt.filter(s=>s.cmd===e):Vt).find(s=>D(s.cmd));return r?(Qe(r.cmd,r.args(t)),r.name):null}function Ze(t){if(process.platform==="darwin")return P("open",["-a","Terminal",t],{detached:!0,stdio:"ignore"}).unref(),!0;if(process.platform==="win32"){if(D("wt"))P("wt.exe",["-d",t],{detached:!0,stdio:"ignore"}).unref();else{let e=D("powershell")?"powershell.exe":"cmd.exe";P("cmd.exe",["/c","start","",e],{cwd:t,detached:!0,stdio:"ignore"}).unref()}return!0}return!1}function tn(t){return ee("iTerm")?(P("open",["-a","iTerm",t],{detached:!0,stdio:"ignore"}).unref(),!0):!1}function en(t){return`'${t.replace(/'/g,"'\\''")}'`}var nn=[{cmd:"x-terminal-emulator",args:t=>["-e","bash","-c",t]},{cmd:"gnome-terminal",args:t=>["--","bash","-c",t]},{cmd:"konsole",args:t=>["-e","bash","-c",t]},{cmd:"xfce4-terminal",args:t=>["-x","bash","-c",t]},{cmd:"kitty",args:t=>["bash","-c",t]},{cmd:"alacritty",args:t=>["-e","bash","-c",t]},{cmd:"xterm",args:t=>["-e","bash","-c",t]}];function ne(t,e){if(!D(e))return!1;if(process.platform==="darwin"){let r=`tell application "Terminal" to do script ("cd " & quoted form of "${t}" & " && ${e}")`;return P("osascript",["-e",r,"-e",'tell application "Terminal" to activate'],{detached:!0,stdio:"ignore"}).unref(),!0}if(process.platform==="win32")return D("wt")?P("wt.exe",["-d",t,"cmd","/k",e],{detached:!0,stdio:"ignore"}).unref():P("cmd.exe",["/c","start","","cmd","/k",e],{cwd:t,detached:!0,stdio:"ignore"}).unref(),!0;let n=`cd ${en(t)} && ${e}; exec bash`;for(let r of nn)if(D(r.cmd))try{return P(r.cmd,r.args(n),{cwd:t,detached:!0,stdio:"ignore"}).unref(),!0}catch{}return!1}function rn(t){return ne(t,"claude")}function sn(t){return ne(t,"pi")}function re(t){if(!t)return!1;try{return process.platform==="darwin"?P("open",[t],{detached:!0,stdio:"ignore"}).unref():process.platform==="win32"?P("cmd.exe",["/c","start","",t],{detached:!0,stdio:"ignore",windowsHide:!0}).unref():P("xdg-open",[t],{detached:!0,stdio:"ignore"}).unref(),!0}catch{return!1}}function se(){let t=process.platform==="darwin",e=process.platform==="win32",n=[];return(t||e)&&n.push({key:"terminal",label:"terminal",run:Ze}),ee("iTerm")&&n.push({key:"iterm",label:"iterm",run:tn}),D("code")&&n.push({key:"code",label:"vscode",run:r=>Qt(r,"code")!==null}),D("cursor")&&n.push({key:"cursor",label:"cursor",run:r=>Qt(r,"cursor")!==null}),D("claude")&&n.push({key:"claude",label:"claude",run:rn}),D("pi")&&n.push({key:"pi",label:"pi",run:sn}),n}import{EventEmitter as Mn}from"node:events";import{spawn as On}from"node:child_process";import Q from"fs-extra";import At from"path";import Dn from"chokidar";import m from"fs-extra";import an from"os";import S from"path";var ie={localhost:"http://app.sleekcms.test/api/mcp",development:"https://app.sleekcms.dev/api/mcp",production:"https://app.sleekcms.com/api/mcp"},cn=["src/views/blocks","src/views/entries","src/views/pages","src/views/layouts","src/models/blocks","src/models/entries","src/models/pages","src/content/pages","src/content/entries","src/public/js","src/public/css"],Bt=class extends Error{status;body;constructor(e,n,r){super(e),this.status=n,this.body=r}};async function Ct(t,e,n,r,s){let o=await fetch(t+r,{method:n,headers:{Authorization:`Bearer ${e}`,...s!==void 0?{"Content-Type":"application/json"}:{}},body:s!==void 0?JSON.stringify(s):void 0});if(!o.ok){let c=await o.text();throw new Bt(`${n} ${r} \u2192 ${o.status}: ${c}`,o.status,c)}return o.json()}async function ln(t,e){e&&(await m.outputFile(S.join(t,"AGENT.md"),e),await m.outputFile(S.join(t,"CLAUDE.md"),e),await m.outputFile(S.join(t,".vscode","copilot-instructions.md"),e)),await m.outputFile(S.join(t,".vscode","settings.json"),JSON.stringify({"files.associations":{"*.model":"javascript"},"[javascript]":{"editor.defaultFormatter":"esbenp.prettier-vscode"},"js/ts.validate.enabled":!1},null,2))}async function un(t,e){if(!e)return;let n=S.join(t,"build.mjs"),r=e.match(/build\.mjs v\d+/)?.[0];if(await m.pathExists(n)){let s=(await m.readFile(n,"utf-8")).match(/build\.mjs v\d+/)?.[0];if(s&&s===r)return}await m.outputFile(n,e)}async function dn(t,e,n){let r=S.join(t,".cache","token");if(await m.pathExists(r)){if((await m.readFile(r,"utf-8")).trim()===e)return;n?.("\u267B\uFE0F Workspace was tied to a different site \u2014 recreating it."),await m.emptyDir(t)}await m.outputFile(r,e)}function gn(t,e){let n=`${e.name.substr(0,20)} ${e.id}`.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,""),r=t||S.join(an.homedir(),"SleekCMS");return S.resolve(r,n)}async function Pt(t){let e=(t.token||"").trim();if(!e)throw new Error("syncSite: token is required");let n=t.log??(v=>console.log(v)),r=t.onProgress,s=(t.env||e.split("-")[2]||"production").toLowerCase(),o=ie[s]||ie.production,c=await Ct(o,e,"GET","/get_site"),h=t.viewsDir?S.resolve(t.viewsDir):gn(t.path,c);await m.ensureDir(h),await dn(h,e,n),await Promise.all(cn.map(v=>m.ensureDir(S.join(h,v)))),await un(h,t.buildScript);let p=S.join(h,".cache","state.json");t.flush&&await m.remove(p);let x=!await m.pathExists(p),w=x?{}:(await m.readJson(p)).fileMap||{},f=0,b=0;return x?({fileMap:w,pulled:b}=await Sn(h,o,e,n,r),await ln(h,t.agentMd)):f=await fn(h,w,o,e,n,r),await m.outputJson(p,{fileMap:w},{spaces:2}),{viewsDir:h,site:c,isFirstRun:x,pushed:f,pulled:b}}var hn=new Set([".DS_Store","Thumbs.db","desktop.ini"]);async function pn(t){let e=S.join(t,"src");if(!await m.pathExists(e))return[];let n=[];async function r(s){let o=await m.readdir(s,{withFileTypes:!0});for(let c of o){if(hn.has(c.name))continue;let h=S.join(s,c.name);c.isDirectory()?await r(h):c.isFile()&&n.push(S.relative(t,h).replace(/\\/g,"/"))}}return await r(e),n}async function fn(t,e,n,r,s,o){let c=[];for(let f of await pn(t)){let b=S.join(t,f),v=e[f],O=await m.stat(b);if(v&&v.mtimeMs===O.mtimeMs)continue;let R=await m.readFile(b,"utf-8");R.trim()&&c.push({rel:f,full:b,stat:O,content:R,prior:v})}if(c.length===0)return 0;let h;try{h=await Ct(n,r,"POST","/save_files",c.map(f=>({path:f.rel,content:f.content})))}catch(f){let b=f;return s(`\u274C Error saving files: ${b.body||b.message}`),0}let p=await wn(t),x=0,w=mn(h);for(let f=0;f<c.length;f++){let b=c[f],v=yn(b,f,h,w);if(o?.(f+1,c.length),!v){let O="No save response returned";p[b.rel]=O,s(`\u274C Error saving ${b.rel}: ${O}`);continue}if(v.error){p[b.rel]=v.error,s(`\u274C Error saving ${b.rel}: ${v.error}`);continue}delete p[b.rel],e[b.rel]={mtimeMs:b.stat.mtimeMs},s(`\u2705 ${b.prior?"Updated":"Created"} ${b.rel}`),x++}return await xn(t,p),x}function mn(t){let e=new Map;for(let n of t)n.path&&e.set(n.path,n);return e}function yn(t,e,n,r){let s=r.get(t.rel);if(s)return s;let o=n[e];if(!o?.path||o.path===t.rel)return o}var $t="sync-errors.log",Et=new Map;async function wn(t){let e=S.resolve(t),n=Et.get(e);if(n)return n;let r=await bn(e);return Et.set(e,r),r}async function bn(t){let e=S.join(t,$t);if(!await m.pathExists(e))return{};let n=await m.readFile(e,"utf-8"),r={};for(let s of n.split(`
|
|
7
|
-
`)
|
|
6
|
+
`).parse(process.argv),Zt.opts()}function j(t){let e=process.platform==="win32"?`where ${t}`:`which ${t}`;try{return ne(e,{stdio:"ignore"}),!0}catch{return!1}}function se(t){if(process.platform!=="darwin")return!1;try{return ne(`osascript -e 'id of application "${t}"'`,{stdio:"ignore"}),!0}catch{return!1}}function sn(t,e){let n={detached:!0,stdio:"ignore",windowsHide:!0};process.platform==="win32"?M("cmd.exe",["/c",t,...e],n).unref():M(t,e,n).unref()}var te=[{name:"VS Code",cmd:"code",args:t=>["-n",t]},{name:"Cursor",cmd:"cursor",args:t=>["-n",t]}];function ee(t,e){let r=(e?te.filter(s=>s.cmd===e):te).find(s=>j(s.cmd));return r?(sn(r.cmd,r.args(t)),r.name):null}function on(t){if(process.platform==="darwin")return M("open",["-a","Terminal",t],{detached:!0,stdio:"ignore"}).unref(),!0;if(process.platform==="win32"){if(j("wt"))M("wt.exe",["-d",t],{detached:!0,stdio:"ignore"}).unref();else{let e=j("powershell")?"powershell.exe":"cmd.exe";M("cmd.exe",["/c","start","",e],{cwd:t,detached:!0,stdio:"ignore"}).unref()}return!0}return!1}function an(t){return se("iTerm")?(M("open",["-a","iTerm",t],{detached:!0,stdio:"ignore"}).unref(),!0):!1}function cn(t){return`'${t.replace(/'/g,"'\\''")}'`}var ln=[{cmd:"x-terminal-emulator",args:t=>["-e","bash","-c",t]},{cmd:"gnome-terminal",args:t=>["--","bash","-c",t]},{cmd:"konsole",args:t=>["-e","bash","-c",t]},{cmd:"xfce4-terminal",args:t=>["-x","bash","-c",t]},{cmd:"kitty",args:t=>["bash","-c",t]},{cmd:"alacritty",args:t=>["-e","bash","-c",t]},{cmd:"xterm",args:t=>["-e","bash","-c",t]}];function ie(t,e){if(!j(e))return!1;if(process.platform==="darwin"){let r=`tell application "Terminal" to do script ("cd " & quoted form of "${t}" & " && ${e}")`;return M("osascript",["-e",r,"-e",'tell application "Terminal" to activate'],{detached:!0,stdio:"ignore"}).unref(),!0}if(process.platform==="win32")return j("wt")?M("wt.exe",["-d",t,"cmd","/k",e],{detached:!0,stdio:"ignore"}).unref():M("cmd.exe",["/c","start","","cmd","/k",e],{cwd:t,detached:!0,stdio:"ignore"}).unref(),!0;let n=`cd ${cn(t)} && ${e}; exec bash`;for(let r of ln)if(j(r.cmd))try{return M(r.cmd,r.args(n),{cwd:t,detached:!0,stdio:"ignore"}).unref(),!0}catch{}return!1}function un(t){return ie(t,"claude")}function dn(t){return ie(t,"pi")}function oe(t){if(!t)return!1;try{return process.platform==="darwin"?M("open",[t],{detached:!0,stdio:"ignore"}).unref():process.platform==="win32"?M("cmd.exe",["/c","start","",t],{detached:!0,stdio:"ignore",windowsHide:!0}).unref():M("xdg-open",[t],{detached:!0,stdio:"ignore"}).unref(),!0}catch{return!1}}function ae(){let t=process.platform==="darwin",e=process.platform==="win32",n=[];return(t||e)&&n.push({key:"terminal",label:"terminal",run:on}),se("iTerm")&&n.push({key:"iterm",label:"iterm",run:an}),j("code")&&n.push({key:"code",label:"vscode",run:r=>ee(r,"code")!==null}),j("cursor")&&n.push({key:"cursor",label:"cursor",run:r=>ee(r,"cursor")!==null}),j("claude")&&n.push({key:"claude",label:"claude",run:un}),j("pi")&&n.push({key:"pi",label:"pi",run:dn}),n}import{EventEmitter as Gn}from"node:events";import{spawn as Un}from"node:child_process";import nt from"fs-extra";import It from"path";import qn from"chokidar";import{createHash as mn}from"crypto";import y from"fs-extra";import hn from"os";import v from"path";var ce={localhost:"http://app.sleekcms.test/api/mcp",development:"https://app.sleekcms.dev/api/mcp",production:"https://app.sleekcms.com/api/mcp"},fn=["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"],Pt=class extends Error{status;body;constructor(e,n,r){super(e),this.status=n,this.body=r}};async function ft(t,e,n,r,s){let i=await fetch(t+r,{method:n,headers:{Authorization:`Bearer ${e}`,...s!==void 0?{"Content-Type":"application/json"}:{}},body:s!==void 0?JSON.stringify(s):void 0});if(!i.ok){let a=await i.text();throw new Pt(`${n} ${r} \u2192 ${i.status}: ${a}`,i.status,a)}return i.json()}async function pn(t,e){e&&(await y.outputFile(v.join(t,"AGENT.md"),e),await y.outputFile(v.join(t,"CLAUDE.md"),e),await y.outputFile(v.join(t,".vscode","copilot-instructions.md"),e)),await y.outputFile(v.join(t,".vscode","settings.json"),JSON.stringify({"files.associations":{"*.model":"javascript"},"[javascript]":{"editor.defaultFormatter":"esbenp.prettier-vscode"},"js/ts.validate.enabled":!1},null,2))}async function yn(t,e){if(!e)return;let n=v.join(t,"build.mjs"),r=e.match(/build\.mjs v\d+/)?.[0];if(await y.pathExists(n)){let s=(await y.readFile(n,"utf-8")).match(/build\.mjs v\d+/)?.[0];if(s&&s===r)return}await y.outputFile(n,e)}async function wn(t,e,n){let r=v.join(t,".cache","token");if(await y.pathExists(r)){if((await y.readFile(r,"utf-8")).trim()===e)return;n?.("\u267B\uFE0F Workspace was tied to a different site \u2014 recreating it."),await y.emptyDir(t)}await y.outputFile(r,e)}function bn(t,e){let n=`${e.name.substr(0,20)} ${e.id}`.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,""),r=t||v.join(hn.homedir(),"SleekCMS");return v.resolve(r,n)}async function Mt(t){let e=(t.token||"").trim();if(!e)throw new Error("syncSite: token is required");let n=t.log??(x=>console.log(x)),r=t.onProgress,s=(t.env||e.split("-")[2]||"production").toLowerCase(),i=ce[s]||ce.production,a=await ft(i,e,"GET","/get_site"),u=t.viewsDir?v.resolve(t.viewsDir):bn(t.path,a);await y.ensureDir(u),await wn(u,e,n),await Promise.all(fn.map(x=>y.ensureDir(v.join(u,x)))),await yn(u,t.buildScript);let h=v.join(u,".cache","state.json");t.flush&&await y.remove(h);let w=!await y.pathExists(h),l=w?{}:(await y.readJson(h)).fileMap||{},b=0,S=0,g=0;if(w)({fileMap:l,pulled:S}=await Dn(u,i,e,n,r)),await pn(u,t.agentMd);else{let x=await Mn(u,l,i,e,n);g=x.renamed,b=await xn(u,l,i,e,n,r,x.held)}return await y.outputJson(h,{fileMap:l},{spaces:2}),{viewsDir:u,site:a,isFirstRun:w,pushed:b,pulled:S,renamed:g}}var tt=t=>mn("sha1").update(t).digest("hex"),Sn=new Set([".DS_Store","Thumbs.db","desktop.ini"]);async function Rt(t){let e=v.join(t,"src");if(!await y.pathExists(e))return[];let n=[];async function r(s){let i=await y.readdir(s,{withFileTypes:!0});for(let a of i){if(Sn.has(a.name))continue;let u=v.join(s,a.name);a.isDirectory()?await r(u):a.isFile()&&n.push(v.relative(t,u).replace(/\\/g,"/"))}}return await r(e),n}async function xn(t,e,n,r,s,i,a=new Set){let u=[];for(let S of await Rt(t)){if(a.has(S))continue;let g=v.join(t,S),x=e[S],C=await y.stat(g);if(x&&x.mtimeMs===C.mtimeMs){x.hash||(x.hash=tt(await y.readFile(g,"utf-8")));continue}let k=await y.readFile(g,"utf-8");k.trim()&&u.push({rel:S,full:g,stat:C,content:k,prior:x})}if(u.length===0)return 0;let h;try{h=await ft(n,r,"POST","/save_files",u.map(S=>({path:S.rel,content:S.content})))}catch(S){let g=S;return s(`\u274C Error saving files: ${g.body||g.message}`),0}let w=await de(t),l=0,b=vn(h);for(let S=0;S<u.length;S++){let g=u[S],x=Tn(g,S,h,b);if(i?.(S+1,u.length),!x){let C="No save response returned";w[g.rel]=C,s(`\u274C Error saving ${g.rel}: ${C}`);continue}if(x.error){w[g.rel]=x.error,s(`\u274C Error saving ${g.rel}: ${x.error}`);continue}delete w[g.rel],e[g.rel]={mtimeMs:g.stat.mtimeMs,hash:tt(g.content)},s(`\u2705 ${g.prior?"Updated":"Created"} ${g.rel}`),l++}return await ge(t,w),l}function vn(t){let e=new Map;for(let n of t)n.path&&e.set(n.path,n);return e}function Tn(t,e,n,r){let s=r.get(t.rel);if(s)return s;let i=n[e];if(!i?.path||i.path===t.rel)return i}var le=/^src\/(?:(?:models|views)\/(?:pages|entries|blocks)|content\/(?:pages|entries))\//,kn={PAGE:"pages",ENTRY:"entries",BLOCK:"blocks"},$n=t=>t.split("/").slice(0,3).join("/");function En(t,e,n){let r=new Map,s=(i,a)=>{let u=`${$n(i)}|${a}`;return r.has(u)||r.set(u,{from:[],to:[],hash:a}),r.get(u)};for(let i of t)s(i,n[i].hash).from.push(i);for(let[i,a]of e)s(i,a).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 Bn(t,e){let n=new Map,r=(s,i,a,u,h)=>{let w=n.get(s);w?(w.pairs.push(h),i<w.rank&&Object.assign(w,{from:a,to:u,rank:i})):n.set(s,{from:a,to:u,rank:i,pairs:[h]})};for(let s of t){let[,i,a,...u]=s.from.split("/"),h=s.to.split("/").slice(3);if(i==="content"&&a==="pages"&&u.length>1){let[l,...b]=u,[S,...g]=h;if(g.length===0)continue;S===l?r(`record:${s.from}`,0,s.from,s.to,s):b.join("/")===g.join("/")&&![...e].some(x=>x.startsWith(`src/content/pages/${l}/`))&&r(`pages:${l}`,3,`src/content/pages/${l}`,`src/content/pages/${S}`,s);continue}let w=u.join("/").replace(/\.[^./]+$/,"");r(`${a}:${w}`,i==="models"?0:i==="views"?1:2,s.from,s.to,s)}return[...n.values()].map(({from:s,to:i,pairs:a})=>({from:s,to:i,pairs:a}))}function ue(t,e){if(t.slug!==void 0&&t.new_slug!==void 0){let a=`src/content/pages/${t.key}/`,u=e.startsWith(a)?e.slice(a.length).match(/^(.+)\.(json|md)$/):null;return u&&u[1].toLowerCase()===t.slug.toLowerCase()?`${a}${t.new_slug}.${u[2]}`:null}let n=kn[t.type],{key:r,new_key:s}=t;if(!n||!s)return null;if(e===`src/models/${n}/${r}.model`)return`src/models/${n}/${s}.model`;if(e===`src/views/${n}/${r}.ejs`)return`src/views/${n}/${s}.ejs`;if(n!=="blocks"&&(e===`src/content/${n}/${r}.json`||e===`src/content/${n}/${r}.md`))return`src/content/${n}/${s}${v.extname(e)}`;let i=`src/content/pages/${r}/`;return n==="pages"&&r.endsWith("+")&&e.startsWith(i)?`src/content/pages/${s}/${e.slice(i.length)}`:null}async function Cn(t,e){let n=t;for(;n.startsWith(e+v.sep);){let r=await y.readdir(n).catch(()=>null);if(!r||r.length>0)return;await y.remove(n),n=v.dirname(n)}}async function Pn(t,e,n,r){let s=l=>v.join(t,l),i=v.join(t,"src"),a=l=>v.resolve(t,l).startsWith(i+v.sep),u=async(l,b)=>{e[l]={mtimeMs:(await y.stat(s(l))).mtimeMs,hash:b}},h=l=>Cn(v.dirname(s(l)),v.join(t,...l.split("/").slice(0,3))),w=[];for(let l of r){let b=ue(n.rename,l.from)??l.to;a(b)&&(b!==l.to&&(await y.move(s(l.to),s(b),{overwrite:!0}),await h(l.to),w.push(l.to)),delete e[l.from],w.push(l.from),await u(b,l.hash))}for(let l of new Set([...await Rt(t),...Object.keys(e)])){let b=ue(n.rename,l);if(!b||!a(b))continue;let S=await y.pathExists(s(l));if(S&&await y.pathExists(s(b)))continue;let g=e[l];if(delete e[l],w.push(l),!S)continue;let x=!!g?.hash&&g.mtimeMs===(await y.stat(s(l))).mtimeMs;await y.move(s(l),s(b)),x&&await u(b,g.hash),await h(l)}for(let l of n.files||[]){let b=e[l.path];if(!b||!a(l.path))continue;let S=await y.stat(s(l.path)).catch(()=>null);!S||S.mtimeMs!==b.mtimeMs||await y.readFile(s(l.path),"utf-8")!==l.content&&(await y.outputFile(s(l.path),l.content),await u(l.path,tt(l.content)))}return w}function On(t){try{let e=JSON.parse(t.body).message;if(e)return e}catch{}return(t.body||t.message||String(t)).slice(0,300)}async function Mn(t,e,n,r,s){let i=new Set,a=await Rt(t),u=new Set(a),h=Object.keys(e).filter(g=>!u.has(g)&&e[g].hash&&le.test(g));if(h.length===0)return{renamed:0,held:i};let w=new Map;for(let g of a){if(e[g]||!le.test(g))continue;let x=await y.readFile(v.join(t,g),"utf-8");x.trim()&&w.set(g,tt(x))}let l=Bn(En(h,w,e),u);if(l.length===0)return{renamed:0,held:i};let b=await de(t),S=0;for(let g of l){let x;try{x=await ft(n,r,"POST","/rename",{from:g.from,to:g.to})}catch(D){let L=D,q=On(L);if(L.status>=400&&L.status<500)s(`\u26A0\uFE0F Couldn't rename ${g.from} \u2192 ${g.to}: ${q} Saving it as a new file.`);else{for(let z of g.pairs)i.add(z.to),b[z.to]=`rename from ${z.from} failed: ${q}`;s(`\u274C Error renaming ${g.from} \u2192 ${g.to}: ${q}`)}continue}let C=await Pn(t,e,x,g.pairs);for(let D of[...C,...g.pairs.map(L=>L.to)])delete b[D];let[k,...P]=x.message.split(`
|
|
7
|
+
`);s(`\u{1F500} ${k}`);for(let D of P)s(` ${D}`);S++}return await ge(t,b),{renamed:S,held:i}}var Dt="sync-errors.log",Ot=new Map;async function de(t){let e=v.resolve(t),n=Ot.get(e);if(n)return n;let r=await Rn(e);return Ot.set(e,r),r}async function Rn(t){let e=v.join(t,Dt);if(!await y.pathExists(e))return{};let n=await y.readFile(e,"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 ge(t,e){let n=v.join(t,Dt),r=Object.entries(e);if(r.length===0){await y.remove(n);return}await y.outputFile(n,r.map(([s,i])=>`${s}: ${i}`).join(`
|
|
8
9
|
`)+`
|
|
9
|
-
`)}async function Sn(t,e,n,r,s){Et.delete(S.resolve(t)),await m.remove(S.join(t,$t)),r("\u{1F4E5} Fetching files...");let o=await Ct(e,n,"GET","/get_files"),c={},h=0;for(let p of o){let x=S.join(t,p.path);await m.outputFile(x,p.content);let w=(await m.stat(x)).mtimeMs;h++,s?.(h,o.length),c[p.path]={mtimeMs:w}}return r(`\u2714\uFE0F Synced ${o.length} file(s).`),{fileMap:c,pulled:h}}import vn from"path";import Tn from"chokidar";var kn=5e3,Bn=/(^|[/\\])(\.DS_Store|Thumbs\.db|desktop\.ini)$/,V=class{viewsDir;onSync;debounceMs;watcher=null;debounceTimer=null;isShuttingDown=!1;dirty=!1;syncInFlight=!1;constructor(e){this.viewsDir=e.viewsDir,this.onSync=e.onSync,this.debounceMs=e.debounceMs??kn}start(){if(this.watcher)return;let e=vn.join(this.viewsDir,"src");this.watcher=Tn.watch([e],{persistent:!0,ignoreInitial:!0,ignored:Bn}).on("change",()=>this.scheduleSync()).on("add",()=>this.scheduleSync()).on("unlink",()=>this.scheduleSync())}setShuttingDown(e){this.isShuttingDown=e}async stop(){this.debounceTimer&&(clearTimeout(this.debounceTimer),this.debounceTimer=null),this.watcher&&(await this.watcher.close(),this.watcher=null)}scheduleSync(){this.isShuttingDown||(this.dirty=!0,this.debounceTimer&&clearTimeout(this.debounceTimer),this.debounceTimer=setTimeout(()=>this.flush(),this.debounceMs))}async flush(){if(this.debounceTimer=null,!(!this.dirty||this.isShuttingDown)){if(this.syncInFlight){this.debounceTimer=setTimeout(()=>this.flush(),this.debounceMs);return}this.dirty=!1,this.syncInFlight=!0;try{await this.onSync()}catch(e){let n=e;console.error("\u274C Sync failed:",n.body||n.message)}finally{this.syncInFlight=!1}}}};import gt from"fs-extra";import En from"os";import ae from"path";var oe={localhost:"http://app.sleekcms.test/api",development:"https://app.sleekcms.dev/api",production:"https://app.sleekcms.com/api"};function Ot(t){return oe[t]||oe.production}var Cn=ae.join(En.homedir(),"SleekCMS"),ht=ae.join(Cn,"auth.json"),Mt;function ce(t){Mt=t||void 0}function Dt(t){let e=[];return t&&e.push(`_auth=${t}`),Mt&&e.push(`_dev=${Mt}`),e.length?e.join("; "):void 0}var L=class extends Error{status;body;constructor(e,n,r=""){super(e),this.name="UserAuthError",this.status=n,this.body=r}};async function le(){try{let t=await gt.readJson(ht);return t&&t.userToken?t:null}catch{return null}}async function ue(t){await gt.outputJson(ht,t,{spaces:2});try{await gt.chmod(ht,384)}catch{}}async function de(){await gt.remove(ht)}async function q(t){let e={},n=Dt(t.userToken);n&&(e.Cookie=n),t.subdomain&&(e["x-subdomain"]=t.subdomain),t.slug&&(e.site=t.slug),t.body!==void 0&&(e["Content-Type"]="application/json");let r=await fetch(Ot(t.env)+t.path,{method:t.method,headers:e,body:t.body!==void 0?JSON.stringify(t.body):void 0});if(!r.ok){let s=await r.text().catch(()=>"");throw new L(`${t.method} ${t.path} \u2192 ${r.status}: ${s}`,r.status,s)}return r.json()}async function ge(t,e){let n={"Content-Type":"application/json"},r=Dt();r&&(n.Cookie=r);let s=await fetch(Ot(t)+"/auth/otp",{method:"PUT",headers:n,body:JSON.stringify({email:e})});if(!s.ok){let c=await s.text().catch(()=>"");throw new L(`Failed to send OTP \u2192 ${s.status}: ${c}`,s.status,c)}return{otp:(await s.json().catch(()=>({}))).do_not_use_in_code}}async function Lt(t,e,n){let r={"Content-Type":"application/json"},s=Dt();s&&(r.Cookie=s);let o=await fetch(Ot(t)+"/auth/login",{method:"POST",headers:r,body:JSON.stringify({email:e,otp:n})});if(!o.ok){let p=await o.text().catch(()=>"");throw new L(`Login failed \u2192 ${o.status}: ${p}`,o.status,p)}let c=o.headers.getSetCookie?.()??[],h=Pn(c);if(!h)throw new L("Login succeeded but no _auth cookie was returned.",500);return h}function Pn(t){for(let e of t){let n=e.match(/(?:^|;\s*)_auth=([^;]+)/);if(n&&n[1])return decodeURIComponent(n[1])}return null}async function Rt(t,e){return q({env:t,userToken:e,method:"GET",path:"/org"})}async function he(t,e,n){return q({env:t,userToken:e,method:"GET",path:"/site",subdomain:n})}function $n(t){if(t==null)return"(unknown error)";if(typeof t=="string")return t;if(typeof t!="object")return String(t);let e=t,n=typeof e.file_path=="string"?e.file_path:"",r=typeof e.path=="string"?e.path:typeof e.filename=="string"?e.filename:"",s=n||r,o=e.line!=null?`:${e.line}`:"",c=e.column!=null?`:${e.column}`:"",h=typeof e.message=="string"&&e.message?e.message:typeof e.name=="string"&&e.name?e.name:"",p=n&&r&&r!==n?` (on page ${r})`:"";if(s)return`${s}${o}${c}${p}: ${h||"(no message)"}`;if(h)return h;try{return JSON.stringify(t)}catch{return"(unknown error)"}}async function pe(t,e,n,r){let s;try{s=await q({env:t,userToken:e,method:"GET",path:"/site_build",subdomain:n,slug:r})}catch{return null}if(!Array.isArray(s)||s.length===0)return null;let o=s[0],c=s.find(p=>typeof p.url=="string"&&p.url.length>0),h=Array.isArray(o.errors)?o.errors.map($n):[];return h.length>0?{status:"error",mode:"preview",errors:h,url:c?.url||void 0,at:o.updated_at?Date.parse(o.updated_at):void 0}:c?{status:"success",mode:"preview",url:c.url||void 0,at:c.updated_at?Date.parse(c.updated_at):void 0}:null}async function fe(t,e,n,r){let{token:s}=await q({env:t,userToken:e,method:"POST",path:"/site_user/api_token",subdomain:n,slug:r,body:{refresh:!1}});return s}async function me(t,e,n){let r=await q({env:t,userToken:e,method:"POST",path:"/org",body:n?{name:n}:{}});return{id:r.id,name:r.name,subdomain:r.subdomain}}async function ye(t,e,n,r){let s=await q({env:t,userToken:e,method:"POST",path:"/site",subdomain:n,body:{name:r}});return{id:s.id,name:s.name,slug:s.slug}}var we=500;function Ln(){return new Date().toTimeString().slice(0,8)}function pt(t){let e=t;if(e.body){try{let n=JSON.parse(e.body);if(n&&typeof n.message=="string")return n.message;if(n&&typeof n.error=="string")return n.error}catch{}return e.body}return e.message}async function be(t){t&&(await Q.remove(At.join(t,"src")),await Q.remove(At.join(t,".cache","state.json")))}var ft=class extends Mn{env="production";userToken="";email;catalog=[];synced=new Map;message;messageIsError=!1;agentMd;buildScript;isShuttingDown=!1;constructor(e,n){super(),this.agentMd=e,this.buildScript=n}changed(){this.emit("change")}flash(e,n=!1){this.message=e,this.messageIsError=n,this.changed()}appendLog(e,n){let r=this.synced.get(e);r&&(r.logs.push({time:Ln(),text:n}),r.logs.length>we&&r.logs.splice(0,r.logs.length-we),this.changed())}siteCount(){return this.synced.size}syncingCount(){let e=0;for(let n of this.synced.values())n.status==="syncing"&&e++;return e}watchingCount(){let e=0;for(let n of this.synced.values())(n.status==="watching"||n.status==="syncing")&&e++;return e}async init(e,n,r){this.env=e,this.userToken=n,r&&(this.email=r);let s=await Rt(e,n);await this.buildCatalog(s)}async buildCatalog(e){let n=[];for(let r of e)try{n.push({org:r,sites:await he(this.env,this.userToken,r.subdomain)})}catch(s){if(s instanceof L&&s.status===401)throw s;n.push({org:r,sites:[]})}this.catalog=n,this.changed()}isSynced(e){return this.synced.has(e)}availableSites(){let e=[];for(let n of this.catalog)for(let r of n.sites)this.synced.has(r.id)||e.push({org:n.org,site:r});return e}async addSite(e,n){if(this.synced.has(n.id))return;let r;try{r=await fe(this.env,this.userToken,e.subdomain,n.slug)}catch(o){this.flash(`Failed to authorize "${n.name}": ${pt(o)}`,!0);return}let s={org:e,site:n,siteToken:r,viewsDir:"",status:"syncing",logs:[]};this.synced.set(n.id,s),this.appendLog(n.id,"\u2795 added \u2014 pulling files"),this.changed();try{let o=await Pt({token:r,env:this.env,agentMd:this.agentMd,buildScript:this.buildScript,log:c=>this.appendLog(n.id,c),onProgress:(c,h)=>{s.progress={done:c,total:h},this.changed()}});s.viewsDir=o.viewsDir,s.progress=void 0,s.status="watching",s.lastSyncedAt=Date.now(),s.lastMsg=`pulled ${o.pulled} file(s)`,s.watcher=new V({viewsDir:o.viewsDir,onSync:()=>this.runSync(n.id)}),s.watcher.start(),this.watchBuildStatus(n.id),this.seedLastBuild(n.id),this.appendLog(n.id,"\u{1F440} watching for changes")}catch(o){let c=o;s.status="error",s.progress=void 0,s.lastMsg=c.body||c.message,this.appendLog(n.id,`\u274C ${c.body||c.message}`)}this.changed()}async removeSite(e){let n=this.synced.get(e);n&&(n.watcher?.setShuttingDown(!0),await n.watcher?.stop(),await n.buildWatcher?.close(),await be(n.viewsDir),this.synced.delete(e),this.changed())}buildSite(e,n=!1){let r=this.synced.get(e);if(!r||!r.viewsDir)return;let s=n?"live":"preview";this.writeBuildFile(r.viewsDir,{status:"building",mode:s,stage:"queued",url:r.lastBuild?.url,at:Date.now()}),On("node",n?["build.mjs","--publish"]:["build.mjs"],{cwd:r.viewsDir,stdio:"ignore"}).on("error",h=>{this.writeBuildFile(r.viewsDir,{status:"error",mode:s,errors:[h.message],url:r.lastBuild?.url,at:Date.now()}),this.appendLog(e,`\u274C build: ${h.message}`)}),this.appendLog(e,n?"\u{1F680} publishing\u2026":"\u{1F528} building\u2026")}buildFile(e){return At.join(e,".cache","build.json")}async readBuildFile(e){try{let n=await Q.readJson(this.buildFile(e));return n&&n.status?n:null}catch{return null}}async writeBuildFile(e,n){let r=this.buildFile(e),s=`${r}.tmp`;try{await Q.outputJson(s,n),await Q.rename(s,r)}catch{}}watchBuildStatus(e){let n=this.synced.get(e);!n||!n.viewsDir||(n.buildWatcher=Dn.watch(this.buildFile(n.viewsDir),{ignoreInitial:!1}).on("add",()=>{this.readBuildStatus(e)}).on("change",()=>{this.readBuildStatus(e)}))}async readBuildStatus(e){let n=this.synced.get(e);if(!n)return;let r=await this.readBuildFile(n.viewsDir);if(!r)return;let s=n.lastBuild?.url;n.lastBuild={status:r.status,mode:r.mode??"preview",url:r.url??(r.status==="success"?void 0:s),errors:r.errors,stage:r.stage,at:r.at},this.changed()}async seedLastBuild(e){let n=this.synced.get(e);if(!n)return;let r=await pe(this.env,this.userToken,n.org.subdomain,n.site.slug);if(!r)return;let s=this.synced.get(e);if(!s)return;let o=await this.readBuildFile(s.viewsDir);o?.at!=null&&(r.at==null||o.at>=r.at)||await this.writeBuildFile(s.viewsDir,r)}async ensureOrg(e){if(e)return e;if(this.catalog[0])return this.catalog[0].org;let n;try{n=await Rt(this.env,this.userToken)}catch(r){return this.flash(`Couldn't check your organizations: ${pt(r)}`,!0),null}if(n.length>0){try{await this.buildCatalog(n)}catch{}return n[0]}try{let r=this.email?`${this.email.split("@")[0]}'s Sites`:"My Sites";this.flash("No organization yet \u2014 creating one\u2026");let s=await me(this.env,this.userToken,r);return this.catalog=[...this.catalog,{org:s,sites:[]}],this.changed(),s}catch(r){return this.flash(`Could not create an organization: ${pt(r)}`,!0),null}}async createNewSite(e,n){let r=await this.ensureOrg(n);if(!r)return;let s;try{s=await ye(this.env,this.userToken,r.subdomain,e)}catch(c){this.flash(`Could not create "${e}": ${pt(c)}`,!0);return}let o=this.catalog.find(c=>c.org.id===r.id);o&&!o.sites.some(c=>c.id===s.id)&&(o.sites=[...o.sites,s]),this.flash(`Created "${s.name}".`),await this.addSite(r,s)}async refetch(e){await this.runSync(e,{flush:!0})}async runSync(e,n={}){let r=this.synced.get(e);if(!(!r||!r.viewsDir||r.status==="paused")){r.status="syncing",this.changed();try{let s=await Pt({token:r.siteToken,env:this.env,viewsDir:r.viewsDir,agentMd:this.agentMd,buildScript:this.buildScript,flush:n.flush,log:o=>this.appendLog(e,o),onProgress:(o,c)=>{r.progress={done:o,total:c},this.changed()}});r.progress=void 0,r.status="watching",r.lastSyncedAt=Date.now(),n.flush?r.lastMsg=`re-fetched ${s.pulled} file(s)`:r.lastMsg=s.pushed>0?`pushed ${s.pushed} file(s)`:"up to date"}catch(s){let o=s;r.status="error",r.progress=void 0,r.lastMsg=o.body||o.message,this.appendLog(e,`\u274C ${o.body||o.message}`)}this.changed()}}async togglePause(e){let n=this.synced.get(e);!n||!n.viewsDir||(n.status==="paused"?(n.watcher=new V({viewsDir:n.viewsDir,onSync:()=>this.runSync(e)}),n.watcher.start(),n.status="watching",this.appendLog(e,"\u25B6 resumed \u2014 watching for changes")):(n.watcher?.setShuttingDown(!0),await n.watcher?.stop(),n.watcher=void 0,n.progress=void 0,n.status="paused",this.appendLog(e,"\u23F8 paused")),this.changed())}async cleanupAll(){for(let e of this.synced.values())e.watcher?.setShuttingDown(!0),await e.watcher?.stop(),await e.buildWatcher?.close(),await be(e.viewsDir)}async exit(){this.isShuttingDown||(this.isShuttingDown=!0,await this.cleanupAll(),this.emit("exit"))}async logout(){this.isShuttingDown||(this.isShuttingDown=!0,await de(),await this.cleanupAll(),this.emit("logout"))}};import{useEffect as Hn,useState as Me}from"react";import{Box as Jn,Text as Xn,useInput as Kn}from"ink";import{useState as Z}from"react";import{Box as tt,Text as H}from"ink";import xe from"ink-text-input";import{jsx as N,jsxs as J}from"react/jsx-runtime";function Se(t){let e=t;return e?.body||e?.message||String(t)}function ve({env:t,initialError:e,onAuthed:n}){let[r,s]=Z("email"),[o,c]=Z(""),[h,p]=Z(""),[x,w]=Z(e),[f,b]=Z(),v=async R=>{let E=R.trim();if(E){w(void 0),s("busy");try{let{otp:$}=await ge(t,E);if(c(E),$){let X=await Lt(t,E,$);await n(X,E);return}b(`OTP sent to ${E}`),s("otp")}catch($){w(Se($)),s("email")}}},O=async R=>{let E=R.trim();if(E){w(void 0),s("busy");try{let $=await Lt(t,o,E);await n($,o)}catch($){w(Se($)),p(""),s("otp")}}};return J(tt,{flexDirection:"column",children:[J(H,{bold:!0,children:["\u25C6 SleekCMS \u2014 sign in",t!=="production"?` (${t})`:""]}),x?J(H,{color:"red",children:["\u2717 ",x]}):null,r==="email"&&J(tt,{marginTop:1,children:[N(H,{children:"Email: "}),N(xe,{value:o,onChange:c,onSubmit:v,placeholder:"you@example.com"})]}),r==="otp"&&J(tt,{flexDirection:"column",marginTop:1,children:[f?N(H,{color:"green",children:f}):null,J(tt,{children:[N(H,{children:"Enter OTP: "}),N(xe,{value:h,onChange:p,onSubmit:O})]})]}),r==="busy"&&N(tt,{marginTop:1,children:N(H,{children:"Working\u2026"})})]})}import{Fragment as An,useEffect as et,useMemo as Fn,useState as C}from"react";import{Box as g,Text as l,useInput as jn,useStdout as _n}from"ink";import Be from"ink-text-input";import Rn from"node:os";function Ft(t){if(!t)return"\u2014";let e=Rn.homedir();return t.startsWith(e)?"~"+t.slice(e.length):t}function jt(t){if(!t)return"\u2014";let e=Math.floor((Date.now()-t)/1e3);if(e<5)return"just now";if(e<60)return`${e}s ago`;let n=Math.floor(e/60);if(n<60)return`${n}m ago`;let r=Math.floor(n/60);return r<24?`${r}h ago`:`${Math.floor(r/24)}d ago`}function yt(t){return!t||t.total<=0?null:Math.round(t.done/t.total*100)}var mt=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"];function Te(t){return mt[(t%mt.length+mt.length)%mt.length]}function wt(t){switch(t){case"syncing":return{dot:"\u25CF",color:"green",label:"syncing"};case"watching":return{dot:"\u25CB",color:"gray",label:"watching"};case"paused":return{dot:"\u2016",color:"yellow",label:"paused"};case"error":return{dot:"\u2717",color:"red",label:"error"}}}function ke(t){if(!t)return{dot:"\xB7",color:"gray",label:"\u2014"};switch(t.status){case"building":return{dot:"\u2234",color:"yellow",label:"building"};case"error":return{dot:"\u2717",color:"red",label:"failed"};case"success":return{dot:"\u2713",color:"green",label:t.mode==="live"?"live":"preview"}}}import{Fragment as nt,jsx as i,jsxs as u}from"react/jsx-runtime";var U=(t,e)=>e<=0?0:Math.max(0,Math.min(t,e-1)),In=16,Nn=5,Un="greenBright",Ee="blueBright",Ce="magentaBright";function Pe({k:t,bg:e=Un}){return i(l,{color:"black",backgroundColor:e,children:` ${t} `})}function j({k:t,label:e,width:n=16,bg:r}){return u(g,{width:n,children:[i(Pe,{k:t,bg:r}),i(l,{color:"gray",children:` ${e}`})]})}function B({k:t,label:e,bg:n}){return u(g,{marginBottom:1,children:[i(g,{width:9,children:i(Pe,{k:t,bg:n})}),i(l,{color:"gray",children:e})]})}function Wn({launchers:t}){return u(g,{flexDirection:"column",borderStyle:"round",borderColor:"green",paddingX:2,paddingY:1,width:66,children:[u(g,{justifyContent:"space-between",children:[i(l,{color:"green",bold:!0,children:"KEYBOARD SHORTCUTS"}),i(l,{color:"gray",children:"? or esc to close"})]}),u(g,{marginTop:1,children:[u(g,{flexDirection:"column",width:34,children:[i(l,{color:"cyan",bold:!0,children:"Navigate"}),i(B,{k:"\u2191\u2193",label:"move selection"}),i(B,{k:"tab",label:"focus logs \xB7 \u2191\u2193 scroll"}),i(B,{k:"esc",label:"clear filter / back"}),i(g,{marginTop:1,children:i(l,{color:"cyan",bold:!0,children:"Global"})}),i(B,{k:"/",label:"search sites"}),i(B,{k:"n",label:"new site"}),i(B,{k:"?",label:"toggle this help",bg:Ee}),i(B,{k:"q",label:"logout"}),i(B,{k:"ctrl+c",label:"quit"})]}),u(g,{flexDirection:"column",flexGrow:1,children:[i(l,{color:"cyan",bold:!0,children:"Site actions"}),i(B,{k:"s",label:"start sync"}),i(B,{k:"d",label:"disconnect"}),i(B,{k:"p",label:"pause / resume sync"}),i(B,{k:"b",label:"build preview"}),i(B,{k:"B",label:"publish live"}),i(B,{k:"o",label:"open preview url"}),t.map((e,n)=>i(B,{k:String(n+1),label:`open in ${e.label}`,bg:Ce},e.key))]})]})]})}function Gn({percent:t,width:e=28}){let n=Math.round(Math.max(0,Math.min(100,t))/100*e);return u(l,{children:[i(l,{color:"green",children:"\u2588".repeat(n)}),i(l,{color:"gray",children:"\u2591".repeat(Math.max(0,e-n))})]})}function qn({frame:t,width:e=24,seg:n=6}){let r=e+n,s=t%r-n,o="";for(let c=0;c<e;c++)o+=c>=s&&c<s+n?"\u25AE":"\u25AF";return i(l,{color:"yellow",children:o})}function $e({controller:t}){let{stdout:e}=_n(),n=Fn(()=>se(),[]),[,r]=C(0),[s,o]=C(()=>new Date),[c,h]=C(null),[p,x]=C(()=>t.synced.size>0||t.catalog.some(d=>d.sites.length>0)?"dashboard":t.catalog.length<=1?"newName":"newOrg"),[w,f]=C(0),[b,v]=C(""),[O,R]=C(!1),[E,$]=C(""),[X,rt]=C(!1),[Nt,Ae]=C(0),[Ut,Wt]=C(!1),[Fe,je]=C(!1),[st,it]=C("list"),[_e,K]=C(0),Gt=()=>r(a=>a+1);et(()=>(t.on("change",Gt),()=>{t.off("change",Gt)}),[t]),et(()=>{let a,d=()=>{o(new Date),a=setTimeout(d,6e4-Date.now()%6e4)};return a=setTimeout(d,6e4-Date.now()%6e4),()=>clearTimeout(a)},[]);let F=t.catalog.flatMap(a=>a.sites.map(d=>({org:a.org,site:d,entry:t.synced.get(d.id)}))).sort((a,d)=>(a.entry?0:1)-(d.entry?0:1)),_=E.trim().toLowerCase(),M=_?F.filter(a=>a.site.name.toLowerCase().includes(_)||(a.org.name||a.org.subdomain||"").toLowerCase().includes(_)):F,W=M.findIndex(a=>a.site.id===c);W<0&&(W=0);let T=M[W],y=T?.entry,ot=Math.max(3,(e?.rows??30)-26),I=y?.logs??[],xt=Math.max(0,I.length-ot),Ie=Math.min(_e,xt),at=I.length-Ie,qt=I.slice(Math.max(0,at-ot),at),Ne=T?.site.id;et(()=>K(0),[Ne]);let A=y?.lastBuild,St=A?.status==="building",vt=ke(A),ct=St;if(!ct){for(let a of t.synced.values())if(a.status==="syncing"){ct=!0;break}}et(()=>{if(!ct)return;let a=setInterval(()=>Ae(d=>d+1),120);return()=>clearInterval(a)},[ct]);let Ht=M.findIndex(a=>!a.entry),G=Math.max(Nn,(e?.rows??30)-In),z=M.length,lt=z>G,Ue=Math.floor(G/2),Y=lt?Math.max(0,Math.min(W-Ue,z-G)):0,Tt=Math.min(z,Y+G),We=M.slice(Y,Tt);et(()=>{p!=="dashboard"||O||F.length>0||(v(""),t.catalog.length<=1?x("newName"):(f(0),x("newOrg")))},[F.length,p,O,t.catalog.length]);let ut=a=>{if(M.length===0)return;let d=U(W+a,M.length);h(M[d].site.id)},Ge=()=>{if(T){if(T.entry){t.flash("Already syncing \u2014 press d to disconnect.");return}t.addSite(T.org,T.site)}},qe=()=>{T?.entry&&t.removeSite(T.site.id)},He=()=>{v(""),t.catalog.length<=1?x("newName"):(f(0),x("newOrg"))},Je=a=>{if(!y){t.flash("Press s to start syncing this site first.",!0);return}je(!0);let d=a.run(y.viewsDir);t.flash(d?`Opened ${a.label} at ${Ft(y.viewsDir)}`:`Couldn't open ${a.label}.`,!d)},Xe=()=>{if(!y){t.flash("Press s to start syncing this site first.",!0);return}t.togglePause(y.site.id)},Jt=a=>{if(!y){t.flash("Press s to start syncing this site first.",!0);return}t.buildSite(y.site.id,a),t.flash(a?"Publishing \u2014 making this live\u2026":"Building preview\u2026")},Ke=()=>{let a=y?.lastBuild?.url;if(!a){t.flash("No build URL yet \u2014 press b to build.",!0);return}t.flash(re(a)?`Opening ${a}`:"Couldn't open the browser.",!1)};jn((a,d)=>{if(Ut){(a==="?"||d.escape)&&Wt(!1);return}if(p==="newOrg"){d.escape?x("dashboard"):d.upArrow?f(k=>U(k-1,t.catalog.length)):d.downArrow?f(k=>U(k+1,t.catalog.length)):d.return&&x("newName");return}if(X){d.escape?(rt(!1),$(""),h(null)):d.return?rt(!1):d.upArrow?ut(-1):d.downArrow&&ut(1);return}if(d.tab){st==="logs"?it("list"):y&&it("logs"),K(0);return}if(st==="logs"){if(d.escape){it("list"),K(0);return}if(d.upArrow)return void K(k=>Math.min(k+1,xt));if(d.downArrow)return void K(k=>Math.max(0,k-1))}if(d.escape){$(""),h(null);return}if(a==="?")return void Wt(!0);if(a==="/")return it("list"),void rt(!0);if(a==="n")return void He();if(a==="q")return void t.logout();if(d.upArrow)return ut(-1);if(d.downArrow)return ut(1);if(a==="s")return Ge();if(a==="d")return qe();if(a==="p")return Xe();if(a==="b")return Jt(!1);if(a==="B")return Jt(!0);if(a==="o")return Ke();if(a>="1"&&a<="9"){let k=n[parseInt(a,10)-1];if(k)return Je(k)}},{isActive:p!=="newName"});let ze=async a=>{let d=a.trim();if(!d){x("dashboard");return}let k=t.catalog[U(w,t.catalog.length)]?.org??t.catalog[0]?.org;R(!0),x("dashboard"),await t.createNewSite(d,k),R(!1)},Xt=u(g,{justifyContent:"space-between",paddingX:1,children:[u(g,{children:[i(l,{color:"green",bold:!0,children:"\u25C6 SleekCMS"}),i(l,{color:"gray",children:" \u2502 "}),i(l,{children:t.email??"signed in"})]}),u(g,{children:[u(l,{color:"gray",children:[_?`${M.length}/${F.length}`:F.length," sites \xB7 "]}),u(l,{color:"green",children:[t.watchingCount()," watching"]}),i(l,{color:"gray",children:` \xB7 ${s.toTimeString().slice(0,5)}`})]})]});return Ut?u(g,{flexDirection:"column",children:[Xt,i(g,{marginTop:1,justifyContent:"center",children:i(Wn,{launchers:n})})]}):u(g,{flexDirection:"column",children:[Xt,u(g,{children:[u(g,{flexDirection:"column",borderStyle:"round",borderColor:"green",width:38,paddingX:1,children:[u(g,{justifyContent:"space-between",children:[i(l,{color:"green",bold:!0,children:"SITE WORKSPACES"}),F.length>G&&!X&&!_?i(l,{color:"gray",children:"/ filter"}):null]}),X?u(g,{children:[i(l,{color:"cyan",children:"/ "}),i(Be,{value:E,onChange:$,onSubmit:()=>rt(!1),placeholder:"type to filter\u2026"})]}):_?i(l,{color:"gray",children:`/ ${E} \xB7 ${M.length} match${M.length===1?"":"es"}`}):null,i(g,{flexDirection:"column",marginTop:1,minHeight:lt?G+3:void 0,children:M.length===0?i(l,{color:"gray",children:_?`no matches for \u201C${E}\u201D`:"no sites \u2014 press n to create one"}):u(nt,{children:[lt?i(l,{color:"gray",children:Y>0?` \u2191 ${Y} more`:" "}):null,We.map((a,d)=>{let k=Y+d,Kt=a.entry?wt(a.entry.status):{dot:"\xB7",color:"gray",label:"idle"},zt=yt(a.entry?.progress),Ye=a.entry?a.entry.status==="syncing"&&zt!=null?`${zt}%`:a.entry.status==="paused"?"paused":"--":"",dt=k===W,Ve=k===Ht&&Ht>0;return u(An,{children:[Ve?i(l,{color:"gray",children:"\u2500".repeat(34)}):null,u(g,{justifyContent:"space-between",children:[u(g,{children:[i(l,{color:dt?"green":"gray",children:dt?"\u276F ":" "}),u(l,{color:a.entry?.status==="watching"||a.entry?.status==="syncing"?"green":Kt.color,children:[a.entry?.status==="syncing"?Te(Nt):a.entry?.status==="watching"?"\u25CF":Kt.dot," "]}),i(l,{color:dt?"white":a.entry?void 0:"gray",bold:dt,children:a.site.name})]}),i(l,{color:a.entry?.status==="syncing"?"green":"gray",children:Ye})]})]},a.site.id)}),lt?i(l,{color:"gray",children:Tt<z?` \u2193 ${z-Tt} more`:" "}):null]})}),i(g,{marginTop:1,children:i(l,{color:"gray",children:"+ new site \xB7 n"})})]}),u(g,{flexDirection:"column",flexGrow:1,marginLeft:1,children:[i(g,{flexDirection:"column",borderStyle:"round",borderColor:"gray",paddingX:1,children:T?u(nt,{children:[u(l,{children:[T.org.name||T.org.subdomain?u(l,{color:"gray",children:[T.org.name||T.org.subdomain," / "]}):null,i(l,{color:"cyan",bold:!0,children:T.site.name})]}),u(g,{marginTop:1,children:[i(l,{color:"gray",children:"status "}),y?i(l,{color:wt(y.status).color,children:`: ${wt(y.status).label}`}):i(l,{color:"gray",children:": idle \u2014 not syncing (press s to start)"})]}),u(g,{children:[i(l,{color:"gray",children:"path "}),i(l,{wrap:"truncate-end",children:y?.viewsDir?Ft(y.viewsDir):"\u2014"})]}),u(g,{children:[i(l,{color:"gray",children:"synced "}),i(l,{children:jt(y?.lastSyncedAt)}),i(l,{color:"gray",children:" progress "}),i(l,{color:y?.status==="syncing"?"green":"gray",children:y?.status==="syncing"?`${yt(y.progress)??0}%`:"\u2014"})]}),i(g,{marginTop:1,children:i(Gn,{percent:y?y.status==="syncing"?yt(y.progress)??0:y.status==="watching"?100:0:0})}),y?u(nt,{children:[u(g,{marginTop:1,children:[i(l,{color:"gray",children:"build "}),i(l,{color:vt.color,children:`${vt.dot} ${vt.label}`}),i(l,{color:"gray",children:" last "}),i(l,{children:St?"in progress":jt(A?.at)})]}),u(g,{children:[i(l,{color:"gray",children:"url "}),A?.url?i(l,{color:"cyan",wrap:"truncate-end",children:A.url}):i(l,{color:"gray",children:"\u2014"})]}),A?.status==="error"&&A.errors?.length?u(g,{children:[i(l,{color:"red",children:"\u258C "}),i(l,{color:"red",wrap:"truncate-end",children:`\u2717 ${A.errors[0]}`})]}):null,St?u(g,{children:[i(qn,{frame:Nt}),i(l,{color:"gray",children:` ${A?.stage??"building"}`})]}):null]}):null]}):i(l,{color:"gray",children:"No site selected."})}),p==="dashboard"?u(g,{flexDirection:"column",borderStyle:"round",borderColor:st==="logs"?"green":"gray",paddingX:1,marginTop:1,height:ot+4,flexShrink:0,overflowY:"hidden",children:[u(g,{justifyContent:"space-between",children:[u(l,{children:[i(l,{color:"cyan",bold:!0,children:"LOGS \xB7 "}),i(l,{color:"white",children:T?.site.name??"\u2014"})]}),y&&I.length>0?st==="logs"?i(l,{color:"green",children:`\u2191\u2193 scroll \xB7 esc back${xt>0?` \xB7 ${at}/${I.length}`:""}`}):I.length>ot?i(l,{color:"gray",children:"tab to scroll"}):null:null]}),i(g,{flexDirection:"column",marginTop:1,children:!y||I.length===0?i(l,{color:"gray",children:"no activity yet"}):qt.map((a,d)=>u(l,{wrap:"truncate-end",children:[u(l,{color:"gray",children:[a.time," "]}),a.text]},at-qt.length+d))})]}):u(g,{flexDirection:"column",borderStyle:"round",borderColor:"blue",paddingX:1,marginTop:1,flexGrow:1,children:[p==="newOrg"&&u(nt,{children:[i(l,{color:"cyan",bold:!0,children:"CREATE SITE \u2014 PICK ORG"}),i(l,{color:"gray",children:"\u2191/\u2193 select \xB7 \u23CE next \xB7 esc cancel"}),i(g,{flexDirection:"column",marginTop:1,children:t.catalog.map((a,d)=>u(l,{color:d===U(w,t.catalog.length)?"blue":void 0,bold:d===U(w,t.catalog.length),children:[d===U(w,t.catalog.length)?"\u276F ":" ",a.org.name||a.org.subdomain]},a.org.id))})]}),p==="newName"&&u(nt,{children:[i(l,{color:"cyan",bold:!0,children:"CREATE SITE"}),u(g,{marginTop:1,children:[i(l,{children:"New site name: "}),i(Be,{value:b,onChange:v,onSubmit:ze})]}),i(l,{color:"gray",children:"\u23CE create \xB7 empty + \u23CE to cancel"})]})]})]})]}),t.message?i(g,{paddingX:1,children:u(l,{color:t.messageIsError?"red":"gray",wrap:"truncate-end",children:[t.messageIsError?"\u2717 ":"",t.message]})}):null,u(g,{flexDirection:"column",marginTop:1,children:[u(g,{paddingX:1,justifyContent:"space-between",children:[u(g,{flexWrap:"wrap",children:[i(j,{k:"\u2191\u2193",label:"select"}),i(j,{k:"/",label:"search"}),i(j,{k:"s",label:"start sync"}),i(j,{k:"d",label:"disconnect"}),F.length===0?i(j,{k:"n",label:"new site"}):null,i(j,{k:"ctrl+c",label:"quit",width:14})]}),i(j,{k:"?",label:"all keys",width:12,bg:Ee})]}),!Fe&&n.length>0?i(g,{paddingX:1,marginTop:1,flexWrap:"wrap",children:n.map((a,d)=>i(j,{k:String(d+1),label:a.label,bg:Ce},a.key))}):null]})]})}import{jsx as bt}from"react/jsx-runtime";function Oe(t){let e=t;return e?.body||e?.message||String(t)}function De({controller:t,env:e,devSecret:n,savedToken:r,savedEmail:s}){let[o,c]=Me(r?"loading":"auth"),[h,p]=Me();Kn((w,f)=>{f.ctrl&&w==="c"&&t.exit()}),Hn(()=>{r&&t.init(e,r,s).then(()=>c("manage")).catch(w=>{w instanceof L&&w.status===401?p("Saved session expired \u2014 please sign in again."):p(Oe(w)),c("auth")})},[]);let x=async(w,f)=>{p(void 0),await ue({env:e,userToken:w,devSecret:n,email:f}),c("loading");try{await t.init(e,w,f),c("manage")}catch(b){p(Oe(b)),c("auth")}};return o==="auth"?bt(ve,{env:e,initialError:h,onAuthed:x}):o==="loading"?bt(Jn,{children:bt(Xn,{children:"Loading your sites\u2026 "})}):bt($e,{controller:t})}import{jsx as nr}from"react/jsx-runtime";var Qn=Yn(import.meta.url),_t=te();if(_t.version){let t=Qn("../package.json");console.log(t.version),process.exit(0)}var Re=It.dirname(zn(import.meta.url)),Zn=Le(It.join(Re,"AGENT.md"),"utf-8"),tr=Le(It.join(Re,"build.mjs"),"utf-8");async function er(){let t=new ft(Zn,tr),e=await le(),n=(_t.env??e?.env??"production").toLowerCase(),r=_t.dev??process.env.SLEEKCMS_DEV_SECRET??e?.devSecret;ce(r),process.stdout.isTTY&&process.stdout.write("\x1B[2J\x1B[3J\x1B[H");let s=Vn(nr(De,{controller:t,env:n,devSecret:r,savedToken:e?.userToken,savedEmail:e?.email}),{exitOnCtrlC:!1}),o=c=>{s.unmount(),console.log(c),process.exit(0)};t.once("exit",()=>o("\u2713 Stopped. Local site files were cleaned up.")),t.once("logout",()=>o("\u{1F44B} Logged out. Run `sleekcms` to sign in again.")),process.on("SIGINT",()=>{t.exit()}),process.on("SIGTERM",()=>{t.exit()}),await s.waitUntilExit()}er().catch(t=>{let e=t;console.error("\u274C",e.body||e.message),process.exit(1)});
|
|
10
|
+
`)}async function Dn(t,e,n,r,s){Ot.delete(v.resolve(t)),await y.remove(v.join(t,Dt)),r("\u{1F4E5} Fetching files...");let i=await ft(e,n,"GET","/get_files"),a={},u=0;for(let h of i){let w=v.join(t,h.path);await y.outputFile(w,h.content);let l=(await y.stat(w)).mtimeMs;u++,s?.(u,i.length),a[h.path]={mtimeMs:l,hash:tt(h.content)}}return r(`\u2714\uFE0F Synced ${i.length} file(s).`),{fileMap:a,pulled:u}}import Ln from"path";import jn from"chokidar";var Fn=5e3,An=/(^|[/\\])(\.DS_Store|Thumbs\.db|desktop\.ini)$/,et=class{viewsDir;onSync;debounceMs;watcher=null;debounceTimer=null;isShuttingDown=!1;dirty=!1;syncInFlight=!1;constructor(e){this.viewsDir=e.viewsDir,this.onSync=e.onSync,this.debounceMs=e.debounceMs??Fn}start(){if(this.watcher)return;let e=Ln.join(this.viewsDir,"src");this.watcher=jn.watch([e],{persistent:!0,ignoreInitial:!0,ignored:An}).on("change",()=>this.scheduleSync()).on("add",()=>this.scheduleSync()).on("unlink",()=>this.scheduleSync()).on("addDir",()=>this.scheduleSync()).on("unlinkDir",()=>this.scheduleSync())}setShuttingDown(e){this.isShuttingDown=e}async stop(){this.debounceTimer&&(clearTimeout(this.debounceTimer),this.debounceTimer=null),this.watcher&&(await this.watcher.close(),this.watcher=null)}scheduleSync(){this.isShuttingDown||(this.dirty=!0,this.debounceTimer&&clearTimeout(this.debounceTimer),this.debounceTimer=setTimeout(()=>this.flush(),this.debounceMs))}async flush(){if(this.debounceTimer=null,!(!this.dirty||this.isShuttingDown)){if(this.syncInFlight){this.debounceTimer=setTimeout(()=>this.flush(),this.debounceMs);return}this.dirty=!1,this.syncInFlight=!0;try{await this.onSync()}catch(e){let n=e;console.error("\u274C Sync failed:",n.body||n.message)}finally{this.syncInFlight=!1}}}};import pt from"fs-extra";import _n from"os";import he from"path";var me={localhost:"http://app.sleekcms.test/api",development:"https://app.sleekcms.dev/api",production:"https://app.sleekcms.com/api"};function jt(t){return me[t]||me.production}var In=he.join(_n.homedir(),"SleekCMS"),yt=he.join(In,"auth.json"),Lt;function fe(t){Lt=t||void 0}function Ft(t){let e=[];return t&&e.push(`_auth=${t}`),Lt&&e.push(`_dev=${Lt}`),e.length?e.join("; "):void 0}var F=class extends Error{status;body;constructor(e,n,r=""){super(e),this.name="UserAuthError",this.status=n,this.body=r}};async function pe(){try{let t=await pt.readJson(yt);return t&&t.userToken?t:null}catch{return null}}async function ye(t){await pt.outputJson(yt,t,{spaces:2});try{await pt.chmod(yt,384)}catch{}}async function we(){await pt.remove(yt)}async function K(t){let e={},n=Ft(t.userToken);n&&(e.Cookie=n),t.subdomain&&(e["x-subdomain"]=t.subdomain),t.slug&&(e.site=t.slug),t.body!==void 0&&(e["Content-Type"]="application/json");let r=await fetch(jt(t.env)+t.path,{method:t.method,headers:e,body:t.body!==void 0?JSON.stringify(t.body):void 0});if(!r.ok){let s=await r.text().catch(()=>"");throw new F(`${t.method} ${t.path} \u2192 ${r.status}: ${s}`,r.status,s)}return r.json()}async function be(t,e){let n={"Content-Type":"application/json"},r=Ft();r&&(n.Cookie=r);let s=await fetch(jt(t)+"/auth/otp",{method:"PUT",headers:n,body:JSON.stringify({email:e})});if(!s.ok){let a=await s.text().catch(()=>"");throw new F(`Failed to send OTP \u2192 ${s.status}: ${a}`,s.status,a)}return{otp:(await s.json().catch(()=>({}))).do_not_use_in_code}}async function At(t,e,n){let r={"Content-Type":"application/json"},s=Ft();s&&(r.Cookie=s);let i=await fetch(jt(t)+"/auth/login",{method:"POST",headers:r,body:JSON.stringify({email:e,otp:n})});if(!i.ok){let h=await i.text().catch(()=>"");throw new F(`Login failed \u2192 ${i.status}: ${h}`,i.status,h)}let a=i.headers.getSetCookie?.()??[],u=Nn(a);if(!u)throw new F("Login succeeded but no _auth cookie was returned.",500);return u}function Nn(t){for(let e of t){let n=e.match(/(?:^|;\s*)_auth=([^;]+)/);if(n&&n[1])return decodeURIComponent(n[1])}return null}async function _t(t,e){return K({env:t,userToken:e,method:"GET",path:"/org"})}async function Se(t,e,n){return K({env:t,userToken:e,method:"GET",path:"/site",subdomain:n})}function Wn(t){if(t==null)return"(unknown error)";if(typeof t=="string")return t;if(typeof t!="object")return String(t);let e=t,n=typeof e.file_path=="string"?e.file_path:"",r=typeof e.path=="string"?e.path:typeof e.filename=="string"?e.filename:"",s=n||r,i=e.line!=null?`:${e.line}`:"",a=e.column!=null?`:${e.column}`:"",u=typeof e.message=="string"&&e.message?e.message:typeof e.name=="string"&&e.name?e.name:"",h=n&&r&&r!==n?` (on page ${r})`:"";if(s)return`${s}${i}${a}${h}: ${u||"(no message)"}`;if(u)return u;try{return JSON.stringify(t)}catch{return"(unknown error)"}}async function xe(t,e,n,r){let s;try{s=await K({env:t,userToken:e,method:"GET",path:"/site_build",subdomain:n,slug:r})}catch{return null}if(!Array.isArray(s)||s.length===0)return null;let i=s[0],a=s.find(h=>typeof h.url=="string"&&h.url.length>0),u=Array.isArray(i.errors)?i.errors.map(Wn):[];return u.length>0?{status:"error",mode:"preview",errors:u,url:a?.url||void 0,at:i.updated_at?Date.parse(i.updated_at):void 0}:a?{status:"success",mode:"preview",url:a.url||void 0,at:a.updated_at?Date.parse(a.updated_at):void 0}:null}async function ve(t,e,n,r){let{token:s}=await K({env:t,userToken:e,method:"POST",path:"/site_user/api_token",subdomain:n,slug:r,body:{refresh:!1}});return s}async function Te(t,e,n){let r=await K({env:t,userToken:e,method:"POST",path:"/org",body:n?{name:n}:{}});return{id:r.id,name:r.name,subdomain:r.subdomain}}async function ke(t,e,n,r){let s=await K({env:t,userToken:e,method:"POST",path:"/site",subdomain:n,body:{name:r}});return{id:s.id,name:s.name,slug:s.slug}}var $e=500;function Hn(){return new Date().toTimeString().slice(0,8)}function wt(t){let e=t;if(e.body){try{let n=JSON.parse(e.body);if(n&&typeof n.message=="string")return n.message;if(n&&typeof n.error=="string")return n.error}catch{}return e.body}return e.message}async function Ee(t){t&&(await nt.remove(It.join(t,"src")),await nt.remove(It.join(t,".cache","state.json")))}var bt=class extends Gn{env="production";userToken="";email;catalog=[];synced=new Map;message;messageIsError=!1;agentMd;buildScript;isShuttingDown=!1;constructor(e,n){super(),this.agentMd=e,this.buildScript=n}changed(){this.emit("change")}flash(e,n=!1){this.message=e,this.messageIsError=n,this.changed()}appendLog(e,n){let r=this.synced.get(e);r&&(r.logs.push({time:Hn(),text:n}),r.logs.length>$e&&r.logs.splice(0,r.logs.length-$e),this.changed())}siteCount(){return this.synced.size}syncingCount(){let e=0;for(let n of this.synced.values())n.status==="syncing"&&e++;return e}watchingCount(){let e=0;for(let n of this.synced.values())(n.status==="watching"||n.status==="syncing")&&e++;return e}async init(e,n,r){this.env=e,this.userToken=n,r&&(this.email=r);let s=await _t(e,n);await this.buildCatalog(s)}async buildCatalog(e){let n=[];for(let r of e)try{n.push({org:r,sites:await Se(this.env,this.userToken,r.subdomain)})}catch(s){if(s instanceof F&&s.status===401)throw s;n.push({org:r,sites:[]})}this.catalog=n,this.changed()}isSynced(e){return this.synced.has(e)}availableSites(){let e=[];for(let n of this.catalog)for(let r of n.sites)this.synced.has(r.id)||e.push({org:n.org,site:r});return e}async addSite(e,n){if(this.synced.has(n.id))return;let r;try{r=await ve(this.env,this.userToken,e.subdomain,n.slug)}catch(i){this.flash(`Failed to authorize "${n.name}": ${wt(i)}`,!0);return}let s={org:e,site:n,siteToken:r,viewsDir:"",status:"syncing",logs:[]};this.synced.set(n.id,s),this.appendLog(n.id,"\u2795 added \u2014 pulling files"),this.changed();try{let i=await Mt({token:r,env:this.env,agentMd:this.agentMd,buildScript:this.buildScript,log:a=>this.appendLog(n.id,a),onProgress:(a,u)=>{s.progress={done:a,total:u},this.changed()}});s.viewsDir=i.viewsDir,s.progress=void 0,s.status="watching",s.lastSyncedAt=Date.now(),s.lastMsg=`pulled ${i.pulled} file(s)`,s.watcher=new et({viewsDir:i.viewsDir,onSync:()=>this.runSync(n.id)}),s.watcher.start(),this.watchBuildStatus(n.id),this.seedLastBuild(n.id),this.appendLog(n.id,"\u{1F440} watching for changes")}catch(i){let a=i;s.status="error",s.progress=void 0,s.lastMsg=a.body||a.message,this.appendLog(n.id,`\u274C ${a.body||a.message}`)}this.changed()}async removeSite(e){let n=this.synced.get(e);n&&(n.watcher?.setShuttingDown(!0),await n.watcher?.stop(),await n.buildWatcher?.close(),await Ee(n.viewsDir),this.synced.delete(e),this.changed())}buildSite(e,n=!1){let r=this.synced.get(e);if(!r||!r.viewsDir)return;let s=n?"live":"preview";this.writeBuildFile(r.viewsDir,{status:"building",mode:s,stage:"queued",url:r.lastBuild?.url,at:Date.now()}),Un("node",n?["build.mjs","--publish"]:["build.mjs"],{cwd:r.viewsDir,stdio:"ignore"}).on("error",u=>{this.writeBuildFile(r.viewsDir,{status:"error",mode:s,errors:[u.message],url:r.lastBuild?.url,at:Date.now()}),this.appendLog(e,`\u274C build: ${u.message}`)}),this.appendLog(e,n?"\u{1F680} publishing\u2026":"\u{1F528} building\u2026")}buildFile(e){return It.join(e,".cache","build.json")}async readBuildFile(e){try{let n=await nt.readJson(this.buildFile(e));return n&&n.status?n:null}catch{return null}}async writeBuildFile(e,n){let r=this.buildFile(e),s=`${r}.tmp`;try{await nt.outputJson(s,n),await nt.rename(s,r)}catch{}}watchBuildStatus(e){let n=this.synced.get(e);!n||!n.viewsDir||(n.buildWatcher=qn.watch(this.buildFile(n.viewsDir),{ignoreInitial:!1}).on("add",()=>{this.readBuildStatus(e)}).on("change",()=>{this.readBuildStatus(e)}))}async readBuildStatus(e){let n=this.synced.get(e);if(!n)return;let r=await this.readBuildFile(n.viewsDir);if(!r)return;let s=n.lastBuild?.url;n.lastBuild={status:r.status,mode:r.mode??"preview",url:r.url??(r.status==="success"?void 0:s),errors:r.errors,stage:r.stage,at:r.at},this.changed()}async seedLastBuild(e){let n=this.synced.get(e);if(!n)return;let r=await xe(this.env,this.userToken,n.org.subdomain,n.site.slug);if(!r)return;let s=this.synced.get(e);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 ensureOrg(e){if(e)return e;if(this.catalog[0])return this.catalog[0].org;let n;try{n=await _t(this.env,this.userToken)}catch(r){return this.flash(`Couldn't check your organizations: ${wt(r)}`,!0),null}if(n.length>0){try{await this.buildCatalog(n)}catch{}return n[0]}try{let r=this.email?`${this.email.split("@")[0]}'s Sites`:"My Sites";this.flash("No organization yet \u2014 creating one\u2026");let s=await Te(this.env,this.userToken,r);return this.catalog=[...this.catalog,{org:s,sites:[]}],this.changed(),s}catch(r){return this.flash(`Could not create an organization: ${wt(r)}`,!0),null}}async createNewSite(e,n){let r=await this.ensureOrg(n);if(!r)return;let s;try{s=await ke(this.env,this.userToken,r.subdomain,e)}catch(a){this.flash(`Could not create "${e}": ${wt(a)}`,!0);return}let i=this.catalog.find(a=>a.org.id===r.id);i&&!i.sites.some(a=>a.id===s.id)&&(i.sites=[...i.sites,s]),this.flash(`Created "${s.name}".`),await this.addSite(r,s)}async refetch(e){await this.runSync(e,{flush:!0})}async runSync(e,n={}){let r=this.synced.get(e);if(!(!r||!r.viewsDir||r.status==="paused")){r.status="syncing",this.changed();try{let s=await Mt({token:r.siteToken,env:this.env,viewsDir:r.viewsDir,agentMd:this.agentMd,buildScript:this.buildScript,flush:n.flush,log:i=>this.appendLog(e,i),onProgress:(i,a)=>{r.progress={done:i,total:a},this.changed()}});if(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){let i=s;r.status="error",r.progress=void 0,r.lastMsg=i.body||i.message,this.appendLog(e,`\u274C ${i.body||i.message}`)}this.changed()}}async togglePause(e){let n=this.synced.get(e);!n||!n.viewsDir||(n.status==="paused"?(n.watcher=new et({viewsDir:n.viewsDir,onSync:()=>this.runSync(e)}),n.watcher.start(),n.status="watching",this.appendLog(e,"\u25B6 resumed \u2014 watching for changes")):(n.watcher?.setShuttingDown(!0),await n.watcher?.stop(),n.watcher=void 0,n.progress=void 0,n.status="paused",this.appendLog(e,"\u23F8 paused")),this.changed())}async cleanupAll(){for(let e of this.synced.values())e.watcher?.setShuttingDown(!0),await e.watcher?.stop(),await e.buildWatcher?.close(),await Ee(e.viewsDir)}async exit(){this.isShuttingDown||(this.isShuttingDown=!0,await this.cleanupAll(),this.emit("exit"))}async logout(){this.isShuttingDown||(this.isShuttingDown=!0,await we(),await this.cleanupAll(),this.emit("logout"))}};import{useEffect as rr,useState as Ae}from"react";import{Box as sr,Text as ir,useInput as or}from"ink";import{useState as rt}from"react";import{Box as st,Text as Y}from"ink";import Be from"ink-text-input";import{jsx as G,jsxs as X}from"react/jsx-runtime";function Ce(t){let e=t;return e?.body||e?.message||String(t)}function Pe({env:t,initialError:e,onAuthed:n}){let[r,s]=rt("email"),[i,a]=rt(""),[u,h]=rt(""),[w,l]=rt(e),[b,S]=rt(),g=async C=>{let k=C.trim();if(k){l(void 0),s("busy");try{let{otp:P}=await be(t,k);if(a(k),P){let D=await At(t,k,P);await n(D,k);return}S(`OTP sent to ${k}`),s("otp")}catch(P){l(Ce(P)),s("email")}}},x=async C=>{let k=C.trim();if(k){l(void 0),s("busy");try{let P=await At(t,i,k);await n(P,i)}catch(P){l(Ce(P)),h(""),s("otp")}}};return X(st,{flexDirection:"column",children:[X(Y,{bold:!0,children:["\u25C6 SleekCMS \u2014 sign in",t!=="production"?` (${t})`:""]}),w?X(Y,{color:"red",children:["\u2717 ",w]}):null,r==="email"&&X(st,{marginTop:1,children:[G(Y,{children:"Email: "}),G(Be,{value:i,onChange:a,onSubmit:g,placeholder:"you@example.com"})]}),r==="otp"&&X(st,{flexDirection:"column",marginTop:1,children:[b?G(Y,{color:"green",children:b}):null,X(st,{children:[G(Y,{children:"Enter OTP: "}),G(Be,{value:u,onChange:h,onSubmit:x})]})]}),r==="busy"&&G(st,{marginTop:1,children:G(Y,{children:"Working\u2026"})})]})}import{Fragment as Kn,useEffect as it,useMemo as Yn,useState as O}from"react";import{Box as p,Text as d,useInput as Xn,useStdout as zn}from"ink";import Re from"ink-text-input";import Jn from"node:os";function Nt(t){if(!t)return"\u2014";let e=Jn.homedir();return t.startsWith(e)?"~"+t.slice(e.length):t}function Wt(t){if(!t)return"\u2014";let e=Math.floor((Date.now()-t)/1e3);if(e<5)return"just now";if(e<60)return`${e}s ago`;let n=Math.floor(e/60);if(n<60)return`${n}m ago`;let r=Math.floor(n/60);return r<24?`${r}h ago`:`${Math.floor(r/24)}d ago`}function xt(t){return!t||t.total<=0?null:Math.round(t.done/t.total*100)}var St=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"];function Oe(t){return St[(t%St.length+St.length)%St.length]}function vt(t){switch(t){case"syncing":return{dot:"\u25CF",color:"green",label:"syncing"};case"watching":return{dot:"\u25CB",color:"gray",label:"watching"};case"paused":return{dot:"\u2016",color:"yellow",label:"paused"};case"error":return{dot:"\u2717",color:"red",label:"error"}}}function Me(t){if(!t)return{dot:"\xB7",color:"gray",label:"\u2014"};switch(t.status){case"building":return{dot:"\u2234",color:"yellow",label:"building"};case"error":return{dot:"\u2717",color:"red",label:"failed"};case"success":return{dot:"\u2713",color:"green",label:t.mode==="live"?"live":"preview"}}}import{Fragment as ot,jsx as o,jsxs as m}from"react/jsx-runtime";var U=(t,e)=>e<=0?0:Math.max(0,Math.min(t,e-1)),Vn=16,Qn=5,Zn="greenBright",De="blueBright",Le="magentaBright";function je({k:t,bg:e=Zn}){return o(d,{color:"black",backgroundColor:e,children:` ${t} `})}function I({k:t,label:e,width:n=16,bg:r}){return m(p,{width:n,children:[o(je,{k:t,bg:r}),o(d,{color:"gray",children:` ${e}`})]})}function B({k:t,label:e,bg:n}){return m(p,{marginBottom:1,children:[o(p,{width:9,children:o(je,{k:t,bg:n})}),o(d,{color:"gray",children:e})]})}function tr({launchers:t}){return m(p,{flexDirection:"column",borderStyle:"round",borderColor:"green",paddingX:2,paddingY:1,width:66,children:[m(p,{justifyContent:"space-between",children:[o(d,{color:"green",bold:!0,children:"KEYBOARD SHORTCUTS"}),o(d,{color:"gray",children:"? or esc to close"})]}),m(p,{marginTop:1,children:[m(p,{flexDirection:"column",width:34,children:[o(d,{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(p,{marginTop:1,children:o(d,{color:"cyan",bold:!0,children:"Global"})}),o(B,{k:"/",label:"search sites"}),o(B,{k:"n",label:"new site"}),o(B,{k:"?",label:"toggle this help",bg:De}),o(B,{k:"q",label:"logout"}),o(B,{k:"ctrl+c",label:"quit"})]}),m(p,{flexDirection:"column",flexGrow:1,children:[o(d,{color:"cyan",bold:!0,children:"Site actions"}),o(B,{k:"s",label:"start sync"}),o(B,{k:"d",label:"disconnect"}),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"}),t.map((e,n)=>o(B,{k:String(n+1),label:`open in ${e.label}`,bg:Le},e.key))]})]})]})}function er({percent:t,width:e=28}){let n=Math.round(Math.max(0,Math.min(100,t))/100*e);return m(d,{children:[o(d,{color:"green",children:"\u2588".repeat(n)}),o(d,{color:"gray",children:"\u2591".repeat(Math.max(0,e-n))})]})}function nr({frame:t,width:e=24,seg:n=6}){let r=e+n,s=t%r-n,i="";for(let a=0;a<e;a++)i+=a>=s&&a<s+n?"\u25AE":"\u25AF";return o(d,{color:"yellow",children:i})}function Fe({controller:t}){let{stdout:e}=zn(),n=Yn(()=>ae(),[]),[,r]=O(0),[s,i]=O(()=>new Date),[a,u]=O(null),[h,w]=O(()=>t.synced.size>0||t.catalog.some(f=>f.sites.length>0)?"dashboard":t.catalog.length<=1?"newName":"newOrg"),[l,b]=O(0),[S,g]=O(""),[x,C]=O(!1),[k,P]=O(""),[D,L]=O(!1),[q,z]=O(0),[qt,Ht]=O(!1),[Ge,Ue]=O(!1),[at,ct]=O("list"),[qe,V]=O(0),Jt=()=>r(c=>c+1);it(()=>(t.on("change",Jt),()=>{t.off("change",Jt)}),[t]),it(()=>{let c,f=()=>{i(new Date),c=setTimeout(f,6e4-Date.now()%6e4)};return c=setTimeout(f,6e4-Date.now()%6e4),()=>clearTimeout(c)},[]);let _=t.catalog.flatMap(c=>c.sites.map(f=>({org:c.org,site:f,entry:t.synced.get(f.id)}))).sort((c,f)=>(c.entry?0:1)-(f.entry?0:1)),N=k.trim().toLowerCase(),R=N?_.filter(c=>c.site.name.toLowerCase().includes(N)||(c.org.name||c.org.subdomain||"").toLowerCase().includes(N)):_,H=R.findIndex(c=>c.site.id===a);H<0&&(H=0);let $=R[H],T=$?.entry,lt=Math.max(3,(e?.rows??30)-26),W=T?.logs??[],kt=Math.max(0,W.length-lt),He=Math.min(qe,kt),ut=W.length-He,Kt=W.slice(Math.max(0,ut-lt),ut),Je=$?.site.id;it(()=>V(0),[Je]);let A=T?.lastBuild,$t=A?.status==="building",Et=Me(A),dt=$t;if(!dt){for(let c of t.synced.values())if(c.status==="syncing"){dt=!0;break}}it(()=>{if(!dt)return;let c=setInterval(()=>z(f=>f+1),120);return()=>clearInterval(c)},[dt]);let Yt=R.findIndex(c=>!c.entry),J=Math.max(Qn,(e?.rows??30)-Vn),Q=R.length,gt=Q>J,Ke=Math.floor(J/2),Z=gt?Math.max(0,Math.min(H-Ke,Q-J)):0,Bt=Math.min(Q,Z+J),Ye=R.slice(Z,Bt);it(()=>{h!=="dashboard"||x||_.length>0||(g(""),t.catalog.length<=1?w("newName"):(b(0),w("newOrg")))},[_.length,h,x,t.catalog.length]);let mt=c=>{if(R.length===0)return;let f=U(H+c,R.length);u(R[f].site.id)},Xe=()=>{if($){if($.entry){t.flash("Already syncing \u2014 press d to disconnect.");return}t.addSite($.org,$.site)}},ze=()=>{$?.entry&&t.removeSite($.site.id)},Ve=()=>{g(""),t.catalog.length<=1?w("newName"):(b(0),w("newOrg"))},Qe=c=>{if(!T){t.flash("Press s to start syncing this site first.",!0);return}Ue(!0);let f=c.run(T.viewsDir);t.flash(f?`Opened ${c.label} at ${Nt(T.viewsDir)}`:`Couldn't open ${c.label}.`,!f)},Ze=()=>{if(!T){t.flash("Press s to start syncing this site first.",!0);return}t.togglePause(T.site.id)},Xt=c=>{if(!T){t.flash("Press s to start syncing this site first.",!0);return}t.buildSite(T.site.id,c),t.flash(c?"Publishing \u2014 making this live\u2026":"Building preview\u2026")},tn=()=>{let c=T?.lastBuild?.url;if(!c){t.flash("No build URL yet \u2014 press b to build.",!0);return}t.flash(oe(c)?`Opening ${c}`:"Couldn't open the browser.",!1)};Xn((c,f)=>{if(qt){(c==="?"||f.escape)&&Ht(!1);return}if(h==="newOrg"){f.escape?w("dashboard"):f.upArrow?b(E=>U(E-1,t.catalog.length)):f.downArrow?b(E=>U(E+1,t.catalog.length)):f.return&&w("newName");return}if(D){f.escape?(L(!1),P(""),u(null)):f.return?L(!1):f.upArrow?mt(-1):f.downArrow&&mt(1);return}if(f.tab){at==="logs"?ct("list"):T&&ct("logs"),V(0);return}if(at==="logs"){if(f.escape){ct("list"),V(0);return}if(f.upArrow)return void V(E=>Math.min(E+1,kt));if(f.downArrow)return void V(E=>Math.max(0,E-1))}if(f.escape){P(""),u(null);return}if(c==="?")return void Ht(!0);if(c==="/")return ct("list"),void L(!0);if(c==="n")return void Ve();if(c==="q")return void t.logout();if(f.upArrow)return mt(-1);if(f.downArrow)return mt(1);if(c==="s")return Xe();if(c==="d")return ze();if(c==="p")return Ze();if(c==="b")return Xt(!1);if(c==="B")return Xt(!0);if(c==="o")return tn();if(c>="1"&&c<="9"){let E=n[parseInt(c,10)-1];if(E)return Qe(E)}},{isActive:h!=="newName"});let en=async c=>{let f=c.trim();if(!f){w("dashboard");return}let E=t.catalog[U(l,t.catalog.length)]?.org??t.catalog[0]?.org;C(!0),w("dashboard"),await t.createNewSite(f,E),C(!1)},zt=m(p,{justifyContent:"space-between",paddingX:1,children:[m(p,{children:[o(d,{color:"green",bold:!0,children:"\u25C6 SleekCMS"}),o(d,{color:"gray",children:" \u2502 "}),o(d,{children:t.email??"signed in"})]}),m(p,{children:[m(d,{color:"gray",children:[N?`${R.length}/${_.length}`:_.length," sites \xB7 "]}),m(d,{color:"green",children:[t.watchingCount()," watching"]}),o(d,{color:"gray",children:` \xB7 ${s.toTimeString().slice(0,5)}`})]})]});return qt?m(p,{flexDirection:"column",children:[zt,o(p,{marginTop:1,justifyContent:"center",children:o(tr,{launchers:n})})]}):m(p,{flexDirection:"column",children:[zt,m(p,{children:[m(p,{flexDirection:"column",borderStyle:"round",borderColor:"green",width:38,paddingX:1,children:[m(p,{justifyContent:"space-between",children:[o(d,{color:"green",bold:!0,children:"SITE WORKSPACES"}),_.length>J&&!D&&!N?o(d,{color:"gray",children:"/ filter"}):null]}),D?m(p,{children:[o(d,{color:"cyan",children:"/ "}),o(Re,{value:k,onChange:P,onSubmit:()=>L(!1),placeholder:"type to filter\u2026"})]}):N?o(d,{color:"gray",children:`/ ${k} \xB7 ${R.length} match${R.length===1?"":"es"}`}):null,o(p,{flexDirection:"column",marginTop:1,minHeight:gt?J+3:void 0,children:R.length===0?o(d,{color:"gray",children:N?`no matches for \u201C${k}\u201D`:"no sites \u2014 press n to create one"}):m(ot,{children:[gt?o(d,{color:"gray",children:Z>0?` \u2191 ${Z} more`:" "}):null,Ye.map((c,f)=>{let E=Z+f,Vt=c.entry?vt(c.entry.status):{dot:"\xB7",color:"gray",label:"idle"},Qt=xt(c.entry?.progress),nn=c.entry?c.entry.status==="syncing"&&Qt!=null?`${Qt}%`:c.entry.status==="paused"?"paused":"--":"",ht=E===H,rn=E===Yt&&Yt>0;return m(Kn,{children:[rn?o(d,{color:"gray",children:"\u2500".repeat(34)}):null,m(p,{justifyContent:"space-between",children:[m(p,{children:[o(d,{color:ht?"green":"gray",children:ht?"\u276F ":" "}),m(d,{color:c.entry?.status==="watching"||c.entry?.status==="syncing"?"green":Vt.color,children:[c.entry?.status==="syncing"?Oe(q):c.entry?.status==="watching"?"\u25CF":Vt.dot," "]}),o(d,{color:ht?"white":c.entry?void 0:"gray",bold:ht,children:c.site.name})]}),o(d,{color:c.entry?.status==="syncing"?"green":"gray",children:nn})]})]},c.site.id)}),gt?o(d,{color:"gray",children:Bt<Q?` \u2193 ${Q-Bt} more`:" "}):null]})}),o(p,{marginTop:1,children:o(d,{color:"gray",children:"+ new site \xB7 n"})})]}),m(p,{flexDirection:"column",flexGrow:1,marginLeft:1,children:[o(p,{flexDirection:"column",borderStyle:"round",borderColor:"gray",paddingX:1,children:$?m(ot,{children:[m(d,{children:[$.org.name||$.org.subdomain?m(d,{color:"gray",children:[$.org.name||$.org.subdomain," / "]}):null,o(d,{color:"cyan",bold:!0,children:$.site.name})]}),m(p,{marginTop:1,children:[o(d,{color:"gray",children:"status "}),T?o(d,{color:vt(T.status).color,children:`: ${vt(T.status).label}`}):o(d,{color:"gray",children:": idle \u2014 not syncing (press s to start)"})]}),m(p,{children:[o(d,{color:"gray",children:"path "}),o(d,{wrap:"truncate-end",children:T?.viewsDir?Nt(T.viewsDir):"\u2014"})]}),m(p,{children:[o(d,{color:"gray",children:"synced "}),o(d,{children:Wt(T?.lastSyncedAt)}),o(d,{color:"gray",children:" progress "}),o(d,{color:T?.status==="syncing"?"green":"gray",children:T?.status==="syncing"?`${xt(T.progress)??0}%`:"\u2014"})]}),o(p,{marginTop:1,children:o(er,{percent:T?T.status==="syncing"?xt(T.progress)??0:T.status==="watching"?100:0:0})}),T?m(ot,{children:[m(p,{marginTop:1,children:[o(d,{color:"gray",children:"build "}),o(d,{color:Et.color,children:`${Et.dot} ${Et.label}`}),o(d,{color:"gray",children:" last "}),o(d,{children:$t?"in progress":Wt(A?.at)})]}),m(p,{children:[o(d,{color:"gray",children:"url "}),A?.url?o(d,{color:"cyan",wrap:"truncate-end",children:A.url}):o(d,{color:"gray",children:"\u2014"})]}),A?.status==="error"&&A.errors?.length?m(p,{children:[o(d,{color:"red",children:"\u258C "}),o(d,{color:"red",wrap:"truncate-end",children:`\u2717 ${A.errors[0]}`})]}):null,$t?m(p,{children:[o(nr,{frame:q}),o(d,{color:"gray",children:` ${A?.stage??"building"}`})]}):null]}):null]}):o(d,{color:"gray",children:"No site selected."})}),h==="dashboard"?m(p,{flexDirection:"column",borderStyle:"round",borderColor:at==="logs"?"green":"gray",paddingX:1,marginTop:1,height:lt+4,flexShrink:0,overflowY:"hidden",children:[m(p,{justifyContent:"space-between",children:[m(d,{children:[o(d,{color:"cyan",bold:!0,children:"LOGS \xB7 "}),o(d,{color:"white",children:$?.site.name??"\u2014"})]}),T&&W.length>0?at==="logs"?o(d,{color:"green",children:`\u2191\u2193 scroll \xB7 esc back${kt>0?` \xB7 ${ut}/${W.length}`:""}`}):W.length>lt?o(d,{color:"gray",children:"tab to scroll"}):null:null]}),o(p,{flexDirection:"column",marginTop:1,children:!T||W.length===0?o(d,{color:"gray",children:"no activity yet"}):Kt.map((c,f)=>m(d,{wrap:"truncate-end",children:[m(d,{color:"gray",children:[c.time," "]}),c.text]},ut-Kt.length+f))})]}):m(p,{flexDirection:"column",borderStyle:"round",borderColor:"blue",paddingX:1,marginTop:1,flexGrow:1,children:[h==="newOrg"&&m(ot,{children:[o(d,{color:"cyan",bold:!0,children:"CREATE SITE \u2014 PICK ORG"}),o(d,{color:"gray",children:"\u2191/\u2193 select \xB7 \u23CE next \xB7 esc cancel"}),o(p,{flexDirection:"column",marginTop:1,children:t.catalog.map((c,f)=>m(d,{color:f===U(l,t.catalog.length)?"blue":void 0,bold:f===U(l,t.catalog.length),children:[f===U(l,t.catalog.length)?"\u276F ":" ",c.org.name||c.org.subdomain]},c.org.id))})]}),h==="newName"&&m(ot,{children:[o(d,{color:"cyan",bold:!0,children:"CREATE SITE"}),m(p,{marginTop:1,children:[o(d,{children:"New site name: "}),o(Re,{value:S,onChange:g,onSubmit:en})]}),o(d,{color:"gray",children:"\u23CE create \xB7 empty + \u23CE to cancel"})]})]})]})]}),t.message?o(p,{paddingX:1,children:m(d,{color:t.messageIsError?"red":"gray",wrap:"truncate-end",children:[t.messageIsError?"\u2717 ":"",t.message]})}):null,m(p,{flexDirection:"column",marginTop:1,children:[m(p,{paddingX:1,justifyContent:"space-between",children:[m(p,{flexWrap:"wrap",children:[o(I,{k:"\u2191\u2193",label:"select"}),o(I,{k:"/",label:"search"}),o(I,{k:"s",label:"start sync"}),o(I,{k:"d",label:"disconnect"}),_.length===0?o(I,{k:"n",label:"new site"}):null,o(I,{k:"ctrl+c",label:"quit",width:14})]}),o(I,{k:"?",label:"all keys",width:12,bg:De})]}),!Ge&&n.length>0?o(p,{paddingX:1,marginTop:1,flexWrap:"wrap",children:n.map((c,f)=>o(I,{k:String(f+1),label:c.label,bg:Le},c.key))}):null]})]})}import{jsx as Tt}from"react/jsx-runtime";function _e(t){let e=t;return e?.body||e?.message||String(t)}function Ie({controller:t,env:e,devSecret:n,savedToken:r,savedEmail:s}){let[i,a]=Ae(r?"loading":"auth"),[u,h]=Ae();or((l,b)=>{b.ctrl&&l==="c"&&t.exit()}),rr(()=>{r&&t.init(e,r,s).then(()=>a("manage")).catch(l=>{l instanceof F&&l.status===401?h("Saved session expired \u2014 please sign in again."):h(_e(l)),a("auth")})},[]);let w=async(l,b)=>{h(void 0),await ye({env:e,userToken:l,devSecret:n,email:b}),a("loading");try{await t.init(e,l,b),a("manage")}catch(S){h(_e(S)),a("auth")}};return i==="auth"?Tt(Pe,{env:e,initialError:u,onAuthed:w}):i==="loading"?Tt(sr,{children:Tt(ir,{children:"Loading your sites\u2026 "})}):Tt(Fe,{controller:t})}import{jsx as hr}from"react/jsx-runtime";var ur=cr(import.meta.url),Gt=re();if(Gt.version){let t=ur("../package.json");console.log(t.version),process.exit(0)}var We=Ut.dirname(ar(import.meta.url)),dr=Ne(Ut.join(We,"AGENT.md"),"utf-8"),gr=Ne(Ut.join(We,"build.mjs"),"utf-8");async function mr(){let t=new bt(dr,gr),e=await pe(),n=(Gt.env??e?.env??"production").toLowerCase(),r=Gt.dev??process.env.SLEEKCMS_DEV_SECRET??e?.devSecret;fe(r),process.stdout.isTTY&&process.stdout.write("\x1B[2J\x1B[3J\x1B[H");let s=lr(hr(Ie,{controller:t,env:n,devSecret:r,savedToken:e?.userToken,savedEmail:e?.email}),{exitOnCtrlC:!1}),i=a=>{s.unmount(),console.log(a),process.exit(0)};t.once("exit",()=>i("\u2713 Stopped. Local site files were cleaned up.")),t.once("logout",()=>i("\u{1F44B} Logged out. Run `sleekcms` to sign in again.")),process.on("SIGINT",()=>{t.exit()}),process.on("SIGTERM",()=>{t.exit()}),await s.waitUntilExit()}mr().catch(t=>{let e=t;console.error("\u274C",e.body||e.message),process.exit(1)});
|