@lunora/cli 1.0.0-alpha.320 → 1.0.0-alpha.322

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/dist/index.d.mts CHANGED
@@ -880,9 +880,10 @@ interface StudioServerHandle {
880
880
  /**
881
881
  * How the dev child runs. `wrangler` is the classic `lunora dev` stack (wrangler
882
882
  * worker + embedded studio + codegen watch) for a standalone class-C project.
883
- * `vite` is a project on `@lunora/vite`: the plugin already runs the worker,
884
- * studio, and codegen inside the Vite dev server, so `lunora dev` runs the
885
- * project's own dev script and gets out of the way — this also covers class-B
883
+ * `vite` is a project whose BUNDLER PLUGIN already runs the worker — `@lunora/vite`
884
+ * inside the Vite dev server, or `@lunora/rspack/rsbuild` alongside an Rsbuild
885
+ * one — together with studio and codegen, so `lunora dev` runs the project's own
886
+ * dev script and gets out of the way — this also covers class-B
886
887
  * frameworks whose own dev server runs the worker in `workerd` (Astro 6 +
887
888
  * `@astrojs/cloudflare`, which embeds `@cloudflare/vite-plugin` in `astro dev`:
888
889
  * SSR + `/_lunora/*` + `ShardDO` in one process, HMR intact). `framework-worker`
package/dist/index.d.ts CHANGED
@@ -880,9 +880,10 @@ interface StudioServerHandle {
880
880
  /**
881
881
  * How the dev child runs. `wrangler` is the classic `lunora dev` stack (wrangler
882
882
  * worker + embedded studio + codegen watch) for a standalone class-C project.
883
- * `vite` is a project on `@lunora/vite`: the plugin already runs the worker,
884
- * studio, and codegen inside the Vite dev server, so `lunora dev` runs the
885
- * project's own dev script and gets out of the way — this also covers class-B
883
+ * `vite` is a project whose BUNDLER PLUGIN already runs the worker — `@lunora/vite`
884
+ * inside the Vite dev server, or `@lunora/rspack/rsbuild` alongside an Rsbuild
885
+ * one — together with studio and codegen, so `lunora dev` runs the project's own
886
+ * dev script and gets out of the way — this also covers class-B
886
887
  * frameworks whose own dev server runs the worker in `workerd` (Astro 6 +
887
888
  * `@astrojs/cloudflare`, which embeds `@cloudflare/vite-plugin` in `astro dev`:
888
889
  * SSR + `/_lunora/*` + `ShardDO` in one process, HMR intact). `framework-worker`
@@ -1,7 +1,7 @@
1
- import{spawnSync as N,spawn as oe}from"node:child_process";import{existsSync as C,watch as Te,readFileSync as M,mkdirSync as _e,openSync as ie,closeSync as se,statSync as Ie,readSync as Le}from"node:fs";import{join as k,dirname as Re}from"node:path";import{runCodegen as ae}from"@lunora/codegen";import{runPostCodegenHook as Ae,detectAgentRules as ce,updateDevServerState as x,readDevServerState as R,claimDevServerState as de,DEV_HANDOFF_ENV as T,clearDevServerState as y,readLiveDevServerState as A,isDevServerReady as B,detectFramework as le,readProjectDependencyNames as Oe,DEV_LOG_FILE_ENV as ue,DEV_DAEMON_ENV as F,detectPackageManager as fe,execArgsFor as $,isRecordedProcessCurrent as De,DEV_LOG_FILE as $e,CODEGEN_ENV as ge,isCodegenDisabled as Ne,detectAiAgent as Ce,readProjectRemotePreference as Me,inferLunoraBindings as xe,packageNamesFromBindings as Fe,ensureDevVarsExample as je,ensureDevVariables as Ge,isInteractive as Ue,DEV_VARS_FILE as _,DEV_VARS_EXAMPLE_FILE as Ve,fillDevSecrets as We,DEV_BINDINGS_FILE as Be,DEV_STATE_FILE as ve,claimAgentRulesHint as He,AGENT_RULES_HINT as Ke,resolveDeployDriver as Ye,resolveProjectTarget as Je,runScriptCommand as Xe,discoverContainerInfo as ze,streamContainerLogs as Qe,parseDevVariableEntries as Ze,formatLunoraEvent as qe}from"@lunora/config";import{resolveRemoteEnabled as er,findWranglerFile as j,readWranglerJsonc as G,materializeRemoteWranglerConfig as rr}from"@lunora/config/cloudflare";import{p as tr,a as nr}from"../packem_shared/deploy-target-DCWwiuAe.mjs";import{w as or}from"../packem_shared/binding-manifest-file-CQ1eQYy1.mjs";import{a as ir}from"../packem_shared/codegen-error-AmH54ofi.mjs";import{a as sr}from"../packem_shared/platform-diagnostics-Cn2g6Jh-.mjs";import{d as ar}from"../packem_shared/command-Mmxzxyr5.mjs";import{EXIT_CODE as U}from"../packem_shared/EXIT_CODE-08cwt3MK.mjs";import{createServer as cr,connect as dr}from"node:net";import{forceJsonLogging as lr}from"../packem_shared/createLogger-Cl18I8AX.mjs";import{networkInterfaces as ur}from"node:os";import{spawnShellCompat as me}from"../packem_shared/createRecordingSpawner-sS7LEN7x.mjs";import{createServer as fr,request as gr}from"node:http";import{pipeline as vr}from"node:stream";import{loadStudioAssets as H,studioAssetsStamp as K,renderStudioHtml as mr,resolveAdminToken as pr,transportRejectionReason as Y,SCHEMA_EDIT_ENDPOINT as wr,POLICY_SCAFFOLD_ENDPOINT as hr,SEED_ENDPOINT as Sr,sendStudioDocument as yr,serveJsonHandler as Er,isStandaloneModulePath as kr,readStandaloneAsset as br,assetContentType as Pr,handleSchemaEditRequest as Tr,handlePolicyScaffoldRequest as _r,handleSeedRequest as Ir,applyStudioAssetCache as Lr}from"@lunora/config/studio-host";import{c as Rr}from"../packem_shared/tui-prompts-B3YwUhGw.mjs";import{setTimeout as I}from"node:timers/promises";import{provisionBindings as Ar}from"./runDeployCommand.mjs";import{a as E}from"../packem_shared/render-lunora-error--4tmM6mt.mjs";const Or=100,Dr=300,$r=/[/\\]/u,Nr=(e,r,n)=>{const{apiSpec:t,logger:o,projectRoot:i,target:s}=e;try{const a=ae({apiSpec:t,lunoraDirectory:r,projectRoot:i,target:s});o.success(`codegen: wrote ${r}/_generated (${n})`),sr(a.platformDiagnostics,o)}catch(a){return o.error(ir(a,n)),!1}return!0},Cr=e=>{const r=e.lunoraDirectory??"lunora",n=k(e.projectRoot,r),t=e.debounceMs??Or;let o=!1,i=!1,s=!1,a,d=0,u=Promise.resolve(),g;const m=new Promise(v=>{g=v}),p=v=>{o||(a=v,!i&&(i=!0,u=(async()=>{try{for(;a!==void 0;){const h=a;if(a=void 0,!!Nr(e,r,h)){s=!0;try{(await Ae({cwd:e.projectRoot,logger:e.logger,spawner:e.spawner,stdoutToStderr:e.jsonLogs})).ran&&(d=Date.now())}finally{s=!1}}}}catch(h){e.logger.error(`codegen watch: ${h instanceof Error?h.message:String(h)}`)}finally{i=!1,g()}})()))};p("startup");const l=v=>(e.logger.warn(`codegen watch unavailable (${v}) — schema edits will NOT auto-regenerate. Run \`lunora codegen\` manually after each edit.`),{close:()=>(o=!0,a=void 0,u),ready:m,watchAvailable:!1});let c,f;if(!C(n))return l(`no such directory: ${n}`);try{f=Te(n,{recursive:!0},(v,h)=>{if(!(typeof h=="string"&&h.split($r).includes("_generated"))){if(s||Date.now()-d<Dr){c&&(clearTimeout(c),c=void 0);return}c&&clearTimeout(c),c=setTimeout(()=>{p(`change: ${h??"?"}`)},t)}})}catch(v){return l(v instanceof Error?v.message:String(v))}const w=f;return{close:()=>(o=!0,a=void 0,c&&clearTimeout(c),w.close(),u),ready:m,watchAvailable:!0}},Mr=64,pe="127.0.0.1",xr=65535,Fr=(e,r=pe)=>new Promise(n=>{const t=cr();t.once("error",()=>{n(!1)}),t.once("listening",()=>{t.close(()=>{n(!0)})}),t.listen(e,r)}),jr=async(e,r=pe,n=Mr)=>{for(let t=e;t<e+n&&t<=xr;t+=1)if(await Fr(t,r))return t;return e},J=(e=ur)=>Object.values(e()).some(r=>r?.some(n=>n.internal&&n.address==="::1")),X="/_lunora",z=e=>{const r=e.indexOf("?");return r===-1?e:e.slice(0,r)},Q=3e4,Z=(e,r,n)=>{const t=gr({headers:{...e.headers,host:n.host},hostname:n.hostname,method:e.method,path:e.url,port:n.port},o=>{r.writeHead(o.statusCode??502,o.headers),vr(o,r,()=>{})});t.setTimeout(Q,()=>{t.destroy(new Error(`no response within ${String(Q)}ms`))}),t.on("error",o=>{if(r.headersSent){r.destroy();return}r.statusCode=o.message.startsWith("no response within")?504:502,r.end(`lunora dev: worker unreachable (${o.message})`)}),r.on("close",()=>{r.writableFinished||t.destroy()}),e.pipe(t)},Gr=5e3,Ur=(e,r,n,t)=>{const o=dr({host:t.hostname,port:Number(t.port)},()=>{o.setTimeout(0);const i=[`${e.method??"GET"} ${e.url??"/"} HTTP/1.1`];for(const[s,a]of Object.entries(e.headers))if(s!=="host")for(const d of Array.isArray(a)?a:[a])d!==void 0&&i.push(`${s}: ${d}`);i.push(`host: ${t.host}`,"",""),o.write(i.join(`\r
2
- `)),n.length>0&&o.write(n),o.pipe(r),r.pipe(o)});o.setTimeout(Gr,()=>o.destroy()),o.on("error",()=>r.destroy()),r.on("error",()=>o.destroy())},Vr=async e=>{const r=e.host??"127.0.0.1",n=r==="127.0.0.1"||r==="localhost"||r==="::1",t=new URL(e.workerOrigin);let o=H(e.logger,import.meta.url),i=K(import.meta.url);const s=mr({adminToken:n?pr(e.cwd):void 0,basePath:"/",dataEditable:n,rulesInstalled:ce(e.cwd).installed,runAsIdentity:n,schemaEditable:n,scriptSrc:"/studio.js",styleHref:"/styles.css"}),a=(l,c,f,w,v)=>{Lr(l,c,v,i)||(c.statusCode=200,c.setHeader("Content-Type",w),c.end(f))},d=(l,c,f)=>{l.statusCode=c,l.setHeader("Content-Type","text/plain"),l.end(f)},u=(l,c,f,w)=>{if(n){Er(l,c,f,e.cwd,{apiSpec:e.apiSpec});return}d(c,403,w)},g=(l,c,f)=>{const w=l==="/styles.css",v=kr(l);if(!w&&!v)return!1;const h=K(import.meta.url);if(h!==i&&(o=H(e.logger,import.meta.url),i=h),o===void 0)return d(f,501,"Lunora studio assets not found — install and build @lunora/studio."),!0;if(w)return a(c,f,o.styles,"text/css; charset=utf-8","styles.css"),!0;const b=l.slice(l.lastIndexOf("/")+1),W=br(b,import.meta.url);return W===void 0?(d(f,404,"Not found"),!0):(a(c,f,W,Pr(b),b),!0)},m=Buffer.from(s),p=fr((l,c)=>{const f=z(l.url??"/");if(n){const v=Y(l,e.logger);if(v!==void 0){d(c,403,v);return}}if(f.startsWith(X)){if(!n){d(c,403,"Lunora studio: the worker admin proxy is only available on a loopback bind.");return}Z(l,c,t);return}if(f===wr){u(l,c,Tr,"Lunora schema editing is only available on loopback hosts in dev.");return}if(f===hr){u(l,c,_r,"Lunora policy scaffolding is only available on loopback hosts in dev.");return}if(f===Sr){u(l,c,Ir,"Lunora data seeding is only available on loopback hosts in dev.");return}if(g(f,l,c))return;const w=l.headers["sec-fetch-mode"];if(n&&typeof w=="string"&&w!=="navigate"){Z(l,c,t);return}yr(c,m)});return p.on("upgrade",(l,c,f)=>{if(!n||Y(l,e.logger)!==void 0){c.destroy();return}z(l.url??"").startsWith(X)?Ur(l,c,f,t):c.destroy()}),await new Promise((l,c)=>{p.once("error",f=>{f.code==="EADDRINUSE"?c(new Error(`studio port ${String(e.port)} is already in use — pass a different port with --port, or stop the process using it`,{cause:f})):c(f)}),p.listen(e.port,r,()=>{l()})}),{close:()=>new Promise(l=>{p.close(()=>{l()})}),url:`http://${r==="0.0.0.0"||r==="::"?"localhost":r}:${String(e.port)}`}},Wr=12e4,V=250,Br=1e3,Hr="LUNORA_DEV_READY_TIMEOUT_MS",we=e=>{if(e!==void 0)return e;const r=Number(process.env[Hr]??"");return Number.isFinite(r)&&r>0?r:Wr},he=async(e,r)=>{try{const n=AbortSignal.timeout(Br);return await(await fetch(new URL("/_lunora/status",e),{redirect:"manual",signal:r===void 0?n:AbortSignal.any([r,n])})).body?.cancel(),!0}catch{return!1}},Kr=async e=>{const{cwd:r,logger:n,origin:t,signal:o}=e,i=e.probe??he,s=we(e.readyTimeoutMs),a=Date.now()+s,d=()=>o.aborted;for(;!d()&&Date.now()<a;){let u=!1;try{u=await i(t,o)}catch{}if(d()||Date.now()>=a)break;if(u)return x(r,{readyAt:new Date().toISOString()},{expectedPid:process.pid}),R(r)?.readyAt!==void 0;try{await I(V,void 0,{signal:o})}catch{}}return d()||n.warn(`server did not answer on ${t} within ${String(Math.round(s/1e3))}s — .lunora/dev.json will have no readyAt, so anything polling for readiness will keep waiting. The server itself is unaffected; raise LUNORA_DEV_READY_TIMEOUT_MS if it is just slow to boot.`),!1},Yr=1e4,Jr=100,Xr=40,P=256*1024,zr=new Set(["nuxt","sveltekit"]),O=e=>zr.has(le(e).framework)?"framework-worker":Oe(e).has("@lunora/vite")?"vite":"wrangler",Se=e=>{const r=fe(e);let n;try{const t=M(k(e,"package.json"),"utf8");n=JSON.parse(t).scripts?.dev}catch{}if(n===void 0||n.trim()===""||n.includes("lunora")){const t=$(r,"vite",["dev"]);return{args:t.args,command:t.command}}return{args:["run","dev"],command:r}},Qr=e=>{_e(Re(e.logPath),{recursive:!0});const r=ie(e.logPath,"w",384);let n;const t=me(e.command,e.args);try{n=oe(t.command,t.args,{cwd:e.cwd,detached:!0,env:e.env,shell:t.shell,stdio:["ignore",r,r],windowsHide:!0})}finally{se(r)}return n.unref(),{exited:new Promise(o=>{n.on("error",()=>{o(1)}),n.on("exit",(i,s)=>{o(i??(s?1:0))})}),pid:n.pid}},Zr=e=>{try{const{size:r}=Ie(e);if(r<=P)return M(e,"utf8");const n=ie(e,"r");try{const t=Buffer.alloc(P),o=Le(n,t,0,P,r-P),i=t.toString("utf8",0,o);return i.slice(i.indexOf(`
3
- `)+1)}finally{se(n)}}catch{return}},ye=(e,r)=>{const n=Zr(e);if(n===void 0)return[];const t=n.split(`
4
- `);return t.at(-1)===""&&t.pop(),r>0?t.slice(-r):t},Ee=(e,r)=>r?.logFile??k(e,$e),ke=e=>{e.info(" Stop: lunora dev stop"),e.info(" Status: lunora dev status"),e.info(" Logs: lunora dev logs")},L=(e,r)=>{e.warn(`Dev server already running at ${r.url} (pid ${String(r.pid)})`),ke(e)},qr=(e,r)=>{e.success(`Dev server running at ${r.url} (pid ${String(r.pid)})`),r.studioUrl!==void 0&&e.info(` Studio: ${r.studioUrl}`),ke(e)},et=async e=>{const{child:r,cwd:n,deadline:t,pollIntervalMs:o,probe:i}=e,s=r.exited.then(a=>({exitCode:a}));for(;Date.now()<t;){const a=A(n);if(a!==void 0&&a.pid!==process.pid&&await i(a.url))return{state:a,status:"ready"};const d=await Promise.race([s,I(o)]);if(d!==void 0)return{...d,status:"exited"}}return{status:"timeout"}},rt=async e=>{const{cwd:r,logger:n}=e,t=Ee(r,void 0),o=we(e.readyTimeoutMs),i=(e.spawnDetached??Qr)({args:e.command.args,command:e.command.command,cwd:r,env:{...process.env,...e.env,[F]:"1",[ue]:t,...process.env.FORCE_COLOR===void 0?{NO_COLOR:"1"}:{},...e.json?{LUNORA_LOG_JSON:"1"}:{}},logPath:t}),s=await et({child:i,cwd:r,deadline:Date.now()+o,pollIntervalMs:e.pollIntervalMs??V,probe:e.probe??he});if(s.status==="ready")return qr(n,s.state),{code:0};if(s.status==="exited"){n.error(`dev server exited before becoming ready (exit code ${String(s.exitCode)}). Last output:`);for(const d of ye(t,Xr))n.error(` ${d}`);return y(r,i.pid??process.pid),{code:s.exitCode===0?1:s.exitCode}}i.pid!==void 0&&R(r)?.pid===process.pid&&x(r,{logFile:t,pid:i.pid});const a=i.pid===void 0?"":` (pid ${String(i.pid)})`;return n.warn(`dev server did not confirm ready within ${String(Math.round(o/1e3))}s — it may still be compiling${a}. Check \`lunora dev status\` and \`lunora dev logs\`; \`lunora dev stop\` shuts it down.`),{code:U.UNAVAILABLE}},be=e=>e.codegen!==!1&&!Ne(process.env[ge]),Pe=e=>{const r={...e.remote===!0?{LUNORA_REMOTE:"1"}:{},...e.codegen===!1?{[ge]:"0"}:{}};return Object.keys(r).length>0?{env:r}:{}},tt=(e,r)=>{const n=["dev"];return e.apiSpec!==void 0&&n.push("--api-spec",e.apiSpec),e.port!==void 0&&n.push("--port",String(e.port)),e.workerPort!==void 0&&n.push("--worker-port",String(e.workerPort)),e.inspectorPort!==void 0&&n.push("--inspector-port",String(e.inspectorPort)),e.emitBindings!==void 0&&n.push("--emit-bindings",e.emitBindings),e.codegen===!1&&n.push("--no-codegen"),e.studio===!1&&n.push("--no-studio"),e.worker===!1&&n.push("--no-worker"),e.target!==void 0&&n.push("--target",e.target),r&&n.push("--remote"),n},nt=async e=>{const{cwd:r,jsonLogs:n,logger:t,options:o,remote:i}=e,s=e.run??rt,a=O(r),d=a==="wrangler"?`http://localhost:${String(o.workerPort??8787)}`:"http://localhost:5173",u=de(r,{background:!0,mode:"cli",pid:process.pid,startedAt:new Date().toISOString(),url:d});if(!u.ok)return u.existing!==void 0&&L(t,u.existing),{code:0};const g={[T]:String(process.pid)};try{return a==="vite"?await s({command:Se(r),cwd:r,env:{...Pe({...o,remote:i}).env,...g},json:n,logger:t}):await s({command:{args:[process.argv[1]??"lunora",...tt(o,i)],command:process.execPath},cwd:r,env:g,json:n,logger:t})}finally{y(r,process.pid)}},ot=e=>{try{const r=N("ps",["-o","pgid=","-p",String(e)],{encoding:"utf8"}),n=Number.parseInt(r.stdout.trim(),10);return Number.isInteger(n)&&n>0?n:void 0}catch{return}},it=(e,r,n=process.platform,t=N)=>{if(n==="win32"){try{t("taskkill",["/pid",String(e.pid),"/T","/F"],{stdio:"ignore"})}catch{}return}if(e.background===!0)try{r(-(ot(e.pid)??e.pid),"SIGKILL");return}catch{}try{r(e.pid,"SIGKILL")}catch{}},st=(e,r)=>(r.json?E({pid:e.pid,stopped:!1}):r.logger.error(`dev server (pid ${String(e.pid)}) survived the force-kill — record kept; retry \`lunora dev stop\` or kill the process manually.`),{code:1}),at=async e=>{const{cwd:r,logger:n}=e,t=R(r),o=e.alive??(m=>m===t?.pid&&De(t)),i=e.signal??((m,p)=>{process.kill(m,p)}),s=e.pollIntervalMs??V,a=e.stopGraceMs??Yr,d=e.platform??process.platform,u=e.spawnSyncImpl??N;if(t===void 0||!o(t.pid))return t!==void 0&&y(r,t.pid),e.json?E({running:!1,stopped:!1}):n.info("No dev server running."),{code:0};try{i(t.pid,"SIGTERM")}catch{}const g=Date.now()+a;for(;o(t.pid)&&Date.now()<g;)await I(s);return o(t.pid)&&(it(t,i,d,u),await I(s),o(t.pid))?st(t,e):(y(r,t.pid),e.json?E({pid:t.pid,stopped:!0}):n.success(`Stopped dev server (pid ${String(t.pid)}).`),{code:0})},ct=e=>{const{cwd:r,logger:n}=e,t=A(r);if(t===void 0)return e.json?E({running:!1}):n.info("No dev server running."),{code:0};const o=e.now??Date.now,i=t.startedAt===void 0?Number.NaN:Date.parse(t.startedAt),s=Number.isFinite(i)?Math.max(0,Math.round((o()-i)/1e3)):void 0;if(e.json)return E({background:t.background===!0,logFile:t.logFile,mode:t.mode,pid:t.pid,ready:B(t),readyAt:t.readyAt,running:!0,startedAt:t.startedAt,studioUrl:t.studioUrl,uptimeSeconds:s,url:t.url}),{code:0};const a=[`pid ${String(t.pid)}`,...s===void 0?[]:[`uptime ${String(s)}s`],t.background===!0?"background":"foreground",B(t)?"ready":"starting"];return n.success(`Dev server running at ${t.url} (${a.join(", ")})`),t.studioUrl!==void 0&&n.info(` Studio: ${t.studioUrl}`),t.logFile!==void 0&&n.info(` Logs: lunora dev logs (${t.logFile})`),{code:0}},dt=e=>{const{cwd:r,logger:n}=e,t=R(r),o=Ee(r,t);if(!C(o))return n.info("No dev server logs found — output is captured when the server runs in background mode (`lunora dev --background`)."),{code:0};const i=ye(o,e.lines??Jr);return i.length>0&&process.stdout.write(`${i.join(`
1
+ import{spawnSync as $,spawn as oe}from"node:child_process";import{existsSync as C,watch as Te,readFileSync as M,mkdirSync as _e,openSync as ie,closeSync as se,statSync as Re,readSync as Ie}from"node:fs";import{join as k,dirname as Le}from"node:path";import{runCodegen as ae}from"@lunora/codegen";import{runPostCodegenHook as Oe,detectAgentRules as ce,updateDevServerState as x,readDevServerState as L,claimDevServerState as de,DEV_HANDOFF_ENV as T,clearDevServerState as y,readLiveDevServerState as O,isDevServerReady as B,detectFramework as le,readProjectDependencyNames as Ae,DEV_LOG_FILE_ENV as ue,DEV_DAEMON_ENV as F,detectPackageManager as fe,execArgsFor as N,isRecordedProcessCurrent as De,DEV_LOG_FILE as Ne,CODEGEN_ENV as ge,isCodegenDisabled as $e,detectAiAgent as Ce,readProjectRemotePreference as Me,inferLunoraBindings as xe,packageNamesFromBindings as Fe,ensureDevVarsExample as je,ensureDevVariables as Ge,isInteractive as Ue,DEV_VARS_FILE as _,DEV_VARS_EXAMPLE_FILE as Ve,fillDevSecrets as We,DEV_BINDINGS_FILE as Be,DEV_STATE_FILE as ve,claimAgentRulesHint as He,AGENT_RULES_HINT as Ke,resolveDeployDriver as Ye,resolveProjectTarget as Je,runScriptCommand as Xe,discoverContainerInfo as ze,streamContainerLogs as Qe,parseDevVariableEntries as Ze,formatLunoraEvent as qe}from"@lunora/config";import{resolveRemoteEnabled as er,findWranglerFile as j,readWranglerJsonc as G,materializeRemoteWranglerConfig as rr}from"@lunora/config/cloudflare";import{p as tr,a as nr}from"../packem_shared/deploy-target-DCWwiuAe.mjs";import{w as or}from"../packem_shared/binding-manifest-file-CQ1eQYy1.mjs";import{a as ir}from"../packem_shared/codegen-error-AmH54ofi.mjs";import{a as sr}from"../packem_shared/platform-diagnostics-Cn2g6Jh-.mjs";import{d as ar}from"../packem_shared/command-Mmxzxyr5.mjs";import{EXIT_CODE as U}from"../packem_shared/EXIT_CODE-08cwt3MK.mjs";import{createServer as cr,connect as dr}from"node:net";import{forceJsonLogging as lr}from"../packem_shared/createLogger-Cl18I8AX.mjs";import{networkInterfaces as ur}from"node:os";import{spawnShellCompat as me}from"../packem_shared/createRecordingSpawner-sS7LEN7x.mjs";import{createServer as fr,request as gr}from"node:http";import{pipeline as vr}from"node:stream";import{loadStudioAssets as H,studioAssetsStamp as K,renderStudioHtml as mr,resolveAdminToken as pr,transportRejectionReason as Y,SCHEMA_EDIT_ENDPOINT as wr,POLICY_SCAFFOLD_ENDPOINT as hr,SEED_ENDPOINT as Sr,sendStudioDocument as yr,serveJsonHandler as Er,isStandaloneModulePath as kr,readStandaloneAsset as br,assetContentType as Pr,handleSchemaEditRequest as Tr,handlePolicyScaffoldRequest as _r,handleSeedRequest as Rr,applyStudioAssetCache as Ir}from"@lunora/config/studio-host";import{c as Lr}from"../packem_shared/tui-prompts-B3YwUhGw.mjs";import{setTimeout as R}from"node:timers/promises";import{provisionBindings as Or}from"./runDeployCommand.mjs";import{a as E}from"../packem_shared/render-lunora-error--4tmM6mt.mjs";const Ar=100,Dr=300,Nr=/[/\\]/u,$r=(e,r,n)=>{const{apiSpec:t,logger:o,projectRoot:i,target:s}=e;try{const a=ae({apiSpec:t,lunoraDirectory:r,projectRoot:i,target:s});o.success(`codegen: wrote ${r}/_generated (${n})`),sr(a.platformDiagnostics,o)}catch(a){return o.error(ir(a,n)),!1}return!0},Cr=e=>{const r=e.lunoraDirectory??"lunora",n=k(e.projectRoot,r),t=e.debounceMs??Ar;let o=!1,i=!1,s=!1,a,d=0,u=Promise.resolve(),g;const m=new Promise(v=>{g=v}),p=v=>{o||(a=v,!i&&(i=!0,u=(async()=>{try{for(;a!==void 0;){const h=a;if(a=void 0,!!$r(e,r,h)){s=!0;try{(await Oe({cwd:e.projectRoot,logger:e.logger,spawner:e.spawner,stdoutToStderr:e.jsonLogs})).ran&&(d=Date.now())}finally{s=!1}}}}catch(h){e.logger.error(`codegen watch: ${h instanceof Error?h.message:String(h)}`)}finally{i=!1,g()}})()))};p("startup");const l=v=>(e.logger.warn(`codegen watch unavailable (${v}) — schema edits will NOT auto-regenerate. Run \`lunora codegen\` manually after each edit.`),{close:()=>(o=!0,a=void 0,u),ready:m,watchAvailable:!1});let c,f;if(!C(n))return l(`no such directory: ${n}`);try{f=Te(n,{recursive:!0},(v,h)=>{if(!(typeof h=="string"&&h.split(Nr).includes("_generated"))){if(s||Date.now()-d<Dr){c&&(clearTimeout(c),c=void 0);return}c&&clearTimeout(c),c=setTimeout(()=>{p(`change: ${h??"?"}`)},t)}})}catch(v){return l(v instanceof Error?v.message:String(v))}const w=f;return{close:()=>(o=!0,a=void 0,c&&clearTimeout(c),w.close(),u),ready:m,watchAvailable:!0}},Mr=64,pe="127.0.0.1",xr=65535,Fr=(e,r=pe)=>new Promise(n=>{const t=cr();t.once("error",()=>{n(!1)}),t.once("listening",()=>{t.close(()=>{n(!0)})}),t.listen(e,r)}),jr=async(e,r=pe,n=Mr)=>{for(let t=e;t<e+n&&t<=xr;t+=1)if(await Fr(t,r))return t;return e},J=(e=ur)=>Object.values(e()).some(r=>r?.some(n=>n.internal&&n.address==="::1")),X="/_lunora",z=e=>{const r=e.indexOf("?");return r===-1?e:e.slice(0,r)},Q=3e4,Z=(e,r,n)=>{const t=gr({headers:{...e.headers,host:n.host},hostname:n.hostname,method:e.method,path:e.url,port:n.port},o=>{r.writeHead(o.statusCode??502,o.headers),vr(o,r,()=>{})});t.setTimeout(Q,()=>{t.destroy(new Error(`no response within ${String(Q)}ms`))}),t.on("error",o=>{if(r.headersSent){r.destroy();return}r.statusCode=o.message.startsWith("no response within")?504:502,r.end(`lunora dev: worker unreachable (${o.message})`)}),r.on("close",()=>{r.writableFinished||t.destroy()}),e.pipe(t)},Gr=5e3,Ur=(e,r,n,t)=>{const o=dr({host:t.hostname,port:Number(t.port)},()=>{o.setTimeout(0);const i=[`${e.method??"GET"} ${e.url??"/"} HTTP/1.1`];for(const[s,a]of Object.entries(e.headers))if(s!=="host")for(const d of Array.isArray(a)?a:[a])d!==void 0&&i.push(`${s}: ${d}`);i.push(`host: ${t.host}`,"",""),o.write(i.join(`\r
2
+ `)),n.length>0&&o.write(n),o.pipe(r),r.pipe(o)});o.setTimeout(Gr,()=>o.destroy()),o.on("error",()=>r.destroy()),r.on("error",()=>o.destroy())},Vr=async e=>{const r=e.host??"127.0.0.1",n=r==="127.0.0.1"||r==="localhost"||r==="::1",t=new URL(e.workerOrigin);let o=H(e.logger,import.meta.url),i=K(import.meta.url);const s=mr({adminToken:n?pr(e.cwd):void 0,basePath:"/",dataEditable:n,rulesInstalled:ce(e.cwd).installed,runAsIdentity:n,schemaEditable:n,scriptSrc:"/studio.js",styleHref:"/styles.css"}),a=(l,c,f,w,v)=>{Ir(l,c,v,i)||(c.statusCode=200,c.setHeader("Content-Type",w),c.end(f))},d=(l,c,f)=>{l.statusCode=c,l.setHeader("Content-Type","text/plain"),l.end(f)},u=(l,c,f,w)=>{if(n){Er(l,c,f,e.cwd,{apiSpec:e.apiSpec});return}d(c,403,w)},g=(l,c,f)=>{const w=l==="/styles.css",v=kr(l);if(!w&&!v)return!1;const h=K(import.meta.url);if(h!==i&&(o=H(e.logger,import.meta.url),i=h),o===void 0)return d(f,501,"Lunora studio assets not found — install and build @lunora/studio."),!0;if(w)return a(c,f,o.styles,"text/css; charset=utf-8","styles.css"),!0;const b=l.slice(l.lastIndexOf("/")+1),W=br(b,import.meta.url);return W===void 0?(d(f,404,"Not found"),!0):(a(c,f,W,Pr(b),b),!0)},m=Buffer.from(s),p=fr((l,c)=>{const f=z(l.url??"/");if(n){const v=Y(l,e.logger);if(v!==void 0){d(c,403,v);return}}if(f.startsWith(X)){if(!n){d(c,403,"Lunora studio: the worker admin proxy is only available on a loopback bind.");return}Z(l,c,t);return}if(f===wr){u(l,c,Tr,"Lunora schema editing is only available on loopback hosts in dev.");return}if(f===hr){u(l,c,_r,"Lunora policy scaffolding is only available on loopback hosts in dev.");return}if(f===Sr){u(l,c,Rr,"Lunora data seeding is only available on loopback hosts in dev.");return}if(g(f,l,c))return;const w=l.headers["sec-fetch-mode"];if(n&&typeof w=="string"&&w!=="navigate"){Z(l,c,t);return}yr(c,m)});return p.on("upgrade",(l,c,f)=>{if(!n||Y(l,e.logger)!==void 0){c.destroy();return}z(l.url??"").startsWith(X)?Ur(l,c,f,t):c.destroy()}),await new Promise((l,c)=>{p.once("error",f=>{f.code==="EADDRINUSE"?c(new Error(`studio port ${String(e.port)} is already in use — pass a different port with --port, or stop the process using it`,{cause:f})):c(f)}),p.listen(e.port,r,()=>{l()})}),{close:()=>new Promise(l=>{p.close(()=>{l()})}),url:`http://${r==="0.0.0.0"||r==="::"?"localhost":r}:${String(e.port)}`}},Wr=12e4,V=250,Br=1e3,Hr="LUNORA_DEV_READY_TIMEOUT_MS",we=e=>{if(e!==void 0)return e;const r=Number(process.env[Hr]??"");return Number.isFinite(r)&&r>0?r:Wr},he=async(e,r)=>{try{const n=AbortSignal.timeout(Br);return await(await fetch(new URL("/_lunora/status",e),{redirect:"manual",signal:r===void 0?n:AbortSignal.any([r,n])})).body?.cancel(),!0}catch{return!1}},Kr=async e=>{const{cwd:r,logger:n,origin:t,signal:o}=e,i=e.probe??he,s=we(e.readyTimeoutMs),a=Date.now()+s,d=()=>o.aborted;for(;!d()&&Date.now()<a;){let u=!1;try{u=await i(t,o)}catch{}if(d()||Date.now()>=a)break;if(u)return x(r,{readyAt:new Date().toISOString()},{expectedPid:process.pid}),L(r)?.readyAt!==void 0;try{await R(V,void 0,{signal:o})}catch{}}return d()||n.warn(`server did not answer on ${t} within ${String(Math.round(s/1e3))}s — .lunora/dev.json will have no readyAt, so anything polling for readiness will keep waiting. The server itself is unaffected; raise LUNORA_DEV_READY_TIMEOUT_MS if it is just slow to boot.`),!1},Yr=1e4,Jr=100,Xr=40,P=256*1024,zr=new Set(["nuxt","sveltekit"]),Qr=["@lunora/rspack","@lunora/vite"],A=e=>{if(zr.has(le(e).framework))return"framework-worker";const r=Ae(e);return Qr.some(n=>r.has(n))?"vite":"wrangler"},Se=e=>{const r=fe(e);let n;try{const t=M(k(e,"package.json"),"utf8");n=JSON.parse(t).scripts?.dev}catch{}if(n===void 0||n.trim()===""||n.includes("lunora")){const t=N(r,"vite",["dev"]);return{args:t.args,command:t.command}}return{args:["run","dev"],command:r}},Zr=e=>{_e(Le(e.logPath),{recursive:!0});const r=ie(e.logPath,"w",384);let n;const t=me(e.command,e.args);try{n=oe(t.command,t.args,{cwd:e.cwd,detached:!0,env:e.env,shell:t.shell,stdio:["ignore",r,r],windowsHide:!0})}finally{se(r)}return n.unref(),{exited:new Promise(o=>{n.on("error",()=>{o(1)}),n.on("exit",(i,s)=>{o(i??(s?1:0))})}),pid:n.pid}},qr=e=>{try{const{size:r}=Re(e);if(r<=P)return M(e,"utf8");const n=ie(e,"r");try{const t=Buffer.alloc(P),o=Ie(n,t,0,P,r-P),i=t.toString("utf8",0,o);return i.slice(i.indexOf(`
3
+ `)+1)}finally{se(n)}}catch{return}},ye=(e,r)=>{const n=qr(e);if(n===void 0)return[];const t=n.split(`
4
+ `);return t.at(-1)===""&&t.pop(),r>0?t.slice(-r):t},Ee=(e,r)=>r?.logFile??k(e,Ne),ke=e=>{e.info(" Stop: lunora dev stop"),e.info(" Status: lunora dev status"),e.info(" Logs: lunora dev logs")},I=(e,r)=>{e.warn(`Dev server already running at ${r.url} (pid ${String(r.pid)})`),ke(e)},et=(e,r)=>{e.success(`Dev server running at ${r.url} (pid ${String(r.pid)})`),r.studioUrl!==void 0&&e.info(` Studio: ${r.studioUrl}`),ke(e)},rt=async e=>{const{child:r,cwd:n,deadline:t,pollIntervalMs:o,probe:i}=e,s=r.exited.then(a=>({exitCode:a}));for(;Date.now()<t;){const a=O(n);if(a!==void 0&&a.pid!==process.pid&&await i(a.url))return{state:a,status:"ready"};const d=await Promise.race([s,R(o)]);if(d!==void 0)return{...d,status:"exited"}}return{status:"timeout"}},tt=async e=>{const{cwd:r,logger:n}=e,t=Ee(r,void 0),o=we(e.readyTimeoutMs),i=(e.spawnDetached??Zr)({args:e.command.args,command:e.command.command,cwd:r,env:{...process.env,...e.env,[F]:"1",[ue]:t,...process.env.FORCE_COLOR===void 0?{NO_COLOR:"1"}:{},...e.json?{LUNORA_LOG_JSON:"1"}:{}},logPath:t}),s=await rt({child:i,cwd:r,deadline:Date.now()+o,pollIntervalMs:e.pollIntervalMs??V,probe:e.probe??he});if(s.status==="ready")return et(n,s.state),{code:0};if(s.status==="exited"){n.error(`dev server exited before becoming ready (exit code ${String(s.exitCode)}). Last output:`);for(const d of ye(t,Xr))n.error(` ${d}`);return y(r,i.pid??process.pid),{code:s.exitCode===0?1:s.exitCode}}i.pid!==void 0&&L(r)?.pid===process.pid&&x(r,{logFile:t,pid:i.pid});const a=i.pid===void 0?"":` (pid ${String(i.pid)})`;return n.warn(`dev server did not confirm ready within ${String(Math.round(o/1e3))}s — it may still be compiling${a}. Check \`lunora dev status\` and \`lunora dev logs\`; \`lunora dev stop\` shuts it down.`),{code:U.UNAVAILABLE}},be=e=>e.codegen!==!1&&!$e(process.env[ge]),Pe=e=>{const r={...e.remote===!0?{LUNORA_REMOTE:"1"}:{},...e.codegen===!1?{[ge]:"0"}:{}};return Object.keys(r).length>0?{env:r}:{}},nt=(e,r)=>{const n=["dev"];return e.apiSpec!==void 0&&n.push("--api-spec",e.apiSpec),e.port!==void 0&&n.push("--port",String(e.port)),e.workerPort!==void 0&&n.push("--worker-port",String(e.workerPort)),e.inspectorPort!==void 0&&n.push("--inspector-port",String(e.inspectorPort)),e.emitBindings!==void 0&&n.push("--emit-bindings",e.emitBindings),e.codegen===!1&&n.push("--no-codegen"),e.studio===!1&&n.push("--no-studio"),e.worker===!1&&n.push("--no-worker"),e.target!==void 0&&n.push("--target",e.target),r&&n.push("--remote"),n},ot=async e=>{const{cwd:r,jsonLogs:n,logger:t,options:o,remote:i}=e,s=e.run??tt,a=A(r),d=a==="wrangler"?`http://localhost:${String(o.workerPort??8787)}`:"http://localhost:5173",u=de(r,{background:!0,mode:"cli",pid:process.pid,startedAt:new Date().toISOString(),url:d});if(!u.ok)return u.existing!==void 0&&I(t,u.existing),{code:0};const g={[T]:String(process.pid)};try{return a==="vite"?await s({command:Se(r),cwd:r,env:{...Pe({...o,remote:i}).env,...g},json:n,logger:t}):await s({command:{args:[process.argv[1]??"lunora",...nt(o,i)],command:process.execPath},cwd:r,env:g,json:n,logger:t})}finally{y(r,process.pid)}},it=e=>{try{const r=$("ps",["-o","pgid=","-p",String(e)],{encoding:"utf8"}),n=Number.parseInt(r.stdout.trim(),10);return Number.isInteger(n)&&n>0?n:void 0}catch{return}},st=(e,r,n=process.platform,t=$)=>{if(n==="win32"){try{t("taskkill",["/pid",String(e.pid),"/T","/F"],{stdio:"ignore"})}catch{}return}if(e.background===!0)try{r(-(it(e.pid)??e.pid),"SIGKILL");return}catch{}try{r(e.pid,"SIGKILL")}catch{}},at=(e,r)=>(r.json?E({pid:e.pid,stopped:!1}):r.logger.error(`dev server (pid ${String(e.pid)}) survived the force-kill — record kept; retry \`lunora dev stop\` or kill the process manually.`),{code:1}),ct=async e=>{const{cwd:r,logger:n}=e,t=L(r),o=e.alive??(m=>m===t?.pid&&De(t)),i=e.signal??((m,p)=>{process.kill(m,p)}),s=e.pollIntervalMs??V,a=e.stopGraceMs??Yr,d=e.platform??process.platform,u=e.spawnSyncImpl??$;if(t===void 0||!o(t.pid))return t!==void 0&&y(r,t.pid),e.json?E({running:!1,stopped:!1}):n.info("No dev server running."),{code:0};try{i(t.pid,"SIGTERM")}catch{}const g=Date.now()+a;for(;o(t.pid)&&Date.now()<g;)await R(s);return o(t.pid)&&(st(t,i,d,u),await R(s),o(t.pid))?at(t,e):(y(r,t.pid),e.json?E({pid:t.pid,stopped:!0}):n.success(`Stopped dev server (pid ${String(t.pid)}).`),{code:0})},dt=e=>{const{cwd:r,logger:n}=e,t=O(r);if(t===void 0)return e.json?E({running:!1}):n.info("No dev server running."),{code:0};const o=e.now??Date.now,i=t.startedAt===void 0?Number.NaN:Date.parse(t.startedAt),s=Number.isFinite(i)?Math.max(0,Math.round((o()-i)/1e3)):void 0;if(e.json)return E({background:t.background===!0,logFile:t.logFile,mode:t.mode,pid:t.pid,ready:B(t),readyAt:t.readyAt,running:!0,startedAt:t.startedAt,studioUrl:t.studioUrl,uptimeSeconds:s,url:t.url}),{code:0};const a=[`pid ${String(t.pid)}`,...s===void 0?[]:[`uptime ${String(s)}s`],t.background===!0?"background":"foreground",B(t)?"ready":"starting"];return n.success(`Dev server running at ${t.url} (${a.join(", ")})`),t.studioUrl!==void 0&&n.info(` Studio: ${t.studioUrl}`),t.logFile!==void 0&&n.info(` Logs: lunora dev logs (${t.logFile})`),{code:0}},lt=e=>{const{cwd:r,logger:n}=e,t=L(r),o=Ee(r,t);if(!C(o))return n.info("No dev server logs found — output is captured when the server runs in background mode (`lunora dev --background`)."),{code:0};const i=ye(o,e.lines??Jr);return i.length>0&&process.stdout.write(`${i.join(`
5
5
  `)}
6
- `),{code:0}},lt=e=>{const{cwd:r,json:n,lines:t,logger:o,subcommand:i}=e;if(i==="stop")return at({cwd:r,json:n,logger:o});if(i==="status")return ct({cwd:r,json:n,logger:o});if(i==="logs")return dt({cwd:r,lines:t,logger:o});if(i!==void 0)return o.error(`dev: unknown subcommand "${i}" — expected stop | status | logs (or no subcommand to start the dev server)`),{code:U.USAGE}},D="wrangler.dev.jsonc",q=6173,S=8787,ee=5173,ut=5e3,ft=(e,r)=>{const n=()=>{};if(!e.remote)return{args:[],plan:{bindings:[],cleanup:n,enabled:!1}};const o=(e.materializeRemote??rr)({enabled:!0,projectRoot:r}),i=o.remoteBindings.map(a=>`${a.binding} (${a.kind})`),{cleanup:s}=o;return o.configPath===void 0?{args:[],plan:{bindings:i,cleanup:s,enabled:!0,reason:o.reason}}:{args:["--config",o.configPath],plan:{bindings:i,cleanup:s,enabled:!0}}},re=e=>G(e).parsed?.dev?.ip,te=(e,r,n)=>{if(n!==void 0){const o=k(e,n);if(C(o)&&re(o)!==void 0)return[]}const t=j(e);return t!==void 0&&re(t)!==void 0?[]:r()?[]:["--ip","127.0.0.1"]},gt=new Set(["127.0.0.1","::1","[::1]","localhost"]),vt=(e,r)=>{let n;try{n=M(k(e,_),"utf8")}catch{return[]}const t=[];for(const o of Ze(n)){let i;try{i=new URL(o.value)}catch{continue}gt.has(i.hostname)&&i.port===String(r)&&t.push(o.key)}return t},mt=async(e,r)=>{if(e.workerPort!==void 0)return e.workerPort;const n=j(r);if(n!==void 0){const{parsed:i}=G(n);if(typeof i?.dev?.port=="number")return i.dev.port}const t=await(e.findFreePort??jr)(S);if(t===S)return t;const o=vt(r,S);if(o.length>0)throw new Error(`port ${String(S)} is in use, but ${_} pins the worker origin to it (${o.join(", ")}). Serving on ${String(t)} would leave those URLs pointing at nothing. Stop whatever holds ${String(S)}, or run \`lunora dev --worker-port ${String(t)}\` and update those values to match.`);return t},pt=(e,r)=>{if(e.inspectorPort!==void 0)return e.inspectorPort;const n=j(r);if(n===void 0)return;const{parsed:t}=G(n),o=t?.dev?.inspector_port;return typeof o=="number"?o:void 0},wt=e=>{const r=e.cwd??process.cwd(),n=fe(r),t=e.flavor??O(r);if(t==="vite"||t==="framework-worker"){const m=Se(r);let p;if(t==="framework-worker"){const l=te(r,e.hasIpv6Loopback??J,D),c=Ye(Je(r)).toolchain?.dev({configPath:D,extraArgs:[...l,"--var","WORKER_ENV:development"]}),f=$(n,c?.tool??"wrangler",c?.args??[]);p={args:f.args,command:f.command,cwd:r,tag:"worker"}}return e.worker===!1&&e.logger.warn(`--no-worker does not apply to the ${t} flavor: Vite owns the worker, codegen and studio in-process. Run your framework's dev script instead.`),e.inspectorPort!==void 0&&e.logger.warn(t==="framework-worker"?`--inspector-port does not apply to the ${t} flavor: pin \`dev.inspector_port\` in ${D} — that is the config the worker sidecar runs.`:`--inspector-port does not apply to the ${t} flavor: Vite owns the worker. Pin it in vite.config — \`lunora({ cloudflare: { inspectorPort: ${String(e.inspectorPort)} } })\`.`),{runsCodegenWatch:!1,flavor:t,ipv4LoopbackForced:!1,remote:{bindings:[],cleanup:()=>{},enabled:e.remote===!0},...p?{sidecar:p}:{},studioEnabled:!1,studioPort:e.port??q,workerEnabled:!0,workerOrigin:`http://localhost:${String(ee)}`,workerPort:ee,wrangler:{args:m.args,command:m.command,cwd:r,...Pe(e),tag:"vite"}}}const o=le(r),i=o.framework==="none"?void 0:`this project uses ${o.framework} — the worker runs inside Vite there. run \`${Xe(n,"dev")}\` for the full app (frontend + HMR); \`lunora dev\` starts only the worker.`,s=e.workerPort??S,a=ft(e,r),d=te(r,e.hasIpv6Loopback??J),u=e.inspectorPort===void 0?[]:["--inspector-port",String(e.inspectorPort)],g=$(n,"wrangler",["dev","--port",String(s),...u,...d,"--var","WORKER_ENV:development",...a.args]);return{runsCodegenWatch:be(e),flavor:t,frameworkHint:i,ipv4LoopbackForced:d.length>0,remote:a.plan,studioEnabled:e.studio!==!1,studioPort:e.port??q,workerEnabled:e.worker!==!1,workerOrigin:`http://localhost:${String(s)}`,workerPort:s,wrangler:{args:g.args,command:g.command,cwd:r,tag:"wrangler"}}},ne=(e,r,n,t)=>{if(e.length===0)return;const o=qe(e);if(o){t[o.level](`[lunora] ${o.text}`);return}const i=`[${r}] ${e}`;n==="stderr"?t.warn(i):t.info(i)},ht=(e,r,n)=>{const t=(o,i)=>{if(!o)return;let s="";o.on("data",a=>{s+=a.toString("utf8");const d=s.split(`
7
- `);s=d.pop()??"";for(const u of d)ne(u.trimEnd(),r,i,n)}),o.on("end",()=>{ne(s.trimEnd(),r,i,n),s=""})};t(e.stdout,"stdout"),t(e.stderr,"stderr")},St=(e,r)=>{const n=me(e.command,e.args),t=oe(n.command,n.args,{cwd:e.cwd??process.cwd(),env:e.env?{...process.env,...e.env}:process.env,shell:n.shell,stdio:["inherit","pipe","pipe"]});return ht(t,e.tag,r),{exited:new Promise(o=>{t.on("error",i=>{r.error(`[${e.tag}] failed to start: ${i.message}`),o(1)}),t.on("exit",(i,s)=>{o(i??(s?1:0))})}),kill:o=>{try{t.kill(o)}catch{}}}},yt=(e,r,n,t)=>{e.info(""),e.success("Lunora dev"),e.info(` ➜ Worker: ${r.workerOrigin}`),n!==void 0&&e.info(` ➜ Studio: ${n}`),r.runsCodegenWatch&&e.info(" ➜ Codegen: watching lunora/"),t!==void 0&&e.info(` ➜ Supervisor: ${t} (needs) · ${ve} (status)`),r.remote.enabled&&(r.remote.bindings.length>0?e.info(` ➜ Remote: ${r.remote.bindings.join(", ")} → deployed worker`):e.warn(` ➜ Remote: requested but inactive (${r.remote.reason??"no eligible bindings"}) — running fully local`)),e.info("")},Et=(e,r)=>{ce(r).installed||!He()||(e.info(` ⓘ ${Ke}`),e.info(""))},kt=(e,r)=>{if(process.env.LUNORA_CONTAINER_LOGS==="0")return;const t=ze(e,"lunora").containers.map(o=>({className:o.className,exportName:o.exportName}));if(t.length!==0)return Qe({containers:t,onLine:o=>{const i=`[container:${o.name}] ${o.text}`;o.level==="error"?r.warn(i):r.info(i)},onUnavailable:o=>{r.warn(`[container] Docker engine unreachable — container logs unavailable (${o})`)}})},bt=async e=>{e.readyProbe?.abort(),await e.codegen?.close().catch(()=>{}),e.containerLogs?.close(),await e.studio?.close().catch(()=>{});try{e.remoteCleanup?.()}catch{}},Pt=async(e,r)=>{try{const t=await xe({projectRoot:r}),o=Fe(t),i=(e.ensureExample??je)(r,o);i.length>0&&e.logger.info(`Updated .dev.vars.example with secrets for: ${o.join(", ")} (${i.join(", ")})`)}catch{}(await(e.ensureEnv??Ge)({confirm:Rr(),cwd:r,info:t=>{e.logger.info(t)}})).status==="declined"&&!Ue()&&e.logger.info(`hint: ${_} was not scaffolded (non-interactive run). Copy ${Ve} → ${_} and fill in secrets, or run \`lunora dev\` in an interactive terminal to scaffold automatically.`);try{(e.fillSecrets??We)({cwd:r,info:t=>{e.logger.info(t)}})}catch{}},Tt=(e,r,n,t,o)=>{if(e.flavor!=="wrangler")return;t!==void 0&&x(r,{studioUrl:t});let i;try{i=kt(r,n)}catch{}return yt(n,e,t,o),i},_t=(e,r)=>{const n=Number(process.env[T]),t=de(r,{background:process.env[F]==="1",logFile:process.env[ue],mode:"cli",pid:process.pid,startedAt:new Date().toISOString(),url:e.workerOrigin},Number.isInteger(n)&&n>0?{supersedePid:n}:void 0);return t.ok?void 0:t.existing},It=e=>{const{cwd:r,destination:n,logger:t,plan:o}=e,i=n!==void 0,s=n??Be,a=or({destination:s,dev:{...o.flavor==="wrangler"?{origin:o.workerOrigin}:{},statusFile:ve},logger:i?t:{...t,success:()=>{},warn:()=>{}},projectRoot:r});return a.error!==void 0?i?a:(t.debug?.(`skipped the default binding manifest: ${a.error}`),{}):{written:s}},Lt=async e=>{const r=e.cwd??process.cwd(),n=e.flavor??O(r),t=n==="wrangler"?await mt(e,r):e.workerPort,o=n==="wrangler"?pt(e,r):e.inspectorPort;return wt({...e,cwd:r,flavor:n,inspectorPort:o,workerPort:t})},Rt=async e=>await new Promise(r=>{const n={},t=s=>{e.info(`received ${s} — shutting down`),n.sigint&&process.off("SIGINT",n.sigint),n.sigterm&&process.off("SIGTERM",n.sigterm),r(0)},o=()=>{t("SIGINT")},i=()=>{t("SIGTERM")};n.sigint=o,n.sigterm=i,process.on("SIGINT",o),process.on("SIGTERM",i)}),At=async(e,r,n)=>{let t=0,o;const i=u=>{e.kill(u),r?.kill(u)},s=()=>{t+=1,t===1?(n.info("received SIGINT — shutting down (press Ctrl-C again to force-kill)"),i("SIGTERM"),o=setTimeout(()=>{i("SIGKILL")},ut),o.unref()):i("SIGKILL")},a=()=>{i("SIGTERM")};process.on("SIGINT",s),process.on("SIGTERM",a);const d=await Promise.race([e.exited.then(u=>({code:u,who:"worker"})),...r===void 0?[]:[r.exited.then(u=>({code:u,who:"sidecar"}))]]);return r!==void 0&&(d.who==="sidecar"?(n.warn("[worker] the Lunora sidecar (wrangler dev) exited — shutting down the framework dev server"),e.kill("SIGTERM")):r.kill("SIGTERM"),await Promise.allSettled([e.exited,r.exited])),o&&clearTimeout(o),process.off("SIGINT",s),process.off("SIGTERM",a),d.code},Ot=async(e,r,n,t)=>{if(r.studioEnabled)try{return await(e.startStudio??Vr)({apiSpec:e.apiSpec,cwd:n,logger:{warnOnce:o=>{t.warn(o)}},port:r.studioPort,workerOrigin:r.workerOrigin})}catch(o){t.warn(`studio server failed to start (${o instanceof Error?o.message:String(o)}) — continuing without it`);return}},Dt=e=>{const r=[e.runsCodegenWatch?"codegen watch":void 0,e.studioEnabled?"studio":void 0].filter(t=>t!==void 0);return`--no-worker: not starting wrangler. ${r.length>0?`${r.join(" + ")} running`:"nothing else to run"}; your task runner owns the worker on ${e.workerOrigin}.`},$t=(e,r,n,t,o)=>{if(!(e.sidecar===void 0||!be(r)))try{ae({apiSpec:r.apiSpec,lunoraDirectory:"lunora",projectRoot:n,target:o})}catch(i){t.warn(`codegen (pre-sidecar) failed: ${i instanceof Error?i.message:String(i)} — the framework dev server will retry`)}},Nt=async e=>{const{logger:r}=e,n=e.cwd??process.cwd(),t=e.flavor??O(n),o=nr(n,e.target);if(o.target===void 0)throw new Error(o.error??"unknown deploy target");const{target:i}=o;t==="wrangler"&&await Ar(n,r,void 0,i,void 0);const s=await Lt({...e,flavor:t}),a={remoteCleanup:s.remote.cleanup};try{const d=Number(process.env[T]),u=A(n);if(u!==void 0&&u.pid!==process.pid&&u.pid!==d)return L(r,u),{code:0,plan:s};const g=_t(s,n);if(g!==void 0)return L(r,g),{code:0,plan:s};(s.flavor==="vite"||s.flavor==="framework-worker")&&(s.wrangler.env={...s.wrangler.env,[T]:String(process.pid)}),await Pt(e,n),r.info(s.flavor==="vite"?"starting vite dev (worker + studio + codegen run inside Vite via @lunora/vite)":"starting wrangler dev + studio"),s.ipv4LoopbackForced&&r.info("no IPv6 loopback (::1) on this host — binding the worker to 127.0.0.1 (--ip) so wrangler dev doesn't crash. Pin `dev.ip` in wrangler.jsonc to override."),s.runsCodegenWatch&&(a.codegen=(e.startCodegen??Cr)({apiSpec:e.apiSpec,jsonLogs:e.jsonLogs,logger:r,projectRoot:n,target:i})),a.studio=await Ot(e,s,n,r);const m=It({cwd:n,destination:e.emitBindings,logger:r,plan:s});if(m.error!==void 0)return r.error(m.error),{code:U.USAGE,plan:s};await a.codegen?.ready;const p=a.studio?.url;s.frameworkHint!==void 0&&r.warn(s.frameworkHint);const l=()=>{s.flavor==="wrangler"&&(a.readyProbe=new AbortController,Kr({cwd:n,logger:r,origin:s.workerOrigin,probe:e.probeReady,signal:a.readyProbe.signal}))};if(!s.workerEnabled)return l(),r.info(Dt(s)),{code:await(e.waitForInterrupt??Rt)(r),plan:s};$t(s,e,n,r,i);const c=e.startWorker??St,f=c(s.wrangler,r),w=s.sidecar===void 0?void 0:c(s.sidecar,r);return l(),a.containerLogs=Tt(s,n,r,p,m.written),Et(r,n),{code:await At(f,w,r),plan:s}}finally{a.readyProbe?.abort(),y(n,process.pid),await bt(a)}},Ct=e=>({codegen:e.codegen===!1?!1:void 0,studio:e.studio===!1?!1:void 0,worker:e.worker===!1?!1:void 0}),sn=ar(async({argument:e,cwd:r,logger:n,options:t})=>{const o=t.json===!0,i=lt({cwd:r,json:o,lines:t.lines,logger:n,subcommand:e[0]});if(i!==void 0)return i;const s=process.env[F]==="1",a=s?void 0:Ce(),d=o||a!==void 0;d&&lr(),a!==void 0&&t.background!==!0&&n.info(`AI agent detected (${a.name} via ${a.variable}) — starting the dev server in background mode with JSON logs. Set LUNORA_AGENT_MODE=0 to opt out.`);const u=er({configPreference:Me(r),envValue:process.env.LUNORA_REMOTE,flag:t.remote});if(!s&&(t.background===!0||a!==void 0)){const g=A(r);return g!==void 0?(L(n,g),{code:0}):nt({cwd:r,jsonLogs:d,logger:n,options:t,remote:u})}return Nt({apiSpec:tr(t.apiSpec),cwd:r,emitBindings:t.emitBindings,inspectorPort:t.inspectorPort,jsonLogs:d,logger:n,port:t.port,remote:u,target:t.target,workerPort:t.workerPort,...Ct(t)})});export{St as defaultWorkerSpawner,O as detectDevFlavor,sn as execute,Ct as negatableDevFlags,wt as planDevCommand,pt as resolveInspectorPort,mt as resolveWorkerPort,Nt as runDevCommand};
6
+ `),{code:0}},ut=e=>{const{cwd:r,json:n,lines:t,logger:o,subcommand:i}=e;if(i==="stop")return ct({cwd:r,json:n,logger:o});if(i==="status")return dt({cwd:r,json:n,logger:o});if(i==="logs")return lt({cwd:r,lines:t,logger:o});if(i!==void 0)return o.error(`dev: unknown subcommand "${i}" — expected stop | status | logs (or no subcommand to start the dev server)`),{code:U.USAGE}},D="wrangler.dev.jsonc",q=6173,S=8787,ee=5173,ft=5e3,gt=(e,r)=>{const n=()=>{};if(!e.remote)return{args:[],plan:{bindings:[],cleanup:n,enabled:!1}};const o=(e.materializeRemote??rr)({enabled:!0,projectRoot:r}),i=o.remoteBindings.map(a=>`${a.binding} (${a.kind})`),{cleanup:s}=o;return o.configPath===void 0?{args:[],plan:{bindings:i,cleanup:s,enabled:!0,reason:o.reason}}:{args:["--config",o.configPath],plan:{bindings:i,cleanup:s,enabled:!0}}},re=e=>G(e).parsed?.dev?.ip,te=(e,r,n)=>{if(n!==void 0){const o=k(e,n);if(C(o)&&re(o)!==void 0)return[]}const t=j(e);return t!==void 0&&re(t)!==void 0?[]:r()?[]:["--ip","127.0.0.1"]},vt=new Set(["127.0.0.1","::1","[::1]","localhost"]),mt=(e,r)=>{let n;try{n=M(k(e,_),"utf8")}catch{return[]}const t=[];for(const o of Ze(n)){let i;try{i=new URL(o.value)}catch{continue}vt.has(i.hostname)&&i.port===String(r)&&t.push(o.key)}return t},pt=async(e,r)=>{if(e.workerPort!==void 0)return e.workerPort;const n=j(r);if(n!==void 0){const{parsed:i}=G(n);if(typeof i?.dev?.port=="number")return i.dev.port}const t=await(e.findFreePort??jr)(S);if(t===S)return t;const o=mt(r,S);if(o.length>0)throw new Error(`port ${String(S)} is in use, but ${_} pins the worker origin to it (${o.join(", ")}). Serving on ${String(t)} would leave those URLs pointing at nothing. Stop whatever holds ${String(S)}, or run \`lunora dev --worker-port ${String(t)}\` and update those values to match.`);return t},wt=(e,r)=>{if(e.inspectorPort!==void 0)return e.inspectorPort;const n=j(r);if(n===void 0)return;const{parsed:t}=G(n),o=t?.dev?.inspector_port;return typeof o=="number"?o:void 0},ht=e=>{const r=e.cwd??process.cwd(),n=fe(r),t=e.flavor??A(r);if(t==="vite"||t==="framework-worker"){const m=Se(r);let p;if(t==="framework-worker"){const l=te(r,e.hasIpv6Loopback??J,D),c=Ye(Je(r)).toolchain?.dev({configPath:D,extraArgs:[...l,"--var","WORKER_ENV:development"]}),f=N(n,c?.tool??"wrangler",c?.args??[]);p={args:f.args,command:f.command,cwd:r,tag:"worker"}}return e.worker===!1&&e.logger.warn(`--no-worker does not apply to the ${t} flavor: Vite owns the worker, codegen and studio in-process. Run your framework's dev script instead.`),e.inspectorPort!==void 0&&e.logger.warn(t==="framework-worker"?`--inspector-port does not apply to the ${t} flavor: pin \`dev.inspector_port\` in ${D} — that is the config the worker sidecar runs.`:`--inspector-port does not apply to the ${t} flavor: Vite owns the worker. Pin it in vite.config — \`lunora({ cloudflare: { inspectorPort: ${String(e.inspectorPort)} } })\`.`),{runsCodegenWatch:!1,flavor:t,ipv4LoopbackForced:!1,remote:{bindings:[],cleanup:()=>{},enabled:e.remote===!0},...p?{sidecar:p}:{},studioEnabled:!1,studioPort:e.port??q,workerEnabled:!0,workerOrigin:`http://localhost:${String(ee)}`,workerPort:ee,wrangler:{args:m.args,command:m.command,cwd:r,...Pe(e),tag:"vite"}}}const o=le(r),i=o.framework==="none"?void 0:`this project uses ${o.framework} — the worker runs inside Vite there. run \`${Xe(n,"dev")}\` for the full app (frontend + HMR); \`lunora dev\` starts only the worker.`,s=e.workerPort??S,a=gt(e,r),d=te(r,e.hasIpv6Loopback??J),u=e.inspectorPort===void 0?[]:["--inspector-port",String(e.inspectorPort)],g=N(n,"wrangler",["dev","--port",String(s),...u,...d,"--var","WORKER_ENV:development",...a.args]);return{runsCodegenWatch:be(e),flavor:t,frameworkHint:i,ipv4LoopbackForced:d.length>0,remote:a.plan,studioEnabled:e.studio!==!1,studioPort:e.port??q,workerEnabled:e.worker!==!1,workerOrigin:`http://localhost:${String(s)}`,workerPort:s,wrangler:{args:g.args,command:g.command,cwd:r,tag:"wrangler"}}},ne=(e,r,n,t)=>{if(e.length===0)return;const o=qe(e);if(o){t[o.level](`[lunora] ${o.text}`);return}const i=`[${r}] ${e}`;n==="stderr"?t.warn(i):t.info(i)},St=(e,r,n)=>{const t=(o,i)=>{if(!o)return;let s="";o.on("data",a=>{s+=a.toString("utf8");const d=s.split(`
7
+ `);s=d.pop()??"";for(const u of d)ne(u.trimEnd(),r,i,n)}),o.on("end",()=>{ne(s.trimEnd(),r,i,n),s=""})};t(e.stdout,"stdout"),t(e.stderr,"stderr")},yt=(e,r)=>{const n=me(e.command,e.args),t=oe(n.command,n.args,{cwd:e.cwd??process.cwd(),env:e.env?{...process.env,...e.env}:process.env,shell:n.shell,stdio:["inherit","pipe","pipe"]});return St(t,e.tag,r),{exited:new Promise(o=>{t.on("error",i=>{r.error(`[${e.tag}] failed to start: ${i.message}`),o(1)}),t.on("exit",(i,s)=>{o(i??(s?1:0))})}),kill:o=>{try{t.kill(o)}catch{}}}},Et=(e,r,n,t)=>{e.info(""),e.success("Lunora dev"),e.info(` ➜ Worker: ${r.workerOrigin}`),n!==void 0&&e.info(` ➜ Studio: ${n}`),r.runsCodegenWatch&&e.info(" ➜ Codegen: watching lunora/"),t!==void 0&&e.info(` ➜ Supervisor: ${t} (needs) · ${ve} (status)`),r.remote.enabled&&(r.remote.bindings.length>0?e.info(` ➜ Remote: ${r.remote.bindings.join(", ")} → deployed worker`):e.warn(` ➜ Remote: requested but inactive (${r.remote.reason??"no eligible bindings"}) — running fully local`)),e.info("")},kt=(e,r)=>{ce(r).installed||!He()||(e.info(` ⓘ ${Ke}`),e.info(""))},bt=(e,r)=>{if(process.env.LUNORA_CONTAINER_LOGS==="0")return;const t=ze(e,"lunora").containers.map(o=>({className:o.className,exportName:o.exportName}));if(t.length!==0)return Qe({containers:t,onLine:o=>{const i=`[container:${o.name}] ${o.text}`;o.level==="error"?r.warn(i):r.info(i)},onUnavailable:o=>{r.warn(`[container] Docker engine unreachable — container logs unavailable (${o})`)}})},Pt=async e=>{e.readyProbe?.abort(),await e.codegen?.close().catch(()=>{}),e.containerLogs?.close(),await e.studio?.close().catch(()=>{});try{e.remoteCleanup?.()}catch{}},Tt=async(e,r)=>{try{const t=await xe({projectRoot:r}),o=Fe(t),i=(e.ensureExample??je)(r,o);i.length>0&&e.logger.info(`Updated .dev.vars.example with secrets for: ${o.join(", ")} (${i.join(", ")})`)}catch{}(await(e.ensureEnv??Ge)({confirm:Lr(),cwd:r,info:t=>{e.logger.info(t)}})).status==="declined"&&!Ue()&&e.logger.info(`hint: ${_} was not scaffolded (non-interactive run). Copy ${Ve} → ${_} and fill in secrets, or run \`lunora dev\` in an interactive terminal to scaffold automatically.`);try{(e.fillSecrets??We)({cwd:r,info:t=>{e.logger.info(t)}})}catch{}},_t=(e,r,n,t,o)=>{if(e.flavor!=="wrangler")return;t!==void 0&&x(r,{studioUrl:t});let i;try{i=bt(r,n)}catch{}return Et(n,e,t,o),i},Rt=(e,r)=>{const n=Number(process.env[T]),t=de(r,{background:process.env[F]==="1",logFile:process.env[ue],mode:"cli",pid:process.pid,startedAt:new Date().toISOString(),url:e.workerOrigin},Number.isInteger(n)&&n>0?{supersedePid:n}:void 0);return t.ok?void 0:t.existing},It=e=>{const{cwd:r,destination:n,logger:t,plan:o}=e,i=n!==void 0,s=n??Be,a=or({destination:s,dev:{...o.flavor==="wrangler"?{origin:o.workerOrigin}:{},statusFile:ve},logger:i?t:{...t,success:()=>{},warn:()=>{}},projectRoot:r});return a.error!==void 0?i?a:(t.debug?.(`skipped the default binding manifest: ${a.error}`),{}):{written:s}},Lt=async e=>{const r=e.cwd??process.cwd(),n=e.flavor??A(r),t=n==="wrangler"?await pt(e,r):e.workerPort,o=n==="wrangler"?wt(e,r):e.inspectorPort;return ht({...e,cwd:r,flavor:n,inspectorPort:o,workerPort:t})},Ot=async e=>await new Promise(r=>{const n={},t=s=>{e.info(`received ${s} — shutting down`),n.sigint&&process.off("SIGINT",n.sigint),n.sigterm&&process.off("SIGTERM",n.sigterm),r(0)},o=()=>{t("SIGINT")},i=()=>{t("SIGTERM")};n.sigint=o,n.sigterm=i,process.on("SIGINT",o),process.on("SIGTERM",i)}),At=async(e,r,n)=>{let t=0,o;const i=u=>{e.kill(u),r?.kill(u)},s=()=>{t+=1,t===1?(n.info("received SIGINT — shutting down (press Ctrl-C again to force-kill)"),i("SIGTERM"),o=setTimeout(()=>{i("SIGKILL")},ft),o.unref()):i("SIGKILL")},a=()=>{i("SIGTERM")};process.on("SIGINT",s),process.on("SIGTERM",a);const d=await Promise.race([e.exited.then(u=>({code:u,who:"worker"})),...r===void 0?[]:[r.exited.then(u=>({code:u,who:"sidecar"}))]]);return r!==void 0&&(d.who==="sidecar"?(n.warn("[worker] the Lunora sidecar (wrangler dev) exited — shutting down the framework dev server"),e.kill("SIGTERM")):r.kill("SIGTERM"),await Promise.allSettled([e.exited,r.exited])),o&&clearTimeout(o),process.off("SIGINT",s),process.off("SIGTERM",a),d.code},Dt=async(e,r,n,t)=>{if(r.studioEnabled)try{return await(e.startStudio??Vr)({apiSpec:e.apiSpec,cwd:n,logger:{warnOnce:o=>{t.warn(o)}},port:r.studioPort,workerOrigin:r.workerOrigin})}catch(o){t.warn(`studio server failed to start (${o instanceof Error?o.message:String(o)}) — continuing without it`);return}},Nt=e=>{const r=[e.runsCodegenWatch?"codegen watch":void 0,e.studioEnabled?"studio":void 0].filter(t=>t!==void 0);return`--no-worker: not starting wrangler. ${r.length>0?`${r.join(" + ")} running`:"nothing else to run"}; your task runner owns the worker on ${e.workerOrigin}.`},$t=(e,r,n,t,o)=>{if(!(e.sidecar===void 0||!be(r)))try{ae({apiSpec:r.apiSpec,lunoraDirectory:"lunora",projectRoot:n,target:o})}catch(i){t.warn(`codegen (pre-sidecar) failed: ${i instanceof Error?i.message:String(i)} — the framework dev server will retry`)}},Ct=async e=>{const{logger:r}=e,n=e.cwd??process.cwd(),t=e.flavor??A(n),o=nr(n,e.target);if(o.target===void 0)throw new Error(o.error??"unknown deploy target");const{target:i}=o;t==="wrangler"&&await Or(n,r,void 0,i,void 0);const s=await Lt({...e,flavor:t}),a={remoteCleanup:s.remote.cleanup};try{const d=Number(process.env[T]),u=O(n);if(u!==void 0&&u.pid!==process.pid&&u.pid!==d)return I(r,u),{code:0,plan:s};const g=Rt(s,n);if(g!==void 0)return I(r,g),{code:0,plan:s};(s.flavor==="vite"||s.flavor==="framework-worker")&&(s.wrangler.env={...s.wrangler.env,[T]:String(process.pid)}),await Tt(e,n),r.info(s.flavor==="vite"?"starting vite dev (worker + studio + codegen run inside Vite via @lunora/vite)":"starting wrangler dev + studio"),s.ipv4LoopbackForced&&r.info("no IPv6 loopback (::1) on this host — binding the worker to 127.0.0.1 (--ip) so wrangler dev doesn't crash. Pin `dev.ip` in wrangler.jsonc to override."),s.runsCodegenWatch&&(a.codegen=(e.startCodegen??Cr)({apiSpec:e.apiSpec,jsonLogs:e.jsonLogs,logger:r,projectRoot:n,target:i})),a.studio=await Dt(e,s,n,r);const m=It({cwd:n,destination:e.emitBindings,logger:r,plan:s});if(m.error!==void 0)return r.error(m.error),{code:U.USAGE,plan:s};await a.codegen?.ready;const p=a.studio?.url;s.frameworkHint!==void 0&&r.warn(s.frameworkHint);const l=()=>{s.flavor==="wrangler"&&(a.readyProbe=new AbortController,Kr({cwd:n,logger:r,origin:s.workerOrigin,probe:e.probeReady,signal:a.readyProbe.signal}))};if(!s.workerEnabled)return l(),r.info(Nt(s)),{code:await(e.waitForInterrupt??Ot)(r),plan:s};$t(s,e,n,r,i);const c=e.startWorker??yt,f=c(s.wrangler,r),w=s.sidecar===void 0?void 0:c(s.sidecar,r);return l(),a.containerLogs=_t(s,n,r,p,m.written),kt(r,n),{code:await At(f,w,r),plan:s}}finally{a.readyProbe?.abort(),y(n,process.pid),await Pt(a)}},Mt=e=>({codegen:e.codegen===!1?!1:void 0,studio:e.studio===!1?!1:void 0,worker:e.worker===!1?!1:void 0}),an=ar(async({argument:e,cwd:r,logger:n,options:t})=>{const o=t.json===!0,i=ut({cwd:r,json:o,lines:t.lines,logger:n,subcommand:e[0]});if(i!==void 0)return i;const s=process.env[F]==="1",a=s?void 0:Ce(),d=o||a!==void 0;d&&lr(),a!==void 0&&t.background!==!0&&n.info(`AI agent detected (${a.name} via ${a.variable}) — starting the dev server in background mode with JSON logs. Set LUNORA_AGENT_MODE=0 to opt out.`);const u=er({configPreference:Me(r),envValue:process.env.LUNORA_REMOTE,flag:t.remote});if(!s&&(t.background===!0||a!==void 0)){const g=O(r);return g!==void 0?(I(n,g),{code:0}):ot({cwd:r,jsonLogs:d,logger:n,options:t,remote:u})}return Ct({apiSpec:tr(t.apiSpec),cwd:r,emitBindings:t.emitBindings,inspectorPort:t.inspectorPort,jsonLogs:d,logger:n,port:t.port,remote:u,target:t.target,workerPort:t.workerPort,...Mt(t)})});export{yt as defaultWorkerSpawner,A as detectDevFlavor,an as execute,Mt as negatableDevFlags,ht as planDevCommand,wt as resolveInspectorPort,pt as resolveWorkerPort,Ct as runDevCommand};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/cli",
3
- "version": "1.0.0-alpha.320",
3
+ "version": "1.0.0-alpha.322",
4
4
  "description": "The Lunora CLI: init, dev, deploy, codegen, migrate, seed, doctor, insights, logs, registry, and the rest of the project commands",
5
5
  "keywords": [
6
6
  "agent-skills",
@@ -52,16 +52,16 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "@bomb.sh/tab": "0.0.22",
55
- "@lunora/advisor": "1.0.0-alpha.168",
56
- "@lunora/bindings": "1.0.0-alpha.81",
57
- "@lunora/codegen": "1.0.0-alpha.234",
58
- "@lunora/config": "1.0.0-alpha.278",
55
+ "@lunora/advisor": "1.0.0-alpha.169",
56
+ "@lunora/bindings": "1.0.0-alpha.82",
57
+ "@lunora/codegen": "1.0.0-alpha.236",
58
+ "@lunora/config": "1.0.0-alpha.280",
59
59
  "@lunora/container": "1.0.0-alpha.63",
60
- "@lunora/d1": "1.0.0-alpha.148",
60
+ "@lunora/d1": "1.0.0-alpha.149",
61
61
  "@lunora/errors": "1.0.0-alpha.47",
62
- "@lunora/mcp": "1.0.0-alpha.193",
63
- "@lunora/runtime": "1.0.0-alpha.160",
64
- "@lunora/seed": "1.0.0-alpha.166",
62
+ "@lunora/mcp": "1.0.0-alpha.195",
63
+ "@lunora/runtime": "1.0.0-alpha.161",
64
+ "@lunora/seed": "1.0.0-alpha.167",
65
65
  "@visulima/cerebro": "3.1.1",
66
66
  "@visulima/error": "6.1.0",
67
67
  "@visulima/fs": "6.0.7",
@@ -11,7 +11,9 @@ and exposes idiomatic Lunora functions for browser uploads, gated downloads,
11
11
  delete, and list — with no bucket credential in the client.
12
12
 
13
13
  A worker-signed URL points at **your Worker**, not at R2:
14
- `${STORAGE_PUBLIC_BASE_URL}/<key>?exp&method&bucket&sig`. The `/storage/*` route
14
+ `<base>/<key>?exp&method&bucket&sig`, where the base is the origin the request
15
+ reached your Worker on (`ctx.origin`) unless `STORAGE_PUBLIC_BASE_URL` overrides
16
+ it. The `/storage/*` route
15
17
  you add in step 4 is what verifies the signature and moves the bytes, for both
16
18
  the upload and the download. (The no-Worker-in-the-path variant is
17
19
  `@lunora/storage`'s S3 presigned URL, `getPresignedUrl` — it needs S3 credentials
@@ -51,21 +53,21 @@ This:
51
53
  2. Adds an R2 bucket binding to `wrangler.jsonc` (`r2_buckets`, binding
52
54
  **`UPLOADS`**, `bucket_name: "replace-me-uploads"` — rename it to a real
53
55
  bucket). It **merges** into any existing `r2_buckets`.
54
- 3. Scaffolds `STORAGE_SIGNING_SECRET` (a secret) and `STORAGE_PUBLIC_BASE_URL`
55
- into `.dev.vars`.
56
+ 3. Scaffolds `STORAGE_SIGNING_SECRET` (a secret) and an empty, optional
57
+ `STORAGE_PUBLIC_BASE_URL` into `.dev.vars`.
56
58
  4. Copies `lunora/storage/index.ts` (the `generateUploadUrl` /
57
59
  `getDownloadUrl` / `deleteObject` / `listObjects` functions) into your
58
60
  project — it is **yours** to edit.
59
61
 
60
62
  ## Step 2: Configure the binding + secrets
61
63
 
62
- | Name | Where | Notes |
63
- | ------------------------- | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
64
- | `UPLOADS` | `wrangler.jsonc` → `r2_buckets[]` | The R2 bucket binding. Point `bucket_name` at a real bucket. |
65
- | `STORAGE_SIGNING_SECRET` | secret (`.dev.vars` / `secret put`) | HMAC secret for signed URLs. Min 32 chars, enforced — a shorter one throws on the first call. Never share across tenants. |
66
- | `STORAGE_PUBLIC_BASE_URL` | var (`.dev.vars` / `wrangler.jsonc`) | **Bare origin** running the `/storage/*` route — your app's own origin (locally the dev server URL, e.g. `http://localhost:5173` under Vite). Scaffolded empty, since the host and port are signed into every URL. A base carrying a path is rejected by the signer. |
64
+ | Name | Where | Notes |
65
+ | ------------------------- | -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
66
+ | `UPLOADS` | `wrangler.jsonc` → `r2_buckets[]` | The R2 bucket binding. Point `bucket_name` at a real bucket. |
67
+ | `STORAGE_SIGNING_SECRET` | secret (`.dev.vars` / `secret put`) | HMAC secret for signed URLs. Min 32 chars, enforced — a shorter one throws on the first call. Never share across tenants. |
68
+ | `STORAGE_PUBLIC_BASE_URL` | optional var (`.dev.vars` / Worker variable) | Leave it **empty** and URLs are signed against the origin the request reached your Worker on (`ctx.origin`), right in dev, previews and production alike. Set a **bare origin** only when another host (a CDN) serves `/storage/*`, or to sign where no request is behind the call: a query, a scheduled job, a workflow step have no `ctx.origin`, so signing there without it throws. A base carrying a path is rejected by the signer. |
67
69
 
68
- `STORAGE_PUBLIC_BASE_URL` must be `https://` anywhere but local dev. A signed URL
70
+ A configured `STORAGE_PUBLIC_BASE_URL` must be `https://` anywhere but local dev. A signed URL
69
71
  _is_ a bearer credential and the object bytes stream through it, so a plaintext
70
72
  origin hands both to anyone on the path. Only `http://localhost` /
71
73
  `http://127.0.0.1` belong in `.dev.vars`.
@@ -309,7 +311,8 @@ bare key back in — the component re-scopes it.
309
311
 
310
312
  - [ ] `lunora registry add storage` run, `pnpm install` done.
311
313
  - [ ] `UPLOADS` bucket bound to a real bucket; `STORAGE_SIGNING_SECRET` (≥32
312
- chars) and `STORAGE_PUBLIC_BASE_URL` (a bare origin) set.
314
+ chars) set; `STORAGE_PUBLIC_BASE_URL` left empty unless another host
315
+ serves `/storage/*` or you sign URLs in a query or a scheduled job.
313
316
  - [ ] `lunora codegen` run so `api.storage.*` is generated.
314
317
  - [ ] `/storage/*` route added, verifying signed URLs on both `PUT` and `GET`.
315
318
  - [ ] Verified a client upload → signed download round-trip.