@sleekcms/sync 2.15.0 → 3.0.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
@@ -12,7 +12,7 @@ Every save auto-syncs. Every change goes live. No build steps. No Git hooks. No
12
12
 
13
13
  - **AI generates your entire site from a description** — models, templates, layouts, styles, and content, all wired up and ready to go.
14
14
  - **AI-generated sites are plain files** — EJS templates, JSON content, CSS, JS. You own every line. Open in VS Code, Cursor, or any editor.
15
- - **Any AI can maintain it** — the CLI injects `AGENT.md`, `CLAUDE.md`, and `.vscode/copilot-instructions.md` into your workspace. GitHub Copilot, Claude, Cursor — they all read the same site reference and understand your structure from day one.
15
+ - **Any AI can maintain it** — the CLI writes `AGENTS.md` into your workspace, the instructions file Claude Code, Codex, Cursor, GitHub Copilot and other agents all read. They get the same site reference and understand your structure from day one.
16
16
  - **Live sync, always** — save a file, it syncs. Open the SleekCMS dashboard and your preview updates in real time.
17
17
  - **One command to start** — no installs required.
18
18
 
@@ -21,10 +21,10 @@ Every save auto-syncs. Every change goes live. No build steps. No Git hooks. No
21
21
  ## Quickstart
22
22
 
23
23
  ```bash
24
- npx @sleekcms/sync --token <YOUR_AUTH_TOKEN>
24
+ npx @sleekcms/sync
25
25
  ```
26
26
 
27
- That's it. The CLI fetches your site, opens an editor prompt, and starts watching for changes. Grab a token from your [SleekCMS > Builder](https://app.sleekcms.com).
27
+ That's it. Your browser opens on SleekCMS: sign in, pick a site (or create a blank one), check the code on the page matches the one in your terminal, and approve. The CLI pulls the site and starts watching for changes. Press `a` to add more sites the same way.
28
28
 
29
29
  ---
30
30
 
@@ -33,14 +33,14 @@ That's it. The CLI fetches your site, opens an editor prompt, and starts watchin
33
33
  ### Run without installing (recommended)
34
34
 
35
35
  ```bash
36
- npx @sleekcms/sync --token <YOUR_AUTH_TOKEN>
36
+ npx @sleekcms/sync
37
37
  ```
38
38
 
39
39
  ### Install globally
40
40
 
41
41
  ```bash
42
42
  npm install -g @sleekcms/sync
43
- sleekcms --token <YOUR_AUTH_TOKEN>
43
+ sleekcms
44
44
  ```
45
45
 
46
46
  ---
@@ -48,15 +48,16 @@ sleekcms --token <YOUR_AUTH_TOKEN>
48
48
  ## Usage
49
49
 
50
50
  ```bash
51
- # Basic — auto-sync on every save
52
- npx @sleekcms/sync --token abc123-xxxx
53
-
54
- # Manual sync — push changes only when you press [s]
55
- npx @sleekcms/sync --token abc123-xxxx --manual
51
+ # Add sites in your browser, then pick which to sync
52
+ npx @sleekcms/sync
56
53
 
54
+ # Keep the workspaces somewhere other than ~/SleekCMS
55
+ npx @sleekcms/sync --path ~/sites
57
56
  ```
58
57
 
59
- Once running, press `r` to re-fetch all files or `x` / `Ctrl+C` to exit. In `--manual` mode, press `s` to push your changes on demand.
58
+ Each site is added on its own (`a`) and approved in the browser; no browser on this machine (over SSH, say)? Open the address the CLI prints on any device. The CLI keeps one token per site in `~/SleekCMS/auth.json` and acts with your role on that site: editors sync content, images and videos, while models, views, setup, building and publishing need an admin.
59
+
60
+ Once running, `s` starts syncing the selected site, `d` disconnects it, `x` forgets a disconnected site, `?` lists every key, and `Ctrl+C` exits.
60
61
 
61
62
  ---
62
63
 
@@ -66,15 +67,9 @@ This is where SleekCMS CLI becomes something different. Instead of hand-crafting
66
67
 
67
68
  ### How it works
68
69
 
69
- When the CLI starts for the first time, it writes three files into your local workspace:
70
-
71
- | File | Picked up by |
72
- |---|---|
73
- | `AGENT.md` | Copilot (agent mode), any generic agent |
74
- | `CLAUDE.md` | Claude / Claude Code |
75
- | `.vscode/copilot-instructions.md` | GitHub Copilot in VS Code |
70
+ When the CLI pulls a site, it writes `AGENTS.md` into the workspace — the shared instructions file AI coding agents read on their own. (Older versions also wrote `AGENT.md`, `CLAUDE.md` and `.vscode/copilot-instructions.md`; those are removed, since a leftover `CLAUDE.md` would stop Claude Code from reading `AGENTS.md`.)
76
71
 
77
- These files contain the complete SleekCMS site-building reference — file naming conventions, model syntax, template helpers, content format, and all the rules an AI needs to produce valid, working code.
72
+ It contains the complete SleekCMS site-building reference — file naming conventions, model syntax, template helpers, content format, and all the rules an AI needs to produce valid, working code.
78
73
 
79
74
  ### Generate a complete site
80
75
 
@@ -120,7 +115,7 @@ There's no lock-in, no proprietary format, no "AI black box". Every file the AI
120
115
  - **Models** are plain JSON-like schema files — add a field, save, done.
121
116
  - **Content** is readable JSON — edit values directly without touching the CMS dashboard.
122
117
 
123
- And because `AGENT.md` / `CLAUDE.md` live in the workspace, any AI session — today or months from now — picks up the same full site context. Ask it to add a dark mode toggle, refactor the blog layout, or generate ten more blog posts. It already knows your site.
118
+ And because `AGENTS.md` lives in the workspace, any AI session — today or months from now — picks up the same full site context. Ask it to add a dark mode toggle, refactor the blog layout, or generate ten more blog posts. It already knows your site.
124
119
 
125
120
  ---
126
121
 
@@ -178,11 +173,10 @@ No manual deploy. No waiting. SleekCMS rebuilds on every sync and the preview re
178
173
 
179
174
  ```
180
175
  <site-name>/
181
- ├── AGENT.md # AI agent reference (auto-generated)
182
- ├── CLAUDE.md # Claude reference (auto-generated)
176
+ ├── AGENTS.md # AI agent reference (auto-generated)
183
177
  ├── .vscode/
184
- │ ├── copilot-instructions.md # GitHub Copilot context (auto-generated)
185
- │ └── settings.json # Editor settings
178
+ │ ├── settings.json # Editor settings (site .json files open as JSON5)
179
+ │ └── extensions.json # Recommends the JSON5 extension
186
180
  │
187
181
  └── src/
188
182
  ├── models/
@@ -447,7 +441,7 @@ Set `cookie_consent: true` in `src/setup.json` and tag any script or iframe that
447
441
  <a data-sleekcms-consent href="#">Cookie settings</a> <!-- reopens the preferences panel; put one in the footer -->
448
442
  ```
449
443
 
450
- Category names are yours (`analytics`, `marketing`, …); the banner offers exactly the ones found on the site. An optional empty `<div data-sleekcms-consent data-position="bottom" data-theme="auto" data-policy-url="/privacy"></div>` in the layout customizes position (`bottom`, `bottom-left`, `bottom-right`, `modal`), theme, policy link, locale and button labels, and `--sleek-consent-bg/text/accent/on-accent/radius` CSS variables restyle it. The full reference is in the `AGENT.md` that `sleekcms sync` writes into your workspace.
444
+ Category names are yours (`analytics`, `marketing`, …); the banner offers exactly the ones found on the site. An optional empty `<div data-sleekcms-consent data-position="bottom" data-theme="auto" data-policy-url="/privacy"></div>` in the layout customizes position (`bottom`, `bottom-left`, `bottom-right`, `modal`), theme, policy link, locale and button labels, and `--sleek-consent-bg/text/accent/on-accent/radius` CSS variables restyle it. The full reference is in the `AGENTS.md` that `sleekcms sync` writes into your workspace.
451
445
 
452
446
  ## Images
453
447
 
@@ -493,7 +487,7 @@ Inside `markdown` fields, the same shortcuts work in standard image syntax. Appe
493
487
  ## Prerequisites
494
488
 
495
489
  - **Node.js** v16 or later
496
- - A **SleekCMS account** with a CLI auth token ([get one here](https://app.sleekcms.com))
490
+ - A **SleekCMS account** ([sign up](https://app.sleekcms.com)) with access to at least one site, or an organization to create one in
497
491
 
498
492
  ---
499
493
 
@@ -501,11 +495,12 @@ Inside `markdown` fields, the same shortcuts work in standard image syntax. Appe
501
495
 
502
496
  | Problem | Fix |
503
497
  |---|---|
504
- | Authentication error | Verify your token in the SleekCMS dashboard |
505
- | No files downloaded | Check your token and `--env` setting |
498
+ | "Access was revoked" | You logged out of all sessions or lost access to the site. Press `a` and add it again; local files are kept and your edits are pushed |
499
+ | Browser didn't open | Open the address printed under the dashboard, on this or any other device |
500
+ | "admin only" on build or publish | Building and publishing need an admin on the site; ask one to build, or to make you an admin |
501
+ | No files downloaded | Check the `--env` setting |
506
502
  | Changes not syncing | Check terminal output; changes are debounced by 5 seconds |
507
- | Wrong workspace opened | Each token maps to a specific workspace folder |
508
- | Workspace disappeared | Expected — the local workspace is wiped on `Ctrl+C` / `x`. Your edits are already on the server; re-run the CLI to pull a fresh copy. |
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. |
509
504
 
510
505
  ---
511
506
 
package/dist/build.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // build.mjs v3 — SleekCMS workspace build/publish script.
2
+ // build.mjs v4 — SleekCMS workspace build/publish script.
3
3
  /**
4
4
  * Self-contained (no `npm install` needed). Run from anywhere inside a synced
5
5
  * SleekCMS workspace — the one `@sleekcms/sync` created, containing `.cache/token`.
@@ -26,8 +26,8 @@ import fs from "node:fs";
26
26
  import path from "node:path";
27
27
  import { pathToFileURL } from "node:url";
28
28
 
29
- // Same env → base-URL table the sync engine uses; env is the 3rd `-`-segment of
30
- // the per-site token (see sync-engine.ts), defaulting to production.
29
+ // Same env → base-URL table the sync engine uses. The workspace records its env in
30
+ // .cache/site.json (see sync-engine.ts); --env / SLEEKCMS_ENV override it.
31
31
  const MCP_BASE_URLS = {
32
32
  localhost: "http://app.sleekcms.test/api/mcp",
33
33
  development: "https://app.sleekcms.dev/api/mcp",
@@ -37,9 +37,15 @@ const MCP_BASE_URLS = {
37
37
  const TERMINAL = new Set(["COMPLETED", "FAILED", "EXPIRED"]);
38
38
  const DEFAULT_MAX_POLLS = 6;
39
39
 
40
- /** Env baked into the per-site token (`<a>-<b>-<env>-…`); falls back to production. */
41
- export function deriveEnv(token, override) {
42
- const env = (override || (token || "").split("-")[2] || "production").toLowerCase();
40
+ /** The workspace's env: the override, else what .cache/site.json records, else production. */
41
+ export function workspaceEnv(ws, override) {
42
+ let recorded;
43
+ try {
44
+ recorded = JSON.parse(fs.readFileSync(path.join(ws, ".cache", "site.json"), "utf-8")).env;
45
+ } catch {
46
+ recorded = undefined;
47
+ }
48
+ const env = String(override || recorded || "production").toLowerCase();
43
49
  return MCP_BASE_URLS[env] ? env : "production";
44
50
  }
45
51
 
@@ -182,7 +188,7 @@ async function main() {
182
188
  process.exit(2);
183
189
  }
184
190
 
185
- const env = deriveEnv(token, flags.env || process.env.SLEEKCMS_ENV);
191
+ const env = workspaceEnv(ws, flags.env || process.env.SLEEKCMS_ENV);
186
192
  const base = mcpBase(env);
187
193
  const maxPolls = Number(process.env.SLEEKCMS_MAX_BUILD_POLLS) || DEFAULT_MAX_POLLS;
188
194
 
@@ -205,7 +211,12 @@ async function main() {
205
211
  });
206
212
  } catch (err) {
207
213
  const msg = err && err.message ? err.message : String(err);
208
- const hint = err && err.httpStatus === 403 ? " (the workspace token must belong to a site ADMIN)" : "";
214
+ const hint =
215
+ err && err.httpStatus === 403
216
+ ? " (building and publishing are for site ADMINs)"
217
+ : err && err.httpStatus === 401
218
+ ? " (access to this site was revoked — add it again in sleekcms)"
219
+ : "";
209
220
  console.error(`❌ Build request failed${hint}: ${msg}`);
210
221
  writeStatus(ws, { status: "error", mode, errors: [msg], at: Date.now() });
211
222
  appendLog(ws, `${new Date().toISOString()}\t${mode}\tERROR\t${msg}`);
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
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",`
2
+ import{readFileSync as kt}from"node:fs";import{fileURLToPath as Vn}from"node:url";import{createRequire as Qn}from"node:module";import Ae from"node:path";import{render as Zn}from"ink";import{execSync as Ye,spawn as B}from"child_process";import{program as Je,Option as He}from"commander";function Xe(){return Je.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 He("-v, --version","output the version number").hideHelp()).addOption(new He("-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
- $ sleekcms # log in and pick sites to sync
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),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(`
6
+ `).parse(process.argv),Je.opts()}function R(e){let t=process.platform==="win32"?`where ${e}`:`which ${e}`;try{return Ye(t,{stdio:"ignore"}),!0}catch{return!1}}function Ke(e){if(process.platform!=="darwin")return!1;try{return Ye(`osascript -e 'id of application "${e}"'`,{stdio:"ignore"}),!0}catch{return!1}}function Ot(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 ze=[{name:"VS Code",cmd:"code",args:e=>["-n",e]},{name:"Cursor",cmd:"cursor",args:e=>["-n",e]}];function qe(e,t){let r=(t?ze.filter(s=>s.cmd===t):ze).find(s=>R(s.cmd));return r?(Ot(r.cmd,r.args(e)),r.name):null}function It(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 Nt(e){return Ke("iTerm")?(B("open",["-a","iTerm",e],{detached:!0,stdio:"ignore"}).unref(),!0):!1}function Wt(e){return`'${e.replace(/'/g,"'\\''")}'`}var Gt=[{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 Ve(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 ${Wt(e)} && ${t}; exec bash`;for(let r of Gt)if(R(r.cmd))try{return B(r.cmd,r.args(n),{cwd:e,detached:!0,stdio:"ignore"}).unref(),!0}catch{}return!1}function Ut(e){return Ve(e,"claude")}function Jt(e){return Ve(e,"pi")}function ae(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 Qe(){let e=process.platform==="darwin",t=process.platform==="win32",n=[];return(e||t)&&n.push({key:"terminal",label:"terminal",run:It}),Ke("iTerm")&&n.push({key:"iterm",label:"iterm",run:Nt}),R("code")&&n.push({key:"code",label:"vscode",run:r=>qe(r,"code")!==null}),R("cursor")&&n.push({key:"cursor",label:"cursor",run:r=>qe(r,"cursor")!==null}),R("claude")&&n.push({key:"claude",label:"claude",run:Ut}),R("pi")&&n.push({key:"pi",label:"pi",run:Jt}),n}import{EventEmitter as Cn}from"node:events";import{spawn as Mn}from"node:child_process";import Z from"fs-extra";import Ce from"path";import Rn from"chokidar";import{createHash as zt}from"crypto";import f from"fs-extra";import qt from"os";import b from"path";var Ze={localhost:"http://app.sleekcms.test/api/mcp",development:"https://app.sleekcms.dev/api/mcp",production:"https://app.sleekcms.com/api/mcp"},Yt=["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"],X=class extends Error{status;body;constructor(t,n,r){super(t),this.status=n,this.body=r}};async function ce(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 X(`${n} ${r} \u2192 ${i.status}: ${d}`,i.status,d)}return i.json()}var Xt=["AGENT.md","CLAUDE.md",b.join(".vscode","copilot-instructions.md")],Kt="mrmlnc.vscode-json5";async function Vt(e,t){t&&(await f.outputFile(b.join(e,"AGENTS.md"),t),await Promise.all(Xt.map(n=>f.remove(b.join(e,n))))),await f.outputFile(b.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 f.outputFile(b.join(e,".vscode","extensions.json"),JSON.stringify({recommendations:[Kt]},null,2))}async function Qt(e,t){if(!t)return;let n=b.join(e,"build.mjs"),r=t.match(/build\.mjs v\d+/)?.[0];if(await f.pathExists(n)){let s=(await f.readFile(n,"utf-8")).match(/build\.mjs v\d+/)?.[0];if(s&&s===r)return}await f.outputFile(n,t)}async function Zt(e,t,n,r){let s=b.join(e,".cache","site.json"),i=await f.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 f.emptyDir(e)),await f.outputJson(s,n),await f.outputFile(b.join(e,".cache","token"),t)}function en(e,t){let n=`${t.name.substr(0,20)} ${t.id}`.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,""),r=e||b.join(qt.homedir(),"SleekCMS");return b.resolve(r,n)}async function ve(e){let t=(e.token||"").trim();if(!t)throw new Error("syncSite: token is required");let n=e.log??(S=>console.log(S)),r=e.onProgress,s=(e.env||"production").toLowerCase(),i=Ze[s]||Ze.production,d=await ce(i,t,"GET","/get_site"),l=e.viewsDir?b.resolve(e.viewsDir):en(e.path,d);await f.ensureDir(l),await Zt(l,t,{env:s,slug:String(d.id)},n),await Promise.all(Yt.map(S=>f.ensureDir(b.join(l,S)))),await Qt(l,e.buildScript);let y=b.join(l,".cache","state.json");e.flush&&await f.remove(y);let v=!await f.pathExists(y),u=v?{}:(await f.readJson(y)).fileMap||{},m=0,w=0,h=0;if(v)({fileMap:u,pulled:w}=await fn(l,i,t,n,r)),await Vt(l,e.agentMd);else{let S=await hn(l,u,i,t,n);h=S.renamed,m=await nn(l,u,i,t,n,r,S.held)}return await f.outputJson(y,{fileMap:u},{spaces:2}),{viewsDir:l,site:d,isFirstRun:v,pushed:m,pulled:w,renamed:h}}var K=e=>zt("sha1").update(e).digest("hex"),tn=new Set([".DS_Store","Thumbs.db","desktop.ini"]);async function Se(e){let t=b.join(e,"src");if(!await f.pathExists(t))return[];let n=[];async function r(s){let i=await f.readdir(s,{withFileTypes:!0});for(let d of i){if(tn.has(d.name))continue;let l=b.join(s,d.name);d.isDirectory()?await r(l):d.isFile()&&n.push(b.relative(e,l).replace(/\\/g,"/"))}}return await r(t),n}async function nn(e,t,n,r,s,i,d=new Set){let l=[];for(let w of await Se(e)){if(d.has(w))continue;let h=b.join(e,w),S=t[w],E=await f.stat(h);if(S&&S.mtimeMs===E.mtimeMs){S.hash||(S.hash=K(await f.readFile(h,"utf-8")));continue}let I=await f.readFile(h,"utf-8");I.trim()&&l.push({rel:w,full:h,stat:E,content:I,prior:S})}if(l.length===0)return 0;let y;try{y=await ce(n,r,"POST","/save_files",l.map(w=>({path:w.rel,content:w.content})))}catch(w){let h=w;return s(`\u274C Error saving files: ${h.body||h.message}`),0}let v=await nt(e),u=0,m=rn(y);for(let w=0;w<l.length;w++){let h=l[w],S=sn(h,w,y,m);if(i?.(w+1,l.length),!S){let E="No save response returned";v[h.rel]=E,s(`\u274C Error saving ${h.rel}: ${E}`);continue}if(S.error){v[h.rel]=S.error,s(`\u274C Error saving ${h.rel}: ${S.error}`);continue}delete v[h.rel],t[h.rel]={mtimeMs:h.stat.mtimeMs,hash:K(h.content)},s(`\u2705 ${h.prior?"Updated":"Created"} ${h.rel}`),u++}return await rt(e,v),u}function rn(e){let t=new Map;for(let n of e)n.path&&t.set(n.path,n);return t}function sn(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 et=/^src\/(?:(?:models|views)\/(?:pages|entries|blocks)|content\/(?:pages|entries))\//,on={PAGE:"pages",ENTRY:"entries",BLOCK:"blocks"},an=e=>e.split("/").slice(0,3).join("/");function cn(e,t,n){let r=new Map,s=(i,d)=>{let l=`${an(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,y)=>{let v=n.get(s);v?(v.pairs.push(y),i<v.rank&&Object.assign(v,{from:d,to:l,rank:i})):n.set(s,{from:d,to:l,rank:i,pairs:[y]})};for(let s of e){let[,i,d,...l]=s.from.split("/"),y=s.to.split("/").slice(3);if(i==="content"&&d==="pages"&&l.length>1){let[u,...m]=l,[w,...h]=y;if(h.length===0)continue;w===u?r(`record:${s.from}`,0,s.from,s.to,s):m.join("/")===h.join("/")&&![...t].some(S=>S.startsWith(`src/content/pages/${u}/`))&&r(`pages:${u}`,3,`src/content/pages/${u}`,`src/content/pages/${w}`,s);continue}let v=l.join("/").replace(/\.[^./]+$/,"");r(`${d}:${v}`,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 tt(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=on[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}${b.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 un(e,t){let n=e;for(;n.startsWith(t+b.sep);){let r=await f.readdir(n).catch(()=>null);if(!r||r.length>0)return;await f.remove(n),n=b.dirname(n)}}async function dn(e,t,n,r){let s=u=>b.join(e,u),i=b.join(e,"src"),d=u=>b.resolve(e,u).startsWith(i+b.sep),l=async(u,m)=>{t[u]={mtimeMs:(await f.stat(s(u))).mtimeMs,hash:m}},y=u=>un(b.dirname(s(u)),b.join(e,...u.split("/").slice(0,3))),v=[];for(let u of r){let m=tt(n.rename,u.from)??u.to;d(m)&&(m!==u.to&&(await f.move(s(u.to),s(m),{overwrite:!0}),await y(u.to),v.push(u.to)),delete t[u.from],v.push(u.from),await l(m,u.hash))}for(let u of new Set([...await Se(e),...Object.keys(t)])){let m=tt(n.rename,u);if(!m||!d(m))continue;let w=await f.pathExists(s(u));if(w&&await f.pathExists(s(m)))continue;let h=t[u];if(delete t[u],v.push(u),!w)continue;let S=!!h?.hash&&h.mtimeMs===(await f.stat(s(u))).mtimeMs;await f.move(s(u),s(m)),S&&await l(m,h.hash),await y(u)}for(let u of n.files||[]){let m=t[u.path];if(!m||!d(u.path))continue;let w=await f.stat(s(u.path)).catch(()=>null);!w||w.mtimeMs!==m.mtimeMs||await f.readFile(s(u.path),"utf-8")!==u.content&&(await f.outputFile(s(u.path),u.content),await l(u.path,K(u.content)))}return v}function gn(e){try{let t=JSON.parse(e.body).message;if(t)return t}catch{}return(e.body||e.message||String(e)).slice(0,300)}async function hn(e,t,n,r,s){let i=new Set,d=await Se(e),l=new Set(d),y=Object.keys(t).filter(h=>!l.has(h)&&t[h].hash&&et.test(h));if(y.length===0)return{renamed:0,held:i};let v=new Map;for(let h of d){if(t[h]||!et.test(h))continue;let S=await f.readFile(b.join(e,h),"utf-8");S.trim()&&v.set(h,K(S))}let u=ln(cn(y,v,t),l);if(u.length===0)return{renamed:0,held:i};let m=await nt(e),w=0;for(let h of u){let S;try{S=await ce(n,r,"POST","/rename",{from:h.from,to:h.to})}catch(C){let M=C,H=gn(M);if(M.status>=400&&M.status<500)s(`\u26A0\uFE0F Couldn't rename ${h.from} \u2192 ${h.to}: ${H} Saving it as a new file.`);else{for(let D of h.pairs)i.add(D.to),m[D.to]=`rename from ${D.from} failed: ${H}`;s(`\u274C Error renaming ${h.from} \u2192 ${h.to}: ${H}`)}continue}let E=await dn(e,t,S,h.pairs);for(let C of[...E,...h.pairs.map(M=>M.to)])delete m[C];let[I,...pe]=S.message.split(`
7
+ `);s(`\u{1F500} ${I}`);for(let C of pe)s(` ${C}`);w++}return await rt(e,m),{renamed:w,held:i}}var xe="sync-errors.log",be=new Map;async function nt(e){let t=b.resolve(e),n=be.get(t);if(n)return n;let r=await pn(t);return be.set(t,r),r}async function pn(e){let t=b.join(e,xe);if(!await f.pathExists(t))return{};let n=await f.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 rt(e,t){let n=b.join(e,xe),r=Object.entries(t);if(r.length===0){await f.remove(n);return}await f.outputFile(n,r.map(([s,i])=>`${s}: ${i}`).join(`
9
9
  `)+`
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)});
10
+ `)}async function fn(e,t,n,r,s){be.delete(b.resolve(e)),await f.remove(b.join(e,xe)),r("\u{1F4E5} Fetching files...");let i=await ce(t,n,"GET","/get_files"),d={},l=0;for(let y of i){let v=b.join(e,y.path);await f.outputFile(v,y.content);let u=(await f.stat(v)).mtimeMs;l++,s?.(l,i.length),d[y.path]={mtimeMs:u,hash:K(y.content)}}return r(`\u2714\uFE0F Synced ${i.length} file(s).`),{fileMap:d,pulled:l}}import mn from"path";import wn from"chokidar";var yn=5e3,bn=/(^|[/\\])(\.DS_Store|Thumbs\.db|desktop\.ini)$/,V=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??yn}start(){if(this.watcher)return;let t=mn.join(this.viewsDir,"src");this.watcher=wn.watch([t],{persistent:!0,ignoreInitial:!0,ignored:bn}).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 Te from"fs-extra";import vn from"os";import it from"path";var st={localhost:"http://app.sleekcms.test/api",development:"https://app.sleekcms.dev/api",production:"https://app.sleekcms.com/api"};function Q(e){return st[e]||st.production}function $e(e){return Q(e).replace(/\/api$/,"")}var Sn=it.join(vn.homedir(),"SleekCMS"),U=it.join(Sn,"auth.json"),ke=class extends Error{status;body;constructor(t,n,r=""){super(t),this.name="ApiError",this.status=n,this.body=r}};async function le(e=U){let t=null;try{t=await Te.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 ot(e,t=U){await Te.outputJson(t,{sites:e},{spaces:2});try{await Te.chmod(t,384)}catch{}}async function Be(e,t=U){let{sites:n}=await le(t),r=[...n.filter(s=>!(s.env===e.env&&s.siteId===e.siteId)),e];return await ot(r,t),r}async function Pe(e,t,n=U){let{sites:r}=await le(n),s=r.filter(i=>!(i.env===e&&i.siteId===t));return await ot(s,n),s}async function Ee(e,t,n,r){let s=await fetch(Q(e)+r,{method:n,headers:{Authorization:`Bearer ${t}`}});if(!s.ok){let i=await s.text().catch(()=>"");throw new ke(`${n} ${r} \u2192 ${s.status}: ${i}`,s.status,i)}return s.json()}function xn(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:"",y=n&&r&&r!==n?` (on page ${r})`:"";if(s)return`${s}${i}${d}${y}: ${l||"(no message)"}`;if(l)return l;try{return JSON.stringify(e)}catch{return"(unknown error)"}}async function at(e,t){let n;try{n=await Ee(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(xn):[];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 Tn,randomBytes as kn}from"crypto";var $n="urn:sleekcms:grant-type:cli_poll",Bn="SleekCMS Sync",ct=e=>e.toString("base64").replace(/=+$/,"").replace(/\+/g,"-").replace(/\//g,"_");function lt(){let e=ct(kn(32)),t=ct(Tn("sha256").update(e).digest());return{verifier:e,challenge:t}}function ut(e){let t=e.replace(/[^A-Za-z0-9]/g,"").slice(0,8).toUpperCase();return`${t.slice(0,4)}-${t.slice(4)}`}async function dt(e,t=fetch){let n={origin:$e(e),authorizeEndpoint:`${Q(e)}/oauth/authorize`};try{let r=await t(`${$e(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 gt(e,t){let n=new URLSearchParams({response_type:"code",client_name:Bn,redirect_uri:`${e.origin}/oauth/cli-done`,code_challenge:t,code_challenge_method:"S256",resource:`${e.origin}/mcp/`});return`${e.authorizeEndpoint}?${n.toString()}`}var J=class extends Error{constructor(t){super(t),this.name="SignInError"}},Pn=e=>new Promise(t=>setTimeout(t,e));async function ht(e,t,n={}){let{timeoutMs:r=10*6e4,signal:s,fetchImpl:i=fetch,sleep:d=Pn}=n,l=n.intervalMs??2e3,y=Date.now()+r,v=`${Q(e)}/oauth/token`;for(;;){if(s?.aborted)throw new J("Cancelled.");let u=null;try{u=await i(v,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({grant_type:$n,code_verifier:t})})}catch{u=null}if(u){let m=await u.json().catch(()=>({}));if(u.ok&&m.access_token)return m.access_token;if(u.status===429)l=Math.min(l*2,3e4);else if(m.error!=="authorization_pending")throw new J(m.error==="invalid_grant"?"That approval can no longer be used. Add the site again.":`Sign-in failed: ${m.error||`HTTP ${u.status}`}`)}if(Date.now()+l>y)throw new J("Timed out waiting for approval in the browser.");await d(l)}}function En(e){let t=e.split(".")[1];if(!t)throw new J("The server returned a token this version can't read.");return JSON.parse(Buffer.from(t,"base64url").toString("utf8"))}async function pt(e,t){let n=En(t),r=await Ee(e,t,"GET","/mcp/get_site");return{env:e,siteId:n.sid,slug:n.slug,name:r.name,org:{name:r.org,subdomain:r.subdomain||n.sub},role:r.role||void 0,token:t,addedAt:Date.now()}}var ft=500;function Ln(){return new Date().toTimeString().slice(0,8)}function Dn(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 mt(e){return e instanceof X&&e.status===401}var jn=e=>({id:e.siteId,name:e.name,slug:e.slug});async function wt(e){e&&(await Z.remove(Ce.join(e,"src")),await Z.remove(Ce.join(e,".cache","state.json")))}var ue=class extends Cn{env="production";saved=[];synced=new Map;connecting=null;message;messageIsError=!1;agentMd;buildScript;configPath;isShuttingDown=!1;basePath;pollOptions={};openBrowser=ae;constructor(t,n,r=U){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:Ln(),text:n}),r.logs.length>ft&&r.logs.splice(0,r.logs.length-ft),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 le(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}=lt(),r=new AbortController;this.connecting={code:ut(n),url:"",abort:r},this.changed();let s=gt(await dt(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 ht(this.env,t,{...this.pollOptions,signal:r.signal});i=await pt(this.env,l)}catch(l){this.connecting=null,r.signal.aborted?this.changed():this.flash(Dn(l),!0);return}this.connecting=null;let d=await Be(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=jn(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 ve({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 V({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(mt(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,Be({...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 Pe(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 Pe(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 wt(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()}),Mn("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 Ce.join(t,".cache","build.json")}async readBuildFile(t){try{let n=await Z.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 Z.outputJson(s,n),await Z.rename(s,r)}catch{}}watchBuildStatus(t){let n=this.synced.get(t);!n||!n.viewsDir||(n.buildWatcher=Rn.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 at(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 ve({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(mt(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 V({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 wt(t.viewsDir)}async exit(){this.isShuttingDown||(this.isShuttingDown=!0,await this.cleanupAll(),this.emit("exit"))}};import{useInput as Xn}from"ink";import{Fragment as Fn,useEffect as ee,useMemo as _n,useRef as On,useState as L}from"react";import{Box as p,Text as c,useInput as In,useStdout as Nn}from"ink";import Wn from"ink-text-input";import An from"node:os";function Me(e){if(!e)return"\u2014";let t=An.homedir();return e.startsWith(t)?"~"+e.slice(t.length):e}function Re(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 ge(e){return!e||e.total<=0?null:Math.round(e.done/e.total*100)}var de=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"];function Le(e){return de[(e%de.length+de.length)%de.length]}function he(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 yt(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 De,jsx as o,jsxs as g}from"react/jsx-runtime";var Gn=(e,t)=>t<=0?0:Math.max(0,Math.min(e,t-1)),Un=16,Jn=5,Hn="greenBright",bt="blueBright",vt="magentaBright";function St({k:e,bg:t=Hn}){return o(c,{color:"black",backgroundColor:t,children:` ${e} `})}function F({k:e,label:t,width:n=16,bg:r}){return g(p,{width:n,children:[o(St,{k:e,bg:r}),o(c,{color:"gray",children:` ${t}`})]})}function $({k:e,label:t,bg:n}){return g(p,{marginBottom:1,children:[o(p,{width:9,children:o(St,{k:e,bg:n})}),o(c,{color:"gray",children:t})]})}function zn({launchers:e}){return g(p,{flexDirection:"column",borderStyle:"round",borderColor:"green",paddingX:2,paddingY:1,width:66,children:[g(p,{justifyContent:"space-between",children:[o(c,{color:"green",bold:!0,children:"KEYBOARD SHORTCUTS"}),o(c,{color:"gray",children:"? or esc to close"})]}),g(p,{marginTop:1,children:[g(p,{flexDirection:"column",width:34,children:[o(c,{color:"cyan",bold:!0,children:"Navigate"}),o($,{k:"\u2191\u2193",label:"move selection"}),o($,{k:"tab",label:"focus logs \xB7 \u2191\u2193 scroll"}),o($,{k:"esc",label:"clear filter / back"}),o(p,{marginTop:1,children:o(c,{color:"cyan",bold:!0,children:"Global"})}),o($,{k:"/",label:"search sites"}),o($,{k:"a",label:"add a site (browser)"}),o($,{k:"?",label:"toggle this help",bg:bt}),o($,{k:"ctrl+c",label:"quit"})]}),g(p,{flexDirection:"column",flexGrow:1,children:[o(c,{color:"cyan",bold:!0,children:"Site actions"}),o($,{k:"s",label:"start sync"}),o($,{k:"d",label:"disconnect"}),o($,{k:"x",label:"forget site"}),o($,{k:"p",label:"pause / resume sync"}),o($,{k:"b",label:"build preview"}),o($,{k:"B",label:"publish live"}),o($,{k:"o",label:"open preview url"}),e.map((t,n)=>o($,{k:String(n+1),label:`open in ${t.label}`,bg:vt},t.key))]})]})]})}function qn({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 Yn({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 xt({controller:e}){let{stdout:t}=Nn(),n=_n(()=>Qe(),[]),[,r]=L(0),[s,i]=L(()=>new Date),[d,l]=L(null),[y,v]=L(""),[u,m]=L(!1),[w,h]=L(0),[S,E]=L(!1),[I,pe]=L(!1),[C,M]=L("list"),[H,D]=L(0),Fe=()=>r(a=>a+1);ee(()=>(e.on("change",Fe),()=>{e.off("change",Fe)}),[e]),ee(()=>{let a,x=()=>{i(new Date),a=setTimeout(x,6e4-Date.now()%6e4)};return a=setTimeout(x,6e4-Date.now()%6e4),()=>clearTimeout(a)},[]);let z=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,x)=>(a.entry?0:1)-(x.entry?0:1)),N=e.connecting,_=y.trim().toLowerCase(),P=_?z.filter(a=>a.site.name.toLowerCase().includes(_)||(a.org.name||a.org.subdomain||"").toLowerCase().includes(_)):z,W=P.findIndex(a=>a.site.id===d);W<0&&(W=0);let k=P[W],T=k?.entry,te=Math.max(3,(t?.rows??30)-26),O=T?.logs??[],fe=Math.max(0,O.length-te),Bt=Math.min(H,fe),ne=O.length-Bt,_e=O.slice(Math.max(0,ne-te),ne),Pt=k?.site.id;ee(()=>D(0),[Pt]);let j=T?.lastBuild,me=j?.status==="building",we=yt(j),re=me||!!N;if(!re){for(let a of e.synced.values())if(a.status==="syncing"){re=!0;break}}ee(()=>{if(!re)return;let a=setInterval(()=>h(x=>x+1),120);return()=>clearInterval(a)},[re]);let Oe=P.findIndex(a=>!a.entry),G=Math.max(Jn,(t?.rows??30)-Un),q=P.length,se=q>G,Et=Math.floor(G/2),Y=se?Math.max(0,Math.min(W-Et,q-G)):0,ye=Math.min(q,Y+G),Ct=P.slice(Y,ye),Ie=On(!1);ee(()=>{Ie.current||(Ie.current=!0,e.saved.length===0&&!e.connecting&&e.connectSite())},[e]);let ie=a=>{if(P.length===0)return;let x=Gn(W+a,P.length);l(P[x].site.id)},Mt=()=>{if(k){if(k.entry){e.flash("Already syncing \u2014 press d to disconnect.");return}e.startSite(k.saved)}},Rt=()=>{k?.entry&&e.removeSite(k.site.id)},Lt=()=>{k&&e.forgetSite(k.site.id)},Dt=a=>{if(!T){e.flash("Press s to start syncing this site first.",!0);return}pe(!0);let x=a.run(T.viewsDir);e.flash(x?`Opened ${a.label} at ${Me(T.viewsDir)}`:`Couldn't open ${a.label}.`,!x)},jt=()=>{if(!T){e.flash("Press s to start syncing this site first.",!0);return}e.togglePause(T.site.id)},Ne=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")},At=()=>{let a=T?.lastBuild?.url;if(!a){e.flash("No build URL yet \u2014 press b to build.",!0);return}e.flash(ae(a)?`Opening ${a}`:"Couldn't open the browser.",!1)};In((a,x)=>{if(S){(a==="?"||x.escape)&&E(!1);return}if(N&&x.escape)return void e.cancelConnect();if(u){x.escape?(m(!1),v(""),l(null)):x.return?m(!1):x.upArrow?ie(-1):x.downArrow&&ie(1);return}if(x.tab){C==="logs"?M("list"):T&&M("logs"),D(0);return}if(C==="logs"){if(x.escape){M("list"),D(0);return}if(x.upArrow)return void D(A=>Math.min(A+1,fe));if(x.downArrow)return void D(A=>Math.max(0,A-1))}if(x.escape){v(""),l(null);return}if(a==="?")return void E(!0);if(a==="/")return M("list"),void m(!0);if(a==="a"||a==="n")return void e.connectSite();if(x.upArrow)return ie(-1);if(x.downArrow)return ie(1);if(a==="s")return Mt();if(a==="d")return Rt();if(a==="x")return Lt();if(a==="p")return jt();if(a==="b")return Ne(!1);if(a==="B")return Ne(!0);if(a==="o")return At();if(a>="1"&&a<="9"){let A=n[parseInt(a,10)-1];if(A)return Dt(A)}});let We=g(p,{justifyContent:"space-between",paddingX:1,children:[g(p,{children:[o(c,{color:"green",bold:!0,children:"\u25C6 SleekCMS"}),o(c,{color:"gray",children:" \u2502 "}),o(c,{children:e.env==="production"?"Sync":`Sync \xB7 ${e.env}`})]}),g(p,{children:[g(c,{color:"gray",children:[_?`${P.length}/${z.length}`:z.length," sites \xB7 "]}),g(c,{color:"green",children:[e.watchingCount()," watching"]}),o(c,{color:"gray",children:` \xB7 ${s.toTimeString().slice(0,5)}`})]})]});return S?g(p,{flexDirection:"column",children:[We,o(p,{marginTop:1,justifyContent:"center",children:o(zn,{launchers:n})})]}):g(p,{flexDirection:"column",children:[We,g(p,{children:[g(p,{flexDirection:"column",borderStyle:"round",borderColor:"green",width:38,paddingX:1,children:[g(p,{justifyContent:"space-between",children:[o(c,{color:"green",bold:!0,children:"SITE WORKSPACES"}),z.length>G&&!u&&!_?o(c,{color:"gray",children:"/ filter"}):null]}),u?g(p,{children:[o(c,{color:"cyan",children:"/ "}),o(Wn,{value:y,onChange:v,onSubmit:()=>m(!1),placeholder:"type to filter\u2026"})]}):_?o(c,{color:"gray",children:`/ ${y} \xB7 ${P.length} match${P.length===1?"":"es"}`}):null,o(p,{flexDirection:"column",marginTop:1,minHeight:se?G+3:void 0,children:P.length===0?o(c,{color:"gray",children:_?`no matches for \u201C${y}\u201D`:"no sites yet \u2014 press a to add one"}):g(De,{children:[se?o(c,{color:"gray",children:Y>0?` \u2191 ${Y} more`:" "}):null,Ct.map((a,x)=>{let A=Y+x,Ge=a.entry?he(a.entry.status):{dot:"\xB7",color:"gray",label:"idle"},Ue=ge(a.entry?.progress),Ft=a.entry?a.entry.status==="syncing"&&Ue!=null?`${Ue}%`:a.entry.status==="paused"?"paused":"--":"",oe=A===W,_t=A===Oe&&Oe>0;return g(Fn,{children:[_t?o(c,{color:"gray",children:"\u2500".repeat(34)}):null,g(p,{justifyContent:"space-between",children:[g(p,{children:[o(c,{color:oe?"green":"gray",children:oe?"\u276F ":" "}),g(c,{color:a.entry?.status==="watching"||a.entry?.status==="syncing"?"green":Ge.color,children:[a.entry?.status==="syncing"?Le(w):a.entry?.status==="watching"?"\u25CF":Ge.dot," "]}),o(c,{color:oe?"white":a.entry?void 0:"gray",bold:oe,children:a.site.name})]}),o(c,{color:a.entry?.status==="syncing"?"green":"gray",children:Ft})]})]},a.site.id)}),se?o(c,{color:"gray",children:ye<q?` \u2193 ${q-ye} more`:" "}):null]})}),o(p,{marginTop:1,children:o(c,{color:"gray",children:"+ add site \xB7 a"})})]}),g(p,{flexDirection:"column",flexGrow:1,marginLeft:1,children:[o(p,{flexDirection:"column",borderStyle:"round",borderColor:"gray",paddingX:1,children:k?g(De,{children:[g(c,{children:[k.org.name||k.org.subdomain?g(c,{color:"gray",children:[k.org.name||k.org.subdomain," / "]}):null,o(c,{color:"cyan",bold:!0,children:k.site.name})]}),g(p,{marginTop:1,children:[o(c,{color:"gray",children:"status "}),T?o(c,{color:he(T.status).color,children:`: ${he(T.status).label}`}):o(c,{color:"gray",children:": idle \u2014 not syncing (press s to start)"})]}),g(p,{children:[o(c,{color:"gray",children:"role "}),o(c,{children:T?.role??k.saved.role??"\u2014"}),(T?.role??k.saved.role??"ADMIN")!=="ADMIN"?o(c,{color:"gray",children:" \xB7 build & publish need ADMIN"}):null]}),g(p,{children:[o(c,{color:"gray",children:"path "}),o(c,{wrap:"truncate-end",children:T?.viewsDir?Me(T.viewsDir):"\u2014"})]}),g(p,{children:[o(c,{color:"gray",children:"synced "}),o(c,{children:Re(T?.lastSyncedAt)}),o(c,{color:"gray",children:" progress "}),o(c,{color:T?.status==="syncing"?"green":"gray",children:T?.status==="syncing"?`${ge(T.progress)??0}%`:"\u2014"})]}),o(p,{marginTop:1,children:o(qn,{percent:T?T.status==="syncing"?ge(T.progress)??0:T.status==="watching"?100:0:0})}),T?g(De,{children:[g(p,{marginTop:1,children:[o(c,{color:"gray",children:"build "}),o(c,{color:we.color,children:`${we.dot} ${we.label}`}),o(c,{color:"gray",children:" last "}),o(c,{children:me?"in progress":Re(j?.at)})]}),g(p,{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(p,{children:[o(c,{color:"red",children:"\u258C "}),o(c,{color:"red",wrap:"truncate-end",children:`\u2717 ${j.errors[0]}`})]}):null,me?g(p,{children:[o(Yn,{frame:w}),o(c,{color:"gray",children:` ${j?.stage??"building"}`})]}):null]}):null]}):o(c,{color:"gray",children:"No site selected."})}),N?g(p,{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(p,{marginTop:1,children:[o(c,{color:"gray",children:"Check the page shows "}),o(c,{color:"cyan",bold:!0,children:N.code})]}),o(c,{color:"gray",children:"Only approve a page that shows this code."}),g(p,{marginTop:1,children:[g(c,{color:"yellow",children:[Le(w)," "]}),o(c,{color:"gray",children:"waiting for approval \xB7 esc to cancel"})]})]}):g(p,{flexDirection:"column",borderStyle:"round",borderColor:C==="logs"?"green":"gray",paddingX:1,marginTop:1,height:te+4,flexShrink:0,overflowY:"hidden",children:[g(p,{justifyContent:"space-between",children:[g(c,{children:[o(c,{color:"cyan",bold:!0,children:"LOGS \xB7 "}),o(c,{color:"white",children:k?.site.name??"\u2014"})]}),T&&O.length>0?C==="logs"?o(c,{color:"green",children:`\u2191\u2193 scroll \xB7 esc back${fe>0?` \xB7 ${ne}/${O.length}`:""}`}):O.length>te?o(c,{color:"gray",children:"tab to scroll"}):null:null]}),o(p,{flexDirection:"column",marginTop:1,children:!T||O.length===0?o(c,{color:"gray",children:"no activity yet"}):_e.map((a,x)=>g(c,{wrap:"truncate-end",children:[g(c,{color:"gray",children:[a.time," "]}),a.text]},ne-_e.length+x))})]})]})]}),N?.url?g(p,{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:N.url})]}):null,e.message?o(p,{paddingX:1,children:g(c,{color:e.messageIsError?"red":"gray",wrap:"truncate-end",children:[e.messageIsError?"\u2717 ":"",e.message]})}):null,g(p,{flexDirection:"column",marginTop:1,children:[g(p,{paddingX:1,justifyContent:"space-between",children:[g(p,{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:bt})]}),!I&&n.length>0?o(p,{paddingX:1,marginTop:1,flexWrap:"wrap",children:n.map((a,x)=>o(F,{k:String(x+1),label:a.label,bg:vt},a.key))}):null]})]})}import{jsx as Kn}from"react/jsx-runtime";function Tt({controller:e}){return Xn((t,n)=>{n.ctrl&&t==="c"&&e.exit()}),Kn(xt,{controller:e})}import{jsx as sr}from"react/jsx-runtime";var er=Qn(import.meta.url),je=Xe();if(je.version){let e=er("../package.json");console.log(e.version),process.exit(0)}var $t=Ae.dirname(Vn(import.meta.url)),tr=kt(Ae.join($t,"AGENTS.md"),"utf-8"),nr=kt(Ae.join($t,"build.mjs"),"utf-8");async function rr(){let e=new ue(tr,nr);e.basePath=je.path,await e.init((je.env??"production").toLowerCase()),process.stdout.isTTY&&process.stdout.write("\x1B[2J\x1B[3J\x1B[H");let t=Zn(sr(Tt,{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()}rr().catch(e=>{let t=e;console.error("\u274C",t.body||t.message),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleekcms/sync",
3
- "version": "2.15.0",
3
+ "version": "3.0.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",
@@ -28,7 +28,7 @@
28
28
  "dist"
29
29
  ],
30
30
  "scripts": {
31
- "build": "npm run clean && node esbuild.config.mjs && cp src/AGENT.md dist/ && cp src/build.mjs dist/ && chmod +x dist/index.js",
31
+ "build": "npm run clean && node esbuild.config.mjs && cp src/AGENTS.md dist/ && cp src/build.mjs dist/ && chmod +x dist/index.js",
32
32
  "dev": "tsx src/index.tsx",
33
33
  "typecheck": "tsc --noEmit",
34
34
  "clean": "rm -rf dist",
File without changes