@avocadostudio-ai/cli 0.25.0 → 0.26.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
@@ -54,6 +54,7 @@ npx @avocadostudio-ai/cli start \
54
54
  | `--orchestrator <url>` | `AVOCADO_ORCHESTRATOR_URL` | `http://localhost:4200` | Orchestrator base URL the editor talks to |
55
55
  | `--preview <url>` | `AVOCADO_SITE_ORIGIN` | `http://localhost:3000` | Default preview site origin (can be overridden per-session via `?previewUrl=`) |
56
56
  | `--publish-token <tok>` | `AVOCADO_PUBLISH_TOKEN` | _unset_ | Forwarded as `x-publish-token` on publish requests |
57
+ | `--draft-secret <s>` | `AVOCADO_SITE_DRAFT_SECRET`, then `DRAFT_MODE_SECRET` | _unset_ | The site's draft secret, for a loopback bind (see below) |
57
58
  | `--port <n>` | `PORT` | `4100` | Port for the editor server |
58
59
  | `--host <addr>` | `HOST` | `127.0.0.1` | Bind address. Use `0.0.0.0` for LAN access |
59
60
 
@@ -63,6 +64,8 @@ Run `avocadostudio --help` for the up-to-date list.
63
64
 
64
65
  The CLI serves the editor as a static SPA and injects a small `<script>` into `<head>` that sets `window.__AVOCADO_CONFIG__` before the app boots. The editor reads from this object first, falling back to its build-time defaults.
65
66
 
67
+ **Credentials stay out of the page on a public bind.** The page is served before the editor's password gate runs, so anything written into it can be read by anyone who loads it. On a non-loopback `--host` the CLI therefore writes neither `siteDraftSecret` nor `publishToken`. After sign-in the editor fetches both from the orchestrator's `GET /editor/credentials`, which reads the orchestrator's own `DRAFT_MODE_SECRET` and `PUBLISH_TOKEN` and requires a valid access token. To host the editor, set those on the orchestrator and an access password, not on the CLI. On `127.0.0.1` the flags still write into the page, as before.
68
+
66
69
  This means the same prebuilt bundle works against any orchestrator — no rebuild needed per deployment. If you want to embed the editor inside your own Next.js app instead of running this CLI, mount the bundle yourself and inject the same `__AVOCADO_CONFIG__` shape; the keys are `orchestratorUrl`, `siteOrigin`, and `publishToken`.
67
70
 
68
71
  ## Compatibility
package/dist/index.js CHANGED
@@ -15579,13 +15579,20 @@ function resolveEditorDist() {
15579
15579
  "Could not locate editor bundle. Expected editor-dist/ (published package) or ../apps/editor/dist (monorepo). Run `pnpm --filter @ai-site-editor/editor build` first."
15580
15580
  );
15581
15581
  }
15582
- function configScript(opts) {
15583
- const cfg = {
15582
+ function isLoopbackHost(host) {
15583
+ const h = host.trim().toLowerCase().replace(/^\[|\]$/g, "");
15584
+ return h === "localhost" || h === "::1" || /^127(\.\d{1,3}){3}$/.test(h);
15585
+ }
15586
+ function editorPageConfig(opts) {
15587
+ const local = isLoopbackHost(opts.host);
15588
+ return {
15584
15589
  orchestratorUrl: opts.orchestrator,
15585
15590
  siteOrigin: opts.siteOrigin,
15586
- publishToken: opts.publishToken,
15587
- siteDraftSecret: opts.siteDraftSecret
15591
+ ...local ? { publishToken: opts.publishToken, siteDraftSecret: opts.siteDraftSecret } : {}
15588
15592
  };
15593
+ }
15594
+ function configScript(opts) {
15595
+ const cfg = editorPageConfig(opts);
15589
15596
  const json2 = JSON.stringify(cfg).replace(/</g, "\\u003c");
15590
15597
  return `<script>window.__AVOCADO_CONFIG__=${json2};</script>`;
15591
15598
  }
@@ -15667,6 +15674,10 @@ async function startCommand(args) {
15667
15674
  ...opts.host === "127.0.0.1" ? [` also reachable at http://localhost:${opts.port} \u2014 the spelling the docs use`] : [],
15668
15675
  ` orchestrator: ${opts.orchestrator}`,
15669
15676
  ` preview site: ${opts.siteOrigin}`,
15677
+ ...isLoopbackHost(opts.host) ? [] : [
15678
+ ` public bind: the draft secret and publish token are not written into the page;`,
15679
+ ` the editor fetches them from the orchestrator after sign-in`
15680
+ ],
15670
15681
  opts.publishToken ? ` publish token: (set)` : ` publish token: (unset)`,
15671
15682
  /*
15672
15683
  * Say this out loud, because its absence is silent and catastrophic.
@@ -15708,7 +15719,9 @@ function printHelp() {
15708
15719
  " --preview <url> Default preview site origin (env AVOCADO_SITE_ORIGIN, default http://localhost:3000)",
15709
15720
  " --publish-token <tok> Forward x-publish-token header (env AVOCADO_PUBLISH_TOKEN)",
15710
15721
  " --draft-secret <s> Site DRAFT_MODE_SECRET; required for editing in production",
15711
- " (env AVOCADO_SITE_DRAFT_SECRET, then DRAFT_MODE_SECRET)",
15722
+ " (env AVOCADO_SITE_DRAFT_SECRET, then DRAFT_MODE_SECRET).",
15723
+ " Written into the page only on a loopback --host; on a public",
15724
+ " bind the editor fetches it from the orchestrator after sign-in",
15712
15725
  " --port <n> Port to serve the editor on (env PORT, default 4100)",
15713
15726
  " --host <addr> Bind address (env HOST, default 127.0.0.1)",
15714
15727
  "",
@@ -1 +1 @@
1
- import{P as b}from"./chunk-2CNEFIQP-BCM0jyMr.js";import{u as y,a as j,E as L,_ as m,L as M,b as p,o as P,i as A}from"./PuckPrototypeRoute-BMFSrgRi.js";import{r as s,j as g}from"./index-BWf4GeHX.js";import{E as N,u as U}from"./index-DxOQxIUC.js";import"./index-C8b9WNzA.js";A();A();function C({content:a,onChange:x,extensions:v,editable:d=!0,onFocusChange:o,name:c}){const[i,E]=P(null,50,{leading:!0,maxWait:200}),l=s.useRef(!1),S=s.useRef(""),f=s.useRef(null),R=!!f.current,_=j(t=>t.state.ui.field.focus===c),T=t=>{f.current&&clearTimeout(f.current),f.current=setTimeout(()=>{S.current===t&&(f.current=null)},200)},u=y(),e=U({extensions:v,content:a,editable:d,immediatelyRender:!1,parseOptions:{preserveWhitespace:"full"},onUpdate:({editor:t})=>{if(l.current||!_){u.getState().setUi({field:{focus:c}});return}const r=t.getHTML(),{from:n,to:h}=t.state.selection;E({from:n,to:h,html:r}),T(r),S.current=r}});return s.useEffect(()=>{if(!e)return;const t=()=>{o==null||o(e)};return e.on("focus",t),()=>{e.off("focus",t)}},[e,o]),s.useEffect(()=>{if(i){const{ui:t}=u.getState().state;x(i.html,{field:m(p({},t.field),{metadata:{from:i.from,to:i.to}})})}},[e,i,x,u,c]),s.useEffect(()=>{e==null||e.setEditable(d)},[e,d]),s.useEffect(()=>{var t;if(!e||R||e.getHTML()===a)return;l.current=!0,e.commands.setContent(a,{emitUpdate:!1});const{ui:n}=u.getState().state;typeof((t=n.field.metadata)==null?void 0:t.from)<"u"&&e.commands.setTextSelection({from:n.field.metadata.from,to:n.field.metadata.to}),l.current=!1},[a,e,u]),e}var F=s.memo(a=>{const{onChange:x,content:v,readOnly:d=!1,field:o,inline:c=!1,onFocus:i,id:E,name:l}=a,{tiptap:S={},options:f}=o,{extensions:R=[]}=S,_=s.useMemo(()=>[b.configure(f),...R],[o,R]),T=y(),u=`${l}${c?"::inline":""}`,e=C({content:v,onChange:x,extensions:_,editable:!d,name:u,onFocusChange:r=>{if(r){const n=T.getState();T.setState({currentRichText:{field:o,editor:r,id:E,inline:c},state:m(p({},n.state),{ui:m(p({},n.state.ui),{field:m(p({},n.state.ui.field),{focus:u})})})}),i==null||i(r)}}}),t=j(r=>{var n,h;return!c&&((n=r.currentRichText)==null?void 0:n.id)===E&&((h=r.currentRichText)!=null&&h.inlineComponentId)?r.currentRichText.editor:e});return e?g.jsx(L,m(p({},a),{editor:e,menu:g.jsx(M,{field:o,editor:t,readOnly:d}),children:g.jsx(N,{editor:e,className:"rich-text"})})):null});F.displayName="Editor";export{F as Editor};
1
+ import{P as b}from"./chunk-2CNEFIQP-kdd7MquI.js";import{u as y,a as j,E as L,_ as m,L as M,b as p,o as P,i as A}from"./PuckPrototypeRoute-CFwlZaQP.js";import{r as s,j as g}from"./index-Cy-PEt_d.js";import{E as N,u as U}from"./index-BI9xm3sn.js";import"./index-bSPYizZ6.js";A();A();function C({content:a,onChange:x,extensions:v,editable:d=!0,onFocusChange:o,name:c}){const[i,E]=P(null,50,{leading:!0,maxWait:200}),l=s.useRef(!1),S=s.useRef(""),f=s.useRef(null),R=!!f.current,_=j(t=>t.state.ui.field.focus===c),T=t=>{f.current&&clearTimeout(f.current),f.current=setTimeout(()=>{S.current===t&&(f.current=null)},200)},u=y(),e=U({extensions:v,content:a,editable:d,immediatelyRender:!1,parseOptions:{preserveWhitespace:"full"},onUpdate:({editor:t})=>{if(l.current||!_){u.getState().setUi({field:{focus:c}});return}const r=t.getHTML(),{from:n,to:h}=t.state.selection;E({from:n,to:h,html:r}),T(r),S.current=r}});return s.useEffect(()=>{if(!e)return;const t=()=>{o==null||o(e)};return e.on("focus",t),()=>{e.off("focus",t)}},[e,o]),s.useEffect(()=>{if(i){const{ui:t}=u.getState().state;x(i.html,{field:m(p({},t.field),{metadata:{from:i.from,to:i.to}})})}},[e,i,x,u,c]),s.useEffect(()=>{e==null||e.setEditable(d)},[e,d]),s.useEffect(()=>{var t;if(!e||R||e.getHTML()===a)return;l.current=!0,e.commands.setContent(a,{emitUpdate:!1});const{ui:n}=u.getState().state;typeof((t=n.field.metadata)==null?void 0:t.from)<"u"&&e.commands.setTextSelection({from:n.field.metadata.from,to:n.field.metadata.to}),l.current=!1},[a,e,u]),e}var F=s.memo(a=>{const{onChange:x,content:v,readOnly:d=!1,field:o,inline:c=!1,onFocus:i,id:E,name:l}=a,{tiptap:S={},options:f}=o,{extensions:R=[]}=S,_=s.useMemo(()=>[b.configure(f),...R],[o,R]),T=y(),u=`${l}${c?"::inline":""}`,e=C({content:v,onChange:x,extensions:_,editable:!d,name:u,onFocusChange:r=>{if(r){const n=T.getState();T.setState({currentRichText:{field:o,editor:r,id:E,inline:c},state:m(p({},n.state),{ui:m(p({},n.state.ui),{field:m(p({},n.state.ui.field),{focus:u})})})}),i==null||i(r)}}}),t=j(r=>{var n,h;return!c&&((n=r.currentRichText)==null?void 0:n.id)===E&&((h=r.currentRichText)!=null&&h.inlineComponentId)?r.currentRichText.editor:e});return e?g.jsx(L,m(p({},a),{editor:e,menu:g.jsx(M,{field:o,editor:t,readOnly:d}),children:g.jsx(N,{editor:e,className:"rich-text"})})):null});F.displayName="Editor";export{F as Editor};