@lunora/cli 1.0.0-alpha.487 → 1.0.0-alpha.489

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.
@@ -1,4 +1,4 @@
1
- import{mkdirSync as Re,writeFileSync as X,existsSync as b,readFileSync as E,readdirSync as K,statSync as Te,rmSync as oe,mkdtempSync as pt,realpathSync as vt}from"node:fs";import{dirname as R,join as p,resolve as _,relative as L,sep as x,extname as yt}from"node:path";import{PROJECT_CONFIG_FILENAMES as wt,isTestPath as we,discoverMigrations as kt,readServiceBindings as bt,readBoundServiceBindings as yr,runCodegen as $t}from"@lunora/codegen";import{readLinkedProject as Ce,discoverSchemaInfo as Pe,resolveSchemaDirectory as O,startDevProcess as St,acceptsConnection as Et,readApiSpecMarker as wr,resolveTargetOrThrow as ke,detectFramework as At,DEFAULT_BUNDLE_META_PATH as Rt,STRICT_ADVISORIES_ENV as Tt,LAZY_FUNCTIONS_ENV as Ct,BUNDLE_META_PATH_ENV as Pt,BUILD_TARGET_ENV as _t,BUILD_ROOT_ENV as It,canonicalPath as jt,COMPOSED_WORKER_ENTRY as Ve,discoverContainerInfo as kr,D1_PLACEHOLDER_ID as Ke,writeLinkedProject as Dt,DEV_VARS_FILE as N,parseDevVariableEntries as br,secretSourcesFromBindings as Ot,inferLunoraBindings as $r,requiredSecrets as Lt,mintableSecretKeysFor as Nt,generateSecretValue as xt,upsertDevVariableLine as Ft,writeDevVariablesFileAtomically as Mt,isPlaceholderValue as Ut,ensureGitignoreEntries as Bt,resolveLazyFunctions as de,resolveDeployDriver as le,MissingBuildOutputError as Wt,clearApiSpecMarker as Ht,createDiscoveryCache as qt,planToolchainInvocation as Sr,probeCelldToolchain as Gt,runPostCodegenHook as Vt}from"@lunora/config";import{findWranglerFile as D,readWranglerJsonc as I,HYPERDRIVE_PLACEHOLDER_ID as Er,applyModify as Ar,mergeWranglerEnvironment as ue,bindingsNeedingDatabase as Kt,warnCloudflareCliConfigOnce as zt,validateWranglerProject as Jt,wranglerCodegenInputs as Yt,reconcileWranglerBindings as Xt,reconcileWranglerCompatibilityDate as Qt,reconcileWranglerCrons as Zt,describePreservedCrons as en}from"@lunora/config/cloudflare";import{Spinner as rn}from"@visulima/spinner";import{r as tn}from"../packem_shared/admin-token-VdUnvnKW.mjs";import{n as nn,r as on,a as sn}from"../packem_shared/admin-url-DJB8P6Jh.mjs";import{r as _e,a as an,e as cn}from"../packem_shared/platform-diagnostics-Cn2g6Jh-.mjs";import{p as dn}from"../packem_shared/api-spec-lYyq4vL6.mjs";import{e as ln,m as un,g as fn,i as Rr,d as gn,b as hn}from"../packem_shared/vectorize-metadata-DK61NmVx.mjs";import{w as mn}from"../packem_shared/binding-manifest-file-CQ1eQYy1.mjs";import{d as pn}from"../packem_shared/command-BpMv2n0o.mjs";import{r as Ie}from"../packem_shared/wrangler-name-Dsk5K1f-.mjs";import{a as vn}from"../packem_shared/deploy-target-resolve-BTCW1Iv4.mjs";import{EXIT_CODE as y}from"../packem_shared/EXIT_CODE-DQVhSE-1.mjs";import{spawn as Tr,spawnSync as yn}from"node:child_process";import{createHash as wn,randomUUID as Cr}from"node:crypto";import{setTimeout as kn}from"node:timers/promises";import{stripVTControlCharacters as bn}from"node:util";import{toolchainExecArgs as z,detectPackageManager as F,execArgsFor as je}from"@lunora/config/cli";import{ts as w,Project as Pr,SyntaxKind as k,Node as S}from"ts-morph";import{r as _r,c as Ir}from"../packem_shared/cloudflare-credentials-BVmZnDZk.mjs";import{f as $n}from"../packem_shared/free-port-DjbFhaaT.mjs";import{l as V}from"../packem_shared/js-literal-FWLi94Lh.mjs";import{containerBuildTag as Sn}from"@lunora/container";import{a as En,i as An}from"../packem_shared/docker-DpVxvYpL.mjs";import{defaultSpawner as q}from"../packem_shared/createRecordingSpawner-DchLi0gR.mjs";import{a as De}from"../packem_shared/resolve-target-BRvw8Pf7.mjs";import{p as Rn,H as Tn,a as Cn,r as Pn,c as _n,b as In}from"../packem_shared/health-probe-By9T5x2y.mjs";import{runAdvisor as jn,openShardAccess as ze}from"@lunora/advisor";import{join as A,relative as J,dirname as jr}from"@visulima/path";import{runMigrateDataCommand as Dn}from"../packem_shared/runMigrateGenerateCommand-CguLEIZp.mjs";import{c as On}from"../packem_shared/tui-prompts-B3YwUhGw.mjs";import{l as Ln}from"../packem_shared/wrangler-secrets-C9siMBwN.mjs";const Nn=Object.freeze({edges:[],nodes:[],modules:[],unresolved:[],version:1}),xn=20,Je=e=>`${e.from} -${e.kind}-> ${e.to}`,ee=(e,r)=>{const t=new Set(e);return r.filter(n=>!t.has(n)).toSorted((n,o)=>n.localeCompare(o))},Fn=(e,r)=>{const t=e.modules.map(i=>i.name),n=r.modules.map(i=>i.name),o=e.edges.map(i=>Je(i)),s=r.edges.map(i=>Je(i));return{addedEdges:ee(o,s),addedModules:ee(t,n),removedEdges:ee(s,o),removedModules:ee(n,t)}},re=(e,r,t)=>{if(t.length===0)return[];const n=t.slice(0,xn).map(s=>` ${e} ${s}`),o=t.length-n.length;return[`${r}:`,...n,...o>0?[` … and ${o.toString()} more`]:[]]},Mn=e=>{const r=[...re("+","modules added",e.addedModules),...re("-","modules removed",e.removedModules),...re("+","edges added",e.addedEdges),...re("-","edges removed",e.removedEdges)];return r.length===0?void 0:`architecture changes since the last deploy:
1
+ import{mkdirSync as Re,writeFileSync as X,existsSync as b,readFileSync as E,readdirSync as K,statSync as Te,rmSync as oe,mkdtempSync as pt,realpathSync as vt}from"node:fs";import{dirname as R,join as p,resolve as _,relative as L,sep as x,extname as yt}from"node:path";import{PROJECT_CONFIG_FILENAMES as wt,isTestPath as we,discoverMigrations as kt,readServiceBindings as bt,readBoundServiceBindings as yr,runCodegen as $t}from"@lunora/codegen";import{readLinkedProject as Ce,discoverSchemaInfo as Pe,resolveSchemaDirectory as O,startDevProcess as St,acceptsConnection as Et,readApiSpecMarker as wr,resolveTargetOrThrow as ke,detectFramework as At,DEFAULT_BUNDLE_META_PATH as Rt,STRICT_ADVISORIES_ENV as Tt,LAZY_FUNCTIONS_ENV as Ct,BUNDLE_META_PATH_ENV as Pt,BUILD_TARGET_ENV as _t,BUILD_ROOT_ENV as It,canonicalPath as jt,COMPOSED_WORKER_ENTRY as Ve,discoverContainerInfo as kr,D1_PLACEHOLDER_ID as Ke,writeLinkedProject as Dt,DEV_VARS_FILE as N,parseDevVariableEntries as br,secretSourcesFromBindings as Ot,inferLunoraBindings as $r,requiredSecrets as Lt,mintableSecretKeysFor as Nt,generateSecretValue as xt,upsertDevVariableLine as Ft,writeDevVariablesFileAtomically as Mt,isPlaceholderValue as Ut,ensureGitignoreEntries as Bt,resolveLazyFunctions as de,resolveDeployDriver as le,MissingBuildOutputError as Wt,clearApiSpecMarker as Ht,createDiscoveryCache as qt,planToolchainInvocation as Sr,probeCelldToolchain as Gt,runPostCodegenHook as Vt}from"@lunora/config";import{findWranglerFile as D,readWranglerJsonc as I,HYPERDRIVE_PLACEHOLDER_ID as Er,applyModify as Ar,mergeWranglerEnvironment as ue,bindingsNeedingDatabase as Kt,warnCloudflareCliConfigOnce as zt,validateWranglerProject as Jt,wranglerCodegenInputs as Yt,reconcileWranglerBindings as Xt,reconcileWranglerCompatibilityDate as Qt,reconcileWranglerCrons as Zt,describePreservedCrons as en}from"@lunora/config/cloudflare";import{Spinner as rn}from"@visulima/spinner";import{r as tn}from"../packem_shared/admin-token-VdUnvnKW.mjs";import{n as nn,r as on,a as sn}from"../packem_shared/admin-url-DJB8P6Jh.mjs";import{r as _e,a as an,e as cn}from"../packem_shared/platform-diagnostics-Cn2g6Jh-.mjs";import{p as dn}from"../packem_shared/api-spec-lYyq4vL6.mjs";import{e as ln,m as un,g as fn,i as Rr,d as gn,b as hn}from"../packem_shared/vectorize-metadata-DK61NmVx.mjs";import{w as mn}from"../packem_shared/binding-manifest-file-BZyQFSTF.mjs";import{d as pn}from"../packem_shared/command-BpMv2n0o.mjs";import{r as Ie}from"../packem_shared/wrangler-name-Dsk5K1f-.mjs";import{a as vn}from"../packem_shared/deploy-target-resolve-BTCW1Iv4.mjs";import{EXIT_CODE as y}from"../packem_shared/EXIT_CODE-DQVhSE-1.mjs";import{spawn as Tr,spawnSync as yn}from"node:child_process";import{createHash as wn,randomUUID as Cr}from"node:crypto";import{setTimeout as kn}from"node:timers/promises";import{stripVTControlCharacters as bn}from"node:util";import{toolchainExecArgs as z,detectPackageManager as F,execArgsFor as je}from"@lunora/config/cli";import{ts as w,Project as Pr,SyntaxKind as k,Node as S}from"ts-morph";import{r as _r,c as Ir}from"../packem_shared/cloudflare-credentials-BVmZnDZk.mjs";import{f as $n}from"../packem_shared/free-port-DjbFhaaT.mjs";import{l as V}from"../packem_shared/js-literal-FWLi94Lh.mjs";import{containerBuildTag as Sn}from"@lunora/container";import{a as En,i as An}from"../packem_shared/docker-DpVxvYpL.mjs";import{defaultSpawner as q}from"../packem_shared/createRecordingSpawner-DchLi0gR.mjs";import{a as De}from"../packem_shared/resolve-target-BRvw8Pf7.mjs";import{p as Rn,H as Tn,a as Cn,r as Pn,c as _n,b as In}from"../packem_shared/health-probe-By9T5x2y.mjs";import{runAdvisor as jn,openShardAccess as ze}from"@lunora/advisor";import{join as A,relative as J,dirname as jr}from"@visulima/path";import{runMigrateDataCommand as Dn}from"../packem_shared/runMigrateGenerateCommand-DHdSMKbu.mjs";import{c as On}from"../packem_shared/tui-prompts-B3YwUhGw.mjs";import{l as Ln}from"../packem_shared/wrangler-secrets-C9siMBwN.mjs";const Nn=Object.freeze({edges:[],nodes:[],modules:[],unresolved:[],version:1}),xn=20,Je=e=>`${e.from} -${e.kind}-> ${e.to}`,ee=(e,r)=>{const t=new Set(e);return r.filter(n=>!t.has(n)).toSorted((n,o)=>n.localeCompare(o))},Fn=(e,r)=>{const t=e.modules.map(i=>i.name),n=r.modules.map(i=>i.name),o=e.edges.map(i=>Je(i)),s=r.edges.map(i=>Je(i));return{addedEdges:ee(o,s),addedModules:ee(t,n),removedEdges:ee(s,o),removedModules:ee(n,t)}},re=(e,r,t)=>{if(t.length===0)return[];const n=t.slice(0,xn).map(s=>` ${e} ${s}`),o=t.length-n.length;return[`${r}:`,...n,...o>0?[` … and ${o.toString()} more`]:[]]},Mn=e=>{const r=[...re("+","modules added",e.addedModules),...re("-","modules removed",e.removedModules),...re("+","edges added",e.addedEdges),...re("-","edges removed",e.removedEdges)];return r.length===0?void 0:`architecture changes since the last deploy:
2
2
  ${r.join(`
3
3
  `)}`},Un=(e,r)=>p(e,".lunora",r===void 0?"architecture.json":`architecture.${r}.json`),Bn=e=>{if(b(e))try{const r=JSON.parse(E(e,"utf8"));return r.version===1&&Array.isArray(r.modules)&&Array.isArray(r.edges)?r:void 0}catch{return}},Wn=e=>{const{cwd:r,environment:t,logger:n}=e,o=Un(r,t),s=Bn(o),i=e.current??(s===void 0?void 0:Nn);if(i===void 0)return;const a=s===void 0?void 0:Mn(Fn(s,i));return a!==void 0&&n.info(a),()=>{try{Re(R(o),{recursive:!0}),X(o,`${JSON.stringify(i,void 0,2)}
4
4
  `,"utf8")}catch(c){n.warn(`could not record the deployed architecture at ${o}: ${c instanceof Error?c.message:String(c)}`)}}},Hn=e=>{const{cwd:r,env:t,logger:n,mintedSecretsFile:o}=e;try{const s=Ce(r),i=s?.workerName??Ie(r),a=e.url??s?.workerUrl;n.success("deploy complete"),n.info(` worker: ${i??"(see wrangler output above)"}`),t!==void 0&&n.info(` env: ${t}`),a===void 0?n.info(" url: run `lunora link --url <https://your-worker>` to record it"):n.info(` url: ${a}`),o!==void 0&&n.info(` secrets: generated value(s) recorded in ${o}`),e.logsAvailable&&n.info(" logs: lunora logs")}catch{}},qn=/\/index$/u,Gn=/[^\dA-Za-z]/gu,Vn=e=>e.replace(qn,"").split("/").map(r=>r.replaceAll(Gn,"_")),Kn=e=>Vn(e).join("_"),zn=/[^a-z0-9-]/gu,Jn=/-$/u,Ye=7,Yn=(e,r)=>{const t=e.toLowerCase(),n=t.replaceAll(zn,"-");if(n===t&&n.length<=r)return n;const o=wn("sha256").update(t).digest("hex").slice(0,Ye-1);return`${n.slice(0,r-Ye).replace(Jn,"")}-${o}`},Xn=32,Dr=15e3,Qn=5432,Zn=3306,eo=/[^A-Za-z0-9]/gu,Y=e=>`LUNORA_${e.replaceAll(eo,"_").toUpperCase()}_URL`,ro=e=>{const r=new Map;for(const t of e){const n=Y(t),o=r.get(n);if(o!==void 0)return{bindings:[o,t],variable:n};r.set(n,t)}},to=(e,r)=>Yn(`${e}-${r}`,Xn),Xe=e=>e==="postgresql"?"postgres":e==="mariadb"?"mysql":e,no=e=>e==="mysql"?Zn:Qn,oo=e=>{try{const r=new URL(e),t=r.protocol.slice(0,-1),n=decodeURIComponent(r.pathname.slice(1));return!["mariadb","mysql","postgres","postgresql"].includes(t)||r.hostname===""||n===""?void 0:{database:n,host:r.hostname,password:decodeURIComponent(r.password),port:r.port===""?no(Xe(t)):Number(r.port),scheme:Xe(t),user:decodeURIComponent(r.username)}}catch{return}},so=(e,r)=>{const t=typeof r?.password=="string"?r.password:"",n=t===""?[]:[t,JSON.stringify(t).slice(1,-1),encodeURIComponent(t)];let o=e;for(const s of n)o=o.replaceAll(s,"<redacted>");return o.split(" ").map(s=>s.includes("://")?"<url>":s).join(" ")},Or=e=>(Array.isArray(e)?e:[]).flatMap((r,t)=>{const{binding:n,id:o}=typeof r=="object"&&r!==null?r:{};return typeof n=="string"&&o===Er?[{binding:n,index:t}]:[]}),se=e=>{let r="";try{const{errors:t}=JSON.parse(e.text);r=(t??[]).flatMap(n=>typeof n.message=="string"?[n.message]:[]).slice(0,2).join("; ")}catch{}return r===""?`HTTP ${String(e.status)}`:`HTTP ${String(e.status)}: ${r}`},io=e=>typeof e=="string"||typeof e=="number"?e.toString():void 0,ao=["scheme","host","port","database","user"],co=e=>e==="postgresql"?"postgres":e,Qe=(e,r)=>{const t=io(r);return e==="scheme"?co(t):t},Ze=50,lo=async(e,r)=>{for(let t=1;t<=Ze;t+=1){const n=await e(t===1?"/hyperdrive/configs":`/hyperdrive/configs?page=${String(t)}`);if(!n.ok)throw new Error(`listing Hyperdrive configs failed (${se(n)})`);if(!Array.isArray(n.body.result))throw new TypeError("the listing of Hyperdrive configs had no result list");const o=n.body.result.find(i=>i.name===r),s=typeof n.body.result_info=="object"&&n.body.result_info!==null?n.body.result_info:{};if(o!==void 0||typeof s.total_pages!="number"||t>=s.total_pages)return o}throw new Error(`the listing of Hyperdrive configs has more than ${String(Ze)} pages, so "${r}" can't be shown to be absent`)},uo=async(e,r,t,n)=>{const o=n?{caching:{disabled:!0}}:{},s=await lo(e,r);if(typeof s?.id=="string"){const c=ao.filter(d=>Qe(d,s.origin?.[d])!==Qe(d,t[d]));if(c.length>0)throw new Error(`a Hyperdrive config named "${r}" already exists for a different origin, or one that can't be confirmed (${c.join(", ")}). Delete it, or rename the Worker, so the binding does not reach the wrong database.`);const l=await e(`/hyperdrive/configs/${encodeURIComponent(s.id)}`,{body:JSON.stringify({origin:t,...o}),method:"PATCH"});if(!l.ok)throw new Error(`updating its credentials failed (${se(l)})`);return{id:s.id,reused:!0}}const i=await e("/hyperdrive/configs",{body:JSON.stringify({name:r,origin:t,...o}),method:"POST"});if(!i.ok)throw new Error(`creating it failed (${se(i)})`);const{id:a}=typeof i.body.result=="object"&&i.body.result!==null?i.body.result:{};if(typeof a!="string")throw new TypeError("the response carried no id");return{id:a,reused:!1}},fo=(e,r,t,n)=>r===void 0&&n?`${Y(e)} is set but is not a usable connection string for the "${e}" Hyperdrive binding: it needs a postgres://, postgresql:// or mysql:// scheme, a host and a database name, with any special characters percent-encoded.`:r===void 0?`the "${e}" Hyperdrive binding has no config yet. Set ${Y(e)} to its postgres:// or mysql:// connection string and Lunora creates the config, or run \`wrangler hyperdrive create\` and put its id in wrangler.jsonc.`:t?void 0:`creating the "${e}" Hyperdrive config needs CLOUDFLARE_API_TOKEN (Hyperdrive: Edit) and CLOUDFLARE_ACCOUNT_ID, or run \`wrangler hyperdrive create\` and put its id in wrangler.jsonc.`,go=async(e,r)=>{const t=to(e.workerName,e.binding),{id:n,reused:o}=await uo(async(s,i)=>Ir({accountId:e.accountId,apiToken:e.token,...e.fetch===void 0?{}:{fetch:e.fetch},init:{...i,signal:AbortSignal.timeout(Dr)},path:s}),t,e.origin,e.disableCaching);return e.logger.success(`${o?"reusing":"created"} Hyperdrive config "${t}" for ${e.binding} → ${e.wranglerPath}`),{id:n,text:Ar(r,[...e.path,e.index,"id"],n)}},ho=e=>e===void 0||e===""?void 0:e,Oe=e=>{const r=p(R(e),".wrangler","deploy","config.json");try{const{configPath:t}=JSON.parse(E(r,"utf8"));return typeof t=="string"?_(R(r),t):void 0}catch{return}},mo=(e,r,t,n,o)=>{const s=Oe(e);if(s===void 0||s===e)return;if(n.built!==n.deployed){o.warn(`the build output ${s} was made for ${n.built===void 0?"the default environment":`"${n.built}"`}, not ${n.deployed===void 0?"the default environment":`"${n.deployed}"`}, so the "${r}" Hyperdrive id was not written there. Build with CLOUDFLARE_ENV set to the environment you deploy, then deploy again.`);return}let i;try{i=I(s)}catch{return}const a=Or(i.parsed?.hyperdrive).find(c=>c.binding===r);a!==void 0&&X(s,Ar(i.text,["hyperdrive",a.index,"id"],t),"utf8")},po=(e,r)=>{const t=typeof e.name=="string"&&e.name!==""?e.name:"lunora";if(r===void 0)return t;const n=e.env?.[r]?.name;return typeof n=="string"&&n!==""?n:`${t}-${r}`},fe=(e,r,t)=>{const n=t??D(e),o=n===void 0?void 0:I(n).parsed;if(n===void 0||o===void 0)return;const s=r===void 0?void 0:o.env?.[r],i=Or(r===void 0?o.hyperdrive:s?.hyperdrive);return i.length===0?void 0:{accountId:s?.account_id??o.account_id,entries:i,path:r===void 0?["hyperdrive"]:["env",r,"hyperdrive"],workerName:po(o,r),wranglerPath:n}},vo=(e,r,t)=>{const n=ro(e.map(({binding:o})=>o));if(n!==void 0){const o=`deploy blocked: the Hyperdrive bindings ${n.bindings.map(s=>`"${s}"`).join(" and ")} both read ${n.variable} and would share one config. Rename one so their names differ in letters or digits, not only in punctuation.`;return t.logger.error(o),o}for(const{binding:o,origin:s,supplied:i}of e){const a=fo(o,s,r,i);if(a!==void 0&&t.dryRun===!0)t.logger.warn(`dry run: ${a} A real deploy stops here.`);else if(a!==void 0){const c=`deploy blocked: ${a}`;return t.logger.error(c),c}}},yo=async(e,r,t,n)=>{let o;try{o=await e(`/hyperdrive/configs/${encodeURIComponent(t)}`)}catch(i){n.warn(`could not check whether the "${r}" Hyperdrive config has query caching off (${i instanceof Error?i.message:String(i)}).`);return}if(!o.ok){n.warn(`could not check whether the "${r}" Hyperdrive config has query caching off (${se(o)}).`);return}const{caching:s}=typeof o.body.result=="object"&&o.body.result!==null?o.body.result:{};s?.disabled!==!0&&n.warn(`the "${r}" Hyperdrive config has query caching on, so .global() tables would read rows up to 75 s old. Run \`wrangler hyperdrive update ${t} --caching-disabled\`.`)},wo=async(e,r)=>{const t=e.wranglerPath??D(e.cwd),n=t===void 0?void 0:I(t).parsed,o=e.wranglerEnvironment===void 0?n:n?.env?.[e.wranglerEnvironment],{accountId:s,token:i}=_r(r,o?.account_id??n?.account_id);if(s===void 0||i===void 0)return;const a=async(c,l)=>Ir({accountId:s,apiToken:i,...e.fetch===void 0?{}:{fetch:e.fetch},init:{...l,signal:AbortSignal.timeout(Dr)},path:c});for(const c of Array.isArray(o?.hyperdrive)?o.hyperdrive:[]){const{binding:l,id:d}=typeof c=="object"&&c!==null?c:{};typeof l=="string"&&typeof d=="string"&&d!==Er&&await yo(a,l,d,e.logger)}},ko=async e=>{const r=e.environment??process.env;e.disableCaching===!0&&e.checkOnly!==!0&&e.dryRun!==!0&&await wo(e,r);const t=fe(e.cwd,e.wranglerEnvironment,e.wranglerPath);if(t===void 0)return;const{entries:n,path:o,wranglerPath:s}=t,{accountId:i,token:a}=_r(r,t.accountId),c=n.map(({binding:d})=>{const u=r[Y(d)]??"";return{binding:d,origin:oo(u),supplied:u!==""}}),l=vo(c,a!==void 0&&i!==void 0,e);if(l!==void 0)return l;if(!(e.dryRun===!0||e.checkOnly===!0))for(const[d,{binding:u,index:f}]of n.entries()){const g=c[d]?.origin;if(!(g===void 0||a===void 0||i===void 0))try{const{id:h,text:v}=await go({accountId:i,binding:u,disableCaching:e.disableCaching===!0,fetch:e.fetch,index:f,logger:e.logger,origin:g,path:o,token:a,workerName:t.workerName,wranglerPath:s},E(s,"utf8"));X(s,v,"utf8"),mo(s,u,h,{built:ho(r.CLOUDFLARE_ENV),deployed:e.wranglerEnvironment},e.logger)}catch(h){const v=h instanceof Error?h.message:String(h),m=`deploy blocked: the "${u}" Hyperdrive config could not be set up: ${so(v,c[d]?.origin)}`;return e.logger.error(m),m}}},bo=(e,r)=>{const t=fe(e,r.env)?.entries[0]?.binding;if(t===void 0)return;const n=`prepare blocked: the "${t}" Hyperdrive binding still has its placeholder id. Run \`lunora deploy\` with ${Y(t)} set to create the config, or put the id from \`wrangler hyperdrive create\` in wrangler.jsonc.`;return r.logger.error(n),n},$o=(e,r)=>{const t=fe(e,r.env)?.entries[0]?.binding;if(t===void 0)return;const n=`deploy blocked: the "${t}" Hyperdrive binding has no config, and a --temporary deploy cannot create one: it would be made in your account while the Worker goes to a temporary one, which a temporary account has no database for. Deploy without --temporary.`;return r.logger.error(n),n},H=async(e,r,t,n,o="create",s)=>{if(n!=="cloudflare")return;if(t==="prepare")return bo(e,r);if(r.temporary===!0)return $o(e,r);const i=r.clientBuildEnvironment===void 0?void 0:{...process.env,CLOUDFLARE_ENV:r.clientBuildEnvironment},a=Pe(e,O(e),r.discoveryCache);return a.error!==void 0&&r.logger.warn(`could not read the schema (${a.error}), so Hyperdrive configs are set up with query caching off.`),ko({checkOnly:o==="check",cwd:e,disableCaching:a.error!==void 0||a.info?.hasHyperdriveGlobalTable===!0,dryRun:r.dryRun,environment:i,logger:r.logger,wranglerEnvironment:r.env,wranglerPath:s})},So=(e,r,t)=>fe(e,r,t)!==void 0,pe=[".ts",".tsx",".mts",".js",".mjs"],Eo=/^[a-z]+:/u,er=/\.[cm]?js$/u,Ao=e=>!e.startsWith(".")&&!e.startsWith("/")&&!e.startsWith("#")&&!Eo.test(e),Ro=e=>e.split("/").slice(0,e.startsWith("@")?2:1).join("/"),To=e=>e.split(x).some(r=>r==="_generated"||r==="node_modules"),Co=e=>e.split(x).includes("node_modules"),rr=(e,r)=>{const t=_(R(e),r),n=er.test(t)?t.replace(er,""):void 0;return[t,...n===void 0?[]:pe.map(s=>`${n}${s}`),...pe.map(s=>`${t}${s}`),...pe.map(s=>p(t,`index${s}`))].find(s=>b(s)&&Te(s).isFile())},Po=/^lunora\.config\.[cm]?[jt]s$/u,be=e=>{if(typeof e=="string")return e;if(typeof e=="object"&&e!==null)for(const[r,t]of Object.entries(e)){const n=r==="types"?void 0:be(t);if(n!==void 0)return n}},_o=(e,r)=>{let t;try{t=JSON.parse(E(p(e,"package.json"),"utf8")).imports}catch{return}const n=be(t?.[r]);if(n!==void 0)return n;for(const[o,s]of Object.entries(t??{})){const[i="",a=""]=o.split("*"),c=be(s);if(c!==void 0&&o.includes("*")&&r.startsWith(i)&&r.endsWith(a)&&r.length>=o.length-1){const l=r.slice(i.length,r.length-a.length);return c.replaceAll("*",()=>l)}}},Io=(e,r)=>{const t=p(e,r,"_generated"),n=K(e).find(o=>Po.test(o));return[...b(t)?K(t).filter(o=>o.endsWith(".ts")).map(o=>p(t,o)):[],...n===void 0?[]:[p(e,n)]]},jo=e=>{const r=w.findConfigFile(e,n=>w.sys.fileExists(n));if(r===void 0)return!1;const t=w.readConfigFile(r,n=>w.sys.readFile(n));return w.parseJsonConfigFileContent(t.config??{},w.sys,R(r)).options.verbatimModuleSyntax===!0},tr=e=>e!==void 0&&(w.isNamedImports(e)||w.isNamedExports(e))&&e.elements.length>0&&e.elements.every(r=>r.isTypeOnly),Do=(e,r)=>{if(w.isImportDeclaration(e)){const{importClause:t}=e;return!(t?.phaseModifier===w.SyntaxKind.TypeKeyword||!r&&t?.name===void 0&&tr(t?.namedBindings))&&w.isStringLiteral(e.moduleSpecifier)?e.moduleSpecifier.text:void 0}if(w.isExportDeclaration(e))return!(e.isTypeOnly||!r&&tr(e.exportClause))&&e.moduleSpecifier!==void 0&&w.isStringLiteral(e.moduleSpecifier)?e.moduleSpecifier.text:void 0},Oo=e=>{if(!w.isCallExpression(e)||e.expression.kind!==w.SyntaxKind.ImportKeyword)return;const[r]=e.arguments;return r!==void 0&&w.isStringLiteralLike(r)?r.text:void 0},Lo=(e,r)=>{const t=w.createSourceFile(e,E(e,"utf8"),w.ScriptTarget.Latest,!1),n=[],o=[],s=i=>{const a=Oo(i),c=a??Do(i,r);c?.startsWith(".")||c?.startsWith("#")?o.push(c):a!==void 0&&Ao(a)&&n.push(a),w.forEachChild(i,s)};return s(t),{dynamicPackages:n,relative:o}},No=(e,r)=>{for(let t=e;;t=R(t)){if(b(p(t,"node_modules",r,"package.json")))return!0;if(R(t)===t)return!1}},xo=(e,r,t)=>{const n=jo(e),o=new Set,s=new Set,i=r!==void 0&&b(_(e,r))?[_(e,r)]:Io(e,t);for(let a=i.shift();a!==void 0;a=i.shift())if(!o.has(a)&&!Co(L(e,a))){o.add(a);const{dynamicPackages:c,relative:l}=Lo(a,n);if(!To(L(e,a)))for(const d of c)s.add(d);i.push(...l.flatMap(d=>{if(!d.startsWith("#"))return rr(a,d)??[];const u=_o(e,d);return u===void 0?[]:rr(p(e,"package.json"),u)??[]}))}return[...s].filter(a=>No(e,Ro(a))).toSorted((a,c)=>a.localeCompare(c))},Fo=18787,nr=3,$e=12e4,Lr=5e3,W=10,Nr=new Set([".js",".mjs",".mts",".ts",".tsx"]),Mo=/\(void 0\)|is not a function|before initialization|of undefined/u,Uo=e=>typeof e=="object"&&e!==null&&typeof e.functionPath=="string"&&(e.deferred===void 0||["agent","module","queue","workflow"].includes(e.deferred))&&typeof e.message=="string",Bo=e=>{const r=O(e),t=new Map;let n;try{n=K(p(e,r),{recursive:!0,encoding:"utf8"})}catch{return t}for(const o of n){const s=yt(o);Nr.has(s)&&!o.split(x).some(i=>i==="_generated"||i==="node_modules")&&t.set(Kn(o.slice(0,-s.length).split(x).join("/")),p(r,o))}return t},or=e=>Mo.test(e)?`
@@ -1 +1 @@
1
- import{existsSync as b,readFileSync as L,realpathSync as A}from"node:fs";import{join as h}from"node:path";import{readProjectTarget as u,DEFAULT_TARGET as f,readServiceBindings as B}from"@lunora/codegen";import{DEV_VARS_FILE as m,parseDevVariableEntries as C,isPlaceholderValue as O,readProjectDependencies as M,discoverSchemaInfo as G,inferLunoraBindings as V,resolveSchemaDirectory as K,probeCelldToolchain as q,D1_PLACEHOLDER_ID as W}from"@lunora/config";import{GLOBAL_FETCH_STRICTLY_PUBLIC_FLAG as g,resolveObservabilitySampling as y,findWranglerFile as T,readWranglerJsonc as D,buildBindingManifest as H,validateWranglerProject as z,UNEXPORTED_CLASS_MARKER as x,detectCloudflareCliConfig as J,collectExportGaps as Y}from"@lunora/config/cloudflare";import{a as Q,i as X,m as Z,c as ee,b as te,d as ne,e as oe,f as re}from"../packem_shared/vectorize-metadata-DK61NmVx.mjs";import{r as se,d as ae}from"../packem_shared/admin-token-VdUnvnKW.mjs";import{r as ie,n as w,c as ce}from"../packem_shared/cloudflare-credentials-BVmZnDZk.mjs";import{d as le}from"../packem_shared/command-BpMv2n0o.mjs";import{i as de}from"../packem_shared/path-containment-CgxYZggb.mjs";import{A as $,a as R,b as k}from"../packem_shared/variables-CPYyJtsL.mjs";const ue=(o,t)=>{const e=new Set,n=s=>{for(const[a,c]of Object.entries(s??{}))typeof c=="string"&&c.trim().length>0&&e.add(a)};n(o.vars);for(const s of Object.values(o.env??{}))n(s.vars);const r=h(t,m);if(b(r))try{n(Object.fromEntries(C(L(r,"utf8")).map(s=>[s.key,s.value])))}catch{}return e},fe=(o,t,e,n)=>{if(o===void 0||!e)return;const r=ue(o,t),s=r.has($),a=o.ai?.binding;!s&&(typeof a!="string"||a.length===0)&&n.push({code:"ai-binding-missing",fix:`Run \`lunora dev\` (it adds the binding), add \`"ai": { "binding": "AI" }\` to wrangler.jsonc, or set ${$} to an OpenAI-compatible proxy.`,level:"warn",message:"the project uses ctx.ai (@lunora/ai / env.AI) but wrangler.jsonc declares no `ai` binding — model calls will fail at runtime."}),r.has(R)&&n.push({code:"ai-gateway-token-unused",fix:"Make the gateway unauthenticated for ctx.ai, or remove the token. A bring-your-own AI SDK provider built with `resolveAiGateway` does send it.",level:"warn",message:`${R} is set, but ctx.ai's Workers AI binding cannot send a gateway token — it is ignored on that path.`}),!s&&!r.has(k)&&n.push({code:"ai-gateway-default",fix:`Run \`lunora cloudflare ai-gateway\` to create a gateway for this app and write ${k} into wrangler vars.`,level:"info",message:`no ${k} in vars or ${m} — \`<provider>/<model>\` slugs route through the account's \`default\` AI Gateway.`})},pe=(o,t,e)=>{if(o===void 0||!t)return;const n=[];(o.compatibility_flags??[]).includes(g)||n.push("the top level");for(const[r,s]of Object.entries(o.env??{}))s.compatibility_flags!==void 0&&!s.compatibility_flags.includes(g)&&n.push(`env.${r}`);n.length>0&&e.push({code:"cimd-fetch-not-strictly-public",fix:`Add "${g}" to "compatibility_flags" in wrangler.jsonc.`,level:"warn",message:`@lunora/auth/cimd/workers is imported, but ${n.join(", ")} of wrangler.jsonc lacks the ${g} compatibility flag — workersCimdFetch() refuses to build without it.`})},N=5e3,he="https://api.cloudflare.com/client/v4/user/tokens/verify",U=o=>{if(typeof o!="object"||o===null||!("result"in o)||"success"in o&&o.success===!1)return;const{result:t}=o;return typeof t=="object"&&t!==null&&"status"in t&&typeof t.status=="string"?t.status:void 0},ge=async(o,t)=>{const e=await t(he,{headers:{Authorization:`Bearer ${o}`},signal:AbortSignal.timeout(N)}),n=await e.json().catch(()=>{});return{reported:e.ok?U(n):void 0,status:e.status}},me=async(o,t,e)=>{const n=await ce({accountId:o,apiToken:t,fetch:e,init:{signal:AbortSignal.timeout(N)},path:"/tokens/verify"});return n.ok?{reported:U(n.body),status:200}:{reported:void 0,status:n.status}},ve=async(o,t,e)=>{const n=await me(o,t,e);return n.reported!==void 0?n.reported==="active"?{verdict:"active"}:{verdict:"rejected"}:n.status===401||n.status===403?{verdict:"rejected"}:{reason:`HTTP ${String(n.status)}`,verdict:"unchecked"}},be=async(o,t,e)=>{const n=await ge(o,e);if(n.reported!==void 0)return n.reported==="active"?{verdict:"active"}:{verdict:"rejected"};if(n.status!==401&&n.status!==403)return{reason:`HTTP ${String(n.status)}`,verdict:"unchecked"};if(t.length===0)return{reason:"the user endpoint refused it, and no account id is set to check an account-owned token",verdict:"unchecked"};const r=await Promise.all(t.map(async s=>await ve(s,o,e).catch(a=>({reason:a instanceof Error?a.message:String(a),verdict:"unchecked"}))));return r.find(s=>s.verdict==="active")??r.find(s=>s.verdict==="unchecked")??{verdict:"rejected"}},we=o=>{const t=[o?.account_id,...Object.values(o?.env??{}).map(e=>e?.account_id)];return[...new Set(t.map(e=>w(e)).filter(e=>e!==void 0))]},ke=async(o,t,e)=>{const n=t.environment??process.env,r=we(o),{accountId:s,token:a}=ie(n,r[0]),c=w(n.CLOUDFLARE_ACCOUNT_ID)===void 0?r:[s].filter(d=>d!==void 0);if(a===void 0){e.push({code:"cloudflare-token-unchecked",fix:"Set CLOUDFLARE_API_TOKEN to verify it.",level:"info",message:"the Cloudflare API token was not verified: CLOUDFLARE_API_TOKEN is not set."});return}const i=t.fetch??globalThis.fetch.bind(globalThis);let l;try{l=await be(a,c,i)}catch(d){l={reason:d instanceof Error?d.message:String(d),verdict:"unchecked"}}l.verdict==="active"?e.push({code:"cloudflare-token-ok",level:"pass",message:"the Cloudflare API token is active."}):l.verdict==="rejected"?e.push({code:"cloudflare-token-rejected",fix:"Create a token at https://dash.cloudflare.com/profile/api-tokens with the Workers Scripts Edit permission, then set CLOUDFLARE_API_TOKEN to it.",level:"fail",message:"the Cloudflare API token is not active, so deploys with it will fail."}):e.push({code:"cloudflare-token-unchecked",level:"info",message:`the Cloudflare API token could not be verified (${l.reason}).`})},S=o=>{const{logs:t,traces:e}=y(o);return Object.entries({logs:t,traces:e}).filter(([,n])=>n.enabled&&n.headSamplingRate>=1).map(([n])=>n)},ye=(o,t)=>{if(o===void 0)return;const e=[],n=S(o.observability);n.length>0&&e.push(`${n.join(" + ")} (top level)`);for(const[r,s]of Object.entries(o.env??{})){const a=s?.observability;if(a===void 0)continue;const c=S(a);c.length>0&&e.push(`${c.join(" + ")} (env.${r})`)}e.length!==0&&t.push({code:"observability-full-sampling",fix:"For a high-traffic Worker, lower head_sampling_rate in wrangler.jsonc: under observability for logs, under observability.traces for traces — e.g. 0.1 keeps 10% of requests. Lunora never changes a rate you set.",level:"info",message:`observability samples every request (head_sampling_rate 1) for ${e.join(", ")}. From 2026-12-01 Workers Observability is billed: Free caps ingestion at 0.5 GB/day; Paid includes 50 GB per billing cycle, then $0.25/GB.`})},v=o=>o.enabled&&o.headSamplingRate>0,j=o=>{if(!o||o.redact_query_string===!0)return!1;const{logs:t,traces:e}=y(o);return v(t)||v(e)},E=o=>{const{logs:t,traces:e}=y(o);return[v(t)&&"request logs",v(e)&&"traces"].filter(Boolean).join(" and ")},_e=(o,t)=>{if(o===void 0)return;const e=[],n=new Set;j(o.observability)&&(e.push("top level"),n.add(E(o.observability)));for(const[r,s]of Object.entries(o.env??{})){const a=s?.observability;j(a)&&(e.push(`env.${r}`),n.add(E(a)))}e.length!==0&&t.push({code:"observability-query-unredacted",fix:'Set "redact_query_string": true in each observability block listed above in wrangler.jsonc. Lunora never changes a value you set.',level:"warn",message:`${[...n].join(" / ")} keep query strings (redact_query_string is not true) for ${e.join(", ")}. Emailed auth links carry their token in the query, so that token is stored with the request URL.`})},P={ai:"Workers AI",browser:"Browser Rendering",images:"Cloudflare Images (Image Transformations)"},I=["Argo Smart Routing","Log Explorer"],Ae=o=>{const t=[["top level",o],...Object.entries(o.env??{}).flatMap(([n,r])=>r?[[`env.${n}`,r]]:[])],e=new Map;for(const[n,r]of t)for(const s of H(r).bindings)P[s.type]!==void 0&&e.set(s.type,[...e.get(s.type)??[],n]);return e},xe=(o,t)=>{if((u(o)??f)!=="cloudflare")return;const e=T(o),n=e===void 0?void 0:D(e).parsed;if(n!==void 0)for(const[r,s]of Ae(n)){const a=P[r]??r;t.push({code:"paid-product-bound",fix:"Create an account budget alert in the dashboard: Manage Account > Billing > Billable Usage > Create budget alert. Lunora cannot create or read one.",level:"info",message:`${a} is bound (${s.join(", ")}) and billed per use. No documented usage-alert product covers it, and lunora cannot read budget alerts, so an account budget alert is the only cap.`})}t.push({code:"paid-product-unknown",level:"info",message:`enablement of ${I.join(" and ")} cannot be read by lunora (dashboard settings); check the dashboard if your account uses ${I.length>1?"them":"it"}.`})},$e=(o,t)=>{for(const e of B(o).services)for(const n of e.publicScopes){const r=n===""?"":` (env.${n})`;t.push({code:"service-workers-dev",fix:`If ${e.name} has no public route (custom domain or route, including ones set outside ${e.wranglerPath}), set \`"workers_dev": false\`${r}. Keep its internal auth (HMAC) either way.`,level:"warn",message:`service ${e.name} (${e.worker}) has no route in its wrangler config and still serves on workers.dev${r}; a custom domain set elsewhere would also be public.`})}},F=o=>{const t=T(o);if(t===void 0)return{parsed:void 0,path:void 0};const{parsed:e}=D(t);return{parsed:e,path:t}},Re=(o,t,e,n)=>{if(e===void 0){n.push({code:"wrangler-missing",fix:"Run `lunora init` (or `lunora dev`) to scaffold and reconcile wrangler.jsonc.",level:"fail",message:"wrangler.jsonc not found."});return}if(t===void 0){n.push({code:"wrangler-unparseable",fix:`Check ${e} is valid JSONC.`,level:"fail",message:`Could not parse ${e}.`});return}const{report:r}=z({projectRoot:o});for(const c of r.errors.filter(i=>i.includes(x)))n.push({code:"wrangler-class-unexported",level:"fail",message:c});for(const c of r.warnings.filter(i=>i.includes("LUNORA_ORIGIN_URL")))n.push({code:"scheduler-origin-missing",fix:"Set LUNORA_ORIGIN_URL in wrangler vars to the worker's public URL (or `wrangler secret put LUNORA_ORIGIN_URL`).",level:"warn",message:c});const s=c=>c.includes(x)||c.includes("SHARD");for(const c of r.errors.filter(i=>!s(i)))n.push({code:"wrangler-invalid",fix:"Fix the reported wrangler.jsonc problem; `lunora verify` reports the same set.",level:"fail",message:c});for(const c of r.warnings.filter(i=>!i.includes("LUNORA_ORIGIN_URL")))n.push({code:"wrangler-advisory",level:"warn",message:c});const a=r.errors.find(c=>c.includes("SHARD"));a===void 0?n.push({code:"wrangler-shard-binding-ok",level:"pass",message:"wrangler.jsonc present with a SHARD durable-object binding."}):n.push({code:"wrangler-shard-binding-missing",fix:"Run `lunora dev` to auto-reconcile, or add the binding manually.",level:"fail",message:a})},Se=(o,t)=>{b(h(o,"lunora.json"))&&t.push({code:"stale-lunora-json",fix:'Move `target` / `remote` into `lunora.config.ts` (`export default { target: "…" }`), then delete lunora.json.',level:"warn",message:"lunora.json is present but no longer read — lunora.config.* replaced it, so any `target` or `remote` in it is being ignored."})},je=(o,t)=>{const e=J(o);e!==void 0&&t.push({code:"cf-config-present",fix:e.fix,level:"warn",message:e.message})},Ee=(o,t)=>{o===void 0||o.limits?.cpu_ms!==void 0||t.push({code:"cpu-limit-missing",fix:'Add `"limits": { "cpu_ms": 30000 }` to wrangler.jsonc, tuned to your slowest legitimate handler.',level:"info",message:"no limits.cpu_ms is set — a runaway handler can burn CPU up to the platform default before anything stops it."})},Ie=(o,t)=>{const e=(o?.r2_buckets??[]).map(n=>n?.bucket_name).filter(n=>typeof n=="string"&&n.length>0);e.length!==0&&t.push({code:"r2-lifecycle-unset",fix:"wrangler r2 bucket lifecycle list <bucket> — add an abort-incomplete-multipart rule if none exists.",level:"info",message:`R2 bucket(s) ${e.join(", ")}: verify a lifecycle rule aborts incomplete multipart uploads — orphaned parts are billed but invisible in a listing.`})},Le=(o,t)=>{const{error:e,info:n}=G(o,"lunora");if(e!==void 0){t.push({code:"schema-unreadable",fix:"Fix the parse error, then re-run `lunora doctor`. `lunora codegen` reports the same failure with the offending source.",level:"fail",message:`lunora/schema.ts could not be parsed, so every schema-derived check was skipped: ${e}`});return}for(const r of n?.vectorMetadata??[]){const s=Z(r.kind);if(s===void 0){t.push({code:"vector-metadata-unfilterable",fix:"Filter on a string, number or boolean column, or drop it from `metadata`.",level:"warn",message:`vector index "${r.index}" declares metadata "${r.property}", whose type Vectorize cannot filter on.`});continue}t.push({code:"vector-metadata-index-required",fix:`wrangler ${ee({index:r.index,property:r.property,type:s}).join(" ")}`,level:"info",message:`vector index "${r.index}" filters on metadata "${r.property}" — that needs a Vectorize metadata index (\`lunora deploy\` creates it).`})}},Ce=o=>{const t=o?.d1_databases;return Array.isArray(t)?t.filter(Boolean):[]},Oe=(o,t,e)=>{const n=Ce(o),r=[];for(const s of n){const a=typeof s.database_id=="string"?s.database_id:"";if(e?oe(a)&&a!==W:re(a)){const i=typeof s.binding=="string"&&s.binding.length>0?s.binding:"<unnamed>";r.push({code:"d1-placeholder-id",fix:e?"Set database_id to the database celld should use.":"Run `lunora doctor --fix` to create the database and write its id into wrangler.jsonc, or run `wrangler d1 create <name>` and paste the returned database_id in yourself.",level:"fail",message:`D1 binding "${i}"${t} has a placeholder database_id ("${a||"<empty>"}").`})}}return r},Te=(o,t,e)=>{if(t===void 0)return;const n=(u(o)??f)==="celld",r=[{config:t,suffix:""},...Object.entries(n?{}:t.env??{}).map(([s,a])=>({config:a,suffix:` in env.${s}`}))];for(const{config:s,suffix:a}of r)e.push(...Oe(s,a,n))},De=(o,t)=>{if(o===void 0)return;const e=(o.send_email??[]).filter(Boolean);for(const n of e){const r=typeof n.destination_address=="string"?n.destination_address:"";if(r!==""&&O(r)){const s=typeof n.name=="string"&&n.name.length>0?n.name:"send_email";t.push({code:"email-destination-placeholder",fix:"Set destination_address to a verified Cloudflare Email Routing address.",level:"warn",message:`send_email binding "${s}" has a placeholder destination_address ("${r}").`})}}},Ne=(o,t)=>{const e=h(o,m);if(!b(e))return;let n;try{n=L(e,"utf8")}catch{return}const r=C(n).filter(s=>X(s.key)&&O(s.value)).map(s=>s.key);r.length>0&&t.push({code:"dev-vars-missing-secret",fix:"Run `lunora dev` to auto-generate secrets, or fill them in by hand.",level:"warn",message:`${m} has unfilled secret value(s): ${r.join(", ")}.`})},Ue=(o,t)=>{const{source:e}=se({cwd:o});e===void 0?t.push({code:"admin-token-missing",fix:"Set LUNORA_ADMIN_TOKEN (env or `.dev.vars`) to enable admin RPCs / studio.",level:"info",message:"LUNORA_ADMIN_TOKEN is not set."}):t.push({code:"admin-token-set",level:"pass",message:`LUNORA_ADMIN_TOKEN is set (${ae(e)}).`})},Pe=async(o,t)=>{try{return await V({projectRoot:o,schemaDir:K(o)})}catch(e){t.push({code:"declared-export-unchecked",fix:"Make sure the worker entry resolves (see `lunora codegen`), then re-run.",level:"warn",message:`could not check whether declared containers/workflows/agents are re-exported by the worker entry: ${e instanceof Error?e.message:String(e)}`});return}},Fe=(o,t)=>{const e=Y(o),n=[...o.containers.map(r=>({...r,kind:"container"})),...o.workflows.map(r=>({...r,kind:"workflow"})),...o.agents.map(r=>({...r,kind:"agent"}))];for(const r of n.filter(s=>s.exported))t.push({code:"declared-export-ok",level:"pass",message:`${r.kind} "${r.exportName}" is exported by the worker entry.`});for(const r of e)t.push({code:"declared-export-missing",fix:`Add \`export * from "./lunora/_generated/${r.module}"\` to your worker entry (or re-run \`vis generate lunora-${r.kind}\`).`,level:"fail",message:`${r.kind} "${r.exportName}" is declared but ${r.className} is not exported by the worker entry.`})},Be=/^[\^~><= ]+/u,Me=/^(?<core>\d+\.\d+\.\d+)(?:-(?<channel>[a-z]+)\.(?<counter>\d+))?$/u,Ge=o=>{const t=o.replace(Be,"").trim(),e=Me.exec(t);if(!e?.groups)return;const{channel:n,core:r,counter:s}=e.groups;return{channel:n??"",core:r??"",counter:s===void 0?void 0:Number.parseInt(s,10),raw:t}},Ve=(o,t)=>{const e=M(o),n=[];for(const[i,l]of Object.entries(e)){if(i!=="lunorash"&&!i.startsWith("@lunora/"))continue;const d=Ge(l);d&&n.push({name:i,version:d})}if(n.length<2)return;const r=i=>i.map(l=>`${l.name}@${l.version.raw}`).join(", "),s=new Set(n.map(i=>i.version.core)),a=new Set(n.map(i=>i.version.channel));if(s.size>1){t.push({code:"version-skew-cores",fix:'Run `pnpm update "@lunora/*" lunorash` (or `npm`/`yarn` equivalent) so every Lunora package moves to the same release.',level:"warn",message:`Lunora packages span ${String(s.size)} different versions: ${r(n)}.`});return}if(a.size>1){t.push({code:"version-skew-channels",fix:"Pick one channel (all stable, or all alpha/beta) and update the odd package out.",level:"warn",message:`Lunora packages mix release channels (${[...a].map(i=>i===""?"stable":i).join(" + ")}): ${r(n)}.`});return}const c=n.map(i=>i.version.counter).filter(i=>i!==void 0);if(c.length>1){const i=Math.min(...c),l=Math.max(...c);t.push({code:"version-counter-spread",level:"info",message:c.length===n.length&&i===l?`Lunora packages are all at ${[...a][0]??""}.${String(l)}.`:`Lunora pre-release counters span ${String(i)}–${String(l)}: ${r(n)}.`})}},Ke=[h("@lunora","cli"),"lunorash"],qe=(o,t,e)=>{if(t===void 0)return;const n=[];for(const s of Ke){const a=h(o,"node_modules",s);if(b(a))try{n.push(A(a))}catch{}}if(n.length===0)return;let r;try{r=A(t)}catch{return}n.some(s=>de(s,r))||e.push({code:"cli-shadowed",fix:"Run the project's own CLI: `pnpm exec lunora …` (or `npx lunora …`).",level:"warn",message:`the running lunora (${r}) is not the project's own install (${n.join(", ")}).`})},We={full:{code:"do-class-budget-full",level:"warn"},near:{code:"do-class-budget-near",level:"warn"},ok:{code:"do-class-budget-ok",level:"pass"},unchecked:{code:"do-class-budget-unchecked",level:"info"}},He=o=>{const t=new Map,e=(n,r)=>{const s=w(n);s!==void 0&&t.set(s,[...t.get(s)??[],r])};e(o?.account_id,"top level");for(const[n,r]of Object.entries(o?.env??{}))e(r?.account_id,`env.${n}`);return t},ze=async(o,t,e,n)=>{if((u(o)??f)!=="cloudflare")return;const s=w((e.environment??process.env).CLOUDFLARE_ACCOUNT_ID)!==void 0?[]:[...He(t)],a=s.length===0?[[void 0,[]]]:s,c=await Promise.all(a.map(async([i,l])=>({accountId:i,blocks:l,budget:await te({accountId:i,environment:e.environment,fetch:e.fetch})})));for(const{accountId:i,blocks:l,budget:d}of c)n.push({...We[d.verdict],message:c.length>1?`account ${String(i)} (${l.join(", ")}): ${d.message}`:d.message,...d.fix===void 0?{}:{fix:d.fix}})},Je="CELLD_BUCKET is not set — a live `lunora deploy` needs it to name the fleet bucket; `lunora dev` and a dry run do not.",Ye=async(o,t,e)=>{if((u(o)??f)!=="celld")return;const n={...process.env,...t.environment},r=await q({dryRun:!0,env:n,purpose:"deploy"}),s=(n.CELLD_BUCKET??"").trim()===""?[...r.warnings,Je]:r.warnings;for(const a of r.errors)e.push({code:"celld-toolchain-missing",level:"fail",message:a});for(const a of s)e.push({code:"celld-toolchain-warn",level:"warn",message:a});r.errors.length===0&&s.length===0&&e.push({code:"celld-toolchain-ok",level:"pass",message:"celld, esbuild and CELLD_BUCKET are in place for a deploy."})},Qe={mismatch:{code:"artifacts-jurisdiction-mismatch",level:"fail"},missing:{code:"artifacts-namespace-missing",level:"fail"},ok:{code:"artifacts-jurisdiction-ok",level:"pass"},unchecked:{code:"artifacts-jurisdiction-unchecked",level:"warn"}},Xe=async(o,t,e,n,r)=>{if(e===void 0||t===void 0||(u(o)??f)!=="cloudflare")return;const s=[["",t],...Object.entries(t.env??{}).map(([i,l])=>[`env.${i}: `,l])],a=new Map;for(const[i,l]of s){const d=l.account_id??t.account_id;for(const p of l.artifacts??[]){const _=`${String(d)}/${String(p?.namespace)}`;typeof p?.binding=="string"&&typeof p.namespace=="string"&&!a.has(_)&&a.set(_,{accountId:d,binding:p.binding,label:i,namespace:p.namespace})}}const c=await Promise.all([...a.values()].map(async({label:i,...l})=>({check:await ne({...l,environment:n.environment,fetch:n.fetch,jurisdiction:e,missingOk:!0}),label:i})));for(const{check:i,label:l}of c)r.push({...Qe[i.verdict],message:`${l}${i.message}`,...i.fix===void 0?{}:{fix:i.fix}})},Ze=async o=>{const t=o.cwd??process.cwd(),e=[],{parsed:n,path:r}=F(t);Re(t,n,r,e),je(t,e),Te(t,n,e),De(n,e),Ee(n,e),Ie(n,e),ye(n,e),_e(n,e),Ne(t,e),Ue(t,e),Ve(t,e),Le(t,e),Se(t,e),qe(t,o.executablePath??process.argv[1],e),$e(t,e),xe(t,e);const s=await Pe(t,e);s!==void 0&&Fe(s,e),fe(n,t,s?.usesAi===!0,e),await Ye(t,o,e),await ze(t,n,o,e),(u(t)??f)==="cloudflare"&&await ke(n,o,e),await Xe(t,n,s?.jurisdiction,o,e),pe(n,s?.usesCimdWorkers===!0,e);const a={fail:0,info:0,pass:0,warn:0};for(const i of e)a[i.level]+=1;const c=a.fail>0?1:0;return{code:c,findings:e,ok:c===0,summary:a}},et={fail:"FAIL",info:"INFO",pass:"PASS",warn:"WARN"},tt=(o,t)=>{t.info("lunora doctor — project preflight");for(const r of o.findings){const s=`[${et[r.level]}] ${r.message}`;r.level==="fail"?t.error(s):r.level==="warn"?t.warn(s):t.info(s),r.fix!==void 0&&r.level!=="pass"&&t.info(` fix: ${r.fix}`)}const{fail:e,warn:n}=o.summary;e>0?t.error(`${String(e)} failure(s), ${String(n)} warning(s).`):n>0?t.warn(`0 failures, ${String(n)} warning(s).`):t.success("all checks passed.")},nt=async o=>{const{logger:t}=o,e=await Ze({...o});return tt(e,t),e},ht=le(async({cwd:o,format:t,logger:e,options:n})=>{if(n.fix===!0){const{path:s}=F(o),a=await Q({configPath:s,cwd:o,dryRun:n.dryRun===!0,logger:e});for(const c of a.applied)e.info(`fix: ${c}`);for(const c of a.skipped)e.warn(`not fixed: ${c}`)}const r=await nt({cwd:o,format:t,logger:e});return{code:r.code,data:{findings:r.findings,summary:r.summary}}});export{ht as execute,Ze as runDoctor,nt as runDoctorCommand};
1
+ import{existsSync as k,readFileSync as P,realpathSync as A}from"node:fs";import{join as h}from"node:path";import{readProjectTarget as u,DEFAULT_TARGET as f,readServiceBindings as F}from"@lunora/codegen";import{DEV_VARS_FILE as v,readDevVariablesRecord as B,isPlaceholderValue as L,parseDevVariableEntries as M,readProjectDependencies as G,discoverSchemaInfo as V,inferLunoraBindings as K,resolveSchemaDirectory as q,probeCelldToolchain as W,D1_PLACEHOLDER_ID as H}from"@lunora/config";import{GLOBAL_FETCH_STRICTLY_PUBLIC_FLAG as g,resolveObservabilitySampling as y,findWranglerFile as C,readWranglerJsonc as O,buildBindingManifest as z,validateWranglerProject as J,UNEXPORTED_CLASS_MARKER as x,detectCloudflareCliConfig as Y,collectExportGaps as Q}from"@lunora/config/cloudflare";import{a as X,i as Z,m as ee,c as te,b as ne,d as oe,e as re,f as se}from"../packem_shared/vectorize-metadata-DK61NmVx.mjs";import{r as ae,d as ie}from"../packem_shared/admin-token-VdUnvnKW.mjs";import{r as ce,n as b,c as le}from"../packem_shared/cloudflare-credentials-BVmZnDZk.mjs";import{d as de}from"../packem_shared/command-BpMv2n0o.mjs";import{i as ue}from"../packem_shared/path-containment-CgxYZggb.mjs";import{A as $,a as R,b as w}from"../packem_shared/variables-CPYyJtsL.mjs";const fe=(o,t)=>{const e=new Set,n=r=>{for(const[s,a]of Object.entries(r??{}))typeof a=="string"&&a.trim().length>0&&e.add(s)};n(o.vars);for(const r of Object.values(o.env??{}))n(r.vars);try{n(B(h(t,v)))}catch{}return e},pe=(o,t,e,n)=>{if(o===void 0||!e)return;const r=fe(o,t),s=r.has($),a=o.ai?.binding;!s&&(typeof a!="string"||a.length===0)&&n.push({code:"ai-binding-missing",fix:`Run \`lunora dev\` (it adds the binding), add \`"ai": { "binding": "AI" }\` to wrangler.jsonc, or set ${$} to an OpenAI-compatible proxy.`,level:"warn",message:"the project uses ctx.ai (@lunora/ai / env.AI) but wrangler.jsonc declares no `ai` binding — model calls will fail at runtime."}),r.has(R)&&n.push({code:"ai-gateway-token-unused",fix:"Make the gateway unauthenticated for ctx.ai, or remove the token. A bring-your-own AI SDK provider built with `resolveAiGateway` does send it.",level:"warn",message:`${R} is set, but ctx.ai's Workers AI binding cannot send a gateway token — it is ignored on that path.`}),!s&&!r.has(w)&&n.push({code:"ai-gateway-default",fix:`Run \`lunora cloudflare ai-gateway\` to create a gateway for this app and write ${w} into wrangler vars.`,level:"info",message:`no ${w} in vars or ${v} — \`<provider>/<model>\` slugs route through the account's \`default\` AI Gateway.`})},he=(o,t,e)=>{if(o===void 0||!t)return;const n=[];(o.compatibility_flags??[]).includes(g)||n.push("the top level");for(const[r,s]of Object.entries(o.env??{}))s.compatibility_flags!==void 0&&!s.compatibility_flags.includes(g)&&n.push(`env.${r}`);n.length>0&&e.push({code:"cimd-fetch-not-strictly-public",fix:`Add "${g}" to "compatibility_flags" in wrangler.jsonc.`,level:"warn",message:`@lunora/auth/cimd/workers is imported, but ${n.join(", ")} of wrangler.jsonc lacks the ${g} compatibility flag — workersCimdFetch() refuses to build without it.`})},D=5e3,ge="https://api.cloudflare.com/client/v4/user/tokens/verify",T=o=>{if(typeof o!="object"||o===null||!("result"in o)||"success"in o&&o.success===!1)return;const{result:t}=o;return typeof t=="object"&&t!==null&&"status"in t&&typeof t.status=="string"?t.status:void 0},ve=async(o,t)=>{const e=await t(ge,{headers:{Authorization:`Bearer ${o}`},signal:AbortSignal.timeout(D)}),n=await e.json().catch(()=>{});return{reported:e.ok?T(n):void 0,status:e.status}},me=async(o,t,e)=>{const n=await le({accountId:o,apiToken:t,fetch:e,init:{signal:AbortSignal.timeout(D)},path:"/tokens/verify"});return n.ok?{reported:T(n.body),status:200}:{reported:void 0,status:n.status}},be=async(o,t,e)=>{const n=await me(o,t,e);return n.reported!==void 0?n.reported==="active"?{verdict:"active"}:{verdict:"rejected"}:n.status===401||n.status===403?{verdict:"rejected"}:{reason:`HTTP ${String(n.status)}`,verdict:"unchecked"}},we=async(o,t,e)=>{const n=await ve(o,e);if(n.reported!==void 0)return n.reported==="active"?{verdict:"active"}:{verdict:"rejected"};if(n.status!==401&&n.status!==403)return{reason:`HTTP ${String(n.status)}`,verdict:"unchecked"};if(t.length===0)return{reason:"the user endpoint refused it, and no account id is set to check an account-owned token",verdict:"unchecked"};const r=await Promise.all(t.map(async s=>await be(s,o,e).catch(a=>({reason:a instanceof Error?a.message:String(a),verdict:"unchecked"}))));return r.find(s=>s.verdict==="active")??r.find(s=>s.verdict==="unchecked")??{verdict:"rejected"}},ke=o=>{const t=[o?.account_id,...Object.values(o?.env??{}).map(e=>e?.account_id)];return[...new Set(t.map(e=>b(e)).filter(e=>e!==void 0))]},ye=async(o,t,e)=>{const n=t.environment??process.env,r=ke(o),{accountId:s,token:a}=ce(n,r[0]),c=b(n.CLOUDFLARE_ACCOUNT_ID)===void 0?r:[s].filter(d=>d!==void 0);if(a===void 0){e.push({code:"cloudflare-token-unchecked",fix:"Set CLOUDFLARE_API_TOKEN to verify it.",level:"info",message:"the Cloudflare API token was not verified: CLOUDFLARE_API_TOKEN is not set."});return}const i=t.fetch??globalThis.fetch.bind(globalThis);let l;try{l=await we(a,c,i)}catch(d){l={reason:d instanceof Error?d.message:String(d),verdict:"unchecked"}}l.verdict==="active"?e.push({code:"cloudflare-token-ok",level:"pass",message:"the Cloudflare API token is active."}):l.verdict==="rejected"?e.push({code:"cloudflare-token-rejected",fix:"Create a token at https://dash.cloudflare.com/profile/api-tokens with the Workers Scripts Edit permission, then set CLOUDFLARE_API_TOKEN to it.",level:"fail",message:"the Cloudflare API token is not active, so deploys with it will fail."}):e.push({code:"cloudflare-token-unchecked",level:"info",message:`the Cloudflare API token could not be verified (${l.reason}).`})},S=o=>{const{logs:t,traces:e}=y(o);return Object.entries({logs:t,traces:e}).filter(([,n])=>n.enabled&&n.headSamplingRate>=1).map(([n])=>n)},_e=(o,t)=>{if(o===void 0)return;const e=[],n=S(o.observability);n.length>0&&e.push(`${n.join(" + ")} (top level)`);for(const[r,s]of Object.entries(o.env??{})){const a=s?.observability;if(a===void 0)continue;const c=S(a);c.length>0&&e.push(`${c.join(" + ")} (env.${r})`)}e.length!==0&&t.push({code:"observability-full-sampling",fix:"For a high-traffic Worker, lower head_sampling_rate in wrangler.jsonc: under observability for logs, under observability.traces for traces — e.g. 0.1 keeps 10% of requests. Lunora never changes a rate you set.",level:"info",message:`observability samples every request (head_sampling_rate 1) for ${e.join(", ")}. From 2026-12-01 Workers Observability is billed: Free caps ingestion at 0.5 GB/day; Paid includes 50 GB per billing cycle, then $0.25/GB.`})},m=o=>o.enabled&&o.headSamplingRate>0,j=o=>{if(!o||o.redact_query_string===!0)return!1;const{logs:t,traces:e}=y(o);return m(t)||m(e)},I=o=>{const{logs:t,traces:e}=y(o);return[m(t)&&"request logs",m(e)&&"traces"].filter(Boolean).join(" and ")},Ae=(o,t)=>{if(o===void 0)return;const e=[],n=new Set;j(o.observability)&&(e.push("top level"),n.add(I(o.observability)));for(const[r,s]of Object.entries(o.env??{})){const a=s?.observability;j(a)&&(e.push(`env.${r}`),n.add(I(a)))}e.length!==0&&t.push({code:"observability-query-unredacted",fix:'Set "redact_query_string": true in each observability block listed above in wrangler.jsonc. Lunora never changes a value you set.',level:"warn",message:`${[...n].join(" / ")} keep query strings (redact_query_string is not true) for ${e.join(", ")}. Emailed auth links carry their token in the query, so that token is stored with the request URL.`})},N={ai:"Workers AI",browser:"Browser Rendering",images:"Cloudflare Images (Image Transformations)"},E=["Argo Smart Routing","Log Explorer"],xe=o=>{const t=[["top level",o],...Object.entries(o.env??{}).flatMap(([n,r])=>r?[[`env.${n}`,r]]:[])],e=new Map;for(const[n,r]of t)for(const s of z(r).bindings)N[s.type]!==void 0&&e.set(s.type,[...e.get(s.type)??[],n]);return e},$e=(o,t)=>{if((u(o)??f)!=="cloudflare")return;const e=C(o),n=e===void 0?void 0:O(e).parsed;if(n!==void 0)for(const[r,s]of xe(n)){const a=N[r]??r;t.push({code:"paid-product-bound",fix:"Create an account budget alert in the dashboard: Manage Account > Billing > Billable Usage > Create budget alert. Lunora cannot create or read one.",level:"info",message:`${a} is bound (${s.join(", ")}) and billed per use. No documented usage-alert product covers it, and lunora cannot read budget alerts, so an account budget alert is the only cap.`})}t.push({code:"paid-product-unknown",level:"info",message:`enablement of ${E.join(" and ")} cannot be read by lunora (dashboard settings); check the dashboard if your account uses ${E.length>1?"them":"it"}.`})},Re=(o,t)=>{for(const e of F(o).services)for(const n of e.publicScopes){const r=n===""?"":` (env.${n})`;t.push({code:"service-workers-dev",fix:`If ${e.name} has no public route (custom domain or route, including ones set outside ${e.wranglerPath}), set \`"workers_dev": false\`${r}. Keep its internal auth (HMAC) either way.`,level:"warn",message:`service ${e.name} (${e.worker}) has no route in its wrangler config and still serves on workers.dev${r}; a custom domain set elsewhere would also be public.`})}},U=o=>{const t=C(o);if(t===void 0)return{parsed:void 0,path:void 0};const{parsed:e}=O(t);return{parsed:e,path:t}},Se=(o,t,e,n)=>{if(e===void 0){n.push({code:"wrangler-missing",fix:"Run `lunora init` (or `lunora dev`) to scaffold and reconcile wrangler.jsonc.",level:"fail",message:"wrangler.jsonc not found."});return}if(t===void 0){n.push({code:"wrangler-unparseable",fix:`Check ${e} is valid JSONC.`,level:"fail",message:`Could not parse ${e}.`});return}const{report:r}=J({projectRoot:o});for(const c of r.errors.filter(i=>i.includes(x)))n.push({code:"wrangler-class-unexported",level:"fail",message:c});for(const c of r.warnings.filter(i=>i.includes("LUNORA_ORIGIN_URL")))n.push({code:"scheduler-origin-missing",fix:"Set LUNORA_ORIGIN_URL in wrangler vars to the worker's public URL (or `wrangler secret put LUNORA_ORIGIN_URL`).",level:"warn",message:c});const s=c=>c.includes(x)||c.includes("SHARD");for(const c of r.errors.filter(i=>!s(i)))n.push({code:"wrangler-invalid",fix:"Fix the reported wrangler.jsonc problem; `lunora verify` reports the same set.",level:"fail",message:c});for(const c of r.warnings.filter(i=>!i.includes("LUNORA_ORIGIN_URL")))n.push({code:"wrangler-advisory",level:"warn",message:c});const a=r.errors.find(c=>c.includes("SHARD"));a===void 0?n.push({code:"wrangler-shard-binding-ok",level:"pass",message:"wrangler.jsonc present with a SHARD durable-object binding."}):n.push({code:"wrangler-shard-binding-missing",fix:"Run `lunora dev` to auto-reconcile, or add the binding manually.",level:"fail",message:a})},je=(o,t)=>{k(h(o,"lunora.json"))&&t.push({code:"stale-lunora-json",fix:'Move `target` / `remote` into `lunora.config.ts` (`export default { target: "…" }`), then delete lunora.json.',level:"warn",message:"lunora.json is present but no longer read — lunora.config.* replaced it, so any `target` or `remote` in it is being ignored."})},Ie=(o,t)=>{const e=Y(o);e!==void 0&&t.push({code:"cf-config-present",fix:e.fix,level:"warn",message:e.message})},Ee=(o,t)=>{o===void 0||o.limits?.cpu_ms!==void 0||t.push({code:"cpu-limit-missing",fix:'Add `"limits": { "cpu_ms": 30000 }` to wrangler.jsonc, tuned to your slowest legitimate handler.',level:"info",message:"no limits.cpu_ms is set — a runaway handler can burn CPU up to the platform default before anything stops it."})},Le=(o,t)=>{const e=(o?.r2_buckets??[]).map(n=>n?.bucket_name).filter(n=>typeof n=="string"&&n.length>0);e.length!==0&&t.push({code:"r2-lifecycle-unset",fix:"wrangler r2 bucket lifecycle list <bucket> — add an abort-incomplete-multipart rule if none exists.",level:"info",message:`R2 bucket(s) ${e.join(", ")}: verify a lifecycle rule aborts incomplete multipart uploads — orphaned parts are billed but invisible in a listing.`})},Ce=(o,t)=>{const{error:e,info:n}=V(o,"lunora");if(e!==void 0){t.push({code:"schema-unreadable",fix:"Fix the parse error, then re-run `lunora doctor`. `lunora codegen` reports the same failure with the offending source.",level:"fail",message:`lunora/schema.ts could not be parsed, so every schema-derived check was skipped: ${e}`});return}for(const r of n?.vectorMetadata??[]){const s=ee(r.kind);if(s===void 0){t.push({code:"vector-metadata-unfilterable",fix:"Filter on a string, number or boolean column, or drop it from `metadata`.",level:"warn",message:`vector index "${r.index}" declares metadata "${r.property}", whose type Vectorize cannot filter on.`});continue}t.push({code:"vector-metadata-index-required",fix:`wrangler ${te({index:r.index,property:r.property,type:s}).join(" ")}`,level:"info",message:`vector index "${r.index}" filters on metadata "${r.property}" — that needs a Vectorize metadata index (\`lunora deploy\` creates it).`})}},Oe=o=>{const t=o?.d1_databases;return Array.isArray(t)?t.filter(Boolean):[]},De=(o,t,e)=>{const n=Oe(o),r=[];for(const s of n){const a=typeof s.database_id=="string"?s.database_id:"";if(e?re(a)&&a!==H:se(a)){const i=typeof s.binding=="string"&&s.binding.length>0?s.binding:"<unnamed>";r.push({code:"d1-placeholder-id",fix:e?"Set database_id to the database celld should use.":"Run `lunora doctor --fix` to create the database and write its id into wrangler.jsonc, or run `wrangler d1 create <name>` and paste the returned database_id in yourself.",level:"fail",message:`D1 binding "${i}"${t} has a placeholder database_id ("${a||"<empty>"}").`})}}return r},Te=(o,t,e)=>{if(t===void 0)return;const n=(u(o)??f)==="celld",r=[{config:t,suffix:""},...Object.entries(n?{}:t.env??{}).map(([s,a])=>({config:a,suffix:` in env.${s}`}))];for(const{config:s,suffix:a}of r)e.push(...De(s,a,n))},Ne=(o,t)=>{if(o===void 0)return;const e=(o.send_email??[]).filter(Boolean);for(const n of e){const r=typeof n.destination_address=="string"?n.destination_address:"";if(r!==""&&L(r)){const s=typeof n.name=="string"&&n.name.length>0?n.name:"send_email";t.push({code:"email-destination-placeholder",fix:"Set destination_address to a verified Cloudflare Email Routing address.",level:"warn",message:`send_email binding "${s}" has a placeholder destination_address ("${r}").`})}}},Ue=(o,t)=>{const e=h(o,v);if(!k(e))return;let n;try{n=P(e,"utf8")}catch{return}const r=M(n).filter(s=>Z(s.key)&&L(s.value)).map(s=>s.key);r.length>0&&t.push({code:"dev-vars-missing-secret",fix:"Run `lunora dev` to auto-generate secrets, or fill them in by hand.",level:"warn",message:`${v} has unfilled secret value(s): ${r.join(", ")}.`})},Pe=(o,t)=>{const{source:e}=ae({cwd:o});e===void 0?t.push({code:"admin-token-missing",fix:"Set LUNORA_ADMIN_TOKEN (env or `.dev.vars`) to enable admin RPCs / studio.",level:"info",message:"LUNORA_ADMIN_TOKEN is not set."}):t.push({code:"admin-token-set",level:"pass",message:`LUNORA_ADMIN_TOKEN is set (${ie(e)}).`})},Fe=async(o,t)=>{try{return await K({projectRoot:o,schemaDir:q(o)})}catch(e){t.push({code:"declared-export-unchecked",fix:"Make sure the worker entry resolves (see `lunora codegen`), then re-run.",level:"warn",message:`could not check whether declared containers/workflows/agents are re-exported by the worker entry: ${e instanceof Error?e.message:String(e)}`});return}},Be=(o,t)=>{const e=Q(o),n=[...o.containers.map(r=>({...r,kind:"container"})),...o.workflows.map(r=>({...r,kind:"workflow"})),...o.agents.map(r=>({...r,kind:"agent"}))];for(const r of n.filter(s=>s.exported))t.push({code:"declared-export-ok",level:"pass",message:`${r.kind} "${r.exportName}" is exported by the worker entry.`});for(const r of e)t.push({code:"declared-export-missing",fix:`Add \`export * from "./lunora/_generated/${r.module}"\` to your worker entry (or re-run \`vis generate lunora-${r.kind}\`).`,level:"fail",message:`${r.kind} "${r.exportName}" is declared but ${r.className} is not exported by the worker entry.`})},Me=/^[\^~><= ]+/u,Ge=/^(?<core>\d+\.\d+\.\d+)(?:-(?<channel>[a-z]+)\.(?<counter>\d+))?$/u,Ve=o=>{const t=o.replace(Me,"").trim(),e=Ge.exec(t);if(!e?.groups)return;const{channel:n,core:r,counter:s}=e.groups;return{channel:n??"",core:r??"",counter:s===void 0?void 0:Number.parseInt(s,10),raw:t}},Ke=(o,t)=>{const e=G(o),n=[];for(const[i,l]of Object.entries(e)){if(i!=="lunorash"&&!i.startsWith("@lunora/"))continue;const d=Ve(l);d&&n.push({name:i,version:d})}if(n.length<2)return;const r=i=>i.map(l=>`${l.name}@${l.version.raw}`).join(", "),s=new Set(n.map(i=>i.version.core)),a=new Set(n.map(i=>i.version.channel));if(s.size>1){t.push({code:"version-skew-cores",fix:'Run `pnpm update "@lunora/*" lunorash` (or `npm`/`yarn` equivalent) so every Lunora package moves to the same release.',level:"warn",message:`Lunora packages span ${String(s.size)} different versions: ${r(n)}.`});return}if(a.size>1){t.push({code:"version-skew-channels",fix:"Pick one channel (all stable, or all alpha/beta) and update the odd package out.",level:"warn",message:`Lunora packages mix release channels (${[...a].map(i=>i===""?"stable":i).join(" + ")}): ${r(n)}.`});return}const c=n.map(i=>i.version.counter).filter(i=>i!==void 0);if(c.length>1){const i=Math.min(...c),l=Math.max(...c);t.push({code:"version-counter-spread",level:"info",message:c.length===n.length&&i===l?`Lunora packages are all at ${[...a][0]??""}.${String(l)}.`:`Lunora pre-release counters span ${String(i)}–${String(l)}: ${r(n)}.`})}},qe=[h("@lunora","cli"),"lunorash"],We=(o,t,e)=>{if(t===void 0)return;const n=[];for(const s of qe){const a=h(o,"node_modules",s);if(k(a))try{n.push(A(a))}catch{}}if(n.length===0)return;let r;try{r=A(t)}catch{return}n.some(s=>ue(s,r))||e.push({code:"cli-shadowed",fix:"Run the project's own CLI: `pnpm exec lunora …` (or `npx lunora …`).",level:"warn",message:`the running lunora (${r}) is not the project's own install (${n.join(", ")}).`})},He={full:{code:"do-class-budget-full",level:"warn"},near:{code:"do-class-budget-near",level:"warn"},ok:{code:"do-class-budget-ok",level:"pass"},unchecked:{code:"do-class-budget-unchecked",level:"info"}},ze=o=>{const t=new Map,e=(n,r)=>{const s=b(n);s!==void 0&&t.set(s,[...t.get(s)??[],r])};e(o?.account_id,"top level");for(const[n,r]of Object.entries(o?.env??{}))e(r?.account_id,`env.${n}`);return t},Je=async(o,t,e,n)=>{if((u(o)??f)!=="cloudflare")return;const s=b((e.environment??process.env).CLOUDFLARE_ACCOUNT_ID)!==void 0?[]:[...ze(t)],a=s.length===0?[[void 0,[]]]:s,c=await Promise.all(a.map(async([i,l])=>({accountId:i,blocks:l,budget:await ne({accountId:i,environment:e.environment,fetch:e.fetch})})));for(const{accountId:i,blocks:l,budget:d}of c)n.push({...He[d.verdict],message:c.length>1?`account ${String(i)} (${l.join(", ")}): ${d.message}`:d.message,...d.fix===void 0?{}:{fix:d.fix}})},Ye="CELLD_BUCKET is not set — a live `lunora deploy` needs it to name the fleet bucket; `lunora dev` and a dry run do not.",Qe=async(o,t,e)=>{if((u(o)??f)!=="celld")return;const n={...process.env,...t.environment},r=await W({dryRun:!0,env:n,purpose:"deploy"}),s=(n.CELLD_BUCKET??"").trim()===""?[...r.warnings,Ye]:r.warnings;for(const a of r.errors)e.push({code:"celld-toolchain-missing",level:"fail",message:a});for(const a of s)e.push({code:"celld-toolchain-warn",level:"warn",message:a});r.errors.length===0&&s.length===0&&e.push({code:"celld-toolchain-ok",level:"pass",message:"celld, esbuild and CELLD_BUCKET are in place for a deploy."})},Xe={mismatch:{code:"artifacts-jurisdiction-mismatch",level:"fail"},missing:{code:"artifacts-namespace-missing",level:"fail"},ok:{code:"artifacts-jurisdiction-ok",level:"pass"},unchecked:{code:"artifacts-jurisdiction-unchecked",level:"warn"}},Ze=async(o,t,e,n,r)=>{if(e===void 0||t===void 0||(u(o)??f)!=="cloudflare")return;const s=[["",t],...Object.entries(t.env??{}).map(([i,l])=>[`env.${i}: `,l])],a=new Map;for(const[i,l]of s){const d=l.account_id??t.account_id;for(const p of l.artifacts??[]){const _=`${String(d)}/${String(p?.namespace)}`;typeof p?.binding=="string"&&typeof p.namespace=="string"&&!a.has(_)&&a.set(_,{accountId:d,binding:p.binding,label:i,namespace:p.namespace})}}const c=await Promise.all([...a.values()].map(async({label:i,...l})=>({check:await oe({...l,environment:n.environment,fetch:n.fetch,jurisdiction:e,missingOk:!0}),label:i})));for(const{check:i,label:l}of c)r.push({...Xe[i.verdict],message:`${l}${i.message}`,...i.fix===void 0?{}:{fix:i.fix}})},et=async o=>{const t=o.cwd??process.cwd(),e=[],{parsed:n,path:r}=U(t);Se(t,n,r,e),Ie(t,e),Te(t,n,e),Ne(n,e),Ee(n,e),Le(n,e),_e(n,e),Ae(n,e),Ue(t,e),Pe(t,e),Ke(t,e),Ce(t,e),je(t,e),We(t,o.executablePath??process.argv[1],e),Re(t,e),$e(t,e);const s=await Fe(t,e);s!==void 0&&Be(s,e),pe(n,t,s?.usesAi===!0,e),await Qe(t,o,e),await Je(t,n,o,e),(u(t)??f)==="cloudflare"&&await ye(n,o,e),await Ze(t,n,s?.jurisdiction,o,e),he(n,s?.usesCimdWorkers===!0,e);const a={fail:0,info:0,pass:0,warn:0};for(const i of e)a[i.level]+=1;const c=a.fail>0?1:0;return{code:c,findings:e,ok:c===0,summary:a}},tt={fail:"FAIL",info:"INFO",pass:"PASS",warn:"WARN"},nt=(o,t)=>{t.info("lunora doctor — project preflight");for(const r of o.findings){const s=`[${tt[r.level]}] ${r.message}`;r.level==="fail"?t.error(s):r.level==="warn"?t.warn(s):t.info(s),r.fix!==void 0&&r.level!=="pass"&&t.info(` fix: ${r.fix}`)}const{fail:e,warn:n}=o.summary;e>0?t.error(`${String(e)} failure(s), ${String(n)} warning(s).`):n>0?t.warn(`0 failures, ${String(n)} warning(s).`):t.success("all checks passed.")},ot=async o=>{const{logger:t}=o,e=await et({...o});return nt(e,t),e},gt=de(async({cwd:o,format:t,logger:e,options:n})=>{if(n.fix===!0){const{path:s}=U(o),a=await X({configPath:s,cwd:o,dryRun:n.dryRun===!0,logger:e});for(const c of a.applied)e.info(`fix: ${c}`);for(const c of a.skipped)e.warn(`not fixed: ${c}`)}const r=await ot({cwd:o,format:t,logger:e});return{code:r.code,data:{findings:r.findings,summary:r.summary}}});export{gt as execute,et as runDoctor,ot as runDoctorCommand};
@@ -1 +1 @@
1
- import{readFileSync as g,existsSync as b}from"node:fs";import{join as y}from"node:path";import{discoverSchema as h}from"@lunora/codegen";import{readLinkedProject as v,readProjectManifest as $}from"@lunora/config";import{findWranglerFile as p}from"@lunora/config/cloudflare";import{parse as k}from"jsonc-parser";import{Project as w}from"ts-morph";import{w as j,d as m}from"../packem_shared/binding-manifest-file-CQ1eQYy1.mjs";import{d as S}from"../packem_shared/command-BpMv2n0o.mjs";import{EXIT_CODE as f}from"../packem_shared/EXIT_CODE-DQVhSE-1.mjs";const d=(n,e)=>{if(n===null||typeof n!="object")return;const i=n[e];return typeof i=="string"&&i.length>0?i:void 0},x=(n,e)=>{if(n===null||typeof n!="object")return[];const i=n[e];return Array.isArray(i)?i:[]},E=(n,e)=>{const{manifest:i}=m(e);return{bindings:(i?.bindings??[]).map(r=>`${r.type}:${r.binding}`),compatibilityDate:d(n,"compatibility_date"),compatibilityFlags:x(n,"compatibility_flags").filter(r=>typeof r=="string"),main:d(n,"main"),name:d(n,"name")}},F=n=>({tables:n.tables.map(e=>{let i="root";return e.shardMode==="global"?i="global":typeof e.shardMode=="object"&&(i=`shardBy(${e.shardMode.field})`),{indexes:e.indexes.length,name:e.name,shard:i}}),vectorIndexes:n.vectorIndexes.length}),P=n=>{const e=$(n);if(e===void 0)return[];const i=["dependencies","devDependencies","peerDependencies","optionalDependencies"],r=new Map;for(const s of i){const a=e[s];if(!(a===null||typeof a!="object"))for(const[t,o]of Object.entries(a))t.startsWith("@lunora/")&&typeof o=="string"&&!r.has(t)&&r.set(t,o)}return[...r.entries()].toSorted(([s],[a])=>s.localeCompare(a)).map(([s,a])=>({name:s,version:a}))},M=n=>{const e=P(n),i=p(n);let r;if(i)try{r=E(k(g(i,"utf8")),n)}catch{r=void 0}const s=y(n,"lunora","schema.ts");let a,t;if(b(s))try{const o=new w({skipAddingFilesFromTsConfig:!0,useInMemoryFileSystem:!1});a=F(h(o,s))}catch(o){t=o instanceof Error?o.message:String(o)}return{link:v(n),lunoraPackages:e,projectRoot:n,schema:a,schemaError:t,wrangler:r,wranglerPath:i}},I=(n,e)=>{if(e.info(`project: ${n.projectRoot}`),e.info(""),e.info("@lunora/* packages:"),n.lunoraPackages.length===0)e.info(" (none found in package.json)");else for(const i of n.lunoraPackages)e.info(` ${i.name}@${i.version}`);if(e.info(""),n.wrangler?(e.info(`wrangler: ${n.wranglerPath??""}`),e.info(` name: ${n.wrangler.name??"<unset>"}`),e.info(` main: ${n.wrangler.main??"<unset>"}`),e.info(` compatibility_date: ${n.wrangler.compatibilityDate??"<unset>"}`),e.info(` compatibility_flags: ${n.wrangler.compatibilityFlags.join(", ")||"<none>"}`),e.info(` bindings: ${n.wrangler.bindings.join(", ")||"<none>"}`)):e.info("wrangler: (not found)"),e.info(""),n.link?(e.info(`link: ${n.link.workerName??"(unnamed)"} -> ${n.link.workerUrl??"<no url>"}`),n.link.env!==void 0&&e.info(` env: ${n.link.env}`)):e.info("link: (not linked — run `lunora link --url <https://your-worker>`)"),e.info(""),n.schemaError!==void 0)e.warn(`schema: parse error — ${n.schemaError}`);else if(n.schema){e.info(`schema: ${String(n.schema.tables.length)} table(s), ${String(n.schema.vectorIndexes)} vector index(es)`);for(const i of n.schema.tables)e.info(` ${i.name} [${i.shard}, ${String(i.indexes)} index(es)]`)}else e.info("schema: (no lunora/schema.ts)")},A=n=>{const{cwd:e,json:i,logger:r,out:s}=n;if(s!==void 0){const{error:o}=j({destination:s,logger:r,projectRoot:e});return o!==void 0?(r.error(o),{code:f.USAGE,error:o}):{code:0}}const{error:a,manifest:t}=m(e);if(t===void 0){const o=a??"could not derive the binding manifest";return r.error(o),{code:f.USAGE,error:o}}if(i)return{code:0,data:t};if(t.bindings.length===0)r.info("No bindings declared.");else{const o=Math.max(...t.bindings.map(c=>c.type.length));r.info(`${String(t.bindings.length)} binding(s):`);for(const c of t.bindings){const l=[c.resource,c.className,c.resourceId].filter(u=>u!==void 0).join(" ");r.info(` ${c.type.padEnd(o)} ${c.binding}${l===""?"":` ${l}`}`)}}return t.crons.length>0&&r.info(`${String(t.crons.length)} cron trigger(s): ${t.crons.join(", ")}`),t.vars.length>0&&r.info(`${String(t.vars.length)} var(s): ${t.vars.join(", ")}`),t.unknown.length>0&&r.warn(`not modelled by the manifest: ${t.unknown.join(", ")} — anything they bind must be provisioned by hand.`),{code:0,data:t}},D=n=>{const e=n.cwd??process.cwd(),i=n.format==="json",{logger:r}=n;if(n.bindings===!0)return A({cwd:e,json:i,logger:r,out:n.out});if(n.out!==void 0){const a="--out only applies with --bindings; the full info snapshot prints to stdout under --format json.";return r.error(a),{code:f.USAGE,error:a}}const s=M(e);return i||I(s,r),{code:0,data:s,snapshot:s}},H=S(({cwd:n,format:e,logger:i,options:r})=>D({bindings:r.bindings===!0,cwd:n,format:e,logger:i,out:r.out}));export{M as collectInfo,H as execute,D as runInfoCommand};
1
+ import{readFileSync as g,existsSync as b}from"node:fs";import{join as y}from"node:path";import{discoverSchema as h}from"@lunora/codegen";import{readLinkedProject as v,readProjectManifest as $}from"@lunora/config";import{findWranglerFile as p}from"@lunora/config/cloudflare";import{parse as k}from"jsonc-parser";import{Project as w}from"ts-morph";import{w as j,d as m}from"../packem_shared/binding-manifest-file-BZyQFSTF.mjs";import{d as S}from"../packem_shared/command-BpMv2n0o.mjs";import{EXIT_CODE as f}from"../packem_shared/EXIT_CODE-DQVhSE-1.mjs";const d=(n,e)=>{if(n===null||typeof n!="object")return;const i=n[e];return typeof i=="string"&&i.length>0?i:void 0},x=(n,e)=>{if(n===null||typeof n!="object")return[];const i=n[e];return Array.isArray(i)?i:[]},E=(n,e)=>{const{manifest:i}=m(e);return{bindings:(i?.bindings??[]).map(r=>`${r.type}:${r.binding}`),compatibilityDate:d(n,"compatibility_date"),compatibilityFlags:x(n,"compatibility_flags").filter(r=>typeof r=="string"),main:d(n,"main"),name:d(n,"name")}},F=n=>({tables:n.tables.map(e=>{let i="root";return e.shardMode==="global"?i="global":typeof e.shardMode=="object"&&(i=`shardBy(${e.shardMode.field})`),{indexes:e.indexes.length,name:e.name,shard:i}}),vectorIndexes:n.vectorIndexes.length}),P=n=>{const e=$(n);if(e===void 0)return[];const i=["dependencies","devDependencies","peerDependencies","optionalDependencies"],r=new Map;for(const s of i){const a=e[s];if(!(a===null||typeof a!="object"))for(const[t,o]of Object.entries(a))t.startsWith("@lunora/")&&typeof o=="string"&&!r.has(t)&&r.set(t,o)}return[...r.entries()].toSorted(([s],[a])=>s.localeCompare(a)).map(([s,a])=>({name:s,version:a}))},M=n=>{const e=P(n),i=p(n);let r;if(i)try{r=E(k(g(i,"utf8")),n)}catch{r=void 0}const s=y(n,"lunora","schema.ts");let a,t;if(b(s))try{const o=new w({skipAddingFilesFromTsConfig:!0,useInMemoryFileSystem:!1});a=F(h(o,s))}catch(o){t=o instanceof Error?o.message:String(o)}return{link:v(n),lunoraPackages:e,projectRoot:n,schema:a,schemaError:t,wrangler:r,wranglerPath:i}},I=(n,e)=>{if(e.info(`project: ${n.projectRoot}`),e.info(""),e.info("@lunora/* packages:"),n.lunoraPackages.length===0)e.info(" (none found in package.json)");else for(const i of n.lunoraPackages)e.info(` ${i.name}@${i.version}`);if(e.info(""),n.wrangler?(e.info(`wrangler: ${n.wranglerPath??""}`),e.info(` name: ${n.wrangler.name??"<unset>"}`),e.info(` main: ${n.wrangler.main??"<unset>"}`),e.info(` compatibility_date: ${n.wrangler.compatibilityDate??"<unset>"}`),e.info(` compatibility_flags: ${n.wrangler.compatibilityFlags.join(", ")||"<none>"}`),e.info(` bindings: ${n.wrangler.bindings.join(", ")||"<none>"}`)):e.info("wrangler: (not found)"),e.info(""),n.link?(e.info(`link: ${n.link.workerName??"(unnamed)"} -> ${n.link.workerUrl??"<no url>"}`),n.link.env!==void 0&&e.info(` env: ${n.link.env}`)):e.info("link: (not linked — run `lunora link --url <https://your-worker>`)"),e.info(""),n.schemaError!==void 0)e.warn(`schema: parse error — ${n.schemaError}`);else if(n.schema){e.info(`schema: ${String(n.schema.tables.length)} table(s), ${String(n.schema.vectorIndexes)} vector index(es)`);for(const i of n.schema.tables)e.info(` ${i.name} [${i.shard}, ${String(i.indexes)} index(es)]`)}else e.info("schema: (no lunora/schema.ts)")},A=n=>{const{cwd:e,json:i,logger:r,out:s}=n;if(s!==void 0){const{error:o}=j({destination:s,logger:r,projectRoot:e});return o!==void 0?(r.error(o),{code:f.USAGE,error:o}):{code:0}}const{error:a,manifest:t}=m(e);if(t===void 0){const o=a??"could not derive the binding manifest";return r.error(o),{code:f.USAGE,error:o}}if(i)return{code:0,data:t};if(t.bindings.length===0)r.info("No bindings declared.");else{const o=Math.max(...t.bindings.map(c=>c.type.length));r.info(`${String(t.bindings.length)} binding(s):`);for(const c of t.bindings){const l=[c.resource,c.className,c.resourceId].filter(u=>u!==void 0).join(" ");r.info(` ${c.type.padEnd(o)} ${c.binding}${l===""?"":` ${l}`}`)}}return t.crons.length>0&&r.info(`${String(t.crons.length)} cron trigger(s): ${t.crons.join(", ")}`),t.vars.length>0&&r.info(`${String(t.vars.length)} var(s): ${t.vars.join(", ")}`),t.unknown.length>0&&r.warn(`not modelled by the manifest: ${t.unknown.join(", ")} — anything they bind must be provisioned by hand.`),{code:0,data:t}},D=n=>{const e=n.cwd??process.cwd(),i=n.format==="json",{logger:r}=n;if(n.bindings===!0)return A({cwd:e,json:i,logger:r,out:n.out});if(n.out!==void 0){const a="--out only applies with --bindings; the full info snapshot prints to stdout under --format json.";return r.error(a),{code:f.USAGE,error:a}}const s=M(e);return i||I(s,r),{code:0,data:s,snapshot:s}},H=S(({cwd:n,format:e,logger:i,options:r})=>D({bindings:r.bindings===!0,cwd:n,format:e,logger:i,out:r.out}));export{M as collectInfo,H as execute,D as runInfoCommand};
@@ -1,8 +1,8 @@
1
- import{projectConfigCandidatePaths as ee,readServiceBindings as ve,createCodegenProject as me,refreshCodegenProject as he,findTsconfig as we,PROJECT_CONFIG_FILENAMES as ye,runCodegen as re}from"@lunora/codegen";import{runPostCodegenHook as pe,resolveLazyFunctions as D,detectAgentRules as te,resolveSchemaDirectory as C,updateDevServerState as oe,readDevServerState as Se,DEV_DAEMON_ENV as ne,detectAiAgent as Ee,readProjectRemotePreference as be,readLiveDevServerState as ae,createDiscoveryCache as Pe,DEV_HANDOFF_ENV as I,clearDevServerState as Re,claimDevServerState as _e,DEV_LOG_FILE_ENV as De,secretSourcesFromBindings as ke,inferLunoraBindings as Te,ensureDevVarsExample as $e,ensureDevVariables as Ce,isInteractive as Ae,DEV_VARS_FILE as H,DEV_VARS_EXAMPLE_FILE as Oe,fillDevSecrets as Fe,DEV_BINDINGS_FILE as Le,DEV_STATE_FILE as ie,writeCelldDevProjection as Ie,claimAgentRulesHint as Ne,AGENT_RULES_HINT as je}from"@lunora/config";import{WRANGLER_FILES as Me,wranglerCodegenInputs as se,resolveRemoteEnabled as We,startLocalHyperdrive as He,describeWithheld as xe}from"@lunora/config/cloudflare";import{p as Ue}from"../packem_shared/api-spec-lYyq4vL6.mjs";import{w as Ve}from"../packem_shared/binding-manifest-file-CQ1eQYy1.mjs";import{createHash as Be}from"node:crypto";import{existsSync as A,watch as N,readFileSync as ce,statSync as ze}from"node:fs";import{join as R,dirname as M}from"node:path";import{STUDIO_WRITE_TEMP_SUFFIX as Ge,consumeStudioWrite as x,loadStudioAssets as U,studioAssetsStamp as V,createStudioDocumentCache as Xe,renderStudioHtml as Je,resolveAdminToken as Ye,requestPathname as B,transportRejectionReason as z,SCHEMA_EDIT_ENDPOINT as Ke,POLICY_SCAFFOLD_ENDPOINT as Qe,SEED_ENDPOINT as Ze,ADVISOR_PROCEDURES_ENDPOINT as qe,sendStudioDocument as er,serveJsonHandler as rr,isStandaloneModulePath as tr,readStandaloneAsset as or,assetContentType as nr,handleSchemaEditRequest as ar,handlePolicyScaffoldRequest as ir,handleSeedRequest as sr,handleAdvisorProceduresRequest as cr,sendStudioAsset as dr}from"@lunora/config/studio-host";import{LunoraError as lr}from"@lunora/errors";import{ts as G}from"ts-morph";import{a as ur}from"../packem_shared/codegen-error-AmH54ofi.mjs";import{a as fr}from"../packem_shared/platform-diagnostics-Cn2g6Jh-.mjs";import{d as gr}from"../packem_shared/command-BpMv2n0o.mjs";import{a as vr}from"../packem_shared/deploy-target-resolve-BTCW1Iv4.mjs";import{EXIT_CODE as $}from"../packem_shared/EXIT_CODE-DQVhSE-1.mjs";import{forceJsonLogging as mr}from"../packem_shared/createLogger-4TujevyB.mjs";import{createServer as hr,request as wr}from"node:http";import{connect as yr}from"node:net";import{pipeline as pr}from"node:stream";import{c as Sr}from"../packem_shared/tui-prompts-B3YwUhGw.mjs";import{setTimeout as Er}from"node:timers/promises";import{r as br,d as Pr,P as Rr,a as _r,b as j,c as Dr,s as kr,n as de,e as Tr,f as $r,g as Cr,h as Ar,i as X,w as Or,j as Fr,k as Lr,t as Ir,l as Nr,m as jr,o as Mr}from"../packem_shared/plan-BH6l7s48.mjs";import{b as Wr}from"./handler.mjs";const Hr=100,xr=/(?:\.[cm]?[jt]sx?|(?:^|[/\\])tsconfig(?:\..+)?\.json)$/u,L=300,J=/[/\\]/u,Y=e=>{try{return ce(e,"utf8")}catch{return""}},Ur=e=>{try{const{mtimeMs:r,size:o}=ze(e);return`${String(r)}:${String(o)}`}catch{return""}},Vr=["pnpm-lock.yaml","package-lock.json","yarn.lock","bun.lock","node_modules/.modules.yaml","node_modules/.package-lock.json","node_modules/.yarn-integrity"],le=e=>{const r=M(e);return r===e?[e]:[e,...le(r)]},Br=e=>{const r={...G.sys,onUnRecoverableConfigFileDiagnostic:()=>{},readDirectory:()=>[]},o=G.getParsedCommandLineOfConfigFile(e,{},r);return o===void 0?"":JSON.stringify(o.options)},zr=(e,r)=>{const o=we(r),n=[...new Set([...ee(r),...ye.map(s=>R(e,s))])].map(s=>`${s}
2
- ${Y(s)}`),a=le(e).flatMap(s=>Vr.map(i=>`${R(s,i)}
3
- ${Ur(R(s,i))}`));return[o===void 0?"":`${o}
1
+ import{projectConfigCandidatePaths as ee,readServiceBindings as ve,createCodegenProject as me,refreshCodegenProject as he,findTsconfig as we,PROJECT_CONFIG_FILENAMES as ye,runCodegen as te}from"@lunora/codegen";import{runPostCodegenHook as pe,resolveLazyFunctions as k,detectAgentRules as re,resolveSchemaDirectory as P,updateDevServerState as oe,readDevServerState as Se,DEV_DAEMON_ENV as ne,detectAiAgent as Ee,readProjectRemotePreference as be,readLiveDevServerState as ae,createDiscoveryCache as Re,DEV_HANDOFF_ENV as I,clearDevServerState as De,claimDevServerState as _e,DEV_LOG_FILE_ENV as ke,secretSourcesFromBindings as Pe,inferLunoraBindings as Te,ensureDevVarsExample as Ce,ensureDevVariables as $e,isInteractive as Ae,DEV_VARS_FILE as H,DEV_VARS_EXAMPLE_FILE as Oe,fillDevSecrets as Fe,checkDevEnv as Le,describeDevEnvCheck as Ie,DEV_BINDINGS_FILE as Ne,DEV_STATE_FILE as ie,writeCelldDevProjection as je,claimAgentRulesHint as Me,AGENT_RULES_HINT as We,devTriggerHints as He,formatDevBannerRow as xe}from"@lunora/config";import{WRANGLER_FILES as Ve,wranglerCodegenInputs as se,resolveRemoteEnabled as Ue,startLocalHyperdrive as Be,describeWithheld as ze}from"@lunora/config/cloudflare";import{p as Ge}from"../packem_shared/api-spec-lYyq4vL6.mjs";import{w as Xe}from"../packem_shared/binding-manifest-file-BZyQFSTF.mjs";import{createHash as Je}from"node:crypto";import{existsSync as A,watch as N,readFileSync as ce,statSync as Ye}from"node:fs";import{join as D,dirname as M}from"node:path";import{STUDIO_WRITE_TEMP_SUFFIX as Ke,consumeStudioWrite as x,loadStudioAssets as V,studioAssetsStamp as U,createStudioDocumentCache as Qe,renderStudioHtml as Ze,resolveAdminToken as qe,requestPathname as B,transportRejectionReason as z,SCHEMA_EDIT_ENDPOINT as et,POLICY_SCAFFOLD_ENDPOINT as tt,SEED_ENDPOINT as rt,ADVISOR_PROCEDURES_ENDPOINT as ot,sendStudioDocument as nt,serveJsonHandler as at,isStandaloneModulePath as it,readStandaloneAsset as st,assetContentType as ct,handleSchemaEditRequest as dt,handlePolicyScaffoldRequest as lt,handleSeedRequest as ut,handleAdvisorProceduresRequest as ft,sendStudioAsset as gt}from"@lunora/config/studio-host";import{LunoraError as vt}from"@lunora/errors";import{ts as G}from"ts-morph";import{a as mt}from"../packem_shared/codegen-error-AmH54ofi.mjs";import{a as ht}from"../packem_shared/platform-diagnostics-Cn2g6Jh-.mjs";import{d as wt}from"../packem_shared/command-BpMv2n0o.mjs";import{a as yt}from"../packem_shared/deploy-target-resolve-BTCW1Iv4.mjs";import{EXIT_CODE as $}from"../packem_shared/EXIT_CODE-DQVhSE-1.mjs";import{forceJsonLogging as pt}from"../packem_shared/createLogger-4TujevyB.mjs";import{createServer as St,request as Et}from"node:http";import{connect as bt}from"node:net";import{pipeline as Rt}from"node:stream";import{c as Dt}from"../packem_shared/tui-prompts-B3YwUhGw.mjs";import{setTimeout as _t}from"node:timers/promises";import{r as kt,d as Pt,P as Tt,a as Ct,b as j,c as $t,s as At,n as de,e as Ot,f as Ft,g as Lt,h as It,i as X,w as Nt,j as jt,k as Mt,t as Wt,l as Ht,m as xt,o as Vt}from"../packem_shared/plan-BF2E-dq4.mjs";import{b as Ut}from"./handler.mjs";const Bt=100,zt=/(?:\.[cm]?[jt]sx?|(?:^|[/\\])tsconfig(?:\..+)?\.json)$/u,L=300,J=/[/\\]/u,Y=e=>{try{return ce(e,"utf8")}catch{return""}},Gt=e=>{try{const{mtimeMs:t,size:o}=Ye(e);return`${String(t)}:${String(o)}`}catch{return""}},Xt=["pnpm-lock.yaml","package-lock.json","yarn.lock","bun.lock","node_modules/.modules.yaml","node_modules/.package-lock.json","node_modules/.yarn-integrity"],le=e=>{const t=M(e);return t===e?[e]:[e,...le(t)]},Jt=e=>{const t={...G.sys,onUnRecoverableConfigFileDiagnostic:()=>{},readDirectory:()=>[]},o=G.getParsedCommandLineOfConfigFile(e,{},t);return o===void 0?"":JSON.stringify(o.options)},Yt=(e,t)=>{const o=we(t),n=[...new Set([...ee(t),...ye.map(s=>D(e,s))])].map(s=>`${s}
2
+ ${Y(s)}`),a=le(e).flatMap(s=>Xt.map(i=>`${D(s,i)}
3
+ ${Gt(D(s,i))}`));return[o===void 0?"":`${o}
4
4
  ${Y(o)}
5
- ${Br(o)}`,...n,...a].join("\0")},Gr=(e,r)=>{let o,t;return{drop:()=>{o=void 0},get:()=>{const n=zr(e,r);return o===void 0||n!==t?(t=n,o=me(r)):he(o,r),o}}},Xr=(e,r,o,t)=>{const{apiSpec:n,lazyFunctions:a,logger:d,projectRoot:s,target:i}=e;try{const g=R(s,r,"schema.ts");if(!A(g))throw new lr("INTERNAL",`schema.ts not found at ${g}`);const h=re({apiSpec:n,lazyFunctions:D(a),lunoraDirectory:r,project:t.get(),projectRoot:s,target:i,...se(s)});d.success(`codegen: wrote ${r}/_generated (${o})`),fr(h.platformDiagnostics,d)}catch(g){return t.drop(),d.error(ur(g,o)),!1}return!0},K=e=>{const r=Be("sha256");for(const o of e){if(r.update(o).update("\0"),A(o)){r.update("present\0");try{r.update(ce(o))}catch{r.update("unreadable")}}else r.update("absent");r.update("\0")}return r.digest("hex")},Q=e=>e instanceof Error?e.stack??e.message:String(e),Jr=(e,r,o)=>{const t=[];for(const n of new Set([...e].map(a=>M(a))))if(A(n))try{const a=N(n,(d,s)=>{typeof s!="string"?r(void 0):e.has(R(n,s))&&r(s)});a.on("error",d=>{o(`codegen watch: stopped watching ${n} for lunora.config changes
6
- ${Q(d)}`),a.close()}),t.push(a)}catch(a){o(`codegen watch: could not watch ${n} for lunora.config changes
7
- ${Q(a)}`)}return t},Yr=e=>{const r=e.lunoraDirectory??"lunora",o=R(e.projectRoot,r),t=e.debounceMs??Hr;let n=!1,a=!1,d=!1,s,i=0,g;const h=[...ee(o),...Me.map(u=>R(e.projectRoot,u))];let p=Promise.resolve();const S=Gr(e.projectRoot,o);let c;const l=new Promise(u=>{c=u}),f=(u,m)=>{n||(s={codegen:m||s?.codegen===!0,reason:u},!a&&(a=!0,p=(async()=>{try{for(;s!==void 0;){const y=s;if(s=void 0,y.codegen&&(g=K(h)),!(y.codegen&&!Xr(e,r,y.reason,S))){d=!0;try{(await pe({cwd:e.projectRoot,logger:e.logger,spawner:e.spawner,stdoutToStderr:e.jsonLogs})).ran&&(i=Date.now())}finally{d=!1}}}}catch(y){e.logger.error(`codegen watch: ${y instanceof Error?y.message:String(y)}`)}finally{a=!1,c()}})()))};f("startup",!0);const w=u=>(e.logger.warn(`codegen watch unavailable (${u}) — schema edits will NOT auto-regenerate. Run \`lunora codegen\` manually after each edit.`),{close:()=>(n=!0,s=void 0,p),ready:l,watchAvailable:!1});let v,P;if(!A(o))return w(`no such directory: ${o}`);const E=new Set;let _=!1;const k=(u,m)=>{if(d||Date.now()-i<L){v&&(clearTimeout(v),v=void 0);let y=!1;for(const b of[...E,u])b!==void 0&&x(b)&&(y=!0);E.clear(),_=!1,y&&f(`change: ${m}`,!1);return}u===void 0?_=!0:E.add(u),v&&clearTimeout(v),v=setTimeout(()=>{const y=[...E].map(ge=>x(ge)),b=_||y.includes(!1);E.clear(),_=!1,f(`change: ${m}`,b)},t)},O=[],F=[],ue=new Set(h);let T;const W=u=>{if(!n){if(!d&&Date.now()-i>=L){clearTimeout(T),(u===void 0||K(h)!==g)&&k(void 0,u??"?");return}clearTimeout(T),T=setTimeout(()=>{W(u)},L)}};try{P=N(o,{recursive:!0},(u,m)=>{if(typeof m!="string"){k(void 0,"?");return}m.split(J).includes("_generated")||m.endsWith(Ge)||k(R(o,m),m)});for(const u of ve(e.projectRoot).services)if(u.rpcEntrypoint!==void 0){const m=N(M(u.wranglerPath),{recursive:!0},(y,b)=>{typeof b=="string"&&xr.test(b)&&!b.split(J).includes("node_modules")&&k(void 0,b)});m.on("error",y=>{e.logger.warn(`codegen watch: stopped watching service "${u.name}" (${y.message})`),m.close()}),O.push(m)}F.push(...Jr(ue,u=>{W(u)},u=>{e.logger.warn(u)}))}catch(u){P?.close();for(const m of O)m.close();for(const m of F)m.close();return w(u instanceof Error?u.message:String(u))}const fe=P;return{close:()=>{n=!0,s=void 0,v&&clearTimeout(v),clearTimeout(T),fe.close();for(const u of O)u.close();for(const u of F)u.close();return p},ready:l,watchAvailable:!0}},Z="/_lunora",Kr=3e4,q=(e,r,o,t=Kr)=>{const n=wr({headers:{...e.headers,host:o.host},hostname:o.hostname,method:e.method,path:e.url,port:o.port},a=>{n.setTimeout(0),r.writeHead(a.statusCode??502,a.headers),pr(a,r,()=>{})});n.setTimeout(t,()=>{n.destroy(new Error(`no response within ${String(t)}ms`))}),n.on("error",a=>{if(r.headersSent){r.destroy();return}r.statusCode=a.message.startsWith("no response within")?504:502,r.end(`lunora dev: worker unreachable (${a.message})`)}),r.on("close",()=>{r.writableFinished||n.destroy()}),e.pipe(n)},Qr=5e3,Zr=(e,r,o,t)=>{const n=yr({host:t.hostname,port:Number(t.port)},()=>{n.setTimeout(0);const a=[`${e.method??"GET"} ${e.url??"/"} HTTP/1.1`];for(const[d,s]of Object.entries(e.headers))if(d!=="host")for(const i of Array.isArray(s)?s:[s])i!==void 0&&a.push(`${d}: ${i}`);a.push(`host: ${t.host}`,"",""),n.write(a.join(`\r
8
- `)),o.length>0&&n.write(o),n.pipe(r),r.pipe(n)});n.setTimeout(Qr,()=>n.destroy()),n.on("error",()=>r.destroy()),r.on("error",()=>n.destroy())},qr=async e=>{const r=e.host??"127.0.0.1",o=r==="127.0.0.1"||r==="localhost"||r==="::1",t=new URL(e.workerOrigin);let n=U(e.logger,import.meta.url),a=V(import.meta.url);const d=Xe(e.cwd,()=>Buffer.from(Je({adminToken:o?Ye(e.cwd):void 0,advisorProcedures:o,basePath:"/",dataEditable:o,rulesInstalled:te(e.cwd).installed,runAsIdentity:o,schemaEditable:o,scriptSrc:"/studio.js",styleHref:"/styles.css"}))),s=(c,l,f,w,v)=>{dr(c,l,f,w,v,a)},i=(c,l,f)=>{c.statusCode=l,c.setHeader("Content-Type","text/plain"),c.end(f)},g=C(e.cwd),h=(c,l,f,w)=>{if(o){rr(c,l,f,e.cwd,{apiSpec:e.apiSpec,lazyFunctions:D(e.lazyFunctions),schemaDirectory:g});return}i(l,403,w)},p=(c,l,f)=>{const w=c==="/styles.css",v=tr(c);if(!w&&!v)return!1;const P=V(import.meta.url);if(P!==a&&(n=U(e.logger,import.meta.url),a=P),n===void 0)return i(f,501,"Lunora studio assets not found — install and build @lunora/studio."),!0;if(w)return s(l,f,n.styles,"text/css; charset=utf-8","styles.css"),!0;const E=c.slice(c.lastIndexOf("/")+1),_=or(E,import.meta.url);return _===void 0?(i(f,404,"Not found"),!0):(s(l,f,_,nr(E),E),!0)};d();const S=hr((c,l)=>{const f=B(c.url??"/");if(o){const v=z(c,e.logger);if(v!==void 0){i(l,403,v);return}}if(f.startsWith(Z)){if(!o){i(l,403,"Lunora studio: the worker admin proxy is only available on a loopback bind.");return}q(c,l,t);return}if(f===Ke){h(c,l,ar,"Lunora schema editing is only available on loopback hosts in dev.");return}if(f===Qe){h(c,l,ir,"Lunora policy scaffolding is only available on loopback hosts in dev.");return}if(f===Ze){h(c,l,sr,"Lunora data seeding is only available on loopback hosts in dev.");return}if(f===qe){h(c,l,cr,"Lunora advisor data is only available on loopback hosts in dev.");return}if(p(f,c,l))return;const w=c.headers["sec-fetch-mode"];if(o&&typeof w=="string"&&w!=="navigate"){q(c,l,t);return}er(l,d())});return S.on("upgrade",(c,l,f)=>{if(!o||z(c,e.logger)!==void 0){l.destroy();return}B(c.url??"").startsWith(Z)?Zr(c,l,f,t):l.destroy()}),await new Promise((c,l)=>{S.once("error",f=>{f.code==="EADDRINUSE"?l(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})):l(f)}),S.listen(e.port,r,()=>{c()})}),{close:()=>new Promise(c=>{S.close(()=>{c()})}),url:`http://${r==="0.0.0.0"||r==="::"?"localhost":r}:${String(e.port)}`}},et=async e=>{const{cwd:r,logger:o,origin:t,signal:n}=e,a=e.probe??Pr,d=br(e.readyTimeoutMs),s=Date.now()+d,i=()=>n.aborted;for(;!i()&&Date.now()<s;){let g=!1;try{g=await a(t,n)}catch{}if(i()||Date.now()>=s)break;if(g)return oe(r,{readyAt:new Date().toISOString()},{expectedPid:process.pid}),Se(r)?.readyAt!==void 0;try{await Er(Rr,void 0,{signal:n})}catch{}}return i()||o.warn(`server did not answer on ${t} within ${String(Math.round(d/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},rt=(e,r,o,t)=>{e.info(""),e.success("Lunora dev"),e.info(` ➜ Worker: ${r.workerOrigin}`),o!==void 0&&e.info(` ➜ Studio: ${o}`),r.runsCodegenWatch&&e.info(" ➜ Codegen: watching lunora/"),t!==void 0&&e.info(` ➜ Supervisor: ${t} (needs) · ${ie} (status)`),r.remote.enabled&&(r.remote.bindings.length>0?e.info(` ➜ Remote: ${r.remote.bindings.join(", ")} → deployed worker`):r.remote.reason!==void 0&&e.warn(` ➜ Remote: requested but inactive (${r.remote.reason}) — running fully local`));for(const n of r.remote.withheld)e.warn(` ➜ ${xe(n)}`);e.info("")},tt=(e,r)=>{te(r).installed||!Ne()||(e.info(` ⓘ ${je}`),e.info(""))},ot=async(e,r,o)=>{let t;try{t=ke(await Te({cache:o,projectRoot:r,schemaDir:C(r)}))}catch{}if(t!==void 0)try{const a=(e.ensureExample??$e)(r,t);if(a.length>0){const d=t.map(s=>s.includes(":")?`${s.replace(":"," (")})`:s);e.logger.info(`Updated .dev.vars.example with secrets for: ${d.join(", ")} (${a.join(", ")})`)}}catch{}(await(e.ensureEnv??Ce)({confirm:Sr(),cwd:r,info:a=>{e.logger.info(a)}})).status==="declined"&&!Ae()&&e.logger.info(`hint: ${H} was not scaffolded (non-interactive run). Copy ${Oe} → ${H} and fill in secrets, or run \`lunora dev\` in an interactive terminal to scaffold automatically.`);try{(e.fillSecrets??Fe)({cwd:r,secretSources:t,info:a=>{e.logger.info(a)}})}catch{}},nt=(e,r,o,t,n)=>{if(e.flavor!=="wrangler")return;t!==void 0&&oe(r,{studioUrl:t});let a;try{a=Mr(r,o)}catch{}return rt(o,e,t,n),a},at=(e,r)=>{const o=Number(process.env[I]),t=_e(r,{background:process.env[ne]==="1",logFile:process.env[De],mode:"cli",pid:process.pid,startedAt:new Date().toISOString(),url:e.workerOrigin},Number.isInteger(o)&&o>0?{supersedePid:o}:void 0);return t.ok?void 0:t.existing},it=e=>{const{cwd:r,destination:o,logger:t,plan:n}=e,a=o!==void 0,d=o??Le,s=Ve({destination:d,dev:{...n.flavor==="wrangler"?{origin:n.workerOrigin}:{},statusFile:ie},logger:a?t:{...t,success:()=>{},warn:()=>{}},projectRoot:r});return s.error!==void 0?a?s:(t.debug?.(`skipped the default binding manifest: ${s.error}`),{}):{written:d}},st=async(e,r,o,t)=>{if(r.studioEnabled)try{return await(e.startStudio??qr)({apiSpec:e.apiSpec,lazyFunctions:D(e.lazyFunctions),cwd:o,logger:{warnOnce:n=>{t.warn(n)}},port:r.studioPort,workerOrigin:r.workerOrigin})}catch(n){t.warn(`studio server failed to start (${n instanceof Error?n.message:String(n)}) — continuing without it`);return}},ct=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}.`},dt=(e,r,o,t,n)=>{if(!(e.sidecar===void 0||!Nr(r)))try{re({apiSpec:r.apiSpec,lazyFunctions:D(r.lazyFunctions),lunoraDirectory:C(o),projectRoot:o,target:n,...se(o)})}catch(a){t.warn(`codegen (pre-sidecar) failed: ${a instanceof Error?a.message:String(a)} — the framework dev server will retry`)}},lt=e=>e.flavor==="vite"?"starting vite dev (worker + studio + codegen run inside Vite via @lunora/vite)":`starting ${e.wrangler.tag} dev + studio`,ut=(e,r)=>{e.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.")},ft=async(e,r,o,t)=>{if(e.celldSession!==!0||e.workerEnabled)return!0;try{for(const n of await(r.writeCelldProjection??Ie)({projectRoot:o}))t.warn(n);return!0}catch(n){return t.error(`could not write the celld projection: ${n instanceof Error?n.message:String(n)}`),!1}},gt=async(e,r,o,t,n)=>{try{return e.celldSession===!0?await jr({logger:n,port:e.workerPort,projectRoot:t,start:r.startCelldSession}):o(e.wrangler,n)}catch(a){n.error(`could not start the worker: ${a instanceof Error?a.message:String(a)}`);return}},vt=async(e,r,o)=>{const t=await(e.startHyperdrive??He)({onWarning:n=>{o.warn(n)},projectRoot:r});return t!==void 0&&t.bindings.length>0&&o.info(`Hyperdrive ${t.bindings.join(", ")}: embedded Postgres (PGlite), data in .wrangler/state/lunora-pglite. Set CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_<BINDING> to use your own.`),t},mt=async e=>{const{logger:r}=e,o=Pe(),t=e.cwd??process.cwd(),n=e.flavor??Tr(t),a=vr(t,e.target);if(a.target===void 0)throw new Error(a.error??"unknown deploy target");const{target:d}=a,s=$r(d,n,r);s==="wrangler"&&await Wr(t,r,void 0,d,void 0,o);const i=await Cr({...e,flavor:s,target:d}),g={remoteCleanup:i.remote.cleanup,serviceConfigCleanup:i.serviceConfigCleanup};try{const h=Number(process.env[I]),p=ae(t);if(p!==void 0&&p.pid!==process.pid&&p.pid!==h)return j(r,p),{code:0,plan:i};const S=at(i,t);if(S!==void 0)return j(r,S),{code:0,plan:i};(i.flavor==="vite"||i.flavor==="framework-worker")&&(i.wrangler.env={...i.wrangler.env,[I]:String(process.pid)}),i.workerEnabled&&d==="cloudflare"&&(g.hyperdrive=await vt(e,t,r)),await ot(e,t,o),r.info(lt(i)),Ar(r,t),ut(i,r),i.runsCodegenWatch&&(g.codegen=(e.startCodegen??Yr)({apiSpec:e.apiSpec,lazyFunctions:D(e.lazyFunctions),jsonLogs:e.jsonLogs,logger:r,lunoraDirectory:C(t),projectRoot:t,target:d})),g.studio=await st(e,i,t,r);const c=it({cwd:t,destination:e.emitBindings,logger:r,plan:i});if(c.error!==void 0)return r.error(c.error),{code:$.USAGE,plan:i};await g.codegen?.ready;const l=g.studio?.url;i.frameworkHint!==void 0&&r.warn(i.frameworkHint);const f=()=>{i.flavor==="wrangler"&&(g.readyProbe=new AbortController,et({cwd:t,logger:r,origin:i.workerOrigin,probe:e.probeReady,signal:g.readyProbe.signal}))};if(!await ft(i,e,t,r))return{code:$.FAILURE,plan:i};if(!i.workerEnabled)return f(),r.info(ct(i)),g.tunnel=X({cwd:t,logger:r,plan:i,tunnel:e.tunnel}),{code:await(e.waitForInterrupt??Or)(r),plan:i};dt(i,e,t,r,d);const w=e.startWorker??Fr,v=await gt(i,e,w,t,r);if(v===void 0)return{code:$.FAILURE,plan:i};const P=i.sidecar===void 0?void 0:w(i.sidecar,r);return f(),g.containerLogs=nt(i,t,r,l,c.written),g.tunnel=X({cwd:t,logger:r,plan:i,tunnel:e.tunnel}),tt(r,t),{code:await Lr(v,P,r),plan:i}}finally{g.readyProbe?.abort(),Re(t,process.pid),await Ir(g)}},ht=e=>({codegen:e.codegen===!1?!1:void 0,studio:e.studio===!1?!1:void 0,worker:e.worker===!1?!1:void 0}),wt=e=>{if(e.local===!0&&e.remote===!0)return"`--local` and `--remote` are mutually exclusive — pass at most one.";const{entries:r,invalid:o}=de(e.allowMail);if(o.length>0)return`\`--allow-mail\` takes email addresses or '*@domain' — not ${o.map(t=>`"${t}"`).join(", ")}.`;if(r.length>0&&e.tunnel!==!0)return"`--allow-mail` only applies to a tunnel — add `--tunnel`."},yt=e=>e.tunnel===!0?{allowMail:de(e.allowMail).entries}:void 0,Vt=gr(async({argument:e,cwd:r,logger:o,options:t})=>{const n=t.json===!0,a=_r({cwd:r,json:n,lines:t.lines,logger:o,subcommand:e[0]});if(a!==void 0)return a;const d=process.env[ne]==="1",s=d?void 0:Ee(),i=n||s!==void 0;i&&mr(),s!==void 0&&t.background!==!0&&o.info(`AI agent detected (${s.name} via ${s.variable}) — starting the dev server in background mode with JSON logs. Set LUNORA_AGENT_MODE=0 to opt out.`);const g=wt(t);if(g!==void 0)return o.error(`dev: ${g}`),{code:$.USAGE};const h=yt(t),p=t.local===!0?!1:We({configPreference:be(r),envValue:process.env.LUNORA_REMOTE,flag:t.remote});if(!d&&(t.background===!0||s!==void 0)){const S=ae(r);return S!==void 0?(j(o,S),{code:0}):(h?.allowMail.length===0&&Dr(o),kr({cwd:r,jsonLogs:i,logger:o,options:t,remote:p}))}return mt({apiSpec:Ue(t.apiSpec),cwd:r,emitBindings:t.emitBindings,inspectorPort:t.inspectorPort,lazyFunctions:D(t.lazyFunctions),jsonLogs:i,local:t.local,logger:o,port:t.port,remote:p,target:t.target,tunnel:h,workerPort:t.workerPort,...ht(t)})});export{wt as devFlagConflict,Vt as execute,ht as negatableDevFlags,mt as runDevCommand};
5
+ ${Jt(o)}`,...n,...a].join("\0")},Kt=(e,t)=>{let o,r;return{drop:()=>{o=void 0},get:()=>{const n=Yt(e,t);return o===void 0||n!==r?(r=n,o=me(t)):he(o,t),o}}},Qt=(e,t,o,r)=>{const{apiSpec:n,lazyFunctions:a,logger:c,projectRoot:s,target:i}=e;try{const g=D(s,t,"schema.ts");if(!A(g))throw new vt("INTERNAL",`schema.ts not found at ${g}`);const h=te({apiSpec:n,lazyFunctions:k(a),lunoraDirectory:t,project:r.get(),projectRoot:s,target:i,...se(s)});c.success(`codegen: wrote ${t}/_generated (${o})`),ht(h.platformDiagnostics,c)}catch(g){return r.drop(),c.error(mt(g,o)),!1}return!0},K=e=>{const t=Je("sha256");for(const o of e){if(t.update(o).update("\0"),A(o)){t.update("present\0");try{t.update(ce(o))}catch{t.update("unreadable")}}else t.update("absent");t.update("\0")}return t.digest("hex")},Q=e=>e instanceof Error?e.stack??e.message:String(e),Zt=(e,t,o)=>{const r=[];for(const n of new Set([...e].map(a=>M(a))))if(A(n))try{const a=N(n,(c,s)=>{typeof s!="string"?t(void 0):e.has(D(n,s))&&t(s)});a.on("error",c=>{o(`codegen watch: stopped watching ${n} for lunora.config changes
6
+ ${Q(c)}`),a.close()}),r.push(a)}catch(a){o(`codegen watch: could not watch ${n} for lunora.config changes
7
+ ${Q(a)}`)}return r},qt=e=>{const t=e.lunoraDirectory??"lunora",o=D(e.projectRoot,t),r=e.debounceMs??Bt;let n=!1,a=!1,c=!1,s,i=0,g;const h=[...ee(o),...Ve.map(u=>D(e.projectRoot,u))];let p=Promise.resolve();const S=Kt(e.projectRoot,o);let d;const l=new Promise(u=>{d=u}),f=(u,m)=>{n||(s={codegen:m||s?.codegen===!0,reason:u},!a&&(a=!0,p=(async()=>{try{for(;s!==void 0;){const y=s;if(s=void 0,y.codegen&&(g=K(h)),!(y.codegen&&!Qt(e,t,y.reason,S))){c=!0;try{(await pe({cwd:e.projectRoot,logger:e.logger,spawner:e.spawner,stdoutToStderr:e.jsonLogs})).ran&&(i=Date.now())}finally{c=!1}}}}catch(y){e.logger.error(`codegen watch: ${y instanceof Error?y.message:String(y)}`)}finally{a=!1,d()}})()))};f("startup",!0);const w=u=>(e.logger.warn(`codegen watch unavailable (${u}) — schema edits will NOT auto-regenerate. Run \`lunora codegen\` manually after each edit.`),{close:()=>(n=!0,s=void 0,p),ready:l,watchAvailable:!1});let v,R;if(!A(o))return w(`no such directory: ${o}`);const E=new Set;let _=!1;const T=(u,m)=>{if(c||Date.now()-i<L){v&&(clearTimeout(v),v=void 0);let y=!1;for(const b of[...E,u])b!==void 0&&x(b)&&(y=!0);E.clear(),_=!1,y&&f(`change: ${m}`,!1);return}u===void 0?_=!0:E.add(u),v&&clearTimeout(v),v=setTimeout(()=>{const y=[...E].map(ge=>x(ge)),b=_||y.includes(!1);E.clear(),_=!1,f(`change: ${m}`,b)},r)},O=[],F=[],ue=new Set(h);let C;const W=u=>{if(!n){if(!c&&Date.now()-i>=L){clearTimeout(C),(u===void 0||K(h)!==g)&&T(void 0,u??"?");return}clearTimeout(C),C=setTimeout(()=>{W(u)},L)}};try{R=N(o,{recursive:!0},(u,m)=>{if(typeof m!="string"){T(void 0,"?");return}m.split(J).includes("_generated")||m.endsWith(Ke)||T(D(o,m),m)});for(const u of ve(e.projectRoot).services)if(u.rpcEntrypoint!==void 0){const m=N(M(u.wranglerPath),{recursive:!0},(y,b)=>{typeof b=="string"&&zt.test(b)&&!b.split(J).includes("node_modules")&&T(void 0,b)});m.on("error",y=>{e.logger.warn(`codegen watch: stopped watching service "${u.name}" (${y.message})`),m.close()}),O.push(m)}F.push(...Zt(ue,u=>{W(u)},u=>{e.logger.warn(u)}))}catch(u){R?.close();for(const m of O)m.close();for(const m of F)m.close();return w(u instanceof Error?u.message:String(u))}const fe=R;return{close:()=>{n=!0,s=void 0,v&&clearTimeout(v),clearTimeout(C),fe.close();for(const u of O)u.close();for(const u of F)u.close();return p},ready:l,watchAvailable:!0}},Z="/_lunora",er=3e4,q=(e,t,o,r=er)=>{const n=Et({headers:{...e.headers,host:o.host},hostname:o.hostname,method:e.method,path:e.url,port:o.port},a=>{n.setTimeout(0),t.writeHead(a.statusCode??502,a.headers),Rt(a,t,()=>{})});n.setTimeout(r,()=>{n.destroy(new Error(`no response within ${String(r)}ms`))}),n.on("error",a=>{if(t.headersSent){t.destroy();return}t.statusCode=a.message.startsWith("no response within")?504:502,t.end(`lunora dev: worker unreachable (${a.message})`)}),t.on("close",()=>{t.writableFinished||n.destroy()}),e.pipe(n)},tr=5e3,rr=(e,t,o,r)=>{const n=bt({host:r.hostname,port:Number(r.port)},()=>{n.setTimeout(0);const a=[`${e.method??"GET"} ${e.url??"/"} HTTP/1.1`];for(const[c,s]of Object.entries(e.headers))if(c!=="host")for(const i of Array.isArray(s)?s:[s])i!==void 0&&a.push(`${c}: ${i}`);a.push(`host: ${r.host}`,"",""),n.write(a.join(`\r
8
+ `)),o.length>0&&n.write(o),n.pipe(t),t.pipe(n)});n.setTimeout(tr,()=>n.destroy()),n.on("error",()=>t.destroy()),t.on("error",()=>n.destroy())},or=async e=>{const t=e.host??"127.0.0.1",o=t==="127.0.0.1"||t==="localhost"||t==="::1",r=new URL(e.workerOrigin);let n=V(e.logger,import.meta.url),a=U(import.meta.url);const c=Qe(e.cwd,()=>Buffer.from(Ze({adminToken:o?qe(e.cwd):void 0,advisorProcedures:o,basePath:"/",dataEditable:o,rulesInstalled:re(e.cwd).installed,runAsIdentity:o,schemaEditable:o,scriptSrc:"/studio.js",styleHref:"/styles.css"}))),s=(d,l,f,w,v)=>{gt(d,l,f,w,v,a)},i=(d,l,f)=>{d.statusCode=l,d.setHeader("Content-Type","text/plain"),d.end(f)},g=P(e.cwd),h=(d,l,f,w)=>{if(o){at(d,l,f,e.cwd,{apiSpec:e.apiSpec,lazyFunctions:k(e.lazyFunctions),schemaDirectory:g});return}i(l,403,w)},p=(d,l,f)=>{const w=d==="/styles.css",v=it(d);if(!w&&!v)return!1;const R=U(import.meta.url);if(R!==a&&(n=V(e.logger,import.meta.url),a=R),n===void 0)return i(f,501,"Lunora studio assets not found — install and build @lunora/studio."),!0;if(w)return s(l,f,n.styles,"text/css; charset=utf-8","styles.css"),!0;const E=d.slice(d.lastIndexOf("/")+1),_=st(E,import.meta.url);return _===void 0?(i(f,404,"Not found"),!0):(s(l,f,_,ct(E),E),!0)};c();const S=St((d,l)=>{const f=B(d.url??"/");if(o){const v=z(d,e.logger);if(v!==void 0){i(l,403,v);return}}if(f.startsWith(Z)){if(!o){i(l,403,"Lunora studio: the worker admin proxy is only available on a loopback bind.");return}q(d,l,r);return}if(f===et){h(d,l,dt,"Lunora schema editing is only available on loopback hosts in dev.");return}if(f===tt){h(d,l,lt,"Lunora policy scaffolding is only available on loopback hosts in dev.");return}if(f===rt){h(d,l,ut,"Lunora data seeding is only available on loopback hosts in dev.");return}if(f===ot){h(d,l,ft,"Lunora advisor data is only available on loopback hosts in dev.");return}if(p(f,d,l))return;const w=d.headers["sec-fetch-mode"];if(o&&typeof w=="string"&&w!=="navigate"){q(d,l,r);return}nt(l,c())});return S.on("upgrade",(d,l,f)=>{if(!o||z(d,e.logger)!==void 0){l.destroy();return}B(d.url??"").startsWith(Z)?rr(d,l,f,r):l.destroy()}),await new Promise((d,l)=>{S.once("error",f=>{f.code==="EADDRINUSE"?l(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})):l(f)}),S.listen(e.port,t,()=>{d()})}),{close:()=>new Promise(d=>{S.close(()=>{d()})}),url:`http://${t==="0.0.0.0"||t==="::"?"localhost":t}:${String(e.port)}`}},nr=async e=>{const{cwd:t,logger:o,origin:r,signal:n}=e,a=e.probe??Pt,c=kt(e.readyTimeoutMs),s=Date.now()+c,i=()=>n.aborted;for(;!i()&&Date.now()<s;){let g=!1;try{g=await a(r,n)}catch{}if(i()||Date.now()>=s)break;if(g)return oe(t,{readyAt:new Date().toISOString()},{expectedPid:process.pid}),Se(t)?.readyAt!==void 0;try{await _t(Tt,void 0,{signal:n})}catch{}}return i()||o.warn(`server did not answer on ${r} within ${String(Math.round(c/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},ar=(e,t,o,r,n)=>{e.info(""),e.success("Lunora dev"),e.info(` ➜ Worker: ${t.workerOrigin}`),r!==void 0&&e.info(` ➜ Studio: ${r}`),t.runsCodegenWatch&&e.info(" ➜ Codegen: watching lunora/");for(const{label:a,text:c}of He({projectRoot:o,schemaDirectory:P(o),studioUrl:r}))e.info(xe(a,c,12));n!==void 0&&e.info(` ➜ Supervisor: ${n} (needs) · ${ie} (status)`),t.remote.enabled&&(t.remote.bindings.length>0?e.info(` ➜ Remote: ${t.remote.bindings.join(", ")} → deployed worker`):t.remote.reason!==void 0&&e.warn(` ➜ Remote: requested but inactive (${t.remote.reason}) — running fully local`));for(const a of t.remote.withheld)e.warn(` ➜ ${ze(a)}`);e.info("")},ir=(e,t)=>{re(t).installed||!Me()||(e.info(` ⓘ ${We}`),e.info(""))},sr=async(e,t,o)=>{if(o.flavor==="wrangler")try{const r=P(t),n=await Le({projectRoot:t,schemaDirectory:r,vars:{WORKER_ENV:"development"},withheld:o.remote.withheld.map(c=>c.binding)}),a=Ie(n,r);a!==void 0&&e[a.level](a.message)}catch{}},cr=async(e,t,o)=>{let r;try{r=Pe(await Te({cache:o,projectRoot:t,schemaDir:P(t)}))}catch{}if(r!==void 0)try{const a=(e.ensureExample??Ce)(t,r);if(a.length>0){const c=r.map(s=>s.includes(":")?`${s.replace(":"," (")})`:s);e.logger.info(`Updated .dev.vars.example with secrets for: ${c.join(", ")} (${a.join(", ")})`)}}catch{}const n=await(e.ensureEnv??$e)({confirm:Dt(),cwd:t,info:a=>{e.logger.info(a)},warn:a=>{e.logger.warn(a)}});if(n.status==="declined"&&!Ae()&&e.logger.info(`hint: ${H} was not scaffolded (non-interactive run). Copy ${Oe} → ${H} and fill in secrets, or run \`lunora dev\` in an interactive terminal to scaffold automatically.`),n.status!=="unreadable")try{(e.fillSecrets??Fe)({cwd:t,secretSources:r,info:a=>{e.logger.info(a)}})}catch{}},dr=(e,t,o,r,n)=>{if(e.flavor!=="wrangler")return;r!==void 0&&oe(t,{studioUrl:r});let a;try{a=Vt(t,o)}catch{}return ar(o,e,t,r,n),a},lr=(e,t)=>{const o=Number(process.env[I]),r=_e(t,{background:process.env[ne]==="1",logFile:process.env[ke],mode:"cli",pid:process.pid,startedAt:new Date().toISOString(),url:e.workerOrigin},Number.isInteger(o)&&o>0?{supersedePid:o}:void 0);return r.ok?void 0:r.existing},ur=e=>{const{cwd:t,destination:o,logger:r,plan:n}=e,a=o!==void 0,c=o??Ne,s=Xe({destination:c,dev:{...n.flavor==="wrangler"?{origin:n.workerOrigin}:{},statusFile:ie},logger:r,projectRoot:t,quiet:!a});return s.error!==void 0?a?s:(r.debug?.(`skipped the default binding manifest: ${s.error}`),{}):{written:c}},fr=async(e,t,o,r)=>{if(t.studioEnabled)try{return await(e.startStudio??or)({apiSpec:e.apiSpec,lazyFunctions:k(e.lazyFunctions),cwd:o,logger:{warnOnce:n=>{r.warn(n)}},port:t.studioPort,workerOrigin:t.workerOrigin})}catch(n){r.warn(`studio server failed to start (${n instanceof Error?n.message:String(n)}) — continuing without it`);return}},gr=e=>{const t=[e.runsCodegenWatch?"codegen watch":void 0,e.studioEnabled?"studio":void 0].filter(r=>r!==void 0);return`--no-worker: not starting wrangler. ${t.length>0?`${t.join(" + ")} running`:"nothing else to run"}; your task runner owns the worker on ${e.workerOrigin}.`},vr=(e,t,o,r,n)=>{if(!(e.sidecar===void 0||!Ht(t)))try{te({apiSpec:t.apiSpec,lazyFunctions:k(t.lazyFunctions),lunoraDirectory:P(o),projectRoot:o,target:n,...se(o)})}catch(a){r.warn(`codegen (pre-sidecar) failed: ${a instanceof Error?a.message:String(a)} — the framework dev server will retry`)}},mr=e=>e.flavor==="vite"?"starting vite dev (worker + studio + codegen run inside Vite via @lunora/vite)":`starting ${e.wrangler.tag} dev + studio`,hr=(e,t)=>{e.ipv4LoopbackForced&&t.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.")},wr=async(e,t,o,r)=>{if(e.celldSession!==!0||e.workerEnabled)return!0;try{for(const n of await(t.writeCelldProjection??je)({projectRoot:o}))r.warn(n);return!0}catch(n){return r.error(`could not write the celld projection: ${n instanceof Error?n.message:String(n)}`),!1}},yr=async(e,t,o,r,n)=>{try{return e.celldSession===!0?await xt({logger:n,port:e.workerPort,projectRoot:r,start:t.startCelldSession}):o(e.wrangler,n)}catch(a){n.error(`could not start the worker: ${a instanceof Error?a.message:String(a)}`);return}},pr=async(e,t,o)=>{const r=await(e.startHyperdrive??Be)({onWarning:n=>{o.warn(n)},projectRoot:t});return r!==void 0&&r.bindings.length>0&&o.info(`Hyperdrive ${r.bindings.join(", ")}: embedded Postgres (PGlite), data in .wrangler/state/lunora-pglite. Set CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_<BINDING> to use your own.`),r},Sr=async e=>{const{logger:t}=e,o=Re(),r=e.cwd??process.cwd(),n=e.flavor??Ot(r),a=yt(r,e.target);if(a.target===void 0)throw new Error(a.error??"unknown deploy target");const{target:c}=a,s=Ft(c,n,t);s==="wrangler"&&await Ut(r,t,void 0,c,void 0,o);const i=await Lt({...e,flavor:s,target:c}),g={remoteCleanup:i.remote.cleanup,serviceConfigCleanup:i.serviceConfigCleanup};try{const h=Number(process.env[I]),p=ae(r);if(p!==void 0&&p.pid!==process.pid&&p.pid!==h)return j(t,p),{code:0,plan:i};const S=lr(i,r);if(S!==void 0)return j(t,S),{code:0,plan:i};(i.flavor==="vite"||i.flavor==="framework-worker")&&(i.wrangler.env={...i.wrangler.env,[I]:String(process.pid)}),i.workerEnabled&&c==="cloudflare"&&(g.hyperdrive=await pr(e,r,t)),await cr(e,r,o),await sr(t,r,i),t.info(mr(i)),It(t,r),hr(i,t),i.runsCodegenWatch&&(g.codegen=(e.startCodegen??qt)({apiSpec:e.apiSpec,lazyFunctions:k(e.lazyFunctions),jsonLogs:e.jsonLogs,logger:t,lunoraDirectory:P(r),projectRoot:r,target:c})),g.studio=await fr(e,i,r,t);const d=ur({cwd:r,destination:e.emitBindings,logger:t,plan:i});if(d.error!==void 0)return t.error(d.error),{code:$.USAGE,plan:i};await g.codegen?.ready;const l=g.studio?.url;i.frameworkHint!==void 0&&t.warn(i.frameworkHint);const f=()=>{i.flavor==="wrangler"&&(g.readyProbe=new AbortController,nr({cwd:r,logger:t,origin:i.workerOrigin,probe:e.probeReady,signal:g.readyProbe.signal}))};if(!await wr(i,e,r,t))return{code:$.FAILURE,plan:i};if(!i.workerEnabled)return f(),t.info(gr(i)),g.tunnel=X({cwd:r,logger:t,plan:i,tunnel:e.tunnel}),{code:await(e.waitForInterrupt??Nt)(t),plan:i};vr(i,e,r,t,c);const w=e.startWorker??jt,v=await yr(i,e,w,r,t);if(v===void 0)return{code:$.FAILURE,plan:i};const R=i.sidecar===void 0?void 0:w(i.sidecar,t);return f(),g.containerLogs=dr(i,r,t,l,d.written),g.tunnel=X({cwd:r,logger:t,plan:i,tunnel:e.tunnel}),ir(t,r),{code:await Mt(v,R,t),plan:i}}finally{g.readyProbe?.abort(),De(r,process.pid),await Wt(g)}},Er=e=>({codegen:e.codegen===!1?!1:void 0,studio:e.studio===!1?!1:void 0,worker:e.worker===!1?!1:void 0}),br=e=>{if(e.local===!0&&e.remote===!0)return"`--local` and `--remote` are mutually exclusive — pass at most one.";const{entries:t,invalid:o}=de(e.allowMail);if(o.length>0)return`\`--allow-mail\` takes email addresses or '*@domain' — not ${o.map(r=>`"${r}"`).join(", ")}.`;if(t.length>0&&e.tunnel!==!0)return"`--allow-mail` only applies to a tunnel — add `--tunnel`."},Rr=e=>e.tunnel===!0?{allowMail:de(e.allowMail).entries}:void 0,Jr=wt(async({argument:e,cwd:t,logger:o,options:r})=>{const n=r.json===!0,a=Ct({cwd:t,json:n,lines:r.lines,logger:o,subcommand:e[0]});if(a!==void 0)return a;const c=process.env[ne]==="1",s=c?void 0:Ee(),i=n||s!==void 0;i&&pt(),s!==void 0&&r.background!==!0&&o.info(`AI agent detected (${s.name} via ${s.variable}) — starting the dev server in background mode with JSON logs. Set LUNORA_AGENT_MODE=0 to opt out.`);const g=br(r);if(g!==void 0)return o.error(`dev: ${g}`),{code:$.USAGE};const h=Rr(r),p=r.local===!0?!1:Ue({configPreference:be(t),envValue:process.env.LUNORA_REMOTE,flag:r.remote});if(!c&&(r.background===!0||s!==void 0)){const S=ae(t);return S!==void 0?(j(o,S),{code:0}):(h?.allowMail.length===0&&$t(o),At({cwd:t,jsonLogs:i,logger:o,options:r,remote:p}))}return Sr({apiSpec:Ge(r.apiSpec),cwd:t,emitBindings:r.emitBindings,inspectorPort:r.inspectorPort,lazyFunctions:k(r.lazyFunctions),jsonLogs:i,local:r.local,logger:o,port:r.port,remote:p,target:r.target,tunnel:h,workerPort:r.workerPort,...Er(r)})});export{br as devFlagConflict,Jr as execute,Er as negatableDevFlags,Sr as runDevCommand};
@@ -0,0 +1,2 @@
1
+ import{mkdirSync as l,writeFileSync as g}from"node:fs";import{findWranglerFile as f,readWranglerJsonc as m,buildBindingManifest as c}from"@lunora/config/cloudflare";import{isAbsolute as h,resolve as u,dirname as v}from"@visulima/path";const w=i=>{const n=f(i),r=n===void 0?void 0:m(n).parsed;return r===void 0?{error:`no readable wrangler config in ${i}. The binding manifest is derived from it, and an empty one would tell a deployer this Worker needs nothing.`}:{manifest:c(r)}},y=i=>{const{destination:n,dev:r,logger:t,projectRoot:a,quiet:d=!1}=i,o=w(a);if(o.manifest===void 0)return{error:o.error};const e={...o.manifest,...r===void 0?{}:{dev:r}},s=h(n)?n:u(a,n);return l(v(s),{recursive:!0}),g(s,`${JSON.stringify(e,void 0,2)}
2
+ `,"utf8"),e.malformed.length>0&&t.warn(`wrangler config: ${e.malformed.join(", ")} ${e.malformed.length===1?"is":"are"} not the shape wrangler expects, so the binding manifest leaves ${e.malformed.length===1?"it":"them"} out. Wrangler reports the exact error when it loads the config.`),d?{}:(t.success(`binding manifest written to ${s} (${e.bindings.length.toString()} bindings, ${e.crons.length.toString()} crons)`),e.unknown.length>0&&t.warn(`binding manifest does not model these wrangler sections: ${e.unknown.join(", ")}. Anything they bind must be provisioned by hand — please report them so the manifest can cover them.`),{})};export{w as d,y as w};
@@ -0,0 +1,9 @@
1
+ import{quoteIdentifier as a,sqlAffinityForKind as T,frameworkColumnDdl as y,OCC_VERSION_COLUMN as E,MAX_D1_TABLE_COLUMNS as d,columnRef as L,physicalIndexName as c}from"@lunora/d1/dialect";import{LunoraError as g}from"@lunora/errors";const f="-- lunora:allow-destructive",w=e=>T(e),p=(e,i)=>{const s=[a(e),i.sqlType];return i.nullable||s.push("NOT NULL"),s.join(" ")},O=e=>{const i=Object.entries(e.columns).map(([o,r])=>` ${p(o,r)}`),s=[...y(),`${a(E)} INTEGER`],n=s.length+i.length;if(n>d)throw new g("VALIDATION_ERROR",`table "${e.name}" needs ${String(n)} columns, over D1's ${String(d)}-column limit — split the table, or move the extra fields into one object field`);const t=[...s.map(o=>` ${o}`),...i].join(`,
2
+ `);return`CREATE TABLE IF NOT EXISTS ${a(e.name)} (
3
+ ${t}
4
+ );`},C=e=>`${f}
5
+ DROP TABLE IF EXISTS ${a(e)};`,S=(e,i,s)=>{const n=`ALTER TABLE ${a(e)} ADD COLUMN ${p(i,{...s,nullable:!0})};`;return s.nullable?n:`${n}
6
+ -- ${a(e)}.${a(i)} is required, so rows written before it read NULL. Backfill them:
7
+ -- UPDATE ${a(e)} SET ${a(i)} = <value> WHERE ${a(i)} IS NULL;`},$=(e,i)=>{const s=i.fields.map(t=>L(t)).join(", ");return`CREATE ${i.unique?"UNIQUE ":""}INDEX IF NOT EXISTS ${c(e,i.name)} ON ${a(e)} (${s});`},I=(e,i)=>`${f}
8
+ DROP INDEX IF EXISTS ${c(e,i)};`,u=e=>e.field===void 0?void 0:JSON.stringify({...e.field,optional:void 0,refined:void 0}),R="https://lunora.sh/docs/concepts/migrations#relaxing-not-null-on-a-d1-table",b=(e,i,s,n,t)=>{const o=u(s),r=u(n);if(s.sqlType!==n.sqlType)t.push({kind:"columnTypeChange",summary:`column type change on ${e}.${i}: ${s.sqlType} → ${n.sqlType} (write SQL manually)`});else if(o!==void 0&&r!==void 0&&o!==r){const l=s.field?.kind===n.field?.kind?`${String(n.field?.kind)} shape/constraints changed`:`${String(s.field?.kind)} → ${String(n.field?.kind)}`;t.push({kind:"columnTypeChange",summary:`column type change on ${e}.${i}: ${l} (write SQL manually)`})}if(s.nullable!==n.nullable){const l=n.nullable?`rebuild the table — ${R}`:"write SQL manually";t.push({kind:"columnTypeChange",summary:`nullability change on ${e}.${i}: ${s.nullable?"NULL":"NOT NULL"} → ${n.nullable?"NULL":"NOT NULL"} (${l})`})}},q=(e,i,s,n,t)=>{for(const[o,r]of Object.entries(s)){const l=i[o];if(l===void 0){n.push({kind:"addColumn",sql:S(e,o,r),summary:`ADD COLUMN ${e}.${o}`});continue}b(e,o,l,r,t)}for(const o of Object.keys(i))s[o]===void 0&&t.push({kind:"dropColumn",summary:`DROP COLUMN ${e}.${o} (SQLite drop-column requires careful migration — write SQL manually)`})},D=(e,i,s,n,t)=>{for(const[o,r]of Object.entries(s)){const l=i[o];if(l===void 0){n.push({kind:"createIndex",sql:$(e,r),summary:`CREATE INDEX ${o} ON ${e}`});continue}(!(l.fields.length===r.fields.length&&l.fields.every((m,h)=>m===r.fields[h]))||l.unique!==r.unique)&&t.push({kind:"indexRename",summary:`index ${o} changed on ${e} — drop+create manually if intentional`})}for(const o of Object.keys(i))s[o]===void 0&&n.push({kind:"dropIndex",sql:I(e,o),summary:`DROP INDEX ${o}`})},N=(e,i,s)=>{s.push({kind:"createTable",sql:O(i),summary:`CREATE TABLE ${e}`});for(const n of Object.values(i.indexes))s.push({kind:"createIndex",sql:$(e,n),summary:`CREATE INDEX ${n.name} ON ${e}`})},U=(e,i)=>{const s=e?.tables??{},n=[],t=[];for(const[o,r]of Object.entries(i.tables)){const l=s[o];if(l===void 0){N(o,r,n);continue}q(o,l.columns,r.columns,n,t),D(o,l.indexes,r.indexes,n,t)}for(const o of Object.keys(s))i.tables[o]===void 0&&n.push({kind:"dropTable",sql:C(o),summary:`DROP TABLE ${o}`});return{empty:n.length===0&&t.length===0,entries:n,unsupported:t}},x=(e,i,s)=>{const n=[`-- Lunora migration: ${e}`,`-- Generated at ${s}`,"-- This file was produced by `lunora migrate generate`. Review carefully before applying.",""];for(const t of i.entries)n.push(`-- ${t.summary}`,t.sql,"");if(i.unsupported.length>0){n.push("-- ---------------------------------------------------------------","-- The following deltas are NOT auto-generated in v0.1.","-- Write the appropriate SQL below by hand:","--");for(const t of i.unsupported)n.push(`-- * ${t.summary}`);n.push("-- ---------------------------------------------------------------","")}return i.empty&&n.push("-- No changes detected. Re-running `lunora migrate generate` will overwrite this file.",""),n.join(`
9
+ `)};export{U as diffSnapshots,S as renderAddColumn,$ as renderCreateIndex,O as renderCreateTable,I as renderDropIndex,C as renderDropTable,x as renderMigrationFile,w as validatorKindToSqlType};
@@ -0,0 +1,5 @@
1
+ import{existsSync as q,mkdirSync as Le,openSync as J,closeSync as X,statSync as Te,readFileSync as Pe,readSync as be}from"node:fs";import{join as N,dirname as _e}from"node:path";import{readDevServiceBindings as Re}from"@lunora/codegen";import{readLiveDevServerState as C,discoverContainerInfo as $e,streamContainerLogs as Ne,formatLunoraEvent as Ce,updateDevServerState as b,resolveLazyFunctions as Q,targetRunsOwnDevServer as D,resolveProjectTarget as Z,claimDevServerState as De,DEV_HANDOFF_ENV as Oe,clearDevServerState as E,readDevServerState as O,isDevServerReady as j,detectFramework as ee,readProjectDependencyNames as xe,DEV_LOG_FILE_ENV as Ae,DEV_DAEMON_ENV as Me,LAZY_FUNCTIONS_ENV as Ue,readProjectManifest as Ge,isRecordedProcessCurrent as Fe,DEV_LOG_FILE as je,CODEGEN_ENV as re,isCodegenDisabled as Ve,planToolchainInvocation as We,startCelldDevSession as Be,resolveDeployDriver as te,DEV_VARS_FILE as ne,readDevVariablesRecord as Ke,resolveSchemaDirectory as ze}from"@lunora/config";import{warnCloudflareCliConfigOnce as He,findWranglerFile as L,readWranglerJsonc as x,materializeDevWranglerConfig as Ye,toRemotePlan as qe,materializeServiceDevConfigs as Je}from"@lunora/config/cloudflare";import{f as Xe}from"./free-port-DjbFhaaT.mjs";import{networkInterfaces as Qe}from"node:os";import{spawn as oe,spawnSync as T}from"node:child_process";import{setTimeout as w}from"node:timers/promises";import{EXIT_CODE as ie}from"./EXIT_CODE-DQVhSE-1.mjs";import{a as h}from"./output-format-DWe_tkTR.mjs";import{spawnShellCompat as se,defaultSpawner as Ze}from"./createRecordingSpawner-DchLi0gR.mjs";import{isLunoraError as er}from"@lunora/errors";import{s as V}from"./index-oHZBGBwB.mjs";import{createInterface as rr}from"node:readline";import{detectPackageManager as A,execArgsFor as ae,toolchainExecArgs as ce,runScriptCommand as tr}from"@lunora/config/cli";const nr=12e4,_=250,or=1e3,ir="LUNORA_DEV_READY_TIMEOUT_MS",sr=e=>{if(e!==void 0)return e;const r=Number(process.env[ir]??"");return Number.isFinite(r)&&r>0?r:nr},ar=async(e,r)=>{try{const n=AbortSignal.timeout(or);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}},le=async(e,r,n)=>{const t=Date.now()+n.timeoutMs;for(;n.signal?.aborted!==!0;){const o=r(C(e));if(o!==void 0)return o;if(Date.now()>=t)return;try{await w(n.intervalMs??500,void 0,{signal:n.signal})}catch{return}}},W=(e,r)=>{rr({crlfDelay:Number.POSITIVE_INFINITY,input:e}).on("line",r)},cr=5e3,de=(e,r,n,t)=>{if(e.length===0)return;const o=Ce(e);if(o){t[o.level](`[lunora] ${o.text}`);return}const i=`[${r}] ${e}`;n==="stderr"?t.warn(i):t.info(i)},lr=(e,r,n=process.platform,t=T)=>{try{if(r==="SIGKILL"&&n==="win32"&&e.pid!==void 0){t("taskkill",["/pid",String(e.pid),"/T","/F"],{stdio:"ignore"});return}e.kill(r)}catch{}},ue=(e,r,n)=>{const t=e.direct===!0?{args:[...e.args],command:e.command,shell:!1}:se(e.command,e.args),o=oe(t.command,t.args,{cwd:e.cwd??process.cwd(),env:e.env?{...process.env,...e.env}:process.env,shell:t.shell,stdio:[e.direct===!0?"ignore":"inherit","pipe","pipe"]});return W(o.stdout,i=>{r(i,"stdout")}),W(o.stderr,i=>{r(i,"stderr")}),{exited:new Promise(i=>{o.on("error",s=>{n?.(s),i(1)}),o.on("exit",(s,a)=>{i(s??(a?1:0))})}),kill:i=>{lr(o,i)}}},pt=(e,r)=>ue(e,(n,t)=>{de(n.trimEnd(),e.tag,t,r)},n=>{r.error(`[${e.tag}] failed to start: ${n.message}`)}),mt=(e,r)=>{if(process.env.LUNORA_CONTAINER_LOGS==="0")return;const t=$e(e,"lunora").containers.map(o=>({className:o.className,exportName:o.exportName}));if(t.length!==0)return Ne({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})`)}})},wt=async e=>{e.readyProbe?.abort(),await e.tunnel?.close(),await e.codegen?.close().catch(()=>{}),e.containerLogs?.close(),await e.studio?.close().catch(()=>{}),await e.hyperdrive?.stop().catch(()=>{});try{e.remoteCleanup?.(),e.serviceConfigCleanup?.()}catch{}},ht=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)}),St=async(e,r,n)=>{let t=0,o;const i=l=>{e.kill(l),r?.kill(l)},s=()=>{t+=1,t===1?(n.info("received SIGINT — shutting down (press Ctrl-C again to force-kill)"),i("SIGTERM"),o=setTimeout(()=>{i("SIGKILL")},cr),o.unref()):i("SIGKILL")},a=()=>{i("SIGTERM")};process.on("SIGINT",s),process.on("SIGTERM",a);const c=await Promise.race([e.exited.then(l=>({code:l,who:"worker"})),...r===void 0?[]:[r.exited.then(l=>({code:l,who:"sidecar"}))]]);return r!==void 0&&(c.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),c.code},y="2026.9.3",dr="https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/",ur=/version (\S+)/u,B=/https:\/\/[\da-z-]+\.trycloudflare\.com/u,fr="1s",vr=5e3,K=async(e,r)=>await Promise.race([e.then(()=>!0),w(r,!1,{ref:!1})]),gr=6e4,pr='Vite answers 403 for hosts it does not know — add `server: { allowedHosts: [".trycloudflare.com"] }` to vite.config if the URL is blocked.',mr=async e=>{const r=`Install or update it: ${dr} (macOS: \`brew install cloudflared\`).`;let n;try{const o=await e({args:["--version"],captureStdoutSilently:!0,command:"cloudflared"});if(o.code!==0)return{message:`\`cloudflared --version\` exited ${String(o.code)}. ${r}`,ok:!1};n=o.stdout??""}catch(o){return er(o)&&o.code==="LOCAL_DEPENDENCY_MISSING"?{message:`--tunnel needs \`cloudflared\` ${y} or newer, and it is not on your PATH. ${r}`,ok:!1}:{message:`\`cloudflared --version\` could not run: ${o instanceof Error?o.message:String(o)}. ${r}`,ok:!1}}const t=V.coerce(ur.exec(n)?.[1])?.version;return t===void 0?{ok:!0,version:void 0}:V.gte(t,y)?{ok:!0,version:t}:{message:`--tunnel needs \`cloudflared\` ${y} or newer (protected quick tunnels); found ${t}. ${r}`,ok:!1}},wr=e=>{const r=[],n=[];for(const t of e??[]){const o=t.split(",").map(i=>i.trim()).filter(i=>i.length>0);o.length>0&&(n.push(...o.filter(i=>!i.includes("@"))),r.push(o.join(",")))}return{entries:r,invalid:n}},hr=(e,r)=>["tunnel","--url",e,"--output","json","--grace-period",fr,...r.flatMap(n=>["--allowed-mail",n])],Sr=e=>{let r;try{r=JSON.parse(e)}catch{r=void 0}if(typeof r=="object"&&r!==null){const n=r,t=typeof n.message=="string"?n.message:"";return{level:typeof n.level=="string"?n.level:void 0,text:t,url:B.exec(t)?.[0]}}return{level:void 0,text:e,url:B.exec(e)?.[0]}},kr=e=>{e.warn(" ⚠ PUBLIC TUNNEL: anyone with the tunnel URL can reach this dev server — there is no sign-in in front of it."),e.warn(" Protect it with a one-time-PIN email allow-list: lunora dev --tunnel --allow-mail you@example.com"),e.warn(" (repeat --allow-mail, pass a comma-separated list, or allow a whole domain with '*@example.com')")},Ir=async(e,r,n,{intervalMs:t,timeoutMs:o=gr}={})=>{const i=await le(e,s=>s?.mode==="vite"?{origin:s.url,ownerPid:s.pid}:void 0,{intervalMs:t,signal:n,timeoutMs:o});return i===void 0&&!n.aborted&&r.warn(`--tunnel: the Vite dev server did not report its URL within ${String(o/1e3)}s — continuing without a tunnel.`),i},yr=e=>{const{allowMail:r,cwd:n,logger:t}=e,o=r.length===0,i=new AbortController,{signal:s}=i,a=()=>s.aborted,c=e.closeTimeoutMs??vr;let l;const d=()=>{i.abort()};process.once("SIGINT",d),process.once("SIGTERM",d);const p=(async()=>{const u=await mr(e.spawner??Ze);if(!u.ok){t.error(u.message),t.warn("continuing without a tunnel.");return}u.version===void 0&&t.warn(`could not read the cloudflared version — --tunnel needs ${y} or newer; trying anyway.`);const v=e.origin.kind==="fixed"?{origin:e.origin.origin,ownerPid:process.pid}:await Ir(n,t,s);if(v===void 0||s.aborted)return;o&&kr(t),t.info(`starting a Cloudflare quick tunnel to ${v.origin}${o?"":" (email one-time-PIN protected)"}…`);let S;const U={failedToStart:!1},Ee=k=>{if(k.length===0)return;const g=Sr(k);if(g.url!==void 0&&S===void 0){S=g.url,t.success(` ➜ Tunnel: ${g.url}${o?" (PUBLIC)":""}`),o||t.info(` Allowed: ${r.join(", ")} — visitors confirm with a one-time PIN; a session lasts up to 4 hours`),e.origin.kind==="vite"&&t.info(` ${pr}`),b(n,{tunnelUrl:g.url},{expectedPid:v.ownerPid});return}g.level==="error"||g.level==="fatal"?t.warn(`[cloudflared] ${g.text}`):t.debug?.(`[cloudflared] ${g.text}`)},P=(e.startChild??ue)({args:hr(v.origin,r),command:"cloudflared",direct:!0},Ee,k=>{U.failedToStart=!0,t.error(`could not start cloudflared (${k.message}) — continuing without a tunnel.`)});l=P;const G=()=>{P.kill("SIGTERM")};s.addEventListener("abort",G,{once:!0});const F=await P.exited;s.removeEventListener("abort",G),l=void 0,S!==void 0&&b(n,{tunnelUrl:void 0},{expectedPid:v.ownerPid}),!(a()||U.failedToStart)&&t.warn(S===void 0?`cloudflared exited (${String(F)}) before a tunnel URL was assigned — continuing without a tunnel.`:`cloudflared exited (${String(F)}) — the tunnel is closed; the dev server keeps running.`)})().catch(u=>{t.error(`--tunnel failed (${u instanceof Error?u.message:String(u)}) — continuing without a tunnel.`)});return{close:async()=>{if(process.off("SIGINT",d),process.off("SIGTERM",d),i.abort(),await K(p,c))return;const u=l;u!==void 0&&(t.warn(`cloudflared did not exit within ${String(c/1e3)}s of SIGTERM — force-killing it.`),u.kill("SIGKILL"),!await K(u.exited,c)&&t.error(`could not confirm that cloudflared stopped — ${o?"the PUBLIC tunnel":"the tunnel"} may still be reachable. Stop the \`cloudflared\` process manually.`))}}},kt=e=>{const{cwd:r,logger:n,plan:t,tunnel:o}=e;if(o!==void 0)return yr({allowMail:o.allowMail,cwd:r,logger:n,origin:t.flavor==="wrangler"?{kind:"fixed",origin:t.workerOrigin}:{kind:"vite"},spawner:o.spawner,startChild:o.startChild})},Er=(e,r)=>{const n=["dev"];if(e.apiSpec!==void 0&&n.push("--api-spec",e.apiSpec),Q(e.lazyFunctions)&&n.push("--lazy-functions"),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),e.tunnel===!0){n.push("--tunnel");for(const t of wr(e.allowMail).entries)n.push("--allow-mail",t)}return r&&n.push("--remote"),e.local===!0&&n.push("--local"),n},Lr=1e4,Tr=100,Pr=40,I=256*1024,br=new Set(["nuxt","sveltekit"]),_r=["@lunora/rspack","@lunora/vite"],M=e=>{if(br.has(ee(e).framework))return"framework-worker";const r=xe(e);return _r.some(n=>r.has(n))?"vite":"wrangler"},fe=e=>{const r=A(e),n=Ge(e)?.scripts,t=typeof n=="object"&&n!==null?n.dev:void 0;if(typeof t!="string"||t.trim()===""||t.includes("lunora")){const o=ae(r,"vite",["dev"]);return{args:o.args,command:o.command}}return{args:["run","dev"],command:r}},Rr=e=>{Le(_e(e.logPath),{recursive:!0});const r=J(e.logPath,"w",384);let n;const t=se(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{X(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}},$r=e=>{try{const{size:r}=Te(e);if(r<=I)return Pe(e,"utf8");const n=J(e,"r");try{const t=Buffer.alloc(I),o=be(n,t,0,I,r-I),i=t.toString("utf8",0,o);return i.slice(i.indexOf(`
2
+ `)+1)}finally{X(n)}}catch{return}},ve=(e,r)=>{const n=$r(e);if(n===void 0)return[];const t=n.split(`
3
+ `);return t.at(-1)===""&&t.pop(),r>0?t.slice(-r):t},ge=(e,r)=>r?.logFile??N(e,je),pe=e=>{e.info(" Stop: lunora dev stop"),e.info(" Status: lunora dev status"),e.info(" Logs: lunora dev logs")},Nr=(e,r)=>{He(r,n=>{e.warn(n)})},Cr=(e,r)=>{e.warn(`Dev server already running at ${r.url} (pid ${String(r.pid)})`),pe(e)},Dr=(e,r)=>{e.success(`Dev server running at ${r.url} (pid ${String(r.pid)})`),r.studioUrl!==void 0&&e.info(` Studio: ${r.studioUrl}`),r.tunnelUrl!==void 0&&e.info(` Tunnel: ${r.tunnelUrl}`),pe(e)},Or=3e4,xr=async(e,r,n,t)=>{const o=await le(e,i=>i?.tunnelUrl,{intervalMs:n,timeoutMs:t});o===void 0?r.warn(" Tunnel: no URL yet — `lunora dev status` shows it once cloudflared reports one; `lunora dev logs` says why if it does not."):r.success(` Tunnel: ${o}`)},Ar=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=C(n);if(a!==void 0&&a.pid!==process.pid&&await i(a.url))return{state:a,status:"ready"};const c=await Promise.race([s,w(o)]);if(c!==void 0)return{...c,status:"exited"}}return{status:"timeout"}},Mr=async e=>{const{cwd:r,logger:n}=e,t=ge(r,void 0),o=sr(e.readyTimeoutMs),i=(e.spawnDetached??Rr)({args:e.command.args,command:e.command.command,cwd:r,env:{...process.env,...e.env,[Me]:"1",[Ae]:t,...process.env.FORCE_COLOR===void 0?{NO_COLOR:"1"}:{},...e.json?{LUNORA_LOG_JSON:"1"}:{}},logPath:t}),s=await Ar({child:i,cwd:r,deadline:Date.now()+o,pollIntervalMs:e.pollIntervalMs??_,probe:e.probe??ar});if(s.status==="ready")return Dr(n,s.state),e.tunnel===!0&&s.state.tunnelUrl===void 0&&await xr(r,n,e.pollIntervalMs??_,e.tunnelUrlTimeoutMs??Or),{code:0};if(s.status==="exited"){n.error(`dev server exited before becoming ready (exit code ${String(s.exitCode)}). Last output:`);for(const c of ve(t,Pr))n.error(` ${c}`);return E(r,i.pid??process.pid),{code:s.exitCode===0?1:s.exitCode}}i.pid!==void 0&&O(r)?.pid===process.pid&&b(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:ie.UNAVAILABLE}},me=e=>e.codegen!==!1&&!Ve(process.env[re]),we=e=>{const r={...Q(e.lazyFunctions)?{[Ue]:"1"}:{},...e.remote===!0?{LUNORA_REMOTE:"1"}:{},...e.codegen===!1?{[re]:"0"}:{}};return Object.keys(r).length>0?{env:r}:{}},It=async e=>{const{cwd:r,jsonLogs:n,logger:t,options:o,remote:i}=e,s=e.run??Mr,a=M(r),c=a==="vite"&&D(o.target??Z(r))?"wrangler":a,l=c==="wrangler"?`http://localhost:${String(o.workerPort??8787)}`:"http://localhost:5173",d=De(r,{background:!0,mode:"cli",pid:process.pid,startedAt:new Date().toISOString(),url:l});if(!d.ok)return d.existing!==void 0&&Cr(t,d.existing),{code:0};const f={[Oe]:String(process.pid)};Nr(t,r);try{return c==="vite"&&o.tunnel!==!0?await s({command:fe(r),cwd:r,env:{...we({...o,remote:i}).env,...f},json:n,logger:t}):await s({command:{args:[process.argv[1]??"lunora",...Er(o,i)],command:process.execPath},cwd:r,env:f,json:n,logger:t,tunnel:o.tunnel===!0})}finally{E(r,process.pid)}},Ur=e=>{try{const r=T("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}},Gr=(e,r,n=process.platform,t=T)=>{if(n==="win32"){try{t("taskkill",["/pid",String(e.pid),"/T","/F"],{stdio:"ignore"})}catch{}return}if(e.background===!0)try{r(-(Ur(e.pid)??e.pid),"SIGKILL");return}catch{}try{r(e.pid,"SIGKILL")}catch{}},Fr=(e,r)=>(r.json?h({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}),jr=async e=>{const{cwd:r,logger:n}=e,t=O(r),o=e.alive??(f=>f===t?.pid&&Fe(t)),i=e.signal??((f,p)=>{process.kill(f,p)}),s=e.pollIntervalMs??_,a=e.stopGraceMs??Lr,c=e.platform??process.platform,l=e.spawnSyncImpl??T;if(t===void 0||!o(t.pid))return t!==void 0&&E(r,t.pid),e.json?h({running:!1,stopped:!1}):n.info("No dev server running."),{code:0};try{i(t.pid,"SIGTERM")}catch{}const d=Date.now()+a;for(;o(t.pid)&&Date.now()<d;)await w(s);return o(t.pid)&&(Gr(t,i,c,l),await w(s),o(t.pid))?Fr(t,e):(E(r,t.pid),e.json?h({pid:t.pid,stopped:!0}):n.success(`Stopped dev server (pid ${String(t.pid)}).`),{code:0})},Vr=e=>{const{cwd:r,logger:n}=e,t=C(r);if(t===void 0)return e.json?h({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 h({background:t.background===!0,logFile:t.logFile,mode:t.mode,pid:t.pid,ready:j(t),readyAt:t.readyAt,running:!0,startedAt:t.startedAt,studioUrl:t.studioUrl,tunnelUrl:t.tunnelUrl,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",j(t)?"ready":"starting"];return n.success(`Dev server running at ${t.url} (${a.join(", ")})`),t.studioUrl!==void 0&&n.info(` Studio: ${t.studioUrl}`),t.tunnelUrl!==void 0&&n.info(` Tunnel: ${t.tunnelUrl}`),t.logFile!==void 0&&n.info(` Logs: lunora dev logs (${t.logFile})`),{code:0}},Wr=e=>{const{cwd:r,logger:n}=e,t=O(r),o=ge(r,t);if(!q(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=ve(o,e.lines??Tr);return i.length>0&&process.stdout.write(`${i.join(`
4
+ `)}
5
+ `),{code:0}},yt=e=>{const{cwd:r,json:n,lines:t,logger:o,subcommand:i}=e;if(i==="stop")return jr({cwd:r,json:n,logger:o});if(i==="status")return Vr({cwd:r,json:n,logger:o});if(i==="logs")return Wr({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:ie.USAGE}},Et=(e,r,n)=>D(e)?(r!=="wrangler"&&n.info(`target ${e} runs its own dev server, so lunora dev serves the worker on it — start the frontend's dev server separately`),"wrangler"):r,Lt=async e=>{const{logger:r}=e,n=await(e.start??Be)({log:(i,{stream:s,tag:a})=>{de(i,a,s,r)},port:e.port,projectRoot:e.projectRoot});let t=()=>{};const o=new Promise(i=>{t=i});return{exited:Promise.race([o,n.exited]),kill:()=>{n.stop().then(()=>0,()=>1).then(t).catch(()=>{})}}},Br=e=>{const{cwd:r,driver:n,options:t,studioPort:o,workerPort:i}=e,{projectConfig:s,toolchain:a}=n;if(s===void 0||a===void 0)throw new Error(`deploy target "${n.id}" runs its own dev server but ships no config projection or toolchain for it`);if(t.remote===!0)throw new Error(`--remote proxies bindings to Cloudflare; ${n.name} has no remote bindings to proxy to`);t.inspectorPort!==void 0&&t.logger.warn(`--inspector-port is a wrangler dev flag; ${n.name} dev has no inspector to pin`);const{command:c,projection:l}=We(n,r,"dev",f=>a.dev({configPath:f,extraArgs:["--port",String(i)]}));l!==void 0&&l.dropped.length>0&&t.logger.info(`${n.name} ignores these wrangler keys, so its dev server runs without them: ${l.dropped.join(", ")}`);const d=ce(A(r),c);return{flavor:"wrangler",ipv4LoopbackForced:!1,remote:{bindings:[],cleanup:()=>{},enabled:!1,withheld:[]},runsCodegenWatch:me(t),celldSession:!0,studioEnabled:t.studio!==!1,studioPort:o,workerEnabled:t.worker!==!1,workerOrigin:`http://localhost:${String(i)}`,workerPort:i,wrangler:{args:d.args,command:d.command,cwd:r,tag:c.tool}}},he=(e=Qe)=>Object.values(e()).some(r=>r?.some(n=>n.internal&&n.address==="::1")),R="wrangler.dev.jsonc",$=6173,m=8787,z=5173,Se=()=>{},Kr=(e,r)=>{if(!e.remote&&e.local===!0)return{args:[],plan:{bindings:[],cleanup:Se,enabled:!1,withheld:[]}};const n=e.materializeDev??Ye,t=qe(n({projectRoot:r,remote:e.remote===!0}),e.remote===!0);return{args:t.configPath===void 0?[]:["--config",t.configPath],plan:t}},ke=(e,r)=>{const n=[...new Set(Re(r,ze(r)).services.map(s=>s.wranglerPath))],{cleanup:t,configPaths:o,withheld:i}=(e.materializeServiceConfigs??Je)(n,{projectRoot:r});return{args:o.flatMap(s=>["--config",s]),cleanup:t,withheld:i}},zr=(e,r,n)=>{const t=L(e);return r.length===0||t===void 0?[]:[...n.length>0?[]:["--config",t],...r]},Ie=e=>e.local===!0?["--local"]:[],H=e=>x(e).parsed?.dev?.ip,ye=(e,r,n)=>{if(n!==void 0){const o=N(e,n);if(q(o)&&H(o)!==void 0)return[]}const t=L(e);return t!==void 0&&H(t)!==void 0?[]:r()?[]:["--ip","127.0.0.1"]},Hr=new Set(["127.0.0.1","::1","[::1]","localhost"]),Yr=(e,r)=>{let n;try{n=Ke(N(e,ne))}catch{return[]}const t=[];for(const[o,i]of Object.entries(n??{})){let s;try{s=new URL(i)}catch{continue}Hr.has(s.hostname)&&s.port===String(r)&&t.push(o)}return t},Y=async(e,r)=>{if(e.workerPort!==void 0)return e.workerPort;const n=L(r);if(n!==void 0){const{parsed:i}=x(n);if(typeof i?.dev?.port=="number")return i.dev.port}const t=await(e.findFreePort??Xe)(m);if(t===m)return t;const o=Yr(r,m);if(o.length>0)throw new Error(`port ${String(m)} is in use, but ${ne} pins the worker origin to it (${o.join(", ")}). Serving on ${String(t)} would leave those URLs pointing at nothing. Stop whatever holds ${String(m)}, or run \`lunora dev --worker-port ${String(t)}\` and update those values to match.`);return t},qr=(e,r)=>{if(e.inspectorPort!==void 0)return e.inspectorPort;const n=L(r);if(n===void 0)return;const{parsed:t}=x(n),o=t?.dev?.inspector_port;return typeof o=="number"?o:void 0},Jr=(e,r,n)=>{const t=ye(r,e.hasIpv6Loopback??he,R),o=te(e.target??Z(r));if(o.toolchain===void 0)throw new Error(`deploy target "${o.id}" has no dev server to run the worker sidecar`);const i=ke(e,r),s=o.toolchain.dev({configPath:R,extraArgs:[...t,...Ie(e),"--var","WORKER_ENV:development",...i.args]}),a=ce(n,s);return{cleanup:i.cleanup,sidecar:{args:a.args,command:a.command,cwd:r,tag:"worker"},withheld:i.withheld}},Xr=(e,r)=>{e.worker===!1&&e.logger.warn(`--no-worker does not apply to the ${r} flavor: Vite owns the worker, codegen and studio in-process. Run your framework's dev script instead.`),e.inspectorPort!==void 0&&e.logger.warn(r==="framework-worker"?`--inspector-port does not apply to the ${r} flavor: pin \`dev.inspector_port\` in ${R} — that is the config the worker sidecar runs.`:`--inspector-port does not apply to the ${r} flavor: Vite owns the worker. Pin it in vite.config — \`lunora({ cloudflare: { inspectorPort: ${String(e.inspectorPort)} } })\`.`),e.local===!0&&r==="vite"&&e.logger.warn("--local does not apply to the vite flavor: Vite owns the worker. Turn remote bindings off in vite.config — `lunora({ cloudflare: { remoteBindings: false } })`.")},Qr=e=>{const r=e.cwd??process.cwd(),n=A(r),t=e.flavor??M(r);if(t==="vite"||t==="framework-worker"){const p=fe(r),u=t==="framework-worker"?Jr(e,r,n):void 0,v=u?.sidecar;return Xr(e,t),{runsCodegenWatch:!1,flavor:t,ipv4LoopbackForced:!1,remote:{bindings:[],cleanup:Se,enabled:e.remote===!0,withheld:u?.withheld??[]},...u?{serviceConfigCleanup:u.cleanup}:{},...v?{sidecar:v}:{},studioEnabled:!1,studioPort:e.port??$,workerEnabled:!0,workerOrigin:`http://localhost:${String(z)}`,workerPort:z,wrangler:{args:p.args,command:p.command,cwd:r,...we(e),tag:"vite"}}}const o=ee(r),i=o.framework==="none"?void 0:`this project uses ${o.framework} — the worker runs inside Vite there. run \`${tr(n,"dev")}\` for the full app (frontend + HMR); \`lunora dev\` starts only the worker.`,s=e.workerPort??m,a=Kr(e,r),c=ye(r,e.hasIpv6Loopback??he),l=e.inspectorPort===void 0?[]:["--inspector-port",String(e.inspectorPort)],d=ke(e,r),f=ae(n,"wrangler",["dev","--port",String(s),...l,...c,...Ie(e),"--var","WORKER_ENV:development",...a.args,...zr(r,d.args,a.args)]);return{runsCodegenWatch:me(e),flavor:t,frameworkHint:i,ipv4LoopbackForced:c.length>0,remote:{...a.plan,withheld:[...a.plan.withheld,...d.withheld]},serviceConfigCleanup:d.cleanup,studioEnabled:e.studio!==!1,studioPort:e.port??$,workerEnabled:e.worker!==!1,workerOrigin:`http://localhost:${String(s)}`,workerPort:s,wrangler:{args:f.args,command:f.command,cwd:r,tag:"wrangler"}}},Tt=async e=>{const r=e.cwd??process.cwd();if(e.target!==void 0&&D(e.target))return Br({cwd:r,driver:te(e.target),options:e,studioPort:e.port??$,workerPort:await Y(e,r)});const n=e.flavor??M(r),t=n==="wrangler"?await Y(e,r):e.workerPort,o=n==="wrangler"?qr(e,r):e.inspectorPort;return Qr({...e,cwd:r,flavor:n,inspectorPort:o,workerPort:t})};export{_ as P,yt as a,Cr as b,kr as c,ar as d,M as e,Et as f,Tt as g,Nr as h,kt as i,pt as j,St as k,me as l,Lt as m,wr as n,mt as o,Qr as p,qr as q,sr as r,It as s,wt as t,Y as u,ht as w};
@@ -1 +1 @@
1
- import"./free-port-DjbFhaaT.mjs";import{g as a,p as s,q as l,u as p}from"./plan-BH6l7s48.mjs";export{a as buildDevPlan,s as planDevCommand,l as resolveInspectorPort,p as resolveWorkerPort};
1
+ import"./free-port-DjbFhaaT.mjs";import{g as a,p as s,q as l,u as p}from"./plan-BF2E-dq4.mjs";export{a as buildDevPlan,s as planDevCommand,l as resolveInspectorPort,p as resolveWorkerPort};
@@ -1 +1 @@
1
- import"./admin-token-VdUnvnKW.mjs";import"./admin-url-DJB8P6Jh.mjs";import"./platform-diagnostics-Cn2g6Jh-.mjs";import"./api-spec-lYyq4vL6.mjs";import{f as D,b as x,r as y,a as P,s as b}from"../packem_chunks/handler.mjs";import"./vectorize-metadata-DK61NmVx.mjs";import"./binding-manifest-file-CQ1eQYy1.mjs";import"./command-BpMv2n0o.mjs";import"./deploy-target-resolve-BTCW1Iv4.mjs";import"./EXIT_CODE-DQVhSE-1.mjs";import"./resolve-target-BRvw8Pf7.mjs";import"./health-probe-By9T5x2y.mjs";import"./createRecordingSpawner-DchLi0gR.mjs";export{D as execute,x as provisionBindings,y as runDeployCommand,P as runPreDeployPipeline,b as smokeLoadDeferredModules};
1
+ import"./admin-token-VdUnvnKW.mjs";import"./admin-url-DJB8P6Jh.mjs";import"./platform-diagnostics-Cn2g6Jh-.mjs";import"./api-spec-lYyq4vL6.mjs";import{f as D,b as x,r as y,a as P,s as b}from"../packem_chunks/handler.mjs";import"./vectorize-metadata-DK61NmVx.mjs";import"./binding-manifest-file-BZyQFSTF.mjs";import"./command-BpMv2n0o.mjs";import"./deploy-target-resolve-BTCW1Iv4.mjs";import"./EXIT_CODE-DQVhSE-1.mjs";import"./resolve-target-BRvw8Pf7.mjs";import"./health-probe-By9T5x2y.mjs";import"./createRecordingSpawner-DchLi0gR.mjs";export{D as execute,x as provisionBindings,y as runDeployCommand,P as runPreDeployPipeline,b as smokeLoadDeferredModules};
@@ -1,4 +1,4 @@
1
- import{existsSync as p,mkdirSync as _,writeFileSync as w,mkdtempSync as C,rmSync as F,readFileSync as U}from"node:fs";import{tmpdir as O}from"node:os";import{discoverMigrations as k,discoverSchema as x,readPackageDependencies as D}from"@lunora/codegen";import{isInteractive as G,promptSelect as P,promptText as j}from"@lunora/config";import{LunoraError as T}from"@lunora/errors";import{join as m}from"@visulima/path";import{Project as v}from"ts-morph";import{r as z,t as L}from"./admin-token-VdUnvnKW.mjs";import{r as B,a as q,n as H}from"./admin-url-DJB8P6Jh.mjs";import{isRefusal as W,exitCodeForStatus as J,EXIT_CODE as d}from"./EXIT_CODE-DQVhSE-1.mjs";import{diffSnapshots as K,renderMigrationFile as V}from"./diffSnapshots-Y1G8am6C.mjs";import X from"./schemaIrToSnapshot-Ck7DSPdf.mjs";import{runExportCommand as Z}from"./runExportCommand-_PtkJJDT.mjs";import{r as Q}from"./import-3Y59Ry9F.mjs";import{readAndLogBody as Y}from"../packem_chunks/runRpcCommand.mjs";const h="__lunora_reproject__",ee=e=>{if(!e.startsWith(h))return;const r=e.slice(h.length);return r===""?void 0:r},re=".snapshot.json",N=/[^\da-z]+/gu,M=(e,r)=>{let t=0,o=e.length;for(;t<o&&e[t]===r;)t+=1;for(;o>t&&e[o-1]===r;)o-=1;return e.slice(t,o)},te=e=>{const r=M(e.toLowerCase().replaceAll(N,"_"),"_");return r===""?"auto":r},oe=e=>{const r=(t,o=2)=>t.toString().padStart(o,"0");return`${String(e.getUTCFullYear())}${r(e.getUTCMonth()+1)}${r(e.getUTCDate())}${r(e.getUTCHours())}${r(e.getUTCMinutes())}${r(e.getUTCSeconds())}`},ae=e=>{if(p(e))try{const r=U(e,"utf8"),t=JSON.parse(r);if(t.version!==1)throw new T("INTERNAL",`unsupported snapshot version: ${t.version}`);return t}catch(r){const t=r instanceof Error?r.message:String(r);throw new T("INTERNAL",`failed to read ${e}: ${t}`,{cause:r})}},je=e=>{const r=e.cwd??process.cwd(),t=m(r,"lunora","schema.ts");if(!p(t)){const f=`schema not found: ${t} — run \`vis generate lunora-table --name=<name>\` to create one`;return e.logger.error(f),{code:d.NOT_FOUND,empty:!0,error:f,migrationFile:""}}const o=new v({skipAddingFilesFromTsConfig:!0}),n=x(o,t),l=X(n),c=m(r,"lunora","migrations"),s=m(c,re);let a;try{a=ae(s)}catch(f){const S=f instanceof Error?f.message:String(f);return e.logger.error(S),{code:d.USAGE,empty:!0,error:S,migrationFile:""}}const i=K(a,l);if(i.empty)return e.logger.info("no schema changes detected — snapshot is already up to date"),{code:0,empty:!0,migrationFile:""};const g=(e.now??(()=>new Date))(),$=te(e.name??"auto"),y=`${oe(g)}_${$}.sql`,b=m(c,y);_(c,{recursive:!0});const R=V($,i,g.toISOString());return w(b,R,"utf8"),w(s,`${JSON.stringify(l,void 0,4)}
1
+ import{existsSync as p,mkdirSync as _,writeFileSync as w,mkdtempSync as C,rmSync as F,readFileSync as U}from"node:fs";import{tmpdir as O}from"node:os";import{discoverMigrations as k,discoverSchema as x,readPackageDependencies as D}from"@lunora/codegen";import{isInteractive as G,promptSelect as P,promptText as j}from"@lunora/config";import{LunoraError as T}from"@lunora/errors";import{join as m}from"@visulima/path";import{Project as v}from"ts-morph";import{r as z,t as L}from"./admin-token-VdUnvnKW.mjs";import{r as B,a as q,n as H}from"./admin-url-DJB8P6Jh.mjs";import{isRefusal as W,exitCodeForStatus as J,EXIT_CODE as d}from"./EXIT_CODE-DQVhSE-1.mjs";import{diffSnapshots as K,renderMigrationFile as V}from"./diffSnapshots-DcE-jQUz.mjs";import X from"./schemaIrToSnapshot-CbWOjany.mjs";import{runExportCommand as Z}from"./runExportCommand-_PtkJJDT.mjs";import{r as Q}from"./import-3Y59Ry9F.mjs";import{readAndLogBody as Y}from"../packem_chunks/runRpcCommand.mjs";const h="__lunora_reproject__",ee=e=>{if(!e.startsWith(h))return;const r=e.slice(h.length);return r===""?void 0:r},re=".snapshot.json",N=/[^\da-z]+/gu,M=(e,r)=>{let t=0,o=e.length;for(;t<o&&e[t]===r;)t+=1;for(;o>t&&e[o-1]===r;)o-=1;return e.slice(t,o)},te=e=>{const r=M(e.toLowerCase().replaceAll(N,"_"),"_");return r===""?"auto":r},oe=e=>{const r=(t,o=2)=>t.toString().padStart(o,"0");return`${String(e.getUTCFullYear())}${r(e.getUTCMonth()+1)}${r(e.getUTCDate())}${r(e.getUTCHours())}${r(e.getUTCMinutes())}${r(e.getUTCSeconds())}`},ae=e=>{if(p(e))try{const r=U(e,"utf8"),t=JSON.parse(r);if(t.version!==1)throw new T("INTERNAL",`unsupported snapshot version: ${t.version}`);return t}catch(r){const t=r instanceof Error?r.message:String(r);throw new T("INTERNAL",`failed to read ${e}: ${t}`,{cause:r})}},je=e=>{const r=e.cwd??process.cwd(),t=m(r,"lunora","schema.ts");if(!p(t)){const f=`schema not found: ${t} — run \`vis generate lunora-table --name=<name>\` to create one`;return e.logger.error(f),{code:d.NOT_FOUND,empty:!0,error:f,migrationFile:""}}const o=new v({skipAddingFilesFromTsConfig:!0}),n=x(o,t),l=X(n),c=m(r,"lunora","migrations"),s=m(c,re);let a;try{a=ae(s)}catch(f){const S=f instanceof Error?f.message:String(f);return e.logger.error(S),{code:d.USAGE,empty:!0,error:S,migrationFile:""}}const i=K(a,l);if(i.empty)return e.logger.info("no schema changes detected — snapshot is already up to date"),{code:0,empty:!0,migrationFile:""};const g=(e.now??(()=>new Date))(),$=te(e.name??"auto"),y=`${oe(g)}_${$}.sql`,b=m(c,y);_(c,{recursive:!0});const R=V($,i,g.toISOString());return w(b,R,"utf8"),w(s,`${JSON.stringify(l,void 0,4)}
2
2
  `,"utf8"),e.logger.success(`wrote ${b}`),i.unsupported.length>0&&e.logger.warn(`${String(i.unsupported.length)} unsupported diff(s) — see the comment block in ${y} and write the SQL manually`),{code:0,empty:!1,migrationFile:b}},ne="migrations.ts",A=/^[A-Za-z_]\w*$/u,ie=new Set(["await","break","case","catch","class","const","continue","debugger","default","delete","do","else","enum","export","extends","false","finally","for","function","if","implements","import","in","instanceof","interface","let","new","null","package","private","protected","public","return","static","super","switch","this","throw","true","try","typeof","var","void","while","with","yield"]),E=/^import\s*\{\s*defineMigration\s*\}\s*from\s*["'](?:@lunora\/server|lunorash\/server)["'][^\n]*$/mu,se=e=>`import { defineMigration } from "${D(e)?.has("lunorash")??!1?"lunorash/server":"@lunora/server"}";`,ce="__lunora_admin__:runMigration",de="__lunora_admin__:migrationStatus",le="/_lunora/migrate",ue=e=>M(e.trim().toLowerCase().replaceAll(N,"-"),"-"),me=e=>e.split("-").filter(r=>r.length>0).map((r,t)=>t===0?r:r.charAt(0).toUpperCase()+r.slice(1)).join(""),ge=e=>{const r=m(e,"lunora","schema.ts");if(!p(r))return[];try{const t=new v({skipAddingFilesFromTsConfig:!0});return x(t,r).tables.map(o=>o.name)}catch{return[]}},fe=async e=>e.length>0?P("Which table does this migration iterate?",e.map(r=>({label:r,value:r}))):j("Target table for the migration: "),he=async(e,r)=>{if(r.table!==void 0)return r.table;const t=r.promptTable??(G()?fe:void 0);if(t===void 0){r.logger.error("migrate create requires a target table when not running interactively — re-run with --table <table>");return}const n=(await t(ge(e)))?.trim();if(n===void 0||n===""){r.logger.error("no table selected — re-run with --table <table>");return}return n},ze=async e=>{const r=e.cwd??process.cwd(),t=ue(e.name);if(t==="")return e.logger.error(`invalid migration name: "${e.name}" — must contain at least one alphanumeric character`),{code:d.USAGE,file:""};const o=me(t);if(!A.test(o)||ie.has(o))return e.logger.error(`invalid migration name: "${e.name}" derives the export \`${o}\`, which is not a valid identifier — pick a name that starts with a letter and isn't a reserved word`),{code:d.USAGE,file:""};const n=await he(r,e);if(n===void 0)return{code:d.USAGE,file:""};if(!A.test(n))return e.logger.error(`invalid table: "${n}" — must be a valid identifier ([A-Za-z_][A-Za-z0-9_]*)`),{code:d.USAGE,file:""};const l=m(r,"lunora"),c=m(l,ne);let s=p(c)?U(c,"utf8"):"";if(s.includes(`id: "${t}"`)||new RegExp(String.raw`\bexport const ${o}\b`,"u").test(s))return e.logger.error(`a migration with id "${t}" (export \`${o}\`) already exists in ${c}`),{code:d.CONFLICT,file:""};const a=se(r);s.trim()===""?s=`${a}
3
3
  `:E.test(s)?s=s.replace(E,a):s=`${a}
4
4
  ${s}`;const i=`export const ${o} = defineMigration({
@@ -1 +1 @@
1
- import{buildSchemaSnapshot as b,isD1GlobalTable as d}from"@lunora/codegen";import{validatorKindToSqlType as p}from"./diffSnapshots-Y1G8am6C.mjs";const k=new Set(["any","null"]),f=(n,e)=>{const t=e.kindOf(n);return t!==void 0&&k.has(t)||e.nullableOf(n)?!0:t==="union"&&(e.membersOf(n)??[]).some(l=>f(l,e))},m=n=>n.kind==="optional"&&n.inner?m(n.inner):n.kind==="literal"&&n.literalValue==="null"?"null":n.kind,i=n=>n.kind==="optional"&&n.inner?i(n.inner):n,O=n=>f(n,{kindOf:m,membersOf:e=>i(e).members,nullableOf:e=>i(e).column?.notNull===!1}),N=n=>p(n.kind==="optional"&&n.inner?n.inner.kind:n.kind),T=(n,e)=>n.kind==="optional"||O(n)?!0:e===void 0?!1:e.nullable===!0,h=d,y=n=>{const e={},t=b(n,[]);for(const l of n.tables){if(!h(l))continue;const a={};for(const[o,c]of Object.entries(l.shape)){const s=t.tables[l.name]?.fields[o],r={nullable:T(c,s),sqlType:N(c)};a[o]=s===void 0?r:{...r,field:s}}const u={};for(const o of l.indexes)u[o.name]={fields:[...o.fields],name:o.name,unique:o.unique??!1};e[l.name]={columns:a,indexes:u,name:l.name}}return{tables:e,version:1}};export{y as default};
1
+ import{buildSchemaSnapshot as b,isD1GlobalTable as d}from"@lunora/codegen";import{validatorKindToSqlType as p}from"./diffSnapshots-DcE-jQUz.mjs";const k=new Set(["any","null"]),f=(n,e)=>{const t=e.kindOf(n);return t!==void 0&&k.has(t)||e.nullableOf(n)?!0:t==="union"&&(e.membersOf(n)??[]).some(l=>f(l,e))},m=n=>n.kind==="optional"&&n.inner?m(n.inner):n.kind==="literal"&&n.literalValue==="null"?"null":n.kind,i=n=>n.kind==="optional"&&n.inner?i(n.inner):n,O=n=>f(n,{kindOf:m,membersOf:e=>i(e).members,nullableOf:e=>i(e).column?.notNull===!1}),N=n=>p(n.kind==="optional"&&n.inner?n.inner.kind:n.kind),T=(n,e)=>n.kind==="optional"||O(n)?!0:e===void 0?!1:e.nullable===!0,h=d,y=n=>{const e={},t=b(n,[]);for(const l of n.tables){if(!h(l))continue;const a={};for(const[o,c]of Object.entries(l.shape)){const s=t.tables[l.name]?.fields[o],r={nullable:T(c,s),sqlType:N(c)};a[o]=s===void 0?r:{...r,field:s}}const u={};for(const o of l.indexes)u[o.name]={fields:[...o.fields],name:o.name,unique:o.unique??!1};e[l.name]={columns:a,indexes:u,name:l.name}}return{tables:e,version:1}};export{y as default};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/cli",
3
- "version": "1.0.0-alpha.487",
3
+ "version": "1.0.0-alpha.489",
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",
@@ -58,13 +58,13 @@
58
58
  "@bomb.sh/tab": "0.0.22",
59
59
  "@lunora/advisor": "^1.0.0-alpha.289",
60
60
  "@lunora/bindings": "1.0.0-alpha.151",
61
- "@lunora/codegen": "^1.0.0-alpha.400",
62
- "@lunora/config": "^1.0.0-alpha.457",
61
+ "@lunora/codegen": "^1.0.0-alpha.403",
62
+ "@lunora/config": "^1.0.0-alpha.459",
63
63
  "@lunora/container": "1.0.0-alpha.108",
64
- "@lunora/d1": "^1.0.0-alpha.233",
64
+ "@lunora/d1": "^1.0.0-alpha.234",
65
65
  "@lunora/errors": ">=1.0.0-alpha.51 <2.0.0-0",
66
66
  "@lunora/mcp": "^1.0.0-alpha.291",
67
- "@lunora/runtime": "^1.0.0-alpha.278",
67
+ "@lunora/runtime": "^1.0.0-alpha.280",
68
68
  "@lunora/seed": "^1.0.0-alpha.282",
69
69
  "@lunora/wire": "1.0.0-alpha.6",
70
70
  "@visulima/cerebro": "4.0.1",
@@ -1,2 +0,0 @@
1
- import{mkdirSync as a,writeFileSync as g}from"node:fs";import{findWranglerFile as f,readWranglerJsonc as l,buildBindingManifest as m}from"@lunora/config/cloudflare";import{isAbsolute as c,resolve as h,dirname as u}from"@visulima/path";const v=r=>{const n=f(r),e=n===void 0?void 0:l(n).parsed;return e===void 0?{error:`no readable wrangler config in ${r}. The binding manifest is derived from it, and an empty one would tell a deployer this Worker needs nothing.`}:{manifest:m(e)}},y=r=>{const{destination:n,dev:e,logger:s,projectRoot:d}=r,t=v(d);if(t.manifest===void 0)return{error:t.error};const i={...t.manifest,...e===void 0?{}:{dev:e}},o=c(n)?n:h(d,n);return a(u(o),{recursive:!0}),g(o,`${JSON.stringify(i,void 0,2)}
2
- `,"utf8"),s.success(`binding manifest written to ${o} (${i.bindings.length.toString()} bindings, ${i.crons.length.toString()} crons)`),i.unknown.length>0&&s.warn(`binding manifest does not model these wrangler sections: ${i.unknown.join(", ")}. Anything they bind must be provisioned by hand — please report them so the manifest can cover them.`),{}};export{v as d,y as w};
@@ -1,7 +0,0 @@
1
- import{quoteIdentifier as a,sqlAffinityForKind as h,frameworkColumnDdl as y,OCC_VERSION_COLUMN as T,MAX_D1_TABLE_COLUMNS as d,columnRef as E,physicalIndexName as c}from"@lunora/d1/dialect";import{LunoraError as L}from"@lunora/errors";const A=e=>h(e),f=(e,i)=>{const s=[a(e),i.sqlType];return i.nullable||s.push("NOT NULL"),s.join(" ")},g=e=>{const i=Object.entries(e.columns).map(([o,r])=>` ${f(o,r)}`),s=[...y(),`${a(T)} INTEGER`],n=s.length+i.length;if(n>d)throw new L("VALIDATION_ERROR",`table "${e.name}" needs ${String(n)} columns, over D1's ${String(d)}-column limit — split the table, or move the extra fields into one object field`);const t=[...s.map(o=>` ${o}`),...i].join(`,
2
- `);return`CREATE TABLE IF NOT EXISTS ${a(e.name)} (
3
- ${t}
4
- );`},O=e=>`DROP TABLE IF EXISTS ${a(e)};`,C=(e,i,s)=>{const n=`ALTER TABLE ${a(e)} ADD COLUMN ${f(i,{...s,nullable:!0})};`;return s.nullable?n:`${n}
5
- -- ${a(e)}.${a(i)} is required, so rows written before it read NULL. Backfill them:
6
- -- UPDATE ${a(e)} SET ${a(i)} = <value> WHERE ${a(i)} IS NULL;`},p=(e,i)=>{const s=i.fields.map(t=>E(t)).join(", ");return`CREATE ${i.unique?"UNIQUE ":""}INDEX IF NOT EXISTS ${c(e,i.name)} ON ${a(e)} (${s});`},S=(e,i)=>`DROP INDEX IF EXISTS ${c(e,i)};`,u=e=>e.field===void 0?void 0:JSON.stringify({...e.field,optional:void 0,refined:void 0}),I="https://lunora.sh/docs/concepts/migrations#relaxing-not-null-on-a-d1-table",b=(e,i,s,n,t)=>{const o=u(s),r=u(n);if(s.sqlType!==n.sqlType)t.push({kind:"columnTypeChange",summary:`column type change on ${e}.${i}: ${s.sqlType} → ${n.sqlType} (write SQL manually)`});else if(o!==void 0&&r!==void 0&&o!==r){const l=s.field?.kind===n.field?.kind?`${String(n.field?.kind)} shape/constraints changed`:`${String(s.field?.kind)} → ${String(n.field?.kind)}`;t.push({kind:"columnTypeChange",summary:`column type change on ${e}.${i}: ${l} (write SQL manually)`})}if(s.nullable!==n.nullable){const l=n.nullable?`rebuild the table — ${I}`:"write SQL manually";t.push({kind:"columnTypeChange",summary:`nullability change on ${e}.${i}: ${s.nullable?"NULL":"NOT NULL"} → ${n.nullable?"NULL":"NOT NULL"} (${l})`})}},q=(e,i,s,n,t)=>{for(const[o,r]of Object.entries(s)){const l=i[o];if(l===void 0){n.push({kind:"addColumn",sql:C(e,o,r),summary:`ADD COLUMN ${e}.${o}`});continue}b(e,o,l,r,t)}for(const o of Object.keys(i))s[o]===void 0&&t.push({kind:"dropColumn",summary:`DROP COLUMN ${e}.${o} (SQLite drop-column requires careful migration — write SQL manually)`})},N=(e,i,s,n,t)=>{for(const[o,r]of Object.entries(s)){const l=i[o];if(l===void 0){n.push({kind:"createIndex",sql:p(e,r),summary:`CREATE INDEX ${o} ON ${e}`});continue}(!(l.fields.length===r.fields.length&&l.fields.every((m,$)=>m===r.fields[$]))||l.unique!==r.unique)&&t.push({kind:"indexRename",summary:`index ${o} changed on ${e} — drop+create manually if intentional`})}for(const o of Object.keys(i))s[o]===void 0&&n.push({kind:"dropIndex",sql:S(e,o),summary:`DROP INDEX ${o}`})},D=(e,i,s)=>{s.push({kind:"createTable",sql:g(i),summary:`CREATE TABLE ${e}`});for(const n of Object.values(i.indexes))s.push({kind:"createIndex",sql:p(e,n),summary:`CREATE INDEX ${n.name} ON ${e}`})},w=(e,i)=>{const s=e?.tables??{},n=[],t=[];for(const[o,r]of Object.entries(i.tables)){const l=s[o];if(l===void 0){D(o,r,n);continue}q(o,l.columns,r.columns,n,t),N(o,l.indexes,r.indexes,n,t)}for(const o of Object.keys(s))i.tables[o]===void 0&&n.push({kind:"dropTable",sql:O(o),summary:`DROP TABLE ${o}`});return{empty:n.length===0&&t.length===0,entries:n,unsupported:t}},U=(e,i,s)=>{const n=[`-- Lunora migration: ${e}`,`-- Generated at ${s}`,"-- This file was produced by `lunora migrate generate`. Review carefully before applying.",""];for(const t of i.entries)n.push(`-- ${t.summary}`,t.sql,"");if(i.unsupported.length>0){n.push("-- ---------------------------------------------------------------","-- The following deltas are NOT auto-generated in v0.1.","-- Write the appropriate SQL below by hand:","--");for(const t of i.unsupported)n.push(`-- * ${t.summary}`);n.push("-- ---------------------------------------------------------------","")}return i.empty&&n.push("-- No changes detected. Re-running `lunora migrate generate` will overwrite this file.",""),n.join(`
7
- `)};export{w as diffSnapshots,C as renderAddColumn,p as renderCreateIndex,g as renderCreateTable,S as renderDropIndex,O as renderDropTable,U as renderMigrationFile,A as validatorKindToSqlType};